denonavr 0.11.2__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/const.py CHANGED
@@ -45,6 +45,31 @@ ReceiverURLs = namedtuple(
45
45
  "command_play",
46
46
  ],
47
47
  )
48
+ TelnetCommands = namedtuple(
49
+ "TelnetCommands",
50
+ [
51
+ "command_sel_src",
52
+ "command_fav_src",
53
+ "command_power_on",
54
+ "command_power_standby",
55
+ "command_volume_up",
56
+ "command_volume_down",
57
+ "command_set_volume",
58
+ "command_mute_on",
59
+ "command_mute_off",
60
+ "command_sel_sound_mode",
61
+ "command_set_all_zone_stereo",
62
+ "command_pause",
63
+ "command_play",
64
+ "command_multieq",
65
+ "command_dynamiceq",
66
+ "command_reflevoffset",
67
+ "command_dynamicvol",
68
+ "command_tonecontrol",
69
+ "command_bass",
70
+ "command_treble",
71
+ ],
72
+ )
48
73
 
49
74
  # AVR-X search patterns
50
75
  DEVICEINFO_AVR_X_PATTERN = re.compile(
@@ -95,7 +120,7 @@ CHANGE_INPUT_MAPPING = {
95
120
  "Spotify": "SPOTIFY",
96
121
  }
97
122
 
98
- TELNET_SOURCES = [
123
+ TELNET_SOURCES = {
99
124
  "CD",
100
125
  "PHONO",
101
126
  "TUNER",
@@ -109,6 +134,7 @@ TELNET_SOURCES = [
109
134
  "NET",
110
135
  "PANDORA",
111
136
  "SIRIUSXM",
137
+ "SOURCE",
112
138
  "LASTFM",
113
139
  "FLICKR",
114
140
  "IRADIO",
@@ -126,7 +152,7 @@ TELNET_SOURCES = [
126
152
  "USB/IPOD",
127
153
  "USB DIRECT",
128
154
  "IPOD DIRECT",
129
- ]
155
+ }
130
156
 
131
157
  TELNET_MAPPING = {
132
158
  "FAVORITES": "Favorites",
@@ -167,6 +193,7 @@ SOUND_MODE_MAPPING = {
167
193
  "DOLBY PL2 CINEMA",
168
194
  "DOLBY PL2 C",
169
195
  "DOLBY PL2 X MOVIE",
196
+ "DOLBY PL2 MOVIE",
170
197
  ],
171
198
  "GAME": [
172
199
  "PLII GAME",
@@ -174,6 +201,7 @@ SOUND_MODE_MAPPING = {
174
201
  "DOLBY PL2 GAME",
175
202
  "DOLBY PL2 G",
176
203
  "DOLBY PL2 X GAME",
204
+ "DOLBY PLII GAME",
177
205
  ],
178
206
  "AUTO": ["None"],
179
207
  "STANDARD": ["None2"],
@@ -188,11 +216,14 @@ SOUND_MODE_MAPPING = {
188
216
  "DOLBY DIGITAL": [
189
217
  "DOLBY DIGITAL",
190
218
  "DOLBY D + DOLBY SURROUND",
219
+ "DOLBY D+DS",
220
+ "DOLBY D+ +DS",
191
221
  "DOLBY DIGITAL +",
192
222
  "STANDARD(DOLBY)",
193
223
  "DOLBY SURROUND",
194
224
  "DOLBY D + +DOLBY SURROUND",
195
225
  "NEURAL",
226
+ "NEURAL:X",
196
227
  "DOLBY HD",
197
228
  "DOLBY HD + DOLBY SURROUND",
198
229
  "MULTI IN + DSUR",
@@ -208,11 +239,15 @@ SOUND_MODE_MAPPING = {
208
239
  "DOLBY AUDIO - TRUEHD + DSUR",
209
240
  "DOLBY AUDIO - DOLBY TRUEHD",
210
241
  "DOLBY AUDIO - TRUEHD + NEURAL:X",
242
+ "DOLBY AUDIO - DD + NEURAL:X",
211
243
  "DOLBY AUDIO - DD + DSUR",
212
244
  "DOLBY AUDIO - DD+ + NEURAL:X",
213
245
  "DOLBY AUDIO - DD+ + DSUR",
246
+ "DOLBY AUDIO-DD+ +DSUR",
214
247
  "DOLBY AUDIO - DOLBY DIGITAL",
215
248
  "DOLBY AUDIO-DSUR",
249
+ "DOLBY AUDIO-DD+DSUR",
250
+ "DOLBY PRO LOGIC",
216
251
  ],
217
252
  "DTS SURROUND": [
218
253
  "DTS SURROUND",
@@ -230,7 +265,8 @@ SOUND_MODE_MAPPING = {
230
265
  "DTS-HD + DSUR",
231
266
  "DTS:X MSTR",
232
267
  ],
233
- "AURO3D": ["AURO-3D", "AURO-2D SURROUND"],
268
+ "AURO3D": ["AURO-3D"],
269
+ "AURO2DSURR": ["AURO-2D SURROUND"],
234
270
  "MCH STEREO": [
235
271
  "MULTI CH STEREO",
236
272
  "MULTI_CH_STEREO",
@@ -391,23 +427,146 @@ ZONE3_URLS = ReceiverURLs(
391
427
  command_play=COMMAND_PLAY,
392
428
  )
393
429
 
394
- # Telnet Commands
395
- TELNET_EVENTS = {"HD", "MS", "MU", "MV", "NS", "PS", "PW", "SI", "SS", "TF"}
430
+ # Telnet Events
431
+ ALL_TELNET_EVENTS = "ALL"
432
+ TELNET_EVENTS = {
433
+ "CV",
434
+ "DC",
435
+ "DIM",
436
+ "ECO",
437
+ "HD",
438
+ "MN",
439
+ "MS",
440
+ "MU",
441
+ "MV",
442
+ "NS",
443
+ "NSA",
444
+ "NSE",
445
+ "OP",
446
+ "PS",
447
+ "PV",
448
+ "PW",
449
+ "RM",
450
+ "SD",
451
+ "SI",
452
+ "SLP",
453
+ "SR",
454
+ "SS",
455
+ "STBY",
456
+ "SV",
457
+ "SY",
458
+ "TF",
459
+ "TM",
460
+ "TP",
461
+ "TR",
462
+ "UG",
463
+ "VS",
464
+ "ZM",
465
+ "Z2",
466
+ "Z3",
467
+ }
468
+ ALL_ZONE_TELNET_EVENTS = {
469
+ "DIM",
470
+ "HD",
471
+ "NS",
472
+ "NSA",
473
+ "NSE",
474
+ "MN",
475
+ "PW",
476
+ "RM",
477
+ "SY",
478
+ "TF",
479
+ "TM",
480
+ "TP",
481
+ "TR",
482
+ "UG",
483
+ }
484
+
485
+ DENONAVR_TELNET_COMMANDS = TelnetCommands(
486
+ command_sel_src="SI",
487
+ command_fav_src="ZM",
488
+ command_power_on="ZMON",
489
+ command_power_standby="ZMOFF",
490
+ command_volume_up="MVUP",
491
+ command_volume_down="MVDOWN",
492
+ command_set_volume="MV{volume:02d}",
493
+ command_mute_on="MUON",
494
+ command_mute_off="MUOFF",
495
+ command_sel_sound_mode="MS",
496
+ command_set_all_zone_stereo="MN",
497
+ command_pause="NS9B",
498
+ command_play="NS9A",
499
+ command_multieq="PSMULTEQ:",
500
+ command_dynamiceq="PSDYNEQ ",
501
+ command_reflevoffset="PSREFLEV ",
502
+ command_dynamicvol="PSDYNVOL ",
503
+ command_tonecontrol="PSTONE CTRL ",
504
+ command_bass="PSBAS ",
505
+ command_treble="PSTRE ",
506
+ )
507
+
508
+ ZONE2_TELNET_COMMANDS = TelnetCommands(
509
+ command_sel_src="Z2",
510
+ command_fav_src="Z2",
511
+ command_power_on="Z2ON",
512
+ command_power_standby="Z2OFF",
513
+ command_volume_up="Z2UP",
514
+ command_volume_down="Z2DOWN",
515
+ command_set_volume="Z2{volume:02d}",
516
+ command_mute_on="Z2MUON",
517
+ command_mute_off="Z2MUOFF",
518
+ command_sel_sound_mode="MS",
519
+ command_set_all_zone_stereo="MN",
520
+ command_pause="NS9B",
521
+ command_play="NS9A",
522
+ command_multieq="PSMULTEQ:",
523
+ command_dynamiceq="PSDYNEQ ",
524
+ command_reflevoffset="PSREFLEV ",
525
+ command_dynamicvol="PSDYNVOL ",
526
+ command_tonecontrol="PSTONE CTRL ",
527
+ command_bass="PSBAS ",
528
+ command_treble="PSTRE ",
529
+ )
530
+
531
+ ZONE3_TELNET_COMMANDS = TelnetCommands(
532
+ command_sel_src="Z3",
533
+ command_fav_src="Z3",
534
+ command_power_on="Z3ON",
535
+ command_power_standby="Z3OFF",
536
+ command_volume_up="Z3UP",
537
+ command_volume_down="Z3DOWN",
538
+ command_set_volume="Z3{volume:02d}",
539
+ command_mute_on="Z3MUON",
540
+ command_mute_off="Z3MUOFF",
541
+ command_sel_sound_mode="MS",
542
+ command_set_all_zone_stereo="MN",
543
+ command_pause="NS9B",
544
+ command_play="NS9A",
545
+ command_multieq="PSMULTEQ:",
546
+ command_dynamiceq="PSDYNEQ ",
547
+ command_reflevoffset="PSREFLEV ",
548
+ command_dynamicvol="PSDYNVOL ",
549
+ command_tonecontrol="PSTONE CTRL ",
550
+ command_bass="PSBAS ",
551
+ command_treble="PSTRE ",
552
+ )
396
553
 
397
554
  # States
398
555
  POWER_ON = "ON"
399
556
  POWER_OFF = "OFF"
400
557
  POWER_STANDBY = "STANDBY"
558
+ POWER_STATES = {POWER_ON, POWER_OFF, POWER_STANDBY}
401
559
  STATE_ON = "on"
402
560
  STATE_OFF = "off"
403
561
  STATE_PLAYING = "playing"
404
562
  STATE_PAUSED = "paused"
405
563
 
406
564
  # Zones
565
+ ALL_ZONES = "All"
407
566
  MAIN_ZONE = "Main"
408
567
  ZONE2 = "Zone2"
409
568
  ZONE3 = "Zone3"
410
- VALID_ZONES = [MAIN_ZONE, ZONE2, ZONE3]
569
+ VALID_ZONES = {MAIN_ZONE, ZONE2, ZONE3}
411
570
 
412
571
  # Setup additional zones
413
572
  NO_ZONES = None
@@ -420,11 +579,41 @@ APPCOMMAND_CMD_TEXT = "cmd_text"
420
579
  APPCOMMAND_NAME = "name"
421
580
 
422
581
  # Audyssey parameter
423
- MULTI_EQ_MAP = {"0": "Off", "1": "Flat", "2": "L/R Bypass", "3": "Reference"}
424
- MULTI_EQ_MAP_LABELS = {(value, key) for key, value in MULTI_EQ_MAP.items()}
582
+ MULTI_EQ_MAP_APPCOMMAND = {"0": "Off", "1": "Flat", "2": "L/R Bypass", "3": "Reference"}
583
+ MULTI_EQ_MAP_TELNET = {
584
+ "OFF": "Off",
585
+ "FLAT": "Flat",
586
+ "BYP.LR": "L/R Bypass",
587
+ "AUDYSSEY": "Reference",
588
+ "MANUAL": "Manual",
589
+ }
590
+ MULTI_EQ_MAP = {**MULTI_EQ_MAP_APPCOMMAND, **MULTI_EQ_MAP_TELNET}
591
+ MULTI_EQ_MAP_LABELS_APPCOMMAND = {
592
+ value: key for key, value in MULTI_EQ_MAP_APPCOMMAND.items()
593
+ }
594
+ MULTI_EQ_MAP_LABELS_TELNET = {value: key for key, value in MULTI_EQ_MAP_TELNET.items()}
425
595
 
426
- REF_LVL_OFFSET_MAP = {"0": "0dB", "1": "+5dB", "2": "+10dB", "3": "+15dB"}
427
- REF_LVL_OFFSET_MAP_LABELS = {(value, key) for key, value in REF_LVL_OFFSET_MAP.items()}
596
+ REF_LVL_OFFSET_MAP_APPCOMMAND = {"0": "0dB", "1": "+5dB", "2": "+10dB", "3": "+15dB"}
597
+ REF_LVL_OFFSET_MAP_TELNET = {"0": "0dB", "5": "+5dB", "10": "+10dB", "15": "+15dB"}
598
+ REF_LVL_OFFSET_MAP = {**REF_LVL_OFFSET_MAP_APPCOMMAND, **REF_LVL_OFFSET_MAP_TELNET}
599
+ REF_LVL_OFFSET_MAP_LABELS_APPCOMMAND = {
600
+ value: key for key, value in REF_LVL_OFFSET_MAP_APPCOMMAND.items()
601
+ }
602
+ REF_LVL_OFFSET_MAP_LABELS_TELNET = {
603
+ value: key for key, value in REF_LVL_OFFSET_MAP_TELNET.items()
604
+ }
428
605
 
429
- DYNAMIC_VOLUME_MAP = {"0": "Off", "1": "Light", "2": "Medium", "3": "Heavy"}
430
- DYNAMIC_VOLUME_MAP_LABELS = {(value, key) for key, value in DYNAMIC_VOLUME_MAP.items()}
606
+ DYNAMIC_VOLUME_MAP_APPCOMMAND = {"0": "Off", "1": "Light", "2": "Medium", "3": "Heavy"}
607
+ DYNAMIC_VOLUME_MAP_TELNET = {
608
+ "OFF": "Off",
609
+ "LIT": "Light",
610
+ "MED": "Medium",
611
+ "HEV": "Heavy",
612
+ }
613
+ DYNAMIC_VOLUME_MAP = {**DYNAMIC_VOLUME_MAP_APPCOMMAND, **DYNAMIC_VOLUME_MAP_TELNET}
614
+ DYNAMIC_VOLUME_MAP_LABELS_APPCOMMAND = {
615
+ value: key for key, value in DYNAMIC_VOLUME_MAP_APPCOMMAND.items()
616
+ }
617
+ DYNAMIC_VOLUME_MAP_LABELS_TELNET = {
618
+ value: key for key, value in DYNAMIC_VOLUME_MAP_TELNET.items()
619
+ }
denonavr/decorators.py CHANGED
@@ -16,6 +16,7 @@ from functools import wraps
16
16
  from typing import Callable, Coroutine, TypeVar
17
17
 
18
18
  import httpx
19
+ from asyncstdlib import lru_cache
19
20
  from defusedxml import DefusedXmlException
20
21
  from defusedxml.ElementTree import ParseError
21
22
 
@@ -34,7 +35,7 @@ AnyT = TypeVar("AnyT")
34
35
 
35
36
  def async_handle_receiver_exceptions(func: Callable[..., AnyT]) -> Callable[..., AnyT]:
36
37
  """
37
- Handle exceptions raised when calling an Denon AVR endpoint asynchronously.
38
+ Handle exceptions raised when calling a Denon AVR endpoint asynchronously.
38
39
 
39
40
  The decorated function must either have a string variable as second
40
41
  argument or as "request" keyword argument.
@@ -48,24 +49,18 @@ def async_handle_receiver_exceptions(func: Callable[..., AnyT]) -> Callable[...,
48
49
  _LOGGER.debug("HTTP status error on request %s", err.request, exc_info=True)
49
50
  # Separate handling of 403 errors
50
51
  if err.response.status_code == 403:
51
- raise AvrForbiddenError(
52
- "HTTPStatusError: {}".format(err), err.request
53
- ) from err
54
- raise AvrRequestError(
55
- "HTTPStatusError: {}".format(err), err.request
56
- ) from err
52
+ raise AvrForbiddenError(f"HTTPStatusError: {err}", err.request) from err
53
+ raise AvrRequestError(f"HTTPStatusError: {err}", err.request) from err
57
54
  except httpx.TimeoutException as err:
58
55
  _LOGGER.debug(
59
56
  "HTTP timeout exception on request %s", err.request, exc_info=True
60
57
  )
61
- raise AvrTimoutError(
62
- "TimeoutException: {}".format(err), err.request
63
- ) from err
58
+ raise AvrTimoutError(f"TimeoutException: {err}", err.request) from err
64
59
  except httpx.NetworkError as err:
65
60
  _LOGGER.debug(
66
61
  "Network error exception on request %s", err.request, exc_info=True
67
62
  )
68
- raise AvrNetworkError("NetworkError: {}".format(err), err.request) from err
63
+ raise AvrNetworkError(f"NetworkError: {err}", err.request) from err
69
64
  except httpx.RemoteProtocolError as err:
70
65
  _LOGGER.debug(
71
66
  "Remote protocol error exception on request %s",
@@ -73,7 +68,7 @@ def async_handle_receiver_exceptions(func: Callable[..., AnyT]) -> Callable[...,
73
68
  exc_info=True,
74
69
  )
75
70
  raise AvrInvalidResponseError(
76
- "RemoteProtocolError: {}".format(err), err.request
71
+ f"RemoteProtocolError: {err}", err.request
77
72
  ) from err
78
73
  except (
79
74
  ET.ParseError,
@@ -85,50 +80,43 @@ def async_handle_receiver_exceptions(func: Callable[..., AnyT]) -> Callable[...,
85
80
  "Defusedxml parse error on request %s", (args, kwargs), exc_info=True
86
81
  )
87
82
  raise AvrInvalidResponseError(
88
- "XMLParseError: {}".format(err), (args, kwargs)
83
+ f"XMLParseError: {err}", (args, kwargs)
89
84
  ) from err
90
85
 
91
86
  return wrapper
92
87
 
93
88
 
94
- def cache_clear_on_exception(func: Callable[..., AnyT]) -> Callable[..., AnyT]:
89
+ def cache_result(func: Callable[..., AnyT]) -> Callable[..., AnyT]:
95
90
  """
96
- Decorate a function to clear lru_cache if an exception occurs.
91
+ Decorate a function to cache its results with an lru_cache of maxsize 16.
97
92
 
98
- The decorator must be placed right before the @lru_cache decorator.
99
- It prevents memory leaks in home-assistant when receiver instances are
100
- created and deleted right away in case the device is offline on setup.
93
+ This decorator also sets an "cache_id" keyword argument if it is not set yet.
94
+ When an exception occurs it clears lru_cache to prevent memory leaks in
95
+ home-assistant when receiver instances are created and deleted right
96
+ away in case the device is offline on setup.
101
97
  """
98
+ if inspect.signature(func).parameters.get("cache_id") is None:
99
+ raise AttributeError(
100
+ f"Function {func} does not have a 'cache_id' keyword parameter"
101
+ )
102
+
103
+ lru_decorator = lru_cache(maxsize=16)
104
+ cached_func = lru_decorator(func)
102
105
 
103
106
  @wraps(func)
104
107
  async def wrapper(*args, **kwargs):
108
+ if kwargs.get("cache_id") is None:
109
+ kwargs["cache_id"] = time.time()
105
110
  try:
106
- return await func(*args, **kwargs)
111
+ return await cached_func(*args, **kwargs)
107
112
  except Exception as err:
108
113
  _LOGGER.debug("Exception %s raised, clearing cache", err)
109
- func.cache_clear()
114
+ cached_func.cache_clear()
110
115
  raise
111
116
 
112
117
  return wrapper
113
118
 
114
119
 
115
- def set_cache_id(func: Callable[..., AnyT]) -> Callable[..., AnyT]:
116
- """
117
- Decorate a function to add cache_id keyword argument if it is not present.
118
-
119
- The function must be called with a fix cache_id keyword argument to be able
120
- to get cached data. This prevents accidential caching of a function result.
121
- """
122
-
123
- @wraps(func)
124
- def wrapper(*args, **kwargs):
125
- if kwargs.get("cache_id") is None:
126
- kwargs["cache_id"] = time.time()
127
- return func(*args, **kwargs)
128
-
129
- return wrapper
130
-
131
-
132
120
  def run_async_synchronously(async_func: Coroutine) -> Callable:
133
121
  """
134
122
  Decorate to run the configured asynchronous function synchronously instead.
@@ -141,13 +129,11 @@ def run_async_synchronously(async_func: Coroutine) -> Callable:
141
129
  def decorator(func: Callable):
142
130
  # Check if function is a coroutine
143
131
  if not inspect.iscoroutinefunction(async_func):
144
- raise AttributeError(
145
- "Function {} is not a coroutine function".format(async_func)
146
- )
132
+ raise AttributeError(f"Function {async_func} is not a coroutine function")
147
133
  # Check if the signature of both functions is equal
148
134
  if inspect.signature(func) != inspect.signature(async_func):
149
135
  raise AttributeError(
150
- "Functions {} and {} have different signatures".format(func, async_func)
136
+ f"Functions {func} and {async_func} have different signatures"
151
137
  )
152
138
 
153
139
  @wraps(func)
denonavr/denonavr.py CHANGED
@@ -124,7 +124,7 @@ class DenonAVR(DenonAVRFoundation):
124
124
  # Name either set explicitly or name of Main Zone with suffix
125
125
  zonename = None
126
126
  if zname is None and self._name is not None:
127
- zonename = "{} {}".format(self._name, zone)
127
+ zonename = f"{self._name} {zone}"
128
128
  zone_device = attr.evolve(self._device, zone=zone)
129
129
  zone_inst = DenonAVR(
130
130
  host=self._host,
@@ -167,7 +167,7 @@ class DenonAVR(DenonAVRFoundation):
167
167
  Method executes the update method for the current receiver type.
168
168
  """
169
169
  # Ensure that the device is setup
170
- if self._is_setup is False:
170
+ if not self._is_setup:
171
171
  await self.async_setup()
172
172
 
173
173
  # Create a cache id for this global update
@@ -226,6 +226,10 @@ class DenonAVR(DenonAVRFoundation):
226
226
  def send_get_command(self, request: str) -> str:
227
227
  """Send HTTP GET command to Denon AVR receiver...for compatibility."""
228
228
 
229
+ async def async_send_telnet_commands(self, *commands: str) -> None:
230
+ """Send telnet commands to the receiver."""
231
+ await self._device.telnet_api.async_send_commands(*commands)
232
+
229
233
  def send_telnet_commands(self, *commands: str) -> None:
230
234
  """Send telnet commands to the receiver."""
231
235
  self._device.telnet_api.send_commands(*commands)
@@ -435,13 +439,18 @@ class DenonAVR(DenonAVRFoundation):
435
439
  return None
436
440
  return self._device.receiver.type
437
441
 
442
+ @property
443
+ def telnet_available(self) -> bool:
444
+ """Return True if telnet is connected and healthy."""
445
+ return self._device.telnet_available
446
+
438
447
  @property
439
448
  def telnet_connected(self) -> bool:
440
449
  """Return True if telnet is connected."""
441
450
  return self._device.telnet_api.connected
442
451
 
443
452
  @property
444
- def telnet_healthy(self) -> Optional[bool]:
453
+ def telnet_healthy(self) -> bool:
445
454
  """Return True if telnet connection is healthy."""
446
455
  return self._device.telnet_api.healthy
447
456
 
@@ -450,6 +459,16 @@ class DenonAVR(DenonAVRFoundation):
450
459
  """Indicate if all inputs are shown or just active one."""
451
460
  return self._show_all_inputs
452
461
 
462
+ @property
463
+ def tone_control_status(self) -> Optional[bool]:
464
+ """Return value of tone control status."""
465
+ return self.tonecontrol.tone_control_status
466
+
467
+ @property
468
+ def tone_control_adjust(self) -> Optional[bool]:
469
+ """Return value of tone control adjust."""
470
+ return self.tonecontrol.tone_control_adjust
471
+
453
472
  @property
454
473
  def bass(self) -> Optional[int]:
455
474
  """Return value of bass."""
@@ -505,10 +524,6 @@ class DenonAVR(DenonAVRFoundation):
505
524
  """Return a list of available MultiEQ settings."""
506
525
  return self.audyssey.multi_eq_setting_list
507
526
 
508
- async def async_dynamic_eq_off(self) -> None:
509
- """Turn DynamicEQ off."""
510
- await self.audyssey.async_dynamiceq_off()
511
-
512
527
  ##########
513
528
  # Setter #
514
529
  ##########
@@ -525,6 +540,10 @@ class DenonAVR(DenonAVRFoundation):
525
540
  raise AvrCommandError("Provided object is not callable")
526
541
  self._device.api.async_client_getter = async_client_getter
527
542
 
543
+ async def async_dynamic_eq_off(self) -> None:
544
+ """Turn DynamicEQ off."""
545
+ await self.audyssey.async_dynamiceq_off()
546
+
528
547
  @run_async_synchronously(async_func=async_dynamic_eq_off)
529
548
  def dynamic_eq_off(self) -> None:
530
549
  """Turn DynamicEQ off."""
@@ -545,6 +564,30 @@ class DenonAVR(DenonAVRFoundation):
545
564
  def toggle_dynamic_eq(self) -> None:
546
565
  """Toggle DynamicEQ."""
547
566
 
567
+ async def async_set_multieq(self, value: str) -> None:
568
+ """Set MultiEQ mode."""
569
+ await self.audyssey.async_set_multieq(value)
570
+
571
+ @run_async_synchronously(async_func=async_set_multieq)
572
+ def set_multieq(self, value: str) -> None:
573
+ """Set MultiEQ mode."""
574
+
575
+ async def async_set_reflevoffset(self, value: str) -> None:
576
+ """Set Reference Level Offset."""
577
+ await self.audyssey.async_set_reflevoffset(value)
578
+
579
+ @run_async_synchronously(async_func=async_set_reflevoffset)
580
+ def set_reflevoffset(self, value: str) -> None:
581
+ """Set Reference Level Offset."""
582
+
583
+ async def async_set_dynamicvol(self, value: str) -> None:
584
+ """Set Dynamic Volume."""
585
+ await self.audyssey.async_set_dynamicvol(value)
586
+
587
+ @run_async_synchronously(async_func=async_set_dynamicvol)
588
+ def set_dynamicvol(self, value: str) -> None:
589
+ """Set Dynamic Volume."""
590
+
548
591
  async def async_set_input_func(self, input_func: str) -> None:
549
592
  """
550
593
  Set input_func of device.