python-linkplay 0.0.11__tar.gz → 0.0.14__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.
Files changed (23) hide show
  1. {python_linkplay-0.0.11/src/python_linkplay.egg-info → python_linkplay-0.0.14}/PKG-INFO +1 -1
  2. python_linkplay-0.0.14/src/linkplay/__version__.py +1 -0
  3. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/bridge.py +28 -4
  4. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/consts.py +4 -0
  5. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/controller.py +24 -19
  6. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/utils.py +0 -2
  7. {python_linkplay-0.0.11 → python_linkplay-0.0.14/src/python_linkplay.egg-info}/PKG-INFO +1 -1
  8. python_linkplay-0.0.11/src/linkplay/__version__.py +0 -1
  9. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/LICENSE +0 -0
  10. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/README.md +0 -0
  11. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/pyproject.toml +0 -0
  12. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/setup.cfg +0 -0
  13. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/setup.py +0 -0
  14. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/__init__.py +0 -0
  15. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/__main__.py +0 -0
  16. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/discovery.py +0 -0
  17. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/endpoint.py +0 -0
  18. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/linkplay/exceptions.py +0 -0
  19. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/python_linkplay.egg-info/SOURCES.txt +0 -0
  20. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/python_linkplay.egg-info/dependency_links.txt +0 -0
  21. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/python_linkplay.egg-info/not-zip-safe +0 -0
  22. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/src/python_linkplay.egg-info/requires.txt +0 -0
  23. {python_linkplay-0.0.11 → python_linkplay-0.0.14}/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.11
3
+ Version: 0.0.14
4
4
  Summary: A Python Library for Seamless LinkPlay Device Control
5
5
  Author: Velleman Group nv
6
6
  License: MIT
@@ -0,0 +1 @@
1
+ __version__ = '0.0.14'
@@ -4,6 +4,7 @@ from typing import Any
4
4
 
5
5
  from linkplay.consts import (
6
6
  INPUT_MODE_MAP,
7
+ LOGGER,
7
8
  PLAY_MODE_SEND_MAP,
8
9
  ChannelType,
9
10
  DeviceAttribute,
@@ -130,6 +131,11 @@ class LinkPlayPlayer:
130
131
  await self.bridge.request(LinkPlayCommand.PAUSE)
131
132
  self.properties[PlayerAttribute.PLAYING_STATUS] = PlayingStatus.PAUSED
132
133
 
134
+ async def stop(self) -> None:
135
+ """Stop the current playing track and remove the selected source."""
136
+ await self.bridge.request(LinkPlayCommand.STOP)
137
+ self.properties[PlayerAttribute.PLAYING_STATUS] = PlayingStatus.STOPPED
138
+
133
139
  async def toggle(self) -> None:
134
140
  """Start playing if the player is currently not playing. Stops playing if it is."""
135
141
  await self.bridge.request(LinkPlayCommand.TOGGLE)
@@ -230,9 +236,12 @@ class LinkPlayPlayer:
230
236
  @property
231
237
  def play_mode(self) -> PlayingMode:
232
238
  """Returns the current playing mode of the player."""
233
- return PlayingMode(
234
- self.properties.get(PlayerAttribute.PLAYBACK_MODE, PlayingMode.IDLE)
235
- )
239
+ try:
240
+ return PlayingMode(
241
+ self.properties.get(PlayerAttribute.PLAYBACK_MODE, PlayingMode.IDLE)
242
+ )
243
+ except ValueError:
244
+ return PlayingMode(PlayingMode.IDLE)
236
245
 
237
246
  @property
238
247
  def loop_mode(self) -> LoopMode:
@@ -250,11 +259,13 @@ class LinkPlayBridge:
250
259
  endpoint: LinkPlayEndpoint
251
260
  device: LinkPlayDevice
252
261
  player: LinkPlayPlayer
262
+ multiroom: LinkPlayMultiroom | None
253
263
 
254
264
  def __init__(self, *, endpoint: LinkPlayEndpoint):
255
265
  self.endpoint = endpoint
256
266
  self.device = LinkPlayDevice(self)
257
267
  self.player = LinkPlayPlayer(self)
268
+ self.multiroom = None
258
269
 
259
270
  def __str__(self) -> str:
260
271
  if self.device.name == "":
@@ -268,14 +279,20 @@ class LinkPlayBridge:
268
279
  "endpoint": self.endpoint.to_dict(),
269
280
  "device": self.device.to_dict(),
270
281
  "player": self.player.to_dict(),
282
+ "multiroom": self.multiroom.to_dict() if self.multiroom else None,
271
283
  }
272
284
 
273
285
  async def json_request(self, command: str) -> dict[str, str]:
274
286
  """Performs a GET request on the given command and returns the result as a JSON object."""
275
- return await self.endpoint.json_request(command)
287
+ LOGGER.debug(str.format("Request {} at {}", command, self.endpoint))
288
+ response = await self.endpoint.json_request(command)
289
+ LOGGER.debug(str.format("Response {}: {}", command, response))
290
+ return response
276
291
 
277
292
  async def request(self, command: str) -> None:
278
293
  """Performs a GET request on the given command and verifies the result."""
294
+
295
+ LOGGER.debug(str.format("Request command at {}: {}", self.endpoint, command))
279
296
  await self.endpoint.request(command)
280
297
 
281
298
 
@@ -290,6 +307,13 @@ class LinkPlayMultiroom:
290
307
  self.leader = leader
291
308
  self.followers = []
292
309
 
310
+ def to_dict(self):
311
+ """Return the state of the LinkPlayMultiroom."""
312
+ return {
313
+ "leader": self.leader.to_dict(),
314
+ "followers": [follower.to_dict() for follower in self.followers],
315
+ }
316
+
293
317
  async def update_status(self, bridges: list[LinkPlayBridge]) -> None:
294
318
  """Updates the multiroom status."""
295
319
  properties: dict[Any, Any] = await self.leader.json_request(
@@ -1,5 +1,8 @@
1
+ import logging
1
2
  from enum import IntFlag, StrEnum
2
3
 
4
+ LOGGER = logging.getLogger("linkplay")
5
+
3
6
  API_ENDPOINT: str = "{}/httpapi.asp?command={}"
4
7
  API_TIMEOUT: int = 2
5
8
  UNKNOWN_TRACK_PLAYING: str = "Unknown"
@@ -79,6 +82,7 @@ class LinkPlayCommand(StrEnum):
79
82
  VOLUME = "setPlayerCmd:vol:{}"
80
83
  PLAYLIST = "setPlayerCmd:playlist:uri:{}"
81
84
  PAUSE = "setPlayerCmd:pause"
85
+ STOP = "setPlayerCmd:stop"
82
86
  TOGGLE = "setPlayerCmd:onepause"
83
87
  EQUALIZER_MODE = "setPlayerCmd:equalizer:{}"
84
88
  LOOP_MODE = "setPlayerCmd:loopmode:{}"
@@ -40,31 +40,36 @@ class LinkPlayController:
40
40
  async def discover_multirooms(self) -> None:
41
41
  """Attempts to discover multirooms on the local network."""
42
42
 
43
+ # Find and update existing multirooms
44
+ multirooms = [bridge.multiroom for bridge in self.bridges if bridge.multiroom]
45
+
46
+ removed_multirooms = []
47
+ for multiroom in multirooms:
48
+ for follower in multiroom.followers:
49
+ follower.multiroom = None
50
+ await multiroom.update_status(self.bridges)
51
+ if len(multiroom.followers) > 0:
52
+ for follower in multiroom.followers:
53
+ follower.multiroom = multiroom
54
+ else:
55
+ multiroom.leader.multiroom = None
56
+ removed_multirooms.append(multiroom)
57
+
43
58
  # Create new multirooms from new bridges
44
- new_multirooms = []
45
59
  for bridge in self.bridges:
46
- has_multiroom = any(
47
- multiroom for multiroom in self.multirooms if multiroom.leader == bridge
48
- )
49
-
50
- if has_multiroom:
60
+ if bridge.multiroom:
51
61
  continue
52
62
 
53
63
  multiroom = LinkPlayMultiroom(bridge)
54
64
  await multiroom.update_status(self.bridges)
55
65
  if len(multiroom.followers) > 0:
56
- new_multirooms.append(multiroom)
57
-
58
- # Update existing multirooms
59
- for multiroom in self.multirooms:
60
- await multiroom.update_status(self.bridges)
66
+ multirooms.append(multiroom)
67
+ bridge.multiroom = multiroom
68
+ for follower in multiroom.followers:
69
+ follower.multiroom = multiroom
61
70
 
62
- # Remove multirooms if they have no followers
63
- empty_multirooms = [
64
- multiroom for multiroom in self.multirooms if not multiroom.followers
65
- ]
66
- for empty_multiroom in empty_multirooms:
67
- self.multirooms.remove(empty_multiroom)
71
+ # Remove multirooms with no followers
72
+ multirooms = [item for item in multirooms if item not in removed_multirooms]
68
73
 
69
- # Add new multirooms
70
- self.multirooms.extend(new_multirooms)
74
+ # Update multirooms in controller
75
+ self.multirooms = multirooms
@@ -24,8 +24,6 @@ from linkplay.consts import (
24
24
  )
25
25
  from linkplay.exceptions import LinkPlayRequestException
26
26
 
27
- _LOGGER = logging.getLogger(__name__)
28
-
29
27
 
30
28
  async def session_call_api(endpoint: str, session: ClientSession, command: str) -> str:
31
29
  """Calls the LinkPlay API and returns the result as a string.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python_linkplay
3
- Version: 0.0.11
3
+ Version: 0.0.14
4
4
  Summary: A Python Library for Seamless LinkPlay Device Control
5
5
  Author: Velleman Group nv
6
6
  License: MIT
@@ -1 +0,0 @@
1
- __version__ = '0.0.11'