denonavr 0.11.5__py3-none-any.whl → 1.0.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 -24
- denonavr/api.py +46 -35
- denonavr/audyssey.py +12 -7
- denonavr/const.py +168 -59
- denonavr/decorators.py +6 -46
- denonavr/denonavr.py +46 -186
- denonavr/foundation.py +120 -33
- denonavr/input.py +16 -6
- denonavr/soundmode.py +51 -36
- denonavr/tonecontrol.py +47 -12
- denonavr/volume.py +4 -2
- denonavr-1.0.0.dist-info/METADATA +158 -0
- denonavr-1.0.0.dist-info/RECORD +19 -0
- {denonavr-0.11.5.dist-info → denonavr-1.0.0.dist-info}/WHEEL +1 -1
- denonavr-0.11.5.dist-info/METADATA +0 -290
- denonavr-0.11.5.dist-info/RECORD +0 -19
- {denonavr-0.11.5.dist-info → denonavr-1.0.0.dist-info}/LICENSE +0 -0
- {denonavr-0.11.5.dist-info → denonavr-1.0.0.dist-info}/top_level.txt +0 -0
denonavr/foundation.py
CHANGED
|
@@ -143,16 +143,19 @@ class DenonAVRDeviceInfo:
|
|
|
143
143
|
async def async_setup(self) -> None:
|
|
144
144
|
"""Ensure that configuration is loaded from receiver asynchronously."""
|
|
145
145
|
async with self._setup_lock:
|
|
146
|
-
|
|
146
|
+
_LOGGER.debug("Starting device setup")
|
|
147
147
|
# Reduce read timeout during receiver identification
|
|
148
148
|
# deviceinfo endpoint takes very long to return 404
|
|
149
149
|
timeout = self.api.timeout
|
|
150
150
|
self.api.timeout = httpx.Timeout(self.api.timeout.connect)
|
|
151
151
|
try:
|
|
152
|
+
_LOGGER.debug("Identifying receiver")
|
|
152
153
|
await self.async_identify_receiver()
|
|
154
|
+
_LOGGER.debug("Getting device info")
|
|
153
155
|
await self.async_get_device_info()
|
|
154
156
|
finally:
|
|
155
157
|
self.api.timeout = timeout
|
|
158
|
+
_LOGGER.debug("Identifying update method")
|
|
156
159
|
await self.async_identify_update_method()
|
|
157
160
|
|
|
158
161
|
# Add tags for a potential AppCommand.xml update
|
|
@@ -166,17 +169,20 @@ class DenonAVRDeviceInfo:
|
|
|
166
169
|
self.telnet_api.register_callback(power_event, self._async_power_callback)
|
|
167
170
|
|
|
168
171
|
self._is_setup = True
|
|
172
|
+
_LOGGER.debug("Finished device setup")
|
|
169
173
|
|
|
170
174
|
async def async_update(
|
|
171
175
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
172
176
|
) -> None:
|
|
173
177
|
"""Update status asynchronously."""
|
|
178
|
+
_LOGGER.debug("Starting device update")
|
|
174
179
|
# Ensure instance is setup before updating
|
|
175
180
|
if not self._is_setup:
|
|
176
181
|
await self.async_setup()
|
|
177
182
|
|
|
178
183
|
# Update power status
|
|
179
184
|
await self.async_update_power(global_update=global_update, cache_id=cache_id)
|
|
185
|
+
_LOGGER.debug("Finished device update")
|
|
180
186
|
|
|
181
187
|
async def async_identify_receiver(self) -> None:
|
|
182
188
|
"""Identify receiver asynchronously."""
|
|
@@ -196,9 +202,9 @@ class DenonAVRDeviceInfo:
|
|
|
196
202
|
)
|
|
197
203
|
except (AvrTimoutError, AvrNetworkError) as err:
|
|
198
204
|
_LOGGER.debug(
|
|
199
|
-
"Connection error on port %s when identifying receiver",
|
|
205
|
+
"Connection error on port %s when identifying receiver: %s",
|
|
200
206
|
r_type.port,
|
|
201
|
-
|
|
207
|
+
err,
|
|
202
208
|
)
|
|
203
209
|
|
|
204
210
|
# Raise error only when occurred at both types
|
|
@@ -210,22 +216,32 @@ class DenonAVRDeviceInfo:
|
|
|
210
216
|
_LOGGER.debug(
|
|
211
217
|
(
|
|
212
218
|
"Request error on port %s when identifying receiver, "
|
|
213
|
-
"device is not a %s
|
|
219
|
+
"device is not a %s receiver: %s"
|
|
214
220
|
),
|
|
215
221
|
r_type.port,
|
|
216
222
|
r_type.type,
|
|
217
|
-
|
|
223
|
+
err,
|
|
218
224
|
)
|
|
219
225
|
else:
|
|
220
226
|
is_avr_x = self._is_avr_x(xml)
|
|
221
227
|
if is_avr_x:
|
|
222
228
|
self.receiver = r_type
|
|
229
|
+
_LOGGER.info(
|
|
230
|
+
"Identified %s receiver using port %s",
|
|
231
|
+
r_type.type,
|
|
232
|
+
r_type.port,
|
|
233
|
+
)
|
|
223
234
|
# Receiver identified, return
|
|
224
235
|
return
|
|
225
236
|
|
|
226
237
|
# If check of Deviceinfo.xml was not successful, receiver is type AVR
|
|
227
238
|
self.receiver = AVR
|
|
228
239
|
self.api.port = AVR.port
|
|
240
|
+
_LOGGER.info(
|
|
241
|
+
"Identified %s receiver using port %s",
|
|
242
|
+
AVR.type,
|
|
243
|
+
AVR.port,
|
|
244
|
+
)
|
|
229
245
|
|
|
230
246
|
@staticmethod
|
|
231
247
|
def _is_avr_x(deviceinfo: ET.Element) -> bool:
|
|
@@ -275,13 +291,11 @@ class DenonAVRDeviceInfo:
|
|
|
275
291
|
)
|
|
276
292
|
except (AvrTimoutError, AvrNetworkError) as err:
|
|
277
293
|
_LOGGER.debug(
|
|
278
|
-
"Connection error when identifying update method",
|
|
294
|
+
"Connection error when identifying update method: %s", err
|
|
279
295
|
)
|
|
280
296
|
raise
|
|
281
297
|
except AvrRequestError as err:
|
|
282
|
-
_LOGGER.debug(
|
|
283
|
-
"Request error when identifying update method", exc_info=err
|
|
284
|
-
)
|
|
298
|
+
_LOGGER.debug("Request error when identifying update method: %s", err)
|
|
285
299
|
self.use_avr_2016_update = False
|
|
286
300
|
_LOGGER.info("AVR-X device, AppCommand.xml interface not supported")
|
|
287
301
|
else:
|
|
@@ -294,11 +308,11 @@ class DenonAVRDeviceInfo:
|
|
|
294
308
|
xml = await self.api.async_get_xml(self.urls.mainzone)
|
|
295
309
|
except (AvrTimoutError, AvrNetworkError) as err:
|
|
296
310
|
_LOGGER.debug(
|
|
297
|
-
"Connection error when identifying update method",
|
|
311
|
+
"Connection error when identifying update method: %s", err
|
|
298
312
|
)
|
|
299
313
|
raise
|
|
300
314
|
except AvrRequestError as err:
|
|
301
|
-
_LOGGER.debug("Request error getting friendly name",
|
|
315
|
+
_LOGGER.debug("Request error getting friendly name: %s", err)
|
|
302
316
|
_LOGGER.info(
|
|
303
317
|
"Receiver name could not be determined. Using standard"
|
|
304
318
|
" name: Denon AVR"
|
|
@@ -320,7 +334,7 @@ class DenonAVRDeviceInfo:
|
|
|
320
334
|
# Result is cached that it can be reused during update
|
|
321
335
|
await self.api.async_get_global_appcommand(cache_id=cache_id)
|
|
322
336
|
except (AvrTimoutError, AvrNetworkError) as err:
|
|
323
|
-
_LOGGER.debug("Connection error when verifying update method",
|
|
337
|
+
_LOGGER.debug("Connection error when verifying update method: %s", err)
|
|
324
338
|
raise
|
|
325
339
|
except AvrForbiddenError:
|
|
326
340
|
# Recovery in case receiver changes port from 80 to 8080 which
|
|
@@ -338,7 +352,7 @@ class DenonAVRDeviceInfo:
|
|
|
338
352
|
else:
|
|
339
353
|
raise
|
|
340
354
|
except AvrIncompleteResponseError as err:
|
|
341
|
-
_LOGGER.debug("Request error when verifying update method",
|
|
355
|
+
_LOGGER.debug("Request error when verifying update method: %s", err)
|
|
342
356
|
# Only AVR_X devices support both interfaces
|
|
343
357
|
if self.receiver == AVR_X:
|
|
344
358
|
_LOGGER.warning(
|
|
@@ -378,10 +392,10 @@ class DenonAVRDeviceInfo:
|
|
|
378
392
|
try:
|
|
379
393
|
res = await self.api.async_get(command, port=port)
|
|
380
394
|
except AvrTimoutError as err:
|
|
381
|
-
_LOGGER.debug("Timeout when getting device info",
|
|
395
|
+
_LOGGER.debug("Timeout when getting device info: %s", err)
|
|
382
396
|
raise
|
|
383
397
|
except AvrNetworkError as err:
|
|
384
|
-
_LOGGER.debug("Network error getting device info",
|
|
398
|
+
_LOGGER.debug("Network error getting device info: %s", err)
|
|
385
399
|
raise
|
|
386
400
|
except AvrRequestError as err:
|
|
387
401
|
_LOGGER.error(
|
|
@@ -444,7 +458,7 @@ class DenonAVRDeviceInfo:
|
|
|
444
458
|
self.urls.appcommand, tuple(power_appcommand), cache_id=cache_id
|
|
445
459
|
)
|
|
446
460
|
except AvrRequestError as err:
|
|
447
|
-
_LOGGER.debug("Error when getting power status",
|
|
461
|
+
_LOGGER.debug("Error when getting power status: %s", err)
|
|
448
462
|
raise
|
|
449
463
|
|
|
450
464
|
# Extract relevant information
|
|
@@ -482,7 +496,7 @@ class DenonAVRDeviceInfo:
|
|
|
482
496
|
xml = await self.api.async_get_xml(url, cache_id=cache_id)
|
|
483
497
|
except AvrRequestError as err:
|
|
484
498
|
_LOGGER.debug(
|
|
485
|
-
"Error when getting power status from url %s", url,
|
|
499
|
+
"Error when getting power status from url %s: %s", url, err
|
|
486
500
|
)
|
|
487
501
|
continue
|
|
488
502
|
|
|
@@ -535,6 +549,87 @@ class DenonAVRDeviceInfo:
|
|
|
535
549
|
else:
|
|
536
550
|
await self.api.async_get_command(self.urls.command_power_standby)
|
|
537
551
|
|
|
552
|
+
async def async_cursor_up(self) -> None:
|
|
553
|
+
"""Cursor Up on receiver via HTTP get command."""
|
|
554
|
+
if self.telnet_available:
|
|
555
|
+
await self.telnet_api.async_send_commands(
|
|
556
|
+
self.telnet_commands.command_cusor_up
|
|
557
|
+
)
|
|
558
|
+
else:
|
|
559
|
+
await self.api.async_get_command(self.urls.command_cusor_up)
|
|
560
|
+
|
|
561
|
+
async def async_cursor_down(self) -> None:
|
|
562
|
+
"""Cursor Down on receiver via HTTP get command."""
|
|
563
|
+
if self.telnet_available:
|
|
564
|
+
await self.telnet_api.async_send_commands(
|
|
565
|
+
self.telnet_commands.command_cusor_down
|
|
566
|
+
)
|
|
567
|
+
else:
|
|
568
|
+
await self.api.async_get_command(self.urls.command_cusor_down)
|
|
569
|
+
|
|
570
|
+
async def async_cursor_left(self) -> None:
|
|
571
|
+
"""Cursor Left on receiver via HTTP get command."""
|
|
572
|
+
if self.telnet_available:
|
|
573
|
+
await self.telnet_api.async_send_commands(
|
|
574
|
+
self.telnet_commands.command_cusor_left
|
|
575
|
+
)
|
|
576
|
+
else:
|
|
577
|
+
await self.api.async_get_command(self.urls.command_cusor_left)
|
|
578
|
+
|
|
579
|
+
async def async_cursor_right(self) -> None:
|
|
580
|
+
"""Cursor Right on receiver via HTTP get command."""
|
|
581
|
+
if self.telnet_available:
|
|
582
|
+
await self.telnet_api.async_send_commands(
|
|
583
|
+
self.telnet_commands.command_cusor_right
|
|
584
|
+
)
|
|
585
|
+
else:
|
|
586
|
+
await self.api.async_get_command(self.urls.command_cusor_right)
|
|
587
|
+
|
|
588
|
+
async def async_cursor_enter(self) -> None:
|
|
589
|
+
"""Cursor Enter on receiver via HTTP get command."""
|
|
590
|
+
if self.telnet_available:
|
|
591
|
+
await self.telnet_api.async_send_commands(
|
|
592
|
+
self.telnet_commands.command_cusor_enter
|
|
593
|
+
)
|
|
594
|
+
else:
|
|
595
|
+
await self.api.async_get_command(self.urls.command_cusor_enter)
|
|
596
|
+
|
|
597
|
+
async def async_back(self) -> None:
|
|
598
|
+
"""Back command on receiver via HTTP get command."""
|
|
599
|
+
if self.telnet_available:
|
|
600
|
+
await self.telnet_api.async_send_commands(self.telnet_commands.command_back)
|
|
601
|
+
else:
|
|
602
|
+
await self.api.async_get_command(self.urls.command_back)
|
|
603
|
+
|
|
604
|
+
async def async_info(self) -> None:
|
|
605
|
+
"""Info OSD on receiver via HTTP get command."""
|
|
606
|
+
if self.telnet_available:
|
|
607
|
+
await self.telnet_api.async_send_commands(self.telnet_commands.command_info)
|
|
608
|
+
else:
|
|
609
|
+
await self.api.async_get_command(self.urls.command_info)
|
|
610
|
+
|
|
611
|
+
async def async_options(self) -> None:
|
|
612
|
+
"""Options menu on receiver via HTTP get command."""
|
|
613
|
+
await self.api.async_get_command(self.urls.command_options)
|
|
614
|
+
|
|
615
|
+
async def async_settings_menu(self) -> None:
|
|
616
|
+
"""Options menu on receiver via HTTP get command."""
|
|
617
|
+
res = await self.api.async_get_command(self.urls.command_setup_query)
|
|
618
|
+
if self.telnet_available:
|
|
619
|
+
if res is not None and res == "MNMEN ON":
|
|
620
|
+
await self.telnet_api.async_send_commands(
|
|
621
|
+
self.telnet_commands.command_setup_close
|
|
622
|
+
)
|
|
623
|
+
else:
|
|
624
|
+
await self.telnet_api.async_send_commands(
|
|
625
|
+
self.telnet_commands.command_setup_open
|
|
626
|
+
)
|
|
627
|
+
else:
|
|
628
|
+
if res is not None and res == "MNMEN ON":
|
|
629
|
+
await self.api.async_get_command(self.urls.command_setup_close)
|
|
630
|
+
else:
|
|
631
|
+
await self.api.async_get_command(self.urls.command_setup_open)
|
|
632
|
+
|
|
538
633
|
|
|
539
634
|
@attr.s(auto_attribs=True, on_setattr=DENON_ATTR_SETATTR)
|
|
540
635
|
class DenonAVRFoundation:
|
|
@@ -558,7 +653,6 @@ class DenonAVRFoundation:
|
|
|
558
653
|
appcommand0300: bool = False,
|
|
559
654
|
global_update: bool = False,
|
|
560
655
|
cache_id: Optional[Hashable] = None,
|
|
561
|
-
ignore_missing_response: bool = False,
|
|
562
656
|
):
|
|
563
657
|
"""Update attributes from AppCommand.xml."""
|
|
564
658
|
# Copy that we do not accidently change the wrong dict
|
|
@@ -581,7 +675,7 @@ class DenonAVRFoundation:
|
|
|
581
675
|
url, tags, cache_id=cache_id
|
|
582
676
|
)
|
|
583
677
|
except AvrRequestError as err:
|
|
584
|
-
_LOGGER.debug("Error when getting status update",
|
|
678
|
+
_LOGGER.debug("Error when getting status update: %s", err)
|
|
585
679
|
raise
|
|
586
680
|
|
|
587
681
|
# Extract relevant information
|
|
@@ -616,10 +710,10 @@ class DenonAVRFoundation:
|
|
|
616
710
|
|
|
617
711
|
except (AttributeError, IndexError) as err:
|
|
618
712
|
_LOGGER.debug(
|
|
619
|
-
"Failed updating attribute %s for zone %s",
|
|
713
|
+
"Failed updating attribute %s for zone %s: %s",
|
|
620
714
|
pattern.update_attribute,
|
|
621
715
|
self._device.zone,
|
|
622
|
-
|
|
716
|
+
err,
|
|
623
717
|
)
|
|
624
718
|
|
|
625
719
|
if start == success:
|
|
@@ -627,24 +721,17 @@ class DenonAVRFoundation:
|
|
|
627
721
|
update_attrs.pop(app_command, None)
|
|
628
722
|
|
|
629
723
|
# Check if each attribute was updated
|
|
630
|
-
if update_attrs
|
|
724
|
+
if update_attrs:
|
|
631
725
|
raise AvrProcessingError(
|
|
632
726
|
f"Some attributes of zone {self._device.zone} not found on update:"
|
|
633
727
|
f" {update_attrs}"
|
|
634
728
|
)
|
|
635
|
-
if update_attrs and ignore_missing_response:
|
|
636
|
-
_LOGGER.debug(
|
|
637
|
-
"Some attributes of zone %s not found on update: %s",
|
|
638
|
-
self._device.zone,
|
|
639
|
-
update_attrs,
|
|
640
|
-
)
|
|
641
729
|
|
|
642
730
|
async def async_update_attrs_status_xml(
|
|
643
731
|
self,
|
|
644
732
|
update_attrs: Dict[str, str],
|
|
645
733
|
urls: List[str],
|
|
646
734
|
cache_id: Optional[Hashable] = None,
|
|
647
|
-
ignore_missing_response: bool = False,
|
|
648
735
|
):
|
|
649
736
|
"""
|
|
650
737
|
Update attributes from status xml.
|
|
@@ -664,7 +751,7 @@ class DenonAVRFoundation:
|
|
|
664
751
|
xml = await self._device.api.async_get_xml(url, cache_id=cache_id)
|
|
665
752
|
except AvrRequestError as err:
|
|
666
753
|
_LOGGER.debug(
|
|
667
|
-
"Error when getting status update from url %s", url,
|
|
754
|
+
"Error when getting status update from url %s: %s", url, err
|
|
668
755
|
)
|
|
669
756
|
continue
|
|
670
757
|
attrs = deepcopy(update_attrs)
|
|
@@ -683,10 +770,10 @@ class DenonAVRFoundation:
|
|
|
683
770
|
|
|
684
771
|
except (AttributeError, IndexError) as err:
|
|
685
772
|
_LOGGER.debug(
|
|
686
|
-
"Failed updating attribute %s for zone %s",
|
|
773
|
+
"Failed updating attribute %s for zone %s: %s",
|
|
687
774
|
name,
|
|
688
775
|
self._device.zone,
|
|
689
|
-
|
|
776
|
+
err,
|
|
690
777
|
)
|
|
691
778
|
|
|
692
779
|
# All done, no need for continuing
|
|
@@ -694,7 +781,7 @@ class DenonAVRFoundation:
|
|
|
694
781
|
break
|
|
695
782
|
|
|
696
783
|
# Check if each attribute was updated
|
|
697
|
-
if update_attrs
|
|
784
|
+
if update_attrs:
|
|
698
785
|
raise AvrProcessingError(
|
|
699
786
|
f"Some attributes of zone {self._device.zone} not found on update:"
|
|
700
787
|
f" {update_attrs}"
|
denonavr/input.py
CHANGED
|
@@ -272,7 +272,8 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
272
272
|
|
|
273
273
|
def _update_netaudio(self) -> None:
|
|
274
274
|
"""Update netaudio information."""
|
|
275
|
-
if self._device.
|
|
275
|
+
if self._device.telnet_available:
|
|
276
|
+
self._device.telnet_api.send_commands("NSE", skip_confirmation=True)
|
|
276
277
|
self._schedule_netaudio_update()
|
|
277
278
|
else:
|
|
278
279
|
self._stop_media_update()
|
|
@@ -314,7 +315,10 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
314
315
|
|
|
315
316
|
def _update_tuner(self) -> None:
|
|
316
317
|
"""Update tuner information."""
|
|
317
|
-
if self._device.
|
|
318
|
+
if self._device.telnet_available:
|
|
319
|
+
self._device.telnet_api.send_commands(
|
|
320
|
+
"TFAN?", "TFANNAME?", skip_confirmation=True
|
|
321
|
+
)
|
|
318
322
|
self._schedule_tuner_update()
|
|
319
323
|
else:
|
|
320
324
|
self._stop_media_update()
|
|
@@ -357,7 +361,8 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
357
361
|
|
|
358
362
|
def _update_hdtuner(self) -> None:
|
|
359
363
|
"""Update HD tuner information."""
|
|
360
|
-
if self._device.
|
|
364
|
+
if self._device.telnet_available:
|
|
365
|
+
self._device.telnet_api.send_commands("HD?", skip_confirmation=True)
|
|
361
366
|
self._schedule_hdtuner_update()
|
|
362
367
|
else:
|
|
363
368
|
self._stop_media_update()
|
|
@@ -402,18 +407,23 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
402
407
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
403
408
|
) -> None:
|
|
404
409
|
"""Update input functions asynchronously."""
|
|
410
|
+
_LOGGER.debug("Starting input update")
|
|
405
411
|
# Ensure instance is setup before updating
|
|
406
412
|
if not self._is_setup:
|
|
407
413
|
self.setup()
|
|
408
414
|
|
|
409
415
|
# Update input functions
|
|
416
|
+
_LOGGER.debug("Updating input functions")
|
|
410
417
|
await self.async_update_inputfuncs(
|
|
411
418
|
global_update=global_update, cache_id=cache_id
|
|
412
419
|
)
|
|
413
420
|
# Update state
|
|
421
|
+
_LOGGER.debug("Updating input state")
|
|
414
422
|
await self.async_update_state(global_update=global_update, cache_id=cache_id)
|
|
415
423
|
# Update media state
|
|
424
|
+
_LOGGER.debug("Updating media state")
|
|
416
425
|
await self.async_update_media_state(cache_id=cache_id)
|
|
426
|
+
_LOGGER.debug("Finished input update")
|
|
417
427
|
|
|
418
428
|
async def async_get_sources_deviceinfo(self) -> Dict[str, str]:
|
|
419
429
|
"""Get sources from Deviceinfo.xml."""
|
|
@@ -423,7 +433,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
423
433
|
self._device.urls.deviceinfo, cache_id=id(self._device)
|
|
424
434
|
)
|
|
425
435
|
except AvrRequestError as err:
|
|
426
|
-
_LOGGER.debug("Error when getting sources",
|
|
436
|
+
_LOGGER.debug("Error when getting sources: %s", err)
|
|
427
437
|
raise
|
|
428
438
|
|
|
429
439
|
receiver_sources = {}
|
|
@@ -481,7 +491,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
481
491
|
self._device.urls.appcommand, tags, cache_id=cache_id
|
|
482
492
|
)
|
|
483
493
|
except AvrRequestError as err:
|
|
484
|
-
_LOGGER.debug("Error when getting changed sources",
|
|
494
|
+
_LOGGER.debug("Error when getting changed sources: %s", err)
|
|
485
495
|
raise
|
|
486
496
|
|
|
487
497
|
for child in xml.findall(
|
|
@@ -544,7 +554,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
544
554
|
f" {self._device.receiver.type}"
|
|
545
555
|
)
|
|
546
556
|
except AvrRequestError as err:
|
|
547
|
-
_LOGGER.debug("Error when getting changed sources",
|
|
557
|
+
_LOGGER.debug("Error when getting changed sources: %s", err)
|
|
548
558
|
raise
|
|
549
559
|
|
|
550
560
|
# Get the relevant tags from XML structure
|
denonavr/soundmode.py
CHANGED
|
@@ -16,14 +16,8 @@ from typing import Dict, List, Optional
|
|
|
16
16
|
import attr
|
|
17
17
|
|
|
18
18
|
from .appcommand import AppCommands
|
|
19
|
-
from .const import
|
|
20
|
-
|
|
21
|
-
AVR_X,
|
|
22
|
-
AVR_X_2016,
|
|
23
|
-
DENON_ATTR_SETATTR,
|
|
24
|
-
SOUND_MODE_MAPPING,
|
|
25
|
-
)
|
|
26
|
-
from .exceptions import AvrCommandError, AvrProcessingError
|
|
19
|
+
from .const import ALL_ZONE_STEREO, DENON_ATTR_SETATTR, SOUND_MODE_MAPPING
|
|
20
|
+
from .exceptions import AvrCommandError, AvrIncompleteResponseError, AvrProcessingError
|
|
27
21
|
from .foundation import DenonAVRFoundation
|
|
28
22
|
|
|
29
23
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -94,6 +88,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
94
88
|
init=False,
|
|
95
89
|
)
|
|
96
90
|
_setup_lock: asyncio.Lock = attr.ib(default=attr.Factory(asyncio.Lock))
|
|
91
|
+
_appcommand_active: bool = attr.ib(converter=bool, default=True, init=False)
|
|
97
92
|
|
|
98
93
|
# Update tags for attributes
|
|
99
94
|
# AppCommand.xml interface
|
|
@@ -105,22 +100,22 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
105
100
|
async def async_setup(self) -> None:
|
|
106
101
|
"""Ensure that the instance is initialized."""
|
|
107
102
|
async with self._setup_lock:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
self.
|
|
116
|
-
|
|
117
|
-
await self.async_update_sound_mode()
|
|
103
|
+
_LOGGER.debug("Starting sound mode setup")
|
|
104
|
+
|
|
105
|
+
# The first update determines if sound mode is supported
|
|
106
|
+
await self.async_update_sound_mode()
|
|
107
|
+
|
|
108
|
+
if self._support_sound_mode and self._appcommand_active:
|
|
109
|
+
# Add tags for a potential AppCommand.xml update
|
|
110
|
+
for tag in self.appcommand_attrs:
|
|
111
|
+
self._device.api.add_appcommand_update_tag(tag)
|
|
118
112
|
|
|
119
113
|
self._device.telnet_api.register_callback(
|
|
120
114
|
"MS", self._async_soundmode_callback
|
|
121
115
|
)
|
|
122
116
|
|
|
123
117
|
self._is_setup = True
|
|
118
|
+
_LOGGER.debug("Finished sound mode setup")
|
|
124
119
|
|
|
125
120
|
async def _async_soundmode_callback(
|
|
126
121
|
self, zone: str, event: str, parameter: str
|
|
@@ -135,6 +130,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
135
130
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
136
131
|
) -> None:
|
|
137
132
|
"""Update sound mode asynchronously."""
|
|
133
|
+
_LOGGER.debug("Starting sound mode update")
|
|
138
134
|
# Ensure instance is setup before updating
|
|
139
135
|
if not self._is_setup:
|
|
140
136
|
await self.async_setup()
|
|
@@ -143,6 +139,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
143
139
|
await self.async_update_sound_mode(
|
|
144
140
|
global_update=global_update, cache_id=cache_id
|
|
145
141
|
)
|
|
142
|
+
_LOGGER.debug("Finished sound mode update")
|
|
146
143
|
|
|
147
144
|
async def async_update_sound_mode(
|
|
148
145
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
@@ -153,29 +150,47 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
153
150
|
"Device is not setup correctly, update method not set"
|
|
154
151
|
)
|
|
155
152
|
|
|
156
|
-
if self.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
153
|
+
if self._is_setup and not self._support_sound_mode:
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
if self._device.use_avr_2016_update and self._appcommand_active:
|
|
157
|
+
try:
|
|
158
|
+
await self.async_update_attrs_appcommand(
|
|
159
|
+
self.appcommand_attrs,
|
|
160
|
+
global_update=global_update,
|
|
161
|
+
cache_id=cache_id,
|
|
162
|
+
)
|
|
163
|
+
except (AvrProcessingError, AvrIncompleteResponseError):
|
|
164
|
+
self._appcommand_active = False
|
|
165
|
+
_LOGGER.debug(
|
|
166
|
+
"Appcommand.xml does not support Sound mode. "
|
|
167
|
+
"Testing status.xml interface next"
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
if not self._is_setup:
|
|
171
|
+
self._support_sound_mode = True
|
|
172
|
+
_LOGGER.info("Sound mode supported")
|
|
163
173
|
return
|
|
164
|
-
|
|
174
|
+
|
|
175
|
+
urls = [self._device.urls.status, self._device.urls.mainzone]
|
|
176
|
+
# There are two different options of sound mode tags
|
|
177
|
+
try:
|
|
178
|
+
await self.async_update_attrs_status_xml(
|
|
179
|
+
self.status_xml_attrs_01, urls, cache_id=cache_id
|
|
180
|
+
)
|
|
181
|
+
except AvrProcessingError:
|
|
165
182
|
try:
|
|
166
183
|
await self.async_update_attrs_status_xml(
|
|
167
|
-
self.
|
|
184
|
+
self.status_xml_attrs_02, urls, cache_id=cache_id
|
|
168
185
|
)
|
|
169
186
|
except AvrProcessingError:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
_LOGGER.info("Sound mode not supported")
|
|
176
|
-
self._support_sound_mode = False
|
|
177
|
-
return
|
|
187
|
+
self._support_sound_mode = False
|
|
188
|
+
_LOGGER.info("Sound mode not supported")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
if not self._is_setup:
|
|
178
192
|
self._support_sound_mode = True
|
|
193
|
+
_LOGGER.info("Sound mode supported")
|
|
179
194
|
|
|
180
195
|
def match_sound_mode(self) -> Optional[str]:
|
|
181
196
|
"""Match the raw_sound_mode to its corresponding sound_mode."""
|
|
@@ -248,7 +263,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
|
|
|
248
263
|
##############
|
|
249
264
|
@property
|
|
250
265
|
def support_sound_mode(self) -> Optional[bool]:
|
|
251
|
-
"""Return True if sound mode supported."""
|
|
266
|
+
"""Return True if sound mode is supported."""
|
|
252
267
|
return self._support_sound_mode
|
|
253
268
|
|
|
254
269
|
@property
|