denonavr 1.1.2__py3-none-any.whl → 1.3.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/soundmode.py CHANGED
@@ -19,22 +19,28 @@ from .appcommand import AppCommands
19
19
  from .const import (
20
20
  ALL_ZONE_STEREO,
21
21
  AURO_3D_MODE_MAP,
22
- AURO_3D_MODE_MAP_MAP_LABELS,
22
+ AURO_3D_MODE_MAP_REVERSE,
23
23
  AURO_MATIC_3D_PRESET_MAP,
24
- AURO_MATIC_3D_PRESET_MAP_LABELS,
24
+ AURO_MATIC_3D_PRESET_MAP_REVERSE,
25
+ DAC_FILTERS_MAP,
26
+ DAC_FILTERS_MAP_REVERSE,
25
27
  DENON_ATTR_SETATTR,
26
28
  DIALOG_ENHANCER_LEVEL_MAP,
27
- DIALOG_ENHANCER_LEVEL_MAP_LABELS,
29
+ DIALOG_ENHANCER_LEVEL_MAP_REVERSE,
28
30
  EFFECT_SPEAKER_SELECTION_MAP,
29
- EFFECT_SPEAKER_SELECTION_MAP_LABELS,
31
+ EFFECT_SPEAKER_SELECTION_MAP_REVERSE,
32
+ MDAX_MAP,
33
+ MDAX_MAP_REVERSE,
30
34
  SOUND_MODE_MAPPING,
31
35
  Auro3DModes,
32
36
  AuroMatic3DPresets,
37
+ DACFilters,
33
38
  DialogEnhancerLevels,
34
39
  DRCs,
35
40
  EffectSpeakers,
36
41
  IMAXHPFs,
37
42
  IMAXLPFs,
43
+ MDAXs,
38
44
  )
39
45
  from .exceptions import AvrCommandError, AvrIncompleteResponseError, AvrProcessingError
40
46
  from .foundation import DenonAVRFoundation, convert_on_off_bool
@@ -119,18 +125,18 @@ class DenonAVRSoundMode(DenonAVRFoundation):
119
125
  converter=attr.converters.optional(convert_on_off_bool), default=None
120
126
  )
121
127
  _dialog_enhancer_level: Optional[str] = attr.ib(
122
- converter=attr.converters.optional(str), default=None
128
+ converter=attr.converters.optional(DIALOG_ENHANCER_LEVEL_MAP.get), default=None
123
129
  )
124
130
  _dialog_enhancer_levels = get_args(DialogEnhancerLevels)
125
131
  _auromatic_3d_preset: Optional[str] = attr.ib(
126
- converter=attr.converters.optional(str), default=None
132
+ converter=attr.converters.optional(AURO_MATIC_3D_PRESET_MAP.get), default=None
127
133
  )
128
134
  _auromatic_3d_presets = get_args(AuroMatic3DPresets)
129
135
  _auromatic_3d_strength: Optional[int] = attr.ib(
130
136
  converter=attr.converters.optional(int), default=None
131
137
  )
132
138
  _auro_3d_mode: Optional[str] = attr.ib(
133
- converter=attr.converters.optional(str), default=None
139
+ converter=attr.converters.optional(AURO_3D_MODE_MAP.get), default=None
134
140
  )
135
141
  _auro_3d_modes = get_args(Auro3DModes)
136
142
  _dialog_control: Optional[int] = attr.ib(
@@ -140,11 +146,20 @@ class DenonAVRSoundMode(DenonAVRFoundation):
140
146
  converter=attr.converters.optional(convert_on_off_bool), default=None
141
147
  )
142
148
  _effect_speaker_selection: Optional[str] = attr.ib(
143
- converter=attr.converters.optional(str), default=None
149
+ converter=attr.converters.optional(EFFECT_SPEAKER_SELECTION_MAP.get),
150
+ default=None,
144
151
  )
145
152
  _effect_speakers = get_args(EffectSpeakers)
146
153
  _drc: Optional[str] = attr.ib(converter=attr.converters.optional(str), default=None)
147
154
  _drcs = get_args(DRCs)
155
+ _mdax: Optional[str] = attr.ib(
156
+ converter=attr.converters.optional(MDAX_MAP.get), default=None
157
+ )
158
+ _mdaxs = get_args(MDAXs)
159
+ _dac_filter: Optional[str] = attr.ib(
160
+ converter=attr.converters.optional(DAC_FILTERS_MAP.get), default=None
161
+ )
162
+ _dac_filters = get_args(DACFilters)
148
163
  _sound_mode_map: Dict[str, list] = attr.ib(
149
164
  validator=attr.validators.deep_mapping(
150
165
  attr.validators.instance_of(str),
@@ -188,43 +203,41 @@ class DenonAVRSoundMode(DenonAVRFoundation):
188
203
  for tag in self.appcommand_attrs:
189
204
  self._device.api.add_appcommand_update_tag(tag)
190
205
 
206
+ self._device.telnet_api.register_callback("MS", self._soundmode_callback)
207
+ self._device.telnet_api.register_callback("PS", self._neural_x_callback)
208
+ self._device.telnet_api.register_callback("PS", self._imax_callback)
209
+ self._device.telnet_api.register_callback("PS", self._cinema_eq_callback)
191
210
  self._device.telnet_api.register_callback(
192
- "MS", self._async_soundmode_callback
193
- )
194
- self._device.telnet_api.register_callback(
195
- "PS", self._async_neural_x_callback
196
- )
197
- self._device.telnet_api.register_callback("PS", self._async_imax_callback)
198
- self._device.telnet_api.register_callback(
199
- "PS", self._async_cinema_eq_callback
211
+ "PS", self._center_spread_callback
200
212
  )
201
213
  self._device.telnet_api.register_callback(
202
- "PS", self._async_center_spread_callback
214
+ "PS", self._loudness_management_callback
203
215
  )
204
216
  self._device.telnet_api.register_callback(
205
- "PS", self._async_loudness_management_callback
217
+ "PS", self._dialog_enhancer_callback
206
218
  )
219
+ self._device.telnet_api.register_callback("PS", self._auro_callback)
207
220
  self._device.telnet_api.register_callback(
208
- "PS", self._async_dialog_enhancer_callback
221
+ "PS", self._dialog_control_callback
209
222
  )
210
- self._device.telnet_api.register_callback("PS", self._async_auro_callback)
211
223
  self._device.telnet_api.register_callback(
212
- "PS", self._async_dialog_control_callback
224
+ "PS", self._speaker_virtualizer_callback
213
225
  )
214
226
  self._device.telnet_api.register_callback(
215
- "PS", self._async_speaker_virtualizer_callback
227
+ "PS", self._effect_speaker_selection_callback
216
228
  )
217
- self._device.telnet_api.register_callback(
218
- "PS", self._async_effect_speaker_selection_callback
219
- )
220
- self._device.telnet_api.register_callback("PS", self._async_drc_callback)
229
+ self._device.telnet_api.register_callback("PS", self._drc_callback)
230
+
231
+ if not self._device.is_denon:
232
+ self._device.telnet_api.register_callback("PS", self._mdax_callback)
233
+ self._device.telnet_api.register_callback(
234
+ "PS", self._dac_filter_callback
235
+ )
221
236
 
222
237
  self._is_setup = True
223
238
  _LOGGER.debug("Finished sound mode setup")
224
239
 
225
- async def _async_soundmode_callback(
226
- self, zone: str, event: str, parameter: str
227
- ) -> None:
240
+ def _soundmode_callback(self, zone: str, event: str, parameter: str) -> None:
228
241
  """Handle a sound mode change event."""
229
242
  if self._device.zone != zone:
230
243
  return
@@ -235,15 +248,13 @@ class DenonAVRSoundMode(DenonAVRFoundation):
235
248
 
236
249
  self._sound_mode_raw = parameter
237
250
 
238
- async def _async_neural_x_callback(
239
- self, zone: str, event: str, parameter: str
240
- ) -> None:
251
+ def _neural_x_callback(self, zone: str, event: str, parameter: str) -> None:
241
252
  """Handle a Neural X:change event."""
242
253
  parameter_name_length = len("NEURAL")
243
254
  if parameter[:parameter_name_length] == "NEURAL":
244
255
  self._neural_x = parameter[parameter_name_length + 1 :]
245
256
 
246
- async def _async_imax_callback(self, zone: str, event: str, parameter: str) -> None:
257
+ def _imax_callback(self, zone: str, event: str, parameter: str) -> None:
247
258
  """Handle an IMAX change event."""
248
259
  key_value = parameter.split()
249
260
  if len(key_value) != 2 or key_value[0][:4] != "IMAX":
@@ -254,68 +265,58 @@ class DenonAVRSoundMode(DenonAVRFoundation):
254
265
  elif key_value[0] == "IMAXAUD":
255
266
  self._imax_audio_settings = parameter[8:]
256
267
  elif key_value[0] == "IMAXHPF":
257
- self._imax_hpf = int(parameter[8:])
268
+ self._imax_hpf = parameter[8:]
258
269
  elif key_value[0] == "IMAXLPF":
259
- self._imax_lpf = int(parameter[8:])
270
+ self._imax_lpf = parameter[8:]
260
271
  elif key_value[0] == "IMAXSWM":
261
272
  self._imax_subwoofer_mode = parameter[8:]
262
273
  elif key_value[0] == "IMAXSWO":
263
274
  self._imax_subwoofer_output = parameter[8:]
264
275
 
265
- async def _async_cinema_eq_callback(
266
- self, zone: str, event: str, parameter: str
267
- ) -> None:
276
+ def _cinema_eq_callback(self, zone: str, event: str, parameter: str) -> None:
268
277
  """Handle a Cinema EQ change event."""
269
278
  if parameter[:10] == "CINEMA EQ.":
270
279
  self._cinema_eq = parameter[10:]
271
280
 
272
- async def _async_center_spread_callback(
273
- self, zone: str, event: str, parameter: str
274
- ) -> None:
281
+ def _center_spread_callback(self, zone: str, event: str, parameter: str) -> None:
275
282
  """Handle a Center Spread change event."""
276
283
  if parameter[:3] == "CES":
277
284
  self._center_spread = parameter[4:]
278
285
 
279
- async def _async_loudness_management_callback(
286
+ def _loudness_management_callback(
280
287
  self, zone: str, event: str, parameter: str
281
288
  ) -> None:
282
289
  """Handle a Loudness Management change event."""
283
290
  if parameter[:3] == "LOM":
284
291
  self._loudness_management = parameter[4:]
285
292
 
286
- async def _async_dialog_enhancer_callback(
287
- self, zone: str, event: str, parameter: str
288
- ) -> None:
293
+ def _dialog_enhancer_callback(self, zone: str, event: str, parameter: str) -> None:
289
294
  """Handle a Dialog Enhancer change event."""
290
295
  if parameter[:3] == "DEH":
291
- self._dialog_enhancer_level = DIALOG_ENHANCER_LEVEL_MAP_LABELS[
292
- parameter[4:]
293
- ]
296
+ self._dialog_enhancer_level = parameter[4:]
294
297
 
295
- async def _async_auro_callback(self, zone: str, event: str, parameter: str) -> None:
298
+ def _auro_callback(self, zone: str, event: str, parameter: str) -> None:
296
299
  """Handle a Auro change event."""
297
300
  key_value = parameter.split()
298
301
  if len(key_value) != 2 or key_value[0][:4] != "AURO":
299
302
  return
300
303
 
301
304
  if key_value[0] == "AUROPR":
302
- self._auromatic_3d_preset = AURO_MATIC_3D_PRESET_MAP_LABELS[parameter[7:]]
305
+ self._auromatic_3d_preset = parameter[7:]
303
306
  elif key_value[0] == "AUROST":
304
- self._auromatic_3d_strength = int(parameter[7:])
307
+ self._auromatic_3d_strength = parameter[7:]
305
308
  elif key_value[0] == "AUROMODE":
306
- self._auro_3d_mode = AURO_3D_MODE_MAP_MAP_LABELS[parameter[9:]]
309
+ self._auro_3d_mode = parameter[9:]
307
310
 
308
- async def _async_dialog_control_callback(
309
- self, zone: str, event: str, parameter: str
310
- ) -> None:
311
+ def _dialog_control_callback(self, zone: str, event: str, parameter: str) -> None:
311
312
  """Handle a Dialog Control change event."""
312
313
  key_value = parameter.split()
313
314
  if len(key_value) != 2 or key_value[0] != "DIC":
314
315
  return
315
316
 
316
- self._dialog_control = int(key_value[1])
317
+ self._dialog_control = key_value[1]
317
318
 
318
- async def _async_speaker_virtualizer_callback(
319
+ def _speaker_virtualizer_callback(
319
320
  self, zone: str, event: str, parameter: str
320
321
  ) -> None:
321
322
  """Handle a Speaker Virtualizer change event."""
@@ -325,7 +326,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
325
326
 
326
327
  self._speaker_virtualizer = key_value[1]
327
328
 
328
- async def _async_effect_speaker_selection_callback(
329
+ def _effect_speaker_selection_callback(
329
330
  self, zone: str, event: str, parameter: str
330
331
  ) -> None:
331
332
  """Handle a Effect Speaker Selection change event."""
@@ -333,11 +334,9 @@ class DenonAVRSoundMode(DenonAVRFoundation):
333
334
  if len(key_value) != 2 or key_value[0] != "SP":
334
335
  return
335
336
 
336
- self._effect_speaker_selection = EFFECT_SPEAKER_SELECTION_MAP_LABELS[
337
- key_value[1]
338
- ]
337
+ self._effect_speaker_selection = key_value[1]
339
338
 
340
- async def _async_drc_callback(self, zone: str, event: str, parameter: str) -> None:
339
+ def _drc_callback(self, zone: str, event: str, parameter: str) -> None:
341
340
  """Handle a DRC change event."""
342
341
  key_value = parameter.split()
343
342
  if len(key_value) != 2 or key_value[0] != "DRC":
@@ -345,6 +344,22 @@ class DenonAVRSoundMode(DenonAVRFoundation):
345
344
 
346
345
  self._drc = key_value[1]
347
346
 
347
+ def _mdax_callback(self, zone: str, event: str, parameter: str) -> None:
348
+ """Handle a M-DAX change event."""
349
+ key_value = parameter.split()
350
+ if len(key_value) != 2 or key_value[0] != "MDAX":
351
+ return
352
+
353
+ self._mdax = key_value[1]
354
+
355
+ def _dac_filter_callback(self, zone: str, event: str, parameter: str) -> None:
356
+ """Handle a DAC Filter change event."""
357
+ key_value = parameter.split()
358
+ if len(key_value) != 2 or key_value[0] != "DACFIL":
359
+ return
360
+
361
+ self._dac_filter = key_value[1]
362
+
348
363
  async def async_update(
349
364
  self, global_update: bool = False, cache_id: Optional[Hashable] = None
350
365
  ) -> None:
@@ -691,6 +706,28 @@ class DenonAVRSoundMode(DenonAVRFoundation):
691
706
  """
692
707
  return self._drc
693
708
 
709
+ @property
710
+ def mdax(self) -> Optional[str]:
711
+ """
712
+ Return the current M-DAX status.
713
+
714
+ Only available on Marantz devices and when using Telnet.
715
+
716
+ Possible values are: "Off", "Low", "Medium", "High"
717
+ """
718
+ return self._mdax
719
+
720
+ @property
721
+ def dac_filter(self) -> Optional[str]:
722
+ """
723
+ Return the current DAC Filter status.
724
+
725
+ Only available on Marantz devices and when using Telnet.
726
+
727
+ Possible values are: "Mode 1", "Mode 2"
728
+ """
729
+ return self._dac_filter
730
+
694
731
  ##########
695
732
  # Setter #
696
733
  ##########
@@ -1017,7 +1054,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
1017
1054
  if level not in self._dialog_enhancer_levels:
1018
1055
  raise AvrCommandError(f"{level} is not a valid dialog enhancer level")
1019
1056
 
1020
- level_mapped = DIALOG_ENHANCER_LEVEL_MAP[level]
1057
+ level_mapped = DIALOG_ENHANCER_LEVEL_MAP_REVERSE[level]
1021
1058
  if self._device.telnet_available:
1022
1059
  await self._device.telnet_api.async_send_commands(
1023
1060
  self._device.telnet_commands.command_dialog_enhancer.format(
@@ -1034,7 +1071,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
1034
1071
  if preset not in self._auromatic_3d_presets:
1035
1072
  raise AvrCommandError(f"{preset} is not a valid Auro-Matic 3D Preset")
1036
1073
 
1037
- local_preset = AURO_MATIC_3D_PRESET_MAP[preset]
1074
+ local_preset = AURO_MATIC_3D_PRESET_MAP_REVERSE[preset]
1038
1075
  if self._device.telnet_available:
1039
1076
  await self._device.telnet_api.async_send_commands(
1040
1077
  self._device.telnet_commands.command_auromatic_3d_preset.format(
@@ -1102,7 +1139,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
1102
1139
  if mode not in self._auro_3d_modes:
1103
1140
  raise AvrCommandError(f"{mode} is not a valid Auro 3D Mode")
1104
1141
 
1105
- local_mode = AURO_3D_MODE_MAP[mode]
1142
+ local_mode = AURO_3D_MODE_MAP_REVERSE[mode]
1106
1143
  if self._device.telnet_available:
1107
1144
  await self._device.telnet_api.async_send_commands(
1108
1145
  self._device.telnet_commands.command_auro_3d_mode.format(
@@ -1199,7 +1236,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
1199
1236
  if mode not in self._effect_speakers:
1200
1237
  raise AvrCommandError(f"{mode} is not a valid effect speaker selection")
1201
1238
 
1202
- local_mode = EFFECT_SPEAKER_SELECTION_MAP[mode]
1239
+ local_mode = EFFECT_SPEAKER_SELECTION_MAP_REVERSE[mode]
1203
1240
  if self._device.telnet_available:
1204
1241
  await self._device.telnet_api.async_send_commands(
1205
1242
  self._device.telnet_commands.command_effect_speaker_selection.format(
@@ -1227,6 +1264,67 @@ class DenonAVRSoundMode(DenonAVRFoundation):
1227
1264
  self._device.urls.command_drc.format(mode=mode)
1228
1265
  )
1229
1266
 
1267
+ async def async_mdax(self, mode: MDAXs) -> None:
1268
+ """
1269
+ Set M-DAX mode.
1270
+
1271
+ Only available for Marantz devices.
1272
+ """
1273
+ if self._device.is_denon:
1274
+ raise AvrCommandError("M-DAX is only available for Marantz devices")
1275
+
1276
+ if mode not in self._mdaxs:
1277
+ raise AvrCommandError(f"{mode} is not a valid M-DAX mode")
1278
+
1279
+ local_mode = MDAX_MAP_REVERSE[mode]
1280
+ if self._device.telnet_available:
1281
+ await self._device.telnet_api.async_send_commands(
1282
+ self._device.telnet_commands.command_mdax.format(mode=local_mode)
1283
+ )
1284
+ else:
1285
+ await self._device.api.async_get_command(
1286
+ self._device.urls.command_mdax.format(mode=local_mode)
1287
+ )
1288
+
1289
+ async def async_dac_filter(self, mode: DACFilters) -> None:
1290
+ """
1291
+ Set DAC Filter mode.
1292
+
1293
+ Only available for Marantz devices.
1294
+ """
1295
+ if self._device.is_denon:
1296
+ raise AvrCommandError("DAC Filter is only available for Marantz devices")
1297
+
1298
+ if mode not in self._dac_filters:
1299
+ raise AvrCommandError(f"{mode} is not a valid DAC Filter mode")
1300
+
1301
+ local_mode = DAC_FILTERS_MAP_REVERSE[mode]
1302
+ if self._device.telnet_available:
1303
+ await self._device.telnet_api.async_send_commands(
1304
+ self._device.telnet_commands.command_dac_filter.format(mode=local_mode)
1305
+ )
1306
+ else:
1307
+ await self._device.api.async_get_command(
1308
+ self._device.urls.command_dac_filter.format(mode=local_mode)
1309
+ )
1310
+
1311
+ async def async_dolby_atmos_toggle(self):
1312
+ """Toggle Dolby Atmos sound mode."""
1313
+ if self._device.telnet_available:
1314
+ command = (
1315
+ self._device.telnet_commands.command_dolby_atmos_toggle_denon
1316
+ if self._device.is_denon
1317
+ else self._device.telnet_commands.command_dolby_atmos_toggle_marantz
1318
+ )
1319
+ await self._device.telnet_api.async_send_commands(command)
1320
+ else:
1321
+ command = (
1322
+ self._device.urls.command_dolby_atmos_toggle_denon
1323
+ if self._device.is_denon
1324
+ else self._device.urls.command_dolby_atmos_toggle_marantz
1325
+ )
1326
+ await self._device.api.async_get_command(command)
1327
+
1230
1328
 
1231
1329
  def sound_mode_factory(instance: DenonAVRFoundation) -> DenonAVRSoundMode:
1232
1330
  """Create DenonAVRSoundMode at receiver instances."""
denonavr/ssdp.py CHANGED
@@ -91,17 +91,18 @@ async def async_identify_denonavr_receivers() -> List[Dict]:
91
91
  # Check which responding device is a DenonAVR device and prepare output
92
92
  receivers = []
93
93
 
94
- for url in urls:
95
- try:
96
- async with httpx.AsyncClient() as client:
97
- res = await client.get(url, timeout=5.0)
98
- res.raise_for_status()
99
- except httpx.HTTPError:
100
- continue
101
- else:
102
- receiver = evaluate_scpd_xml(url, res.text)
103
- if receiver is not None:
104
- receivers.append(receiver)
94
+ async with httpx.AsyncClient() as client:
95
+ for url in urls:
96
+ try:
97
+ async with client.stream("GET", url, timeout=5.0) as res:
98
+ res.raise_for_status()
99
+ await res.aread()
100
+ except httpx.HTTPError:
101
+ continue
102
+ else:
103
+ receiver = evaluate_scpd_xml(url, res.text)
104
+ if receiver is not None:
105
+ receivers.append(receiver)
105
106
 
106
107
  return receivers
107
108
 
denonavr/tonecontrol.py CHANGED
@@ -67,16 +67,12 @@ class DenonAVRToneControl(DenonAVRFoundation):
67
67
  for tag in self.appcommand_attrs:
68
68
  self._device.api.add_appcommand_update_tag(tag)
69
69
 
70
- self._device.telnet_api.register_callback(
71
- "PS", self._async_sound_detail_callback
72
- )
70
+ self._device.telnet_api.register_callback("PS", self._sound_detail_callback)
73
71
 
74
72
  self._is_setup = True
75
73
  _LOGGER.debug("Finished tone control setup")
76
74
 
77
- async def _async_sound_detail_callback(
78
- self, zone: str, event: str, parameter: str
79
- ) -> None:
75
+ def _sound_detail_callback(self, zone: str, event: str, parameter: str) -> None:
80
76
  """Handle a sound detail change event."""
81
77
  if self._device.zone != zone:
82
78
  return