python-linkplay 0.2.2__tar.gz → 0.2.4__tar.gz
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.
- {python_linkplay-0.2.2/src/python_linkplay.egg-info → python_linkplay-0.2.4}/PKG-INFO +1 -1
- python_linkplay-0.2.4/src/linkplay/__version__.py +1 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/bridge.py +49 -1
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/consts.py +48 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/controller.py +13 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4/src/python_linkplay.egg-info}/PKG-INFO +1 -1
- python_linkplay-0.2.2/src/linkplay/__version__.py +0 -1
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/LICENSE +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/README.md +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/pyproject.toml +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/setup.cfg +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/setup.py +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/__init__.py +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/__main__.py +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/discovery.py +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/endpoint.py +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/exceptions.py +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/manufacturers.py +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/linkplay/utils.py +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/python_linkplay.egg-info/SOURCES.txt +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/python_linkplay.egg-info/dependency_links.txt +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/python_linkplay.egg-info/not-zip-safe +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/python_linkplay.egg-info/requires.txt +0 -0
- {python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/python_linkplay.egg-info/top_level.txt +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = '0.2.4'
|
@@ -1,18 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import time
|
4
|
-
from typing import Any
|
4
|
+
from typing import Any, Callable
|
5
5
|
|
6
6
|
from linkplay.consts import (
|
7
7
|
INPUT_MODE_MAP,
|
8
8
|
LOGGER,
|
9
9
|
PLAY_MODE_SEND_MAP,
|
10
|
+
AudioOutputHwMode,
|
10
11
|
ChannelType,
|
11
12
|
DeviceAttribute,
|
12
13
|
EqualizerMode,
|
13
14
|
InputMode,
|
14
15
|
LinkPlayCommand,
|
15
16
|
LoopMode,
|
17
|
+
MetaInfo,
|
18
|
+
MetaInfoMetaData,
|
16
19
|
MultiroomAttribute,
|
17
20
|
MuteMode,
|
18
21
|
PlayerAttribute,
|
@@ -36,6 +39,8 @@ class LinkPlayDevice:
|
|
36
39
|
bridge: LinkPlayBridge
|
37
40
|
properties: dict[DeviceAttribute, str]
|
38
41
|
|
42
|
+
controller: Callable[[], None] | None = None
|
43
|
+
|
39
44
|
def __init__(self, bridge: LinkPlayBridge):
|
40
45
|
self.bridge = bridge
|
41
46
|
self.properties = dict.fromkeys(DeviceAttribute.__members__.values(), "")
|
@@ -44,6 +49,10 @@ class LinkPlayDevice:
|
|
44
49
|
"""Return the state of the LinkPlayDevice."""
|
45
50
|
return {"properties": self.properties}
|
46
51
|
|
52
|
+
def set_callback(self, controller: Callable[[], None]) -> None:
|
53
|
+
"""Sets a callback function to notify events."""
|
54
|
+
self.controller = controller
|
55
|
+
|
47
56
|
async def update_status(self) -> None:
|
48
57
|
"""Update the device status."""
|
49
58
|
self.properties = await self.bridge.json_request(LinkPlayCommand.DEVICE_STATUS) # type: ignore[assignment]
|
@@ -122,11 +131,15 @@ class LinkPlayPlayer:
|
|
122
131
|
bridge: LinkPlayBridge
|
123
132
|
properties: dict[PlayerAttribute, str]
|
124
133
|
custom_properties: dict[PlayerAttribute, str]
|
134
|
+
metainfo: dict[MetaInfo, dict[MetaInfoMetaData, str]]
|
135
|
+
|
136
|
+
previous_playing_mode: PlayingMode | None = None
|
125
137
|
|
126
138
|
def __init__(self, bridge: LinkPlayBridge):
|
127
139
|
self.bridge = bridge
|
128
140
|
self.properties = dict.fromkeys(PlayerAttribute.__members__.values(), "")
|
129
141
|
self.custom_properties = dict.fromkeys(PlayerAttribute.__members__.values(), "")
|
142
|
+
self.metainfo = dict.fromkeys(MetaInfo.__members__.values(), {})
|
130
143
|
|
131
144
|
def to_dict(self):
|
132
145
|
"""Return the state of the LinkPlayPlayer."""
|
@@ -139,6 +152,26 @@ class LinkPlayPlayer:
|
|
139
152
|
) # type: ignore[assignment]
|
140
153
|
|
141
154
|
self.properties = fixup_player_properties(properties)
|
155
|
+
if self.bridge.device.manufacturer == MANUFACTURER_WIIM:
|
156
|
+
self.metainfo: dict[
|
157
|
+
MetaInfo, dict[MetaInfoMetaData, str]
|
158
|
+
] = await self.bridge.json_request(LinkPlayCommand.META_INFO) # type: ignore[assignment]
|
159
|
+
else:
|
160
|
+
self.metainfo = {}
|
161
|
+
|
162
|
+
# handle multiroom changes
|
163
|
+
if self.bridge.device.controller is not None and (
|
164
|
+
(
|
165
|
+
self.previous_playing_mode != PlayingMode.FOLLOWER
|
166
|
+
and self.play_mode == PlayingMode.FOLLOWER
|
167
|
+
)
|
168
|
+
or (
|
169
|
+
self.previous_playing_mode == PlayingMode.FOLLOWER
|
170
|
+
and self.play_mode != PlayingMode.FOLLOWER
|
171
|
+
)
|
172
|
+
):
|
173
|
+
self.bridge.device.controller()
|
174
|
+
self.previous_playing_mode = self.play_mode
|
142
175
|
|
143
176
|
async def next(self) -> None:
|
144
177
|
"""Play the next song in the playlist."""
|
@@ -255,6 +288,14 @@ class LinkPlayPlayer:
|
|
255
288
|
):
|
256
289
|
await self.bridge.request(LinkPlayCommand.SEEK.format(position))
|
257
290
|
|
291
|
+
async def set_audio_output_hw_mode(self, mode: AudioOutputHwMode) -> None:
|
292
|
+
"""Set the audio hardware output."""
|
293
|
+
await self.bridge.request(LinkPlayCommand.AUDIO_OUTPUT_HW_MODE_SET.format(mode))
|
294
|
+
|
295
|
+
async def get_audio_output_hw_mode(self) -> None:
|
296
|
+
"""Get the audio hardware output."""
|
297
|
+
await self.bridge.json_request(LinkPlayCommand.AUDIO_OUTPUT_HW_MODE)
|
298
|
+
|
258
299
|
@property
|
259
300
|
def muted(self) -> bool:
|
260
301
|
"""Returns if the player is muted."""
|
@@ -278,6 +319,13 @@ class LinkPlayPlayer:
|
|
278
319
|
"""Returns if the currently playing album."""
|
279
320
|
return self.properties.get(PlayerAttribute.ALBUM, "")
|
280
321
|
|
322
|
+
@property
|
323
|
+
def album_art(self) -> str:
|
324
|
+
"""Returns the url to the album art."""
|
325
|
+
return self.metainfo.get(MetaInfo.METADATA, {}).get(
|
326
|
+
MetaInfoMetaData.ALBUM_ART, ""
|
327
|
+
)
|
328
|
+
|
281
329
|
@property
|
282
330
|
def volume(self) -> int:
|
283
331
|
"""Returns the player volume, expressed in %."""
|
@@ -100,6 +100,9 @@ class LinkPlayCommand(StrEnum):
|
|
100
100
|
PLAY_PRESET = "MCUKeyShortClick:{}"
|
101
101
|
TIMESYNC = "timeSync:{}"
|
102
102
|
WIIM_EQ_LOAD = "EQLoad:{}"
|
103
|
+
META_INFO = "getMetaInfo"
|
104
|
+
AUDIO_OUTPUT_HW_MODE_SET = "setAudioOutputHardwareMode:{}"
|
105
|
+
AUDIO_OUTPUT_HW_MODE = "getNewAudioOutputHardwareMode"
|
103
106
|
|
104
107
|
|
105
108
|
class LinkPlayTcpUartCommand(StrEnum):
|
@@ -470,3 +473,48 @@ class MultiroomAttribute(StrEnum):
|
|
470
473
|
|
471
474
|
def __repr__(self):
|
472
475
|
return self.value
|
476
|
+
|
477
|
+
|
478
|
+
class MetaInfo(StrEnum):
|
479
|
+
|
480
|
+
METADATA = "metaData"
|
481
|
+
|
482
|
+
def __str__(self):
|
483
|
+
return self.value
|
484
|
+
|
485
|
+
def __repr__(self):
|
486
|
+
return self.value
|
487
|
+
|
488
|
+
class MetaInfoMetaData(StrEnum):
|
489
|
+
"""Defines the metadata within the metainfo."""
|
490
|
+
|
491
|
+
ALBUM_TITLE = "album"
|
492
|
+
TRACK_TITLE = "title"
|
493
|
+
TRACK_SUBTITLE = "subtitle"
|
494
|
+
ALBUM_ART = "albumArtURI"
|
495
|
+
SAMPLE_RATE = "sampleRate"
|
496
|
+
BIT_DEPTH = "bitDepth"
|
497
|
+
BIT_RATE = "bitRate"
|
498
|
+
TRACK_ID = "trackId"
|
499
|
+
|
500
|
+
def __str__(self):
|
501
|
+
return self.value
|
502
|
+
|
503
|
+
def __repr__(self):
|
504
|
+
return self.value
|
505
|
+
|
506
|
+
|
507
|
+
class AudioOutputHwMode(StrEnum):
|
508
|
+
"""Defines a output mode for the hardware."""
|
509
|
+
OPTICAL = "1"
|
510
|
+
LINE_OUT = "2"
|
511
|
+
COAXIAL = "3"
|
512
|
+
HEADPHONES = "4"
|
513
|
+
|
514
|
+
# Map between a play mode and how to activate the play mode
|
515
|
+
AUDIO_OUTPUT_HW_MODE_MAP: dict[AudioOutputHwMode, str] = { # case sensitive!
|
516
|
+
AudioOutputHwMode.OPTICAL: "optical",
|
517
|
+
AudioOutputHwMode.LINE_OUT: "line-out",
|
518
|
+
AudioOutputHwMode.COAXIAL: "co-axial",
|
519
|
+
AudioOutputHwMode.HEADPHONES: "headphones",
|
520
|
+
}
|
@@ -18,6 +18,16 @@ class LinkPlayController:
|
|
18
18
|
self.bridges = []
|
19
19
|
self.multirooms = []
|
20
20
|
|
21
|
+
def get_bridge_callback(self):
|
22
|
+
"""Returns an async callback function for LinkPlayBridge."""
|
23
|
+
|
24
|
+
async def callback() -> None:
|
25
|
+
"""Async callback function to handle events from a LinkPlayBridge."""
|
26
|
+
LOGGER.debug("Controller event received")
|
27
|
+
await self.discover_multirooms()
|
28
|
+
|
29
|
+
return callback
|
30
|
+
|
21
31
|
async def discover_bridges(self) -> None:
|
22
32
|
"""Attempts to discover LinkPlay devices on the local network."""
|
23
33
|
|
@@ -46,6 +56,7 @@ class LinkPlayController:
|
|
46
56
|
# Add bridge
|
47
57
|
current_bridges = [bridge.device.uuid for bridge in self.bridges]
|
48
58
|
if bridge_to_add.device.uuid not in current_bridges:
|
59
|
+
bridge_to_add.device.set_callback(self.get_bridge_callback())
|
49
60
|
self.bridges.append(bridge_to_add)
|
50
61
|
|
51
62
|
async def remove_bridge(self, bridge_to_remove: LinkPlayBridge) -> None:
|
@@ -80,6 +91,8 @@ class LinkPlayController:
|
|
80
91
|
multiroom.leader.multiroom = None
|
81
92
|
removed_multirooms.append(multiroom)
|
82
93
|
except LinkPlayInvalidDataException as exc:
|
94
|
+
multiroom.leader.multiroom = None
|
95
|
+
removed_multirooms.append(multiroom)
|
83
96
|
LOGGER.exception(exc)
|
84
97
|
|
85
98
|
# Create new multirooms from new bridges
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = '0.2.2'
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{python_linkplay-0.2.2 → python_linkplay-0.2.4}/src/python_linkplay.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|