soco-cli 0.4.75__tar.gz → 0.4.77__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {soco-cli-0.4.75/soco_cli.egg-info → soco_cli-0.4.77}/PKG-INFO +2 -2
- {soco-cli-0.4.75 → soco_cli-0.4.77}/README.md +17 -1
- {soco-cli-0.4.75 → soco_cli-0.4.77}/requirements.txt +1 -1
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/__init__.py +1 -1
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/action_processor.py +28 -4
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/alarms.py +20 -16
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/http_api.py +17 -4
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/interactive.py +6 -4
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/match_speaker_names.py +3 -2
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/play_local_file.py +23 -5
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/sonos.py +3 -2
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/speakers.py +9 -7
- {soco-cli-0.4.75 → soco_cli-0.4.77/soco_cli.egg-info}/PKG-INFO +2 -2
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli.egg-info/requires.txt +1 -1
- {soco-cli-0.4.75 → soco_cli-0.4.77}/LICENSE +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/MANIFEST.in +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/PYPI_README.md +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/pyproject.toml +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/setup.cfg +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/setup.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/__main__.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/aliases.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/api.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/check_for_update.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/cmd_parser.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/keystroke_capture.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/m3u_parser.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/play_local_file_lists.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/sonos_discover.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/speaker_info.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/track_follow.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/utils.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli/wait_actions.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli.egg-info/SOURCES.txt +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli.egg-info/dependency_links.txt +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli.egg-info/entry_points.txt +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/soco_cli.egg-info/top_level.txt +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/tests/test_cli.py +0 -0
- {soco-cli-0.4.75 → soco_cli-0.4.77}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: soco-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.77
|
|
4
4
|
Summary: Sonos command line control utility, based on SoCo
|
|
5
5
|
Author-email: Avantrec Ltd <soco_cli@avantrec.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/avantrec/soco-cli
|
|
@@ -12,7 +12,7 @@ Requires-Python: >=3.5
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: soco==0.27.1; python_version < "3.6"
|
|
15
|
-
Requires-Dist: soco>=0.
|
|
15
|
+
Requires-Dist: soco>=0.30.2; python_version >= "3.6"
|
|
16
16
|
Requires-Dist: ifaddr==0.1.7; python_version < "3.7"
|
|
17
17
|
Requires-Dist: ifaddr>=0.2.0; python_version >= "3.7"
|
|
18
18
|
Requires-Dist: tabulate
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
* [Using the Local Speaker Cache](#using-the-local-speaker-cache)
|
|
63
63
|
* [HTTP Request Structure](#http-request-structure)
|
|
64
64
|
* [Return Values](#return-values)
|
|
65
|
+
* [Asynchronous Commands (Experimental)](#asynchronous-commands-experimental)
|
|
65
66
|
* [Macros: Defining Custom HTTP API Server Actions](#macros-defining-custom-http-api-server-actions)
|
|
66
67
|
* [Macro Definition and Usage](#macro-definition-and-usage)
|
|
67
68
|
* [Macro Arguments](#macro-arguments)
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
* [Resources](#resources)
|
|
85
86
|
|
|
86
87
|
<!-- Created by https://github.com/ekalinin/github-markdown-toc -->
|
|
87
|
-
<!-- Added by: pwt, at:
|
|
88
|
+
<!-- Added by: pwt, at: Sat May 11 11:52:19 BST 2024 -->
|
|
88
89
|
|
|
89
90
|
<!--te-->
|
|
90
91
|
|
|
@@ -409,6 +410,7 @@ sonos Kitchen play_from_queue 5
|
|
|
409
410
|
- **`play_m3u <m3u_file> <options>`** (or **`play_local_m3u`**): Plays a local M3U/M3U8 playlist consisting of local audio files (in supported audio formats). Can be followed by options `p` to print each filename before it plays, and/or `s` to shuffle the playlist, or `r` to play a single, random track from the playlist. (If using multiple options, concatenate them: e.g., `ps`.) Example: `sonos Study play_m3u my_playlist.m3u ps`. Add the `i` option to invoke **interactive** mode, which allows use of the keyboard to go to the (N)ext track, to (P)ause, or to (R)esume playback.
|
|
410
411
|
- **`play_mode` (or `mode`)**: Returns the play mode of the speaker, one of `NORMAL`, `REPEAT_ONE`, `REPEAT_ALL`, `SHUFFLE`, `SHUFFLE_REPEAT_ONE`, or `SHUFFLE_NOREPEAT`.
|
|
411
412
|
- **`play_mode <mode>` (or `mode`)**: Sets the play mode of the speaker to `<mode>`, which is one of the values above.
|
|
413
|
+
- **`play_sharelink <sharelink>`**: Adds a sharelink to the end of the queue and starts playback.
|
|
412
414
|
- **`play_uri <uri> <title>` (or `uri`, `pu`)**: Plays the audio object given by the `<uri>` parameter (e.g., a radio stream URL). `<title>` is optional, and if present will be used for the title of the audio stream.
|
|
413
415
|
- **`previous` (or `prev`)**: Move to the previous track (if applicable for the audio source).
|
|
414
416
|
- **`repeat` (or `rpt`)**: Returns the repeat mode state: 'off', 'one', or 'all'.
|
|
@@ -1075,6 +1077,20 @@ If the command is successful, the **`result`** field contains the result string,
|
|
|
1075
1077
|
|
|
1076
1078
|
If the command is unsuccessful, the **`error_msg`** field contains an error message describing the error.
|
|
1077
1079
|
|
|
1080
|
+
### Asynchronous Commands (Experimental)
|
|
1081
|
+
|
|
1082
|
+
It's sometimes useful for the HTTP API server to respond immediately while its invoked action continues to run in the background. For example, if one wants to invoke a `play_file` action, and allow it to run in the background.
|
|
1083
|
+
|
|
1084
|
+
This can be achieved by prefixing the action with `async_`. For example:
|
|
1085
|
+
|
|
1086
|
+
```commandline
|
|
1087
|
+
http://192.168.0.100:8000/Kitchen/async_play_file/my_file.mp3
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
Note that the data returned in this case may not be useful: it will simply indicate whether the background command was invoked successfully, not whether the command was successful.
|
|
1091
|
+
|
|
1092
|
+
The `async_` functionality does not (yet) apply to the Macros feature described below.
|
|
1093
|
+
|
|
1078
1094
|
### Macros: Defining Custom HTTP API Server Actions
|
|
1079
1095
|
|
|
1080
1096
|
The **macros** feature allows the creation of custom actions or sequences of actions to be executed by the HTTP API server, and available at the `/macro/<macro_name>` endpoint. Macros are defined in a text file that is loaded when the server starts, and which can subsequently be reloaded using the `/macros/reload` endpoint.
|
|
@@ -1659,8 +1659,7 @@ def wait_stopped_for_core(speaker, action, duration_arg, not_paused=False):
|
|
|
1659
1659
|
start_time = current_time
|
|
1660
1660
|
remaining_time = duration - (current_time - start_time)
|
|
1661
1661
|
logging.info(
|
|
1662
|
-
"Elapsed since last 'STOPPED' = {}s | total elapsed = {}s | remaining = {}s"
|
|
1663
|
-
.format(
|
|
1662
|
+
"Elapsed since last 'STOPPED' = {}s | total elapsed = {}s | remaining = {}s".format(
|
|
1664
1663
|
int(current_time - start_time),
|
|
1665
1664
|
int(current_time - original_start_time),
|
|
1666
1665
|
int(remaining_time),
|
|
@@ -2358,8 +2357,9 @@ def trueplay(speaker, action, args, soco_function, use_local_speaker_list):
|
|
|
2358
2357
|
parameter_type_error(action, "on|off")
|
|
2359
2358
|
except:
|
|
2360
2359
|
error_report(
|
|
2361
|
-
"No Trueplay profile available for '{}' (or Trueplay not supported)"
|
|
2362
|
-
|
|
2360
|
+
"No Trueplay profile available for '{}' (or Trueplay not supported)".format(
|
|
2361
|
+
speaker.player_name
|
|
2362
|
+
)
|
|
2363
2363
|
)
|
|
2364
2364
|
return False
|
|
2365
2365
|
return True
|
|
@@ -2579,6 +2579,29 @@ def add_sharelink_to_queue(
|
|
|
2579
2579
|
return True
|
|
2580
2580
|
|
|
2581
2581
|
|
|
2582
|
+
@one_parameter
|
|
2583
|
+
def play_sharelink(speaker, action, args, soco_function, use_local_speaker_list):
|
|
2584
|
+
share_link = ShareLinkPlugin(speaker)
|
|
2585
|
+
uri = args[0]
|
|
2586
|
+
|
|
2587
|
+
if not share_link.is_share_link(uri):
|
|
2588
|
+
error_report("Invalid sharelink: '{}'".format(uri))
|
|
2589
|
+
return False
|
|
2590
|
+
|
|
2591
|
+
position = speaker.queue_size + 1
|
|
2592
|
+
|
|
2593
|
+
try:
|
|
2594
|
+
# Return the queue position of the first added item
|
|
2595
|
+
queue_position = share_link.add_share_link_to_queue(uri, position)
|
|
2596
|
+
save_queue_insertion_position(queue_position)
|
|
2597
|
+
except SoCoUPnPException as e:
|
|
2598
|
+
error_report("Unable to play sharelink to queue: {}".format(e))
|
|
2599
|
+
return False
|
|
2600
|
+
|
|
2601
|
+
speaker.play_from_queue(queue_position - 1)
|
|
2602
|
+
return True
|
|
2603
|
+
|
|
2604
|
+
|
|
2582
2605
|
@zero_parameters
|
|
2583
2606
|
def reboot_count(speaker, action, args, soco_function, use_local_speaker_list):
|
|
2584
2607
|
print(speaker.boot_seqnum)
|
|
@@ -3142,6 +3165,7 @@ actions = {
|
|
|
3142
3165
|
"channel": SonosFunction(get_channel, "", True),
|
|
3143
3166
|
"add_sharelink_to_queue": SonosFunction(add_sharelink_to_queue, "", True),
|
|
3144
3167
|
"sharelink": SonosFunction(add_sharelink_to_queue, "", True),
|
|
3168
|
+
"play_sharelink": SonosFunction(play_sharelink, "", True),
|
|
3145
3169
|
"is_indexing": SonosFunction(is_indexing, "", False),
|
|
3146
3170
|
"reboot_count": SonosFunction(reboot_count, "", False),
|
|
3147
3171
|
"play_directory": SonosFunction(play_directory, "", True),
|
|
@@ -53,18 +53,20 @@ def list_alarms(speaker, action, args, soco_function, use_local_speaker_list):
|
|
|
53
53
|
duration = alarm.duration.strftime("%H:%M")
|
|
54
54
|
else:
|
|
55
55
|
duration = "No Limit"
|
|
56
|
-
details.append(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
details.append(
|
|
57
|
+
[
|
|
58
|
+
alarm.alarm_id,
|
|
59
|
+
alarm.zone.player_name,
|
|
60
|
+
time,
|
|
61
|
+
duration,
|
|
62
|
+
alarm.recurrence,
|
|
63
|
+
convert_true_false(alarm.enabled),
|
|
64
|
+
title,
|
|
65
|
+
alarm.play_mode,
|
|
66
|
+
alarm.volume,
|
|
67
|
+
convert_true_false(alarm.include_linked_zones),
|
|
68
|
+
]
|
|
69
|
+
)
|
|
68
70
|
|
|
69
71
|
# Sort alarms by start time, room. Apply sorts in reverse order.
|
|
70
72
|
details.sort(key=lambda field: field[1]) # Room
|
|
@@ -341,8 +343,9 @@ def copy_modify_alarm(speaker, action, args, soco_function, use_local_speaker_li
|
|
|
341
343
|
break
|
|
342
344
|
else:
|
|
343
345
|
error_report(
|
|
344
|
-
"Alarm ID '{}' not found; use the 'alarms' action to find the integer ID"
|
|
345
|
-
|
|
346
|
+
"Alarm ID '{}' not found; use the 'alarms' action to find the integer ID".format(
|
|
347
|
+
alarm_id
|
|
348
|
+
)
|
|
346
349
|
)
|
|
347
350
|
return False
|
|
348
351
|
|
|
@@ -360,8 +363,9 @@ def copy_modify_alarm(speaker, action, args, soco_function, use_local_speaker_li
|
|
|
360
363
|
new_alarm.save()
|
|
361
364
|
except SoCoUPnPException as e:
|
|
362
365
|
error_report(
|
|
363
|
-
"Failed to copy/move alarm; did you remember to modify the start time?: {}"
|
|
364
|
-
|
|
366
|
+
"Failed to copy/move alarm; did you remember to modify the start time?: {}".format(
|
|
367
|
+
e
|
|
368
|
+
)
|
|
365
369
|
)
|
|
366
370
|
return False
|
|
367
371
|
|
|
@@ -10,7 +10,7 @@ import argparse
|
|
|
10
10
|
import pprint
|
|
11
11
|
import shlex
|
|
12
12
|
from os.path import abspath
|
|
13
|
-
from subprocess import STDOUT, CalledProcessError, check_output
|
|
13
|
+
from subprocess import STDOUT, CalledProcessError, Popen, check_output
|
|
14
14
|
from typing import Dict, Tuple
|
|
15
15
|
|
|
16
16
|
import uvicorn # type: ignore
|
|
@@ -33,6 +33,7 @@ PREFIX_MACRO = PREFIX + "Macro: "
|
|
|
33
33
|
MACROS: Dict[str, str] = {}
|
|
34
34
|
MACRO_FILE = ""
|
|
35
35
|
PP = pprint.PrettyPrinter(indent=len(PREFIX_MACRO))
|
|
36
|
+
ASYNC_PREFIX = "async_"
|
|
36
37
|
|
|
37
38
|
# Gets used with the local speaker list only
|
|
38
39
|
SPEAKER_LIST = Speakers(network_timeout=1.0)
|
|
@@ -56,9 +57,21 @@ def command_core(
|
|
|
56
57
|
device, error_msg = get_speaker(speaker, use_local_speaker_list=use_local)
|
|
57
58
|
if device:
|
|
58
59
|
speaker = device.player_name
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
if not action.startswith(ASYNC_PREFIX):
|
|
61
|
+
exit_code, result, error_msg = sc_run(
|
|
62
|
+
device, action, *args, use_local_speaker_list=use_local
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
action = action.replace(ASYNC_PREFIX, "")
|
|
66
|
+
try:
|
|
67
|
+
Popen(["sonos", str(device.ip_address), action, *args])
|
|
68
|
+
exit_code = 0
|
|
69
|
+
error_msg = ""
|
|
70
|
+
result = ""
|
|
71
|
+
except Exception as e:
|
|
72
|
+
exit_code = 1
|
|
73
|
+
error_msg = str(e)
|
|
74
|
+
result = ""
|
|
62
75
|
else:
|
|
63
76
|
exit_code = 1
|
|
64
77
|
result = ""
|
|
@@ -443,8 +443,9 @@ def interactive_loop(
|
|
|
443
443
|
name = args.pop(0)
|
|
444
444
|
if name in ACTIONS_TO_EXEC_NO_SPEAKER:
|
|
445
445
|
print(
|
|
446
|
-
"Please set an active speaker to use the '{}' action"
|
|
447
|
-
|
|
446
|
+
"Please set an active speaker to use the '{}' action".format(
|
|
447
|
+
name
|
|
448
|
+
)
|
|
448
449
|
)
|
|
449
450
|
continue
|
|
450
451
|
else:
|
|
@@ -649,8 +650,9 @@ def _get_speaker_names(use_local_speaker_list=False):
|
|
|
649
650
|
names = speaker_cache().get_all_speaker_names()
|
|
650
651
|
except Exception as e:
|
|
651
652
|
print(
|
|
652
|
-
"Speaker listing failed: please check your network connection [{}]"
|
|
653
|
-
|
|
653
|
+
"Speaker listing failed: please check your network connection [{}]".format(
|
|
654
|
+
e
|
|
655
|
+
)
|
|
654
656
|
)
|
|
655
657
|
names = []
|
|
656
658
|
return names
|
|
@@ -36,8 +36,9 @@ def speaker_name_matches(name_supplied, name_stored):
|
|
|
36
36
|
name_stored = name_stored.replace("’", "'")
|
|
37
37
|
if name_supplied == name_stored:
|
|
38
38
|
logging.info(
|
|
39
|
-
"Found apostrophe-normalised exact speaker name match for '{}' as '{}'"
|
|
40
|
-
|
|
39
|
+
"Found apostrophe-normalised exact speaker name match for '{}' as '{}'".format(
|
|
40
|
+
name_supplied_original, name_stored_original
|
|
41
|
+
)
|
|
41
42
|
)
|
|
42
43
|
return True, True
|
|
43
44
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import functools
|
|
4
4
|
import http.client
|
|
5
5
|
import logging
|
|
6
|
+
import socket
|
|
6
7
|
import sys
|
|
7
8
|
import time
|
|
8
9
|
import urllib.parse
|
|
@@ -148,19 +149,36 @@ def get_server_ip(speaker: SoCo) -> Optional[str]:
|
|
|
148
149
|
for adapter in adapters:
|
|
149
150
|
for ip in adapter.ips:
|
|
150
151
|
if ip.is_IPv4 and ip.ip != "127.0.0.1":
|
|
152
|
+
# Find an available port
|
|
153
|
+
for port in range(PORT_START, PORT_END + 1):
|
|
154
|
+
try:
|
|
155
|
+
logging.info("Checking if port {} is available".format(port))
|
|
156
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
157
|
+
s.bind((ip.ip, port))
|
|
158
|
+
available_port = port
|
|
159
|
+
logging.info("Port {} is available".format(port))
|
|
160
|
+
break
|
|
161
|
+
except socket.error:
|
|
162
|
+
continue
|
|
163
|
+
else:
|
|
164
|
+
logging.info("No available ports for IP address '{}'".format(ip.ip))
|
|
165
|
+
continue
|
|
151
166
|
try:
|
|
152
167
|
logging.info(
|
|
153
|
-
"Checking target speaker's reachability from IP address '{}'"
|
|
154
|
-
|
|
168
|
+
"Checking target speaker's reachability from IP address '{}'".format(
|
|
169
|
+
ip.ip
|
|
170
|
+
)
|
|
155
171
|
)
|
|
156
172
|
http_connection = http.client.HTTPConnection(
|
|
157
173
|
speaker.ip_address,
|
|
158
174
|
port=1400,
|
|
159
|
-
timeout=0
|
|
160
|
-
source_address=(ip.ip,
|
|
175
|
+
timeout=5.0,
|
|
176
|
+
source_address=(ip.ip, available_port),
|
|
161
177
|
)
|
|
162
178
|
http_connection.request("GET", "/status/info")
|
|
163
|
-
|
|
179
|
+
response = http_connection.getresponse()
|
|
180
|
+
http_connection.close()
|
|
181
|
+
if response.status == 200:
|
|
164
182
|
return ip.ip
|
|
165
183
|
except:
|
|
166
184
|
continue
|
|
@@ -410,8 +410,9 @@ def main():
|
|
|
410
410
|
if env_speaker:
|
|
411
411
|
if env_spkr_inserted[sequence_pointer] is False:
|
|
412
412
|
logging.info(
|
|
413
|
-
"Getting speaker name '{}' from the $SPKR environment variable"
|
|
414
|
-
|
|
413
|
+
"Getting speaker name '{}' from the $SPKR environment variable".format(
|
|
414
|
+
env_speaker
|
|
415
|
+
)
|
|
415
416
|
)
|
|
416
417
|
sequence.insert(0, env_speaker)
|
|
417
418
|
speaker_name = env_speaker
|
|
@@ -305,13 +305,15 @@ class Speakers:
|
|
|
305
305
|
visible = "Visible"
|
|
306
306
|
else:
|
|
307
307
|
visible = "Hidden"
|
|
308
|
-
households[device.household_id].append(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
308
|
+
households[device.household_id].append(
|
|
309
|
+
(
|
|
310
|
+
device.speaker_name,
|
|
311
|
+
device.ip_address,
|
|
312
|
+
device.model_name.replace("Sonos ", ""),
|
|
313
|
+
visible,
|
|
314
|
+
device.display_version,
|
|
315
|
+
)
|
|
316
|
+
)
|
|
315
317
|
num_devices += 1
|
|
316
318
|
|
|
317
319
|
headers = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: soco-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.77
|
|
4
4
|
Summary: Sonos command line control utility, based on SoCo
|
|
5
5
|
Author-email: Avantrec Ltd <soco_cli@avantrec.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/avantrec/soco-cli
|
|
@@ -12,7 +12,7 @@ Requires-Python: >=3.5
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: soco==0.27.1; python_version < "3.6"
|
|
15
|
-
Requires-Dist: soco>=0.
|
|
15
|
+
Requires-Dist: soco>=0.30.2; python_version >= "3.6"
|
|
16
16
|
Requires-Dist: ifaddr==0.1.7; python_version < "3.7"
|
|
17
17
|
Requires-Dist: ifaddr>=0.2.0; python_version >= "3.7"
|
|
18
18
|
Requires-Dist: tabulate
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|