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/__init__.py +1 -1
- denonavr/api.py +150 -115
- denonavr/appcommand.py +48 -12
- denonavr/audyssey.py +28 -29
- denonavr/const.py +520 -126
- denonavr/decorators.py +2 -3
- denonavr/denonavr.py +198 -100
- denonavr/dirac.py +7 -11
- denonavr/foundation.py +587 -363
- denonavr/input.py +110 -71
- denonavr/soundmode.py +164 -66
- denonavr/ssdp.py +12 -11
- denonavr/tonecontrol.py +2 -6
- denonavr/volume.py +47 -66
- {denonavr-1.1.2.dist-info → denonavr-1.3.0.dist-info}/METADATA +2 -1
- denonavr-1.3.0.dist-info/RECORD +20 -0
- {denonavr-1.1.2.dist-info → denonavr-1.3.0.dist-info}/WHEEL +1 -1
- denonavr-1.1.2.dist-info/RECORD +0 -20
- {denonavr-1.1.2.dist-info → denonavr-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {denonavr-1.1.2.dist-info → denonavr-1.3.0.dist-info}/top_level.txt +0 -0
denonavr/input.py
CHANGED
|
@@ -28,6 +28,7 @@ from .const import (
|
|
|
28
28
|
DENON_ATTR_SETATTR,
|
|
29
29
|
HDTUNER_SOURCES,
|
|
30
30
|
MAIN_ZONE,
|
|
31
|
+
NETAUDIO_PLAYING,
|
|
31
32
|
NETAUDIO_SOURCES,
|
|
32
33
|
PLAYING_SOURCES,
|
|
33
34
|
POWER_ON,
|
|
@@ -36,6 +37,7 @@ from .const import (
|
|
|
36
37
|
STATE_ON,
|
|
37
38
|
STATE_PAUSED,
|
|
38
39
|
STATE_PLAYING,
|
|
40
|
+
STATE_STOPPED,
|
|
39
41
|
STATIC_ALBUM_URL,
|
|
40
42
|
TELNET_MAPPING,
|
|
41
43
|
TUNER_SOURCES,
|
|
@@ -73,14 +75,16 @@ def set_input_func(
|
|
|
73
75
|
if value is None:
|
|
74
76
|
return value
|
|
75
77
|
# Map from input_func_map
|
|
76
|
-
#
|
|
77
|
-
if
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
# Some inputs are not always listed in available sources
|
|
79
|
+
if (
|
|
80
|
+
value in {"AirPlay", "Internet Radio", "Media Server", "NET"}
|
|
81
|
+
and value not in instance._input_func_map
|
|
82
|
+
):
|
|
83
|
+
instance._additional_input_funcs.add(value)
|
|
84
|
+
instance._input_func_map[value] = value
|
|
85
|
+
instance._input_func_map_rev[value] = value
|
|
86
|
+
instance._netaudio_func_list.append(value)
|
|
87
|
+
instance._playing_func_list.append(value)
|
|
84
88
|
try:
|
|
85
89
|
input_func = instance._input_func_map_rev[value]
|
|
86
90
|
except KeyError:
|
|
@@ -164,7 +168,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
164
168
|
_image_url: Optional[str] = attr.ib(
|
|
165
169
|
converter=attr.converters.optional(str), default=None
|
|
166
170
|
)
|
|
167
|
-
_image_available: Optional[
|
|
171
|
+
_image_available: Optional[bool] = attr.ib(
|
|
168
172
|
converter=attr.converters.optional(str), default=None
|
|
169
173
|
)
|
|
170
174
|
|
|
@@ -174,6 +178,8 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
174
178
|
),
|
|
175
179
|
default=attr.Factory(set),
|
|
176
180
|
)
|
|
181
|
+
_callback_tasks: Set[asyncio.Task] = attr.ib(default=attr.Factory(set))
|
|
182
|
+
_netaudio_state: str = attr.ib(converter=fix_string, default="")
|
|
177
183
|
|
|
178
184
|
# Update tags for attributes
|
|
179
185
|
# AppCommand.xml interface
|
|
@@ -197,22 +203,18 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
197
203
|
power_event = "Z2"
|
|
198
204
|
elif self._device.zone == ZONE3:
|
|
199
205
|
power_event = "Z3"
|
|
206
|
+
self._device.telnet_api.register_callback(power_event, self._power_callback)
|
|
207
|
+
self._device.telnet_api.register_callback("SI", self._input_callback)
|
|
208
|
+
self._device.telnet_api.register_callback("NSE", self._netaudio_callback)
|
|
209
|
+
self._device.telnet_api.register_callback("TF", self._tuner_callback)
|
|
210
|
+
self._device.telnet_api.register_callback("HD", self._hdtuner_callback)
|
|
200
211
|
self._device.telnet_api.register_callback(
|
|
201
|
-
|
|
202
|
-
)
|
|
203
|
-
self._device.telnet_api.register_callback("SI", self._async_input_callback)
|
|
204
|
-
self._device.telnet_api.register_callback("NSE", self._async_netaudio_callback)
|
|
205
|
-
self._device.telnet_api.register_callback("TF", self._async_tuner_callback)
|
|
206
|
-
self._device.telnet_api.register_callback("HD", self._async_hdtuner_callback)
|
|
207
|
-
self._device.telnet_api.register_callback(
|
|
208
|
-
"SS", self._async_input_func_update_callback
|
|
212
|
+
"SS", self._input_func_update_callback
|
|
209
213
|
)
|
|
210
214
|
|
|
211
215
|
self._is_setup = True
|
|
212
216
|
|
|
213
|
-
|
|
214
|
-
self, zone: str, event: str, parameter: str
|
|
215
|
-
) -> None:
|
|
217
|
+
def _input_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
216
218
|
"""Handle an input change event."""
|
|
217
219
|
if self._device.zone != zone:
|
|
218
220
|
return
|
|
@@ -228,9 +230,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
228
230
|
self._unset_media_state()
|
|
229
231
|
self._state = STATE_ON
|
|
230
232
|
|
|
231
|
-
|
|
232
|
-
self, zone: str, event: str, parameter: str
|
|
233
|
-
) -> None:
|
|
233
|
+
def _power_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
234
234
|
"""Handle a power change event."""
|
|
235
235
|
if self._device.zone != zone:
|
|
236
236
|
return
|
|
@@ -240,7 +240,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
240
240
|
self._unset_media_state()
|
|
241
241
|
self._state = STATE_OFF
|
|
242
242
|
elif self._schedule_media_updates():
|
|
243
|
-
self._state =
|
|
243
|
+
self._state = STATE_ON
|
|
244
244
|
else:
|
|
245
245
|
self._unset_media_state()
|
|
246
246
|
self._state = STATE_ON
|
|
@@ -285,16 +285,24 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
285
285
|
else:
|
|
286
286
|
self._stop_media_update()
|
|
287
287
|
|
|
288
|
-
|
|
289
|
-
self, zone: str, event: str, parameter: str
|
|
290
|
-
) -> None:
|
|
288
|
+
def _netaudio_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
291
289
|
"""Handle a netaudio update event."""
|
|
292
290
|
if self._device.power != POWER_ON:
|
|
293
291
|
return
|
|
294
292
|
if self._input_func not in self._netaudio_func_list:
|
|
295
293
|
return
|
|
296
294
|
|
|
297
|
-
if parameter.startswith("
|
|
295
|
+
if parameter.startswith("0"):
|
|
296
|
+
if parameter[1:].startswith(NETAUDIO_PLAYING):
|
|
297
|
+
# It is not possible to see if the device is playing or paused.
|
|
298
|
+
# We assume it is playing first.
|
|
299
|
+
# Then, the state might be changed by async_play and async_pause.
|
|
300
|
+
if self._state not in {STATE_PLAYING, STATE_PAUSED}:
|
|
301
|
+
self._state = STATE_PLAYING
|
|
302
|
+
else:
|
|
303
|
+
self._state = STATE_STOPPED
|
|
304
|
+
self._netaudio_state = parameter[1:]
|
|
305
|
+
elif parameter.startswith("1"):
|
|
298
306
|
self._title = parameter[1:]
|
|
299
307
|
elif parameter.startswith("2"):
|
|
300
308
|
self._artist = parameter[1:]
|
|
@@ -305,12 +313,13 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
305
313
|
self._station = None
|
|
306
314
|
|
|
307
315
|
# Refresh cover with a hash for media URL when track is changing
|
|
308
|
-
self.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
316
|
+
self._set_image_url(
|
|
317
|
+
ALBUM_COVERS_URL.format(
|
|
318
|
+
host=self._device.api.host,
|
|
319
|
+
port=self._device.api.port,
|
|
320
|
+
hash=hash((self._title, self._artist, self._album)),
|
|
321
|
+
)
|
|
312
322
|
)
|
|
313
|
-
await self._async_test_image_accessible()
|
|
314
323
|
|
|
315
324
|
def _schedule_tuner_update(self) -> None:
|
|
316
325
|
"""Schedule a tuner update task."""
|
|
@@ -330,9 +339,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
330
339
|
else:
|
|
331
340
|
self._stop_media_update()
|
|
332
341
|
|
|
333
|
-
|
|
334
|
-
self, zone: str, event: str, parameter: str
|
|
335
|
-
) -> None:
|
|
342
|
+
def _tuner_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
336
343
|
"""Handle a tuner update event."""
|
|
337
344
|
if self._device.power != POWER_ON:
|
|
338
345
|
return
|
|
@@ -353,10 +360,11 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
353
360
|
self._album = None
|
|
354
361
|
|
|
355
362
|
# No special cover, using a static one
|
|
356
|
-
self.
|
|
357
|
-
|
|
363
|
+
self._set_image_url(
|
|
364
|
+
STATIC_ALBUM_URL.format(
|
|
365
|
+
host=self._device.api.host, port=self._device.api.port
|
|
366
|
+
)
|
|
358
367
|
)
|
|
359
|
-
await self._async_test_image_accessible()
|
|
360
368
|
|
|
361
369
|
def _schedule_hdtuner_update(self) -> None:
|
|
362
370
|
"""Schedule a HD tuner update task."""
|
|
@@ -374,9 +382,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
374
382
|
else:
|
|
375
383
|
self._stop_media_update()
|
|
376
384
|
|
|
377
|
-
|
|
378
|
-
self, zone: str, event: str, parameter: str
|
|
379
|
-
) -> None:
|
|
385
|
+
def _hdtuner_callback(self, zone: str, event: str, parameter: str) -> None:
|
|
380
386
|
"""Handle an HD tuner update event."""
|
|
381
387
|
if self._device.power != POWER_ON:
|
|
382
388
|
return
|
|
@@ -396,19 +402,35 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
396
402
|
self._frequency = None
|
|
397
403
|
|
|
398
404
|
# No special cover, using a static one
|
|
399
|
-
self.
|
|
400
|
-
|
|
405
|
+
self._set_image_url(
|
|
406
|
+
STATIC_ALBUM_URL.format(
|
|
407
|
+
host=self._device.api.host, port=self._device.api.port
|
|
408
|
+
)
|
|
401
409
|
)
|
|
402
|
-
await self._async_test_image_accessible()
|
|
403
410
|
|
|
404
|
-
|
|
411
|
+
def _input_func_update_callback(
|
|
405
412
|
self, zone: str, event: str, parameter: str
|
|
406
413
|
) -> None:
|
|
407
414
|
"""Handle input func update events."""
|
|
408
415
|
if self._input_func_update_lock.locked():
|
|
409
416
|
return
|
|
410
|
-
|
|
411
|
-
|
|
417
|
+
task = asyncio.create_task(self.async_update_inputfuncs())
|
|
418
|
+
self._callback_tasks.add(task)
|
|
419
|
+
task.add_done_callback(self._callback_tasks.discard)
|
|
420
|
+
|
|
421
|
+
def _set_image_url(self, image_url: str) -> None:
|
|
422
|
+
"""Set image URL if it is accessible."""
|
|
423
|
+
if self._image_available is False:
|
|
424
|
+
return
|
|
425
|
+
|
|
426
|
+
self._image_url = image_url
|
|
427
|
+
|
|
428
|
+
if self._image_available:
|
|
429
|
+
return
|
|
430
|
+
|
|
431
|
+
task = asyncio.create_task(self._async_test_image_accessible())
|
|
432
|
+
self._callback_tasks.add(task)
|
|
433
|
+
task.add_done_callback(self._callback_tasks.discard)
|
|
412
434
|
|
|
413
435
|
async def async_update(
|
|
414
436
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
@@ -525,7 +547,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
525
547
|
|
|
526
548
|
self._replace_duplicate_sources(renamed_sources)
|
|
527
549
|
|
|
528
|
-
return
|
|
550
|
+
return renamed_sources, deleted_sources
|
|
529
551
|
|
|
530
552
|
async def async_get_changed_sources_status_xml(
|
|
531
553
|
self, cache_id: Optional[Hashable] = None
|
|
@@ -615,22 +637,23 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
615
637
|
|
|
616
638
|
self._replace_duplicate_sources(renamed_sources)
|
|
617
639
|
|
|
618
|
-
return
|
|
640
|
+
return renamed_sources, deleted_sources
|
|
619
641
|
|
|
620
642
|
async def async_update_inputfuncs(
|
|
621
643
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
622
644
|
) -> None:
|
|
623
645
|
"""Update sources list from receiver."""
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
646
|
+
async with self._input_func_update_lock:
|
|
647
|
+
if self._device.receiver in [AVR_X, AVR_X_2016]:
|
|
648
|
+
await self._async_update_inputfuncs_avr_x(
|
|
649
|
+
global_update=global_update, cache_id=cache_id
|
|
650
|
+
)
|
|
651
|
+
elif self._device.receiver in [AVR]:
|
|
652
|
+
await self._async_update_inputfuncs_avr(cache_id=cache_id)
|
|
653
|
+
else:
|
|
654
|
+
raise AvrProcessingError(
|
|
655
|
+
"Device is not setup correctly, receiver type not set"
|
|
656
|
+
)
|
|
634
657
|
|
|
635
658
|
async def _async_update_inputfuncs_avr_x(
|
|
636
659
|
self, global_update: bool = False, cache_id: Optional[Hashable] = None
|
|
@@ -813,6 +836,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
813
836
|
"_title": "./szLine/value[2]",
|
|
814
837
|
"_artist": "./szLine/value[3]",
|
|
815
838
|
"_album": "./szLine/value[5]",
|
|
839
|
+
"_netaudio_state": "./szLine/value[1]",
|
|
816
840
|
}
|
|
817
841
|
self._band = None
|
|
818
842
|
self._frequency = None
|
|
@@ -862,8 +886,15 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
862
886
|
port=self._device.api.port,
|
|
863
887
|
hash=hash((self._title, self._artist, self._album)),
|
|
864
888
|
)
|
|
865
|
-
|
|
866
|
-
|
|
889
|
+
|
|
890
|
+
if self._netaudio_state.startswith(NETAUDIO_PLAYING):
|
|
891
|
+
# It is not possible to see if the device is playing or paused.
|
|
892
|
+
# We assume it is playing first.
|
|
893
|
+
# Then, the state might be changed by async_play and async_pause.
|
|
894
|
+
if self._state not in {STATE_PLAYING, STATE_PAUSED}:
|
|
895
|
+
self._state = STATE_PLAYING
|
|
896
|
+
else:
|
|
897
|
+
self._state = STATE_STOPPED
|
|
867
898
|
|
|
868
899
|
await self._async_test_image_accessible()
|
|
869
900
|
|
|
@@ -1070,13 +1101,9 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
1070
1101
|
await self.async_play()
|
|
1071
1102
|
|
|
1072
1103
|
async def async_play(self) -> None:
|
|
1073
|
-
"""Send play command to receiver command
|
|
1104
|
+
"""Send play command to receiver command."""
|
|
1074
1105
|
# Use pause command only for sources which support NETAUDIO
|
|
1075
1106
|
if self._input_func in self._netaudio_func_list:
|
|
1076
|
-
# In fact play command is a play/pause toggle. Thus checking state
|
|
1077
|
-
if self._state == STATE_PLAYING:
|
|
1078
|
-
_LOGGER.info("Already playing, play command not sent")
|
|
1079
|
-
return
|
|
1080
1107
|
if self._device.telnet_available:
|
|
1081
1108
|
await self._device.telnet_api.async_send_commands(
|
|
1082
1109
|
self._device.telnet_commands.command_play
|
|
@@ -1086,12 +1113,12 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
1086
1113
|
self._state = STATE_PLAYING
|
|
1087
1114
|
|
|
1088
1115
|
async def async_pause(self) -> None:
|
|
1089
|
-
"""Send pause command to receiver command
|
|
1116
|
+
"""Send pause command to receiver command."""
|
|
1090
1117
|
# Use pause command only for sources which support NETAUDIO
|
|
1091
1118
|
if self._input_func in self._netaudio_func_list:
|
|
1092
1119
|
if self._device.telnet_available:
|
|
1093
1120
|
await self._device.telnet_api.async_send_commands(
|
|
1094
|
-
self._device.telnet_commands.
|
|
1121
|
+
self._device.telnet_commands.command_pause
|
|
1095
1122
|
)
|
|
1096
1123
|
else:
|
|
1097
1124
|
await self._device.api.async_get_command(
|
|
@@ -1099,8 +1126,20 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
1099
1126
|
)
|
|
1100
1127
|
self._state = STATE_PAUSED
|
|
1101
1128
|
|
|
1129
|
+
async def async_stop(self) -> None:
|
|
1130
|
+
"""Send stop command to receiver command."""
|
|
1131
|
+
# Use stop command only for sources which support NETAUDIO
|
|
1132
|
+
if self._input_func in self._netaudio_func_list:
|
|
1133
|
+
if self._device.telnet_available:
|
|
1134
|
+
await self._device.telnet_api.async_send_commands(
|
|
1135
|
+
self._device.telnet_commands.command_stop
|
|
1136
|
+
)
|
|
1137
|
+
else:
|
|
1138
|
+
await self._device.api.async_get_command(self._device.urls.command_stop)
|
|
1139
|
+
self._state = STATE_STOPPED
|
|
1140
|
+
|
|
1102
1141
|
async def async_previous_track(self) -> None:
|
|
1103
|
-
"""Send previous track command to receiver command
|
|
1142
|
+
"""Send previous track command to receiver command."""
|
|
1104
1143
|
# Use previous track button only for sources which support NETAUDIO
|
|
1105
1144
|
if self._input_func in self._netaudio_func_list:
|
|
1106
1145
|
body = {
|
|
@@ -1113,7 +1152,7 @@ class DenonAVRInput(DenonAVRFoundation):
|
|
|
1113
1152
|
)
|
|
1114
1153
|
|
|
1115
1154
|
async def async_next_track(self) -> None:
|
|
1116
|
-
"""Send next track command to receiver command
|
|
1155
|
+
"""Send next track command to receiver command."""
|
|
1117
1156
|
# Use next track button only for sources which support NETAUDIO
|
|
1118
1157
|
if self._input_func in self._netaudio_func_list:
|
|
1119
1158
|
body = {
|