aiolibresync 0.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.
@@ -0,0 +1,86 @@
1
+ """Async client for Libre Wireless LibreSync audio devices.
2
+
3
+ import asyncio
4
+ from aiolibresync import DeviceState, LibreSyncClient
5
+
6
+ async def main() -> None:
7
+ client = LibreSyncClient("192.168.1.50")
8
+ ready = asyncio.Event()
9
+
10
+ def on_state(state: DeviceState) -> None:
11
+ print(state)
12
+ if state.available:
13
+ ready.set()
14
+
15
+ client.subscribe(on_state)
16
+ await client.async_connect()
17
+ await ready.wait()
18
+ await client.async_set_power(True)
19
+ await asyncio.sleep(60)
20
+ await client.async_disconnect()
21
+
22
+ asyncio.run(main())
23
+
24
+ `async_connect()` returns as soon as the two sockets have been *scheduled*, not
25
+ once they are open, so a command issued on the next line raises
26
+ `NotConnectedError`. `state.available` is the readiness signal — it turns true
27
+ when both ports are connected — and waiting on it is what the example's
28
+ `asyncio.Event` is for. It goes false again on any disconnection, so a long-
29
+ running program should treat it as a condition, not a one-off.
30
+
31
+ `DeviceState` reports playback twice, because the hub does. `play_state` is the
32
+ streaming renderer's transport and answers `PLAYING` to a physical input with
33
+ nothing connected to it; `audio_state` is whether sound is coming out, from
34
+ `02 21` byte 8, and is right on all ten sources. **Read `state.playback`**,
35
+ which is the second wherever it is known and the first otherwise.
36
+
37
+ Finding a hub, when its address is not already known:
38
+
39
+ from aiolibresync import async_discover
40
+
41
+ for device in await async_discover():
42
+ print(device.host, device.udn, device.name)
43
+
44
+ `async_discover()` sends a multicast search and waits the full timeout — it does
45
+ not return early on the first answer — and `async_probe(host)` performs the same
46
+ identification for an address the user already has. Both are read-only: a
47
+ search, an HTTP GET of the device description, and a TCP connect to 50006 that
48
+ sends nothing.
49
+
50
+ The codec is importable on its own — `aiolibresync.frames`,
51
+ `aiolibresync.systemcontrol`, `aiolibresync.media` — and does no I/O.
52
+ `frames.build_media` is deliberately not re-exported here: its `command`
53
+ argument is a LUCI message-box id, and the box space includes factory reset.
54
+ Use the named operations in `aiolibresync.media` instead.
55
+ """
56
+
57
+ from .client import LibreSyncClient
58
+ from .const import CONTROL_PORT, MEDIA_PORT, POLL_INTERVAL
59
+ from .discovery import DiscoveredDevice, async_discover, async_probe
60
+ from .exceptions import (
61
+ ConfirmationTimeout,
62
+ LibreSyncError,
63
+ NotConnectedError,
64
+ UnsafeCommandError,
65
+ )
66
+ from .state import DeviceState, NowPlaying, PlayState, Source
67
+
68
+ __version__ = "0.2.0"
69
+
70
+ __all__ = [
71
+ "CONTROL_PORT",
72
+ "MEDIA_PORT",
73
+ "POLL_INTERVAL",
74
+ "ConfirmationTimeout",
75
+ "DeviceState",
76
+ "DiscoveredDevice",
77
+ "LibreSyncClient",
78
+ "LibreSyncError",
79
+ "NotConnectedError",
80
+ "NowPlaying",
81
+ "PlayState",
82
+ "Source",
83
+ "UnsafeCommandError",
84
+ "async_discover",
85
+ "async_probe",
86
+ ]