vsslctrl 0.1.9.dev1__py3-none-any.whl → 0.1.10.dev1__py3-none-any.whl
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.
- vsslctrl/__init__.py +3 -2
- vsslctrl/api_alpha.py +57 -9
- vsslctrl/core.py +14 -0
- vsslctrl/data_structure.py +74 -17
- vsslctrl/device.py +8 -7
- vsslctrl/group.py +30 -4
- vsslctrl/io.py +13 -7
- vsslctrl/settings.py +13 -8
- {vsslctrl-0.1.9.dev1.dist-info → vsslctrl-0.1.10.dev1.dist-info}/METADATA +100 -37
- vsslctrl-0.1.10.dev1.dist-info/RECORD +23 -0
- {vsslctrl-0.1.9.dev1.dist-info → vsslctrl-0.1.10.dev1.dist-info}/WHEEL +1 -1
- vsslctrl-0.1.9.dev1.dist-info/RECORD +0 -23
- {vsslctrl-0.1.9.dev1.dist-info → vsslctrl-0.1.10.dev1.dist-info}/LICENSE +0 -0
- {vsslctrl-0.1.9.dev1.dist-info → vsslctrl-0.1.10.dev1.dist-info}/top_level.txt +0 -0
vsslctrl/__init__.py
CHANGED
vsslctrl/api_alpha.py
CHANGED
|
@@ -12,6 +12,7 @@ from .settings import EQSettings
|
|
|
12
12
|
from .utils import hex_to_int, clamp_volume, hex_to_bytearray_string
|
|
13
13
|
from .decorators import logging_helpers
|
|
14
14
|
from .data_structure import (
|
|
15
|
+
ZoneIDs,
|
|
15
16
|
ZoneStatusExtKeys,
|
|
16
17
|
ZoneEQStatusExtKeys,
|
|
17
18
|
ZoneRouterStatusExtKeys,
|
|
@@ -195,6 +196,17 @@ class APIAlpha(APIBase):
|
|
|
195
196
|
self._log_debug("Requesting status transport state")
|
|
196
197
|
self.send(bytearray([16, 7, 1, 0]))
|
|
197
198
|
|
|
199
|
+
#
|
|
200
|
+
# 0C [12]
|
|
201
|
+
# Party Mode
|
|
202
|
+
#
|
|
203
|
+
def request_action_0C(self, state: int):
|
|
204
|
+
self._log_debug(f"Requesting to set party memeber: {state}")
|
|
205
|
+
command = self._add_zone_id_to_request(
|
|
206
|
+
bytearray([16, 11, 2, 0, int(not not state)])
|
|
207
|
+
)
|
|
208
|
+
self.send(command)
|
|
209
|
+
|
|
198
210
|
#
|
|
199
211
|
# 0D [13]
|
|
200
212
|
# EQ
|
|
@@ -383,9 +395,17 @@ class APIAlpha(APIBase):
|
|
|
383
395
|
# Reboot All Zones
|
|
384
396
|
#
|
|
385
397
|
def request_action_33_device(self):
|
|
386
|
-
self._log_debug(
|
|
398
|
+
self._log_debug("Requesting to reboot device")
|
|
387
399
|
self.send(bytearray([16, 51, 2, 0, 1]))
|
|
388
400
|
|
|
401
|
+
#
|
|
402
|
+
# 2B [43]
|
|
403
|
+
# Factory Reset Device
|
|
404
|
+
#
|
|
405
|
+
def request_action_2B(self):
|
|
406
|
+
self._log_debug("Requesting to factory reset device")
|
|
407
|
+
self.send(bytearray([16, 43, 2, 8, 0]))
|
|
408
|
+
|
|
389
409
|
#
|
|
390
410
|
# 47 [71]
|
|
391
411
|
# Set Input Priority
|
|
@@ -481,6 +501,16 @@ class APIAlpha(APIBase):
|
|
|
481
501
|
command = bytearray([16, 79, 2, 8, int(state)])
|
|
482
502
|
self.send(command)
|
|
483
503
|
|
|
504
|
+
#
|
|
505
|
+
# 57 [87]
|
|
506
|
+
# Subwoofer Crossover
|
|
507
|
+
#
|
|
508
|
+
def request_action_57(self, freq: int):
|
|
509
|
+
self._log_debug(f"Requesting to set subwoofer crossover: {freq}")
|
|
510
|
+
# We hard code the zone ID to be 7 since this A.1(x)
|
|
511
|
+
command = bytearray([16, 87, 3, ZoneIDs.A1, 0, freq])
|
|
512
|
+
self.send(command)
|
|
513
|
+
|
|
484
514
|
#
|
|
485
515
|
#
|
|
486
516
|
#
|
|
@@ -581,6 +611,9 @@ class APIAlpha(APIBase):
|
|
|
581
611
|
# Not used?
|
|
582
612
|
|
|
583
613
|
# B2Nm - Bus2 Name - For A3.X this is the optical input name
|
|
614
|
+
# B1Nm - Bus1 Name - For A1 this is the Optical input
|
|
615
|
+
# B2Nm - Bus2 Name - For A1 this is the coax
|
|
616
|
+
|
|
584
617
|
if DeviceStatusExtKeys.OPTICAL_INPUT_NAME in metadata:
|
|
585
618
|
self.vssl.settings._set_property(
|
|
586
619
|
"optical_input_name",
|
|
@@ -662,9 +695,10 @@ class APIAlpha(APIBase):
|
|
|
662
695
|
self.zone._set_property("mute", bool(int(metadata[ZoneStatusExtKeys.MUTE])))
|
|
663
696
|
|
|
664
697
|
# Party Mode
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
698
|
+
if ZoneStatusExtKeys.PARTY_ZONE in metadata:
|
|
699
|
+
self.zone.group._set_property(
|
|
700
|
+
"is_party_zone_member", int(metadata[ZoneStatusExtKeys.PARTY_ZONE])
|
|
701
|
+
)
|
|
668
702
|
|
|
669
703
|
# Group Index see below
|
|
670
704
|
if ZoneStatusExtKeys.GROUP_INDEX in metadata:
|
|
@@ -929,11 +963,15 @@ class APIAlpha(APIBase):
|
|
|
929
963
|
self.zone.transport._set_property("state", state)
|
|
930
964
|
|
|
931
965
|
#
|
|
932
|
-
#
|
|
933
|
-
# Party Mode
|
|
966
|
+
# 0C [12]
|
|
967
|
+
# Party Mode
|
|
934
968
|
#
|
|
935
|
-
|
|
936
|
-
|
|
969
|
+
# Not supported on X series
|
|
970
|
+
#
|
|
971
|
+
def response_action_0C(self, hexl: list, response: bytes):
|
|
972
|
+
state = hex_to_int(hexl[4])
|
|
973
|
+
self._log_debug(f"Received party member state: {state}")
|
|
974
|
+
self.zone.group._set_property("is_party_zone_member", state)
|
|
937
975
|
|
|
938
976
|
#
|
|
939
977
|
# 0E [14]
|
|
@@ -943,7 +981,7 @@ class APIAlpha(APIBase):
|
|
|
943
981
|
if hex_to_int(hexl[2]) == 3:
|
|
944
982
|
freq = hex_to_int(hexl[4])
|
|
945
983
|
value = hex_to_int(hexl[5])
|
|
946
|
-
self._log_debug(f"Received EQ
|
|
984
|
+
self._log_debug(f"Received EQ frequency:{freq} value: {value}")
|
|
947
985
|
self.zone.settings.eq._set_eq_freq(freq, value)
|
|
948
986
|
|
|
949
987
|
#
|
|
@@ -1087,6 +1125,16 @@ class APIAlpha(APIBase):
|
|
|
1087
1125
|
self._log_debug(f"Received adaptive power setting: {enabled}")
|
|
1088
1126
|
self.vssl.settings.power._set_property("adaptive", bool(int(enabled)))
|
|
1089
1127
|
|
|
1128
|
+
#
|
|
1129
|
+
# 58 [88]
|
|
1130
|
+
# Subwoofer Crossover Feedback
|
|
1131
|
+
#
|
|
1132
|
+
def response_action_58(self, hexl: list, response: bytes):
|
|
1133
|
+
if hex_to_int(hexl[2]) == 3:
|
|
1134
|
+
freq = hex_to_int(hexl[5])
|
|
1135
|
+
self._log_debug(f"Received subwoofer crossover frequency: {freq}")
|
|
1136
|
+
self.zone.settings.subwoofer._set_property("crossover", freq)
|
|
1137
|
+
|
|
1090
1138
|
#
|
|
1091
1139
|
# Command confimation
|
|
1092
1140
|
#
|
vsslctrl/core.py
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import asyncio
|
|
5
5
|
from typing import Dict, Union, List
|
|
6
6
|
|
|
7
|
+
from . import VSSL_VERSION
|
|
7
8
|
from .zone import Zone
|
|
8
9
|
from .exceptions import VsslCtrlException, ZoneError, ZeroConfNotInstalled
|
|
9
10
|
from .event_bus import EventBus
|
|
@@ -93,6 +94,7 @@ class Vssl:
|
|
|
93
94
|
raise VsslCtrlException(message)
|
|
94
95
|
|
|
95
96
|
# Output a bit of helpful info
|
|
97
|
+
self._log_info(f"vsslctrl Version: {VSSL_VERSION}")
|
|
96
98
|
self._log_info(f"Device Model: {self.model.name}")
|
|
97
99
|
self._log_info(f"Device SW Version: {self.sw_version}")
|
|
98
100
|
self._log_info(f"Device Serial: {self.serial}")
|
|
@@ -196,6 +198,10 @@ class Vssl:
|
|
|
196
198
|
#
|
|
197
199
|
#
|
|
198
200
|
# THIS WONT WORK FOR ORIGINAL A SERIES! TODO!
|
|
201
|
+
# Maybe we can just use the ID of the zone because 7 will be an A1(x) otherwise
|
|
202
|
+
# we will need to count the zones.
|
|
203
|
+
#
|
|
204
|
+
# Or we just require a model to be passed?
|
|
199
205
|
#
|
|
200
206
|
#
|
|
201
207
|
def _infer_device_model(self, data: Dict[str, int]):
|
|
@@ -301,6 +307,14 @@ class Vssl:
|
|
|
301
307
|
if zone:
|
|
302
308
|
zone.api_alpha.request_action_33_device()
|
|
303
309
|
|
|
310
|
+
#
|
|
311
|
+
# Factory Reset Device
|
|
312
|
+
#
|
|
313
|
+
def factory_reset(self):
|
|
314
|
+
zone = self.get_connected_zone()
|
|
315
|
+
if zone:
|
|
316
|
+
zone.api_alpha.request_action_2B()
|
|
317
|
+
|
|
304
318
|
#
|
|
305
319
|
# Zones Groups. Build a dict of zone according to group membership
|
|
306
320
|
#
|
vsslctrl/data_structure.py
CHANGED
|
@@ -49,6 +49,16 @@ JSON Structure
|
|
|
49
49
|
|
|
50
50
|
DO NOT CHANGE - VSSL Defined
|
|
51
51
|
|
|
52
|
+
A.1
|
|
53
|
+
{
|
|
54
|
+
"B1Src":"3",
|
|
55
|
+
"B2Src":"3",
|
|
56
|
+
"B1Nm":"OPT",
|
|
57
|
+
"B2Nm":"COAX",
|
|
58
|
+
"dev":"VSSL A.1",
|
|
59
|
+
"ver":"p15265.033.3703"
|
|
60
|
+
}
|
|
61
|
+
|
|
52
62
|
A.3x
|
|
53
63
|
{
|
|
54
64
|
"B1Src": "3",
|
|
@@ -112,17 +122,15 @@ DO NOT CHANGE - VSSL Defined
|
|
|
112
122
|
IRMskL / IRMskH:
|
|
113
123
|
These could potentially be related to Infrared (IR) remote control signals. "IRMskL" and "IRMskH" might represent the low and high values of the modulation frequency or pulse width for an infrared signal.
|
|
114
124
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Drk: ?
|
|
125
|
-
|
|
125
|
+
A.1
|
|
126
|
+
{
|
|
127
|
+
'IRMskL': '255',
|
|
128
|
+
'IRMskH': '255',
|
|
129
|
+
'BTSta': '0',
|
|
130
|
+
'Crs': '0',
|
|
131
|
+
'Fes': '0',
|
|
132
|
+
'Drk': '0'
|
|
133
|
+
}
|
|
126
134
|
|
|
127
135
|
A.3x
|
|
128
136
|
{
|
|
@@ -161,7 +169,7 @@ class DeviceStatusExtendedExtKeys:
|
|
|
161
169
|
IR_HIGH = "IRMskH" # Guess - To Confirm
|
|
162
170
|
IR_LOW = "IRMskL" # Guess - To Confirm
|
|
163
171
|
BLUETOOTH_STATUS = "BTSta" # Guess - To Confirm
|
|
164
|
-
SUBWOOFER_CROSSOVER = "Crs"
|
|
172
|
+
SUBWOOFER_CROSSOVER = "Crs"
|
|
165
173
|
|
|
166
174
|
|
|
167
175
|
"""
|
|
@@ -169,6 +177,23 @@ JSON Structure
|
|
|
169
177
|
|
|
170
178
|
DO NOT CHANGE - VSSL Defined
|
|
171
179
|
|
|
180
|
+
A.1
|
|
181
|
+
{
|
|
182
|
+
'id': '7',
|
|
183
|
+
'ac': '1',
|
|
184
|
+
'mc': 'XXXXXXXXXXXX',
|
|
185
|
+
'vol': '20',
|
|
186
|
+
'mt': '0',
|
|
187
|
+
'pa': '0',
|
|
188
|
+
'rm': '0',
|
|
189
|
+
'ts': '0',
|
|
190
|
+
'lb': '17',
|
|
191
|
+
'tp': '1',
|
|
192
|
+
'wr': '0',
|
|
193
|
+
'as': '0',
|
|
194
|
+
'rg': '0'
|
|
195
|
+
}
|
|
196
|
+
|
|
172
197
|
A.3x
|
|
173
198
|
{
|
|
174
199
|
"id": "1",
|
|
@@ -234,7 +259,7 @@ class ZoneStatusExtKeys:
|
|
|
234
259
|
SERIAL_NUMBER = "mc"
|
|
235
260
|
VOLUME = "vol"
|
|
236
261
|
MUTE = "mt"
|
|
237
|
-
|
|
262
|
+
PARTY_ZONE = "pa"
|
|
238
263
|
GROUP_INDEX = "rm"
|
|
239
264
|
TRACK_SOURCE = "lb"
|
|
240
265
|
DISABLED = "wr"
|
|
@@ -245,10 +270,26 @@ JSON Structure
|
|
|
245
270
|
|
|
246
271
|
DO NOT CHANGE - VSSL Defined
|
|
247
272
|
|
|
273
|
+
A.1
|
|
274
|
+
{
|
|
275
|
+
"mono":"0",
|
|
276
|
+
"AiNm":"",
|
|
277
|
+
"eq1":"100",
|
|
278
|
+
"eq2":"97",
|
|
279
|
+
"eq3":"103",
|
|
280
|
+
"eq4":"103",
|
|
281
|
+
"eq5":"103",
|
|
282
|
+
"eq6":"99",
|
|
283
|
+
"eq7":"99",
|
|
284
|
+
"voll":"75",
|
|
285
|
+
"volr":"75",
|
|
286
|
+
"vold":"0"
|
|
287
|
+
}
|
|
288
|
+
|
|
248
289
|
A.3x
|
|
249
290
|
{
|
|
250
291
|
"mono": "0",
|
|
251
|
-
"AiNm": "Analog In
|
|
292
|
+
"AiNm": "Analog In",
|
|
252
293
|
"eq1": "100",
|
|
253
294
|
"eq2": "100",
|
|
254
295
|
"eq3": "100",
|
|
@@ -264,7 +305,7 @@ A.3x
|
|
|
264
305
|
A.3
|
|
265
306
|
{
|
|
266
307
|
'mono': '0',
|
|
267
|
-
'AiNm': '
|
|
308
|
+
'AiNm': 'Analog In',
|
|
268
309
|
'eq1': '106',
|
|
269
310
|
'eq2': '102',
|
|
270
311
|
'eq3': '100',
|
|
@@ -315,6 +356,22 @@ JSON Structure
|
|
|
315
356
|
|
|
316
357
|
DO NOT CHANGE - VSSL Defined
|
|
317
358
|
|
|
359
|
+
A.1
|
|
360
|
+
{
|
|
361
|
+
"ECO":"0",
|
|
362
|
+
"eqsw":"1",
|
|
363
|
+
"inSrc":"3",
|
|
364
|
+
"SP":"0",
|
|
365
|
+
"BF1":"0",
|
|
366
|
+
"BF2":"0",
|
|
367
|
+
"GRM":"0",
|
|
368
|
+
"GRS":"255",
|
|
369
|
+
"Pwr":"1",
|
|
370
|
+
"Bvr":"7",
|
|
371
|
+
"fxv":"20",
|
|
372
|
+
"AtPwr":"1"
|
|
373
|
+
}
|
|
374
|
+
|
|
318
375
|
A.3x
|
|
319
376
|
{
|
|
320
377
|
"ECO": "0",
|
|
@@ -374,8 +431,8 @@ class ZoneRouterStatusExtKeys:
|
|
|
374
431
|
EQ_ENABLED = "eqsw"
|
|
375
432
|
INPUT_SOURCE = "inSrc"
|
|
376
433
|
SOURCE_PRIORITY = "SP"
|
|
377
|
-
ANALOG_OUTPUT_1_FIXED_VOLUME = "BF1"
|
|
378
|
-
ANALOG_OUTPUT_2_FIXED_VOLUME = "BF2"
|
|
434
|
+
ANALOG_OUTPUT_1_FIXED_VOLUME = "BF1" # TODO On A3 / A6 is this Bus 1?
|
|
435
|
+
ANALOG_OUTPUT_2_FIXED_VOLUME = "BF2" # TODO On A6 is this Bus 2?
|
|
379
436
|
ANALOG_OUTPUT_3_FIXED_VOLUME = "BF3"
|
|
380
437
|
ANALOG_OUTPUT_4_FIXED_VOLUME = "BF4"
|
|
381
438
|
ANALOG_OUTPUT_5_FIXED_VOLUME = "BF5"
|
vsslctrl/device.py
CHANGED
|
@@ -9,7 +9,7 @@ A.1x:
|
|
|
9
9
|
SUB OUT
|
|
10
10
|
ANALOG IN 1
|
|
11
11
|
ANALOG OUT 1
|
|
12
|
-
COAXIAL IN
|
|
12
|
+
COAXIAL IN
|
|
13
13
|
COAXIAL OUT
|
|
14
14
|
OPTICAL IN
|
|
15
15
|
OPTICAL OUT
|
|
@@ -19,7 +19,7 @@ A1:
|
|
|
19
19
|
SUB OUT
|
|
20
20
|
ANALOG IN 1
|
|
21
21
|
ANALOG OUT 1
|
|
22
|
-
COAXIAL IN
|
|
22
|
+
COAXIAL IN
|
|
23
23
|
COAXIAL OUT
|
|
24
24
|
OPTICAL IN
|
|
25
25
|
OPTICAL OUT
|
|
@@ -77,6 +77,8 @@ ANALOG IN 6
|
|
|
77
77
|
|
|
78
78
|
---------------------------------
|
|
79
79
|
|
|
80
|
+
Following is from Source: https://vssl.gitbook.io/vssl-rest-api/zone-control/set-analog-input-source
|
|
81
|
+
|
|
80
82
|
Analog Input Source legend: input: inputs specific to model (e.g. A3 does not have zones 4-6 or bus input 2, only A3 has optical input, ...)
|
|
81
83
|
|
|
82
84
|
|
|
@@ -92,8 +94,7 @@ Analog Input Source legend: input: inputs specific to model (e.g. A3 does not ha
|
|
|
92
94
|
16 - optical input
|
|
93
95
|
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
TODO COAX Input and output!
|
|
97
98
|
|
|
98
99
|
"""
|
|
99
100
|
|
|
@@ -180,7 +181,7 @@ ANALOG_OUTPUT_SOURCES_FOR_A6 = list(AnalogOutput.Sources)
|
|
|
180
181
|
class Features(VsslIntEnum):
|
|
181
182
|
GROUPING = 1000
|
|
182
183
|
BLUETOOTH = 1001
|
|
183
|
-
|
|
184
|
+
PARTY_ZONE = 1002
|
|
184
185
|
SUBWOOFER_CROSSOVER = 1003
|
|
185
186
|
|
|
186
187
|
|
|
@@ -247,7 +248,7 @@ class Models(VsslEnum):
|
|
|
247
248
|
"zones": THREE_ZONES,
|
|
248
249
|
"input_sources": INPUT_SOURCES_FOR_A3,
|
|
249
250
|
"analog_output_sources": ANALOG_OUTPUT_SOURCES_FOR_A3,
|
|
250
|
-
"features": [Features.GROUPING, Features.
|
|
251
|
+
"features": [Features.GROUPING, Features.PARTY_ZONE],
|
|
251
252
|
}
|
|
252
253
|
)
|
|
253
254
|
A6 = Model(
|
|
@@ -256,7 +257,7 @@ class Models(VsslEnum):
|
|
|
256
257
|
"zones": SIX_ZONES,
|
|
257
258
|
"input_sources": INPUT_SOURCES_FOR_A6,
|
|
258
259
|
"analog_output_sources": ANALOG_OUTPUT_SOURCES_FOR_A6,
|
|
259
|
-
"features": [Features.GROUPING, Features.
|
|
260
|
+
"features": [Features.GROUPING, Features.PARTY_ZONE],
|
|
260
261
|
}
|
|
261
262
|
)
|
|
262
263
|
|
vsslctrl/group.py
CHANGED
|
@@ -32,8 +32,14 @@ class ZoneGroup(ZoneDataClass):
|
|
|
32
32
|
INDEX_CHANGE = PREFIX + "index_change"
|
|
33
33
|
SOURCE_CHANGE = PREFIX + "source_change"
|
|
34
34
|
IS_MASTER_CHANGE = PREFIX + "is_master_change"
|
|
35
|
+
IS_PARTY_ZONE_MEMBER_CHANGE = PREFIX + "is_party_zone_member_change"
|
|
35
36
|
|
|
36
|
-
DEFAULTS = {
|
|
37
|
+
DEFAULTS = {
|
|
38
|
+
"index": 0,
|
|
39
|
+
"source": None,
|
|
40
|
+
"is_master": False,
|
|
41
|
+
"is_party_zone_member": False,
|
|
42
|
+
}
|
|
37
43
|
|
|
38
44
|
def __init__(self, zone: "zone.Zone"):
|
|
39
45
|
self.zone = zone
|
|
@@ -52,9 +58,10 @@ class ZoneGroup(ZoneDataClass):
|
|
|
52
58
|
self._index_id = zone.id + 8
|
|
53
59
|
|
|
54
60
|
# index is assigned when a stream is started (see action_32)
|
|
55
|
-
self._index =
|
|
56
|
-
self._source =
|
|
57
|
-
self._is_master =
|
|
61
|
+
self._index = self.DEFAULTS["index"]
|
|
62
|
+
self._source = self.DEFAULTS["source"]
|
|
63
|
+
self._is_master = self.DEFAULTS["is_master"]
|
|
64
|
+
self._is_party_zone_member = self.DEFAULTS["is_party_zone_member"]
|
|
58
65
|
|
|
59
66
|
#
|
|
60
67
|
# Group Add Zone
|
|
@@ -191,3 +198,22 @@ class ZoneGroup(ZoneDataClass):
|
|
|
191
198
|
for zone in self.zone.vssl.zones.values()
|
|
192
199
|
if zone.group.index == self.index and zone.group.is_member
|
|
193
200
|
]
|
|
201
|
+
|
|
202
|
+
#
|
|
203
|
+
# Party Mode - Is this zone a party mode member
|
|
204
|
+
#
|
|
205
|
+
@property
|
|
206
|
+
def is_party_zone_member(self):
|
|
207
|
+
return bool(self._is_party_zone_member)
|
|
208
|
+
|
|
209
|
+
@is_party_zone_member.setter
|
|
210
|
+
def is_party_zone_member(self, state: int):
|
|
211
|
+
# Check this device supports party mode
|
|
212
|
+
if not self.zone.vssl.model.supports_feature(DeviceFeatures.PARTY_ZONE):
|
|
213
|
+
self.zone._log_error(f"VSSL {self.zone.vssl.model.name} doesnt party zone")
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
self.zone.api_alpha.request_action_0C(state)
|
|
217
|
+
|
|
218
|
+
def is_party_zone_member_toggle(self):
|
|
219
|
+
self.is_party_zone_member = False if self.is_party_zone_member else True
|
vsslctrl/io.py
CHANGED
|
@@ -25,8 +25,8 @@ class InputRouter(ZoneDataClass):
|
|
|
25
25
|
#
|
|
26
26
|
class Sources(VsslIntEnum):
|
|
27
27
|
STREAM = 0
|
|
28
|
-
BUS_IN_1 = 1
|
|
29
|
-
BUS_IN_2 = 2
|
|
28
|
+
BUS_IN_1 = 1
|
|
29
|
+
BUS_IN_2 = 2
|
|
30
30
|
ANALOG_IN_1 = 3
|
|
31
31
|
ANALOG_IN_2 = 4
|
|
32
32
|
ANALOG_IN_3 = 5
|
|
@@ -112,7 +112,13 @@ class AnalogOutput(ZoneDataClass):
|
|
|
112
112
|
Should this be on the VSSL or Zone? For now its on the zone, because the zone will
|
|
113
113
|
receive feedback for the corrosponding analog output id
|
|
114
114
|
|
|
115
|
-
Zones will be determined by source Input Priority
|
|
115
|
+
Zones will be determined by source Input Priority @see InputRouter class
|
|
116
|
+
|
|
117
|
+
TODO: Support for Bus 1 & 2 - Bus 1 and 2 outs can have fixed volume also.
|
|
118
|
+
Since the BUS zones dont have a dedicated zone, maybe we should move these
|
|
119
|
+
commands to the VSSL device control class instead of the zone class?
|
|
120
|
+
|
|
121
|
+
TODO: A1 input and output mappings!
|
|
116
122
|
"""
|
|
117
123
|
|
|
118
124
|
#
|
|
@@ -122,8 +128,8 @@ class AnalogOutput(ZoneDataClass):
|
|
|
122
128
|
#
|
|
123
129
|
class Sources(VsslIntEnum):
|
|
124
130
|
OFF = 0 # Disconnected / Off / No Output
|
|
125
|
-
BUS_IN_1 = 1 # TODO bus or party mode of original A series
|
|
126
|
-
BUS_IN_2 = 2 # TODO bus or party mode of original A series
|
|
131
|
+
BUS_IN_1 = 1 # TODO bus or party mode of original A series?
|
|
132
|
+
BUS_IN_2 = 2 # TODO bus or party mode of original A series?
|
|
127
133
|
ZONE_1 = 3
|
|
128
134
|
ZONE_2 = 4
|
|
129
135
|
ZONE_3 = 5
|
|
@@ -149,10 +155,10 @@ class AnalogOutput(ZoneDataClass):
|
|
|
149
155
|
self.zone = zone
|
|
150
156
|
|
|
151
157
|
self._is_fixed_volume = self.DEFAULTS["is_fixed_volume"]
|
|
152
|
-
self._source = self.
|
|
158
|
+
self._source = self.DEFAULTS["source"]
|
|
153
159
|
|
|
154
160
|
#
|
|
155
|
-
# Analog Output Fix Volume.
|
|
161
|
+
# Analog Output Fix Volume. i.e output wont respond to volume control
|
|
156
162
|
#
|
|
157
163
|
@property
|
|
158
164
|
def is_fixed_volume(self):
|
vsslctrl/settings.py
CHANGED
|
@@ -690,8 +690,8 @@ class EQSettings(ZoneDataClass):
|
|
|
690
690
|
|
|
691
691
|
# Not in io.py to prevent circ import in device.py
|
|
692
692
|
class SubwooferSettings(ZoneDataClass):
|
|
693
|
-
MIN_VALUE =
|
|
694
|
-
MAX_VALUE =
|
|
693
|
+
MIN_VALUE = 50
|
|
694
|
+
MAX_VALUE = 200
|
|
695
695
|
|
|
696
696
|
#
|
|
697
697
|
# Subwoofer events
|
|
@@ -703,6 +703,10 @@ class SubwooferSettings(ZoneDataClass):
|
|
|
703
703
|
#
|
|
704
704
|
# Defaults
|
|
705
705
|
#
|
|
706
|
+
# 0 is full range / off
|
|
707
|
+
#
|
|
708
|
+
# The crossover is adjustable from 50-200Hz.
|
|
709
|
+
#
|
|
706
710
|
DEFAULTS = {"crossover": 0}
|
|
707
711
|
|
|
708
712
|
def __init__(self, zone: "zone.Zone"):
|
|
@@ -713,7 +717,11 @@ class SubwooferSettings(ZoneDataClass):
|
|
|
713
717
|
#
|
|
714
718
|
# Clamp between min and max
|
|
715
719
|
#
|
|
716
|
-
def _clamp_crossover(self, value: int =
|
|
720
|
+
def _clamp_crossover(self, value: int = 0):
|
|
721
|
+
# Allow 0 to enable full range setting
|
|
722
|
+
if value < self.MIN_VALUE:
|
|
723
|
+
return 0
|
|
724
|
+
|
|
717
725
|
return int(max(self.MIN_VALUE, min(value, self.MAX_VALUE)))
|
|
718
726
|
|
|
719
727
|
#
|
|
@@ -732,11 +740,8 @@ class SubwooferSettings(ZoneDataClass):
|
|
|
732
740
|
f"VSSL {self.zone.vssl.model.name} does not have a subwoofer output"
|
|
733
741
|
)
|
|
734
742
|
return
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
#
|
|
738
|
-
self.zone._log_info(f"TODO - crossover set function")
|
|
739
|
-
# self.zone.api_alpha.request_action_crossover(freq)
|
|
743
|
+
|
|
744
|
+
self.zone.api_alpha.request_action_57(freq)
|
|
740
745
|
|
|
741
746
|
def _set_crossover(self, freq: int):
|
|
742
747
|
freq = self._clamp_crossover(freq)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vsslctrl
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10.dev1
|
|
4
4
|
Summary: Package for controlling VSSL's range of streaming amplifiers
|
|
5
5
|
Author-email: vsslctrl <vsslcontrolled@proton.me>
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -30,8 +30,10 @@ Requires-Dist: zeroconf ==0.132.2 ; extra == 'optional'
|
|
|
30
30
|
I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
|
|
31
31
|
|
|
32
32
|
Tested on:
|
|
33
|
-
-
|
|
34
|
-
-
|
|
33
|
+
- **A.1** software version **p15265.033.3703** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
34
|
+
- **A.3** software version **p12013.141.3703** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
35
|
+
- **A.3x** software version **p15305.016.3701**
|
|
36
|
+
- **A.6x** software version **p15305.017.3701** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
35
37
|
|
|
36
38
|
## Important
|
|
37
39
|
|
|
@@ -39,52 +41,79 @@ There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/ge
|
|
|
39
41
|
|
|
40
42
|
## TODOs
|
|
41
43
|
|
|
42
|
-
* **A.1(x)** specific control e.g
|
|
44
|
+
* **A.1(x)** specific control e.g bluetooth
|
|
43
45
|
* Better test coverage
|
|
44
46
|
|
|
45
47
|
## Basic Usage
|
|
46
48
|
|
|
47
49
|
`vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
|
|
48
50
|
|
|
51
|
+
### A.1 Example
|
|
52
|
+
|
|
49
53
|
```python
|
|
50
54
|
import asyncio
|
|
51
55
|
from vsslctrl import Vssl, DeviceModels, Zone, ZoneIDs
|
|
52
56
|
|
|
53
57
|
async def main():
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
|
|
59
|
+
# Represents a physical VSSL amplifier
|
|
60
|
+
vssl = Vssl(DeviceModels.A1)
|
|
61
|
+
a1 = vssl.add_zone(ZoneIDs.A1, '192.168.1.10')
|
|
62
|
+
|
|
63
|
+
# Connect and initiate zones.
|
|
64
|
+
await vssl.initialise()
|
|
65
|
+
|
|
66
|
+
"""Control Examples"""
|
|
67
|
+
# Print device name
|
|
68
|
+
print(a1.settings.name)
|
|
69
|
+
# Set volume to 25%
|
|
70
|
+
a1.volume = 25
|
|
71
|
+
# Pause
|
|
72
|
+
a1.pause()
|
|
73
|
+
# Print track name
|
|
74
|
+
print(a1.track.name)
|
|
75
|
+
|
|
76
|
+
# Shutdown and disconnect all zones
|
|
77
|
+
await vssl.shutdown()
|
|
57
78
|
|
|
58
|
-
|
|
79
|
+
asyncio.run(main())
|
|
80
|
+
```
|
|
59
81
|
|
|
60
|
-
|
|
82
|
+
### A.3x Example
|
|
61
83
|
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
```python
|
|
85
|
+
import asyncio
|
|
86
|
+
from vsslctrl import Vssl, DeviceModels, Zone, ZoneIDs
|
|
64
87
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
88
|
+
async def main():
|
|
89
|
+
|
|
90
|
+
# Represents a physical VSSL amplifier
|
|
91
|
+
# If no DeviceModels is passed, vsslctrl will default to the feature set of the X series amps
|
|
92
|
+
vssl = Vssl(DeviceModels.A3X)
|
|
70
93
|
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
# Add each you wish to control
|
|
95
|
+
zone1 = vssl.add_zone(ZoneIDs.ZONE_1, '192.168.1.10')
|
|
96
|
+
zone2 = vssl.add_zone(ZoneIDs.ZONE_2, '192.168.1.11')
|
|
97
|
+
zone3 = vssl.add_zone(ZoneIDs.ZONE_3, '192.168.1.12')
|
|
98
|
+
#... up to 6 zones
|
|
73
99
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
print(zone1.settings.name)
|
|
77
|
-
# Set zone2 volume to 25%
|
|
78
|
-
zone2.volume = 25
|
|
79
|
-
# Pause zone3
|
|
80
|
-
zone3.pause()
|
|
81
|
-
# or zone3.transport.pause()
|
|
82
|
-
# Print zone1 track name
|
|
83
|
-
print(zone1.track.name)
|
|
100
|
+
# Connect and initiate zones.
|
|
101
|
+
await vssl.initialise()
|
|
84
102
|
|
|
103
|
+
"""Control Examples"""
|
|
104
|
+
# Print zone1 name
|
|
105
|
+
print(zone1.settings.name)
|
|
106
|
+
# Set zone2 volume to 25%
|
|
107
|
+
zone2.volume = 25
|
|
108
|
+
# Pause zone3
|
|
109
|
+
zone3.pause()
|
|
110
|
+
# or zone3.transport.pause()
|
|
111
|
+
# Print zone1 track name
|
|
112
|
+
print(zone1.track.name)
|
|
85
113
|
|
|
86
|
-
|
|
87
|
-
|
|
114
|
+
|
|
115
|
+
# Shutdown and disconnect all zones
|
|
116
|
+
await vssl.shutdown()
|
|
88
117
|
|
|
89
118
|
|
|
90
119
|
asyncio.run(main())
|
|
@@ -107,7 +136,7 @@ print(zone_name)
|
|
|
107
136
|
>>> 'Living Room'
|
|
108
137
|
```
|
|
109
138
|
|
|
110
|
-
**Important** in the above example, `zone1.settings.name` wont be set to its new value until after the VSSL device has changed the name and the `Zone` class has received
|
|
139
|
+
**Important** in the above example, `zone1.settings.name` wont be set to its new value until after the VSSL device has changed the name and the `Zone` class has received confirmation feedback. If you need to wait for the value change, you can await a `[property_name]_CHANGE` events.
|
|
111
140
|
|
|
112
141
|
|
|
113
142
|
# `Vssl`
|
|
@@ -118,11 +147,15 @@ print(zone_name)
|
|
|
118
147
|
| `serial` | Serial number | `str` readonly
|
|
119
148
|
| `model` | Device Model | `int` readonly
|
|
120
149
|
| `reboot()` | Reboot all zones | `func` |
|
|
150
|
+
| `factory_reset()` | Factory reset device | `func` |
|
|
151
|
+
|
|
121
152
|
|
|
122
153
|
```python
|
|
123
154
|
"""Example"""
|
|
124
155
|
# Reboot all zones
|
|
125
156
|
vssl.reboot()
|
|
157
|
+
# Do a factory reset (reset all settings)
|
|
158
|
+
vssl.factory_reset()
|
|
126
159
|
```
|
|
127
160
|
|
|
128
161
|
## `Vssl.settings`
|
|
@@ -131,6 +164,8 @@ vssl.reboot()
|
|
|
131
164
|
| ---------------------- | ----------- | ----------- |
|
|
132
165
|
| `name` | Device name | `str`
|
|
133
166
|
| `optical_input_name` | Name of the optical input | `str`
|
|
167
|
+
| `bluetooth` | Bluetooth enabled / disabled | `bool`
|
|
168
|
+
| `bluetooth_toggle()` | Toggle Bluetooth | `func` |
|
|
134
169
|
|
|
135
170
|
```python
|
|
136
171
|
"""Example"""
|
|
@@ -138,6 +173,10 @@ vssl.reboot()
|
|
|
138
173
|
vssl.settings.name = 'My House'
|
|
139
174
|
# Setting optical input name
|
|
140
175
|
vssl.settings.optical_input_name = 'Optical Input 1'
|
|
176
|
+
# Enable Bluetooth
|
|
177
|
+
vssl.settings.bluetooth = True
|
|
178
|
+
# Toggle Bluetooth
|
|
179
|
+
vssl.settings.bluetooth_toggle()
|
|
141
180
|
```
|
|
142
181
|
|
|
143
182
|
## `Vssl.settings.power`
|
|
@@ -225,7 +264,7 @@ zone1.transport.state = ZoneTransport.States.PAUSE
|
|
|
225
264
|
## `Zone.track`
|
|
226
265
|
|
|
227
266
|
* Not all sources have complete metadata - missing value will be set to defaults.
|
|
228
|
-
* Airplay track `progress` is not
|
|
267
|
+
* Airplay track `progress` is not available.
|
|
229
268
|
|
|
230
269
|
| Property | Description | Type | Values |
|
|
231
270
|
| ---------------------- | ----------- | ----------- |----------- |
|
|
@@ -265,7 +304,7 @@ zone1.input.priority = InputRouter.Priorities.LOCAL
|
|
|
265
304
|
|
|
266
305
|
## `Zone.group`
|
|
267
306
|
|
|
268
|
-
|
|
307
|
+
Officially unsupported in X series amplifiers.
|
|
269
308
|
|
|
270
309
|
| Property | Description | Type | Values |
|
|
271
310
|
| ---------------------- | ----------- | ----------- |----------- |
|
|
@@ -275,6 +314,7 @@ Working on A.3x but offically unsupported in x series amplifiers.
|
|
|
275
314
|
| `remove_member()` | Remove zone from group | `func` | `ZoneIDs`
|
|
276
315
|
| `dissolve()` | Dissolve group / remove all members | `func` |
|
|
277
316
|
| `leave()` | Leave the group if a member | `func` |
|
|
317
|
+
| `is_party_zone_member` | Member of Party Zone | `bool` |
|
|
278
318
|
|
|
279
319
|
```python
|
|
280
320
|
"""Examples"""
|
|
@@ -285,6 +325,10 @@ zone2.group.leave() # or
|
|
|
285
325
|
zone1.group.remove_member(ZoneIDs.ZONE_2)
|
|
286
326
|
# If zone 1 is a master, remove all members
|
|
287
327
|
zone1.group.dissolve()
|
|
328
|
+
# Add zone to the party zone group
|
|
329
|
+
zone1.group.is_party_zone_member = True
|
|
330
|
+
# Toggle Party Zone Membership
|
|
331
|
+
zone1.group.is_party_zone_member_toggle()
|
|
288
332
|
```
|
|
289
333
|
|
|
290
334
|
## `Zone.analog_output`
|
|
@@ -384,6 +428,23 @@ EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `
|
|
|
384
428
|
zone1.settings.eq.khz1_db = -2
|
|
385
429
|
```
|
|
386
430
|
|
|
431
|
+
## `Zone.settings.subwoofer`
|
|
432
|
+
|
|
433
|
+
* **A.1 and A.1x only**
|
|
434
|
+
* Set `0` for full frequency range
|
|
435
|
+
|
|
436
|
+
| Property | Description | Type | Values | Default |
|
|
437
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
438
|
+
| `crossover` | Set "sub out" crossover frequency from 50-200Hz. | `int` | `0` or `50...200` | `0`
|
|
439
|
+
|
|
440
|
+
```python
|
|
441
|
+
"""Examples"""
|
|
442
|
+
# Set subwoofer ouput crossover to 100hz
|
|
443
|
+
zone1.settings.subwoofer.crossover = 100
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
|
|
387
448
|
## Another (Lite) Way
|
|
388
449
|
|
|
389
450
|
If you perfer to not run the complete intergration, you can send basic HEX commands to the VSSL device using [Netcat](https://nc110.sourceforge.io/) (or any network tool) on port `50002`.
|
|
@@ -433,9 +494,11 @@ shell_command:
|
|
|
433
494
|
|
|
434
495
|
## Credit
|
|
435
496
|
|
|
497
|
+
Thanks to [@dj-jam](https://github.com/dj-jam) for the continued testing.
|
|
498
|
+
|
|
436
499
|
The VSSL API was reverse engineered using Wireshark, VSSLs native "legacy" iOS app and their deprecated [vsslagent](https://vssl.gitbook.io/vssl-rest-api/getting-started/start).
|
|
437
500
|
|
|
438
|
-
|
|
501
|
+
Motivation for this project was to integrate VSSLs amplifiers into [Home Assistant](https://www.home-assistant.io/) and have control over different subnets (not mDNS dependant)
|
|
439
502
|
|
|
440
503
|
## Known Issues & Limitiations
|
|
441
504
|
|
|
@@ -443,15 +506,15 @@ Motovation for this project was to intergrate VSSLs amplifiers into [Home Assist
|
|
|
443
506
|
* VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
|
|
444
507
|
* Not all sources set the volume to 0 when the zone is muted
|
|
445
508
|
* Grouping feedback is flaky on the X series amplifiers
|
|
446
|
-
* Airplay `Zone.track.progress` is not
|
|
509
|
+
* Airplay `Zone.track.progress` is not available.
|
|
447
510
|
* Cant stop a URL playback, feedback is worng at least
|
|
448
511
|
* VSSL likes to cache old track metadata. For example when playing a URL after Spotify, often the device will respond with the previous (Spotify) tracks metadata
|
|
449
|
-
* `stop()` is intended to disconnect the client and pause the stream.
|
|
512
|
+
* `stop()` is intended to disconnect the client and pause the stream. Doesn’t always function this way, depending on stream source
|
|
450
513
|
* Occasionally a zones might stop responding to certain commands, issuing the `reboot` command generally corrects
|
|
451
514
|
|
|
452
515
|
## Future
|
|
453
516
|
|
|
454
|
-
* A.1(x) coverage i.e Bluetooth
|
|
517
|
+
* A.1(x) coverage i.e Bluetooth
|
|
455
518
|
* REST API / Web App
|
|
456
519
|
* Save and recall EQ
|
|
457
520
|
* IR Control
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
vsslctrl/__init__.py,sha256=Pi0MgplOWvu3EpicZcmY05ctjtsmj-Stvv9GQZVsJW0,174
|
|
2
|
+
vsslctrl/api_alpha.py,sha256=I3BcyphEHtW89vN363W7hiGHiPhHnQFO6krmxtMwGQc,35504
|
|
3
|
+
vsslctrl/api_base.py,sha256=a80CRtUkYK2OwXQOL8bP-YOvt4YE-ALd-VONza_gjK8,9623
|
|
4
|
+
vsslctrl/api_bravo.py,sha256=pchZN16KIvdSrqkI1WU9PA9IEmxkyyP-XifxpRC0jDE,12283
|
|
5
|
+
vsslctrl/core.py,sha256=uUNb8bU2JRZxDbzWgwrblXjKHYdeIWbuogSp9qQX3U8,9555
|
|
6
|
+
vsslctrl/data_structure.py,sha256=Doxyl5S6ecdAKk1Z4UO1BlCw2kAntUtU4RBVXhW9ZN0,10322
|
|
7
|
+
vsslctrl/decorators.py,sha256=lG8cYSSA-fNvdcp_90lv58tj50bF6iPoigLKcucWSSI,1707
|
|
8
|
+
vsslctrl/device.py,sha256=zJg51SiOv8IRyxHRXx7SrYXeyOGEeZY3id7NL3fTuTw,6070
|
|
9
|
+
vsslctrl/discovery.py,sha256=3z7z6YQUtvjFvRlQOZXIu6Wd4M896rMbi8SUkTub3AM,5568
|
|
10
|
+
vsslctrl/event_bus.py,sha256=9liAEJnZfthJwy5O-qc3pDIWRBv5AcV19PjMCwH_niU,5014
|
|
11
|
+
vsslctrl/exceptions.py,sha256=EEfNlu--9PMIidj3V3ZB8c7p3My8siqKw7YIOjPFaqE,419
|
|
12
|
+
vsslctrl/group.py,sha256=DJRD3pLha5tvpN_RTO8Zg311TaNWQ_O4p5bJjZ47aWE,6311
|
|
13
|
+
vsslctrl/io.py,sha256=bucsUQXUAlXx8D_s_Sa20eTamT_FiR1cKwMX4Yl4ySM,6902
|
|
14
|
+
vsslctrl/settings.py,sha256=_VtZLTYXdo2otyqs4iruCaOwclnoMafpsvPS1UJGXA4,19083
|
|
15
|
+
vsslctrl/track.py,sha256=_lQNHYe8ZBm3v1lehFLjZruF5-Rz8PGWuQN-zISBgwc,10136
|
|
16
|
+
vsslctrl/transport.py,sha256=XJFy0kl9y7hm3YV4_9ElBrq3u6--_k73UKJqKdO6Rqg,6993
|
|
17
|
+
vsslctrl/utils.py,sha256=FWOMHM_sl1Cp1xab-V6b-A5B7qRmDN67hMMxV4ReWCk,2087
|
|
18
|
+
vsslctrl/zone.py,sha256=teSk8oE3LKteidRnuVK3koUOqtN-90LclEx0-T4FhAE,12978
|
|
19
|
+
vsslctrl-0.1.10.dev1.dist-info/LICENSE,sha256=67Ekj8PIxjEvkPd7L3GYJ8dyvn1EC4sPM3q3FKtpN6w,1065
|
|
20
|
+
vsslctrl-0.1.10.dev1.dist-info/METADATA,sha256=VJzk7TXVa5INkaz-0oZyzYUlsQHNeSZmHu6Vf5Ds-b8,18817
|
|
21
|
+
vsslctrl-0.1.10.dev1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
22
|
+
vsslctrl-0.1.10.dev1.dist-info/top_level.txt,sha256=MRycO_RKageNX27UZ2bLJDMlfeWTQzUZlsd3hS-0u3Y,9
|
|
23
|
+
vsslctrl-0.1.10.dev1.dist-info/RECORD,,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
vsslctrl/__init__.py,sha256=W7cljoE7L3DukAVr_TEqkxFycYrUbJiqaHrOlbPkmKU,145
|
|
2
|
-
vsslctrl/api_alpha.py,sha256=UxsMKOFK4Uit3QoF96Wa3VE1WdbCq7FOJjt-3fewAe0,33962
|
|
3
|
-
vsslctrl/api_base.py,sha256=a80CRtUkYK2OwXQOL8bP-YOvt4YE-ALd-VONza_gjK8,9623
|
|
4
|
-
vsslctrl/api_bravo.py,sha256=pchZN16KIvdSrqkI1WU9PA9IEmxkyyP-XifxpRC0jDE,12283
|
|
5
|
-
vsslctrl/core.py,sha256=G6XSH2_sxVaB6AfR6Btt8SMqiihlBQ6n_UZB9lsO7fw,9114
|
|
6
|
-
vsslctrl/data_structure.py,sha256=i6xG72kQqFDSoEhI307Iw9QXTquMNMc4qMUGOK0MEbw,9793
|
|
7
|
-
vsslctrl/decorators.py,sha256=lG8cYSSA-fNvdcp_90lv58tj50bF6iPoigLKcucWSSI,1707
|
|
8
|
-
vsslctrl/device.py,sha256=M6fcd2ggE-ConHoENpQ3WtGQzmSyok9t-8Kt5pd-r6U,6104
|
|
9
|
-
vsslctrl/discovery.py,sha256=3z7z6YQUtvjFvRlQOZXIu6Wd4M896rMbi8SUkTub3AM,5568
|
|
10
|
-
vsslctrl/event_bus.py,sha256=9liAEJnZfthJwy5O-qc3pDIWRBv5AcV19PjMCwH_niU,5014
|
|
11
|
-
vsslctrl/exceptions.py,sha256=EEfNlu--9PMIidj3V3ZB8c7p3My8siqKw7YIOjPFaqE,419
|
|
12
|
-
vsslctrl/group.py,sha256=B7lmJb6DIwXGnMyTKsp-JfRWVCVRFQYzlvS71tutfx4,5368
|
|
13
|
-
vsslctrl/io.py,sha256=RZCLxa0RAnyhRdDTM7IFpLcpYeQfV90DzVEJdNjR6lQ,6684
|
|
14
|
-
vsslctrl/settings.py,sha256=ZUa_ixohGzowj6mhGlCcVAr_ZfsbpU_N5jK-7pEA5Jc,18997
|
|
15
|
-
vsslctrl/track.py,sha256=_lQNHYe8ZBm3v1lehFLjZruF5-Rz8PGWuQN-zISBgwc,10136
|
|
16
|
-
vsslctrl/transport.py,sha256=XJFy0kl9y7hm3YV4_9ElBrq3u6--_k73UKJqKdO6Rqg,6993
|
|
17
|
-
vsslctrl/utils.py,sha256=FWOMHM_sl1Cp1xab-V6b-A5B7qRmDN67hMMxV4ReWCk,2087
|
|
18
|
-
vsslctrl/zone.py,sha256=teSk8oE3LKteidRnuVK3koUOqtN-90LclEx0-T4FhAE,12978
|
|
19
|
-
vsslctrl-0.1.9.dev1.dist-info/LICENSE,sha256=67Ekj8PIxjEvkPd7L3GYJ8dyvn1EC4sPM3q3FKtpN6w,1065
|
|
20
|
-
vsslctrl-0.1.9.dev1.dist-info/METADATA,sha256=fKQI1brfCRDqoXlXfPNB_22heY6oQC-fxjADf02JYro,16990
|
|
21
|
-
vsslctrl-0.1.9.dev1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
22
|
-
vsslctrl-0.1.9.dev1.dist-info/top_level.txt,sha256=MRycO_RKageNX27UZ2bLJDMlfeWTQzUZlsd3hS-0u3Y,9
|
|
23
|
-
vsslctrl-0.1.9.dev1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|