python-linkplay 0.0.5__tar.gz → 0.0.6__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.0.5/src/python_linkplay.egg-info → python_linkplay-0.0.6}/PKG-INFO +2 -2
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/pyproject.toml +4 -1
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/setup.cfg +1 -6
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/linkplay/__main__.py +1 -0
- python_linkplay-0.0.6/src/linkplay/__version__.py +1 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/linkplay/bridge.py +33 -14
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/linkplay/consts.py +43 -30
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/linkplay/controller.py +12 -4
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/linkplay/discovery.py +17 -9
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/linkplay/utils.py +9 -4
- {python_linkplay-0.0.5 → python_linkplay-0.0.6/src/python_linkplay.egg-info}/PKG-INFO +2 -2
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/python_linkplay.egg-info/requires.txt +1 -1
- python_linkplay-0.0.5/src/linkplay/__version__.py +0 -1
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/LICENSE +0 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/README.md +0 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/setup.py +0 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/linkplay/__init__.py +0 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/linkplay/exceptions.py +0 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/python_linkplay.egg-info/SOURCES.txt +0 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/python_linkplay.egg-info/dependency_links.txt +0 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/python_linkplay.egg-info/not-zip-safe +0 -0
- {python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/python_linkplay.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: python_linkplay
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.6
|
4
4
|
Summary: A Python Library for Seamless LinkPlay Device Control
|
5
5
|
Author: Velleman Group nv
|
6
6
|
License: MIT
|
@@ -17,7 +17,7 @@ Requires-Dist: pytest-cov>=4.1.0; extra == "testing"
|
|
17
17
|
Requires-Dist: pytest-mock>=3.10.0; extra == "testing"
|
18
18
|
Requires-Dist: pytest-asyncio>=0.23.3; extra == "testing"
|
19
19
|
Requires-Dist: mypy>=1.3.0; extra == "testing"
|
20
|
-
Requires-Dist:
|
20
|
+
Requires-Dist: ruff>=0.5.4; extra == "testing"
|
21
21
|
Requires-Dist: tox>=4.6.0; extra == "testing"
|
22
22
|
Requires-Dist: typing-extensions>=4.6.3; extra == "testing"
|
23
23
|
|
@@ -33,15 +33,10 @@ testing =
|
|
33
33
|
pytest-mock>=3.10.0
|
34
34
|
pytest-asyncio>=0.23.3
|
35
35
|
mypy>=1.3.0
|
36
|
-
|
36
|
+
ruff>=0.5.4
|
37
37
|
tox>=4.6.0
|
38
38
|
typing-extensions>=4.6.3
|
39
39
|
|
40
|
-
[flake8]
|
41
|
-
max-line-length = 160
|
42
|
-
per-file-ignores =
|
43
|
-
*/__init__.py: F401
|
44
|
-
|
45
40
|
[egg_info]
|
46
41
|
tag_build =
|
47
42
|
tag_date = 0
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = '0.0.6'
|
@@ -16,17 +16,19 @@ from linkplay.consts import (
|
|
16
16
|
SpeakerType,
|
17
17
|
PlayingMode,
|
18
18
|
INPUT_MODE_MAP,
|
19
|
-
MultiroomAttribute
|
19
|
+
MultiroomAttribute,
|
20
20
|
)
|
21
21
|
|
22
22
|
from linkplay.utils import session_call_api_json, session_call_api_ok, decode_hexstr
|
23
23
|
|
24
24
|
|
25
|
-
class LinkPlayDevice
|
25
|
+
class LinkPlayDevice:
|
26
26
|
"""Represents a LinkPlay device."""
|
27
27
|
|
28
28
|
bridge: LinkPlayBridge
|
29
|
-
properties: dict[DeviceAttribute, str] = dict.fromkeys(
|
29
|
+
properties: dict[DeviceAttribute, str] = dict.fromkeys(
|
30
|
+
DeviceAttribute.__members__.values(), ""
|
31
|
+
)
|
30
32
|
|
31
33
|
def __init__(self, bridge: LinkPlayBridge):
|
32
34
|
self.bridge = bridge
|
@@ -53,7 +55,9 @@ class LinkPlayDevice():
|
|
53
55
|
def playmode_support(self) -> list[PlayingMode]:
|
54
56
|
"""Returns the player playmode support."""
|
55
57
|
|
56
|
-
flags = InputMode(
|
58
|
+
flags = InputMode(
|
59
|
+
int(self.properties[DeviceAttribute.PLAYMODE_SUPPORT], base=16)
|
60
|
+
)
|
57
61
|
return [INPUT_MODE_MAP[flag] for flag in flags]
|
58
62
|
|
59
63
|
@property
|
@@ -62,11 +66,13 @@ class LinkPlayDevice():
|
|
62
66
|
return self.properties[DeviceAttribute.ETH_DHCP]
|
63
67
|
|
64
68
|
|
65
|
-
class LinkPlayPlayer
|
69
|
+
class LinkPlayPlayer:
|
66
70
|
"""Represents a LinkPlay player."""
|
67
71
|
|
68
72
|
bridge: LinkPlayBridge
|
69
|
-
properties: dict[PlayerAttribute, str] = dict.fromkeys(
|
73
|
+
properties: dict[PlayerAttribute, str] = dict.fromkeys(
|
74
|
+
PlayerAttribute.__members__.values(), ""
|
75
|
+
)
|
70
76
|
|
71
77
|
def __init__(self, bridge: LinkPlayBridge):
|
72
78
|
self.bridge = bridge
|
@@ -134,7 +140,9 @@ class LinkPlayPlayer():
|
|
134
140
|
|
135
141
|
async def set_play_mode(self, mode: PlayingMode) -> None:
|
136
142
|
"""Set the play mode."""
|
137
|
-
await self.bridge.request(
|
143
|
+
await self.bridge.request(
|
144
|
+
LinkPlayCommand.SWITCH_MODE.format(PLAY_MODE_SEND_MAP[mode])
|
145
|
+
) # type: ignore[str-format]
|
138
146
|
|
139
147
|
@property
|
140
148
|
def muted(self) -> bool:
|
@@ -202,7 +210,7 @@ class LinkPlayPlayer():
|
|
202
210
|
return LoopMode(self.properties[PlayerAttribute.PLAYLIST_MODE])
|
203
211
|
|
204
212
|
|
205
|
-
class LinkPlayBridge
|
213
|
+
class LinkPlayBridge:
|
206
214
|
"""Represents a LinkPlay bridge to control the device and player attached to it."""
|
207
215
|
|
208
216
|
protocol: str
|
@@ -238,7 +246,7 @@ class LinkPlayBridge():
|
|
238
246
|
await session_call_api_ok(self.endpoint, self.session, command)
|
239
247
|
|
240
248
|
|
241
|
-
class LinkPlayMultiroom
|
249
|
+
class LinkPlayMultiroom:
|
242
250
|
"""Represents a LinkPlay multiroom group. Contains a leader and a list of followers.
|
243
251
|
The leader is the device that controls the group."""
|
244
252
|
|
@@ -251,14 +259,21 @@ class LinkPlayMultiroom():
|
|
251
259
|
|
252
260
|
async def update_status(self, bridges: list[LinkPlayBridge]) -> None:
|
253
261
|
"""Updates the multiroom status."""
|
254
|
-
properties: dict[Any, Any] = await self.leader.json_request(
|
262
|
+
properties: dict[Any, Any] = await self.leader.json_request(
|
263
|
+
LinkPlayCommand.MULTIROOM_LIST
|
264
|
+
)
|
255
265
|
|
256
266
|
self.followers = []
|
257
267
|
if int(properties[MultiroomAttribute.NUM_FOLLOWERS]) == 0:
|
258
268
|
return
|
259
269
|
|
260
|
-
follower_uuids = [
|
261
|
-
|
270
|
+
follower_uuids = [
|
271
|
+
follower[MultiroomAttribute.UUID]
|
272
|
+
for follower in properties[MultiroomAttribute.FOLLOWER_LIST]
|
273
|
+
]
|
274
|
+
new_followers = [
|
275
|
+
bridge for bridge in bridges if bridge.device.uuid in follower_uuids
|
276
|
+
]
|
262
277
|
self.followers.extend(new_followers)
|
263
278
|
|
264
279
|
async def ungroup(self) -> None:
|
@@ -268,12 +283,16 @@ class LinkPlayMultiroom():
|
|
268
283
|
|
269
284
|
async def add_follower(self, follower: LinkPlayBridge) -> None:
|
270
285
|
"""Adds a follower to the multiroom group."""
|
271
|
-
await follower.request(
|
286
|
+
await follower.request(
|
287
|
+
LinkPlayCommand.MULTIROOM_JOIN.format(self.leader.device.eth)
|
288
|
+
) # type: ignore[str-format]
|
272
289
|
self.followers.append(follower)
|
273
290
|
|
274
291
|
async def remove_follower(self, follower: LinkPlayBridge) -> None:
|
275
292
|
"""Removes a follower from the multiroom group."""
|
276
|
-
await self.leader.request(
|
293
|
+
await self.leader.request(
|
294
|
+
LinkPlayCommand.MULTIROOM_KICK.format(follower.device.eth)
|
295
|
+
) # type: ignore[str-format]
|
277
296
|
self.followers.remove(follower)
|
278
297
|
|
279
298
|
async def set_volume(self, value: int) -> None:
|
@@ -3,11 +3,12 @@ from enum import StrEnum, IntFlag
|
|
3
3
|
API_ENDPOINT: str = "{}/httpapi.asp?command={}"
|
4
4
|
API_TIMEOUT: int = 2
|
5
5
|
UNKNOWN_TRACK_PLAYING: str = "Unknown"
|
6
|
-
UPNP_DEVICE_TYPE =
|
6
|
+
UPNP_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaRenderer:1"
|
7
7
|
|
8
8
|
|
9
9
|
class LinkPlayCommand(StrEnum):
|
10
10
|
"""Defines the LinkPlay commands."""
|
11
|
+
|
11
12
|
DEVICE_STATUS = "getStatus"
|
12
13
|
SYSLOG = "getsyslog"
|
13
14
|
UPDATE_SERVER = "GetUpdateServer"
|
@@ -39,12 +40,14 @@ class LinkPlayCommand(StrEnum):
|
|
39
40
|
|
40
41
|
class SpeakerType(StrEnum):
|
41
42
|
"""Defines the speaker type."""
|
43
|
+
|
42
44
|
MAIN_SPEAKER = "0"
|
43
45
|
SUB_SPEAKER = "1"
|
44
46
|
|
45
47
|
|
46
48
|
class ChannelType(StrEnum):
|
47
49
|
"""Defines the channel type."""
|
50
|
+
|
48
51
|
STEREO = "0"
|
49
52
|
LEFT_CHANNEL = "1"
|
50
53
|
RIGHT_CHANNEL = "2"
|
@@ -52,6 +55,7 @@ class ChannelType(StrEnum):
|
|
52
55
|
|
53
56
|
class PlayingMode(StrEnum):
|
54
57
|
"""Defines a possible playing mode."""
|
58
|
+
|
55
59
|
IDLE = "-1"
|
56
60
|
NONE = "0"
|
57
61
|
AIRPLAY = "1"
|
@@ -88,48 +92,51 @@ class PlayingMode(StrEnum):
|
|
88
92
|
|
89
93
|
|
90
94
|
# Map between a play mode and how to activate the play mode
|
91
|
-
PLAY_MODE_SEND_MAP: dict[PlayingMode, str] = {
|
92
|
-
PlayingMode.NONE:
|
93
|
-
PlayingMode.AIRPLAY:
|
94
|
-
PlayingMode.DLNA:
|
95
|
-
PlayingMode.QPLAY:
|
96
|
-
PlayingMode.NETWORK:
|
97
|
-
PlayingMode.WIIMU_LOCAL:
|
98
|
-
PlayingMode.TF_CARD_1:
|
99
|
-
PlayingMode.API:
|
100
|
-
PlayingMode.UDISK:
|
101
|
-
PlayingMode.ALARM:
|
102
|
-
PlayingMode.SPOTIFY:
|
103
|
-
PlayingMode.LINE_IN:
|
104
|
-
PlayingMode.BLUETOOTH:
|
105
|
-
PlayingMode.OPTICAL:
|
106
|
-
PlayingMode.RCA:
|
107
|
-
PlayingMode.COAXIAL:
|
108
|
-
PlayingMode.FM:
|
109
|
-
PlayingMode.LINE_IN_2:
|
110
|
-
PlayingMode.XLR:
|
111
|
-
PlayingMode.HDMI:
|
112
|
-
PlayingMode.MIRROR:
|
113
|
-
PlayingMode.USB_DAC:
|
114
|
-
PlayingMode.TF_CARD_2:
|
115
|
-
PlayingMode.TALK:
|
116
|
-
PlayingMode.SLAVE:
|
117
|
-
PlayingMode.OPTICAL_2:
|
95
|
+
PLAY_MODE_SEND_MAP: dict[PlayingMode, str] = { # case sensitive!
|
96
|
+
PlayingMode.NONE: "Idle",
|
97
|
+
PlayingMode.AIRPLAY: "Airplay",
|
98
|
+
PlayingMode.DLNA: "DLNA",
|
99
|
+
PlayingMode.QPLAY: "QPlay",
|
100
|
+
PlayingMode.NETWORK: "wifi",
|
101
|
+
PlayingMode.WIIMU_LOCAL: "udisk",
|
102
|
+
PlayingMode.TF_CARD_1: "TFcard",
|
103
|
+
PlayingMode.API: "API",
|
104
|
+
PlayingMode.UDISK: "udisk",
|
105
|
+
PlayingMode.ALARM: "Alarm",
|
106
|
+
PlayingMode.SPOTIFY: "Spotify",
|
107
|
+
PlayingMode.LINE_IN: "line-in",
|
108
|
+
PlayingMode.BLUETOOTH: "bluetooth",
|
109
|
+
PlayingMode.OPTICAL: "optical",
|
110
|
+
PlayingMode.RCA: "RCA",
|
111
|
+
PlayingMode.COAXIAL: "co-axial",
|
112
|
+
PlayingMode.FM: "FM",
|
113
|
+
PlayingMode.LINE_IN_2: "line-in2",
|
114
|
+
PlayingMode.XLR: "XLR",
|
115
|
+
PlayingMode.HDMI: "HDMI",
|
116
|
+
PlayingMode.MIRROR: "cd",
|
117
|
+
PlayingMode.USB_DAC: "USB DAC",
|
118
|
+
PlayingMode.TF_CARD_2: "TFcard",
|
119
|
+
PlayingMode.TALK: "Talk",
|
120
|
+
PlayingMode.SLAVE: "Idle",
|
121
|
+
PlayingMode.OPTICAL_2: "optical2",
|
118
122
|
}
|
119
123
|
|
120
124
|
|
121
125
|
class LoopMode(StrEnum):
|
122
126
|
"""Defines the loop mode."""
|
127
|
+
|
123
128
|
CONTINOUS_PLAY_ONE_SONG = "-1"
|
124
129
|
PLAY_IN_ORDER = "0"
|
125
130
|
CONTINUOUS_PLAYBACK = "1"
|
126
131
|
RANDOM_PLAYBACK = "2"
|
127
132
|
LIST_CYCLE = "3"
|
128
|
-
|
133
|
+
SHUFF_DISABLED_REPEAT_DISABLED = "4"
|
134
|
+
SHUFF_ENABLED_REPEAT_ENABLED_LOOP_ONCE = "5"
|
129
135
|
|
130
136
|
|
131
137
|
class EqualizerMode(StrEnum):
|
132
138
|
"""Defines the equalizer mode."""
|
139
|
+
|
133
140
|
NONE = "0"
|
134
141
|
CLASSIC = "1"
|
135
142
|
POP = "2"
|
@@ -139,6 +146,7 @@ class EqualizerMode(StrEnum):
|
|
139
146
|
|
140
147
|
class PlayingStatus(StrEnum):
|
141
148
|
"""Defines the playing status."""
|
149
|
+
|
142
150
|
PLAYING = "play"
|
143
151
|
LOADING = "load"
|
144
152
|
STOPPED = "stop"
|
@@ -147,12 +155,14 @@ class PlayingStatus(StrEnum):
|
|
147
155
|
|
148
156
|
class MuteMode(StrEnum):
|
149
157
|
"""Defines the mute mode."""
|
158
|
+
|
150
159
|
UNMUTED = "0"
|
151
160
|
MUTED = "1"
|
152
161
|
|
153
162
|
|
154
163
|
class InputMode(IntFlag):
|
155
164
|
"""Defines which inputs the player supports."""
|
165
|
+
|
156
166
|
LINE_IN = 2
|
157
167
|
BLUETOOTH = 4
|
158
168
|
USB = 8
|
@@ -172,12 +182,13 @@ INPUT_MODE_MAP: dict[InputMode, PlayingMode] = {
|
|
172
182
|
InputMode.COAXIAL: PlayingMode.COAXIAL,
|
173
183
|
InputMode.LINE_IN_2: PlayingMode.LINE_IN_2,
|
174
184
|
InputMode.USB_DAC: PlayingMode.USB_DAC,
|
175
|
-
InputMode.OPTICAL_2: PlayingMode.OPTICAL_2
|
185
|
+
InputMode.OPTICAL_2: PlayingMode.OPTICAL_2,
|
176
186
|
}
|
177
187
|
|
178
188
|
|
179
189
|
class PlayerAttribute(StrEnum):
|
180
190
|
"""Defines the player attributes."""
|
191
|
+
|
181
192
|
SPEAKER_TYPE = "type"
|
182
193
|
CHANNEL_TYPE = "ch"
|
183
194
|
PLAYBACK_MODE = "mode"
|
@@ -199,6 +210,7 @@ class PlayerAttribute(StrEnum):
|
|
199
210
|
|
200
211
|
class DeviceAttribute(StrEnum):
|
201
212
|
"""Defines the device attributes."""
|
213
|
+
|
202
214
|
UUID = "uuid"
|
203
215
|
DEVICE_NAME = "DeviceName"
|
204
216
|
GROUP_NAME = "GroupName"
|
@@ -282,6 +294,7 @@ class DeviceAttribute(StrEnum):
|
|
282
294
|
|
283
295
|
class MultiroomAttribute(StrEnum):
|
284
296
|
"""Defines the player attributes."""
|
297
|
+
|
285
298
|
NUM_FOLLOWERS = "slaves"
|
286
299
|
FOLLOWER_LIST = "slave_list"
|
287
300
|
UUID = "uuid"
|
@@ -4,7 +4,7 @@ from linkplay.bridge import LinkPlayBridge, LinkPlayMultiroom
|
|
4
4
|
from linkplay.discovery import discover_linkplay_bridges
|
5
5
|
|
6
6
|
|
7
|
-
class LinkPlayController
|
7
|
+
class LinkPlayController:
|
8
8
|
"""Represents a LinkPlay controller to manage the devices and multirooms."""
|
9
9
|
|
10
10
|
session: ClientSession
|
@@ -22,7 +22,11 @@ class LinkPlayController():
|
|
22
22
|
# Discover new bridges
|
23
23
|
discovered_bridges = await discover_linkplay_bridges(self.session)
|
24
24
|
current_bridges = [bridge.device.uuid for bridge in self.bridges]
|
25
|
-
new_bridges = [
|
25
|
+
new_bridges = [
|
26
|
+
discovered_bridge
|
27
|
+
for discovered_bridge in discovered_bridges
|
28
|
+
if discovered_bridge.device.uuid not in current_bridges
|
29
|
+
]
|
26
30
|
self.bridges.extend(new_bridges)
|
27
31
|
|
28
32
|
async def discover_multirooms(self) -> None:
|
@@ -31,7 +35,9 @@ class LinkPlayController():
|
|
31
35
|
# Create new multirooms from new bridges
|
32
36
|
new_multirooms = []
|
33
37
|
for bridge in self.bridges:
|
34
|
-
has_multiroom = any(
|
38
|
+
has_multiroom = any(
|
39
|
+
multiroom for multiroom in self.multirooms if multiroom.leader == bridge
|
40
|
+
)
|
35
41
|
|
36
42
|
if has_multiroom:
|
37
43
|
continue
|
@@ -46,7 +52,9 @@ class LinkPlayController():
|
|
46
52
|
await multiroom.update_status(self.bridges)
|
47
53
|
|
48
54
|
# Remove multirooms if they have no followers
|
49
|
-
empty_multirooms = [
|
55
|
+
empty_multirooms = [
|
56
|
+
multiroom for multiroom in self.multirooms if not multiroom.followers
|
57
|
+
]
|
50
58
|
for empty_multiroom in empty_multirooms:
|
51
59
|
self.multirooms.remove(empty_multiroom)
|
52
60
|
|
@@ -9,7 +9,9 @@ from linkplay.bridge import LinkPlayBridge
|
|
9
9
|
from linkplay.exceptions import LinkPlayRequestException
|
10
10
|
|
11
11
|
|
12
|
-
async def linkplay_factory_bridge(
|
12
|
+
async def linkplay_factory_bridge(
|
13
|
+
ip_address: str, session: ClientSession
|
14
|
+
) -> LinkPlayBridge | None:
|
13
15
|
"""Attempts to create a LinkPlayBridge from the given IP address.
|
14
16
|
Returns None if the device is not an expected LinkPlay device."""
|
15
17
|
bridge = LinkPlayBridge("http", ip_address, session)
|
@@ -21,12 +23,14 @@ async def linkplay_factory_bridge(ip_address: str, session: ClientSession) -> Li
|
|
21
23
|
return bridge
|
22
24
|
|
23
25
|
|
24
|
-
async def discover_linkplay_bridges(
|
26
|
+
async def discover_linkplay_bridges(
|
27
|
+
session: ClientSession, discovery_through_multiroom: bool = True
|
28
|
+
) -> list[LinkPlayBridge]:
|
25
29
|
"""Attempts to discover LinkPlay devices on the local network."""
|
26
30
|
bridges: dict[str, LinkPlayBridge] = {}
|
27
31
|
|
28
32
|
async def add_linkplay_device_to_list(upnp_device: CaseInsensitiveDict):
|
29
|
-
ip_address: str | None = upnp_device.get(
|
33
|
+
ip_address: str | None = upnp_device.get("_host")
|
30
34
|
|
31
35
|
if not ip_address:
|
32
36
|
return
|
@@ -35,8 +39,7 @@ async def discover_linkplay_bridges(session: ClientSession, discovery_through_mu
|
|
35
39
|
bridges[bridge.device.uuid] = bridge
|
36
40
|
|
37
41
|
await async_search(
|
38
|
-
search_target=UPNP_DEVICE_TYPE,
|
39
|
-
async_callback=add_linkplay_device_to_list
|
42
|
+
search_target=UPNP_DEVICE_TYPE, async_callback=add_linkplay_device_to_list
|
40
43
|
)
|
41
44
|
|
42
45
|
# Discover additional bridges through grouped multirooms
|
@@ -51,17 +54,22 @@ async def discover_linkplay_bridges(session: ClientSession, discovery_through_mu
|
|
51
54
|
return list(bridges.values())
|
52
55
|
|
53
56
|
|
54
|
-
async def discover_bridges_through_multiroom(
|
55
|
-
|
57
|
+
async def discover_bridges_through_multiroom(
|
58
|
+
bridge: LinkPlayBridge, session: ClientSession
|
59
|
+
) -> list[LinkPlayBridge]:
|
56
60
|
"""Discovers bridges through the multiroom of the provided bridge."""
|
57
|
-
properties: dict[Any, Any] = await bridge.json_request(
|
61
|
+
properties: dict[Any, Any] = await bridge.json_request(
|
62
|
+
LinkPlayCommand.MULTIROOM_LIST
|
63
|
+
)
|
58
64
|
|
59
65
|
if int(properties[MultiroomAttribute.NUM_FOLLOWERS]) == 0:
|
60
66
|
return []
|
61
67
|
|
62
68
|
followers: list[LinkPlayBridge] = []
|
63
69
|
for follower in properties[MultiroomAttribute.FOLLOWER_LIST]:
|
64
|
-
if new_bridge := await linkplay_factory_bridge(
|
70
|
+
if new_bridge := await linkplay_factory_bridge(
|
71
|
+
follower[MultiroomAttribute.IP], session
|
72
|
+
):
|
65
73
|
followers.append(new_bridge)
|
66
74
|
|
67
75
|
return followers
|
@@ -34,19 +34,24 @@ async def session_call_api(endpoint: str, session: ClientSession, command: str)
|
|
34
34
|
raise LinkPlayRequestException(f"Error requesting data from '{url}'") from error
|
35
35
|
|
36
36
|
if response.status != HTTPStatus.OK:
|
37
|
-
raise LinkPlayRequestException(
|
37
|
+
raise LinkPlayRequestException(
|
38
|
+
f"Unexpected HTTPStatus {response.status} received from '{url}'"
|
39
|
+
)
|
38
40
|
|
39
41
|
return await response.text()
|
40
42
|
|
41
43
|
|
42
|
-
async def session_call_api_json(
|
43
|
-
|
44
|
+
async def session_call_api_json(
|
45
|
+
endpoint: str, session: ClientSession, command: str
|
46
|
+
) -> Dict[str, str]:
|
44
47
|
"""Calls the LinkPlay API and returns the result as a JSON object."""
|
45
48
|
result = await session_call_api(endpoint, session, command)
|
46
49
|
return json.loads(result) # type: ignore
|
47
50
|
|
48
51
|
|
49
|
-
async def session_call_api_ok(
|
52
|
+
async def session_call_api_ok(
|
53
|
+
endpoint: str, session: ClientSession, command: str
|
54
|
+
) -> None:
|
50
55
|
"""Calls the LinkPlay API and checks if the response is OK. Throws exception if not."""
|
51
56
|
result = await session_call_api(endpoint, session, command)
|
52
57
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: python_linkplay
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.6
|
4
4
|
Summary: A Python Library for Seamless LinkPlay Device Control
|
5
5
|
Author: Velleman Group nv
|
6
6
|
License: MIT
|
@@ -17,7 +17,7 @@ Requires-Dist: pytest-cov>=4.1.0; extra == "testing"
|
|
17
17
|
Requires-Dist: pytest-mock>=3.10.0; extra == "testing"
|
18
18
|
Requires-Dist: pytest-asyncio>=0.23.3; extra == "testing"
|
19
19
|
Requires-Dist: mypy>=1.3.0; extra == "testing"
|
20
|
-
Requires-Dist:
|
20
|
+
Requires-Dist: ruff>=0.5.4; extra == "testing"
|
21
21
|
Requires-Dist: tox>=4.6.0; extra == "testing"
|
22
22
|
Requires-Dist: typing-extensions>=4.6.3; extra == "testing"
|
23
23
|
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = '0.0.5'
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{python_linkplay-0.0.5 → python_linkplay-0.0.6}/src/python_linkplay.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|