vsslctrl 0.1.4.dev1__tar.gz → 0.1.6.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.4.dev1 → vsslctrl-0.1.6.dev1}/PKG-INFO +51 -49
  2. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/README.md +49 -47
  3. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/pyproject.toml +2 -2
  4. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/api_alpha.py +8 -6
  5. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/api_base.py +13 -0
  6. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/api_bravo.py +5 -3
  7. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/core.py +1 -1
  8. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/data_structure.py +187 -39
  9. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/io.py +1 -1
  10. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/utils.py +13 -0
  11. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/zone.py +5 -0
  12. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/PKG-INFO +51 -49
  13. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/LICENSE +0 -0
  14. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/setup.cfg +0 -0
  15. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/tests/test_event_bus.py +0 -0
  16. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/tests/test_integration.py +0 -0
  17. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/tests/test_settings.py +0 -0
  18. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/tests/test_zone.py +0 -0
  19. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/__init__.py +0 -0
  20. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/decorators.py +0 -0
  21. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/device.py +0 -0
  22. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/discovery.py +0 -0
  23. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/event_bus.py +0 -0
  24. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/exceptions.py +0 -0
  25. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/group.py +0 -0
  26. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/settings.py +0 -0
  27. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/track.py +0 -0
  28. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/transport.py +0 -0
  29. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/SOURCES.txt +0 -0
  30. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/dependency_links.txt +0 -0
  31. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/requires.txt +0 -0
  32. {vsslctrl-0.1.4.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vsslctrl
3
- Version: 0.1.4.dev1
4
- Summary: Package for controlling VSSL amplifiers
3
+ Version: 0.1.6.dev1
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
7
7
  Classifier: Programming Language :: Python :: 3
@@ -20,29 +20,30 @@ Provides-Extra: optional
20
20
  Requires-Dist: zeroconf==0.132.2; extra == "optional"
21
21
 
22
22
  # vsslctrl
23
- Package for controlling [VSSL](https://www.vssl.com/) range of streaming amplifiers.
23
+
24
+ Package for controlling [VSSL's](https://www.vssl.com/) range of streaming amplifiers.
24
25
 
25
26
  **`vsslctrl` is not endorsed or affiliated with [VSSL](https://www.vssl.com/) in any manner.**
26
27
 
27
- 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)
28
+ ## Help
29
+
30
+ I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
28
31
 
29
- I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping.
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**
30
35
 
31
- Important
32
- -----------
33
- Only tested on a VSSL **A.3x** software version **p15305.016.3701**.
36
+ ## Important
34
37
 
35
- **Warning:** no *[VSSL Agent](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* should be running on the same network. If you dont know what this is, then you can probably ignore this notice.
38
+ There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* running on the same network. If you dont know what this is, then you can ignore this notice.
36
39
 
37
40
  ## TODOs
38
41
 
39
- * Test on other models (hardware needed)
40
- * Home Assistant integration. In progress, [here](https://github.com/vsslctrl/integration.home-assistant)
41
- * ~~Function scoping to supported feature / models~~ (needs tests)
42
+ * **A1(.x)** specific control e.g sub crossover, bluetooth
42
43
  * Better test coverage
43
44
 
44
- Basic Usage
45
- -----------
45
+ ## Basic Usage
46
+
46
47
  `vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
47
48
 
48
49
  ```python
@@ -89,8 +90,7 @@ async def main():
89
90
  asyncio.run(main())
90
91
  ```
91
92
 
92
- API
93
- -----------
93
+ # API
94
94
 
95
95
  Most functionality is achived via `getters` and `setters` of the two main classes `Vssl`, `Zone`.
96
96
 
@@ -225,7 +225,7 @@ zone1.transport.state = ZoneTransport.States.PAUSE
225
225
  ## `Zone.track`
226
226
 
227
227
  * Not all sources have complete metadata - missing value will be set to defaults.
228
- * Airplay track position is not avaiable.
228
+ * Airplay track `progress` is not avaiable.
229
229
 
230
230
  | Property | Description | Type | Values |
231
231
  | ---------------------- | ----------- | ----------- |----------- |
@@ -282,10 +282,10 @@ zone1.group.dissolve()
282
282
 
283
283
  ## `Zone.analog_output`
284
284
 
285
- | Property | Description | Type | Values |
286
- | ---------------------- | ----------- | ----------- |----------- |
287
- | `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources`
288
- | `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` readonly
285
+ | Property | Description | Type | Values | Default |
286
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
287
+ | `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
288
+ | `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` | |`False`
289
289
  | `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
290
290
 
291
291
  ```python
@@ -302,12 +302,12 @@ zone1.analog_output.is_fixed_volume = True
302
302
 
303
303
  ## `Zone.settings`
304
304
 
305
- | Property | Description | Type | Values |
306
- | ---------------------- | ----------- | ----------- |----------- |
305
+ | Property | Description | Type | Values | Default |
306
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
307
307
  | `name` | Name | `str` |
308
- | `disabled` | Disable the zone | `bool`
308
+ | `disabled` | Disable the zone | `bool` || `False`
309
309
  | `disabled_toggle()` | disable / enable | `func` |
310
- | `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono`
310
+ | `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono` | `Stereo`
311
311
  | `mono_toggle()` | Toggle mono or stereo | `func` |
312
312
 
313
313
  ```python
@@ -322,10 +322,10 @@ zone1.mono_toggle()
322
322
 
323
323
  ## `Zone.settings.analog_input`
324
324
 
325
- | Property | Description | Type | Values |
326
- | ---------------------- | ----------- | ----------- |----------- |
325
+ | Property | Description | Type | Values | Default |
326
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
327
327
  | `name` | Name | `str` |
328
- | `fixed_gain` | Fix the input gain to a specific value |`int` | `0...100`
328
+ | `fixed_gain` | Fix the input gain to a specific value |`int` | `0...100` | `0` is disabled or variable gain
329
329
 
330
330
  ```python
331
331
  """Examples"""
@@ -339,11 +339,11 @@ zone1.settings.analog_input.fixed_gain = 50
339
339
 
340
340
  ## `Zone.settings.volume`
341
341
 
342
- | Property | Description | Type | Values |
343
- | ---------------------- | ----------- | ----------- |----------- |
344
- | `default_on` | Default on volume | `int` | `0...100`
345
- | `max_left` | Max volume left channel | `int` | `0...100`
346
- | `max_right` | Max volume right channel | `int` | `0...100`
342
+ | Property | Description | Type | Values | Default |
343
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
344
+ | `default_on` | Default on volume | `int` | `0...100` | `0` is disabled
345
+ | `max_left` | Max volume left channel | `int` | `0...100` | `75`
346
+ | `max_right` | Max volume right channel | `int` | `0...100` | `75`
347
347
 
348
348
  ```python
349
349
  """Examples"""
@@ -355,21 +355,21 @@ zone1.settings.volume.default_on = 75
355
355
 
356
356
  ## `Zone.settings.eq`
357
357
 
358
- | Property | Description | Type | Values |
359
- | ---------------------- | ----------- | ----------- |----------- |
360
- | `enabled` | Enable / disable EQ | `bool`
358
+ | Property | Description | Type | Values | Default |
359
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
360
+ | `enabled` | Enable / disable EQ | `bool` | | `False`
361
361
 
362
362
  EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `-10`dB to `+10`dB
363
363
 
364
- | Property | Description | Type | Values |
365
- | ---------------------- | ----------- | ----------- |----------- |
366
- | `hz60_db` | 60Hz | `int` | `-10...10`
367
- | `hz200_db` | 200Hz | `int` | `-10...10`
368
- | `hz500_db` | 500Hz | `int` | `-10...10`
369
- | `khz1_db` | 1kHz | `int` | `-10...10`
370
- | `khz4_db` | 4kHz | `int` | `-10...10`
371
- | `khz8_db` | 8kHz | `int` | `-10...10`
372
- | `khz15_db` | 15kHz | `int` | `-10...10`
364
+ | Property | Description | Type | Values | Default |
365
+ | ---------------------- | ----------- | ----------- |----------- | ----------- |
366
+ | `hz60_db` | 60Hz | `int` | `-10...10` | `0`
367
+ | `hz200_db` | 200Hz | `int` | `-10...10` | `0`
368
+ | `hz500_db` | 500Hz | `int` | `-10...10` | `0`
369
+ | `khz1_db` | 1kHz | `int` | `-10...10` | `0`
370
+ | `khz4_db` | 4kHz | `int` | `-10...10` | `0`
371
+ | `khz8_db` | 8kHz | `int` | `-10...10` | `0`
372
+ | `khz15_db` | 15kHz | `int` | `-10...10` | `0`
373
373
 
374
374
  ```python
375
375
  """Examples"""
@@ -379,7 +379,7 @@ zone1.settings.eq.khz1_db = -2
379
379
 
380
380
  ## Another (Lite) Way
381
381
 
382
- 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 ant network tool) on port `50002`.
382
+ 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`.
383
383
 
384
384
  | HEX | Description |
385
385
  | ---------------------- | ----------- |
@@ -428,13 +428,15 @@ shell_command:
428
428
 
429
429
  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).
430
430
 
431
+ 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)
432
+
431
433
  ## Known Issues & Limitiations
432
434
 
433
- * Tested on VSSL **A.3x** software version **p15305.016.3701**
434
- * Not tested on A.1x, A.6.x or original A series range of amplifiers (testers welcome)
435
+ * Not tested on A.1x or original A series range of amplifiers (testers welcome)
435
436
  * VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
436
437
  * Not all sources set the volume to 0 when the zone is muted
437
438
  * Grouping feedback is flaky on the X series amplifiers
439
+ * Airplay `Zone.track.progress` is not avaiable.
438
440
  * Cant stop a URL playback, feedback is worng at least
439
441
  * 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
440
442
  * `stop()` is intended to disconnect the client and pause the stream. Doesnt always function this way, depending on stream source
@@ -442,8 +444,8 @@ The VSSL API was reverse engineered using Wireshark, VSSLs native "legacy" iOS a
442
444
 
443
445
  ## Future
444
446
 
447
+ * A.1(x) coverage i.e Bluetooth and subwoofer control
445
448
  * REST API / Web App
446
449
  * Save and recall EQ
447
- * A.1(x) coverage i.e Bluetooth
448
450
  * IR Control
449
451
 
@@ -1,27 +1,28 @@
1
1
  # vsslctrl
2
- Package for controlling [VSSL](https://www.vssl.com/) range of streaming amplifiers.
2
+
3
+ Package for controlling [VSSL's](https://www.vssl.com/) range of streaming amplifiers.
3
4
 
4
5
  **`vsslctrl` is not endorsed or affiliated with [VSSL](https://www.vssl.com/) in any manner.**
5
6
 
6
- 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)
7
+ ## Help
8
+
9
+ I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
7
10
 
8
- I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping.
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**
9
14
 
10
- Important
11
- -----------
12
- Only tested on a VSSL **A.3x** software version **p15305.016.3701**.
15
+ ## Important
13
16
 
14
- **Warning:** no *[VSSL Agent](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* should be running on the same network. If you dont know what this is, then you can probably ignore this notice.
17
+ There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* running on the same network. If you dont know what this is, then you can ignore this notice.
15
18
 
16
19
  ## TODOs
17
20
 
18
- * Test on other models (hardware needed)
19
- * Home Assistant integration. In progress, [here](https://github.com/vsslctrl/integration.home-assistant)
20
- * ~~Function scoping to supported feature / models~~ (needs tests)
21
+ * **A1(.x)** specific control e.g sub crossover, bluetooth
21
22
  * Better test coverage
22
23
 
23
- Basic Usage
24
- -----------
24
+ ## Basic Usage
25
+
25
26
  `vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
26
27
 
27
28
  ```python
@@ -68,8 +69,7 @@ async def main():
68
69
  asyncio.run(main())
69
70
  ```
70
71
 
71
- API
72
- -----------
72
+ # API
73
73
 
74
74
  Most functionality is achived via `getters` and `setters` of the two main classes `Vssl`, `Zone`.
75
75
 
@@ -204,7 +204,7 @@ zone1.transport.state = ZoneTransport.States.PAUSE
204
204
  ## `Zone.track`
205
205
 
206
206
  * Not all sources have complete metadata - missing value will be set to defaults.
207
- * Airplay track position is not avaiable.
207
+ * Airplay track `progress` is not avaiable.
208
208
 
209
209
  | Property | Description | Type | Values |
210
210
  | ---------------------- | ----------- | ----------- |----------- |
@@ -261,10 +261,10 @@ zone1.group.dissolve()
261
261
 
262
262
  ## `Zone.analog_output`
263
263
 
264
- | Property | Description | Type | Values |
265
- | ---------------------- | ----------- | ----------- |----------- |
266
- | `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources`
267
- | `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` readonly
264
+ | Property | Description | Type | Values | Default |
265
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
266
+ | `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
267
+ | `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` | |`False`
268
268
  | `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
269
269
 
270
270
  ```python
@@ -281,12 +281,12 @@ zone1.analog_output.is_fixed_volume = True
281
281
 
282
282
  ## `Zone.settings`
283
283
 
284
- | Property | Description | Type | Values |
285
- | ---------------------- | ----------- | ----------- |----------- |
284
+ | Property | Description | Type | Values | Default |
285
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
286
286
  | `name` | Name | `str` |
287
- | `disabled` | Disable the zone | `bool`
287
+ | `disabled` | Disable the zone | `bool` || `False`
288
288
  | `disabled_toggle()` | disable / enable | `func` |
289
- | `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono`
289
+ | `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono` | `Stereo`
290
290
  | `mono_toggle()` | Toggle mono or stereo | `func` |
291
291
 
292
292
  ```python
@@ -301,10 +301,10 @@ zone1.mono_toggle()
301
301
 
302
302
  ## `Zone.settings.analog_input`
303
303
 
304
- | Property | Description | Type | Values |
305
- | ---------------------- | ----------- | ----------- |----------- |
304
+ | Property | Description | Type | Values | Default |
305
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
306
306
  | `name` | Name | `str` |
307
- | `fixed_gain` | Fix the input gain to a specific value |`int` | `0...100`
307
+ | `fixed_gain` | Fix the input gain to a specific value |`int` | `0...100` | `0` is disabled or variable gain
308
308
 
309
309
  ```python
310
310
  """Examples"""
@@ -318,11 +318,11 @@ zone1.settings.analog_input.fixed_gain = 50
318
318
 
319
319
  ## `Zone.settings.volume`
320
320
 
321
- | Property | Description | Type | Values |
322
- | ---------------------- | ----------- | ----------- |----------- |
323
- | `default_on` | Default on volume | `int` | `0...100`
324
- | `max_left` | Max volume left channel | `int` | `0...100`
325
- | `max_right` | Max volume right channel | `int` | `0...100`
321
+ | Property | Description | Type | Values | Default |
322
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
323
+ | `default_on` | Default on volume | `int` | `0...100` | `0` is disabled
324
+ | `max_left` | Max volume left channel | `int` | `0...100` | `75`
325
+ | `max_right` | Max volume right channel | `int` | `0...100` | `75`
326
326
 
327
327
  ```python
328
328
  """Examples"""
@@ -334,21 +334,21 @@ zone1.settings.volume.default_on = 75
334
334
 
335
335
  ## `Zone.settings.eq`
336
336
 
337
- | Property | Description | Type | Values |
338
- | ---------------------- | ----------- | ----------- |----------- |
339
- | `enabled` | Enable / disable EQ | `bool`
337
+ | Property | Description | Type | Values | Default |
338
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
339
+ | `enabled` | Enable / disable EQ | `bool` | | `False`
340
340
 
341
341
  EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `-10`dB to `+10`dB
342
342
 
343
- | Property | Description | Type | Values |
344
- | ---------------------- | ----------- | ----------- |----------- |
345
- | `hz60_db` | 60Hz | `int` | `-10...10`
346
- | `hz200_db` | 200Hz | `int` | `-10...10`
347
- | `hz500_db` | 500Hz | `int` | `-10...10`
348
- | `khz1_db` | 1kHz | `int` | `-10...10`
349
- | `khz4_db` | 4kHz | `int` | `-10...10`
350
- | `khz8_db` | 8kHz | `int` | `-10...10`
351
- | `khz15_db` | 15kHz | `int` | `-10...10`
343
+ | Property | Description | Type | Values | Default |
344
+ | ---------------------- | ----------- | ----------- |----------- | ----------- |
345
+ | `hz60_db` | 60Hz | `int` | `-10...10` | `0`
346
+ | `hz200_db` | 200Hz | `int` | `-10...10` | `0`
347
+ | `hz500_db` | 500Hz | `int` | `-10...10` | `0`
348
+ | `khz1_db` | 1kHz | `int` | `-10...10` | `0`
349
+ | `khz4_db` | 4kHz | `int` | `-10...10` | `0`
350
+ | `khz8_db` | 8kHz | `int` | `-10...10` | `0`
351
+ | `khz15_db` | 15kHz | `int` | `-10...10` | `0`
352
352
 
353
353
  ```python
354
354
  """Examples"""
@@ -358,7 +358,7 @@ zone1.settings.eq.khz1_db = -2
358
358
 
359
359
  ## Another (Lite) Way
360
360
 
361
- 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 ant network tool) on port `50002`.
361
+ 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`.
362
362
 
363
363
  | HEX | Description |
364
364
  | ---------------------- | ----------- |
@@ -407,13 +407,15 @@ shell_command:
407
407
 
408
408
  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).
409
409
 
410
+ 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)
411
+
410
412
  ## Known Issues & Limitiations
411
413
 
412
- * Tested on VSSL **A.3x** software version **p15305.016.3701**
413
- * Not tested on A.1x, A.6.x or original A series range of amplifiers (testers welcome)
414
+ * Not tested on A.1x or original A series range of amplifiers (testers welcome)
414
415
  * VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
415
416
  * Not all sources set the volume to 0 when the zone is muted
416
417
  * Grouping feedback is flaky on the X series amplifiers
418
+ * Airplay `Zone.track.progress` is not avaiable.
417
419
  * Cant stop a URL playback, feedback is worng at least
418
420
  * 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
419
421
  * `stop()` is intended to disconnect the client and pause the stream. Doesnt always function this way, depending on stream source
@@ -421,8 +423,8 @@ The VSSL API was reverse engineered using Wireshark, VSSLs native "legacy" iOS a
421
423
 
422
424
  ## Future
423
425
 
426
+ * A.1(x) coverage i.e Bluetooth and subwoofer control
424
427
  * REST API / Web App
425
428
  * Save and recall EQ
426
- * A.1(x) coverage i.e Bluetooth
427
429
  * IR Control
428
430
 
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vsslctrl"
7
- version = "0.1.4.dev1"
8
- description = "Package for controlling VSSL amplifiers"
7
+ version = "0.1.6.dev1"
8
+ description = "Package for controlling VSSL's range of streaming amplifiers"
9
9
  authors = [
10
10
  { name="vsslctrl", email="vsslcontrolled@proton.me" },
11
11
  ]
@@ -9,7 +9,7 @@ from .api_base import APIBase
9
9
  from .transport import ZoneTransport
10
10
  from .settings import EQSettings
11
11
 
12
- from .utils import hex_to_int, clamp_volume
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
15
  ZoneStatusExtKeys,
@@ -199,7 +199,7 @@ class APIAlpha(APIBase):
199
199
  # EQ
200
200
  #
201
201
  def request_action_0D(self, freq: "EQSettings.Freqs", value: int = 0):
202
- clamped = max(90, min(value, 110))
202
+ clamped = max(EQSettings.MIN_VALUE, min(value, EQSettings.MAX_VALUE))
203
203
  self._log_debug(
204
204
  f"Requesting to set EQ: {freq.name[1:]} ({freq.value}) to {clamped}"
205
205
  )
@@ -924,9 +924,9 @@ class APIAlpha(APIBase):
924
924
 
925
925
  #
926
926
  # 17 [23]
927
- # Received Keep Alive
927
+ # Keep Alive
928
928
  #
929
- def response_action_17(self, response: bytes):
929
+ def response_action_17(self, hexl: list, response: bytes):
930
930
  self._log_debug(f"Z{self.zone.id} Alpha - Received keep alive: {response}")
931
931
  # TODO
932
932
  pass
@@ -1026,8 +1026,9 @@ class APIAlpha(APIBase):
1026
1026
  # Command confimation
1027
1027
  #
1028
1028
  def response_action_confimation(self, response: bytes):
1029
+ cmd = response.hex()
1029
1030
  self._log_debug(
1030
- f"Received command confimation: {response[1]}. Hex: {response.hex()}"
1031
+ f"Received command confimation: {hex_to_bytearray_string(cmd)} Hex: {cmd}"
1031
1032
  )
1032
1033
 
1033
1034
  #
@@ -1035,6 +1036,7 @@ class APIAlpha(APIBase):
1035
1036
  # Default Action
1036
1037
  #
1037
1038
  def response_action_default(self, hexl: list, response: bytes):
1039
+ cmd = response.hex()
1038
1040
  self._log_debug(
1039
- f"Received unknown command {hexl[1].upper()}. Hex: {response.hex()}"
1041
+ f"Received unknown command: {hex_to_bytearray_string(cmd)} Hex: {cmd}"
1040
1042
  )
@@ -158,6 +158,19 @@ class APIBase(ABC):
158
158
 
159
159
  except asyncio.CancelledError:
160
160
  self._log_debug(f"writer close timeout")
161
+ except asyncio.TimeoutError as e:
162
+ self._log_error(f"Timeout while closing connection: {e}")
163
+ # Handle the timeout: log, retry, or take other actions
164
+ except ConnectionResetError as e:
165
+ self._log_error(f"Connection reset by peer: {e}")
166
+ # Handle the error: log, retry, or take other actions
167
+ except Exception as e:
168
+ self._log_error(f"Unexpected error occurred while disconnecting: {e}")
169
+ # Handle unexpected errors
170
+ finally:
171
+ self._writer = None
172
+
173
+ self._reader = None
161
174
 
162
175
  self._log_info(f"{self.host}:{self.port}: disconnected")
163
176
 
@@ -5,7 +5,7 @@ import struct
5
5
  import logging
6
6
 
7
7
  from .api_base import APIBase
8
- from .utils import hex_to_int
8
+ from .utils import hex_to_int, hex_to_bytearray_string
9
9
  from .decorators import logging_helpers
10
10
  from .data_structure import TrackMetadataExtKeys
11
11
 
@@ -407,8 +407,10 @@ class APIBravo(APIBase):
407
407
  #
408
408
  def response_action_70(self, hexl: list, response: bytes):
409
409
  length = 4 if hexl[0] == 16 else 10
410
- cmd = hexl[length:]
411
- self._log_debug(f"Command confirmation: {response[length:]}")
410
+ cmd = response[length:].hex()
411
+ self._log_debug(
412
+ f"Received command confimation: {hex_to_bytearray_string(cmd)} Hex: {cmd}"
413
+ )
412
414
 
413
415
  #
414
416
  # Default
@@ -69,7 +69,7 @@ class Vssl:
69
69
  self.Events.MODEL_CHANGE, self.ENTITY_ID
70
70
  )
71
71
 
72
- # Lets make sure the zone is initialised, otherwsie we fail all
72
+ # Lets make sure the zone is initialised, otherwsie we fail for all zones
73
73
  await first_zone.initialise()
74
74
 
75
75
  # Only continue after we have a model
@@ -43,12 +43,35 @@ class ZoneIDs(VsslIntEnum):
43
43
  ZONE_6 = 6
44
44
 
45
45
 
46
- """ JSON Structure
47
-
48
- DO NOT CHANGE - VSSL Defined
49
-
50
- {'B1Src': '3', 'B2Src': '4', 'B3Src': '5', 'B1Nm': '', 'B2Nm': 'Optical In', 'dev': 'Device Name', 'ver': 'p15305.016.3701'}
51
-
46
+ """
47
+ JSON Structure
48
+
49
+ DO NOT CHANGE - VSSL Defined
50
+
51
+ A3.x
52
+ {
53
+ "B1Src": "3",
54
+ "B2Src": "4",
55
+ "B3Src": "5",
56
+ "B1Nm": "",
57
+ "B2Nm": "Optical In",
58
+ "dev": "Device Name",
59
+ "ver": "p15305.016.3701"
60
+ }
61
+
62
+ A6.x
63
+ {
64
+ "B1Src": "3",
65
+ "B2Src": "4",
66
+ "B3Src": "5",
67
+ "B4Src": "6",
68
+ "B5Src": "7",
69
+ "B6Src": "8",
70
+ "B1Nm": "",
71
+ "B2Nm": "",
72
+ "dev": "VSSL A.6x",
73
+ "ver": "p15305.017.3701"
74
+ }
52
75
  """
53
76
 
54
77
 
@@ -69,13 +92,50 @@ class DeviceStatusExtKeys:
69
92
  return f"B{zone_id}Src"
70
93
 
71
94
 
72
- """ JSON Structure
73
-
74
- DO NOT CHANGE - VSSL Defined
75
-
76
- {'id': '1', 'ac': '0', 'mc': 'XXXXXXXXXXXX', 'vol': '20', 'mt': '0', 'pa': '0', 'rm': '0', 'ts': '14',
77
- 'alex': '14', 'nmd': '0', 'ird': '14', 'lb': '24', 'tp': '13', 'wr': '0', 'as': '0', 'rg': '0'}
78
-
95
+ """
96
+ JSON Structure
97
+
98
+ DO NOT CHANGE - VSSL Defined
99
+
100
+ A3.x
101
+ {
102
+ "id": "1",
103
+ "ac": "0",
104
+ "mc": "XXXXXXXXXXXX",
105
+ "vol": "20",
106
+ "mt": "0",
107
+ "pa": "0",
108
+ "rm": "0",
109
+ "ts": "14",
110
+ "alex": "14",
111
+ "nmd": "0",
112
+ "ird": "14",
113
+ "lb": "24",
114
+ "tp": "13",
115
+ "wr": "0",
116
+ "as": "0",
117
+ "rg": "0"
118
+ }
119
+
120
+ A6.x
121
+ {
122
+ "id": "1",
123
+ "ac": "0",
124
+ "mc": "XXXXXXXXXXXX",
125
+ "vol": "50",
126
+ "mt": "0",
127
+ "pa": "0",
128
+ "rm": "0",
129
+ "ts": "0",
130
+ "alex": "126",
131
+ "nmd": "0",
132
+ "ird": "255",
133
+ "lb": "17",
134
+ "tp": "16",
135
+ "wr": "0",
136
+ "as": "0",
137
+ "rg": "0"
138
+ }
79
139
  """
80
140
 
81
141
 
@@ -91,13 +151,42 @@ class ZoneStatusExtKeys:
91
151
  DISABLED = "wr"
92
152
 
93
153
 
94
- """ JSON Structure
95
-
96
- DO NOT CHANGE - VSSL Defined
97
-
98
- {'mono': '0', 'AiNm': 'Analog In 1', 'eq1': '100', 'eq2': '100', 'eq3': '100', 'eq4': '100',
99
- 'eq5': '100', 'eq6': '100', 'eq7': '100', 'voll': '75', 'volr': '75', 'vold': '0'}
100
-
154
+ """
155
+ JSON Structure
156
+
157
+ DO NOT CHANGE - VSSL Defined
158
+
159
+ A3.x
160
+ {
161
+ "mono": "0",
162
+ "AiNm": "Analog In 1",
163
+ "eq1": "100",
164
+ "eq2": "100",
165
+ "eq3": "100",
166
+ "eq4": "100",
167
+ "eq5": "100",
168
+ "eq6": "100",
169
+ "eq7": "100",
170
+ "voll": "75",
171
+ "volr": "75",
172
+ "vold": "0"
173
+ }
174
+
175
+ A6.x
176
+ {
177
+ "mono": "0",
178
+ "AiNm": "",
179
+ "eq1": "100",
180
+ "eq2": "100",
181
+ "eq3": "100",
182
+ "eq4": "100",
183
+ "eq5": "100",
184
+ "eq6": "100",
185
+ "eq7": "100",
186
+ "voll": "75",
187
+ "volr": "75",
188
+ "vold": "0"
189
+ }
101
190
  """
102
191
 
103
192
 
@@ -116,13 +205,47 @@ class ZoneEQStatusExtKeys:
116
205
  VOL_DEFAULT_ON = "vold"
117
206
 
118
207
 
119
- """ JSON Structure
120
-
121
- DO NOT CHANGE - VSSL Defined
122
-
123
- {'ECO': '0', 'eqsw': '1', 'inSrc': '0', 'SP': '0', 'BF1': '0', 'BF2': '0', 'BF3': '0',
124
- 'GRM': '0', 'GRS': '255', 'Pwr': '0', 'Bvr': '1', 'fxv': '24', 'AtPwr': '1'}
125
-
208
+ """
209
+ JSON Structure
210
+
211
+ DO NOT CHANGE - VSSL Defined
212
+
213
+ A3.x
214
+ {
215
+ "ECO": "0",
216
+ "eqsw": "1",
217
+ "inSrc": "0",
218
+ "SP": "0",
219
+ "BF1": "0",
220
+ "BF2": "0",
221
+ "BF3": "0",
222
+ "GRM": "0",
223
+ "GRS": "255",
224
+ "Pwr": "0",
225
+ "Bvr": "1",
226
+ "fxv": "24",
227
+ "AtPwr": "1"
228
+ }
229
+
230
+ A6.x
231
+ {
232
+ "ECO": "0",
233
+ "eqsw": "1",
234
+ "inSrc": "0",
235
+ "SP": "0",
236
+ "BF1": "0",
237
+ "BF2": "0",
238
+ "BF3": "0",
239
+ "BF4": "0",
240
+ "BF5": "0",
241
+ "BF6": "0",
242
+ "GRM": "0",
243
+ "GRS": "255",
244
+ "Pwr": "0",
245
+ "Bvr": "2",
246
+ "fxv": "25",
247
+ "AtPwr": "1"
248
+ }
126
249
  """
127
250
 
128
251
 
@@ -130,7 +253,12 @@ class ZoneRouterStatusExtKeys:
130
253
  EQ_ENABLED = "eqsw"
131
254
  INPUT_SOURCE = "inSrc"
132
255
  SOURCE_PRIORITY = "SP"
133
- ANALOG_OUTPUT_FIXED_VOLUME_PREFIX = "BF"
256
+ ANALOG_OUTPUT_1_FIXED_VOLUME = "BF1"
257
+ ANALOG_OUTPUT_2_FIXED_VOLUME = "BF2"
258
+ ANALOG_OUTPUT_3_FIXED_VOLUME = "BF3"
259
+ ANALOG_OUTPUT_4_FIXED_VOLUME = "BF4"
260
+ ANALOG_OUTPUT_5_FIXED_VOLUME = "BF5"
261
+ ANALOG_OUTPUT_6_FIXED_VOLUME = "BF6"
134
262
  GROUP_SOURCE = "GRS"
135
263
  GROUP_MASTER = "GRM"
136
264
  POWER_STATE = "Pwr"
@@ -142,17 +270,37 @@ class ZoneRouterStatusExtKeys:
142
270
  return f"BF{zone_id}"
143
271
 
144
272
 
145
- """ JSON Structure
146
-
147
- DO NOT CHANGE - VSSL Defined
148
-
149
- {'Album': 'International Skankers', 'Artist': 'Ashkabad', 'BitDepth': 16,
150
- 'BitRate': '320000', 'CoverArtUrl': 'https://i.scdn.co/image/ab67616d0000b2730cbb03a339c6ffd18d10eab2',
151
- 'Current Source': 4, 'Current_time': -1, 'DSDType': '', 'Fav': False, 'FileSize': 0, 'Genre': '',
152
- 'Index': 0, 'Mime': 'Ogg', 'Next': False, 'PlayState': 0, 'PlayUrl': 'spotify:track:0IHTiLO5qBYhf7Hmn0UDBN',
153
- 'Prev': False, 'Repeat': 0, 'SampleRate': '44100', 'Seek': False, 'Shuffle': 0, 'SinglePlay': False,
154
- 'TotalTime': 203087, 'TrackName': 'Beijing'}
155
-
273
+ """
274
+ JSON Structure
275
+
276
+ DO NOT CHANGE - VSSL Defined
277
+
278
+ {
279
+ "Album": "International Skankers",
280
+ "Artist": "Ashkabad",
281
+ "BitDepth": 16,
282
+ "BitRate": "320000",
283
+ "CoverArtUrl": "https://i.scdn.co/image/ab67616d0000b2730cbb03a339c6ffd18d10eab2",
284
+ "Current Source": 4,
285
+ "Current_time": -1,
286
+ "DSDType": "",
287
+ "Fav": False,
288
+ "FileSize": 0,
289
+ "Genre": "",
290
+ "Index": 0,
291
+ "Mime": "Ogg",
292
+ "Next": False,
293
+ "PlayState": 0,
294
+ "PlayUrl": "spotify:track:0IHTiLO5qBYhf7Hmn0UDBN",
295
+ "Prev": False,
296
+ "Repeat": 0,
297
+ "SampleRate": "44100",
298
+ "Seek": False,
299
+ "Shuffle": 0,
300
+ "SinglePlay": False,
301
+ "TotalTime": 203087,
302
+ "TrackName": "Beijing"
303
+ }
156
304
  """
157
305
 
158
306
 
@@ -139,7 +139,7 @@ class AnalogOutput(ZoneDataClass):
139
139
  self.zone = zone
140
140
 
141
141
  self._is_fixed_volume = False
142
- self._source = self.Sources(zone.id + 3)
142
+ self._source = self.Sources(zone.id + 2)
143
143
 
144
144
  #
145
145
  # Analog Output Fix Volume. Output wont respond to volume control
@@ -38,6 +38,19 @@ def group_list_by_property(input_list, property_key):
38
38
  return grouped_dict
39
39
 
40
40
 
41
+ #
42
+ # Logging Helper to show a command in bytearray([]) syntax
43
+ #
44
+ def hex_to_bytearray_string(hex_string):
45
+ # Convert the hex string to a bytearray
46
+ byte_array = bytearray.fromhex(hex_string)
47
+
48
+ # Create the bytearray string representation
49
+ bytearray_str = f'bytearray([{", ".join(map(str, byte_array))}])'
50
+
51
+ return bytearray_str
52
+
53
+
41
54
  #
42
55
  # Repeat Timer
43
56
  #
@@ -267,6 +267,11 @@ class Zone:
267
267
  def _set_mac_addr(self, mac: str):
268
268
  mac = mac.strip()
269
269
  if mac != self.mac_addr:
270
+ # Strip Wlan0: from beginging of string
271
+ # Original A series amps had this prefix
272
+ if mac.startswith("Wlan0:"):
273
+ mac = mac[len("Wlan0:") :]
274
+
270
275
  # Define the regular expression pattern for a MAC address
271
276
  mac_pattern = re.compile(r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$")
272
277
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vsslctrl
3
- Version: 0.1.4.dev1
4
- Summary: Package for controlling VSSL amplifiers
3
+ Version: 0.1.6.dev1
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
7
7
  Classifier: Programming Language :: Python :: 3
@@ -20,29 +20,30 @@ Provides-Extra: optional
20
20
  Requires-Dist: zeroconf==0.132.2; extra == "optional"
21
21
 
22
22
  # vsslctrl
23
- Package for controlling [VSSL](https://www.vssl.com/) range of streaming amplifiers.
23
+
24
+ Package for controlling [VSSL's](https://www.vssl.com/) range of streaming amplifiers.
24
25
 
25
26
  **`vsslctrl` is not endorsed or affiliated with [VSSL](https://www.vssl.com/) in any manner.**
26
27
 
27
- 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)
28
+ ## Help
29
+
30
+ I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
28
31
 
29
- I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping.
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**
30
35
 
31
- Important
32
- -----------
33
- Only tested on a VSSL **A.3x** software version **p15305.016.3701**.
36
+ ## Important
34
37
 
35
- **Warning:** no *[VSSL Agent](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* should be running on the same network. If you dont know what this is, then you can probably ignore this notice.
38
+ There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* running on the same network. If you dont know what this is, then you can ignore this notice.
36
39
 
37
40
  ## TODOs
38
41
 
39
- * Test on other models (hardware needed)
40
- * Home Assistant integration. In progress, [here](https://github.com/vsslctrl/integration.home-assistant)
41
- * ~~Function scoping to supported feature / models~~ (needs tests)
42
+ * **A1(.x)** specific control e.g sub crossover, bluetooth
42
43
  * Better test coverage
43
44
 
44
- Basic Usage
45
- -----------
45
+ ## Basic Usage
46
+
46
47
  `vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
47
48
 
48
49
  ```python
@@ -89,8 +90,7 @@ async def main():
89
90
  asyncio.run(main())
90
91
  ```
91
92
 
92
- API
93
- -----------
93
+ # API
94
94
 
95
95
  Most functionality is achived via `getters` and `setters` of the two main classes `Vssl`, `Zone`.
96
96
 
@@ -225,7 +225,7 @@ zone1.transport.state = ZoneTransport.States.PAUSE
225
225
  ## `Zone.track`
226
226
 
227
227
  * Not all sources have complete metadata - missing value will be set to defaults.
228
- * Airplay track position is not avaiable.
228
+ * Airplay track `progress` is not avaiable.
229
229
 
230
230
  | Property | Description | Type | Values |
231
231
  | ---------------------- | ----------- | ----------- |----------- |
@@ -282,10 +282,10 @@ zone1.group.dissolve()
282
282
 
283
283
  ## `Zone.analog_output`
284
284
 
285
- | Property | Description | Type | Values |
286
- | ---------------------- | ----------- | ----------- |----------- |
287
- | `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources`
288
- | `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` readonly
285
+ | Property | Description | Type | Values | Default |
286
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
287
+ | `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
288
+ | `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` | |`False`
289
289
  | `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
290
290
 
291
291
  ```python
@@ -302,12 +302,12 @@ zone1.analog_output.is_fixed_volume = True
302
302
 
303
303
  ## `Zone.settings`
304
304
 
305
- | Property | Description | Type | Values |
306
- | ---------------------- | ----------- | ----------- |----------- |
305
+ | Property | Description | Type | Values | Default |
306
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
307
307
  | `name` | Name | `str` |
308
- | `disabled` | Disable the zone | `bool`
308
+ | `disabled` | Disable the zone | `bool` || `False`
309
309
  | `disabled_toggle()` | disable / enable | `func` |
310
- | `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono`
310
+ | `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono` | `Stereo`
311
311
  | `mono_toggle()` | Toggle mono or stereo | `func` |
312
312
 
313
313
  ```python
@@ -322,10 +322,10 @@ zone1.mono_toggle()
322
322
 
323
323
  ## `Zone.settings.analog_input`
324
324
 
325
- | Property | Description | Type | Values |
326
- | ---------------------- | ----------- | ----------- |----------- |
325
+ | Property | Description | Type | Values | Default |
326
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
327
327
  | `name` | Name | `str` |
328
- | `fixed_gain` | Fix the input gain to a specific value |`int` | `0...100`
328
+ | `fixed_gain` | Fix the input gain to a specific value |`int` | `0...100` | `0` is disabled or variable gain
329
329
 
330
330
  ```python
331
331
  """Examples"""
@@ -339,11 +339,11 @@ zone1.settings.analog_input.fixed_gain = 50
339
339
 
340
340
  ## `Zone.settings.volume`
341
341
 
342
- | Property | Description | Type | Values |
343
- | ---------------------- | ----------- | ----------- |----------- |
344
- | `default_on` | Default on volume | `int` | `0...100`
345
- | `max_left` | Max volume left channel | `int` | `0...100`
346
- | `max_right` | Max volume right channel | `int` | `0...100`
342
+ | Property | Description | Type | Values | Default |
343
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
344
+ | `default_on` | Default on volume | `int` | `0...100` | `0` is disabled
345
+ | `max_left` | Max volume left channel | `int` | `0...100` | `75`
346
+ | `max_right` | Max volume right channel | `int` | `0...100` | `75`
347
347
 
348
348
  ```python
349
349
  """Examples"""
@@ -355,21 +355,21 @@ zone1.settings.volume.default_on = 75
355
355
 
356
356
  ## `Zone.settings.eq`
357
357
 
358
- | Property | Description | Type | Values |
359
- | ---------------------- | ----------- | ----------- |----------- |
360
- | `enabled` | Enable / disable EQ | `bool`
358
+ | Property | Description | Type | Values | Default |
359
+ | ---------------------- | ----------- | ----------- |----------- |----------- |
360
+ | `enabled` | Enable / disable EQ | `bool` | | `False`
361
361
 
362
362
  EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `-10`dB to `+10`dB
363
363
 
364
- | Property | Description | Type | Values |
365
- | ---------------------- | ----------- | ----------- |----------- |
366
- | `hz60_db` | 60Hz | `int` | `-10...10`
367
- | `hz200_db` | 200Hz | `int` | `-10...10`
368
- | `hz500_db` | 500Hz | `int` | `-10...10`
369
- | `khz1_db` | 1kHz | `int` | `-10...10`
370
- | `khz4_db` | 4kHz | `int` | `-10...10`
371
- | `khz8_db` | 8kHz | `int` | `-10...10`
372
- | `khz15_db` | 15kHz | `int` | `-10...10`
364
+ | Property | Description | Type | Values | Default |
365
+ | ---------------------- | ----------- | ----------- |----------- | ----------- |
366
+ | `hz60_db` | 60Hz | `int` | `-10...10` | `0`
367
+ | `hz200_db` | 200Hz | `int` | `-10...10` | `0`
368
+ | `hz500_db` | 500Hz | `int` | `-10...10` | `0`
369
+ | `khz1_db` | 1kHz | `int` | `-10...10` | `0`
370
+ | `khz4_db` | 4kHz | `int` | `-10...10` | `0`
371
+ | `khz8_db` | 8kHz | `int` | `-10...10` | `0`
372
+ | `khz15_db` | 15kHz | `int` | `-10...10` | `0`
373
373
 
374
374
  ```python
375
375
  """Examples"""
@@ -379,7 +379,7 @@ zone1.settings.eq.khz1_db = -2
379
379
 
380
380
  ## Another (Lite) Way
381
381
 
382
- 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 ant network tool) on port `50002`.
382
+ 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`.
383
383
 
384
384
  | HEX | Description |
385
385
  | ---------------------- | ----------- |
@@ -428,13 +428,15 @@ shell_command:
428
428
 
429
429
  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).
430
430
 
431
+ 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)
432
+
431
433
  ## Known Issues & Limitiations
432
434
 
433
- * Tested on VSSL **A.3x** software version **p15305.016.3701**
434
- * Not tested on A.1x, A.6.x or original A series range of amplifiers (testers welcome)
435
+ * Not tested on A.1x or original A series range of amplifiers (testers welcome)
435
436
  * VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
436
437
  * Not all sources set the volume to 0 when the zone is muted
437
438
  * Grouping feedback is flaky on the X series amplifiers
439
+ * Airplay `Zone.track.progress` is not avaiable.
438
440
  * Cant stop a URL playback, feedback is worng at least
439
441
  * 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
440
442
  * `stop()` is intended to disconnect the client and pause the stream. Doesnt always function this way, depending on stream source
@@ -442,8 +444,8 @@ The VSSL API was reverse engineered using Wireshark, VSSLs native "legacy" iOS a
442
444
 
443
445
  ## Future
444
446
 
447
+ * A.1(x) coverage i.e Bluetooth and subwoofer control
445
448
  * REST API / Web App
446
449
  * Save and recall EQ
447
- * A.1(x) coverage i.e Bluetooth
448
450
  * IR Control
449
451
 
File without changes
File without changes