vsslctrl 0.1.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vsslctrl/__init__.py +4 -0
- vsslctrl/api_alpha.py +1040 -0
- vsslctrl/api_base.py +321 -0
- vsslctrl/api_bravo.py +419 -0
- vsslctrl/core.py +322 -0
- vsslctrl/data_structure.py +242 -0
- vsslctrl/decorators.py +61 -0
- vsslctrl/discovery.py +193 -0
- vsslctrl/event_bus.py +159 -0
- vsslctrl/exceptions.py +21 -0
- vsslctrl/group.py +187 -0
- vsslctrl/io.py +229 -0
- vsslctrl/settings.py +655 -0
- vsslctrl/track.py +337 -0
- vsslctrl/transport.py +242 -0
- vsslctrl/utils.py +75 -0
- vsslctrl/zone.py +452 -0
- vsslctrl-0.1.0.dev1.dist-info/LICENSE +21 -0
- vsslctrl-0.1.0.dev1.dist-info/METADATA +385 -0
- vsslctrl-0.1.0.dev1.dist-info/RECORD +22 -0
- vsslctrl-0.1.0.dev1.dist-info/WHEEL +5 -0
- vsslctrl-0.1.0.dev1.dist-info/top_level.txt +1 -0
vsslctrl/settings.py
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Dict, Union
|
|
3
|
+
from .utils import clamp_volume
|
|
4
|
+
from .io import AnalogInput
|
|
5
|
+
from .data_structure import (
|
|
6
|
+
VsslIntEnum,
|
|
7
|
+
VsslDataClass,
|
|
8
|
+
ZoneDataClass,
|
|
9
|
+
ZoneEQStatusExtKeys,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
VSSL_SETTINGS_EVENT_PREFIX = "vssl.settings."
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VsslSettings(VsslDataClass):
|
|
16
|
+
class Keys:
|
|
17
|
+
NAME = "name"
|
|
18
|
+
OPTICAL_INPUT_NAME = "optical_input_name"
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# VSSL Events
|
|
22
|
+
#
|
|
23
|
+
class Events:
|
|
24
|
+
PREFIX = VSSL_SETTINGS_EVENT_PREFIX
|
|
25
|
+
NAME_CHANGE = PREFIX + "name_changed"
|
|
26
|
+
OPTICAL_INPUT_NAME_CHANGE = PREFIX + "optical_input_name_changed"
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Defaults
|
|
30
|
+
#
|
|
31
|
+
DEFAULTS = {Keys.NAME: None, Keys.OPTICAL_INPUT_NAME: "Optical In"}
|
|
32
|
+
|
|
33
|
+
def __init__(self, vssl: "vsslctrl.Vssl"):
|
|
34
|
+
self._vssl = vssl
|
|
35
|
+
|
|
36
|
+
self._name = None # device name
|
|
37
|
+
self._optical_input_name = self.DEFAULTS[self.Keys.OPTICAL_INPUT_NAME]
|
|
38
|
+
self.power = VsslPowerSettings(vssl)
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# Name
|
|
42
|
+
#
|
|
43
|
+
@property
|
|
44
|
+
def name(self):
|
|
45
|
+
return self._name
|
|
46
|
+
|
|
47
|
+
@name.setter
|
|
48
|
+
def name(self, name: str):
|
|
49
|
+
zone = self._vssl.get_connected_zone()
|
|
50
|
+
if zone and name:
|
|
51
|
+
zone.api_alpha.request_action_18(name)
|
|
52
|
+
|
|
53
|
+
#
|
|
54
|
+
# Optical Input Name
|
|
55
|
+
#
|
|
56
|
+
@property
|
|
57
|
+
def optical_input_name(self):
|
|
58
|
+
return self._optical_input_name
|
|
59
|
+
|
|
60
|
+
@optical_input_name.setter
|
|
61
|
+
def optical_input_name(self, name: str):
|
|
62
|
+
zone = self._vssl.get_connected_zone()
|
|
63
|
+
if zone:
|
|
64
|
+
zone.api_alpha.request_action_15_12(name)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class VsslPowerSettings(VsslDataClass):
|
|
68
|
+
#
|
|
69
|
+
# Transport States
|
|
70
|
+
#
|
|
71
|
+
# DO NOT CHANGE - VSSL Defined
|
|
72
|
+
#
|
|
73
|
+
class States(VsslIntEnum):
|
|
74
|
+
ON = 0
|
|
75
|
+
STANDBY = 1
|
|
76
|
+
SLEEP = 2
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# Volume Setting Events
|
|
80
|
+
#
|
|
81
|
+
class Events:
|
|
82
|
+
PREFIX = VSSL_SETTINGS_EVENT_PREFIX + "power."
|
|
83
|
+
STATE_CHANGE = PREFIX + "state_changed"
|
|
84
|
+
ADAPTIVE_CHANGE = PREFIX + "adaptive_changed"
|
|
85
|
+
|
|
86
|
+
#
|
|
87
|
+
# Defaults
|
|
88
|
+
#
|
|
89
|
+
DEFAULTS = {"state": States.ON, "adaptive": False}
|
|
90
|
+
|
|
91
|
+
def __init__(self, vssl: "vsslctrl.Vssl"):
|
|
92
|
+
self._vssl = vssl
|
|
93
|
+
|
|
94
|
+
self._state = self.States.ON
|
|
95
|
+
self._adaptive = True # 1 = auto, 0 = always on
|
|
96
|
+
|
|
97
|
+
#
|
|
98
|
+
# Power State
|
|
99
|
+
#
|
|
100
|
+
@property
|
|
101
|
+
def state(self):
|
|
102
|
+
return self._state
|
|
103
|
+
|
|
104
|
+
@state.setter
|
|
105
|
+
def state(self, state: int):
|
|
106
|
+
# read-only
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
def _set_state(self, state: int):
|
|
110
|
+
if self.state != state:
|
|
111
|
+
if self.States.is_valid(state):
|
|
112
|
+
self._state = self.States(state)
|
|
113
|
+
return True
|
|
114
|
+
else:
|
|
115
|
+
self._vssl._log_warning(
|
|
116
|
+
f"VsslPowerSettings.States {state} doesnt exist"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
#
|
|
120
|
+
# Adaptive Power (always on or auto)
|
|
121
|
+
#
|
|
122
|
+
# 1 = Auto / Adaptive Power On
|
|
123
|
+
# 0 = Always On
|
|
124
|
+
#
|
|
125
|
+
@property
|
|
126
|
+
def adaptive(self):
|
|
127
|
+
return bool(self._adaptive)
|
|
128
|
+
|
|
129
|
+
@adaptive.setter
|
|
130
|
+
def adaptive(self, enabled: bool):
|
|
131
|
+
zone = self._vssl.get_connected_zone()
|
|
132
|
+
if zone:
|
|
133
|
+
zone.api_alpha.request_action_4F(not not enabled)
|
|
134
|
+
|
|
135
|
+
def adaptive_toggle(self):
|
|
136
|
+
self.adaptive = False if self.adaptive else True
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class ZoneSettings(ZoneDataClass):
|
|
140
|
+
class StereoMono(VsslIntEnum):
|
|
141
|
+
"""StereoMono
|
|
142
|
+
|
|
143
|
+
0: Stereo
|
|
144
|
+
1: Mono
|
|
145
|
+
|
|
146
|
+
DO NOT CHANGE - VSSL Defined
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
Stereo = 0
|
|
150
|
+
Mono = 1
|
|
151
|
+
|
|
152
|
+
class Events:
|
|
153
|
+
"""Setting Events"""
|
|
154
|
+
|
|
155
|
+
PREFIX = "zone.settings."
|
|
156
|
+
DISABLED_CHANGE = PREFIX + "disabled_changed"
|
|
157
|
+
NAME_CHANGE = PREFIX + "name_changed"
|
|
158
|
+
MONO_CHANGE = PREFIX + "mono_changed"
|
|
159
|
+
|
|
160
|
+
def __init__(self, zone: "zone.Zone"):
|
|
161
|
+
self.zone = zone
|
|
162
|
+
|
|
163
|
+
self._disabled = False
|
|
164
|
+
self._name = f"Zone {zone.id}"
|
|
165
|
+
self._mono = False
|
|
166
|
+
|
|
167
|
+
self.eq = EQSettings(zone)
|
|
168
|
+
self.volume = VolumeSettings(zone)
|
|
169
|
+
self.analog_input = AnalogInput(zone)
|
|
170
|
+
|
|
171
|
+
#
|
|
172
|
+
# Disable the zone
|
|
173
|
+
#
|
|
174
|
+
@property
|
|
175
|
+
def disabled(self):
|
|
176
|
+
return self._disabled
|
|
177
|
+
|
|
178
|
+
@disabled.setter
|
|
179
|
+
def disabled(self, disabled: Union[bool, int]):
|
|
180
|
+
self.zone.api_alpha.request_action_25(not not disabled)
|
|
181
|
+
|
|
182
|
+
def disabled_toggle(self):
|
|
183
|
+
self.disabled = False if self.disabled else True
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# Name
|
|
187
|
+
#
|
|
188
|
+
@property
|
|
189
|
+
def name(self):
|
|
190
|
+
return self._name
|
|
191
|
+
|
|
192
|
+
@name.setter
|
|
193
|
+
def name(self, name: str):
|
|
194
|
+
self.zone.api_bravo.request_action_5A_set(str(name))
|
|
195
|
+
# Incase for some network reason _request_name arrives before the change
|
|
196
|
+
# the setter will remove the request once set
|
|
197
|
+
self.zone._poller.append(self.zone._request_name)
|
|
198
|
+
self.zone._request_name()
|
|
199
|
+
|
|
200
|
+
def _set_name(self, name: str):
|
|
201
|
+
if name != self._name:
|
|
202
|
+
self._name = name
|
|
203
|
+
self.zone._poller.remove(self.zone._request_name)
|
|
204
|
+
return True
|
|
205
|
+
|
|
206
|
+
#
|
|
207
|
+
# Mono
|
|
208
|
+
# Set the output to mono
|
|
209
|
+
#
|
|
210
|
+
@property
|
|
211
|
+
def mono(self):
|
|
212
|
+
return self._mono
|
|
213
|
+
|
|
214
|
+
@mono.setter
|
|
215
|
+
def mono(self, mono: "ZoneSettings.StereoMono"):
|
|
216
|
+
if self.StereoMono.is_valid(mono):
|
|
217
|
+
self.zone.api_alpha.request_action_mono_set(mono)
|
|
218
|
+
else:
|
|
219
|
+
self.zone._log_error(f"ZoneSettings.StereoMono {mono} doesnt exist")
|
|
220
|
+
|
|
221
|
+
def _set_mono(self, mono: int):
|
|
222
|
+
if self.mono != mono:
|
|
223
|
+
if self.StereoMono.is_valid(mono):
|
|
224
|
+
self._mono = self.StereoMono(mono)
|
|
225
|
+
return True
|
|
226
|
+
else:
|
|
227
|
+
self.zone._log_error(f"ZoneSettings.StereoMono {mono} doesnt exist")
|
|
228
|
+
|
|
229
|
+
def mono_toggle(self):
|
|
230
|
+
self.mono = (
|
|
231
|
+
self.StereoMono.Stereo
|
|
232
|
+
if self.mono == self.StereoMono.Mono
|
|
233
|
+
else self.StereoMono.Mono
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class VolumeSettings(ZoneDataClass):
|
|
238
|
+
class Keys:
|
|
239
|
+
DEFAULT_ON = "default_on"
|
|
240
|
+
MAX_LEFT = "max_left"
|
|
241
|
+
MAX_RIGHT = "max_right"
|
|
242
|
+
|
|
243
|
+
class Events:
|
|
244
|
+
"""Volume Setting Events"""
|
|
245
|
+
|
|
246
|
+
PREFIX = "zone.settings.volume."
|
|
247
|
+
DEFAULT_ON_CHANGE = PREFIX + "default_on_changed"
|
|
248
|
+
MAX_LEFT_CHANGE = PREFIX + "max_left_changed"
|
|
249
|
+
MAX_RIGHT_CHANGE = PREFIX + "max_right_changed"
|
|
250
|
+
|
|
251
|
+
#
|
|
252
|
+
# Defaults
|
|
253
|
+
#
|
|
254
|
+
DEFAULTS = {
|
|
255
|
+
Keys.DEFAULT_ON: 0,
|
|
256
|
+
Keys.MAX_LEFT: 75,
|
|
257
|
+
Keys.MAX_RIGHT: 75,
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
#
|
|
261
|
+
# Key Map for the reponse JSON to our internal structure
|
|
262
|
+
#
|
|
263
|
+
KEY_MAP = {
|
|
264
|
+
ZoneEQStatusExtKeys.VOL_DEFAULT_ON: Keys.DEFAULT_ON,
|
|
265
|
+
ZoneEQStatusExtKeys.VOL_MAX_LEFT: Keys.MAX_LEFT,
|
|
266
|
+
ZoneEQStatusExtKeys.VOL_MAX_RIGHT: Keys.MAX_RIGHT,
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
def __init__(self, zone: "zone.Zone"):
|
|
270
|
+
self.zone = zone
|
|
271
|
+
|
|
272
|
+
self._default_on = self.DEFAULTS[self.Keys.DEFAULT_ON]
|
|
273
|
+
self._max_left = self.DEFAULTS[self.Keys.MAX_LEFT]
|
|
274
|
+
self._max_right = self.DEFAULTS[self.Keys.MAX_RIGHT]
|
|
275
|
+
|
|
276
|
+
#
|
|
277
|
+
# Update from a JSON dict passed
|
|
278
|
+
#
|
|
279
|
+
def _map_response_dict(self, volume_data: Dict[str, int]) -> None:
|
|
280
|
+
for volume_data_key, settings_prop in self.KEY_MAP.items():
|
|
281
|
+
if volume_data_key in volume_data:
|
|
282
|
+
set_func = f"_set_{settings_prop}"
|
|
283
|
+
if hasattr(self, set_func):
|
|
284
|
+
getattr(self, set_func)(volume_data[volume_data_key])
|
|
285
|
+
|
|
286
|
+
#
|
|
287
|
+
# Default On Volume
|
|
288
|
+
#
|
|
289
|
+
# When enabled, volume automatically reverts to the determined volume level
|
|
290
|
+
# when initiating a new audio session
|
|
291
|
+
#
|
|
292
|
+
# 0 is off (no default on vol)
|
|
293
|
+
#
|
|
294
|
+
@property
|
|
295
|
+
def default_on(self):
|
|
296
|
+
return self._default_on
|
|
297
|
+
|
|
298
|
+
@default_on.setter
|
|
299
|
+
def default_on(self, vol: int):
|
|
300
|
+
self.zone.api_alpha.request_action_05_08(vol)
|
|
301
|
+
|
|
302
|
+
def _set_default_on(self, vol: int):
|
|
303
|
+
vol = clamp_volume(vol)
|
|
304
|
+
if self.default_on != vol:
|
|
305
|
+
self._default_on = vol
|
|
306
|
+
return True
|
|
307
|
+
|
|
308
|
+
#
|
|
309
|
+
# Max Left Volume
|
|
310
|
+
#
|
|
311
|
+
@property
|
|
312
|
+
def max_left(self):
|
|
313
|
+
return self._max_left
|
|
314
|
+
|
|
315
|
+
@max_left.setter
|
|
316
|
+
def max_left(self, vol: int):
|
|
317
|
+
self.zone.api_alpha.request_action_05_01(vol)
|
|
318
|
+
|
|
319
|
+
def _set_max_left(self, vol: int):
|
|
320
|
+
vol = clamp_volume(vol)
|
|
321
|
+
if self.max_left != vol:
|
|
322
|
+
self._max_left = vol
|
|
323
|
+
return True
|
|
324
|
+
|
|
325
|
+
#
|
|
326
|
+
# Max Right Volume
|
|
327
|
+
#
|
|
328
|
+
@property
|
|
329
|
+
def max_right(self):
|
|
330
|
+
return self._max_right
|
|
331
|
+
|
|
332
|
+
@max_right.setter
|
|
333
|
+
def max_right(self, vol: int):
|
|
334
|
+
self.zone.api_alpha.request_action_05_02(vol)
|
|
335
|
+
|
|
336
|
+
def _set_max_right(self, vol: int):
|
|
337
|
+
vol = clamp_volume(vol)
|
|
338
|
+
if self.max_right != vol:
|
|
339
|
+
self._max_right = vol
|
|
340
|
+
return True
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class EQSettings(ZoneDataClass):
|
|
344
|
+
MIN_VALUE = 90
|
|
345
|
+
MAX_VALUE = 110
|
|
346
|
+
MIN_VALUE_DB = -10
|
|
347
|
+
MAX_VALUE_DB = 10
|
|
348
|
+
|
|
349
|
+
class Keys:
|
|
350
|
+
ENABLED = "enabled"
|
|
351
|
+
HZ60 = "hz60" # 60Hz
|
|
352
|
+
HZ200 = "hz200" # 200Hz
|
|
353
|
+
HZ500 = "hz500" # 500Hz
|
|
354
|
+
KHZ1 = "khz1" # 1kHz
|
|
355
|
+
KHZ4 = "khz4" # 4kHz
|
|
356
|
+
KHZ8 = "khz8" # 8kHz
|
|
357
|
+
KHZ15 = "khz15" # 15kHz
|
|
358
|
+
|
|
359
|
+
#
|
|
360
|
+
# EQ Frequencies
|
|
361
|
+
#
|
|
362
|
+
# DO NOT CHANGE - VSSL Defined
|
|
363
|
+
#
|
|
364
|
+
class Freqs(VsslIntEnum):
|
|
365
|
+
HZ60 = 1 # 60Hz
|
|
366
|
+
HZ200 = 2 # 200Hz
|
|
367
|
+
HZ500 = 3 # 500Hz
|
|
368
|
+
KHZ1 = 4 # 1kHz
|
|
369
|
+
KHZ4 = 5 # 4kHz
|
|
370
|
+
KHZ8 = 6 # 8kHz
|
|
371
|
+
KHZ15 = 7 # 15kHz
|
|
372
|
+
|
|
373
|
+
#
|
|
374
|
+
# Volume Setting Events
|
|
375
|
+
#
|
|
376
|
+
class Events:
|
|
377
|
+
PREFIX = "zone.settings.eq."
|
|
378
|
+
ENABLED_CHANGE = PREFIX + "enabled_change"
|
|
379
|
+
HZ60_CHANGE = PREFIX + "hz60_change"
|
|
380
|
+
HZ200_CHANGE = PREFIX + "hz200_change"
|
|
381
|
+
HZ500_CHANGE = PREFIX + "hz500_change"
|
|
382
|
+
KHZ1_CHANGE = PREFIX + "khz1_change"
|
|
383
|
+
KHZ4_CHANGE = PREFIX + "khz4_change"
|
|
384
|
+
KHZ8_CHANGE = PREFIX + "khz8_change"
|
|
385
|
+
KHZ15_CHANGE = PREFIX + "khz15_change"
|
|
386
|
+
|
|
387
|
+
#
|
|
388
|
+
# Defaults
|
|
389
|
+
#
|
|
390
|
+
DEFAULTS = {
|
|
391
|
+
Keys.ENABLED: False,
|
|
392
|
+
Keys.HZ60: 100,
|
|
393
|
+
Keys.HZ200: 100,
|
|
394
|
+
Keys.HZ500: 100,
|
|
395
|
+
Keys.KHZ1: 100,
|
|
396
|
+
Keys.KHZ4: 100,
|
|
397
|
+
Keys.KHZ8: 100,
|
|
398
|
+
Keys.KHZ15: 100,
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
#
|
|
402
|
+
# Key Map
|
|
403
|
+
#
|
|
404
|
+
KEY_MAP = {
|
|
405
|
+
ZoneEQStatusExtKeys.HZ60: Keys.HZ60,
|
|
406
|
+
ZoneEQStatusExtKeys.HZ200: Keys.HZ200,
|
|
407
|
+
ZoneEQStatusExtKeys.HZ500: Keys.HZ500,
|
|
408
|
+
ZoneEQStatusExtKeys.KHZ1: Keys.KHZ1,
|
|
409
|
+
ZoneEQStatusExtKeys.KHZ4: Keys.KHZ4,
|
|
410
|
+
ZoneEQStatusExtKeys.KHZ8: Keys.KHZ8,
|
|
411
|
+
ZoneEQStatusExtKeys.KHZ15: Keys.KHZ15,
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
def __init__(self, zone: "zone.Zone"):
|
|
415
|
+
self.zone = zone
|
|
416
|
+
|
|
417
|
+
self._enabled = self.DEFAULTS[self.Keys.ENABLED]
|
|
418
|
+
self._hz60 = self.DEFAULTS[self.Keys.HZ60]
|
|
419
|
+
self._hz200 = self.DEFAULTS[self.Keys.HZ200]
|
|
420
|
+
self._hz500 = self.DEFAULTS[self.Keys.HZ500]
|
|
421
|
+
self._khz1 = self.DEFAULTS[self.Keys.KHZ1]
|
|
422
|
+
self._khz4 = self.DEFAULTS[self.Keys.KHZ4]
|
|
423
|
+
self._khz8 = self.DEFAULTS[self.Keys.KHZ8]
|
|
424
|
+
self._khz15 = self.DEFAULTS[self.Keys.KHZ15]
|
|
425
|
+
|
|
426
|
+
def as_dict(self, with_db=True):
|
|
427
|
+
settings = dict(self)
|
|
428
|
+
|
|
429
|
+
if with_db:
|
|
430
|
+
for json_data_key, setting_key in self.KEY_MAP.items():
|
|
431
|
+
key = setting_key + "_db"
|
|
432
|
+
settings[key] = getattr(self, key)
|
|
433
|
+
|
|
434
|
+
return settings
|
|
435
|
+
|
|
436
|
+
#
|
|
437
|
+
# Clamp betwwen 90 and 110
|
|
438
|
+
#
|
|
439
|
+
def _clamp(self, value: int = 100):
|
|
440
|
+
return int(max(self.MIN_VALUE, min(value, self.MAX_VALUE)))
|
|
441
|
+
|
|
442
|
+
#
|
|
443
|
+
# Map between -10 and +10
|
|
444
|
+
#
|
|
445
|
+
# 90 = -10db
|
|
446
|
+
# 100 = 0db
|
|
447
|
+
# 110 = +10db
|
|
448
|
+
#
|
|
449
|
+
def _map_clamp(self, input_value: int = 0, to_db: bool = True):
|
|
450
|
+
"""Convert / Map values between 90 & 110 to their -10db & +10db equilivents"""
|
|
451
|
+
|
|
452
|
+
if to_db:
|
|
453
|
+
# Ensure the mapped value is clamped between 90 and 110
|
|
454
|
+
clamped_value = self._clamp(input_value)
|
|
455
|
+
|
|
456
|
+
# Map the clamped value to the range -10 to 10
|
|
457
|
+
return int(
|
|
458
|
+
((clamped_value - self.MIN_VALUE) / (self.MAX_VALUE - self.MIN_VALUE))
|
|
459
|
+
* 20
|
|
460
|
+
- self.MAX_VALUE_DB
|
|
461
|
+
)
|
|
462
|
+
else:
|
|
463
|
+
# Map the input value from the range -10 to 10 to the range 90 to 110
|
|
464
|
+
mapped_value = ((input_value + self.MAX_VALUE_DB) / 20) * (
|
|
465
|
+
self.MAX_VALUE - self.MIN_VALUE
|
|
466
|
+
) + self.MIN_VALUE
|
|
467
|
+
|
|
468
|
+
# Ensure the result is clamped between 90 and 110
|
|
469
|
+
return self._clamp(mapped_value)
|
|
470
|
+
|
|
471
|
+
#
|
|
472
|
+
# Set EQ Value
|
|
473
|
+
#
|
|
474
|
+
# Expects a value between: 90 to 110
|
|
475
|
+
#
|
|
476
|
+
def _set_frequency_on_device(self, freq: "EQSettings.Freqs", value: int):
|
|
477
|
+
if self.Freqs.is_valid(freq):
|
|
478
|
+
self.zone.api_alpha.request_action_0D(freq, self._clamp(value))
|
|
479
|
+
else:
|
|
480
|
+
self.zone._log_error(f"EQSettings.Freqs {freq} doesnt exist")
|
|
481
|
+
|
|
482
|
+
#
|
|
483
|
+
# Set EQ Value, using dB as an input
|
|
484
|
+
#
|
|
485
|
+
# Expects: -10 to +10
|
|
486
|
+
#
|
|
487
|
+
def _set_frequency_on_device_db(self, freq: int, value: int):
|
|
488
|
+
self._set_frequency_on_device(freq, self._map_clamp(value, False))
|
|
489
|
+
|
|
490
|
+
#
|
|
491
|
+
# Updade a property and emit and event if changed
|
|
492
|
+
#
|
|
493
|
+
def _set_eq_freq(self, freq: int, new_value: int):
|
|
494
|
+
if self.Freqs.is_valid(freq):
|
|
495
|
+
freq = self.Freqs(freq)
|
|
496
|
+
self._set_property(getattr(self.Keys, freq.name), self._clamp(new_value))
|
|
497
|
+
else:
|
|
498
|
+
self.zone._log_error(f"EQSettings.Freqs {freq} doesnt exist")
|
|
499
|
+
|
|
500
|
+
#
|
|
501
|
+
# Update from a JSON dict passed
|
|
502
|
+
#
|
|
503
|
+
def _map_response_dict(self, json_data: Dict[str, int]) -> None:
|
|
504
|
+
for json_data_key, setting_key in self.KEY_MAP.items():
|
|
505
|
+
if json_data_key in json_data:
|
|
506
|
+
self._set_property(
|
|
507
|
+
setting_key, self._clamp(int(json_data[json_data_key]))
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
#
|
|
511
|
+
# EQ Enabled
|
|
512
|
+
#
|
|
513
|
+
@property
|
|
514
|
+
def enabled(self):
|
|
515
|
+
return self._enabled
|
|
516
|
+
|
|
517
|
+
@enabled.setter
|
|
518
|
+
def enabled(self, enabled: Union[bool, int]):
|
|
519
|
+
self.zone.api_alpha.request_action_2D(enabled)
|
|
520
|
+
|
|
521
|
+
def enabled_toggle(self):
|
|
522
|
+
self.enabled = False if self.enabled else True
|
|
523
|
+
|
|
524
|
+
#
|
|
525
|
+
# EQ 60Hz
|
|
526
|
+
#
|
|
527
|
+
@property
|
|
528
|
+
def hz60(self):
|
|
529
|
+
return self._hz60
|
|
530
|
+
|
|
531
|
+
@hz60.setter
|
|
532
|
+
def hz60(self, val: int):
|
|
533
|
+
self._set_frequency_on_device(self.Freqs.HZ60, val)
|
|
534
|
+
|
|
535
|
+
@property
|
|
536
|
+
def hz60_db(self):
|
|
537
|
+
return self._map_clamp(self.hz60)
|
|
538
|
+
|
|
539
|
+
@hz60_db.setter
|
|
540
|
+
def hz60_db(self, val: int):
|
|
541
|
+
self._set_frequency_on_device_db(self.Freqs.HZ60, val)
|
|
542
|
+
|
|
543
|
+
#
|
|
544
|
+
# EQ 200Hz
|
|
545
|
+
#
|
|
546
|
+
@property
|
|
547
|
+
def hz200(self):
|
|
548
|
+
return self._hz200
|
|
549
|
+
|
|
550
|
+
@hz200.setter
|
|
551
|
+
def hz200(self, val: int):
|
|
552
|
+
self._set_frequency_on_device(self.Freqs.HZ200, val)
|
|
553
|
+
|
|
554
|
+
@property
|
|
555
|
+
def hz200_db(self):
|
|
556
|
+
return self._map_clamp(self.hz200)
|
|
557
|
+
|
|
558
|
+
@hz200_db.setter
|
|
559
|
+
def hz200_db(self, val: int):
|
|
560
|
+
self._set_frequency_on_device_db(self.Freqs.HZ200, val)
|
|
561
|
+
|
|
562
|
+
#
|
|
563
|
+
# EQ 500Hz
|
|
564
|
+
#
|
|
565
|
+
@property
|
|
566
|
+
def hz500(self):
|
|
567
|
+
return self._hz500
|
|
568
|
+
|
|
569
|
+
@hz500.setter
|
|
570
|
+
def hz500(self, val: int):
|
|
571
|
+
self._set_frequency_on_device(self.Freqs.HZ500, val)
|
|
572
|
+
|
|
573
|
+
@property
|
|
574
|
+
def hz500_db(self):
|
|
575
|
+
return self._map_clamp(self.hz500)
|
|
576
|
+
|
|
577
|
+
@hz500_db.setter
|
|
578
|
+
def hz500_db(self, val: int):
|
|
579
|
+
self._set_frequency_on_device_db(self.Freqs.HZ500, val)
|
|
580
|
+
|
|
581
|
+
#
|
|
582
|
+
# EQ 1kHz
|
|
583
|
+
#
|
|
584
|
+
@property
|
|
585
|
+
def khz1(self):
|
|
586
|
+
return self._khz1
|
|
587
|
+
|
|
588
|
+
@khz1.setter
|
|
589
|
+
def khz1(self, val: int):
|
|
590
|
+
self._set_frequency_on_device(self.Freqs.KHZ1, val)
|
|
591
|
+
|
|
592
|
+
@property
|
|
593
|
+
def khz1_db(self):
|
|
594
|
+
return self._map_clamp(self.khz1)
|
|
595
|
+
|
|
596
|
+
@khz1_db.setter
|
|
597
|
+
def khz1_db(self, val: int):
|
|
598
|
+
self._set_frequency_on_device_db(self.Freqs.KHZ1, val)
|
|
599
|
+
|
|
600
|
+
#
|
|
601
|
+
# EQ 4kHz
|
|
602
|
+
#
|
|
603
|
+
@property
|
|
604
|
+
def khz4(self):
|
|
605
|
+
return self._khz4
|
|
606
|
+
|
|
607
|
+
@khz4.setter
|
|
608
|
+
def khz4(self, val: int):
|
|
609
|
+
self._set_frequency_on_device(self.Freqs.KHZ4, val)
|
|
610
|
+
|
|
611
|
+
@property
|
|
612
|
+
def khz4_db(self):
|
|
613
|
+
return self._map_clamp(self.khz4)
|
|
614
|
+
|
|
615
|
+
@khz4_db.setter
|
|
616
|
+
def khz4_db(self, val: int):
|
|
617
|
+
self._set_frequency_on_device_db(self.Freqs.KHZ4, val)
|
|
618
|
+
|
|
619
|
+
#
|
|
620
|
+
# EQ 8kHz
|
|
621
|
+
#
|
|
622
|
+
@property
|
|
623
|
+
def khz8(self):
|
|
624
|
+
return self._khz8
|
|
625
|
+
|
|
626
|
+
@khz8.setter
|
|
627
|
+
def khz8(self, val: int):
|
|
628
|
+
self._set_frequency_on_device(self.Freqs.KHZ8, val)
|
|
629
|
+
|
|
630
|
+
@property
|
|
631
|
+
def khz8_db(self):
|
|
632
|
+
return self._map_clamp(self.khz8)
|
|
633
|
+
|
|
634
|
+
@khz8_db.setter
|
|
635
|
+
def khz8_db(self, val: int):
|
|
636
|
+
self._set_frequency_on_device_db(self.Freqs.KHZ8, val)
|
|
637
|
+
|
|
638
|
+
#
|
|
639
|
+
# EQ 15kHz
|
|
640
|
+
#
|
|
641
|
+
@property
|
|
642
|
+
def khz15(self):
|
|
643
|
+
return self._khz15
|
|
644
|
+
|
|
645
|
+
@khz15.setter
|
|
646
|
+
def khz15(self, val: int):
|
|
647
|
+
self._set_frequency_on_device(self.Freqs.KHZ15, val)
|
|
648
|
+
|
|
649
|
+
@property
|
|
650
|
+
def khz15_db(self):
|
|
651
|
+
return self._map_clamp(self.khz15)
|
|
652
|
+
|
|
653
|
+
@khz15_db.setter
|
|
654
|
+
def khz15_db(self, val: int):
|
|
655
|
+
self._set_frequency_on_device_db(self.Freqs.KHZ15, val)
|