vsslctrl 0.1.10.dev1__tar.gz → 0.1.12.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.10.dev1 → vsslctrl-0.1.12.dev1}/PKG-INFO +86 -91
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/README.md +85 -90
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/pyproject.toml +1 -1
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/tests/test_integration.py +364 -277
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/tests/test_settings.py +3 -2
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/tests/test_zone.py +3 -3
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/__init__.py +1 -1
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/api_alpha.py +357 -253
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/core.py +84 -97
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/data_structure.py +64 -18
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/decorators.py +54 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/device.py +70 -15
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/event_bus.py +4 -4
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/group.py +4 -6
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/io.py +88 -23
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/settings.py +36 -15
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/track.py +3 -4
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/transport.py +1 -1
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/utils.py +11 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/zone.py +25 -11
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl.egg-info/PKG-INFO +86 -91
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/LICENSE +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/setup.cfg +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/tests/test_event_bus.py +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/api_base.py +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/api_bravo.py +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/discovery.py +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl/exceptions.py +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl.egg-info/SOURCES.txt +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl.egg-info/dependency_links.txt +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.dev1}/vsslctrl.egg-info/requires.txt +0 -0
- {vsslctrl-0.1.10.dev1 → vsslctrl-0.1.12.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.
|
|
3
|
+
Version: 0.1.12.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
|
|
@@ -21,28 +21,44 @@ Requires-Dist: zeroconf==0.132.2; extra == "optional"
|
|
|
21
21
|
|
|
22
22
|
# vsslctrl
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Package for controlling [VSSL's](https://www.vssl.com/) range of streaming amplifiers.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
## Coverage
|
|
27
|
+
|
|
28
|
+
Tested on:
|
|
29
|
+
|
|
30
|
+
| Model | Software Version | Note |
|
|
31
|
+
| ------------|--------- | -------------
|
|
32
|
+
| A.3x | p15305.016.3701 |
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) with basic functionality working on:
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
| Model | Software Version | User Reported |
|
|
38
|
+
| ------------|--------- | -------------
|
|
39
|
+
| A.1 | p15265.033.3703 | ✔️
|
|
40
|
+
| A.3 | p12013.141.3703 | ✔️
|
|
41
|
+
| A.3x | p15305.016.3701 |
|
|
42
|
+
| A.6x | p15305.017.3701 | ✔️
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Testers Needed
|
|
29
46
|
|
|
30
47
|
I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
|
|
31
48
|
|
|
32
|
-
|
|
33
|
-
- **A.1** software version **p15265.033.3703** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
34
|
-
- **A.3** software version **p12013.141.3703** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
35
|
-
- **A.3x** software version **p15305.016.3701**
|
|
36
|
-
- **A.6x** software version **p15305.017.3701** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
49
|
+
|
|
37
50
|
|
|
38
51
|
## Important
|
|
39
52
|
|
|
40
|
-
There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* running on the
|
|
53
|
+
There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* running on the network. If you dont know what this is, then you can ignore this notice.
|
|
54
|
+
|
|
55
|
+
**`vsslctrl` is not endorsed or affiliated with [VSSL](https://www.vssl.com/) in any manner.**
|
|
41
56
|
|
|
42
57
|
## TODOs
|
|
43
58
|
|
|
44
|
-
* **A.1(x)** specific control e.g bluetooth
|
|
45
59
|
* Better test coverage
|
|
60
|
+
* A.1(x) testing - e.g output settings
|
|
61
|
+
* More controls - e.g IR Control
|
|
46
62
|
|
|
47
63
|
## Basic Usage
|
|
48
64
|
|
|
@@ -58,7 +74,7 @@ async def main():
|
|
|
58
74
|
|
|
59
75
|
# Represents a physical VSSL amplifier
|
|
60
76
|
vssl = Vssl(DeviceModels.A1)
|
|
61
|
-
a1 = vssl.add_zone(
|
|
77
|
+
a1 = vssl.add_zone('192.168.1.10')
|
|
62
78
|
|
|
63
79
|
# Connect and initiate zones.
|
|
64
80
|
await vssl.initialise()
|
|
@@ -88,14 +104,13 @@ from vsslctrl import Vssl, DeviceModels, Zone, ZoneIDs
|
|
|
88
104
|
async def main():
|
|
89
105
|
|
|
90
106
|
# Represents a physical VSSL amplifier
|
|
91
|
-
# If no DeviceModels is passed, vsslctrl will default to the feature set of the X series amps
|
|
92
107
|
vssl = Vssl(DeviceModels.A3X)
|
|
93
108
|
|
|
94
109
|
# Add each you wish to control
|
|
95
|
-
zone1 = vssl.add_zone(
|
|
96
|
-
zone2 = vssl.add_zone(
|
|
97
|
-
zone3 = vssl.add_zone(
|
|
98
|
-
#... up to 6 zones
|
|
110
|
+
zone1 = vssl.add_zone('192.168.1.10', ZoneIDs.ZONE_1)
|
|
111
|
+
zone2 = vssl.add_zone('192.168.1.11', ZoneIDs.ZONE_2)
|
|
112
|
+
zone3 = vssl.add_zone('192.168.1.12', ZoneIDs.ZONE_3)
|
|
113
|
+
#... up to 6 zones for A.6(x)
|
|
99
114
|
|
|
100
115
|
# Connect and initiate zones.
|
|
101
116
|
await vssl.initialise()
|
|
@@ -119,9 +134,36 @@ async def main():
|
|
|
119
134
|
asyncio.run(main())
|
|
120
135
|
```
|
|
121
136
|
|
|
137
|
+
# `DeviceModels`
|
|
138
|
+
|
|
139
|
+
A device model has to be passed to VSSL so it knows internally what features are supported by the device.
|
|
140
|
+
This might be removed in the future if we can differentiate different models from the API.
|
|
141
|
+
|
|
142
|
+
| Property | Description |
|
|
143
|
+
| ------------|--------- |
|
|
144
|
+
| `A1X` | A.1x |
|
|
145
|
+
| `A3X` | A.3x |
|
|
146
|
+
| `A6X` | A.6x |
|
|
147
|
+
| `A1` | A1 |
|
|
148
|
+
| `A3` | A3 |
|
|
149
|
+
| `A6` | A6 |
|
|
150
|
+
|
|
151
|
+
# `ZoneIDs`
|
|
152
|
+
|
|
153
|
+
| Property | Description |
|
|
154
|
+
| ------------|--------- |
|
|
155
|
+
| `A1` | A.1(x) Only |
|
|
156
|
+
| `ZONE_1` | Zone 1 of A.3(x) and A.6(x) |
|
|
157
|
+
| `ZONE_2` | Zone 2 of A.3(x) and A.6(x) |
|
|
158
|
+
| `ZONE_3` | Zone 3 of A.3(x) and A.6(x) |
|
|
159
|
+
| `ZONE_4` | Zone 4 of A.6(x) |
|
|
160
|
+
| `ZONE_5` | Zone 5 of A.6(x) |
|
|
161
|
+
| `ZONE_6` | Zone 6 of A.6(x) |
|
|
162
|
+
|
|
163
|
+
|
|
122
164
|
# API
|
|
123
165
|
|
|
124
|
-
Most functionality is
|
|
166
|
+
Most functionality is achieved via `getters` and `setters` of the two main classes `Vssl`, `Zone`.
|
|
125
167
|
|
|
126
168
|
The classes will update the physical VSSL device when setting a property and once feedback has been received, the classes internal state will be updated. For example:
|
|
127
169
|
|
|
@@ -138,7 +180,6 @@ print(zone_name)
|
|
|
138
180
|
|
|
139
181
|
**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.
|
|
140
182
|
|
|
141
|
-
|
|
142
183
|
# `Vssl`
|
|
143
184
|
|
|
144
185
|
| Property | Description | Type |
|
|
@@ -160,19 +201,20 @@ vssl.factory_reset()
|
|
|
160
201
|
|
|
161
202
|
## `Vssl.settings`
|
|
162
203
|
|
|
163
|
-
| Property | Description | Type |
|
|
164
|
-
| ---------------------- | ----------- | ----------- |
|
|
165
|
-
| `name` | Device name | `str`
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
204
|
+
| Property | Description | Type | Model: Default |
|
|
205
|
+
| ---------------------- | ----------- | ----------- | ----------- |
|
|
206
|
+
| `name` | Device name | `str` |
|
|
207
|
+
| `bus_1_name` | Name of Bus 1 | `str` | <ul><li>A.1: Optical Input</li><li>A.3x/6x: Not Used</li></ul>
|
|
208
|
+
| `bus_2_name` | Name of Bus 2 | `str` | <ul><li>A.1: Coax Input</li><li>A.3x/6x: Optical Input</li></ul>
|
|
209
|
+
| `bluetooth` | Bluetooth enabled / disabled | `bool` |
|
|
168
210
|
| `bluetooth_toggle()` | Toggle Bluetooth | `func` |
|
|
169
211
|
|
|
170
212
|
```python
|
|
171
213
|
"""Example"""
|
|
172
214
|
# Setting device name
|
|
173
215
|
vssl.settings.name = 'My House'
|
|
174
|
-
# Setting
|
|
175
|
-
vssl.settings.
|
|
216
|
+
# Setting bus 2 name
|
|
217
|
+
vssl.settings.bus_2_name = 'Optical Input'
|
|
176
218
|
# Enable Bluetooth
|
|
177
219
|
vssl.settings.bluetooth = True
|
|
178
220
|
# Toggle Bluetooth
|
|
@@ -283,28 +325,38 @@ zone1.transport.state = ZoneTransport.States.PAUSE
|
|
|
283
325
|
|
|
284
326
|
| Property | Description | Type | Values |
|
|
285
327
|
| ---------------------- | ----------- | ----------- |----------- |
|
|
286
|
-
| `source` | Change input source | `int` | `InputRouter.Sources`
|
|
287
|
-
| `priority` | Change input priority
|
|
328
|
+
| `source` | Change input source.<br/>Source to be played out the zones speakers | `int` | `InputRouter.Sources`
|
|
329
|
+
| `priority` | Change input priority.<br/>Stream or local to have precedence | `int` | `InputRouter.Priorities`
|
|
288
330
|
|
|
289
|
-
|
|
331
|
+
|
|
332
|
+
### A.1(x) Source Routing Order
|
|
333
|
+
A.1 and A.1x don't support manually changing the input source. Instead a fixed source routing order is used:
|
|
334
|
+
|
|
335
|
+
1. Optical Input
|
|
336
|
+
2. Coaxial Input
|
|
337
|
+
3. Analog Input
|
|
338
|
+
|
|
339
|
+
Input `InputRouter.Priorities` still apply.
|
|
340
|
+
|
|
341
|
+
### Input Priority / Precedence
|
|
290
342
|
|
|
291
343
|
| `InputRouter.Priorities` | Priority Order |
|
|
292
344
|
| ---------------------- | ----------- |
|
|
293
|
-
| **`STREAM`** | <ol><li>Stream</li><li>Party Zone</li><li>Bus 1
|
|
294
|
-
| **`LOCAL`** | <ol><li>Bus 1
|
|
345
|
+
| **`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>
|
|
346
|
+
| **`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>
|
|
295
347
|
|
|
296
348
|
```python
|
|
297
349
|
"""Example"""
|
|
298
350
|
# Change zone 1 to listen to analog input 4
|
|
299
351
|
zone1.input.source = InputRouter.Sources.ANALOG_IN_4
|
|
300
352
|
|
|
301
|
-
# Change zone 1 to perfer
|
|
353
|
+
# Change zone 1 to perfer local inputs over stream
|
|
302
354
|
zone1.input.priority = InputRouter.Priorities.LOCAL
|
|
303
355
|
```
|
|
304
356
|
|
|
305
357
|
## `Zone.group`
|
|
306
358
|
|
|
307
|
-
|
|
359
|
+
Unsupported on X series amplifiers.
|
|
308
360
|
|
|
309
361
|
| Property | Description | Type | Values |
|
|
310
362
|
| ---------------------- | ----------- | ----------- |----------- |
|
|
@@ -335,7 +387,7 @@ zone1.group.is_party_zone_member_toggle()
|
|
|
335
387
|
|
|
336
388
|
| Property | Description | Type | Values | Default |
|
|
337
389
|
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
338
|
-
| `source` | Where the AO is routed from. i.e
|
|
390
|
+
| `source` | Where the AO is routed from. i.e stream, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
|
|
339
391
|
| `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` | |`False`
|
|
340
392
|
| `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
|
|
341
393
|
|
|
@@ -443,55 +495,6 @@ zone1.settings.eq.khz1_db = -2
|
|
|
443
495
|
zone1.settings.subwoofer.crossover = 100
|
|
444
496
|
```
|
|
445
497
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
## Another (Lite) Way
|
|
449
|
-
|
|
450
|
-
If you perfer to not run the complete intergration, you can send basic HEX commands to the VSSL device using [Netcat](https://nc110.sourceforge.io/) (or any network tool) on port `50002`.
|
|
451
|
-
|
|
452
|
-
| HEX | Description |
|
|
453
|
-
| ---------------------- | ----------- |
|
|
454
|
-
| `\x10\x05\x03\x0{Zone Number}\xff\x03` | Volume Up
|
|
455
|
-
| `\x10\x05\x03\x0{Zone Number}\xfe\x03` | Volume Down
|
|
456
|
-
| `\x10\x11\x02\x0{Zone Number}\x01` | Mute
|
|
457
|
-
| All commands can be found by looking [here](https://github.com/vsslctrl/vsslctrl/blob/2c43c2f2393b94bc0e062d2ab90144343eca16ef/vsslctrl/api_alpha.py) |
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
The `volume up` HEX command for `Zone 2` would be `\x10\x05\x03\x02\xff\x03`
|
|
461
|
-
|
|
462
|
-
Now send the raw HEX using Netcat to the device using this syntax:
|
|
463
|
-
|
|
464
|
-
`echo -e "{HEX Command}" | nc {IP Address} 50002`
|
|
465
|
-
|
|
466
|
-
For example to send `volume up` to `Zone 2`:
|
|
467
|
-
|
|
468
|
-
`echo -e "\x10\x05\x03\x02\xff\x03" | nc 192.168.1.11 50002`
|
|
469
|
-
|
|
470
|
-
Home Assistant `Configuration.yaml` example:
|
|
471
|
-
|
|
472
|
-
```ymal
|
|
473
|
-
...
|
|
474
|
-
|
|
475
|
-
shell_command:
|
|
476
|
-
#Zone 1
|
|
477
|
-
vssl_zone_1_volume_up: 'echo -e "\x10\x05\x03\x01\xff\x03" | nc 192.168.1.10 50002'
|
|
478
|
-
vssl_zone_1_volume_down: 'echo -e "\x10\x05\x03\x01\xfe\x03" | nc 192.168.1.10 50002'
|
|
479
|
-
vssl_zone_1_mute: 'echo -e "\x10\x11\x02\x01\x01" | nc 192.168.1.10 50002'
|
|
480
|
-
vssl_zone_1_unmute: 'echo -e "\x10\x11\x02\x01\x00" | nc 192.168.1.10 50002'
|
|
481
|
-
#Zone 2
|
|
482
|
-
vssl_zone_2_volume_up: 'echo -e "\x10\x05\x03\x02\xff\x03" | nc 192.168.1.11 50002'
|
|
483
|
-
vssl_zone_2_volume_down: 'echo -e "\x10\x05\x03\x02\xfe\x03" | nc 192.168.1.11 50002'
|
|
484
|
-
vssl_zone_2_mute: 'echo -e "\x10\x11\x02\x02\x01" | nc 192.168.1.11 50002'
|
|
485
|
-
vssl_zone_2_unmute: 'echo -e "\x10\x11\x02\x02\x00" | nc 192.168.1.11 50002'
|
|
486
|
-
#Zone 3
|
|
487
|
-
vssl_zone_3_volume_up: 'echo -e "\x10\x05\x03\x03\xff\x03" | nc 192.168.1.12 50002'
|
|
488
|
-
vssl_zone_3_volume_down: 'echo -e "\x10\x05\x03\x03\xfe\x03" | nc 192.168.1.12 50002'
|
|
489
|
-
vssl_zone_3_mute: 'echo -e "\x10\x11\x02\x03\x01" | nc 192.168.1.12 50002'
|
|
490
|
-
vssl_zone_3_unmute: 'echo -e "\x10\x11\x02\x03\x00" | nc 192.168.1.12 50002'
|
|
491
|
-
|
|
492
|
-
...
|
|
493
|
-
```
|
|
494
|
-
|
|
495
498
|
## Credit
|
|
496
499
|
|
|
497
500
|
Thanks to [@dj-jam](https://github.com/dj-jam) for the continued testing.
|
|
@@ -505,17 +508,9 @@ Motivation for this project was to integrate VSSLs amplifiers into [Home Assista
|
|
|
505
508
|
* Not tested on A.1x or original A series range of amplifiers (testers welcome)
|
|
506
509
|
* VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
|
|
507
510
|
* Not all sources set the volume to 0 when the zone is muted
|
|
508
|
-
* Grouping feedback is flaky on the X series amplifiers
|
|
509
511
|
* Airplay `Zone.track.progress` is not available.
|
|
510
512
|
* Cant stop a URL playback, feedback is worng at least
|
|
511
513
|
* 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
|
|
512
514
|
* `stop()` is intended to disconnect the client and pause the stream. Doesn’t always function this way, depending on stream source
|
|
513
515
|
* Occasionally a zones might stop responding to certain commands, issuing the `reboot` command generally corrects
|
|
514
516
|
|
|
515
|
-
## Future
|
|
516
|
-
|
|
517
|
-
* A.1(x) coverage i.e Bluetooth
|
|
518
|
-
* REST API / Web App
|
|
519
|
-
* Save and recall EQ
|
|
520
|
-
* IR Control
|
|
521
|
-
|
|
@@ -1,27 +1,43 @@
|
|
|
1
1
|
# vsslctrl
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Package for controlling [VSSL's](https://www.vssl.com/) range of streaming amplifiers.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Coverage
|
|
6
|
+
|
|
7
|
+
Tested on:
|
|
8
|
+
|
|
9
|
+
| Model | Software Version | Note |
|
|
10
|
+
| ------------|--------- | -------------
|
|
11
|
+
| A.3x | p15305.016.3701 |
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) with basic functionality working on:
|
|
6
15
|
|
|
7
|
-
|
|
16
|
+
| Model | Software Version | User Reported |
|
|
17
|
+
| ------------|--------- | -------------
|
|
18
|
+
| A.1 | p15265.033.3703 | ✔️
|
|
19
|
+
| A.3 | p12013.141.3703 | ✔️
|
|
20
|
+
| A.3x | p15305.016.3701 |
|
|
21
|
+
| A.6x | p15305.017.3701 | ✔️
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Testers Needed
|
|
8
25
|
|
|
9
26
|
I am looking for testers with any VSSL amplifier models, please get in touch if you interested in helping. <vsslcontrolled@proton.me>
|
|
10
27
|
|
|
11
|
-
|
|
12
|
-
- **A.1** software version **p15265.033.3703** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
13
|
-
- **A.3** software version **p12013.141.3703** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
14
|
-
- **A.3x** software version **p15305.016.3701**
|
|
15
|
-
- **A.6x** software version **p15305.017.3701** via Home Assistant [integration](https://github.com/vsslctrl/integration.home-assistant) (reported)
|
|
28
|
+
|
|
16
29
|
|
|
17
30
|
## Important
|
|
18
31
|
|
|
19
|
-
There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* running on the
|
|
32
|
+
There should not be any *[VSSL Agent's](https://vssl.gitbook.io/vssl-rest-api/getting-started/start)* running on the network. If you dont know what this is, then you can ignore this notice.
|
|
33
|
+
|
|
34
|
+
**`vsslctrl` is not endorsed or affiliated with [VSSL](https://www.vssl.com/) in any manner.**
|
|
20
35
|
|
|
21
36
|
## TODOs
|
|
22
37
|
|
|
23
|
-
* **A.1(x)** specific control e.g bluetooth
|
|
24
38
|
* Better test coverage
|
|
39
|
+
* A.1(x) testing - e.g output settings
|
|
40
|
+
* More controls - e.g IR Control
|
|
25
41
|
|
|
26
42
|
## Basic Usage
|
|
27
43
|
|
|
@@ -37,7 +53,7 @@ async def main():
|
|
|
37
53
|
|
|
38
54
|
# Represents a physical VSSL amplifier
|
|
39
55
|
vssl = Vssl(DeviceModels.A1)
|
|
40
|
-
a1 = vssl.add_zone(
|
|
56
|
+
a1 = vssl.add_zone('192.168.1.10')
|
|
41
57
|
|
|
42
58
|
# Connect and initiate zones.
|
|
43
59
|
await vssl.initialise()
|
|
@@ -67,14 +83,13 @@ from vsslctrl import Vssl, DeviceModels, Zone, ZoneIDs
|
|
|
67
83
|
async def main():
|
|
68
84
|
|
|
69
85
|
# Represents a physical VSSL amplifier
|
|
70
|
-
# If no DeviceModels is passed, vsslctrl will default to the feature set of the X series amps
|
|
71
86
|
vssl = Vssl(DeviceModels.A3X)
|
|
72
87
|
|
|
73
88
|
# Add each you wish to control
|
|
74
|
-
zone1 = vssl.add_zone(
|
|
75
|
-
zone2 = vssl.add_zone(
|
|
76
|
-
zone3 = vssl.add_zone(
|
|
77
|
-
#... up to 6 zones
|
|
89
|
+
zone1 = vssl.add_zone('192.168.1.10', ZoneIDs.ZONE_1)
|
|
90
|
+
zone2 = vssl.add_zone('192.168.1.11', ZoneIDs.ZONE_2)
|
|
91
|
+
zone3 = vssl.add_zone('192.168.1.12', ZoneIDs.ZONE_3)
|
|
92
|
+
#... up to 6 zones for A.6(x)
|
|
78
93
|
|
|
79
94
|
# Connect and initiate zones.
|
|
80
95
|
await vssl.initialise()
|
|
@@ -98,9 +113,36 @@ async def main():
|
|
|
98
113
|
asyncio.run(main())
|
|
99
114
|
```
|
|
100
115
|
|
|
116
|
+
# `DeviceModels`
|
|
117
|
+
|
|
118
|
+
A device model has to be passed to VSSL so it knows internally what features are supported by the device.
|
|
119
|
+
This might be removed in the future if we can differentiate different models from the API.
|
|
120
|
+
|
|
121
|
+
| Property | Description |
|
|
122
|
+
| ------------|--------- |
|
|
123
|
+
| `A1X` | A.1x |
|
|
124
|
+
| `A3X` | A.3x |
|
|
125
|
+
| `A6X` | A.6x |
|
|
126
|
+
| `A1` | A1 |
|
|
127
|
+
| `A3` | A3 |
|
|
128
|
+
| `A6` | A6 |
|
|
129
|
+
|
|
130
|
+
# `ZoneIDs`
|
|
131
|
+
|
|
132
|
+
| Property | Description |
|
|
133
|
+
| ------------|--------- |
|
|
134
|
+
| `A1` | A.1(x) Only |
|
|
135
|
+
| `ZONE_1` | Zone 1 of A.3(x) and A.6(x) |
|
|
136
|
+
| `ZONE_2` | Zone 2 of A.3(x) and A.6(x) |
|
|
137
|
+
| `ZONE_3` | Zone 3 of A.3(x) and A.6(x) |
|
|
138
|
+
| `ZONE_4` | Zone 4 of A.6(x) |
|
|
139
|
+
| `ZONE_5` | Zone 5 of A.6(x) |
|
|
140
|
+
| `ZONE_6` | Zone 6 of A.6(x) |
|
|
141
|
+
|
|
142
|
+
|
|
101
143
|
# API
|
|
102
144
|
|
|
103
|
-
Most functionality is
|
|
145
|
+
Most functionality is achieved via `getters` and `setters` of the two main classes `Vssl`, `Zone`.
|
|
104
146
|
|
|
105
147
|
The classes will update the physical VSSL device when setting a property and once feedback has been received, the classes internal state will be updated. For example:
|
|
106
148
|
|
|
@@ -117,7 +159,6 @@ print(zone_name)
|
|
|
117
159
|
|
|
118
160
|
**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.
|
|
119
161
|
|
|
120
|
-
|
|
121
162
|
# `Vssl`
|
|
122
163
|
|
|
123
164
|
| Property | Description | Type |
|
|
@@ -139,19 +180,20 @@ vssl.factory_reset()
|
|
|
139
180
|
|
|
140
181
|
## `Vssl.settings`
|
|
141
182
|
|
|
142
|
-
| Property | Description | Type |
|
|
143
|
-
| ---------------------- | ----------- | ----------- |
|
|
144
|
-
| `name` | Device name | `str`
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
183
|
+
| Property | Description | Type | Model: Default |
|
|
184
|
+
| ---------------------- | ----------- | ----------- | ----------- |
|
|
185
|
+
| `name` | Device name | `str` |
|
|
186
|
+
| `bus_1_name` | Name of Bus 1 | `str` | <ul><li>A.1: Optical Input</li><li>A.3x/6x: Not Used</li></ul>
|
|
187
|
+
| `bus_2_name` | Name of Bus 2 | `str` | <ul><li>A.1: Coax Input</li><li>A.3x/6x: Optical Input</li></ul>
|
|
188
|
+
| `bluetooth` | Bluetooth enabled / disabled | `bool` |
|
|
147
189
|
| `bluetooth_toggle()` | Toggle Bluetooth | `func` |
|
|
148
190
|
|
|
149
191
|
```python
|
|
150
192
|
"""Example"""
|
|
151
193
|
# Setting device name
|
|
152
194
|
vssl.settings.name = 'My House'
|
|
153
|
-
# Setting
|
|
154
|
-
vssl.settings.
|
|
195
|
+
# Setting bus 2 name
|
|
196
|
+
vssl.settings.bus_2_name = 'Optical Input'
|
|
155
197
|
# Enable Bluetooth
|
|
156
198
|
vssl.settings.bluetooth = True
|
|
157
199
|
# Toggle Bluetooth
|
|
@@ -262,28 +304,38 @@ zone1.transport.state = ZoneTransport.States.PAUSE
|
|
|
262
304
|
|
|
263
305
|
| Property | Description | Type | Values |
|
|
264
306
|
| ---------------------- | ----------- | ----------- |----------- |
|
|
265
|
-
| `source` | Change input source | `int` | `InputRouter.Sources`
|
|
266
|
-
| `priority` | Change input priority
|
|
307
|
+
| `source` | Change input source.<br/>Source to be played out the zones speakers | `int` | `InputRouter.Sources`
|
|
308
|
+
| `priority` | Change input priority.<br/>Stream or local to have precedence | `int` | `InputRouter.Priorities`
|
|
267
309
|
|
|
268
|
-
|
|
310
|
+
|
|
311
|
+
### A.1(x) Source Routing Order
|
|
312
|
+
A.1 and A.1x don't support manually changing the input source. Instead a fixed source routing order is used:
|
|
313
|
+
|
|
314
|
+
1. Optical Input
|
|
315
|
+
2. Coaxial Input
|
|
316
|
+
3. Analog Input
|
|
317
|
+
|
|
318
|
+
Input `InputRouter.Priorities` still apply.
|
|
319
|
+
|
|
320
|
+
### Input Priority / Precedence
|
|
269
321
|
|
|
270
322
|
| `InputRouter.Priorities` | Priority Order |
|
|
271
323
|
| ---------------------- | ----------- |
|
|
272
|
-
| **`STREAM`** | <ol><li>Stream</li><li>Party Zone</li><li>Bus 1
|
|
273
|
-
| **`LOCAL`** | <ol><li>Bus 1
|
|
324
|
+
| **`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>
|
|
325
|
+
| **`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>
|
|
274
326
|
|
|
275
327
|
```python
|
|
276
328
|
"""Example"""
|
|
277
329
|
# Change zone 1 to listen to analog input 4
|
|
278
330
|
zone1.input.source = InputRouter.Sources.ANALOG_IN_4
|
|
279
331
|
|
|
280
|
-
# Change zone 1 to perfer
|
|
332
|
+
# Change zone 1 to perfer local inputs over stream
|
|
281
333
|
zone1.input.priority = InputRouter.Priorities.LOCAL
|
|
282
334
|
```
|
|
283
335
|
|
|
284
336
|
## `Zone.group`
|
|
285
337
|
|
|
286
|
-
|
|
338
|
+
Unsupported on X series amplifiers.
|
|
287
339
|
|
|
288
340
|
| Property | Description | Type | Values |
|
|
289
341
|
| ---------------------- | ----------- | ----------- |----------- |
|
|
@@ -314,7 +366,7 @@ zone1.group.is_party_zone_member_toggle()
|
|
|
314
366
|
|
|
315
367
|
| Property | Description | Type | Values | Default |
|
|
316
368
|
| ---------------------- | ----------- | ----------- |----------- |----------- |
|
|
317
|
-
| `source` | Where the AO is routed from. i.e
|
|
369
|
+
| `source` | Where the AO is routed from. i.e stream, optical input or off | `int` | `AnalogOutput.Sources` | `Off`
|
|
318
370
|
| `is_fixed_volume` | Fix the output volume. Output wont respond to volume control | `bool` | |`False`
|
|
319
371
|
| `is_fixed_volume_toggle()` | Toggle fixed volume | `func` |
|
|
320
372
|
|
|
@@ -422,55 +474,6 @@ zone1.settings.eq.khz1_db = -2
|
|
|
422
474
|
zone1.settings.subwoofer.crossover = 100
|
|
423
475
|
```
|
|
424
476
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
## Another (Lite) Way
|
|
428
|
-
|
|
429
|
-
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`.
|
|
430
|
-
|
|
431
|
-
| HEX | Description |
|
|
432
|
-
| ---------------------- | ----------- |
|
|
433
|
-
| `\x10\x05\x03\x0{Zone Number}\xff\x03` | Volume Up
|
|
434
|
-
| `\x10\x05\x03\x0{Zone Number}\xfe\x03` | Volume Down
|
|
435
|
-
| `\x10\x11\x02\x0{Zone Number}\x01` | Mute
|
|
436
|
-
| All commands can be found by looking [here](https://github.com/vsslctrl/vsslctrl/blob/2c43c2f2393b94bc0e062d2ab90144343eca16ef/vsslctrl/api_alpha.py) |
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
The `volume up` HEX command for `Zone 2` would be `\x10\x05\x03\x02\xff\x03`
|
|
440
|
-
|
|
441
|
-
Now send the raw HEX using Netcat to the device using this syntax:
|
|
442
|
-
|
|
443
|
-
`echo -e "{HEX Command}" | nc {IP Address} 50002`
|
|
444
|
-
|
|
445
|
-
For example to send `volume up` to `Zone 2`:
|
|
446
|
-
|
|
447
|
-
`echo -e "\x10\x05\x03\x02\xff\x03" | nc 192.168.1.11 50002`
|
|
448
|
-
|
|
449
|
-
Home Assistant `Configuration.yaml` example:
|
|
450
|
-
|
|
451
|
-
```ymal
|
|
452
|
-
...
|
|
453
|
-
|
|
454
|
-
shell_command:
|
|
455
|
-
#Zone 1
|
|
456
|
-
vssl_zone_1_volume_up: 'echo -e "\x10\x05\x03\x01\xff\x03" | nc 192.168.1.10 50002'
|
|
457
|
-
vssl_zone_1_volume_down: 'echo -e "\x10\x05\x03\x01\xfe\x03" | nc 192.168.1.10 50002'
|
|
458
|
-
vssl_zone_1_mute: 'echo -e "\x10\x11\x02\x01\x01" | nc 192.168.1.10 50002'
|
|
459
|
-
vssl_zone_1_unmute: 'echo -e "\x10\x11\x02\x01\x00" | nc 192.168.1.10 50002'
|
|
460
|
-
#Zone 2
|
|
461
|
-
vssl_zone_2_volume_up: 'echo -e "\x10\x05\x03\x02\xff\x03" | nc 192.168.1.11 50002'
|
|
462
|
-
vssl_zone_2_volume_down: 'echo -e "\x10\x05\x03\x02\xfe\x03" | nc 192.168.1.11 50002'
|
|
463
|
-
vssl_zone_2_mute: 'echo -e "\x10\x11\x02\x02\x01" | nc 192.168.1.11 50002'
|
|
464
|
-
vssl_zone_2_unmute: 'echo -e "\x10\x11\x02\x02\x00" | nc 192.168.1.11 50002'
|
|
465
|
-
#Zone 3
|
|
466
|
-
vssl_zone_3_volume_up: 'echo -e "\x10\x05\x03\x03\xff\x03" | nc 192.168.1.12 50002'
|
|
467
|
-
vssl_zone_3_volume_down: 'echo -e "\x10\x05\x03\x03\xfe\x03" | nc 192.168.1.12 50002'
|
|
468
|
-
vssl_zone_3_mute: 'echo -e "\x10\x11\x02\x03\x01" | nc 192.168.1.12 50002'
|
|
469
|
-
vssl_zone_3_unmute: 'echo -e "\x10\x11\x02\x03\x00" | nc 192.168.1.12 50002'
|
|
470
|
-
|
|
471
|
-
...
|
|
472
|
-
```
|
|
473
|
-
|
|
474
477
|
## Credit
|
|
475
478
|
|
|
476
479
|
Thanks to [@dj-jam](https://github.com/dj-jam) for the continued testing.
|
|
@@ -484,17 +487,9 @@ Motivation for this project was to integrate VSSLs amplifiers into [Home Assista
|
|
|
484
487
|
* Not tested on A.1x or original A series range of amplifiers (testers welcome)
|
|
485
488
|
* VSSL can not start a stream except for playing a URL directly. This is a limitation of the hardware itself.
|
|
486
489
|
* Not all sources set the volume to 0 when the zone is muted
|
|
487
|
-
* Grouping feedback is flaky on the X series amplifiers
|
|
488
490
|
* Airplay `Zone.track.progress` is not available.
|
|
489
491
|
* Cant stop a URL playback, feedback is worng at least
|
|
490
492
|
* 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
|
|
491
493
|
* `stop()` is intended to disconnect the client and pause the stream. Doesn’t always function this way, depending on stream source
|
|
492
494
|
* Occasionally a zones might stop responding to certain commands, issuing the `reboot` command generally corrects
|
|
493
495
|
|
|
494
|
-
## Future
|
|
495
|
-
|
|
496
|
-
* A.1(x) coverage i.e Bluetooth
|
|
497
|
-
* REST API / Web App
|
|
498
|
-
* Save and recall EQ
|
|
499
|
-
* IR Control
|
|
500
|
-
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vsslctrl"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.12.dev1"
|
|
8
8
|
description = "Package for controlling VSSL's range of streaming amplifiers"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="vsslctrl", email="vsslcontrolled@proton.me" },
|