python-linkplay 0.1.0__tar.gz → 0.2.0__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 (24) hide show
  1. {python_linkplay-0.1.0/src/python_linkplay.egg-info → python_linkplay-0.2.0}/PKG-INFO +7 -3
  2. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/README.md +4 -1
  3. python_linkplay-0.2.0/src/linkplay/__version__.py +1 -0
  4. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/bridge.py +117 -13
  5. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/consts.py +30 -5
  6. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/controller.py +17 -0
  7. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/endpoint.py +6 -1
  8. python_linkplay-0.2.0/src/linkplay/manufacturers.py +74 -0
  9. {python_linkplay-0.1.0 → python_linkplay-0.2.0/src/python_linkplay.egg-info}/PKG-INFO +7 -3
  10. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/python_linkplay.egg-info/SOURCES.txt +1 -0
  11. python_linkplay-0.1.0/src/linkplay/__version__.py +0 -1
  12. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/LICENSE +0 -0
  13. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/pyproject.toml +0 -0
  14. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/setup.cfg +0 -0
  15. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/setup.py +0 -0
  16. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/__init__.py +0 -0
  17. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/__main__.py +0 -0
  18. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/discovery.py +0 -0
  19. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/exceptions.py +0 -0
  20. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/linkplay/utils.py +0 -0
  21. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/python_linkplay.egg-info/dependency_links.txt +0 -0
  22. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/python_linkplay.egg-info/not-zip-safe +0 -0
  23. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/python_linkplay.egg-info/requires.txt +0 -0
  24. {python_linkplay-0.1.0 → python_linkplay-0.2.0}/src/python_linkplay.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python_linkplay
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A Python Library for Seamless LinkPlay Device Control
5
5
  Author: Velleman Group nv
6
6
  License: MIT
@@ -24,12 +24,13 @@ Requires-Dist: ruff>=0.5.4; extra == "testing"
24
24
  Requires-Dist: tox>=4.6.0; extra == "testing"
25
25
  Requires-Dist: typing-extensions>=4.6.3; extra == "testing"
26
26
  Requires-Dist: pre-commit>=3.8.0; extra == "testing"
27
-
27
+ Dynamic: license-file
28
28
 
29
29
  [![PyPI package](https://badge.fury.io/py/python-linkplay.svg)](https://pypi.org/project/python-linkplay/)
30
30
  [![Release](https://github.com/velleman/python-linkplay/actions/workflows/release.yaml/badge.svg)](https://github.com/velleman/python-linkplay/actions/workflows/release.yaml)
31
31
 
32
32
  # python-linkplay
33
+
33
34
  A Python Library for Seamless LinkPlay Device Control
34
35
 
35
36
  ## Intro
@@ -54,6 +55,9 @@ Welcome to python-linkplay, a powerful and user-friendly Python library designed
54
55
  - https://developer.arylic.com/httpapi/
55
56
  - http://airscope-audio.net/core2/pdf/airscope-module-http.pdf
56
57
  - https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Mini.pdf
58
+ - https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Products.pdf
59
+ - https://github.com/DanBrezeanu/wiim-extended-http-api
60
+ - https://github.com/cvdlinden/wiim-httpapi
57
61
 
58
62
  ## Multiroom
59
63
 
@@ -1,8 +1,8 @@
1
-
2
1
  [![PyPI package](https://badge.fury.io/py/python-linkplay.svg)](https://pypi.org/project/python-linkplay/)
3
2
  [![Release](https://github.com/velleman/python-linkplay/actions/workflows/release.yaml/badge.svg)](https://github.com/velleman/python-linkplay/actions/workflows/release.yaml)
4
3
 
5
4
  # python-linkplay
5
+
6
6
  A Python Library for Seamless LinkPlay Device Control
7
7
 
8
8
  ## Intro
@@ -27,6 +27,9 @@ Welcome to python-linkplay, a powerful and user-friendly Python library designed
27
27
  - https://developer.arylic.com/httpapi/
28
28
  - http://airscope-audio.net/core2/pdf/airscope-module-http.pdf
29
29
  - https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Mini.pdf
30
+ - https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Products.pdf
31
+ - https://github.com/DanBrezeanu/wiim-extended-http-api
32
+ - https://github.com/cvdlinden/wiim-httpapi
30
33
 
31
34
  ## Multiroom
32
35
 
@@ -0,0 +1 @@
1
+ __version__ = '0.2.0'
@@ -22,6 +22,7 @@ from linkplay.consts import (
22
22
  )
23
23
  from linkplay.endpoint import LinkPlayEndpoint
24
24
  from linkplay.exceptions import LinkPlayInvalidDataException
25
+ from linkplay.manufacturers import MANUFACTURER_WIIM, get_info_from_project
25
26
  from linkplay.utils import fixup_player_properties
26
27
 
27
28
 
@@ -57,6 +58,22 @@ class LinkPlayDevice:
57
58
  """The name of the device."""
58
59
  return self.properties.get(DeviceAttribute.DEVICE_NAME, "")
59
60
 
61
+ @property
62
+ def manufacturer(self) -> str:
63
+ """The manufacturer of the device."""
64
+ manufacturer, _ = get_info_from_project(
65
+ self.properties.get(DeviceAttribute.PROJECT, "")
66
+ )
67
+ return manufacturer
68
+
69
+ @property
70
+ def model(self) -> str:
71
+ """The model of the device."""
72
+ _, model = get_info_from_project(
73
+ self.properties.get(DeviceAttribute.PROJECT, "")
74
+ )
75
+ return model
76
+
60
77
  @property
61
78
  def playmode_support(self) -> list[PlayingMode]:
62
79
  """Returns the player playmode support."""
@@ -69,21 +86,25 @@ class LinkPlayDevice:
69
86
  playing_modes.insert(0, PlayingMode.NETWORK) # always supported
70
87
  return playing_modes
71
88
 
89
+ @property
90
+ def mac(self) -> str | None:
91
+ """Returns the mac address."""
92
+ mac = self.properties.get(DeviceAttribute.ETH_MAC_ADDRESS)
93
+ if mac == "00:00:00:00:00:00" or mac is None:
94
+ mac = self.properties.get(DeviceAttribute.STA_MAC_ADDRESS)
95
+ if mac == "00:00:00:00:00:00" or mac is None:
96
+ mac = self.properties.get(DeviceAttribute.MAC_ADDRESS)
97
+ return mac
98
+
72
99
  @property
73
100
  def eth(self) -> str | None:
74
101
  """Returns the ethernet address."""
75
- eth2 = self.properties.get(DeviceAttribute.ETH2)
76
- eth0 = self.properties.get(DeviceAttribute.ETH0)
77
- for eth in [eth2, eth0]:
78
- if eth == "0.0.0.0":
79
- eth = None
80
- return (
81
- eth2
82
- if eth2
83
- else eth0
84
- if eth0
85
- else self.properties.get(DeviceAttribute.APCLI0)
86
- )
102
+ eth = self.properties.get(DeviceAttribute.ETH2)
103
+ if eth == "0.0.0.0" or eth == "" or eth is None:
104
+ eth = self.properties.get(DeviceAttribute.ETH0)
105
+ if eth == "0.0.0.0" or eth == "" or eth is None:
106
+ eth = self.properties.get(DeviceAttribute.APCLI0)
107
+ return eth
87
108
 
88
109
  async def timesync(self) -> None:
89
110
  """Sync the time."""
@@ -166,7 +187,47 @@ class LinkPlayPlayer:
166
187
 
167
188
  async def set_equalizer_mode(self, mode: EqualizerMode) -> None:
168
189
  """Set the equalizer mode."""
169
- await self.bridge.request(LinkPlayCommand.EQUALIZER_MODE.format(mode)) # type: ignore[str-format]
190
+ if self.bridge.device.manufacturer == MANUFACTURER_WIIM:
191
+ # WiiM devices have a different equalizer mode handling
192
+ # and don't support the general equalizer mode command
193
+ if mode not in self.available_equalizer_modes:
194
+ raise ValueError(
195
+ f"Invalid equalizer mode {mode}. Allowed equalizer modes are {self.available_equalizer_modes}."
196
+ )
197
+
198
+ if mode == EqualizerMode.NONE:
199
+ await self.bridge.json_request(LinkPlayCommand.WIIM_EQUALIZER_OFF)
200
+ else:
201
+ await self.bridge.json_request(
202
+ LinkPlayCommand.WIIM_EQ_LOAD.format(mode)
203
+ )
204
+ # WiiM doesn't update the property after setting it
205
+ self.properties[PlayerAttribute.EQUALIZER_MODE] = mode
206
+ else:
207
+ await self._set_normal_equalizer_mode(mode)
208
+
209
+ async def _set_normal_equalizer_mode(self, mode: EqualizerMode) -> None:
210
+ """Set the equalizer mode."""
211
+ equalizer_mode_as_number = None
212
+ match mode:
213
+ case EqualizerMode.NONE:
214
+ equalizer_mode_as_number = "0"
215
+ case EqualizerMode.CLASSIC:
216
+ equalizer_mode_as_number = "1"
217
+ case EqualizerMode.POP:
218
+ equalizer_mode_as_number = "2"
219
+ case EqualizerMode.JAZZ:
220
+ equalizer_mode_as_number = "3"
221
+ case EqualizerMode.VOCAL:
222
+ equalizer_mode_as_number = "4"
223
+ if equalizer_mode_as_number is None:
224
+ raise ValueError(
225
+ f"Invalid equalizer mode {mode}. Allowed equalizer modes are {self.available_equalizer_modes}."
226
+ )
227
+
228
+ await self.bridge.request(
229
+ LinkPlayCommand.EQUALIZER_MODE.format(equalizer_mode_as_number)
230
+ )
170
231
 
171
232
  async def set_loop_mode(self, mode: LoopMode) -> None:
172
233
  """Set the loop mode."""
@@ -256,10 +317,53 @@ class LinkPlayPlayer:
256
317
  @property
257
318
  def equalizer_mode(self) -> EqualizerMode:
258
319
  """Returns the current equalizer mode."""
320
+ if self.bridge.device.manufacturer == MANUFACTURER_WIIM:
321
+ return EqualizerMode(
322
+ self.properties.get(PlayerAttribute.EQUALIZER_MODE, EqualizerMode.NONE)
323
+ )
324
+
259
325
  return EqualizerMode(
260
326
  self.properties.get(PlayerAttribute.EQUALIZER_MODE, EqualizerMode.CLASSIC)
261
327
  )
262
328
 
329
+ @property
330
+ def available_equalizer_modes(self) -> list[EqualizerMode]:
331
+ """Returns the available equalizer modes."""
332
+ if self.bridge.device.manufacturer == MANUFACTURER_WIIM:
333
+ return [
334
+ EqualizerMode.NONE,
335
+ EqualizerMode.FLAT,
336
+ EqualizerMode.ACOUSTIC,
337
+ EqualizerMode.BASS_BOOSTER,
338
+ EqualizerMode.BASS_REDUCER,
339
+ EqualizerMode.CLASSICAL,
340
+ EqualizerMode.DANCE,
341
+ EqualizerMode.DEEP,
342
+ EqualizerMode.ELECTRONIC,
343
+ EqualizerMode.HIP_HOP,
344
+ EqualizerMode.JAZZ,
345
+ EqualizerMode.LATIN,
346
+ EqualizerMode.LOUDNESS,
347
+ EqualizerMode.LOUNGE,
348
+ EqualizerMode.PIANO,
349
+ EqualizerMode.POP,
350
+ EqualizerMode.R_B,
351
+ EqualizerMode.ROCK,
352
+ EqualizerMode.SMALL_SPEAKERS,
353
+ EqualizerMode.SPOKEN_WORD,
354
+ EqualizerMode.TREBLE_BOOSTER,
355
+ EqualizerMode.TREBLE_REDUCER,
356
+ EqualizerMode.VOCAL_BOOSTER,
357
+ ]
358
+
359
+ return [
360
+ EqualizerMode.NONE,
361
+ EqualizerMode.CLASSIC,
362
+ EqualizerMode.POP,
363
+ EqualizerMode.JAZZ,
364
+ EqualizerMode.VOCAL,
365
+ ]
366
+
263
367
  @property
264
368
  def speaker_type(self) -> SpeakerType:
265
369
  """Returns the current speaker the player is playing on."""
@@ -85,6 +85,8 @@ class LinkPlayCommand(StrEnum):
85
85
  STOP = "setPlayerCmd:stop"
86
86
  TOGGLE = "setPlayerCmd:onepause"
87
87
  EQUALIZER_MODE = "setPlayerCmd:equalizer:{}"
88
+ WIIM_EQUALIZER_ON = "EQOn"
89
+ WIIM_EQUALIZER_OFF = "EQOff"
88
90
  LOOP_MODE = "setPlayerCmd:loopmode:{}"
89
91
  SWITCH_MODE = "setPlayerCmd:switchmode:{}"
90
92
  M3U_PLAYLIST = "setPlayerCmd:m3u:play:{}"
@@ -97,6 +99,7 @@ class LinkPlayCommand(StrEnum):
97
99
  MULTIROOM_JOIN = "ConnectMasterAp:JoinGroupMaster:eth{}:wifi0.0.0.0"
98
100
  PLAY_PRESET = "MCUKeyShortClick:{}"
99
101
  TIMESYNC = "timeSync:{}"
102
+ WIIM_EQ_LOAD = "EQLoad:{}"
100
103
 
101
104
 
102
105
  class LinkPlayTcpUartCommand(StrEnum):
@@ -219,11 +222,33 @@ class LoopMode(StrEnum):
219
222
  class EqualizerMode(StrEnum):
220
223
  """Defines the equalizer mode."""
221
224
 
222
- NONE = "0"
223
- CLASSIC = "1"
224
- POP = "2"
225
- JAZZ = "3"
226
- VOCAL = "4"
225
+ NONE = "None"
226
+ CLASSIC = "Classic"
227
+ POP = "Pop"
228
+ JAZZ = "Jazz"
229
+ VOCAL = "Vocal"
230
+
231
+ # Custom equalizer modes specificly for WiiM devices
232
+ FLAT = "Flat"
233
+ ACOUSTIC = "Acoustic"
234
+ BASS_BOOSTER = "Bass Booster"
235
+ BASS_REDUCER = "Bass Reducer"
236
+ CLASSICAL = "Classical"
237
+ DANCE = "Dance"
238
+ DEEP = "Deep"
239
+ ELECTRONIC = "Electronic"
240
+ HIP_HOP = "Hip-Hop"
241
+ LATIN = "Latin"
242
+ LOUDNESS = "Loudness"
243
+ LOUNGE = "Lounge"
244
+ PIANO = "Piano"
245
+ R_B = "R&B"
246
+ ROCK = "Rock"
247
+ SMALL_SPEAKERS = "Small Speakers"
248
+ SPOKEN_WORD = "Spoken Word"
249
+ TREBLE_BOOSTER = "Treble Booster"
250
+ TREBLE_REDUCER = "Treble Reducer"
251
+ VOCAL_BOOSTER = "Vocal Booster"
227
252
 
228
253
 
229
254
  class PlayingStatus(StrEnum):
@@ -31,6 +31,15 @@ class LinkPlayController:
31
31
  ]
32
32
  self.bridges.extend(new_bridges)
33
33
 
34
+ async def find_bridge(self, bridge_uuid: str) -> LinkPlayBridge | None:
35
+ """Find a LinkPlay device by its bridge uuid."""
36
+
37
+ for bridge in self.bridges:
38
+ if bridge.device.uuid == bridge_uuid:
39
+ return bridge
40
+
41
+ return None
42
+
34
43
  async def add_bridge(self, bridge_to_add: LinkPlayBridge) -> None:
35
44
  """Add given LinkPlay device if not already added."""
36
45
 
@@ -39,6 +48,14 @@ class LinkPlayController:
39
48
  if bridge_to_add.device.uuid not in current_bridges:
40
49
  self.bridges.append(bridge_to_add)
41
50
 
51
+ async def remove_bridge(self, bridge_to_remove: LinkPlayBridge) -> None:
52
+ """Remove given LinkPlay device if not already deleted."""
53
+
54
+ # Remove bridge
55
+ current_bridges = [bridge.device.uuid for bridge in self.bridges]
56
+ if bridge_to_remove.device.uuid in current_bridges:
57
+ self.bridges.remove(bridge_to_remove)
58
+
42
59
  async def discover_multirooms(self) -> None:
43
60
  """Attempts to discover multirooms on the local network."""
44
61
 
@@ -37,7 +37,12 @@ class LinkPlayApiEndpoint(LinkPlayEndpoint):
37
37
  "http",
38
38
  "https",
39
39
  ], "Protocol must be either 'http' or 'https'"
40
- self._endpoint: str = f"{protocol}://{endpoint}:{port}"
40
+ include_port = (protocol == "http" and port != 80) or (
41
+ protocol == "https" and port != 443
42
+ )
43
+ port_suffix = f":{port}" if include_port else ""
44
+ self._endpoint: str = f"{protocol}://{endpoint}{port_suffix}"
45
+
41
46
  self._session: ClientSession = session
42
47
 
43
48
  def to_dict(self):
@@ -0,0 +1,74 @@
1
+ """Manufacturers and their devices for the LinkPlay component."""
2
+
3
+ from typing import Final
4
+
5
+ MANUFACTURER_ARTSOUND: Final[str] = "ArtSound"
6
+ MANUFACTURER_ARYLIC: Final[str] = "Arylic"
7
+ MANUFACTURER_IEAST: Final[str] = "iEAST"
8
+ MANUFACTURER_WIIM: Final[str] = "WiiM"
9
+ MANUFACTURER_GGMM: Final[str] = "GGMM"
10
+ MANUFACTURER_MEDION: Final[str] = "Medion"
11
+ MANUFACTURER_GENERIC: Final[str] = "Generic"
12
+ MODELS_ARTSOUND_SMART_ZONE4: Final[str] = "Smart Zone 4 AMP"
13
+ MODELS_ARTSOUND_SMART_HYDE: Final[str] = "Smart Hyde"
14
+ MODELS_ARYLIC_S50: Final[str] = "S50+"
15
+ MODELS_ARYLIC_S50_PRO: Final[str] = "S50 Pro"
16
+ MODELS_ARYLIC_A30: Final[str] = "A30"
17
+ MODELS_ARYLIC_A50: Final[str] = "A50"
18
+ MODELS_ARYLIC_A50S: Final[str] = "A50+"
19
+ MODELS_ARYLIC_UP2STREAM_AMP: Final[str] = "Up2Stream Amp 2.0"
20
+ MODELS_ARYLIC_UP2STREAM_AMP_2P1: Final[str] = "Up2Stream Amp 2.1"
21
+ MODELS_ARYLIC_UP2STREAM_AMP_V3: Final[str] = "Up2Stream Amp v3"
22
+ MODELS_ARYLIC_UP2STREAM_AMP_V4: Final[str] = "Up2Stream Amp v4"
23
+ MODELS_ARYLIC_UP2STREAM_PRO: Final[str] = "Up2Stream Pro v1"
24
+ MODELS_ARYLIC_UP2STREAM_PRO_V3: Final[str] = "Up2Stream Pro v3"
25
+ MODELS_ARYLIC_S10P: Final[str] = "Arylic S10+"
26
+ MODELS_ARYLIC_UP2STREAM_PLATE_AMP: Final[str] = "Up2Stream Plate Amp"
27
+ MODELS_IEAST_AUDIOCAST_M5: Final[str] = "AudioCast M5"
28
+ MODELS_WIIM_AMP: Final[str] = "WiiM Amp"
29
+ MODELS_WIIM_MINI: Final[str] = "WiiM Mini"
30
+ MODELS_WIIM_PRO: Final[str] = "WiiM Pro"
31
+ MODELS_GGMM_GGMM_E2: Final[str] = "GGMM E2"
32
+ MODELS_MEDION_MD_43970: Final[str] = "Life P66970 (MD 43970)"
33
+ MODELS_GENERIC: Final[str] = "Generic"
34
+
35
+ PROJECTID_LOOKUP: Final[dict[str, tuple[str, str]]] = {
36
+ "SMART_ZONE4_AMP": (MANUFACTURER_ARTSOUND, MODELS_ARTSOUND_SMART_ZONE4),
37
+ "SMART_HYDE": (MANUFACTURER_ARTSOUND, MODELS_ARTSOUND_SMART_HYDE),
38
+ "ARYLIC_S50": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_S50),
39
+ "RP0016_S50PRO_S": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_S50_PRO),
40
+ "RP0011_WB60_S": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_A30),
41
+ "X-50": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_A50),
42
+ "ARYLIC_A50S": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_A50S),
43
+ "RP0011_WB60": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_AMP),
44
+ "UP2STREAM_AMP_V3": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_AMP_V3),
45
+ "UP2STREAM_AMP_V4": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_AMP_V4),
46
+ "UP2STREAM_PRO_V3": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_PRO_V3),
47
+ "S10P_WIFI": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_S10P),
48
+ "ARYLIC_V20": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_PLATE_AMP),
49
+ "UP2STREAM_MINI_V3": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
50
+ "UP2STREAM_AMP_2P1": (MANUFACTURER_ARYLIC, MODELS_ARYLIC_UP2STREAM_AMP_2P1),
51
+ "RP0014_A50C_S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
52
+ "ARYLIC_A30": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
53
+ "ARYLIC_SUBWOOFER": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
54
+ "ARYLIC_S50A": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
55
+ "RP0010_D5_S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
56
+ "RP0001": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
57
+ "RP0013_WA31S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
58
+ "RP0010_D5": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
59
+ "RP0013_WA31S_S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
60
+ "RP0014_A50D_S": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
61
+ "ARYLIC_A50TE": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
62
+ "ARYLIC_A50N": (MANUFACTURER_ARYLIC, MODELS_GENERIC),
63
+ "iEAST-02": (MANUFACTURER_IEAST, MODELS_IEAST_AUDIOCAST_M5),
64
+ "WiiM_Amp_4layer": (MANUFACTURER_WIIM, MODELS_WIIM_AMP),
65
+ "WiiM_Pro_with_gc4a": (MANUFACTURER_WIIM, MODELS_WIIM_PRO),
66
+ "Muzo_Mini": (MANUFACTURER_WIIM, MODELS_WIIM_MINI),
67
+ "GGMM_E2A": (MANUFACTURER_GGMM, MODELS_GGMM_GGMM_E2),
68
+ "A16": (MANUFACTURER_MEDION, MODELS_MEDION_MD_43970),
69
+ }
70
+
71
+
72
+ def get_info_from_project(project: str) -> tuple[str, str]:
73
+ """Get manufacturer and model info based on given project."""
74
+ return PROJECTID_LOOKUP.get(project, (MANUFACTURER_GENERIC, MODELS_GENERIC))
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python_linkplay
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A Python Library for Seamless LinkPlay Device Control
5
5
  Author: Velleman Group nv
6
6
  License: MIT
@@ -24,12 +24,13 @@ Requires-Dist: ruff>=0.5.4; extra == "testing"
24
24
  Requires-Dist: tox>=4.6.0; extra == "testing"
25
25
  Requires-Dist: typing-extensions>=4.6.3; extra == "testing"
26
26
  Requires-Dist: pre-commit>=3.8.0; extra == "testing"
27
-
27
+ Dynamic: license-file
28
28
 
29
29
  [![PyPI package](https://badge.fury.io/py/python-linkplay.svg)](https://pypi.org/project/python-linkplay/)
30
30
  [![Release](https://github.com/velleman/python-linkplay/actions/workflows/release.yaml/badge.svg)](https://github.com/velleman/python-linkplay/actions/workflows/release.yaml)
31
31
 
32
32
  # python-linkplay
33
+
33
34
  A Python Library for Seamless LinkPlay Device Control
34
35
 
35
36
  ## Intro
@@ -54,6 +55,9 @@ Welcome to python-linkplay, a powerful and user-friendly Python library designed
54
55
  - https://developer.arylic.com/httpapi/
55
56
  - http://airscope-audio.net/core2/pdf/airscope-module-http.pdf
56
57
  - https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Mini.pdf
58
+ - https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Products.pdf
59
+ - https://github.com/DanBrezeanu/wiim-extended-http-api
60
+ - https://github.com/cvdlinden/wiim-httpapi
57
61
 
58
62
  ## Multiroom
59
63
 
@@ -12,6 +12,7 @@ src/linkplay/controller.py
12
12
  src/linkplay/discovery.py
13
13
  src/linkplay/endpoint.py
14
14
  src/linkplay/exceptions.py
15
+ src/linkplay/manufacturers.py
15
16
  src/linkplay/utils.py
16
17
  src/python_linkplay.egg-info/PKG-INFO
17
18
  src/python_linkplay.egg-info/SOURCES.txt
@@ -1 +0,0 @@
1
- __version__ = '0.1.0'
File without changes