denonavr 1.1.2__py3-none-any.whl → 1.2.0__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.
- denonavr/__init__.py +1 -1
- denonavr/api.py +145 -112
- denonavr/audyssey.py +2 -6
- denonavr/const.py +109 -0
- denonavr/denonavr.py +64 -5
- denonavr/dirac.py +2 -6
- denonavr/foundation.py +221 -85
- denonavr/input.py +56 -48
- denonavr/soundmode.py +126 -42
- denonavr/ssdp.py +12 -11
- denonavr/tonecontrol.py +2 -6
- denonavr/volume.py +14 -30
- {denonavr-1.1.2.dist-info → denonavr-1.2.0.dist-info}/METADATA +2 -1
- denonavr-1.2.0.dist-info/RECORD +20 -0
- denonavr-1.1.2.dist-info/RECORD +0 -20
- {denonavr-1.1.2.dist-info → denonavr-1.2.0.dist-info}/WHEEL +0 -0
- {denonavr-1.1.2.dist-info → denonavr-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {denonavr-1.1.2.dist-info → denonavr-1.2.0.dist-info}/top_level.txt +0 -0
denonavr/input.py
CHANGED
|
@@ -164,7 +164,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
164
164
|
_image_url: Optional[str] = attr.ib(
|
|
165
165
|
converter=attr.converters.optional(str), default=None
|
|
166
166
|
)
|
|
167
|
-
_image_available: Optional[
|
|
167
|
+
_image_available: Optional[bool] = attr.ib(
|
|
168
168
|
converter=attr.converters.optional(str), default=None
|
|
169
169
|
)
|
|
170
170
|
|
|
@@ -174,6 +174,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
174
174
|
),
|
|
175
175
|
default=attr.Factory(set),
|
|
176
176
|
)
|
|
177
|
+
_callback_tasks: Set[asyncio.Task] = attr.ib(attr.Factory(set))
|
|
177
178
|
|
|
178
179
|
# Update tags for attributes
|
|
179
180
|
# AppCommand.xml interface
|
|
@@ -197,22 +198,18 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
197
198
|
power_event = "Z2"
|
|
198
199
|
elif self._device.zone == ZONE3:
|
|
199
200
|
power_event = "Z3"
|
|
201
|
+
self._device.telnet_api.register_callback(power_event, self._power_callback)
|
|
202
|
+
self._device.telnet_api.register_callback("SI", self._input_callback)
|
|
203
|
+
self._device.telnet_api.register_callback("NSE", self._netaudio_callback)
|
|
204
|
+
self._device.telnet_api.register_callback("TF", self._tuner_callback)
|
|
205
|
+
self._device.telnet_api.register_callback("HD", self._hdtuner_callback)
|
|
200
206
|
self._device.telnet_api.register_callback(
|
|
201
|
-
|
|
202
|
-
)
|
|
203
|
-
self._device.telnet_api.register_callback("SI", self._async_input_callback)
|
|
204
|
-
self._device.telnet_api.register_callback("NSE", self._async_netaudio_callback)
|
|
205
|
-
self._device.telnet_api.register_callback("TF", self._async_tuner_callback)
|
|
206
|
-
self._device.telnet_api.register_callback("HD", self._async_hdtuner_callback)
|
|
207
|
-
self._device.telnet_api.register_callback(
|
|
208
|
-
"SS", self._async_input_func_update_callback
|
|
207
|
+
"SS", self._input_func_update_callback
|
|
209
208
|
)
|
|
210
209
|
|
|
211
210
|
self._is_setup = True
|
|
212
211
|
|
|
213
|
-
|
|
214
|
-
self, zone: str, event: str, parameter: str
|
|
215
|
-
) -> None:
|
|
212
|
+
def _input_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
216
213
|
"""Handle an input change event."""
|
|
217
214
|
if self._device.zone != zone:
|
|
218
215
|
return
|
|
@@ -228,9 +225,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
228
225
|
self._unset_media_state()
|
|
229
226
|
self._state = STATE_ON
|
|
230
227
|
|
|
231
|
-
|
|
232
|
-
self, zone: str, event: str, parameter: str
|
|
233
|
-
) -> None:
|
|
228
|
+
def _power_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
234
229
|
"""Handle a power change event."""
|
|
235
230
|
if self._device.zone != zone:
|
|
236
231
|
return
|
|
@@ -285,9 +280,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
285
280
|
else:
|
|
286
281
|
self._stop_media_update()
|
|
287
282
|
|
|
288
|
-
|
|
289
|
-
self, zone: str, event: str, parameter: str
|
|
290
|
-
) -> None:
|
|
283
|
+
def _netaudio_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
291
284
|
"""Handle a netaudio update event."""
|
|
292
285
|
if self._device.power != POWER_ON:
|
|
293
286
|
return
|
|
@@ -305,12 +298,13 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
305
298
|
self._station = None
|
|
306
299
|
|
|
307
300
|
# Refresh cover with a hash for media URL when track is changing
|
|
308
|
-
self.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
301
|
+
self._set_image_url(
|
|
302
|
+
ALBUM_COVERS_URL.format(
|
|
303
|
+
host=self._device.api.host,
|
|
304
|
+
port=self._device.api.port,
|
|
305
|
+
hash=hash((self._title, self._artist, self._album)),
|
|
306
|
+
)
|
|
312
307
|
)
|
|
313
|
-
await self._async_test_image_accessible()
|
|
314
308
|
|
|
315
309
|
def _schedule_tuner_update(self) -> None:
|
|
316
310
|
"""Schedule a tuner update task."""
|
|
@@ -330,9 +324,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
330
324
|
else:
|
|
331
325
|
self._stop_media_update()
|
|
332
326
|
|
|
333
|
-
|
|
334
|
-
self, zone: str, event: str, parameter: str
|
|
335
|
-
) -> None:
|
|
327
|
+
def _tuner_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
336
328
|
"""Handle a tuner update event."""
|
|
337
329
|
if self._device.power != POWER_ON:
|
|
338
330
|
return
|
|
@@ -353,10 +345,11 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
353
345
|
self._album = None
|
|
354
346
|
|
|
355
347
|
# No special cover, using a static one
|
|
356
|
-
self.
|
|
357
|
-
|
|
348
|
+
self._set_image_url(
|
|
349
|
+
STATIC_ALBUM_URL.format(
|
|
350
|
+
host=self._device.api.host, port=self._device.api.port
|
|
351
|
+
)
|
|
358
352
|
)
|
|
359
|
-
await self._async_test_image_accessible()
|
|
360
353
|
|
|
361
354
|
def _schedule_hdtuner_update(self) -> None:
|
|
362
355
|
"""Schedule a HD tuner update task."""
|
|
@@ -374,9 +367,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
374
367
|
else:
|
|
375
368
|
self._stop_media_update()
|
|
376
369
|
|
|
377
|
-
|
|
378
|
-
self, zone: str, event: str, parameter: str
|
|
379
|
-
) -> None:
|
|
370
|
+
def _hdtuner_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
380
371
|
"""Handle an HD tuner update event."""
|
|
381
372
|
if self._device.power != POWER_ON:
|
|
382
373
|
return
|
|
@@ -396,19 +387,35 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
396
387
|
self._frequency = None
|
|
397
388
|
|
|
398
389
|
# No special cover, using a static one
|
|
399
|
-
self.
|
|
400
|
-
|
|
390
|
+
self._set_image_url(
|
|
391
|
+
STATIC_ALBUM_URL.format(
|
|
392
|
+
host=self._device.api.host, port=self._device.api.port
|
|
393
|
+
)
|
|
401
394
|
)
|
|
402
|
-
await self._async_test_image_accessible()
|
|
403
395
|
|
|
404
|
-
|
|
396
|
+
def _input_func_update_callback(
|
|
405
397
|
self, zone: str, event: str, parameter: str
|
|
406
398
|
) -> None:
|
|
407
399
|
"""Handle input func update events."""
|
|
408
400
|
if self._input_func_update_lock.locked():
|
|
409
401
|
return
|
|
410
|
-
|
|
411
|
-
|
|
402
|
+
task = asyncio.create_task(self.async_update_inputfuncs())
|
|
403
|
+
self._callback_tasks.add(task)
|
|
404
|
+
task.add_done_callback(self._callback_tasks.discard)
|
|
405
|
+
|
|
406
|
+
def _set_image_url(self, image_url: str) -> None:
|
|
407
|
+
"""Set image URL if it is accessible."""
|
|
408
|
+
if self._image_available is False:
|
|
409
|
+
return
|
|
410
|
+
|
|
411
|
+
self._image_url = image_url
|
|
412
|
+
|
|
413
|
+
if self._image_available:
|
|
414
|
+
return
|
|
415
|
+
|
|
416
|
+
task = asyncio.create_task(self._async_test_image_accessible())
|
|
417
|
+
self._callback_tasks.add(task)
|
|
418
|
+
task.add_done_callback(self._callback_tasks.discard)
|
|
412
419
|
|
|
413
420
|
async def async_update(
|
|
414
421
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
@@ -621,16 +628,17 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
621
628
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
622
629
|
) -> None:
|
|
623
630
|
"""Update sources list from receiver."""
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
631
|
+
async with self._input_func_update_lock:
|
|
632
|
+
if self._device.receiver in [AVR_X, AVR_X_2016]:
|
|
633
|
+
await self._async_update_inputfuncs_avr_x(
|
|
634
|
+
global_update=global_update, cache_id=cache_id
|
|
635
|
+
)
|
|
636
|
+
elif self._device.receiver in [AVR]:
|
|
637
|
+
await self._async_update_inputfuncs_avr(cache_id=cache_id)
|
|
638
|
+
else:
|
|
639
|
+
raise AvrProcessingError(
|
|
640
|
+
"Device is not setup correctly, receiver type not set"
|
|
641
|
+
)
|
|
634
642
|
|
|
635
643
|
async def _async_update_inputfuncs_avr_x(
|
|
636
644
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
denonavr/soundmode.py
CHANGED
|
@@ -22,19 +22,25 @@ from .const import (
|
|
|
22
22
|
AURO_3D_MODE_MAP_MAP_LABELS,
|
|
23
23
|
AURO_MATIC_3D_PRESET_MAP,
|
|
24
24
|
AURO_MATIC_3D_PRESET_MAP_LABELS,
|
|
25
|
+
DAC_FILTERS_MAP,
|
|
26
|
+
DAC_FILTERS_MAP_LABELS,
|
|
25
27
|
DENON_ATTR_SETATTR,
|
|
26
28
|
DIALOG_ENHANCER_LEVEL_MAP,
|
|
27
29
|
DIALOG_ENHANCER_LEVEL_MAP_LABELS,
|
|
28
30
|
EFFECT_SPEAKER_SELECTION_MAP,
|
|
29
31
|
EFFECT_SPEAKER_SELECTION_MAP_LABELS,
|
|
32
|
+
MDAX_MAP,
|
|
33
|
+
MDAX_MAP_LABELS,
|
|
30
34
|
SOUND_MODE_MAPPING,
|
|
31
35
|
Auro3DModes,
|
|
32
36
|
AuroMatic3DPresets,
|
|
37
|
+
DACFilters,
|
|
33
38
|
DialogEnhancerLevels,
|
|
34
39
|
DRCs,
|
|
35
40
|
EffectSpeakers,
|
|
36
41
|
IMAXHPFs,
|
|
37
42
|
IMAXLPFs,
|
|
43
|
+
MDAXs,
|
|
38
44
|
)
|
|
39
45
|
from .exceptions import AvrCommandError, AvrIncompleteResponseError, AvrProcessingError
|
|
40
46
|
from .foundation import DenonAVRFoundation, convert_on_off_bool
|
|
@@ -145,6 +151,14 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
145
151
|
_effect_speakers = get_args(EffectSpeakers)
|
|
146
152
|
_drc: Optional[str] = attr.ib(converter=attr.converters.optional(str), default=None)
|
|
147
153
|
_drcs = get_args(DRCs)
|
|
154
|
+
_mdax: Optional[str] = attr.ib(
|
|
155
|
+
converter=attr.converters.optional(str), default=None
|
|
156
|
+
)
|
|
157
|
+
_mdaxs = get_args(MDAXs)
|
|
158
|
+
_dac_filter: Optional[str] = attr.ib(
|
|
159
|
+
converter=attr.converters.optional(str), default=None
|
|
160
|
+
)
|
|
161
|
+
_dac_filters = get_args(DACFilters)
|
|
148
162
|
_sound_mode_map: Dict[str, list] = attr.ib(
|
|
149
163
|
validator=attr.validators.deep_mapping(
|
|
150
164
|
attr.validators.instance_of(str),
|
|
@@ -188,43 +202,41 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
188
202
|
for tag in self.appcommand_attrs:
|
|
189
203
|
self._device.api.add_appcommand_update_tag(tag)
|
|
190
204
|
|
|
205
|
+
self._device.telnet_api.register_callback("MS", self._soundmode_callback)
|
|
206
|
+
self._device.telnet_api.register_callback("PS", self._neural_x_callback)
|
|
207
|
+
self._device.telnet_api.register_callback("PS", self._imax_callback)
|
|
208
|
+
self._device.telnet_api.register_callback("PS", self._cinema_eq_callback)
|
|
191
209
|
self._device.telnet_api.register_callback(
|
|
192
|
-
"
|
|
193
|
-
)
|
|
194
|
-
self._device.telnet_api.register_callback(
|
|
195
|
-
"PS", self._async_neural_x_callback
|
|
196
|
-
)
|
|
197
|
-
self._device.telnet_api.register_callback("PS", self._async_imax_callback)
|
|
198
|
-
self._device.telnet_api.register_callback(
|
|
199
|
-
"PS", self._async_cinema_eq_callback
|
|
200
|
-
)
|
|
201
|
-
self._device.telnet_api.register_callback(
|
|
202
|
-
"PS", self._async_center_spread_callback
|
|
210
|
+
"PS", self._center_spread_callback
|
|
203
211
|
)
|
|
204
212
|
self._device.telnet_api.register_callback(
|
|
205
|
-
"PS", self.
|
|
213
|
+
"PS", self._loudness_management_callback
|
|
206
214
|
)
|
|
207
215
|
self._device.telnet_api.register_callback(
|
|
208
|
-
"PS", self.
|
|
216
|
+
"PS", self._dialog_enhancer_callback
|
|
209
217
|
)
|
|
210
|
-
self._device.telnet_api.register_callback("PS", self.
|
|
218
|
+
self._device.telnet_api.register_callback("PS", self._auro_callback)
|
|
211
219
|
self._device.telnet_api.register_callback(
|
|
212
|
-
"PS", self.
|
|
220
|
+
"PS", self._dialog_control_callback
|
|
213
221
|
)
|
|
214
222
|
self._device.telnet_api.register_callback(
|
|
215
|
-
"PS", self.
|
|
223
|
+
"PS", self._speaker_virtualizer_callback
|
|
216
224
|
)
|
|
217
225
|
self._device.telnet_api.register_callback(
|
|
218
|
-
"PS", self.
|
|
226
|
+
"PS", self._effect_speaker_selection_callback
|
|
219
227
|
)
|
|
220
|
-
self._device.telnet_api.register_callback("PS", self.
|
|
228
|
+
self._device.telnet_api.register_callback("PS", self._drc_callback)
|
|
229
|
+
|
|
230
|
+
if not self._device.is_denon:
|
|
231
|
+
self._device.telnet_api.register_callback("PS", self._mdax_callback)
|
|
232
|
+
self._device.telnet_api.register_callback(
|
|
233
|
+
"PS", self._dac_filter_callback
|
|
234
|
+
)
|
|
221
235
|
|
|
222
236
|
self._is_setup = True
|
|
223
237
|
_LOGGER.debug("Finished sound mode setup")
|
|
224
238
|
|
|
225
|
-
|
|
226
|
-
self, zone: str, event: str, parameter: str
|
|
227
|
-
) -> None:
|
|
239
|
+
def _soundmode_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
228
240
|
"""Handle a sound mode change event."""
|
|
229
241
|
if self._device.zone != zone:
|
|
230
242
|
return
|
|
@@ -235,15 +247,13 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
235
247
|
|
|
236
248
|
self._sound_mode_raw = parameter
|
|
237
249
|
|
|
238
|
-
|
|
239
|
-
self, zone: str, event: str, parameter: str
|
|
240
|
-
) -> None:
|
|
250
|
+
def _neural_x_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
241
251
|
"""Handle a Neural X:change event."""
|
|
242
252
|
parameter_name_length = len("NEURAL")
|
|
243
253
|
if parameter[:parameter_name_length] == "NEURAL":
|
|
244
254
|
self._neural_x = parameter[parameter_name_length + 1 :]
|
|
245
255
|
|
|
246
|
-
|
|
256
|
+
def _imax_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
247
257
|
"""Handle an IMAX change event."""
|
|
248
258
|
key_value = parameter.split()
|
|
249
259
|
if len(key_value) != 2 or key_value[0][:4] != "IMAX":
|
|
@@ -262,37 +272,31 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
262
272
|
elif key_value[0] == "IMAXSWO":
|
|
263
273
|
self._imax_subwoofer_output = parameter[8:]
|
|
264
274
|
|
|
265
|
-
|
|
266
|
-
self, zone: str, event: str, parameter: str
|
|
267
|
-
) -> None:
|
|
275
|
+
def _cinema_eq_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
268
276
|
"""Handle a Cinema EQ change event."""
|
|
269
277
|
if parameter[:10] == "CINEMA EQ.":
|
|
270
278
|
self._cinema_eq = parameter[10:]
|
|
271
279
|
|
|
272
|
-
|
|
273
|
-
self, zone: str, event: str, parameter: str
|
|
274
|
-
) -> None:
|
|
280
|
+
def _center_spread_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
275
281
|
"""Handle a Center Spread change event."""
|
|
276
282
|
if parameter[:3] == "CES":
|
|
277
283
|
self._center_spread = parameter[4:]
|
|
278
284
|
|
|
279
|
-
|
|
285
|
+
def _loudness_management_callback(
|
|
280
286
|
self, zone: str, event: str, parameter: str
|
|
281
287
|
) -> None:
|
|
282
288
|
"""Handle a Loudness Management change event."""
|
|
283
289
|
if parameter[:3] == "LOM":
|
|
284
290
|
self._loudness_management = parameter[4:]
|
|
285
291
|
|
|
286
|
-
|
|
287
|
-
self, zone: str, event: str, parameter: str
|
|
288
|
-
) -> None:
|
|
292
|
+
def _dialog_enhancer_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
289
293
|
"""Handle a Dialog Enhancer change event."""
|
|
290
294
|
if parameter[:3] == "DEH":
|
|
291
295
|
self._dialog_enhancer_level = DIALOG_ENHANCER_LEVEL_MAP_LABELS[
|
|
292
296
|
parameter[4:]
|
|
293
297
|
]
|
|
294
298
|
|
|
295
|
-
|
|
299
|
+
def _auro_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
296
300
|
"""Handle a Auro change event."""
|
|
297
301
|
key_value = parameter.split()
|
|
298
302
|
if len(key_value) != 2 or key_value[0][:4] != "AURO":
|
|
@@ -305,9 +309,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
305
309
|
elif key_value[0] == "AUROMODE":
|
|
306
310
|
self._auro_3d_mode = AURO_3D_MODE_MAP_MAP_LABELS[parameter[9:]]
|
|
307
311
|
|
|
308
|
-
|
|
309
|
-
self, zone: str, event: str, parameter: str
|
|
310
|
-
) -> None:
|
|
312
|
+
def _dialog_control_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
311
313
|
"""Handle a Dialog Control change event."""
|
|
312
314
|
key_value = parameter.split()
|
|
313
315
|
if len(key_value) != 2 or key_value[0] != "DIC":
|
|
@@ -315,7 +317,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
315
317
|
|
|
316
318
|
self._dialog_control = int(key_value[1])
|
|
317
319
|
|
|
318
|
-
|
|
320
|
+
def _speaker_virtualizer_callback(
|
|
319
321
|
self, zone: str, event: str, parameter: str
|
|
320
322
|
) -> None:
|
|
321
323
|
"""Handle a Speaker Virtualizer change event."""
|
|
@@ -325,7 +327,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
325
327
|
|
|
326
328
|
self._speaker_virtualizer = key_value[1]
|
|
327
329
|
|
|
328
|
-
|
|
330
|
+
def _effect_speaker_selection_callback(
|
|
329
331
|
self, zone: str, event: str, parameter: str
|
|
330
332
|
) -> None:
|
|
331
333
|
"""Handle a Effect Speaker Selection change event."""
|
|
@@ -337,7 +339,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
337
339
|
key_value[1]
|
|
338
340
|
]
|
|
339
341
|
|
|
340
|
-
|
|
342
|
+
def _drc_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
341
343
|
"""Handle a DRC change event."""
|
|
342
344
|
key_value = parameter.split()
|
|
343
345
|
if len(key_value) != 2 or key_value[0] != "DRC":
|
|
@@ -345,6 +347,22 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
345
347
|
|
|
346
348
|
self._drc = key_value[1]
|
|
347
349
|
|
|
350
|
+
def _mdax_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
351
|
+
"""Handle a M-DAX change event."""
|
|
352
|
+
key_value = parameter.split()
|
|
353
|
+
if len(key_value) != 2 or key_value[0] != "MDAX":
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
self._mdax = MDAX_MAP_LABELS[key_value[1]]
|
|
357
|
+
|
|
358
|
+
def _dac_filter_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
359
|
+
"""Handle a DAC Filter change event."""
|
|
360
|
+
key_value = parameter.split()
|
|
361
|
+
if len(key_value) != 2 or key_value[0] != "DACFIL":
|
|
362
|
+
return
|
|
363
|
+
|
|
364
|
+
self._dac_filter = DAC_FILTERS_MAP_LABELS[key_value[1]]
|
|
365
|
+
|
|
348
366
|
async def async_update(
|
|
349
367
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
350
368
|
) -> None:
|
|
@@ -691,6 +709,28 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
691
709
|
"""
|
|
692
710
|
return self._drc
|
|
693
711
|
|
|
712
|
+
@property
|
|
713
|
+
def mdax(self) -> Optional[str]:
|
|
714
|
+
"""
|
|
715
|
+
Return the current M-DAX status.
|
|
716
|
+
|
|
717
|
+
Only available on Marantz devices and when using Telnet.
|
|
718
|
+
|
|
719
|
+
Possible values are: "Off", "Low", "Medium", "High"
|
|
720
|
+
"""
|
|
721
|
+
return self._mdax
|
|
722
|
+
|
|
723
|
+
@property
|
|
724
|
+
def dac_filter(self) -> Optional[str]:
|
|
725
|
+
"""
|
|
726
|
+
Return the current DAC Filter status.
|
|
727
|
+
|
|
728
|
+
Only available on Marantz devices and when using Telnet.
|
|
729
|
+
|
|
730
|
+
Possible values are: "Mode 1", "Mode 2"
|
|
731
|
+
"""
|
|
732
|
+
return self._dac_filter
|
|
733
|
+
|
|
694
734
|
##########
|
|
695
735
|
# Setter #
|
|
696
736
|
##########
|
|
@@ -1227,6 +1267,50 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
1227
1267
|
self._device.urls.command_drc.format(mode=mode)
|
|
1228
1268
|
)
|
|
1229
1269
|
|
|
1270
|
+
async def async_mdax(self, mode: MDAXs) -> None:
|
|
1271
|
+
"""
|
|
1272
|
+
Set M-DAX mode.
|
|
1273
|
+
|
|
1274
|
+
Only available for Marantz devices.
|
|
1275
|
+
"""
|
|
1276
|
+
if self._device.is_denon:
|
|
1277
|
+
raise AvrCommandError("M-DAX is only available for Marantz devices")
|
|
1278
|
+
|
|
1279
|
+
if mode not in self._mdaxs:
|
|
1280
|
+
raise AvrCommandError(f"{mode} is not a valid M-DAX mode")
|
|
1281
|
+
|
|
1282
|
+
local_mode = MDAX_MAP[mode]
|
|
1283
|
+
if self._device.telnet_available:
|
|
1284
|
+
await self._device.telnet_api.async_send_commands(
|
|
1285
|
+
self._device.telnet_commands.command_mdax.format(mode=local_mode)
|
|
1286
|
+
)
|
|
1287
|
+
else:
|
|
1288
|
+
await self._device.api.async_get_command(
|
|
1289
|
+
self._device.urls.command_mdax.format(mode=local_mode)
|
|
1290
|
+
)
|
|
1291
|
+
|
|
1292
|
+
async def async_dac_filter(self, mode: DACFilters) -> None:
|
|
1293
|
+
"""
|
|
1294
|
+
Set DAC Filter mode.
|
|
1295
|
+
|
|
1296
|
+
Only available for Marantz devices.
|
|
1297
|
+
"""
|
|
1298
|
+
if self._device.is_denon:
|
|
1299
|
+
raise AvrCommandError("DAC Filter is only available for Marantz devices")
|
|
1300
|
+
|
|
1301
|
+
if mode not in self._dac_filters:
|
|
1302
|
+
raise AvrCommandError(f"{mode} is not a valid DAC Filter mode")
|
|
1303
|
+
|
|
1304
|
+
local_mode = DAC_FILTERS_MAP[mode]
|
|
1305
|
+
if self._device.telnet_available:
|
|
1306
|
+
await self._device.telnet_api.async_send_commands(
|
|
1307
|
+
self._device.telnet_commands.command_dac_filter.format(mode=local_mode)
|
|
1308
|
+
)
|
|
1309
|
+
else:
|
|
1310
|
+
await self._device.api.async_get_command(
|
|
1311
|
+
self._device.urls.command_dac_filter.format(mode=local_mode)
|
|
1312
|
+
)
|
|
1313
|
+
|
|
1230
1314
|
|
|
1231
1315
|
def sound_mode_factory(instance: DenonAVRFoundation) -> DenonAVRSoundMode:
|
|
1232
1316
|
"""Create DenonAVRSoundMode at receiver instances."""
|
denonavr/ssdp.py
CHANGED
|
@@ -91,17 +91,18 @@ async def async_identify_denonavr_receivers() -> List[Dict]:
|
|
|
91
91
|
# Check which responding device is a DenonAVR device and prepare output
|
|
92
92
|
receivers = []
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
94
|
+
async with httpx.AsyncClient() as client:
|
|
95
|
+
for url in urls:
|
|
96
|
+
try:
|
|
97
|
+
async with client.stream("GET", url, timeout=5.0) as res:
|
|
98
|
+
res.raise_for_status()
|
|
99
|
+
await res.aread()
|
|
100
|
+
except httpx.HTTPError:
|
|
101
|
+
continue
|
|
102
|
+
else:
|
|
103
|
+
receiver = evaluate_scpd_xml(url, res.text)
|
|
104
|
+
if receiver is not None:
|
|
105
|
+
receivers.append(receiver)
|
|
105
106
|
|
|
106
107
|
return receivers
|
|
107
108
|
|
denonavr/tonecontrol.py
CHANGED
|
@@ -67,16 +67,12 @@ class DenonAVRToneControl(DenonAVRFoundation):
|
|
|
67
67
|
for tag in self.appcommand_attrs:
|
|
68
68
|
self._device.api.add_appcommand_update_tag(tag)
|
|
69
69
|
|
|
70
|
-
self._device.telnet_api.register_callback(
|
|
71
|
-
"PS", self._async_sound_detail_callback
|
|
72
|
-
)
|
|
70
|
+
self._device.telnet_api.register_callback("PS", self._sound_detail_callback)
|
|
73
71
|
|
|
74
72
|
self._is_setup = True
|
|
75
73
|
_LOGGER.debug("Finished tone control setup")
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
self, zone: str, event: str, parameter: str
|
|
79
|
-
) -> None:
|
|
75
|
+
def _sound_detail_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
80
76
|
"""Handle a sound detail change event."""
|
|
81
77
|
if self._device.zone != zone:
|
|
82
78
|
return
|
denonavr/volume.py
CHANGED
|
@@ -82,25 +82,17 @@ class DenonAVRVolume(DenonAVRFoundation):
|
|
|
82
82
|
for tag in self.appcommand_attrs:
|
|
83
83
|
self._device.api.add_appcommand_update_tag(tag)
|
|
84
84
|
|
|
85
|
-
self._device.telnet_api.register_callback("MV", self.
|
|
86
|
-
self._device.telnet_api.register_callback("MU", self.
|
|
87
|
-
self._device.telnet_api.register_callback(
|
|
88
|
-
|
|
89
|
-
)
|
|
90
|
-
self._device.telnet_api.register_callback(
|
|
91
|
-
|
|
92
|
-
)
|
|
93
|
-
self._device.telnet_api.register_callback(
|
|
94
|
-
"PS", self._async_subwoofer_levels_callback
|
|
95
|
-
)
|
|
96
|
-
self._device.telnet_api.register_callback("PS", self._async_lfe_callback)
|
|
97
|
-
self._device.telnet_api.register_callback("PS", self._async_bass_sync_callback)
|
|
85
|
+
self._device.telnet_api.register_callback("MV", self._volume_callback)
|
|
86
|
+
self._device.telnet_api.register_callback("MU", self._mute_callback)
|
|
87
|
+
self._device.telnet_api.register_callback("CV", self._channel_volume_callback)
|
|
88
|
+
self._device.telnet_api.register_callback("PS", self._subwoofer_state_callback)
|
|
89
|
+
self._device.telnet_api.register_callback("PS", self._subwoofer_levels_callback)
|
|
90
|
+
self._device.telnet_api.register_callback("PS", self._lfe_callback)
|
|
91
|
+
self._device.telnet_api.register_callback("PS", self._bass_sync_callback)
|
|
98
92
|
|
|
99
93
|
self._is_setup = True
|
|
100
94
|
|
|
101
|
-
|
|
102
|
-
self, zone: str, event: str, parameter: str
|
|
103
|
-
) -> None:
|
|
95
|
+
def _volume_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
104
96
|
"""Handle a volume change event."""
|
|
105
97
|
if self._device.zone != zone:
|
|
106
98
|
return
|
|
@@ -112,16 +104,14 @@ class DenonAVRVolume(DenonAVRFoundation):
|
|
|
112
104
|
fraction = 0.1 * float(parameter[2])
|
|
113
105
|
self._volume = -80.0 + whole_number + fraction
|
|
114
106
|
|
|
115
|
-
|
|
107
|
+
def _mute_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
116
108
|
"""Handle a muting change event."""
|
|
117
109
|
if self._device.zone != zone:
|
|
118
110
|
return
|
|
119
111
|
|
|
120
112
|
self._muted = parameter
|
|
121
113
|
|
|
122
|
-
|
|
123
|
-
self, zone: str, event: str, parameter: str
|
|
124
|
-
) -> None:
|
|
114
|
+
def _channel_volume_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
125
115
|
"""Handle a channel volume change event."""
|
|
126
116
|
if event != "CV":
|
|
127
117
|
return
|
|
@@ -141,16 +131,12 @@ class DenonAVRVolume(DenonAVRFoundation):
|
|
|
141
131
|
volume = channel_volume[1]
|
|
142
132
|
self._channel_volumes[channel] = CHANNEL_VOLUME_MAP[volume]
|
|
143
133
|
|
|
144
|
-
|
|
145
|
-
self, zone: str, event: str, parameter: str
|
|
146
|
-
) -> None:
|
|
134
|
+
def _subwoofer_state_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
147
135
|
"""Handle a subwoofer state change event."""
|
|
148
136
|
if parameter[:3] == "SWR":
|
|
149
137
|
self._subwoofer = parameter[4:]
|
|
150
138
|
|
|
151
|
-
|
|
152
|
-
self, zone: str, event: str, parameter: str
|
|
153
|
-
) -> None:
|
|
139
|
+
def _subwoofer_levels_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
154
140
|
"""Handle a subwoofer levels change event."""
|
|
155
141
|
if parameter[:3] != "SWL":
|
|
156
142
|
return
|
|
@@ -173,16 +159,14 @@ class DenonAVRVolume(DenonAVRFoundation):
|
|
|
173
159
|
elif level in CHANNEL_VOLUME_MAP:
|
|
174
160
|
self._subwoofer_levels[subwoofer] = CHANNEL_VOLUME_MAP[level]
|
|
175
161
|
|
|
176
|
-
|
|
162
|
+
def _lfe_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
177
163
|
"""Handle a LFE change event."""
|
|
178
164
|
if parameter[:3] != "LFE":
|
|
179
165
|
return
|
|
180
166
|
|
|
181
167
|
self._lfe = int(parameter[4:]) * -1
|
|
182
168
|
|
|
183
|
-
|
|
184
|
-
self, zone: str, event: str, parameter: str
|
|
185
|
-
) -> None:
|
|
169
|
+
def _bass_sync_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
186
170
|
"""Handle a LFE change event."""
|
|
187
171
|
if parameter[:3] != "BSC":
|
|
188
172
|
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: denonavr
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Automation Library for Denon AVR receivers
|
|
5
5
|
Author-email: Oliver Goetz <scarface@mywoh.de>
|
|
6
6
|
License: MIT
|
|
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
24
|
Requires-Python: >=3.8
|
|
24
25
|
Description-Content-Type: text/markdown; charset=UTF-8
|
|
25
26
|
License-File: LICENSE
|