denonavr 1.0.0__py3-none-any.whl → 1.1.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 +181 -60
- denonavr/audyssey.py +105 -0
- denonavr/const.py +880 -2
- denonavr/decorators.py +5 -32
- denonavr/denonavr.py +500 -3
- denonavr/dirac.py +89 -0
- denonavr/foundation.py +1196 -26
- denonavr/input.py +44 -3
- denonavr/soundmode.py +912 -3
- denonavr/volume.py +415 -4
- {denonavr-1.0.0.dist-info → denonavr-1.1.0.dist-info}/METADATA +21 -37
- denonavr-1.1.0.dist-info/RECORD +20 -0
- {denonavr-1.0.0.dist-info → denonavr-1.1.0.dist-info}/WHEEL +1 -1
- denonavr-1.0.0.dist-info/RECORD +0 -19
- {denonavr-1.0.0.dist-info → denonavr-1.1.0.dist-info/licenses}/LICENSE +0 -0
- {denonavr-1.0.0.dist-info → denonavr-1.1.0.dist-info}/top_level.txt +0 -0
denonavr/__init__.py
CHANGED
denonavr/api.py
CHANGED
|
@@ -31,7 +31,8 @@ from typing import (
|
|
|
31
31
|
|
|
32
32
|
import attr
|
|
33
33
|
import httpx
|
|
34
|
-
from defusedxml
|
|
34
|
+
from defusedxml import DefusedXmlException
|
|
35
|
+
from defusedxml.ElementTree import ParseError, fromstring
|
|
35
36
|
|
|
36
37
|
from .appcommand import AppCommandCmd
|
|
37
38
|
from .const import (
|
|
@@ -83,16 +84,86 @@ def telnet_event_map_factory() -> Dict[str, List]:
|
|
|
83
84
|
return dict(event_map)
|
|
84
85
|
|
|
85
86
|
|
|
86
|
-
@attr.s(auto_attribs=True, hash=False
|
|
87
|
+
@attr.s(auto_attribs=True, hash=False)
|
|
88
|
+
class HTTPXAsyncClient:
|
|
89
|
+
"""Perform cached HTTP calls with httpx.AsyncClient."""
|
|
90
|
+
|
|
91
|
+
client_getter: Callable[[], httpx.AsyncClient] = attr.ib(
|
|
92
|
+
validator=attr.validators.is_callable(),
|
|
93
|
+
default=get_default_async_client,
|
|
94
|
+
init=False,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def __hash__(self) -> int:
|
|
98
|
+
"""Hash the class using its ID that caching works."""
|
|
99
|
+
return id(self)
|
|
100
|
+
|
|
101
|
+
@cache_result
|
|
102
|
+
@async_handle_receiver_exceptions
|
|
103
|
+
async def async_get(
|
|
104
|
+
self,
|
|
105
|
+
url: str,
|
|
106
|
+
timeout: float,
|
|
107
|
+
read_timeout: float,
|
|
108
|
+
*,
|
|
109
|
+
cache_id: Hashable = None,
|
|
110
|
+
) -> httpx.Response:
|
|
111
|
+
"""Call GET endpoint of Denon AVR receiver asynchronously."""
|
|
112
|
+
client = self.client_getter()
|
|
113
|
+
try:
|
|
114
|
+
res = await client.get(
|
|
115
|
+
url, timeout=httpx.Timeout(timeout, read=read_timeout)
|
|
116
|
+
)
|
|
117
|
+
res.raise_for_status()
|
|
118
|
+
finally:
|
|
119
|
+
# Close the default AsyncClient but keep custom clients open
|
|
120
|
+
if self.is_default_async_client():
|
|
121
|
+
await client.aclose()
|
|
122
|
+
|
|
123
|
+
return res
|
|
124
|
+
|
|
125
|
+
@cache_result
|
|
126
|
+
@async_handle_receiver_exceptions
|
|
127
|
+
async def async_post(
|
|
128
|
+
self,
|
|
129
|
+
url: str,
|
|
130
|
+
timeout: float,
|
|
131
|
+
read_timeout: float,
|
|
132
|
+
*,
|
|
133
|
+
content: Optional[bytes] = None,
|
|
134
|
+
data: Optional[Dict] = None,
|
|
135
|
+
cache_id: Hashable = None,
|
|
136
|
+
) -> httpx.Response:
|
|
137
|
+
"""Call GET endpoint of Denon AVR receiver asynchronously."""
|
|
138
|
+
client = self.client_getter()
|
|
139
|
+
try:
|
|
140
|
+
res = await client.post(
|
|
141
|
+
url,
|
|
142
|
+
content=content,
|
|
143
|
+
data=data,
|
|
144
|
+
timeout=httpx.Timeout(timeout, read=read_timeout),
|
|
145
|
+
)
|
|
146
|
+
res.raise_for_status()
|
|
147
|
+
finally:
|
|
148
|
+
# Close the default AsyncClient but keep custom clients open
|
|
149
|
+
if self.is_default_async_client():
|
|
150
|
+
await client.aclose()
|
|
151
|
+
|
|
152
|
+
return res
|
|
153
|
+
|
|
154
|
+
def is_default_async_client(self) -> bool:
|
|
155
|
+
"""Check if default httpx.AsyncClient getter is used."""
|
|
156
|
+
return self.client_getter is get_default_async_client
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@attr.s(auto_attribs=True, on_setattr=DENON_ATTR_SETATTR)
|
|
87
160
|
class DenonAVRApi:
|
|
88
161
|
"""Perform API calls to Denon AVR REST interface."""
|
|
89
162
|
|
|
90
163
|
host: str = attr.ib(converter=str, default="localhost")
|
|
91
164
|
port: int = attr.ib(converter=int, default=80)
|
|
92
|
-
timeout:
|
|
93
|
-
|
|
94
|
-
default=httpx.Timeout(2.0, read=15.0),
|
|
95
|
-
)
|
|
165
|
+
timeout: float = attr.ib(converter=float, default=2.0)
|
|
166
|
+
read_timeout: float = attr.ib(converter=float, default=15.0)
|
|
96
167
|
_appcommand_update_tags: Tuple[AppCommandCmd] = attr.ib(
|
|
97
168
|
validator=attr.validators.deep_iterable(
|
|
98
169
|
attr.validators.instance_of(AppCommandCmd),
|
|
@@ -107,23 +178,18 @@ class DenonAVRApi:
|
|
|
107
178
|
),
|
|
108
179
|
default=attr.Factory(tuple),
|
|
109
180
|
)
|
|
110
|
-
|
|
111
|
-
validator=attr.validators.
|
|
112
|
-
default=
|
|
181
|
+
httpx_async_client: HTTPXAsyncClient = attr.ib(
|
|
182
|
+
validator=attr.validators.instance_of(HTTPXAsyncClient),
|
|
183
|
+
default=attr.Factory(HTTPXAsyncClient),
|
|
113
184
|
init=False,
|
|
114
185
|
)
|
|
115
186
|
|
|
116
|
-
def __hash__(self) -> int:
|
|
117
|
-
"""
|
|
118
|
-
Hash the class in a custom way that caching works.
|
|
119
|
-
|
|
120
|
-
It should react on changes of host and port.
|
|
121
|
-
"""
|
|
122
|
-
return hash((self.host, self.port))
|
|
123
|
-
|
|
124
|
-
@async_handle_receiver_exceptions
|
|
125
187
|
async def async_get(
|
|
126
|
-
self,
|
|
188
|
+
self,
|
|
189
|
+
request: str,
|
|
190
|
+
*,
|
|
191
|
+
port: Optional[int] = None,
|
|
192
|
+
cache_id: Hashable = None,
|
|
127
193
|
) -> httpx.Response:
|
|
128
194
|
"""Call GET endpoint of Denon AVR receiver asynchronously."""
|
|
129
195
|
# Use default port of the receiver if no different port is specified
|
|
@@ -131,24 +197,18 @@ class DenonAVRApi:
|
|
|
131
197
|
|
|
132
198
|
endpoint = f"http://{self.host}:{port}{request}"
|
|
133
199
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
res.raise_for_status()
|
|
138
|
-
finally:
|
|
139
|
-
# Close the default AsyncClient but keep custom clients open
|
|
140
|
-
if self.is_default_async_client():
|
|
141
|
-
await client.aclose()
|
|
142
|
-
|
|
143
|
-
return res
|
|
200
|
+
return await self.httpx_async_client.async_get(
|
|
201
|
+
endpoint, self.timeout, self.read_timeout, cache_id=cache_id
|
|
202
|
+
)
|
|
144
203
|
|
|
145
|
-
@async_handle_receiver_exceptions
|
|
146
204
|
async def async_post(
|
|
147
205
|
self,
|
|
148
206
|
request: str,
|
|
207
|
+
*,
|
|
149
208
|
content: Optional[bytes] = None,
|
|
150
209
|
data: Optional[Dict] = None,
|
|
151
210
|
port: Optional[int] = None,
|
|
211
|
+
cache_id: Hashable = None,
|
|
152
212
|
) -> httpx.Response:
|
|
153
213
|
"""Call POST endpoint of Denon AVR receiver asynchronously."""
|
|
154
214
|
# Use default port of the receiver if no different port is specified
|
|
@@ -156,20 +216,15 @@ class DenonAVRApi:
|
|
|
156
216
|
|
|
157
217
|
endpoint = f"http://{self.host}:{port}{request}"
|
|
158
218
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if self.is_default_async_client():
|
|
168
|
-
await client.aclose()
|
|
169
|
-
|
|
170
|
-
return res
|
|
219
|
+
return await self.httpx_async_client.async_post(
|
|
220
|
+
endpoint,
|
|
221
|
+
self.timeout,
|
|
222
|
+
self.read_timeout,
|
|
223
|
+
content=content,
|
|
224
|
+
data=data,
|
|
225
|
+
cache_id=cache_id,
|
|
226
|
+
)
|
|
171
227
|
|
|
172
|
-
@async_handle_receiver_exceptions
|
|
173
228
|
async def async_get_command(self, request: str) -> str:
|
|
174
229
|
"""Send HTTP GET command to Denon AVR receiver asynchronously."""
|
|
175
230
|
# HTTP GET to endpoint
|
|
@@ -177,34 +232,46 @@ class DenonAVRApi:
|
|
|
177
232
|
# Return text
|
|
178
233
|
return res.text
|
|
179
234
|
|
|
180
|
-
@cache_result
|
|
181
|
-
@async_handle_receiver_exceptions
|
|
182
235
|
async def async_get_xml(
|
|
183
|
-
self, request: str, cache_id: Hashable = None
|
|
236
|
+
self, request: str, *, cache_id: Hashable = None
|
|
184
237
|
) -> ET.Element:
|
|
185
238
|
"""Return XML data from HTTP GET endpoint asynchronously."""
|
|
186
239
|
# HTTP GET to endpoint
|
|
187
|
-
res = await self.async_get(request)
|
|
240
|
+
res = await self.async_get(request, cache_id=cache_id)
|
|
188
241
|
# create ElementTree
|
|
189
|
-
|
|
242
|
+
try:
|
|
243
|
+
xml_root = fromstring(res.text)
|
|
244
|
+
except (
|
|
245
|
+
ET.ParseError,
|
|
246
|
+
DefusedXmlException,
|
|
247
|
+
ParseError,
|
|
248
|
+
UnicodeDecodeError,
|
|
249
|
+
) as err:
|
|
250
|
+
raise AvrInvalidResponseError(f"XMLParseError: {err}", request) from err
|
|
190
251
|
# Check validity of XML
|
|
191
252
|
self.check_xml_validity(request, xml_root)
|
|
192
253
|
# Return ElementTree element
|
|
193
254
|
return xml_root
|
|
194
255
|
|
|
195
|
-
@cache_result
|
|
196
|
-
@async_handle_receiver_exceptions
|
|
197
256
|
async def async_post_appcommand(
|
|
198
|
-
self, request: str, cmds: Tuple[AppCommandCmd], cache_id: Hashable = None
|
|
257
|
+
self, request: str, cmds: Tuple[AppCommandCmd], *, cache_id: Hashable = None
|
|
199
258
|
) -> ET.Element:
|
|
200
259
|
"""Return XML from Appcommand(0300) endpoint asynchronously."""
|
|
201
260
|
# Prepare XML body for POST call
|
|
202
261
|
content = self.prepare_appcommand_body(cmds)
|
|
203
262
|
_LOGGER.debug("Content for %s endpoint: %s", request, content)
|
|
204
263
|
# HTTP POST to endpoint
|
|
205
|
-
res = await self.async_post(request, content=content)
|
|
264
|
+
res = await self.async_post(request, content=content, cache_id=cache_id)
|
|
206
265
|
# create ElementTree
|
|
207
|
-
|
|
266
|
+
try:
|
|
267
|
+
xml_root = fromstring(res.text)
|
|
268
|
+
except (
|
|
269
|
+
ET.ParseError,
|
|
270
|
+
DefusedXmlException,
|
|
271
|
+
ParseError,
|
|
272
|
+
UnicodeDecodeError,
|
|
273
|
+
) as err:
|
|
274
|
+
raise AvrInvalidResponseError(f"XMLParseError: {err}", request) from err
|
|
208
275
|
# Check validity of XML
|
|
209
276
|
self.check_xml_validity(request, xml_root)
|
|
210
277
|
# Add query tags to result
|
|
@@ -350,10 +417,6 @@ class DenonAVRApi:
|
|
|
350
417
|
|
|
351
418
|
return body_bytes
|
|
352
419
|
|
|
353
|
-
def is_default_async_client(self) -> bool:
|
|
354
|
-
"""Check if default httpx.AsyncClient getter is used."""
|
|
355
|
-
return self.async_client_getter is get_default_async_client
|
|
356
|
-
|
|
357
420
|
|
|
358
421
|
class DenonAVRTelnetProtocol(asyncio.Protocol):
|
|
359
422
|
"""Protocol for the Denon AVR Telnet interface."""
|
|
@@ -481,8 +544,8 @@ class DenonAVRTelnetApi:
|
|
|
481
544
|
"%s: Connection failed on telnet reconnect: %s", self.host, err
|
|
482
545
|
)
|
|
483
546
|
raise AvrNetworkError(f"OSError: {err}", "telnet connect") from err
|
|
484
|
-
_LOGGER.debug("%s: telnet connection established", self.host)
|
|
485
547
|
self._protocol = cast(DenonAVRTelnetProtocol, transport_protocol[1])
|
|
548
|
+
_LOGGER.debug("%s: telnet connection established", self.host)
|
|
486
549
|
self._connection_enabled = True
|
|
487
550
|
self._last_message_time = time.monotonic()
|
|
488
551
|
self._schedule_monitor()
|
|
@@ -500,10 +563,55 @@ class DenonAVRTelnetApi:
|
|
|
500
563
|
"PSBAS ?",
|
|
501
564
|
"PSTRE ?",
|
|
502
565
|
"PSDYNEQ ?",
|
|
566
|
+
"PSLFC ?",
|
|
567
|
+
"PSCNTAMT ?",
|
|
503
568
|
"PSMULTEQ: ?",
|
|
504
569
|
"PSREFLEV ?",
|
|
505
570
|
"PSDYNVOL ?",
|
|
506
571
|
"MS?",
|
|
572
|
+
"MNMEN?",
|
|
573
|
+
"DIM ?",
|
|
574
|
+
"PSDELAY?",
|
|
575
|
+
"ECO?",
|
|
576
|
+
"VSMONI ?",
|
|
577
|
+
"PSDIRAC ?",
|
|
578
|
+
"CV?",
|
|
579
|
+
"PSNEURAL ?",
|
|
580
|
+
"PSIMAX ?",
|
|
581
|
+
"PSIMAXAUD ?",
|
|
582
|
+
"PSIMAXHPF ?",
|
|
583
|
+
"PSIMAXLPF ?",
|
|
584
|
+
"PSIMAXSWM ?",
|
|
585
|
+
"PSIMAXSWO ?",
|
|
586
|
+
"PSSWR ?",
|
|
587
|
+
"PSSWL ?",
|
|
588
|
+
"SSTTR ?",
|
|
589
|
+
"MSQUICK ?",
|
|
590
|
+
"STBY?",
|
|
591
|
+
"SLP?",
|
|
592
|
+
"VSAUDIO ?",
|
|
593
|
+
"PSCES ?",
|
|
594
|
+
"VSVPM ?",
|
|
595
|
+
"PSLFE ?",
|
|
596
|
+
"PSLOM ?",
|
|
597
|
+
"PSBSC ?",
|
|
598
|
+
"PSDEH ?",
|
|
599
|
+
"PSCINEMA EQ. ?",
|
|
600
|
+
"PSAUROPR ?",
|
|
601
|
+
"PSAUROST ?",
|
|
602
|
+
"PSAUROMODE ?",
|
|
603
|
+
"PSRSZ ?",
|
|
604
|
+
"TR?",
|
|
605
|
+
"SPPR ?",
|
|
606
|
+
"BTTX ?",
|
|
607
|
+
"PSDIC ?",
|
|
608
|
+
"PSSPV ?",
|
|
609
|
+
"PSSP: ?",
|
|
610
|
+
"PSDRC ?",
|
|
611
|
+
"PSDEL ?",
|
|
612
|
+
"PSRSTR ?",
|
|
613
|
+
"PSGEQ ?",
|
|
614
|
+
"PSHEQ ?",
|
|
507
615
|
skip_confirmation=True,
|
|
508
616
|
)
|
|
509
617
|
|
|
@@ -543,7 +651,8 @@ class DenonAVRTelnetApi:
|
|
|
543
651
|
self._stop_monitor()
|
|
544
652
|
if not self._connection_enabled:
|
|
545
653
|
return
|
|
546
|
-
self._reconnect_task
|
|
654
|
+
if self._reconnect_task is None:
|
|
655
|
+
self._reconnect_task = asyncio.create_task(self._async_reconnect())
|
|
547
656
|
|
|
548
657
|
async def async_disconnect(self) -> None:
|
|
549
658
|
"""Close the connection to the receiver asynchronously."""
|
|
@@ -581,6 +690,12 @@ class DenonAVRTelnetApi:
|
|
|
581
690
|
)
|
|
582
691
|
except AvrNetworkError as err:
|
|
583
692
|
_LOGGER.debug("%s: %s", self.host, err)
|
|
693
|
+
except AvrProcessingError as err:
|
|
694
|
+
_LOGGER.debug(
|
|
695
|
+
"%s: Failed updating state on telnet reconnect: %s",
|
|
696
|
+
self.host,
|
|
697
|
+
err,
|
|
698
|
+
)
|
|
584
699
|
except Exception as err: # pylint: disable=broad-except
|
|
585
700
|
_LOGGER.error(
|
|
586
701
|
"%s: Unexpected exception on telnet reconnect",
|
|
@@ -589,11 +704,13 @@ class DenonAVRTelnetApi:
|
|
|
589
704
|
)
|
|
590
705
|
else:
|
|
591
706
|
_LOGGER.info("%s: Telnet reconnected", self.host)
|
|
592
|
-
|
|
707
|
+
break
|
|
593
708
|
|
|
594
709
|
await asyncio.sleep(backoff)
|
|
595
710
|
backoff = min(30.0, backoff * 2)
|
|
596
711
|
|
|
712
|
+
self._reconnect_task = None
|
|
713
|
+
|
|
597
714
|
def register_callback(
|
|
598
715
|
self, event: str, callback: Callable[[str, str, str], Awaitable[None]]
|
|
599
716
|
) -> None:
|
|
@@ -604,6 +721,8 @@ class DenonAVRTelnetApi:
|
|
|
604
721
|
|
|
605
722
|
if event not in self._callbacks.keys():
|
|
606
723
|
self._callbacks[event] = []
|
|
724
|
+
elif callback in self._callbacks[event]:
|
|
725
|
+
return
|
|
607
726
|
self._callbacks[event].append(callback)
|
|
608
727
|
|
|
609
728
|
def unregister_callback(
|
|
@@ -618,6 +737,8 @@ class DenonAVRTelnetApi:
|
|
|
618
737
|
self, callback: Callable[[str], Awaitable[None]]
|
|
619
738
|
) -> None:
|
|
620
739
|
"""Register a callback handler for raw telnet messages."""
|
|
740
|
+
if callback in self._raw_callbacks:
|
|
741
|
+
return
|
|
621
742
|
self._raw_callbacks.append(callback)
|
|
622
743
|
|
|
623
744
|
def _unregister_raw_callback(
|
denonavr/audyssey.py
CHANGED
|
@@ -60,6 +60,12 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
60
60
|
_multeq_control: Optional[bool] = attr.ib(
|
|
61
61
|
converter=attr.converters.optional(convert_string_int_bool), default=None
|
|
62
62
|
)
|
|
63
|
+
_lfc: Optional[bool] = attr.ib(
|
|
64
|
+
converter=attr.converters.optional(convert_string_int_bool), default=None
|
|
65
|
+
)
|
|
66
|
+
_containment_amount: Optional[int] = attr.ib(
|
|
67
|
+
converter=attr.converters.optional(int), default=None
|
|
68
|
+
)
|
|
63
69
|
|
|
64
70
|
# Update tags for attributes
|
|
65
71
|
# AppCommand0300.xml interface
|
|
@@ -94,6 +100,12 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
94
100
|
self._dynamiceq = "1"
|
|
95
101
|
elif parameter == "DYNEQ OFF":
|
|
96
102
|
self._dynamiceq = "0"
|
|
103
|
+
elif parameter == "LFC ON":
|
|
104
|
+
self._lfc = "1"
|
|
105
|
+
elif parameter == "LFC OFF":
|
|
106
|
+
self._lfc = "0"
|
|
107
|
+
elif parameter[:6] == "CNTAMT":
|
|
108
|
+
self._containment_amount = int(parameter[7:])
|
|
97
109
|
|
|
98
110
|
async def async_update(
|
|
99
111
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
@@ -186,6 +198,24 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
186
198
|
return list(MULTI_EQ_MAP_LABELS_TELNET.keys())
|
|
187
199
|
return list(MULTI_EQ_MAP_LABELS_APPCOMMAND.keys())
|
|
188
200
|
|
|
201
|
+
@property
|
|
202
|
+
def lfc(self) -> Optional[bool]:
|
|
203
|
+
"""
|
|
204
|
+
Return value of LFC.
|
|
205
|
+
|
|
206
|
+
Only available if using Telnet.
|
|
207
|
+
"""
|
|
208
|
+
return self._lfc
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def containment_amount(self) -> Optional[int]:
|
|
212
|
+
"""
|
|
213
|
+
Return value of Containment Amount.
|
|
214
|
+
|
|
215
|
+
Only available if using Telnet.
|
|
216
|
+
"""
|
|
217
|
+
return self._containment_amount
|
|
218
|
+
|
|
189
219
|
##########
|
|
190
220
|
# Setter #
|
|
191
221
|
##########
|
|
@@ -284,6 +314,81 @@ class DenonAVRAudyssey(DenonAVRFoundation):
|
|
|
284
314
|
else:
|
|
285
315
|
await self.async_dynamiceq_on()
|
|
286
316
|
|
|
317
|
+
async def async_lfc_on(self):
|
|
318
|
+
"""Turn LFC on."""
|
|
319
|
+
if self._device.telnet_available:
|
|
320
|
+
await self._device.telnet_api.async_send_commands(
|
|
321
|
+
self._device.telnet_commands.command_lfc.format(mode="ON")
|
|
322
|
+
)
|
|
323
|
+
return
|
|
324
|
+
await self._device.api.async_get_command(
|
|
325
|
+
self._device.urls.command_lfc.format(mode="ON")
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
async def async_lfc_off(self):
|
|
329
|
+
"""Turn LFC off."""
|
|
330
|
+
if self._device.telnet_available:
|
|
331
|
+
await self._device.telnet_api.async_send_commands(
|
|
332
|
+
self._device.telnet_commands.command_lfc.format(mode="OFF")
|
|
333
|
+
)
|
|
334
|
+
return
|
|
335
|
+
await self._device.api.async_get_command(
|
|
336
|
+
self._device.urls.command_lfc.format(mode="OFF")
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
async def async_toggle_lfc(self):
|
|
340
|
+
"""Toggle LFC."""
|
|
341
|
+
if self._lfc:
|
|
342
|
+
await self.async_lfc_off()
|
|
343
|
+
else:
|
|
344
|
+
await self.async_lfc_on()
|
|
345
|
+
|
|
346
|
+
async def async_containment_amount(self, amount: int) -> None:
|
|
347
|
+
"""
|
|
348
|
+
Set Containment Amount.
|
|
349
|
+
|
|
350
|
+
Valid values are 1-7.
|
|
351
|
+
"""
|
|
352
|
+
if amount < 1 or amount > 7:
|
|
353
|
+
raise AvrCommandError("Containment amount must be between 1 and 7")
|
|
354
|
+
local_amount = f"{amount:02}"
|
|
355
|
+
if self._device.telnet_available:
|
|
356
|
+
await self._device.telnet_api.async_send_commands(
|
|
357
|
+
self._device.telnet_commands.command_containment_amount.format(
|
|
358
|
+
value=local_amount
|
|
359
|
+
)
|
|
360
|
+
)
|
|
361
|
+
return
|
|
362
|
+
await self._device.api.async_get_command(
|
|
363
|
+
self._device.urls.command_containment_amount.format(value=local_amount)
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
async def async_containment_amount_up(self) -> None:
|
|
367
|
+
"""Increase Containment Amount."""
|
|
368
|
+
if self._device.telnet_available:
|
|
369
|
+
await self._device.telnet_api.async_send_commands(
|
|
370
|
+
self._device.telnet_commands.command_containment_amount.format(
|
|
371
|
+
value="UP"
|
|
372
|
+
)
|
|
373
|
+
)
|
|
374
|
+
return
|
|
375
|
+
await self._device.api.async_get_command(
|
|
376
|
+
self._device.urls.command_containment_amount.format(value="UP")
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
async def async_containment_amount_down(self) -> None:
|
|
380
|
+
"""Decrease Containment Amount."""
|
|
381
|
+
if self._device.telnet_available:
|
|
382
|
+
await self._device.telnet_api.async_send_commands(
|
|
383
|
+
self._device.telnet_commands.command_containment_amount.format(
|
|
384
|
+
value="DOWN"
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
return
|
|
388
|
+
await self._device.api.async_get_command(
|
|
389
|
+
self._device.urls.command_containment_amount.format(value="DOWN")
|
|
390
|
+
)
|
|
391
|
+
|
|
287
392
|
|
|
288
393
|
def audyssey_factory(instance: DenonAVRFoundation) -> DenonAVRAudyssey:
|
|
289
394
|
"""Create DenonAVRAudyssey at receiver instances."""
|