vsslctrl 0.1.9.dev1__tar.gz → 0.1.11.dev1__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.
Files changed (32) hide show
  1. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/PKG-INFO +127 -49
  2. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/README.md +126 -48
  3. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/pyproject.toml +1 -1
  4. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/tests/test_integration.py +119 -98
  5. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/__init__.py +3 -2
  6. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/api_alpha.py +387 -233
  7. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/core.py +14 -0
  8. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/data_structure.py +133 -31
  9. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/decorators.py +54 -0
  10. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/device.py +75 -19
  11. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/group.py +32 -7
  12. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/io.py +95 -24
  13. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/settings.py +49 -23
  14. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl.egg-info/PKG-INFO +127 -49
  15. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/LICENSE +0 -0
  16. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/setup.cfg +0 -0
  17. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/tests/test_event_bus.py +0 -0
  18. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/tests/test_settings.py +0 -0
  19. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/tests/test_zone.py +0 -0
  20. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/api_base.py +0 -0
  21. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/api_bravo.py +0 -0
  22. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/discovery.py +0 -0
  23. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/event_bus.py +0 -0
  24. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/exceptions.py +0 -0
  25. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/track.py +0 -0
  26. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/transport.py +0 -0
  27. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/utils.py +0 -0
  28. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl/zone.py +0 -0
  29. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl.egg-info/SOURCES.txt +0 -0
  30. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl.egg-info/dependency_links.txt +0 -0
  31. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl.egg-info/requires.txt +0 -0
  32. {vsslctrl-0.1.9.dev1 → vsslctrl-0.1.11.dev1}/vsslctrl.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vsslctrl
3
- Version: 0.1.9.dev1
3
+ Version: 0.1.11.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,13 @@ 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
- - Test suite run on a VSSL **A.3x** software version **p15305.016.3701**
34
- - The Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) is reported working on a **A.6x** software version **p15305.017.3701**
33
+ - **A.3x** software version **p15305.016.3701**
34
+
35
+ Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) with basic functionality working on:
36
+ - **A.1** software version **p15265.033.3703** (reported)
37
+ - **A.3** software version **p12013.141.3703** (reported)
38
+ - **A.3x** software version **p15305.016.3701**
39
+ - **A.6x** software version **p15305.017.3701** (reported)
35
40
 
36
41
  ## Important
37
42
 
@@ -39,52 +44,80 @@ There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/ge
39
44
 
40
45
  ## TODOs
41
46
 
42
- * **A.1(x)** specific control e.g sub crossover, bluetooth
47
+ * Correct IO mapping between models and versions
43
48
  * Better test coverage
49
+ * A.1(x) testing
44
50
 
45
51
  ## Basic Usage
46
52
 
47
53
  `vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
48
54
 
55
+ ### A.1 Example
56
+
49
57
  ```python
50
58
  import asyncio
51
59
  from vsslctrl import Vssl, DeviceModels, Zone, ZoneIDs
52
60
 
53
61
  async def main():
54
-
55
- # Represents a physical VSSL amplifier
56
- vssl = Vssl()
62
+
63
+ # Represents a physical VSSL amplifier
64
+ vssl = Vssl(DeviceModels.A1)
65
+ a1 = vssl.add_zone(ZoneIDs.A1, '192.168.1.10')
66
+
67
+ # Connect and initiate zones.
68
+ await vssl.initialise()
69
+
70
+ """Control Examples"""
71
+ # Print device name
72
+ print(a1.settings.name)
73
+ # Set volume to 25%
74
+ a1.volume = 25
75
+ # Pause
76
+ a1.pause()
77
+ # Print track name
78
+ print(a1.track.name)
79
+
80
+ # Shutdown and disconnect all zones
81
+ await vssl.shutdown()
57
82
 
58
- """Optional to init Vssl with a device model
83
+ asyncio.run(main())
84
+ ```
59
85
 
60
- vssl = Vssl(DeviceModels.A3X)
86
+ ### A.3x Example
61
87
 
62
- If no DeviceModels is passed, vsslctrl will default to the feature set of the X series amps
63
- """
88
+ ```python
89
+ import asyncio
90
+ from vsslctrl import Vssl, DeviceModels, Zone, ZoneIDs
91
+
92
+ async def main():
93
+
94
+ # Represents a physical VSSL amplifier
95
+ # If no DeviceModels is passed, vsslctrl will default to the feature set of the X series amps
96
+ vssl = Vssl(DeviceModels.A3X)
64
97
 
65
- # Add each you wish to control
66
- zone1 = vssl.add_zone(ZoneIDs.ZONE_1, '192.168.1.10')
67
- zone2 = vssl.add_zone(ZoneIDs.ZONE_2, '192.168.1.11')
68
- zone3 = vssl.add_zone(ZoneIDs.ZONE_3, '192.168.1.12')
69
- #... up to 6 zones
98
+ # Add each you wish to control
99
+ zone1 = vssl.add_zone(ZoneIDs.ZONE_1, '192.168.1.10')
100
+ zone2 = vssl.add_zone(ZoneIDs.ZONE_2, '192.168.1.11')
101
+ zone3 = vssl.add_zone(ZoneIDs.ZONE_3, '192.168.1.12')
102
+ #... up to 6 zones
70
103
 
71
- # Connect and initiate zones.
72
- await vssl.initialise()
104
+ # Connect and initiate zones.
105
+ await vssl.initialise()
73
106
 
74
- """Control Examples"""
75
- # Print zone1 name
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)
107
+ """Control Examples"""
108
+ # Print zone1 name
109
+ print(zone1.settings.name)
110
+ # Set zone2 volume to 25%
111
+ zone2.volume = 25
112
+ # Pause zone3
113
+ zone3.pause()
114
+ # or zone3.transport.pause()
115
+ # Print zone1 track name
116
+ print(zone1.track.name)
84
117
 
85
118
 
86
- # Shutdown and disconnect all zones
87
- await vssl.shutdown()
119
+ # Shutdown and disconnect all zones
120
+ await vssl.shutdown()
88
121
 
89
122
 
90
123
  asyncio.run(main())
@@ -107,7 +140,7 @@ print(zone_name)
107
140
  >>> 'Living Room'
108
141
  ```
109
142
 
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 confimation feedback. If you need to wait for the value change, you can await a `[property_name]_CHANGE` events.
143
+ **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
144
 
112
145
 
113
146
  # `Vssl`
@@ -118,26 +151,37 @@ print(zone_name)
118
151
  | `serial` | Serial number | `str` readonly
119
152
  | `model` | Device Model | `int` readonly
120
153
  | `reboot()` | Reboot all zones | `func` |
154
+ | `factory_reset()` | Factory reset device | `func` |
155
+
121
156
 
122
157
  ```python
123
158
  """Example"""
124
159
  # Reboot all zones
125
160
  vssl.reboot()
161
+ # Do a factory reset (reset all settings)
162
+ vssl.factory_reset()
126
163
  ```
127
164
 
128
165
  ## `Vssl.settings`
129
166
 
130
- | Property | Description | Type |
131
- | ---------------------- | ----------- | ----------- |
132
- | `name` | Device name | `str`
133
- | `optical_input_name` | Name of the optical input | `str`
167
+ | Property | Description | Type | Notes / Defaults |
168
+ | ---------------------- | ----------- | ----------- | ----------- |
169
+ | `name` | Device name | `str` |
170
+ | `bus_1_name` | Name of Bus 1 | `str` | <ul><li>A.1: Optical Input</li><li>A.3x/6x: Not Used</li></ul>
171
+ | `bus_2_name` | Name of Bus 2 | `str` | <ul><li>A.1: Coax Input</li><li>A.3x/6x: Optical Input</li></ul>
172
+ | `bluetooth` | Bluetooth enabled / disabled | `bool` |
173
+ | `bluetooth_toggle()` | Toggle Bluetooth | `func` |
134
174
 
135
175
  ```python
136
176
  """Example"""
137
177
  # Setting device name
138
178
  vssl.settings.name = 'My House'
139
- # Setting optical input name
140
- vssl.settings.optical_input_name = 'Optical Input 1'
179
+ # Setting bus 2 name
180
+ vssl.settings.bus_2_name = 'Optical Input'
181
+ # Enable Bluetooth
182
+ vssl.settings.bluetooth = True
183
+ # Toggle Bluetooth
184
+ vssl.settings.bluetooth_toggle()
141
185
  ```
142
186
 
143
187
  ## `Vssl.settings.power`
@@ -225,7 +269,7 @@ zone1.transport.state = ZoneTransport.States.PAUSE
225
269
  ## `Zone.track`
226
270
 
227
271
  * Not all sources have complete metadata - missing value will be set to defaults.
228
- * Airplay track `progress` is not avaiable.
272
+ * Airplay track `progress` is not available.
229
273
 
230
274
  | Property | Description | Type | Values |
231
275
  | ---------------------- | ----------- | ----------- |----------- |
@@ -244,15 +288,25 @@ zone1.transport.state = ZoneTransport.States.PAUSE
244
288
 
245
289
  | Property | Description | Type | Values |
246
290
  | ---------------------- | ----------- | ----------- |----------- |
247
- | `source` | Change input source | `int` | `InputRouter.Sources`
248
- | `priority` | Change input priority. Stream or analog in higher priority | `int` | `InputRouter.Priorities`
291
+ | `source` | Change input source.<br/>Source to be played out the zones speakers | `int` | `InputRouter.Sources`
292
+ | `priority` | Change input priority.<br/>Stream or local to have precedence | `int` | `InputRouter.Priorities`
293
+
249
294
 
250
- ### Priority Order:
295
+ ### A.1(x) Source Routing Order
296
+ A.1 and A.1x don't support manually changing the input source. Instead a fixed source routing order is used:
297
+
298
+ 1. Optical Input
299
+ 2. Coaxial Input
300
+ 3. Analog Input
301
+
302
+ Input `InputRouter.Priorities` still apply.
303
+
304
+ ### Input Priority / Precedence
251
305
 
252
306
  | `InputRouter.Priorities` | Priority Order |
253
307
  | ---------------------- | ----------- |
254
- | **`STREAM`** | <ol><li>Stream</li><li>Party Zone</li><li>Bus 1 In</li><li>Bus 2 In</li><li>Analog Input</li></ol>
255
- | **`LOCAL`** | <ol><li>Bus 1 In</li><li>Bus 2 In</li><li>Analog Input</li><li>Stream</li><li>Party Zone</li></ol>
308
+ | **`STREAM`** | <ol><li>Stream</li><li>Party Zone</li><li>Bus 1</li><li>Bus 2</li><li>Optical</li><li>Coaxial</li><li>Analog</li></ol>
309
+ | **`LOCAL`** | <ol><li>Bus 1</li><li>Bus 2</li><li>Optical</li><li>Coaxial</li><li>Analog</li><li>Stream</li><li>Party Zone</li></ol>
256
310
 
257
311
  ```python
258
312
  """Example"""
@@ -265,7 +319,7 @@ zone1.input.priority = InputRouter.Priorities.LOCAL
265
319
 
266
320
  ## `Zone.group`
267
321
 
268
- Working on A.3x but offically unsupported in x series amplifiers.
322
+ Officially unsupported in X series amplifiers.
269
323
 
270
324
  | Property | Description | Type | Values |
271
325
  | ---------------------- | ----------- | ----------- |----------- |
@@ -275,6 +329,7 @@ Working on A.3x but offically unsupported in x series amplifiers.
275
329
  | `remove_member()` | Remove zone from group | `func` | `ZoneIDs`
276
330
  | `dissolve()` | Dissolve group / remove all members | `func` |
277
331
  | `leave()` | Leave the group if a member | `func` |
332
+ | `is_party_zone_member` | Member of Party Zone | `bool` |
278
333
 
279
334
  ```python
280
335
  """Examples"""
@@ -285,13 +340,17 @@ zone2.group.leave() # or
285
340
  zone1.group.remove_member(ZoneIDs.ZONE_2)
286
341
  # If zone 1 is a master, remove all members
287
342
  zone1.group.dissolve()
343
+ # Add zone to the party zone group
344
+ zone1.group.is_party_zone_member = True
345
+ # Toggle Party Zone Membership
346
+ zone1.group.is_party_zone_member_toggle()
288
347
  ```
289
348
 
290
349
  ## `Zone.analog_output`
291
350
 
292
351
  | Property | Description | Type | Values | Default |
293
352
  | ---------------------- | ----------- | ----------- |----------- |----------- |
294
- | `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
353
+ | `source` | Where the AO is routed from. i.e stream, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
295
354
  | `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` | |`False`
296
355
  | `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
297
356
 
@@ -384,6 +443,23 @@ EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `
384
443
  zone1.settings.eq.khz1_db = -2
385
444
  ```
386
445
 
446
+ ## `Zone.settings.subwoofer`
447
+
448
+ * **A.1 and A.1x only**
449
+ * Set `0` for full frequency range
450
+
451
+ | Property | Description | Type | Values | Default |
452
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
453
+ | `crossover` | Set "sub out" crossover frequency from 50-200Hz. | `int` | `0` or `50...200` | `0`
454
+
455
+ ```python
456
+ """Examples"""
457
+ # Set subwoofer ouput crossover to 100hz
458
+ zone1.settings.subwoofer.crossover = 100
459
+ ```
460
+
461
+
462
+
387
463
  ## Another (Lite) Way
388
464
 
389
465
  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 +509,11 @@ shell_command:
433
509
 
434
510
  ## Credit
435
511
 
512
+ Thanks to [@dj-jam](https://github.com/dj-jam) for the continued testing.
513
+
436
514
  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
515
 
438
- Motovation for this project was to intergrate VSSLs amplifiers into [Home Assistant](https://www.home-assistant.io/) and have control over different subnets (not mDNS dependant)
516
+ 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
517
 
440
518
  ## Known Issues & Limitiations
441
519
 
@@ -443,15 +521,15 @@ Motovation for this project was to intergrate VSSLs amplifiers into [Home Assist
443
521
  * VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
444
522
  * Not all sources set the volume to 0 when the zone is muted
445
523
  * Grouping feedback is flaky on the X series amplifiers
446
- * Airplay `Zone.track.progress` is not avaiable.
524
+ * Airplay `Zone.track.progress` is not available.
447
525
  * Cant stop a URL playback, feedback is worng at least
448
526
  * 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. Doesnt always function this way, depending on stream source
527
+ * `stop()` is intended to disconnect the client and pause the stream. Doesn’t always function this way, depending on stream source
450
528
  * Occasionally a zones might stop responding to certain commands, issuing the `reboot` command generally corrects
451
529
 
452
530
  ## Future
453
531
 
454
- * A.1(x) coverage i.e Bluetooth and subwoofer control
532
+ * A.1(x) coverage i.e Bluetooth
455
533
  * REST API / Web App
456
534
  * Save and recall EQ
457
535
  * IR Control
@@ -9,8 +9,13 @@
9
9
  I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
10
10
 
11
11
  Tested on:
12
- - Test suite run on a VSSL **A.3x** software version **p15305.016.3701**
13
- - The Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) is reported working on a **A.6x** software version **p15305.017.3701**
12
+ - **A.3x** software version **p15305.016.3701**
13
+
14
+ Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) with basic functionality working on:
15
+ - **A.1** software version **p15265.033.3703** (reported)
16
+ - **A.3** software version **p12013.141.3703** (reported)
17
+ - **A.3x** software version **p15305.016.3701**
18
+ - **A.6x** software version **p15305.017.3701** (reported)
14
19
 
15
20
  ## Important
16
21
 
@@ -18,52 +23,80 @@ There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/ge
18
23
 
19
24
  ## TODOs
20
25
 
21
- * **A.1(x)** specific control e.g sub crossover, bluetooth
26
+ * Correct IO mapping between models and versions
22
27
  * Better test coverage
28
+ * A.1(x) testing
23
29
 
24
30
  ## Basic Usage
25
31
 
26
32
  `vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
27
33
 
34
+ ### A.1 Example
35
+
28
36
  ```python
29
37
  import asyncio
30
38
  from vsslctrl import Vssl, DeviceModels, Zone, ZoneIDs
31
39
 
32
40
  async def main():
33
-
34
- # Represents a physical VSSL amplifier
35
- vssl = Vssl()
41
+
42
+ # Represents a physical VSSL amplifier
43
+ vssl = Vssl(DeviceModels.A1)
44
+ a1 = vssl.add_zone(ZoneIDs.A1, '192.168.1.10')
45
+
46
+ # Connect and initiate zones.
47
+ await vssl.initialise()
48
+
49
+ """Control Examples"""
50
+ # Print device name
51
+ print(a1.settings.name)
52
+ # Set volume to 25%
53
+ a1.volume = 25
54
+ # Pause
55
+ a1.pause()
56
+ # Print track name
57
+ print(a1.track.name)
58
+
59
+ # Shutdown and disconnect all zones
60
+ await vssl.shutdown()
36
61
 
37
- """Optional to init Vssl with a device model
62
+ asyncio.run(main())
63
+ ```
38
64
 
39
- vssl = Vssl(DeviceModels.A3X)
65
+ ### A.3x Example
40
66
 
41
- If no DeviceModels is passed, vsslctrl will default to the feature set of the X series amps
42
- """
67
+ ```python
68
+ import asyncio
69
+ from vsslctrl import Vssl, DeviceModels, Zone, ZoneIDs
70
+
71
+ async def main():
72
+
73
+ # Represents a physical VSSL amplifier
74
+ # If no DeviceModels is passed, vsslctrl will default to the feature set of the X series amps
75
+ vssl = Vssl(DeviceModels.A3X)
43
76
 
44
- # Add each you wish to control
45
- zone1 = vssl.add_zone(ZoneIDs.ZONE_1, '192.168.1.10')
46
- zone2 = vssl.add_zone(ZoneIDs.ZONE_2, '192.168.1.11')
47
- zone3 = vssl.add_zone(ZoneIDs.ZONE_3, '192.168.1.12')
48
- #... up to 6 zones
77
+ # Add each you wish to control
78
+ zone1 = vssl.add_zone(ZoneIDs.ZONE_1, '192.168.1.10')
79
+ zone2 = vssl.add_zone(ZoneIDs.ZONE_2, '192.168.1.11')
80
+ zone3 = vssl.add_zone(ZoneIDs.ZONE_3, '192.168.1.12')
81
+ #... up to 6 zones
49
82
 
50
- # Connect and initiate zones.
51
- await vssl.initialise()
83
+ # Connect and initiate zones.
84
+ await vssl.initialise()
52
85
 
53
- """Control Examples"""
54
- # Print zone1 name
55
- print(zone1.settings.name)
56
- # Set zone2 volume to 25%
57
- zone2.volume = 25
58
- # Pause zone3
59
- zone3.pause()
60
- # or zone3.transport.pause()
61
- # Print zone1 track name
62
- print(zone1.track.name)
86
+ """Control Examples"""
87
+ # Print zone1 name
88
+ print(zone1.settings.name)
89
+ # Set zone2 volume to 25%
90
+ zone2.volume = 25
91
+ # Pause zone3
92
+ zone3.pause()
93
+ # or zone3.transport.pause()
94
+ # Print zone1 track name
95
+ print(zone1.track.name)
63
96
 
64
97
 
65
- # Shutdown and disconnect all zones
66
- await vssl.shutdown()
98
+ # Shutdown and disconnect all zones
99
+ await vssl.shutdown()
67
100
 
68
101
 
69
102
  asyncio.run(main())
@@ -86,7 +119,7 @@ print(zone_name)
86
119
  >>> 'Living Room'
87
120
  ```
88
121
 
89
- **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 confimation feedback. If you need to wait for the value change, you can await a `[property_name]_CHANGE` events.
122
+ **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.
90
123
 
91
124
 
92
125
  # `Vssl`
@@ -97,26 +130,37 @@ print(zone_name)
97
130
  | `serial` | Serial number | `str` readonly
98
131
  | `model` | Device Model | `int` readonly
99
132
  | `reboot()` | Reboot all zones | `func` |
133
+ | `factory_reset()` | Factory reset device | `func` |
134
+
100
135
 
101
136
  ```python
102
137
  """Example"""
103
138
  # Reboot all zones
104
139
  vssl.reboot()
140
+ # Do a factory reset (reset all settings)
141
+ vssl.factory_reset()
105
142
  ```
106
143
 
107
144
  ## `Vssl.settings`
108
145
 
109
- | Property | Description | Type |
110
- | ---------------------- | ----------- | ----------- |
111
- | `name` | Device name | `str`
112
- | `optical_input_name` | Name of the optical input | `str`
146
+ | Property | Description | Type | Notes / Defaults |
147
+ | ---------------------- | ----------- | ----------- | ----------- |
148
+ | `name` | Device name | `str` |
149
+ | `bus_1_name` | Name of Bus 1 | `str` | <ul><li>A.1: Optical Input</li><li>A.3x/6x: Not Used</li></ul>
150
+ | `bus_2_name` | Name of Bus 2 | `str` | <ul><li>A.1: Coax Input</li><li>A.3x/6x: Optical Input</li></ul>
151
+ | `bluetooth` | Bluetooth enabled / disabled | `bool` |
152
+ | `bluetooth_toggle()` | Toggle Bluetooth | `func` |
113
153
 
114
154
  ```python
115
155
  """Example"""
116
156
  # Setting device name
117
157
  vssl.settings.name = 'My House'
118
- # Setting optical input name
119
- vssl.settings.optical_input_name = 'Optical Input 1'
158
+ # Setting bus 2 name
159
+ vssl.settings.bus_2_name = 'Optical Input'
160
+ # Enable Bluetooth
161
+ vssl.settings.bluetooth = True
162
+ # Toggle Bluetooth
163
+ vssl.settings.bluetooth_toggle()
120
164
  ```
121
165
 
122
166
  ## `Vssl.settings.power`
@@ -204,7 +248,7 @@ zone1.transport.state = ZoneTransport.States.PAUSE
204
248
  ## `Zone.track`
205
249
 
206
250
  * Not all sources have complete metadata - missing value will be set to defaults.
207
- * Airplay track `progress` is not avaiable.
251
+ * Airplay track `progress` is not available.
208
252
 
209
253
  | Property | Description | Type | Values |
210
254
  | ---------------------- | ----------- | ----------- |----------- |
@@ -223,15 +267,25 @@ zone1.transport.state = ZoneTransport.States.PAUSE
223
267
 
224
268
  | Property | Description | Type | Values |
225
269
  | ---------------------- | ----------- | ----------- |----------- |
226
- | `source` | Change input source | `int` | `InputRouter.Sources`
227
- | `priority` | Change input priority. Stream or analog in higher priority | `int` | `InputRouter.Priorities`
270
+ | `source` | Change input source.<br/>Source to be played out the zones speakers | `int` | `InputRouter.Sources`
271
+ | `priority` | Change input priority.<br/>Stream or local to have precedence | `int` | `InputRouter.Priorities`
272
+
228
273
 
229
- ### Priority Order:
274
+ ### A.1(x) Source Routing Order
275
+ A.1 and A.1x don't support manually changing the input source. Instead a fixed source routing order is used:
276
+
277
+ 1. Optical Input
278
+ 2. Coaxial Input
279
+ 3. Analog Input
280
+
281
+ Input `InputRouter.Priorities` still apply.
282
+
283
+ ### Input Priority / Precedence
230
284
 
231
285
  | `InputRouter.Priorities` | Priority Order |
232
286
  | ---------------------- | ----------- |
233
- | **`STREAM`** | <ol><li>Stream</li><li>Party Zone</li><li>Bus 1 In</li><li>Bus 2 In</li><li>Analog Input</li></ol>
234
- | **`LOCAL`** | <ol><li>Bus 1 In</li><li>Bus 2 In</li><li>Analog Input</li><li>Stream</li><li>Party Zone</li></ol>
287
+ | **`STREAM`** | <ol><li>Stream</li><li>Party Zone</li><li>Bus 1</li><li>Bus 2</li><li>Optical</li><li>Coaxial</li><li>Analog</li></ol>
288
+ | **`LOCAL`** | <ol><li>Bus 1</li><li>Bus 2</li><li>Optical</li><li>Coaxial</li><li>Analog</li><li>Stream</li><li>Party Zone</li></ol>
235
289
 
236
290
  ```python
237
291
  """Example"""
@@ -244,7 +298,7 @@ zone1.input.priority = InputRouter.Priorities.LOCAL
244
298
 
245
299
  ## `Zone.group`
246
300
 
247
- Working on A.3x but offically unsupported in x series amplifiers.
301
+ Officially unsupported in X series amplifiers.
248
302
 
249
303
  | Property | Description | Type | Values |
250
304
  | ---------------------- | ----------- | ----------- |----------- |
@@ -254,6 +308,7 @@ Working on A.3x but offically unsupported in x series amplifiers.
254
308
  | `remove_member()` | Remove zone from group | `func` | `ZoneIDs`
255
309
  | `dissolve()` | Dissolve group / remove all members | `func` |
256
310
  | `leave()` | Leave the group if a member | `func` |
311
+ | `is_party_zone_member` | Member of Party Zone | `bool` |
257
312
 
258
313
  ```python
259
314
  """Examples"""
@@ -264,13 +319,17 @@ zone2.group.leave() # or
264
319
  zone1.group.remove_member(ZoneIDs.ZONE_2)
265
320
  # If zone 1 is a master, remove all members
266
321
  zone1.group.dissolve()
322
+ # Add zone to the party zone group
323
+ zone1.group.is_party_zone_member = True
324
+ # Toggle Party Zone Membership
325
+ zone1.group.is_party_zone_member_toggle()
267
326
  ```
268
327
 
269
328
  ## `Zone.analog_output`
270
329
 
271
330
  | Property | Description | Type | Values | Default |
272
331
  | ---------------------- | ----------- | ----------- |----------- |----------- |
273
- | `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
332
+ | `source` | Where the AO is routed from. i.e stream, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
274
333
  | `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` | |`False`
275
334
  | `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
276
335
 
@@ -363,6 +422,23 @@ EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `
363
422
  zone1.settings.eq.khz1_db = -2
364
423
  ```
365
424
 
425
+ ## `Zone.settings.subwoofer`
426
+
427
+ * **A.1 and A.1x only**
428
+ * Set `0` for full frequency range
429
+
430
+ | Property | Description | Type | Values | Default |
431
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
432
+ | `crossover` | Set "sub out" crossover frequency from 50-200Hz. | `int` | `0` or `50...200` | `0`
433
+
434
+ ```python
435
+ """Examples"""
436
+ # Set subwoofer ouput crossover to 100hz
437
+ zone1.settings.subwoofer.crossover = 100
438
+ ```
439
+
440
+
441
+
366
442
  ## Another (Lite) Way
367
443
 
368
444
  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`.
@@ -412,9 +488,11 @@ shell_command:
412
488
 
413
489
  ## Credit
414
490
 
491
+ Thanks to [@dj-jam](https://github.com/dj-jam) for the continued testing.
492
+
415
493
  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).
416
494
 
417
- Motovation for this project was to intergrate VSSLs amplifiers into [Home Assistant](https://www.home-assistant.io/) and have control over different subnets (not mDNS dependant)
495
+ 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)
418
496
 
419
497
  ## Known Issues & Limitiations
420
498
 
@@ -422,15 +500,15 @@ Motovation for this project was to intergrate VSSLs amplifiers into [Home Assist
422
500
  * VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
423
501
  * Not all sources set the volume to 0 when the zone is muted
424
502
  * Grouping feedback is flaky on the X series amplifiers
425
- * Airplay `Zone.track.progress` is not avaiable.
503
+ * Airplay `Zone.track.progress` is not available.
426
504
  * Cant stop a URL playback, feedback is worng at least
427
505
  * 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
428
- * `stop()` is intended to disconnect the client and pause the stream. Doesnt always function this way, depending on stream source
506
+ * `stop()` is intended to disconnect the client and pause the stream. Doesn’t always function this way, depending on stream source
429
507
  * Occasionally a zones might stop responding to certain commands, issuing the `reboot` command generally corrects
430
508
 
431
509
  ## Future
432
510
 
433
- * A.1(x) coverage i.e Bluetooth and subwoofer control
511
+ * A.1(x) coverage i.e Bluetooth
434
512
  * REST API / Web App
435
513
  * Save and recall EQ
436
514
  * IR Control
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vsslctrl"
7
- version = "0.1.9.dev1"
7
+ version = "0.1.11.dev1"
8
8
  description = "Package for controlling VSSL's range of streaming amplifiers"
9
9
  authors = [
10
10
  { name="vsslctrl", email="vsslcontrolled@proton.me" },