vsslctrl 0.1.5.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.
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/PKG-INFO +47 -48
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/README.md +45 -46
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/pyproject.toml +2 -2
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/api_alpha.py +8 -6
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/api_bravo.py +5 -3
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/core.py +1 -1
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/data_structure.py +6 -1
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/utils.py +13 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/zone.py +5 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/PKG-INFO +47 -48
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/LICENSE +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/setup.cfg +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/tests/test_event_bus.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/tests/test_integration.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/tests/test_settings.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/tests/test_zone.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/__init__.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/api_base.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/decorators.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/device.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/discovery.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/event_bus.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/exceptions.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/group.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/io.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/settings.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/track.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl/transport.py +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/SOURCES.txt +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/dependency_links.txt +0 -0
- {vsslctrl-0.1.5.dev1 → vsslctrl-0.1.6.dev1}/vsslctrl.egg-info/requires.txt +0 -0
- {vsslctrl-0.1.5.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
|
-
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,31 +20,30 @@ Provides-Extra: optional
|
|
|
20
20
|
Requires-Dist: zeroconf==0.132.2; extra == "optional"
|
|
21
21
|
|
|
22
22
|
# vsslctrl
|
|
23
|
-
|
|
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
|
-
|
|
28
|
+
## Help
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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**
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
## Important
|
|
36
37
|
|
|
37
|
-
|
|
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.
|
|
38
39
|
|
|
39
40
|
## TODOs
|
|
40
41
|
|
|
41
|
-
*
|
|
42
|
-
* Home Assistant integration. In progress, [here](https://github.com/vsslctrl/integration.home-assistant)
|
|
43
|
-
* ~~Function scoping to supported feature / models~~ (needs tests)
|
|
42
|
+
* **A1(.x)** specific control e.g sub crossover, bluetooth
|
|
44
43
|
* Better test coverage
|
|
45
44
|
|
|
46
|
-
Basic Usage
|
|
47
|
-
|
|
45
|
+
## Basic Usage
|
|
46
|
+
|
|
48
47
|
`vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
|
|
49
48
|
|
|
50
49
|
```python
|
|
@@ -91,8 +90,7 @@ async def main():
|
|
|
91
90
|
asyncio.run(main())
|
|
92
91
|
```
|
|
93
92
|
|
|
94
|
-
API
|
|
95
|
-
-----------
|
|
93
|
+
# API
|
|
96
94
|
|
|
97
95
|
Most functionality is achived via `getters` and `setters` of the two main classes `Vssl`, `Zone`.
|
|
98
96
|
|
|
@@ -284,10 +282,10 @@ zone1.group.dissolve()
|
|
|
284
282
|
|
|
285
283
|
## `Zone.analog_output`
|
|
286
284
|
|
|
287
|
-
| Property | Description | Type | Values |
|
|
288
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
289
|
-
| `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources`
|
|
290
|
-
| `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool`
|
|
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`
|
|
291
289
|
| `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
|
|
292
290
|
|
|
293
291
|
```python
|
|
@@ -304,12 +302,12 @@ zone1.analog_output.is_fixed_volume = True
|
|
|
304
302
|
|
|
305
303
|
## `Zone.settings`
|
|
306
304
|
|
|
307
|
-
| Property | Description | Type | Values |
|
|
308
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
305
|
+
| Property | Description | Type | Values | Default |
|
|
306
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
309
307
|
| `name` | Name | `str` |
|
|
310
|
-
| `disabled` | Disable the zone | `bool`
|
|
308
|
+
| `disabled` | Disable the zone | `bool` || `False`
|
|
311
309
|
| `disabled_toggle()` | disable / enable | `func` |
|
|
312
|
-
| `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono`
|
|
310
|
+
| `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono` | `Stereo`
|
|
313
311
|
| `mono_toggle()` | Toggle mono or stereo | `func` |
|
|
314
312
|
|
|
315
313
|
```python
|
|
@@ -324,10 +322,10 @@ zone1.mono_toggle()
|
|
|
324
322
|
|
|
325
323
|
## `Zone.settings.analog_input`
|
|
326
324
|
|
|
327
|
-
| Property | Description | Type | Values |
|
|
328
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
325
|
+
| Property | Description | Type | Values | Default |
|
|
326
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
329
327
|
| `name` | Name | `str` |
|
|
330
|
-
| `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
|
|
331
329
|
|
|
332
330
|
```python
|
|
333
331
|
"""Examples"""
|
|
@@ -341,11 +339,11 @@ zone1.settings.analog_input.fixed_gain = 50
|
|
|
341
339
|
|
|
342
340
|
## `Zone.settings.volume`
|
|
343
341
|
|
|
344
|
-
| Property | Description | Type | Values |
|
|
345
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
346
|
-
| `default_on` | Default on volume | `int` | `0...100`
|
|
347
|
-
| `max_left` | Max volume left channel | `int` | `0...100`
|
|
348
|
-
| `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`
|
|
349
347
|
|
|
350
348
|
```python
|
|
351
349
|
"""Examples"""
|
|
@@ -357,21 +355,21 @@ zone1.settings.volume.default_on = 75
|
|
|
357
355
|
|
|
358
356
|
## `Zone.settings.eq`
|
|
359
357
|
|
|
360
|
-
| Property | Description | Type | Values |
|
|
361
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
362
|
-
| `enabled` | Enable / disable EQ | `bool`
|
|
358
|
+
| Property | Description | Type | Values | Default |
|
|
359
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
360
|
+
| `enabled` | Enable / disable EQ | `bool` | | `False`
|
|
363
361
|
|
|
364
362
|
EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `-10`dB to `+10`dB
|
|
365
363
|
|
|
366
|
-
| Property | Description | Type | Values |
|
|
367
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
368
|
-
| `hz60_db` | 60Hz | `int` | `-10...10`
|
|
369
|
-
| `hz200_db` | 200Hz | `int` | `-10...10`
|
|
370
|
-
| `hz500_db` | 500Hz | `int` | `-10...10`
|
|
371
|
-
| `khz1_db` | 1kHz | `int` | `-10...10`
|
|
372
|
-
| `khz4_db` | 4kHz | `int` | `-10...10`
|
|
373
|
-
| `khz8_db` | 8kHz | `int` | `-10...10`
|
|
374
|
-
| `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`
|
|
375
373
|
|
|
376
374
|
```python
|
|
377
375
|
"""Examples"""
|
|
@@ -430,10 +428,11 @@ shell_command:
|
|
|
430
428
|
|
|
431
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).
|
|
432
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
|
+
|
|
433
433
|
## Known Issues & Limitiations
|
|
434
434
|
|
|
435
|
-
*
|
|
436
|
-
* 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)
|
|
437
436
|
* VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
|
|
438
437
|
* Not all sources set the volume to 0 when the zone is muted
|
|
439
438
|
* Grouping feedback is flaky on the X series amplifiers
|
|
@@ -445,8 +444,8 @@ The VSSL API was reverse engineered using Wireshark, VSSLs native "legacy" iOS a
|
|
|
445
444
|
|
|
446
445
|
## Future
|
|
447
446
|
|
|
447
|
+
* A.1(x) coverage i.e Bluetooth and subwoofer control
|
|
448
448
|
* REST API / Web App
|
|
449
449
|
* Save and recall EQ
|
|
450
|
-
* A.1(x) coverage i.e Bluetooth
|
|
451
450
|
* IR Control
|
|
452
451
|
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
# vsslctrl
|
|
2
|
-
|
|
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
|
-
|
|
7
|
+
## Help
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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**
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
## Important
|
|
15
16
|
|
|
16
|
-
|
|
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.
|
|
17
18
|
|
|
18
19
|
## TODOs
|
|
19
20
|
|
|
20
|
-
*
|
|
21
|
-
* Home Assistant integration. In progress, [here](https://github.com/vsslctrl/integration.home-assistant)
|
|
22
|
-
* ~~Function scoping to supported feature / models~~ (needs tests)
|
|
21
|
+
* **A1(.x)** specific control e.g sub crossover, bluetooth
|
|
23
22
|
* Better test coverage
|
|
24
23
|
|
|
25
|
-
Basic Usage
|
|
26
|
-
|
|
24
|
+
## Basic Usage
|
|
25
|
+
|
|
27
26
|
`vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
|
|
28
27
|
|
|
29
28
|
```python
|
|
@@ -70,8 +69,7 @@ async def main():
|
|
|
70
69
|
asyncio.run(main())
|
|
71
70
|
```
|
|
72
71
|
|
|
73
|
-
API
|
|
74
|
-
-----------
|
|
72
|
+
# API
|
|
75
73
|
|
|
76
74
|
Most functionality is achived via `getters` and `setters` of the two main classes `Vssl`, `Zone`.
|
|
77
75
|
|
|
@@ -263,10 +261,10 @@ zone1.group.dissolve()
|
|
|
263
261
|
|
|
264
262
|
## `Zone.analog_output`
|
|
265
263
|
|
|
266
|
-
| Property | Description | Type | Values |
|
|
267
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
268
|
-
| `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources`
|
|
269
|
-
| `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool`
|
|
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`
|
|
270
268
|
| `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
|
|
271
269
|
|
|
272
270
|
```python
|
|
@@ -283,12 +281,12 @@ zone1.analog_output.is_fixed_volume = True
|
|
|
283
281
|
|
|
284
282
|
## `Zone.settings`
|
|
285
283
|
|
|
286
|
-
| Property | Description | Type | Values |
|
|
287
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
284
|
+
| Property | Description | Type | Values | Default |
|
|
285
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
288
286
|
| `name` | Name | `str` |
|
|
289
|
-
| `disabled` | Disable the zone | `bool`
|
|
287
|
+
| `disabled` | Disable the zone | `bool` || `False`
|
|
290
288
|
| `disabled_toggle()` | disable / enable | `func` |
|
|
291
|
-
| `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono`
|
|
289
|
+
| `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono` | `Stereo`
|
|
292
290
|
| `mono_toggle()` | Toggle mono or stereo | `func` |
|
|
293
291
|
|
|
294
292
|
```python
|
|
@@ -303,10 +301,10 @@ zone1.mono_toggle()
|
|
|
303
301
|
|
|
304
302
|
## `Zone.settings.analog_input`
|
|
305
303
|
|
|
306
|
-
| Property | Description | Type | Values |
|
|
307
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
304
|
+
| Property | Description | Type | Values | Default |
|
|
305
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
308
306
|
| `name` | Name | `str` |
|
|
309
|
-
| `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
|
|
310
308
|
|
|
311
309
|
```python
|
|
312
310
|
"""Examples"""
|
|
@@ -320,11 +318,11 @@ zone1.settings.analog_input.fixed_gain = 50
|
|
|
320
318
|
|
|
321
319
|
## `Zone.settings.volume`
|
|
322
320
|
|
|
323
|
-
| Property | Description | Type | Values |
|
|
324
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
325
|
-
| `default_on` | Default on volume | `int` | `0...100`
|
|
326
|
-
| `max_left` | Max volume left channel | `int` | `0...100`
|
|
327
|
-
| `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`
|
|
328
326
|
|
|
329
327
|
```python
|
|
330
328
|
"""Examples"""
|
|
@@ -336,21 +334,21 @@ zone1.settings.volume.default_on = 75
|
|
|
336
334
|
|
|
337
335
|
## `Zone.settings.eq`
|
|
338
336
|
|
|
339
|
-
| Property | Description | Type | Values |
|
|
340
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
341
|
-
| `enabled` | Enable / disable EQ | `bool`
|
|
337
|
+
| Property | Description | Type | Values | Default |
|
|
338
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
339
|
+
| `enabled` | Enable / disable EQ | `bool` | | `False`
|
|
342
340
|
|
|
343
341
|
EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `-10`dB to `+10`dB
|
|
344
342
|
|
|
345
|
-
| Property | Description | Type | Values |
|
|
346
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
347
|
-
| `hz60_db` | 60Hz | `int` | `-10...10`
|
|
348
|
-
| `hz200_db` | 200Hz | `int` | `-10...10`
|
|
349
|
-
| `hz500_db` | 500Hz | `int` | `-10...10`
|
|
350
|
-
| `khz1_db` | 1kHz | `int` | `-10...10`
|
|
351
|
-
| `khz4_db` | 4kHz | `int` | `-10...10`
|
|
352
|
-
| `khz8_db` | 8kHz | `int` | `-10...10`
|
|
353
|
-
| `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`
|
|
354
352
|
|
|
355
353
|
```python
|
|
356
354
|
"""Examples"""
|
|
@@ -409,10 +407,11 @@ shell_command:
|
|
|
409
407
|
|
|
410
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).
|
|
411
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
|
+
|
|
412
412
|
## Known Issues & Limitiations
|
|
413
413
|
|
|
414
|
-
*
|
|
415
|
-
* 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)
|
|
416
415
|
* VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
|
|
417
416
|
* Not all sources set the volume to 0 when the zone is muted
|
|
418
417
|
* Grouping feedback is flaky on the X series amplifiers
|
|
@@ -424,8 +423,8 @@ The VSSL API was reverse engineered using Wireshark, VSSLs native "legacy" iOS a
|
|
|
424
423
|
|
|
425
424
|
## Future
|
|
426
425
|
|
|
426
|
+
* A.1(x) coverage i.e Bluetooth and subwoofer control
|
|
427
427
|
* REST API / Web App
|
|
428
428
|
* Save and recall EQ
|
|
429
|
-
* A.1(x) coverage i.e Bluetooth
|
|
430
429
|
* IR Control
|
|
431
430
|
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vsslctrl"
|
|
7
|
-
version = "0.1.
|
|
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(
|
|
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
|
-
#
|
|
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: {
|
|
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 {
|
|
1041
|
+
f"Received unknown command: {hex_to_bytearray_string(cmd)} Hex: {cmd}"
|
|
1040
1042
|
)
|
|
@@ -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 =
|
|
411
|
-
self._log_debug(
|
|
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
|
|
@@ -253,7 +253,12 @@ class ZoneRouterStatusExtKeys:
|
|
|
253
253
|
EQ_ENABLED = "eqsw"
|
|
254
254
|
INPUT_SOURCE = "inSrc"
|
|
255
255
|
SOURCE_PRIORITY = "SP"
|
|
256
|
-
|
|
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"
|
|
257
262
|
GROUP_SOURCE = "GRS"
|
|
258
263
|
GROUP_MASTER = "GRM"
|
|
259
264
|
POWER_STATE = "Pwr"
|
|
@@ -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
|
-
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,31 +20,30 @@ Provides-Extra: optional
|
|
|
20
20
|
Requires-Dist: zeroconf==0.132.2; extra == "optional"
|
|
21
21
|
|
|
22
22
|
# vsslctrl
|
|
23
|
-
|
|
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
|
-
|
|
28
|
+
## Help
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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**
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
## Important
|
|
36
37
|
|
|
37
|
-
|
|
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.
|
|
38
39
|
|
|
39
40
|
## TODOs
|
|
40
41
|
|
|
41
|
-
*
|
|
42
|
-
* Home Assistant integration. In progress, [here](https://github.com/vsslctrl/integration.home-assistant)
|
|
43
|
-
* ~~Function scoping to supported feature / models~~ (needs tests)
|
|
42
|
+
* **A1(.x)** specific control e.g sub crossover, bluetooth
|
|
44
43
|
* Better test coverage
|
|
45
44
|
|
|
46
|
-
Basic Usage
|
|
47
|
-
|
|
45
|
+
## Basic Usage
|
|
46
|
+
|
|
48
47
|
`vsslctrl` needs to be running inside a **[asyncio](https://docs.python.org/3/library/asyncio.html)** event loop.
|
|
49
48
|
|
|
50
49
|
```python
|
|
@@ -91,8 +90,7 @@ async def main():
|
|
|
91
90
|
asyncio.run(main())
|
|
92
91
|
```
|
|
93
92
|
|
|
94
|
-
API
|
|
95
|
-
-----------
|
|
93
|
+
# API
|
|
96
94
|
|
|
97
95
|
Most functionality is achived via `getters` and `setters` of the two main classes `Vssl`, `Zone`.
|
|
98
96
|
|
|
@@ -284,10 +282,10 @@ zone1.group.dissolve()
|
|
|
284
282
|
|
|
285
283
|
## `Zone.analog_output`
|
|
286
284
|
|
|
287
|
-
| Property | Description | Type | Values |
|
|
288
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
289
|
-
| `source` | Where the AO is routed from. i.e a zone, optical input or off | `int` | `AnalogOutput.Sources`
|
|
290
|
-
| `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool`
|
|
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`
|
|
291
289
|
| `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
|
|
292
290
|
|
|
293
291
|
```python
|
|
@@ -304,12 +302,12 @@ zone1.analog_output.is_fixed_volume = True
|
|
|
304
302
|
|
|
305
303
|
## `Zone.settings`
|
|
306
304
|
|
|
307
|
-
| Property | Description | Type | Values |
|
|
308
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
305
|
+
| Property | Description | Type | Values | Default |
|
|
306
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
309
307
|
| `name` | Name | `str` |
|
|
310
|
-
| `disabled` | Disable the zone | `bool`
|
|
308
|
+
| `disabled` | Disable the zone | `bool` || `False`
|
|
311
309
|
| `disabled_toggle()` | disable / enable | `func` |
|
|
312
|
-
| `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono`
|
|
310
|
+
| `mono` | Set output to mono or stereo | `int` | `ZoneSettings.StereoMono` | `Stereo`
|
|
313
311
|
| `mono_toggle()` | Toggle mono or stereo | `func` |
|
|
314
312
|
|
|
315
313
|
```python
|
|
@@ -324,10 +322,10 @@ zone1.mono_toggle()
|
|
|
324
322
|
|
|
325
323
|
## `Zone.settings.analog_input`
|
|
326
324
|
|
|
327
|
-
| Property | Description | Type | Values |
|
|
328
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
325
|
+
| Property | Description | Type | Values | Default |
|
|
326
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
329
327
|
| `name` | Name | `str` |
|
|
330
|
-
| `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
|
|
331
329
|
|
|
332
330
|
```python
|
|
333
331
|
"""Examples"""
|
|
@@ -341,11 +339,11 @@ zone1.settings.analog_input.fixed_gain = 50
|
|
|
341
339
|
|
|
342
340
|
## `Zone.settings.volume`
|
|
343
341
|
|
|
344
|
-
| Property | Description | Type | Values |
|
|
345
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
346
|
-
| `default_on` | Default on volume | `int` | `0...100`
|
|
347
|
-
| `max_left` | Max volume left channel | `int` | `0...100`
|
|
348
|
-
| `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`
|
|
349
347
|
|
|
350
348
|
```python
|
|
351
349
|
"""Examples"""
|
|
@@ -357,21 +355,21 @@ zone1.settings.volume.default_on = 75
|
|
|
357
355
|
|
|
358
356
|
## `Zone.settings.eq`
|
|
359
357
|
|
|
360
|
-
| Property | Description | Type | Values |
|
|
361
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
362
|
-
| `enabled` | Enable / disable EQ | `bool`
|
|
358
|
+
| Property | Description | Type | Values | Default |
|
|
359
|
+
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
360
|
+
| `enabled` | Enable / disable EQ | `bool` | | `False`
|
|
363
361
|
|
|
364
362
|
EQ to be set in [decibel](https://en.wikipedia.org/wiki/Decibel) using a range `-10`dB to `+10`dB
|
|
365
363
|
|
|
366
|
-
| Property | Description | Type | Values |
|
|
367
|
-
| ---------------------- | ----------- | ----------- |----------- |
|
|
368
|
-
| `hz60_db` | 60Hz | `int` | `-10...10`
|
|
369
|
-
| `hz200_db` | 200Hz | `int` | `-10...10`
|
|
370
|
-
| `hz500_db` | 500Hz | `int` | `-10...10`
|
|
371
|
-
| `khz1_db` | 1kHz | `int` | `-10...10`
|
|
372
|
-
| `khz4_db` | 4kHz | `int` | `-10...10`
|
|
373
|
-
| `khz8_db` | 8kHz | `int` | `-10...10`
|
|
374
|
-
| `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`
|
|
375
373
|
|
|
376
374
|
```python
|
|
377
375
|
"""Examples"""
|
|
@@ -430,10 +428,11 @@ shell_command:
|
|
|
430
428
|
|
|
431
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).
|
|
432
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
|
+
|
|
433
433
|
## Known Issues & Limitiations
|
|
434
434
|
|
|
435
|
-
*
|
|
436
|
-
* 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)
|
|
437
436
|
* VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
|
|
438
437
|
* Not all sources set the volume to 0 when the zone is muted
|
|
439
438
|
* Grouping feedback is flaky on the X series amplifiers
|
|
@@ -445,8 +444,8 @@ The VSSL API was reverse engineered using Wireshark, VSSLs native "legacy" iOS a
|
|
|
445
444
|
|
|
446
445
|
## Future
|
|
447
446
|
|
|
447
|
+
* A.1(x) coverage i.e Bluetooth and subwoofer control
|
|
448
448
|
* REST API / Web App
|
|
449
449
|
* Save and recall EQ
|
|
450
|
-
* A.1(x) coverage i.e Bluetooth
|
|
451
450
|
* IR Control
|
|
452
451
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|