denonavr 0.11.3__py3-none-any.whl → 0.11.6__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 +144 -49
- denonavr/audyssey.py +59 -21
- denonavr/const.py +93 -10
- denonavr/decorators.py +27 -41
- denonavr/denonavr.py +50 -7
- denonavr/foundation.py +51 -41
- denonavr/input.py +49 -45
- denonavr/soundmode.py +14 -8
- denonavr/ssdp.py +13 -13
- denonavr/tonecontrol.py +83 -27
- denonavr/volume.py +29 -23
- {denonavr-0.11.3.dist-info → denonavr-0.11.6.dist-info}/METADATA +12 -10
- denonavr-0.11.6.dist-info/RECORD +19 -0
- {denonavr-0.11.3.dist-info → denonavr-0.11.6.dist-info}/WHEEL +1 -1
- denonavr-0.11.3.dist-info/RECORD +0 -19
- {denonavr-0.11.3.dist-info → denonavr-0.11.6.dist-info}/LICENSE +0 -0
- {denonavr-0.11.3.dist-info → denonavr-0.11.6.dist-info}/top_level.txt +0 -0
denonavr/__init__.py
CHANGED
denonavr/api.py
CHANGED
|
@@ -14,26 +14,30 @@ import sys
|
|
|
14
14
|
import time
|
|
15
15
|
import xml.etree.ElementTree as ET
|
|
16
16
|
from collections import defaultdict
|
|
17
|
+
from collections.abc import Hashable
|
|
17
18
|
from io import BytesIO
|
|
18
19
|
from typing import (
|
|
19
20
|
Awaitable,
|
|
20
21
|
Callable,
|
|
22
|
+
Coroutine,
|
|
21
23
|
DefaultDict,
|
|
22
24
|
Dict,
|
|
23
|
-
Hashable,
|
|
24
25
|
List,
|
|
25
26
|
Optional,
|
|
27
|
+
Set,
|
|
26
28
|
Tuple,
|
|
27
29
|
cast,
|
|
28
30
|
)
|
|
29
31
|
|
|
30
32
|
import attr
|
|
31
33
|
import httpx
|
|
32
|
-
from asyncstdlib import lru_cache
|
|
33
34
|
from defusedxml.ElementTree import fromstring
|
|
34
35
|
|
|
35
36
|
from .appcommand import AppCommandCmd
|
|
36
37
|
from .const import (
|
|
38
|
+
ALL_TELNET_EVENTS,
|
|
39
|
+
ALL_ZONE_TELNET_EVENTS,
|
|
40
|
+
ALL_ZONES,
|
|
37
41
|
APPCOMMAND0300_URL,
|
|
38
42
|
APPCOMMAND_CMD_TEXT,
|
|
39
43
|
APPCOMMAND_NAME,
|
|
@@ -45,15 +49,12 @@ from .const import (
|
|
|
45
49
|
ZONE2,
|
|
46
50
|
ZONE3,
|
|
47
51
|
)
|
|
48
|
-
from .decorators import
|
|
49
|
-
async_handle_receiver_exceptions,
|
|
50
|
-
cache_clear_on_exception,
|
|
51
|
-
set_cache_id,
|
|
52
|
-
)
|
|
52
|
+
from .decorators import async_handle_receiver_exceptions, cache_result
|
|
53
53
|
from .exceptions import (
|
|
54
54
|
AvrIncompleteResponseError,
|
|
55
55
|
AvrInvalidResponseError,
|
|
56
56
|
AvrNetworkError,
|
|
57
|
+
AvrProcessingError,
|
|
57
58
|
AvrTimoutError,
|
|
58
59
|
)
|
|
59
60
|
|
|
@@ -128,9 +129,7 @@ class DenonAVRApi:
|
|
|
128
129
|
# Use default port of the receiver if no different port is specified
|
|
129
130
|
port = port if port is not None else self.port
|
|
130
131
|
|
|
131
|
-
endpoint = "http://{host}:{port}{request}"
|
|
132
|
-
host=self.host, port=port, request=request
|
|
133
|
-
)
|
|
132
|
+
endpoint = f"http://{self.host}:{port}{request}"
|
|
134
133
|
|
|
135
134
|
client = self.async_client_getter()
|
|
136
135
|
try:
|
|
@@ -155,9 +154,7 @@ class DenonAVRApi:
|
|
|
155
154
|
# Use default port of the receiver if no different port is specified
|
|
156
155
|
port = port if port is not None else self.port
|
|
157
156
|
|
|
158
|
-
endpoint = "http://{host}:{port}{request}"
|
|
159
|
-
host=self.host, port=port, request=request
|
|
160
|
-
)
|
|
157
|
+
endpoint = f"http://{self.host}:{port}{request}"
|
|
161
158
|
|
|
162
159
|
client = self.async_client_getter()
|
|
163
160
|
try:
|
|
@@ -180,9 +177,7 @@ class DenonAVRApi:
|
|
|
180
177
|
# Return text
|
|
181
178
|
return res.text
|
|
182
179
|
|
|
183
|
-
@
|
|
184
|
-
@cache_clear_on_exception
|
|
185
|
-
@lru_cache(maxsize=32)
|
|
180
|
+
@cache_result
|
|
186
181
|
@async_handle_receiver_exceptions
|
|
187
182
|
async def async_get_xml(
|
|
188
183
|
self, request: str, cache_id: Hashable = None
|
|
@@ -197,9 +192,7 @@ class DenonAVRApi:
|
|
|
197
192
|
# Return ElementTree element
|
|
198
193
|
return xml_root
|
|
199
194
|
|
|
200
|
-
@
|
|
201
|
-
@cache_clear_on_exception
|
|
202
|
-
@lru_cache(maxsize=32)
|
|
195
|
+
@cache_result
|
|
203
196
|
@async_handle_receiver_exceptions
|
|
204
197
|
async def async_post_appcommand(
|
|
205
198
|
self, request: str, cmds: Tuple[AppCommandCmd], cache_id: Hashable = None
|
|
@@ -222,7 +215,7 @@ class DenonAVRApi:
|
|
|
222
215
|
def add_appcommand_update_tag(self, tag: AppCommandCmd) -> None:
|
|
223
216
|
"""Add appcommand tag for full update."""
|
|
224
217
|
if tag.cmd_id != "1":
|
|
225
|
-
raise ValueError("cmd_id is {} but must be 1"
|
|
218
|
+
raise ValueError(f"cmd_id is {tag.cmd_id} but must be 1")
|
|
226
219
|
|
|
227
220
|
# Remove response pattern from tag because it is not relevant for query
|
|
228
221
|
tag = attr.evolve(tag, response_pattern=tuple())
|
|
@@ -234,7 +227,7 @@ class DenonAVRApi:
|
|
|
234
227
|
def add_appcommand0300_update_tag(self, tag: AppCommandCmd) -> None:
|
|
235
228
|
"""Add appcommand0300 tag for full update."""
|
|
236
229
|
if tag.cmd_id != "3":
|
|
237
|
-
raise ValueError("cmd_id is {} but must be 3"
|
|
230
|
+
raise ValueError(f"cmd_id is {tag.cmd_id} but must be 3")
|
|
238
231
|
|
|
239
232
|
# Remove response pattern from tag because it is not relevant for query
|
|
240
233
|
tag = attr.evolve(tag, response_pattern=tuple())
|
|
@@ -268,16 +261,20 @@ class DenonAVRApi:
|
|
|
268
261
|
"""
|
|
269
262
|
if len(cmd_list) != len(xml_root):
|
|
270
263
|
raise AvrIncompleteResponseError(
|
|
271
|
-
|
|
272
|
-
|
|
264
|
+
(
|
|
265
|
+
"Invalid length of response XML. Query has"
|
|
266
|
+
f" {len(cmd_list)} elements, response {len(xml_root)}"
|
|
267
|
+
),
|
|
273
268
|
request,
|
|
274
269
|
)
|
|
275
270
|
|
|
276
271
|
for i, child in enumerate(xml_root):
|
|
277
272
|
if child.tag not in ["cmd", "error"]:
|
|
278
273
|
raise AvrInvalidResponseError(
|
|
279
|
-
|
|
280
|
-
|
|
274
|
+
(
|
|
275
|
+
'Returned document contains a tag other than "cmd" and'
|
|
276
|
+
f' "error": {child.tag}'
|
|
277
|
+
),
|
|
281
278
|
request,
|
|
282
279
|
)
|
|
283
280
|
# Find corresponding attributes from request XML if set and add
|
|
@@ -354,7 +351,7 @@ class DenonAVRApi:
|
|
|
354
351
|
return body_bytes
|
|
355
352
|
|
|
356
353
|
def is_default_async_client(self) -> bool:
|
|
357
|
-
"""Check if default httpx.
|
|
354
|
+
"""Check if default httpx.AsyncClient getter is used."""
|
|
358
355
|
return self.async_client_getter is get_default_async_client
|
|
359
356
|
|
|
360
357
|
|
|
@@ -427,11 +424,28 @@ class DenonAVRTelnetApi:
|
|
|
427
424
|
_telnet_event_map: Dict[str, List] = attr.ib(
|
|
428
425
|
default=attr.Factory(telnet_event_map_factory)
|
|
429
426
|
)
|
|
430
|
-
|
|
427
|
+
_callback_tasks: Set[asyncio.Task] = attr.ib(attr.Factory(set))
|
|
428
|
+
_send_lock: asyncio.Lock = attr.ib(default=attr.Factory(asyncio.Lock))
|
|
429
|
+
_send_confirmation_timeout: float = attr.ib(converter=float, default=2.0)
|
|
430
|
+
_send_confirmation_event: asyncio.Event = attr.ib(
|
|
431
|
+
default=attr.Factory(asyncio.Event)
|
|
432
|
+
)
|
|
433
|
+
_send_confirmation_command: str = attr.ib(converter=str, default="")
|
|
434
|
+
_send_tasks: Set[asyncio.Task] = attr.ib(attr.Factory(set))
|
|
435
|
+
_callbacks: Dict[str, List[Coroutine]] = attr.ib(
|
|
431
436
|
validator=attr.validators.instance_of(dict),
|
|
432
437
|
default=attr.Factory(dict),
|
|
433
438
|
init=False,
|
|
434
439
|
)
|
|
440
|
+
_raw_callbacks: List[Coroutine] = attr.ib(
|
|
441
|
+
validator=attr.validators.instance_of(list),
|
|
442
|
+
default=attr.Factory(list),
|
|
443
|
+
init=False,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
def __attrs_post_init__(self) -> None:
|
|
447
|
+
"""Initialize special attributes."""
|
|
448
|
+
self._register_raw_callback(self._async_send_confirmation_callback)
|
|
435
449
|
|
|
436
450
|
async def async_connect(self) -> None:
|
|
437
451
|
"""Connect to the receiver asynchronously."""
|
|
@@ -457,27 +471,43 @@ class DenonAVRTelnetApi:
|
|
|
457
471
|
)
|
|
458
472
|
except asyncio.TimeoutError as err:
|
|
459
473
|
_LOGGER.debug("%s: Timeout exception on telnet connect", self.host)
|
|
460
|
-
raise AvrTimoutError(
|
|
461
|
-
"TimeoutException: {}".format(err), "telnet connect"
|
|
462
|
-
) from err
|
|
474
|
+
raise AvrTimoutError(f"TimeoutException: {err}", "telnet connect") from err
|
|
463
475
|
except ConnectionRefusedError as err:
|
|
464
476
|
_LOGGER.debug(
|
|
465
477
|
"%s: Connection refused on telnet connect", self.host, exc_info=True
|
|
466
478
|
)
|
|
467
479
|
raise AvrNetworkError(
|
|
468
|
-
"ConnectionRefusedError: {}"
|
|
480
|
+
f"ConnectionRefusedError: {err}", "telnet connect"
|
|
469
481
|
) from err
|
|
470
482
|
except (OSError, IOError) as err:
|
|
471
483
|
_LOGGER.debug(
|
|
472
484
|
"%s: Connection failed on telnet reconnect", self.host, exc_info=True
|
|
473
485
|
)
|
|
474
|
-
raise AvrNetworkError("OSError: {}"
|
|
486
|
+
raise AvrNetworkError(f"OSError: {err}", "telnet connect") from err
|
|
475
487
|
_LOGGER.debug("%s: telnet connection complete", self.host)
|
|
476
488
|
self._protocol = cast(DenonAVRTelnetProtocol, transport_protocol[1])
|
|
477
489
|
self._connection_enabled = True
|
|
478
490
|
self._last_message_time = time.monotonic()
|
|
479
491
|
self._schedule_monitor()
|
|
480
|
-
|
|
492
|
+
# Trigger update of all attributes
|
|
493
|
+
await self.async_send_commands(
|
|
494
|
+
"ZM?",
|
|
495
|
+
"SI?",
|
|
496
|
+
"MV?",
|
|
497
|
+
"MU?",
|
|
498
|
+
"Z2?",
|
|
499
|
+
"Z2MU?",
|
|
500
|
+
"Z3?",
|
|
501
|
+
"Z3MU?",
|
|
502
|
+
"PSTONE CTRL ?",
|
|
503
|
+
"PSBAS ?",
|
|
504
|
+
"PSTRE ?",
|
|
505
|
+
"PSDYNEQ ?",
|
|
506
|
+
"PSMULTEQ: ?",
|
|
507
|
+
"PSREFLEV ?",
|
|
508
|
+
"PSDYNVOL ?",
|
|
509
|
+
"MS?",
|
|
510
|
+
)
|
|
481
511
|
|
|
482
512
|
def _schedule_monitor(self) -> None:
|
|
483
513
|
"""Start the monitor task."""
|
|
@@ -568,8 +598,8 @@ class DenonAVRTelnetApi:
|
|
|
568
598
|
) -> None:
|
|
569
599
|
"""Register a callback handler for an event type."""
|
|
570
600
|
# Validate the passed in type
|
|
571
|
-
if event !=
|
|
572
|
-
raise ValueError("{} is not a valid callback type."
|
|
601
|
+
if event != ALL_TELNET_EVENTS and event not in TELNET_EVENTS:
|
|
602
|
+
raise ValueError(f"{event} is not a valid callback type.")
|
|
573
603
|
|
|
574
604
|
if event not in self._callbacks.keys():
|
|
575
605
|
self._callbacks[event] = []
|
|
@@ -583,13 +613,24 @@ class DenonAVRTelnetApi:
|
|
|
583
613
|
return
|
|
584
614
|
self._callbacks[event].remove(callback)
|
|
585
615
|
|
|
616
|
+
def _register_raw_callback(
|
|
617
|
+
self, callback: Callable[[str], Awaitable[None]]
|
|
618
|
+
) -> None:
|
|
619
|
+
"""Register a callback handler for raw telnet messages."""
|
|
620
|
+
self._raw_callbacks.append(callback)
|
|
621
|
+
|
|
622
|
+
def _unregister_raw_callback(
|
|
623
|
+
self, callback: Callable[[str], Awaitable[None]]
|
|
624
|
+
) -> None:
|
|
625
|
+
"""Unregister a callback handler for raw telnet messages."""
|
|
626
|
+
self._raw_callbacks.remove(callback)
|
|
627
|
+
|
|
586
628
|
def _process_event(self, message: str) -> None:
|
|
587
629
|
"""Process a realtime event."""
|
|
588
630
|
_LOGGER.debug("Incoming Telnet message: %s", message)
|
|
589
631
|
self._last_message_time = time.monotonic()
|
|
590
632
|
if len(message) < 3:
|
|
591
633
|
return
|
|
592
|
-
zone = MAIN_ZONE
|
|
593
634
|
|
|
594
635
|
# Event is 2 characters
|
|
595
636
|
event = self._get_event(message)
|
|
@@ -603,7 +644,11 @@ class DenonAVRTelnetApi:
|
|
|
603
644
|
if parameter[0:3] == "MAX":
|
|
604
645
|
return
|
|
605
646
|
|
|
606
|
-
|
|
647
|
+
# Determine zone
|
|
648
|
+
zone = MAIN_ZONE
|
|
649
|
+
if event in ALL_ZONE_TELNET_EVENTS:
|
|
650
|
+
zone = ALL_ZONES
|
|
651
|
+
elif event in {"Z2", "Z3"}:
|
|
607
652
|
if event == "Z2":
|
|
608
653
|
zone = ZONE2
|
|
609
654
|
else:
|
|
@@ -620,10 +665,28 @@ class DenonAVRTelnetApi:
|
|
|
620
665
|
if event not in TELNET_EVENTS:
|
|
621
666
|
return
|
|
622
667
|
|
|
623
|
-
asyncio.create_task(
|
|
668
|
+
task = asyncio.create_task(
|
|
669
|
+
self._async_run_callbacks(message, event, zone, parameter)
|
|
670
|
+
)
|
|
671
|
+
self._callback_tasks.add(task)
|
|
672
|
+
task.add_done_callback(self._callback_tasks.discard)
|
|
673
|
+
|
|
674
|
+
async def _async_run_callbacks(
|
|
675
|
+
self, message: str, event: str, zone: str, parameter: str
|
|
676
|
+
) -> None:
|
|
677
|
+
"""Handle triggering the registered callbacks."""
|
|
678
|
+
for callback in self._raw_callbacks:
|
|
679
|
+
try:
|
|
680
|
+
await callback(message)
|
|
681
|
+
except Exception as err: # pylint: disable=broad-except
|
|
682
|
+
# We don't want a single bad callback to trip up the
|
|
683
|
+
# whole system and prevent further execution
|
|
684
|
+
_LOGGER.error(
|
|
685
|
+
"%s: Raw callback caused an unhandled exception %s",
|
|
686
|
+
self.host,
|
|
687
|
+
err,
|
|
688
|
+
)
|
|
624
689
|
|
|
625
|
-
async def _async_run_callbacks(self, event: str, zone: str, parameter: str) -> None:
|
|
626
|
-
"""Handle triggering the registered callbacks for the event."""
|
|
627
690
|
if event in self._callbacks.keys():
|
|
628
691
|
for callback in self._callbacks[event]:
|
|
629
692
|
try:
|
|
@@ -637,8 +700,8 @@ class DenonAVRTelnetApi:
|
|
|
637
700
|
err,
|
|
638
701
|
)
|
|
639
702
|
|
|
640
|
-
if
|
|
641
|
-
for callback in self._callbacks[
|
|
703
|
+
if ALL_TELNET_EVENTS in self._callbacks.keys():
|
|
704
|
+
for callback in self._callbacks[ALL_TELNET_EVENTS]:
|
|
642
705
|
try:
|
|
643
706
|
await callback(zone, event, parameter)
|
|
644
707
|
except Exception as err: # pylint: disable=broad-except
|
|
@@ -658,15 +721,47 @@ class DenonAVRTelnetApi:
|
|
|
658
721
|
return event
|
|
659
722
|
return ""
|
|
660
723
|
|
|
661
|
-
def
|
|
724
|
+
async def _async_send_confirmation_callback(self, message: str) -> None:
|
|
725
|
+
"""Confirm that the telnet command has been executed."""
|
|
726
|
+
if len(message) < 3:
|
|
727
|
+
return
|
|
728
|
+
command = self._send_confirmation_command
|
|
729
|
+
if self._get_event(message) == self._get_event(self._send_confirmation_command):
|
|
730
|
+
self._send_confirmation_command = ""
|
|
731
|
+
self._send_confirmation_event.set()
|
|
732
|
+
_LOGGER.debug("Command %s confirmed", command)
|
|
733
|
+
|
|
734
|
+
async def _async_send_command(self, command: str) -> None:
|
|
735
|
+
"""Send one telnet command to the receiver."""
|
|
736
|
+
async with self._send_lock:
|
|
737
|
+
self._send_confirmation_command = command
|
|
738
|
+
self._send_confirmation_event.clear()
|
|
739
|
+
if not self.connected or not self.healthy:
|
|
740
|
+
raise AvrProcessingError(
|
|
741
|
+
f"Error sending command {command}. Telnet connected: "
|
|
742
|
+
f"{self.connected}, Connection healthy: {self.healthy}"
|
|
743
|
+
)
|
|
744
|
+
self._protocol.write(f"{command}\r")
|
|
745
|
+
try:
|
|
746
|
+
await asyncio.wait_for(
|
|
747
|
+
self._send_confirmation_event.wait(),
|
|
748
|
+
self._send_confirmation_timeout,
|
|
749
|
+
)
|
|
750
|
+
except asyncio.TimeoutError:
|
|
751
|
+
_LOGGER.info("Timeout waiting for confirmation of command: %s", command)
|
|
752
|
+
finally:
|
|
753
|
+
self._send_confirmation_command = ""
|
|
754
|
+
|
|
755
|
+
async def async_send_commands(self, *commands: str) -> None:
|
|
662
756
|
"""Send telnet commands to the receiver."""
|
|
663
|
-
if not self.connected:
|
|
664
|
-
return False
|
|
665
|
-
if not self.healthy:
|
|
666
|
-
return False
|
|
667
757
|
for command in commands:
|
|
668
|
-
self.
|
|
669
|
-
|
|
758
|
+
await self._async_send_command(command)
|
|
759
|
+
|
|
760
|
+
def send_commands(self, *commands: str) -> None:
|
|
761
|
+
"""Send telnet commands to the receiver."""
|
|
762
|
+
task = asyncio.create_task(self.async_send_commands(*commands))
|
|
763
|
+
self._send_tasks.add(task)
|
|
764
|
+
task.add_done_callback(self._send_tasks.discard)
|
|
670
765
|
|
|
671
766
|
##############
|
|
672
767
|
# Properties #
|
denonavr/audyssey.py
CHANGED
|
@@ -8,7 +8,8 @@ This module implements the Audyssey settings of Denon AVR receivers.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
|
-
from
|
|
11
|
+
from collections.abc import Hashable
|
|
12
|
+
from typing import List, Optional
|
|
12
13
|
|
|
13
14
|
import attr
|
|
14
15
|
|
|
@@ -16,11 +17,14 @@ from .appcommand import AppCommandCmd, AppCommandCmdParam, AppCommands
|
|
|
16
17
|
from .const import (
|
|
17
18
|
DENON_ATTR_SETATTR,
|
|
18
19
|
DYNAMIC_VOLUME_MAP,
|
|
19
|
-
|
|
20
|
+
DYNAMIC_VOLUME_MAP_LABELS_APPCOMMAND,
|
|
21
|
+
DYNAMIC_VOLUME_MAP_LABELS_TELNET,
|
|
20
22
|
MULTI_EQ_MAP,
|
|
21
|
-
|
|
23
|
+
MULTI_EQ_MAP_LABELS_APPCOMMAND,
|
|
24
|
+
MULTI_EQ_MAP_LABELS_TELNET,
|
|
22
25
|
REF_LVL_OFFSET_MAP,
|
|
23
|
-
|
|
26
|
+
REF_LVL_OFFSET_MAP_LABELS_APPCOMMAND,
|
|
27
|
+
REF_LVL_OFFSET_MAP_LABELS_TELNET,
|
|
24
28
|
)
|
|
25
29
|
from .exceptions import AvrCommandError, AvrProcessingError
|
|
26
30
|
from .foundation import DenonAVRFoundation, convert_string_int_bool
|
|
@@ -129,13 +133,9 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
129
133
|
|
|
130
134
|
try:
|
|
131
135
|
if res.find("cmd").text != "OK":
|
|
132
|
-
raise AvrProcessingError(
|
|
133
|
-
"SetAudyssey command {} failed".format(cmd.name)
|
|
134
|
-
)
|
|
136
|
+
raise AvrProcessingError(f"SetAudyssey command {cmd.name} failed")
|
|
135
137
|
except AttributeError as err:
|
|
136
|
-
raise AvrProcessingError(
|
|
137
|
-
"SetAudyssey command {} failed".format(cmd.name)
|
|
138
|
-
) from err
|
|
138
|
+
raise AvrProcessingError(f"SetAudyssey command {cmd.name} failed") from err
|
|
139
139
|
|
|
140
140
|
##############
|
|
141
141
|
# Properties #
|
|
@@ -153,7 +153,9 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
153
153
|
@property
|
|
154
154
|
def reference_level_offset_setting_list(self) -> List[str]:
|
|
155
155
|
"""Return a list of available reference level offset settings."""
|
|
156
|
-
|
|
156
|
+
if self._device.telnet_available:
|
|
157
|
+
return list(REF_LVL_OFFSET_MAP_LABELS_TELNET.keys())
|
|
158
|
+
return list(REF_LVL_OFFSET_MAP_LABELS_APPCOMMAND.keys())
|
|
157
159
|
|
|
158
160
|
@property
|
|
159
161
|
def dynamic_volume(self) -> Optional[str]:
|
|
@@ -163,7 +165,9 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
163
165
|
@property
|
|
164
166
|
def dynamic_volume_setting_list(self) -> List[str]:
|
|
165
167
|
"""Return a list of available Dynamic Volume settings."""
|
|
166
|
-
|
|
168
|
+
if self._device.telnet_available:
|
|
169
|
+
return list(DYNAMIC_VOLUME_MAP_LABELS_TELNET.keys())
|
|
170
|
+
return list(DYNAMIC_VOLUME_MAP_LABELS_APPCOMMAND.keys())
|
|
167
171
|
|
|
168
172
|
@property
|
|
169
173
|
def multi_eq(self) -> Optional[str]:
|
|
@@ -173,13 +177,19 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
173
177
|
@property
|
|
174
178
|
def multi_eq_setting_list(self) -> List[str]:
|
|
175
179
|
"""Return a list of available MultiEQ settings."""
|
|
176
|
-
|
|
180
|
+
if self._device.telnet_available:
|
|
181
|
+
return list(MULTI_EQ_MAP_LABELS_TELNET.keys())
|
|
182
|
+
return list(MULTI_EQ_MAP_LABELS_APPCOMMAND.keys())
|
|
177
183
|
|
|
178
184
|
##########
|
|
179
185
|
# Setter #
|
|
180
186
|
##########
|
|
181
187
|
async def async_dynamiceq_off(self) -> None:
|
|
182
188
|
"""Turn DynamicEQ off."""
|
|
189
|
+
if self._device.telnet_available:
|
|
190
|
+
telnet_command = self._device.telnet_commands.command_dynamiceq + "OFF"
|
|
191
|
+
await self._device.telnet_api.async_send_commands(telnet_command)
|
|
192
|
+
return
|
|
183
193
|
cmd = attr.evolve(
|
|
184
194
|
AppCommands.SetAudysseyDynamicEQ,
|
|
185
195
|
param_list=(AppCommandCmdParam(name="dynamiceq", text=0),),
|
|
@@ -188,6 +198,10 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
188
198
|
|
|
189
199
|
async def async_dynamiceq_on(self) -> None:
|
|
190
200
|
"""Turn DynamicEQ on."""
|
|
201
|
+
if self._device.telnet_available:
|
|
202
|
+
telnet_command = self._device.telnet_commands.command_dynamiceq + "ON"
|
|
203
|
+
await self._device.telnet_api.async_send_commands(telnet_command)
|
|
204
|
+
return
|
|
191
205
|
cmd = attr.evolve(
|
|
192
206
|
AppCommands.SetAudysseyDynamicEQ,
|
|
193
207
|
param_list=(AppCommandCmdParam(name="dynamiceq", text=1),),
|
|
@@ -196,9 +210,17 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
196
210
|
|
|
197
211
|
async def async_set_multieq(self, value: str) -> None:
|
|
198
212
|
"""Set MultiEQ mode."""
|
|
199
|
-
|
|
213
|
+
if self._device.telnet_available:
|
|
214
|
+
setting = MULTI_EQ_MAP_LABELS_TELNET.get(value)
|
|
215
|
+
if setting is None:
|
|
216
|
+
raise AvrCommandError(f"Value {value} not known for MultiEQ")
|
|
217
|
+
telnet_command = self._device.telnet_commands.command_multieq + setting
|
|
218
|
+
await self._device.telnet_api.async_send_commands(telnet_command)
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
setting = MULTI_EQ_MAP_LABELS_APPCOMMAND.get(value)
|
|
200
222
|
if setting is None:
|
|
201
|
-
raise AvrCommandError("Value {} not known for MultiEQ"
|
|
223
|
+
raise AvrCommandError(f"Value {value} not known for MultiEQ")
|
|
202
224
|
cmd = attr.evolve(
|
|
203
225
|
AppCommands.SetAudysseyMultiEQ,
|
|
204
226
|
param_list=(AppCommandCmdParam(name="multeq", text=setting),),
|
|
@@ -212,11 +234,19 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
212
234
|
raise AvrCommandError(
|
|
213
235
|
"Reference level could only be set when DynamicEQ is active"
|
|
214
236
|
)
|
|
215
|
-
|
|
237
|
+
if self._device.telnet_available:
|
|
238
|
+
setting = REF_LVL_OFFSET_MAP_LABELS_TELNET.get(value)
|
|
239
|
+
if setting is None:
|
|
240
|
+
raise AvrCommandError(
|
|
241
|
+
f"Value {value} not known for Reference level offset"
|
|
242
|
+
)
|
|
243
|
+
telnet_command = self._device.telnet_commands.command_reflevoffset + setting
|
|
244
|
+
await self._device.telnet_api.async_send_commands(telnet_command)
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
setting = REF_LVL_OFFSET_MAP_LABELS_APPCOMMAND.get(value)
|
|
216
248
|
if setting is None:
|
|
217
|
-
raise AvrCommandError(
|
|
218
|
-
"Value {} not known for Reference level offset".format(value)
|
|
219
|
-
)
|
|
249
|
+
raise AvrCommandError(f"Value {value} not known for Reference level offset")
|
|
220
250
|
cmd = attr.evolve(
|
|
221
251
|
AppCommands.SetAudysseyReflevoffset,
|
|
222
252
|
param_list=(AppCommandCmdParam(name="reflevoffset", text=setting),),
|
|
@@ -225,9 +255,17 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
225
255
|
|
|
226
256
|
async def async_set_dynamicvol(self, value: str) -> None:
|
|
227
257
|
"""Set Dynamic Volume."""
|
|
228
|
-
|
|
258
|
+
if self._device.telnet_available:
|
|
259
|
+
setting = DYNAMIC_VOLUME_MAP_LABELS_TELNET.get(value)
|
|
260
|
+
if setting is None:
|
|
261
|
+
raise AvrCommandError(f"Value {value} not known for Dynamic Volume")
|
|
262
|
+
telnet_command = self._device.telnet_commands.command_dynamicvol + setting
|
|
263
|
+
await self._device.telnet_api.async_send_commands(telnet_command)
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
setting = DYNAMIC_VOLUME_MAP_LABELS_APPCOMMAND.get(value)
|
|
229
267
|
if setting is None:
|
|
230
|
-
raise AvrCommandError("Value {} not known for Dynamic Volume"
|
|
268
|
+
raise AvrCommandError(f"Value {value} not known for Dynamic Volume")
|
|
231
269
|
cmd = attr.evolve(
|
|
232
270
|
AppCommands.SetAudysseyDynamicvol,
|
|
233
271
|
param_list=(AppCommandCmdParam(name="dynamicvol", text=setting),),
|