denonavr 1.1.1__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/foundation.py CHANGED
@@ -39,7 +39,10 @@ from .const import (
39
39
  DIMMER_MODE_MAP_LABELS,
40
40
  ECO_MODE_MAP,
41
41
  ECO_MODE_MAP_LABELS,
42
+ HDMI_OUTPUT_MAP,
42
43
  HDMI_OUTPUT_MAP_LABELS,
44
+ ILLUMINATION_MAP,
45
+ ILLUMINATION_MAP_LABELS,
43
46
  MAIN_ZONE,
44
47
  POWER_STATES,
45
48
  SETTINGS_MENU_STATES,
@@ -60,6 +63,7 @@ from .const import (
60
63
  EcoModes,
61
64
  HDMIAudioDecodes,
62
65
  HDMIOutputs,
66
+ Illuminations,
63
67
  PanelLocks,
64
68
  ReceiverType,
65
69
  ReceiverURLs,
@@ -153,6 +157,7 @@ class DenonAVRDeviceInfo:
153
157
  _dimmer: Optional[str] = attr.ib(
154
158
  converter=attr.converters.optional(str), default=None
155
159
  )
160
+ _dimmer_modes = get_args(DimmerModes)
156
161
  _auto_standby: Optional[str] = attr.ib(
157
162
  converter=attr.converters.optional(str), default=None
158
163
  )
@@ -166,9 +171,11 @@ class DenonAVRDeviceInfo:
166
171
  _eco_mode: Optional[str] = attr.ib(
167
172
  converter=attr.converters.optional(str), default=None
168
173
  )
174
+ _eco_modes = get_args(EcoModes)
169
175
  _hdmi_output: Optional[str] = attr.ib(
170
176
  converter=attr.converters.optional(str), default=None
171
177
  )
178
+ _hdmi_outputs = get_args(HDMIOutputs)
172
179
  _hdmi_audio_decode: Optional[str] = attr.ib(
173
180
  converter=attr.converters.optional(str), default=None
174
181
  )
@@ -216,6 +223,13 @@ class DenonAVRDeviceInfo:
216
223
  _headphone_eq: Optional[bool] = attr.ib(
217
224
  converter=attr.converters.optional(convert_on_off_bool), default=None
218
225
  )
226
+ _illumination: Optional[str] = attr.ib(
227
+ converter=attr.converters.optional(str), default=None
228
+ )
229
+ _illuminations = get_args(Illuminations)
230
+ _auto_lip_sync: Optional[bool] = attr.ib(
231
+ converter=attr.converters.optional(convert_on_off_bool), default=None
232
+ )
219
233
  _is_setup: bool = attr.ib(converter=bool, default=False, init=False)
220
234
  _allow_recovery: bool = attr.ib(converter=bool, default=True, init=True)
221
235
  _setup_lock: asyncio.Lock = attr.ib(default=attr.Factory(asyncio.Lock))
@@ -235,16 +249,12 @@ class DenonAVRDeviceInfo:
235
249
  else:
236
250
  raise ValueError(f"Invalid zone {self.zone}")
237
251
 
238
- async def _async_power_callback(
239
- self, zone: str, event: str, parameter: str
240
- ) -> None:
252
+ def _power_callback(self, zone: str, event: str, parameter: str) -> None:
241
253
  """Handle a power change event."""
242
254
  if self.zone == zone and parameter in POWER_STATES:
243
255
  self._power = parameter
244
256
 
245
- async def _async_settings_menu_callback(
246
- self, zone: str, event: str, parameter: str
247
- ) -> None:
257
+ def _settings_menu_callback(self, zone: str, event: str, parameter: str) -> None:
248
258
  """Handle a settings menu event."""
249
259
  if (
250
260
  event == "MN"
@@ -253,23 +263,17 @@ class DenonAVRDeviceInfo:
253
263
  ):
254
264
  self._settings_menu = parameter[4:]
255
265
 
256
- async def _async_dimmer_callback(
257
- self, zone: str, event: str, parameter: str
258
- ) -> None:
266
+ def _dimmer_callback(self, zone: str, event: str, parameter: str) -> None:
259
267
  """Handle a dimmer change event."""
260
268
  if event == "DIM" and parameter[1:] in DIMMER_MODE_MAP_LABELS:
261
269
  self._dimmer = DIMMER_MODE_MAP_LABELS[parameter[1:]]
262
270
 
263
- async def _async_auto_standby_callback(
264
- self, zone: str, event: str, parameter: str
265
- ) -> None:
271
+ def _auto_standby_callback(self, zone: str, event: str, parameter: str) -> None:
266
272
  """Handle a auto standby change event."""
267
273
  if zone == "Main" and event == "STBY":
268
274
  self._auto_standby = parameter
269
275
 
270
- async def _async_auto_sleep_callback(
271
- self, zone: str, event: str, parameter: str
272
- ) -> None:
276
+ def _auto_sleep_callback(self, zone: str, event: str, parameter: str) -> None:
273
277
  """Handle a sleep change event."""
274
278
  if event != "SLP":
275
279
  return
@@ -279,18 +283,14 @@ class DenonAVRDeviceInfo:
279
283
  else:
280
284
  self._sleep = int(parameter)
281
285
 
282
- async def _async_room_size_callback(
283
- self, zone: str, event: str, parameter: str
284
- ) -> None:
286
+ def _room_size_callback(self, zone: str, event: str, parameter: str) -> None:
285
287
  """Handle a room size change event."""
286
288
  if parameter[:3] != "RSZ":
287
289
  return
288
290
 
289
291
  self._room_size = parameter[4:]
290
292
 
291
- async def _async_trigger_callback(
292
- self, zone: str, event: str, parameter: str
293
- ) -> None:
293
+ def _trigger_callback(self, zone: str, event: str, parameter: str) -> None:
294
294
  """Handle a trigger change event."""
295
295
  if event != "TR":
296
296
  return
@@ -304,35 +304,29 @@ class DenonAVRDeviceInfo:
304
304
 
305
305
  self._triggers[int(values[0])] = values[1]
306
306
 
307
- async def _async_delay_callback(
308
- self, zone: str, event: str, parameter: str
309
- ) -> None:
307
+ def _delay_callback(self, zone: str, event: str, parameter: str) -> None:
310
308
  """Handle a delay change event."""
311
- if event == "PS" and parameter[0:3] == "DEL":
312
- self._delay = int(parameter[4:])
309
+ if event == "PS" and parameter[0:5] == "DELAY":
310
+ self._delay = int(parameter[6:])
313
311
 
314
- async def _async_eco_mode_callback(
315
- self, zone: str, event: str, parameter: str
316
- ) -> None:
312
+ def _eco_mode_callback(self, zone: str, event: str, parameter: str) -> None:
317
313
  """Handle an Eco-mode change event."""
318
314
  if event == "ECO" and parameter in ECO_MODE_MAP_LABELS:
319
315
  self._eco_mode = ECO_MODE_MAP_LABELS[parameter]
320
316
 
321
- async def _async_hdmi_output_callback(
322
- self, zone: str, event: str, parameter: str
323
- ) -> None:
317
+ def _hdmi_output_callback(self, zone: str, event: str, parameter: str) -> None:
324
318
  """Handle a HDMI output change event."""
325
319
  if event == "VS" and parameter[0:4] == "MONI":
326
320
  self._hdmi_output = HDMI_OUTPUT_MAP_LABELS[parameter]
327
321
 
328
- async def _async_hdmi_audio_decode_callback(
322
+ def _hdmi_audio_decode_callback(
329
323
  self, zone: str, event: str, parameter: str
330
324
  ) -> None:
331
325
  """Handle a HDMI Audio Decode mode change event."""
332
326
  if event == "VS" and parameter[0:5] == "AUDIO":
333
327
  self._hdmi_audio_decode = parameter[6:]
334
328
 
335
- async def _async_video_processing_mode_callback(
329
+ def _video_processing_mode_callback(
336
330
  self, zone: str, event: str, parameter: str
337
331
  ) -> None:
338
332
  """Handle a Video Processing Mode change event."""
@@ -341,7 +335,7 @@ class DenonAVRDeviceInfo:
341
335
  parameter[3:]
342
336
  ]
343
337
 
344
- async def _async_tactile_transducer_callback(
338
+ def _tactile_transducer_callback(
345
339
  self, zone: str, event: str, parameter: str
346
340
  ) -> None:
347
341
  """Handle a tactile transducer change event."""
@@ -361,9 +355,7 @@ class DenonAVRDeviceInfo:
361
355
  elif key == "TTRLPF":
362
356
  self._tactile_transducer_lpf = f"{int(value)} Hz"
363
357
 
364
- async def _async_speaker_preset_callback(
365
- self, zone: str, event: str, parameter: str
366
- ) -> None:
358
+ def _speaker_preset_callback(self, zone: str, event: str, parameter: str) -> None:
367
359
  """Handle a speaker preset change event."""
368
360
  if event != "SP":
369
361
  return
@@ -371,7 +363,7 @@ class DenonAVRDeviceInfo:
371
363
  if parameter[0:2] == "PR":
372
364
  self._speaker_preset = int(parameter[3:])
373
365
 
374
- async def _async_bt_callback(self, zone: str, event: str, parameter: str) -> None:
366
+ def _bt_callback(self, zone: str, event: str, parameter: str) -> None:
375
367
  """Handle a Bluetooth change event."""
376
368
  if event != "BT" or parameter[0:2] != "TX":
377
369
  return
@@ -381,42 +373,56 @@ class DenonAVRDeviceInfo:
381
373
  else:
382
374
  self._bt_output_mode = BLUETOOTH_OUTPUT_MAP_LABELS[parameter[3:]]
383
375
 
384
- async def _async_delay_time_callback(
385
- self, zone: str, event: str, parameter: str
386
- ) -> None:
376
+ def _delay_time_callback(self, zone: str, event: str, parameter: str) -> None:
387
377
  """Handle a delay time change event."""
388
- if event != "PS" or parameter[0:3] != "DEL":
378
+ # do not match "DELAY" as it's another event
379
+ if event != "PS" or parameter[0:3] != "DEL" or parameter[0:5] == "DELAY":
389
380
  return
390
381
 
391
382
  self._delay_time = int(parameter[4:])
392
383
 
393
- async def _async_audio_restorer_callback(
394
- self, zone: str, event: str, parameter: str
395
- ) -> None:
384
+ def _audio_restorer_callback(self, zone: str, event: str, parameter: str) -> None:
396
385
  """Handle an audio restorer change event."""
397
386
  if event != "PS" or parameter[0:4] != "RSTR":
398
387
  return
399
388
 
400
389
  self._audio_restorer = AUDIO_RESTORER_MAP_LABELS[parameter[5:]]
401
390
 
402
- async def _async_graphic_eq_callback(
403
- self, zone: str, event: str, parameter: str
404
- ) -> None:
391
+ def _graphic_eq_callback(self, zone: str, event: str, parameter: str) -> None:
405
392
  """Handle a Graphic EQ change event."""
406
393
  if parameter[0:3] != "GEQ":
407
394
  return
408
395
 
409
396
  self._graphic_eq = parameter[4:]
410
397
 
411
- async def _async_headphone_eq_callback(
412
- self, zone: str, event: str, parameter: str
413
- ) -> None:
398
+ def _headphone_eq_callback(self, zone: str, event: str, parameter: str) -> None:
414
399
  """Handle a Headphone EQ change event."""
415
400
  if parameter[0:3] != "HEQ":
416
401
  return
417
402
 
418
403
  self._headphone_eq = parameter[4:]
419
404
 
405
+ def _illumination_callback(self, zone: str, event: str, parameter: str) -> None:
406
+ """Handle an illumination change event."""
407
+ if event != "ILB" or parameter[0:3] != "ILL":
408
+ return
409
+
410
+ self._illumination = ILLUMINATION_MAP_LABELS[parameter[4:]]
411
+
412
+ def _auto_lip_sync_callback(self, zone: str, event: str, parameter: str) -> None:
413
+ """Handle a auto lip sync change event."""
414
+ if event != "PS" or parameter[0:3] != "HOS":
415
+ return
416
+
417
+ if parameter[6:] == "HOSALS":
418
+ auto_lip_sync = parameter[5:]
419
+ elif parameter[3:] == "HOS":
420
+ auto_lip_sync = parameter[4:]
421
+ else:
422
+ return
423
+
424
+ self._auto_lip_sync = auto_lip_sync
425
+
420
426
  def get_own_zone(self) -> str:
421
427
  """
422
428
  Get zone from actual instance.
@@ -454,32 +460,32 @@ class DenonAVRDeviceInfo:
454
460
  power_event = "Z2"
455
461
  elif self.zone == ZONE3:
456
462
  power_event = "Z3"
457
- self.telnet_api.register_callback(power_event, self._async_power_callback)
458
-
459
- self.telnet_api.register_callback("MN", self._async_settings_menu_callback)
460
- self.telnet_api.register_callback("DIM", self._async_dimmer_callback)
461
- self.telnet_api.register_callback("PS", self._async_delay_callback)
462
- self.telnet_api.register_callback("ECO", self._async_eco_mode_callback)
463
- self.telnet_api.register_callback("VS", self._async_hdmi_output_callback)
464
- self.telnet_api.register_callback(
465
- "VS", self._async_hdmi_audio_decode_callback
466
- )
467
- self.telnet_api.register_callback(
468
- "VS", self._async_video_processing_mode_callback
469
- )
463
+ self.telnet_api.register_callback(power_event, self._power_callback)
464
+
465
+ self.telnet_api.register_callback("MN", self._settings_menu_callback)
466
+ self.telnet_api.register_callback("DIM", self._dimmer_callback)
467
+ self.telnet_api.register_callback("PS", self._delay_callback)
468
+ self.telnet_api.register_callback("ECO", self._eco_mode_callback)
469
+ self.telnet_api.register_callback("VS", self._hdmi_output_callback)
470
+ self.telnet_api.register_callback("VS", self._hdmi_audio_decode_callback)
470
471
  self.telnet_api.register_callback(
471
- "SS", self._async_tactile_transducer_callback
472
- )
473
- self.telnet_api.register_callback("STBY", self._async_auto_standby_callback)
474
- self.telnet_api.register_callback("SLP", self._async_auto_sleep_callback)
475
- self.telnet_api.register_callback("PS", self._async_room_size_callback)
476
- self.telnet_api.register_callback("TR", self._async_trigger_callback)
477
- self.telnet_api.register_callback("SP", self._async_speaker_preset_callback)
478
- self.telnet_api.register_callback("BT", self._async_bt_callback)
479
- self.telnet_api.register_callback("PS", self._async_delay_time_callback)
480
- self.telnet_api.register_callback("PS", self._async_audio_restorer_callback)
481
- self.telnet_api.register_callback("PS", self._async_graphic_eq_callback)
482
- self.telnet_api.register_callback("PS", self._async_headphone_eq_callback)
472
+ "VS", self._video_processing_mode_callback
473
+ )
474
+ self.telnet_api.register_callback("SS", self._tactile_transducer_callback)
475
+ self.telnet_api.register_callback("STBY", self._auto_standby_callback)
476
+ self.telnet_api.register_callback("SLP", self._auto_sleep_callback)
477
+ self.telnet_api.register_callback("PS", self._room_size_callback)
478
+ self.telnet_api.register_callback("TR", self._trigger_callback)
479
+ self.telnet_api.register_callback("SP", self._speaker_preset_callback)
480
+ self.telnet_api.register_callback("BT", self._bt_callback)
481
+ self.telnet_api.register_callback("PS", self._delay_time_callback)
482
+ self.telnet_api.register_callback("PS", self._audio_restorer_callback)
483
+ self.telnet_api.register_callback("PS", self._graphic_eq_callback)
484
+ self.telnet_api.register_callback("PS", self._headphone_eq_callback)
485
+
486
+ if not self.is_denon:
487
+ self.telnet_api.register_callback("ILB", self._illumination_callback)
488
+ self.telnet_api.register_callback("SS", self._auto_lip_sync_callback)
483
489
 
484
490
  self._is_setup = True
485
491
  _LOGGER.debug("Finished device setup")
@@ -724,6 +730,7 @@ class DenonAVRDeviceInfo:
724
730
 
725
731
  if device_info is None:
726
732
  self.manufacturer = "Denon"
733
+ self.telnet_api.is_denon = self.is_denon
727
734
  self.model_name = "Unknown"
728
735
  self.serial_number = None
729
736
  _LOGGER.warning(
@@ -739,6 +746,7 @@ class DenonAVRDeviceInfo:
739
746
  if self.friendly_name is None and "friendlyName" in device_info:
740
747
  self.friendly_name = device_info["friendlyName"]
741
748
  self.manufacturer = device_info["manufacturer"]
749
+ self.telnet_api.is_denon = self.is_denon
742
750
  self.model_name = device_info["modelName"]
743
751
  self.serial_number = device_info["serialNumber"]
744
752
 
@@ -1046,11 +1054,36 @@ class DenonAVRDeviceInfo:
1046
1054
  """
1047
1055
  return self._headphone_eq
1048
1056
 
1057
+ @property
1058
+ def illumination(self) -> Optional[str]:
1059
+ """
1060
+ Return the illumination status for the device.
1061
+
1062
+ Only available on Marantz devices and when using Telnet.
1063
+
1064
+ Possible values are: "Auto", "Bright", "Dim", "Dark", "Off"
1065
+ """
1066
+ return self._illumination
1067
+
1068
+ @property
1069
+ def auto_lip_sync(self) -> Optional[bool]:
1070
+ """
1071
+ Return the auto lip sync status for the device.
1072
+
1073
+ Only available on Marantz devices and when using Telnet.
1074
+ """
1075
+ return self._auto_lip_sync
1076
+
1049
1077
  @property
1050
1078
  def telnet_available(self) -> bool:
1051
1079
  """Return true if telnet is connected and healthy."""
1052
1080
  return self.telnet_api.connected and self.telnet_api.healthy
1053
1081
 
1082
+ @property
1083
+ def is_denon(self) -> bool:
1084
+ """Return true if the receiver is a Denon device."""
1085
+ return "denon" in self.manufacturer.lower()
1086
+
1054
1087
  ##########
1055
1088
  # Getter #
1056
1089
  ##########
@@ -1199,7 +1232,7 @@ class DenonAVRDeviceInfo:
1199
1232
 
1200
1233
  async def async_dimmer(self, mode: DimmerModes) -> None:
1201
1234
  """Set dimmer mode on receiver via HTTP get command."""
1202
- if mode not in DimmerModes:
1235
+ if mode not in self._dimmer_modes:
1203
1236
  raise AvrCommandError("Invalid dimmer mode")
1204
1237
 
1205
1238
  mapped_mode = DIMMER_MODE_MAP[mode]
@@ -1397,15 +1430,19 @@ class DenonAVRDeviceInfo:
1397
1430
  raise AvrCommandError("Quick select number must be between 1 and 5")
1398
1431
 
1399
1432
  if self.telnet_available:
1433
+ if self.is_denon:
1434
+ command = self.telnet_commands.command_quick_select_mode
1435
+ else:
1436
+ command = self.telnet_commands.command_smart_select_mode
1400
1437
  await self.telnet_api.async_send_commands(
1401
- self.telnet_commands.command_quick_select_mode.format(
1402
- number=quick_select_number
1403
- )
1438
+ command.format(number=quick_select_number)
1404
1439
  )
1405
1440
  else:
1406
- await self.api.async_get_command(
1407
- self.urls.command_quick_select_mode.format(number=quick_select_number)
1408
- )
1441
+ if self.is_denon:
1442
+ command = self.urls.command_quick_select_mode
1443
+ else:
1444
+ command = self.urls.command_smart_select_mode
1445
+ await self.api.async_get_command(command.format(number=quick_select_number))
1409
1446
 
1410
1447
  async def async_quick_select_memory(self, quick_select_number: int) -> None:
1411
1448
  """
@@ -1417,15 +1454,19 @@ class DenonAVRDeviceInfo:
1417
1454
  raise AvrCommandError("Quick select number must be between 1 and 5")
1418
1455
 
1419
1456
  if self.telnet_available:
1457
+ if self.is_denon:
1458
+ command = self.telnet_commands.command_quick_select_memory
1459
+ else:
1460
+ command = self.telnet_commands.command_smart_select_memory
1420
1461
  await self.telnet_api.async_send_commands(
1421
- self.telnet_commands.command_quick_select_memory.format(
1422
- number=quick_select_number
1423
- )
1462
+ command.format(number=quick_select_number)
1424
1463
  )
1425
1464
  else:
1426
- await self.api.async_get_command(
1427
- self.urls.command_quick_select_memory.format(number=quick_select_number)
1428
- )
1465
+ if self.is_denon:
1466
+ command = self.urls.command_quick_select_memory
1467
+ else:
1468
+ command = self.urls.command_smart_select_memory
1469
+ await self.api.async_get_command(command.format(number=quick_select_number))
1429
1470
 
1430
1471
  async def async_delay_up(self) -> None:
1431
1472
  """Delay up on receiver via HTTP get command."""
@@ -1447,7 +1488,7 @@ class DenonAVRDeviceInfo:
1447
1488
 
1448
1489
  async def async_eco_mode(self, mode: EcoModes) -> None:
1449
1490
  """Set Eco mode."""
1450
- if mode not in EcoModes:
1491
+ if mode not in self._eco_modes:
1451
1492
  raise AvrCommandError("Invalid Eco mode")
1452
1493
 
1453
1494
  mapped_mode = ECO_MODE_MAP[mode]
@@ -1462,10 +1503,10 @@ class DenonAVRDeviceInfo:
1462
1503
 
1463
1504
  async def async_hdmi_output(self, output: HDMIOutputs) -> None:
1464
1505
  """Set HDMI output."""
1465
- if output not in HDMIOutputs:
1506
+ if output not in self._hdmi_outputs:
1466
1507
  raise AvrCommandError("Invalid HDMI output mode")
1467
1508
 
1468
- mapped_output = HDMIOutputs[output]
1509
+ mapped_output = HDMI_OUTPUT_MAP[output]
1469
1510
  if self.telnet_available:
1470
1511
  await self.telnet_api.async_send_commands(
1471
1512
  self.telnet_commands.command_hdmi_output.format(output=mapped_output)
@@ -1505,11 +1546,17 @@ class DenonAVRDeviceInfo:
1505
1546
  self.urls.command_video_processing_mode.format(mode=processing_mode)
1506
1547
  )
1507
1548
 
1508
- async def async_status(self) -> str:
1549
+ async def async_status(self) -> None:
1509
1550
  """Get status of receiver via HTTP get command."""
1510
- if "denon" not in self.manufacturer.lower():
1551
+ if not self.is_denon:
1511
1552
  raise AvrCommandError("Status command is only supported for Denon devices")
1512
- return await self.api.async_get_command(self.urls.command_status)
1553
+
1554
+ if self.telnet_available:
1555
+ await self.telnet_api.async_send_commands(
1556
+ self.telnet_commands.command_status
1557
+ )
1558
+ else:
1559
+ await self.api.async_get_command(self.urls.command_status)
1513
1560
 
1514
1561
  async def async_system_reset(self) -> None:
1515
1562
  """DANGER! Reset the receiver via HTTP get command."""
@@ -1799,6 +1846,108 @@ class DenonAVRDeviceInfo:
1799
1846
  else:
1800
1847
  await self.async_headphone_eq_on()
1801
1848
 
1849
+ async def async_hdmi_cec_on(self) -> None:
1850
+ """Turn on HDMI CEC on receiver via HTTP get command."""
1851
+ if self.telnet_available:
1852
+ await self.telnet_api.async_send_commands(
1853
+ self.telnet_commands.command_denon_hdmi_cec_on
1854
+ if self.is_denon
1855
+ else self.urls.command_marantz_hdmi_cec_on
1856
+ )
1857
+ else:
1858
+ await self.api.async_get_command(
1859
+ self.urls.command_denon_hdmi_cec_on
1860
+ if self.is_denon
1861
+ else self.urls.command_marantz_hdmi_cec_on
1862
+ )
1863
+
1864
+ async def async_hdmi_cec_off(self) -> None:
1865
+ """Turn off HDMI CEC on receiver via HTTP get command."""
1866
+ if self.telnet_available:
1867
+ await self.telnet_api.async_send_commands(
1868
+ self.telnet_commands.command_denon_hdmi_cec_off
1869
+ if self.is_denon
1870
+ else self.urls.command_marantz_hdmi_cec_off
1871
+ )
1872
+ else:
1873
+ await self.api.async_get_command(
1874
+ self.urls.command_denon_hdmi_cec_off
1875
+ if self.is_denon
1876
+ else self.urls.command_marantz_hdmi_cec_off
1877
+ )
1878
+
1879
+ async def async_illumination(self, mode: Illuminations):
1880
+ """
1881
+ Set illumination mode on receiver via HTTP get command.
1882
+
1883
+ Only available on Marantz devices.
1884
+ """
1885
+ if self.is_denon:
1886
+ raise AvrCommandError("Illumination is only available for Marantz devices")
1887
+
1888
+ if mode not in self._illuminations:
1889
+ raise AvrCommandError("Invalid illumination mode")
1890
+
1891
+ mapped_mode = ILLUMINATION_MAP[mode]
1892
+ if self.telnet_available:
1893
+ await self.telnet_api.async_send_commands(
1894
+ self.telnet_commands.command_illumination.format(mode=mapped_mode)
1895
+ )
1896
+ else:
1897
+ await self.api.async_get_command(
1898
+ self.urls.command_illumination.format(mode=mapped_mode)
1899
+ )
1900
+
1901
+ async def async_auto_lip_sync_on(self) -> None:
1902
+ """
1903
+ Turn on auto lip sync on receiver via HTTP get command.
1904
+
1905
+ Only available on Marantz devices.
1906
+ """
1907
+ if self.is_denon:
1908
+ raise AvrCommandError("Auto lip sync is only available for Marantz devices")
1909
+
1910
+ if self.telnet_available:
1911
+ await self.telnet_api.async_send_commands(
1912
+ self.telnet_commands.command_auto_lip_sync.format(mode="ON")
1913
+ )
1914
+ else:
1915
+ await self.api.async_get_command(
1916
+ self.urls.command_auto_lip_sync.format(mode="ON")
1917
+ )
1918
+
1919
+ async def async_auto_lip_sync_off(self) -> None:
1920
+ """
1921
+ Turn off auto lip sync on receiver via HTTP get command.
1922
+
1923
+ Only available on Marantz devices.
1924
+ """
1925
+ if self.is_denon:
1926
+ raise AvrCommandError("Auto lip sync is only available for Marantz devices")
1927
+
1928
+ if self.telnet_available:
1929
+ await self.telnet_api.async_send_commands(
1930
+ self.telnet_commands.command_auto_lip_sync.format(mode="OFF")
1931
+ )
1932
+ else:
1933
+ await self.api.async_get_command(
1934
+ self.urls.command_auto_lip_sync.format(mode="OFF")
1935
+ )
1936
+
1937
+ async def async_auto_lip_sync_toggle(self) -> None:
1938
+ """
1939
+ Toggle auto lip sync on receiver via HTTP get command.
1940
+
1941
+ Only available on Marantz devices and when using Telnet.
1942
+ """
1943
+ if self.is_denon:
1944
+ raise AvrCommandError("Auto lip sync is only available for Marantz devices")
1945
+
1946
+ if self._auto_lip_sync:
1947
+ await self.async_auto_lip_sync_off()
1948
+ else:
1949
+ await self.async_auto_lip_sync_on()
1950
+
1802
1951
 
1803
1952
  @attr.s(auto_attribs=True, on_setattr=DENON_ATTR_SETATTR)
1804
1953
  class DenonAVRFoundation: