lametric-py 1.0.2__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Caspar Weber
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,189 @@
1
+ Metadata-Version: 2.4
2
+ Name: lametric-py
3
+ Version: 1.0.2
4
+ Author: Caspar Weber
5
+ License: MIT
6
+ Requires-Python: >=3.14
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: yarl>=1.0.0
10
+ Requires-Dist: aiohttp>=3.0.0
11
+ Requires-Dist: backoff>=2.0.0
12
+ Requires-Dist: mashumaro>=3.0.0
13
+ Requires-Dist: awesomeversion>=25.0.0
14
+ Requires-Dist: orjson>=3.0.0
15
+ Dynamic: license-file
16
+
17
+ # lametric-py
18
+
19
+ Async Python client for LaMetric devices and the LaMetric cloud API.
20
+
21
+ The package wraps the documented local device API, notification models, and the
22
+ LaMetric Streaming Protocol (LMSP) used by LaMetric SKY devices.
23
+
24
+ ## Features
25
+
26
+ - Async local device client for display, audio, Bluetooth, apps, notifications, and streaming
27
+ - Async cloud client for account and device lookup
28
+ - Typed dataclass models for API payloads and responses
29
+ - Notification builders for text, goal, and chart frames
30
+ - LMSP helpers for starting a stream and sending RGB888 UDP frames
31
+
32
+ ## Requirements
33
+
34
+ - Python 3.14+
35
+ - A LaMetric device on the local network for device API features
36
+ - A LaMetric developer token for cloud API features
37
+
38
+ ## Installation
39
+
40
+ This repository is currently set up as a source install.
41
+
42
+ ```bash
43
+ git clone <repository-url>
44
+ cd lametric-py
45
+ uv sync --dev
46
+ ```
47
+
48
+ If you prefer `pip`:
49
+
50
+ ```bash
51
+ pip install -e .
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ### Local Device Client
57
+
58
+ ```python
59
+ import asyncio
60
+
61
+ from src import LaMetricDevice
62
+
63
+
64
+ async def main() -> None:
65
+ async with LaMetricDevice(host="192.168.1.42", api_key="device-api-key") as device:
66
+ state = await device.state
67
+ print(state.name)
68
+
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ### Send a Notification
74
+
75
+ ```python
76
+ import asyncio
77
+
78
+ from src import (
79
+ BuiltinSound,
80
+ LaMetricDevice,
81
+ Notification,
82
+ NotificationData,
83
+ NotificationPriority,
84
+ NotificationSound,
85
+ SimpleFrame,
86
+ )
87
+
88
+
89
+ async def main() -> None:
90
+ notification = Notification(
91
+ priority=NotificationPriority.INFO,
92
+ model=NotificationData(
93
+ frames=[SimpleFrame(text="Deploy finished")],
94
+ sound=BuiltinSound(id=NotificationSound.POSITIVE1),
95
+ ),
96
+ )
97
+
98
+ async with LaMetricDevice(host="192.168.1.42", api_key="device-api-key") as device:
99
+ notification_id = await device.send_notification(notification)
100
+ print(notification_id)
101
+
102
+
103
+ asyncio.run(main())
104
+ ```
105
+
106
+ ### Query the Cloud API
107
+
108
+ ```python
109
+ import asyncio
110
+
111
+ from src import LaMetricCloud
112
+
113
+
114
+ async def main() -> None:
115
+ async with LaMetricCloud(token="developer-token") as cloud:
116
+ user = await cloud.current_user
117
+ devices = await cloud.devices
118
+ print(user.email, len(devices))
119
+
120
+
121
+ asyncio.run(main())
122
+ ```
123
+
124
+ ## Streaming Example
125
+
126
+ ```python
127
+ import asyncio
128
+
129
+ from src import (
130
+ CanvasFillType,
131
+ CanvasPostProcess,
132
+ CanvasPostProcessType,
133
+ CanvasRenderMode,
134
+ LaMetricDevice,
135
+ StreamConfig,
136
+ )
137
+
138
+
139
+ async def main() -> None:
140
+ config = StreamConfig(
141
+ fill_type=CanvasFillType.SCALE,
142
+ render_mode=CanvasRenderMode.PIXEL,
143
+ post_process=CanvasPostProcess(type=CanvasPostProcessType.NONE),
144
+ )
145
+
146
+ async with LaMetricDevice(host="192.168.1.42", api_key="device-api-key") as device:
147
+ session_id = await device.start_stream(config)
148
+ if session_id is None:
149
+ return
150
+
151
+ stream_state = await device.stream_state
152
+ frame = bytes(
153
+ [255, 0, 0]
154
+ * (stream_state.canvas.pixel.size.width * stream_state.canvas.pixel.size.height)
155
+ )
156
+ await device.send_stream_data(session_id, frame)
157
+ await device.stop_stream()
158
+
159
+
160
+ asyncio.run(main())
161
+ ```
162
+
163
+ ## Public API
164
+
165
+ The package currently re-exports its public surface from `src`.
166
+
167
+ - Clients: `LaMetricDevice`, `LaMetricCloud`
168
+ - Exceptions: `LaMetricApiError`, `LaMetricConnectionError`, `LaMetricAuthenticationError`, `LaMetricUnsupportedError`
169
+ - Models: apps, notifications, state payloads, stream config/state types
170
+ - Enums: notification, screensaver, display, and streaming constants
171
+
172
+ ## Documentation
173
+
174
+ - `docs/usage.md` for the local device and cloud clients
175
+ - `docs/streaming.md` for LMSP details and the packet format used by `send_stream_data`
176
+ - `docs/development.md` for local development, linting, typing, and tests
177
+
178
+ ## Development
179
+
180
+ Useful commands:
181
+
182
+ ```bash
183
+ uv run ruff check .
184
+ uv run ruff format .
185
+ uv run mypy .
186
+ uv run pytest
187
+ ```
188
+
189
+ The CI workflow runs Ruff, mypy, pytest, coverage export, and semantic-release.
@@ -0,0 +1,173 @@
1
+ # lametric-py
2
+
3
+ Async Python client for LaMetric devices and the LaMetric cloud API.
4
+
5
+ The package wraps the documented local device API, notification models, and the
6
+ LaMetric Streaming Protocol (LMSP) used by LaMetric SKY devices.
7
+
8
+ ## Features
9
+
10
+ - Async local device client for display, audio, Bluetooth, apps, notifications, and streaming
11
+ - Async cloud client for account and device lookup
12
+ - Typed dataclass models for API payloads and responses
13
+ - Notification builders for text, goal, and chart frames
14
+ - LMSP helpers for starting a stream and sending RGB888 UDP frames
15
+
16
+ ## Requirements
17
+
18
+ - Python 3.14+
19
+ - A LaMetric device on the local network for device API features
20
+ - A LaMetric developer token for cloud API features
21
+
22
+ ## Installation
23
+
24
+ This repository is currently set up as a source install.
25
+
26
+ ```bash
27
+ git clone <repository-url>
28
+ cd lametric-py
29
+ uv sync --dev
30
+ ```
31
+
32
+ If you prefer `pip`:
33
+
34
+ ```bash
35
+ pip install -e .
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ### Local Device Client
41
+
42
+ ```python
43
+ import asyncio
44
+
45
+ from src import LaMetricDevice
46
+
47
+
48
+ async def main() -> None:
49
+ async with LaMetricDevice(host="192.168.1.42", api_key="device-api-key") as device:
50
+ state = await device.state
51
+ print(state.name)
52
+
53
+
54
+ asyncio.run(main())
55
+ ```
56
+
57
+ ### Send a Notification
58
+
59
+ ```python
60
+ import asyncio
61
+
62
+ from src import (
63
+ BuiltinSound,
64
+ LaMetricDevice,
65
+ Notification,
66
+ NotificationData,
67
+ NotificationPriority,
68
+ NotificationSound,
69
+ SimpleFrame,
70
+ )
71
+
72
+
73
+ async def main() -> None:
74
+ notification = Notification(
75
+ priority=NotificationPriority.INFO,
76
+ model=NotificationData(
77
+ frames=[SimpleFrame(text="Deploy finished")],
78
+ sound=BuiltinSound(id=NotificationSound.POSITIVE1),
79
+ ),
80
+ )
81
+
82
+ async with LaMetricDevice(host="192.168.1.42", api_key="device-api-key") as device:
83
+ notification_id = await device.send_notification(notification)
84
+ print(notification_id)
85
+
86
+
87
+ asyncio.run(main())
88
+ ```
89
+
90
+ ### Query the Cloud API
91
+
92
+ ```python
93
+ import asyncio
94
+
95
+ from src import LaMetricCloud
96
+
97
+
98
+ async def main() -> None:
99
+ async with LaMetricCloud(token="developer-token") as cloud:
100
+ user = await cloud.current_user
101
+ devices = await cloud.devices
102
+ print(user.email, len(devices))
103
+
104
+
105
+ asyncio.run(main())
106
+ ```
107
+
108
+ ## Streaming Example
109
+
110
+ ```python
111
+ import asyncio
112
+
113
+ from src import (
114
+ CanvasFillType,
115
+ CanvasPostProcess,
116
+ CanvasPostProcessType,
117
+ CanvasRenderMode,
118
+ LaMetricDevice,
119
+ StreamConfig,
120
+ )
121
+
122
+
123
+ async def main() -> None:
124
+ config = StreamConfig(
125
+ fill_type=CanvasFillType.SCALE,
126
+ render_mode=CanvasRenderMode.PIXEL,
127
+ post_process=CanvasPostProcess(type=CanvasPostProcessType.NONE),
128
+ )
129
+
130
+ async with LaMetricDevice(host="192.168.1.42", api_key="device-api-key") as device:
131
+ session_id = await device.start_stream(config)
132
+ if session_id is None:
133
+ return
134
+
135
+ stream_state = await device.stream_state
136
+ frame = bytes(
137
+ [255, 0, 0]
138
+ * (stream_state.canvas.pixel.size.width * stream_state.canvas.pixel.size.height)
139
+ )
140
+ await device.send_stream_data(session_id, frame)
141
+ await device.stop_stream()
142
+
143
+
144
+ asyncio.run(main())
145
+ ```
146
+
147
+ ## Public API
148
+
149
+ The package currently re-exports its public surface from `src`.
150
+
151
+ - Clients: `LaMetricDevice`, `LaMetricCloud`
152
+ - Exceptions: `LaMetricApiError`, `LaMetricConnectionError`, `LaMetricAuthenticationError`, `LaMetricUnsupportedError`
153
+ - Models: apps, notifications, state payloads, stream config/state types
154
+ - Enums: notification, screensaver, display, and streaming constants
155
+
156
+ ## Documentation
157
+
158
+ - `docs/usage.md` for the local device and cloud clients
159
+ - `docs/streaming.md` for LMSP details and the packet format used by `send_stream_data`
160
+ - `docs/development.md` for local development, linting, typing, and tests
161
+
162
+ ## Development
163
+
164
+ Useful commands:
165
+
166
+ ```bash
167
+ uv run ruff check .
168
+ uv run ruff format .
169
+ uv run mypy .
170
+ uv run pytest
171
+ ```
172
+
173
+ The CI workflow runs Ruff, mypy, pytest, coverage export, and semantic-release.
@@ -0,0 +1,79 @@
1
+ [project]
2
+ name = "lametric-py"
3
+ version = "1.0.2"
4
+ description = ""
5
+ authors = [
6
+ { name = "Caspar Weber" }
7
+ ]
8
+ dependencies = [
9
+ "yarl>=1.0.0",
10
+ "aiohttp>=3.0.0",
11
+ "backoff>=2.0.0",
12
+ "mashumaro>=3.0.0",
13
+ "awesomeversion>=25.0.0",
14
+ "orjson>=3.0.0",
15
+ ]
16
+ requires-python = ">=3.14"
17
+ readme = "README.md"
18
+ license = { text = "MIT" }
19
+
20
+ # ------------------------------------------------------------------------------
21
+ # UV & DEV DEPENDENCIES
22
+ # ------------------------------------------------------------------------------
23
+ [dependency-groups]
24
+ dev = [
25
+ "pytest>=8.0.0",
26
+ "pytest-cov>=4.1.0",
27
+ "mypy>=1.9.0",
28
+ "ruff>=0.3.0",
29
+ "pre-commit>=3.6.0",
30
+ "python-semantic-release>=9.0.0",
31
+ "commitizen>=3.0.0",
32
+ ]
33
+
34
+ # ------------------------------------------------------------------------------
35
+ # RUFF (Linter & Formatter)
36
+ # ------------------------------------------------------------------------------
37
+ [tool.ruff]
38
+ target-version = "py314"
39
+ line-length = 88
40
+
41
+ [tool.ruff.lint]
42
+ # E: Pycodestyle, F: Pyflakes, I: Isort (Imports), N: Naming, UP: Upgrade, B: Bugbear
43
+ select = ["E", "F", "I", "N", "UP", "B"]
44
+ ignore = []
45
+
46
+ [tool.ruff.format]
47
+ quote-style = "double"
48
+ indent-style = "space"
49
+
50
+ # ------------------------------------------------------------------------------
51
+ # MYPY (Type Checking)
52
+ # ------------------------------------------------------------------------------
53
+ [tool.mypy]
54
+ python_version = "3.14"
55
+ strict = true
56
+ ignore_missing_imports = true
57
+ warn_unused_configs = true
58
+ disallow_untyped_defs = true
59
+
60
+ # ------------------------------------------------------------------------------
61
+ # PYTEST (Testing)
62
+ # ------------------------------------------------------------------------------
63
+ [tool.pytest.ini_options]
64
+ testpaths = ["tests"]
65
+ pythonpath = ["."]
66
+ addopts = "--cov=src --cov-report=term-missing"
67
+
68
+ # ------------------------------------------------------------------------------
69
+ # SEMANTIC RELEASE (Auto-Versioning)
70
+ # ------------------------------------------------------------------------------
71
+ [tool.semantic_release]
72
+ version_toml = ["pyproject.toml:project.version"]
73
+ major_on_zero = false
74
+
75
+ [tool.semantic_release.changelog]
76
+ exclude_commit_patterns = ["chore", "ci"]
77
+
78
+ [tool.semantic_release.branches.main]
79
+ match = "main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,127 @@
1
+ """Public package exports for lametric-py."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ from .cloud import CloudDevice, CloudUser, LaMetricCloud
6
+ from .const import (
7
+ AlarmSound,
8
+ BrightnessMode,
9
+ CanvasFillType,
10
+ CanvasPostProcessEffectType,
11
+ CanvasPostProcessType,
12
+ CanvasRenderMode,
13
+ DeviceModels,
14
+ DeviceModes,
15
+ DisplayType,
16
+ IconType,
17
+ NotificationPriority,
18
+ NotificationSound,
19
+ NotificationType,
20
+ ScreensaverModes,
21
+ SoundCategory,
22
+ )
23
+ from .device import LaMetricDevice
24
+ from .device_apps import App, Parameter, Widget
25
+ from .device_configs import (
26
+ CanvasFadingPixelsEffectParameters,
27
+ CanvasPostProcess,
28
+ CanvasPostProcessParameters,
29
+ ScreensaverConfig,
30
+ ScreensaverConfigParams,
31
+ StreamConfig,
32
+ )
33
+ from .device_notifications import (
34
+ BuiltinSound,
35
+ GoalFrame,
36
+ GoalFrameData,
37
+ Notification,
38
+ NotificationData,
39
+ SimpleFrame,
40
+ SpikeChartFrame,
41
+ WebSound,
42
+ )
43
+ from .device_states import (
44
+ Canvas,
45
+ CanvasArea,
46
+ CanvasSize,
47
+ DeviceAudioState,
48
+ DeviceBluetoothState,
49
+ DeviceDisplayState,
50
+ DeviceSoftwareUpdate,
51
+ DeviceState,
52
+ DeviceWiFiState,
53
+ IntRange,
54
+ Screensaver,
55
+ StreamState,
56
+ TimeBasedScreensaverMode,
57
+ WhenDarkScreensaverMode,
58
+ )
59
+ from .exceptions import (
60
+ LaMetricApiError,
61
+ LaMetricAuthenticationError,
62
+ LaMetricConnectionError,
63
+ LaMetricUnsupportedError,
64
+ )
65
+
66
+ try:
67
+ __version__ = version("lametric-py")
68
+ except PackageNotFoundError:
69
+ __version__ = "0.0.0"
70
+
71
+ __all__ = [
72
+ "AlarmSound",
73
+ "App",
74
+ "BrightnessMode",
75
+ "BuiltinSound",
76
+ "Canvas",
77
+ "CanvasArea",
78
+ "CanvasFadingPixelsEffectParameters",
79
+ "CanvasFillType",
80
+ "CanvasPostProcess",
81
+ "CanvasPostProcessEffectType",
82
+ "CanvasPostProcessParameters",
83
+ "CanvasPostProcessType",
84
+ "CanvasRenderMode",
85
+ "CanvasSize",
86
+ "CloudDevice",
87
+ "CloudUser",
88
+ "DeviceAudioState",
89
+ "DeviceBluetoothState",
90
+ "DeviceDisplayState",
91
+ "DeviceModes",
92
+ "DeviceModels",
93
+ "DeviceSoftwareUpdate",
94
+ "DeviceState",
95
+ "DeviceWiFiState",
96
+ "DisplayType",
97
+ "GoalFrame",
98
+ "GoalFrameData",
99
+ "IconType",
100
+ "IntRange",
101
+ "LaMetricApiError",
102
+ "LaMetricAuthenticationError",
103
+ "LaMetricCloud",
104
+ "LaMetricConnectionError",
105
+ "LaMetricDevice",
106
+ "LaMetricUnsupportedError",
107
+ "Notification",
108
+ "NotificationData",
109
+ "NotificationPriority",
110
+ "NotificationSound",
111
+ "NotificationType",
112
+ "Parameter",
113
+ "Screensaver",
114
+ "ScreensaverConfig",
115
+ "ScreensaverConfigParams",
116
+ "ScreensaverModes",
117
+ "SimpleFrame",
118
+ "SoundCategory",
119
+ "SpikeChartFrame",
120
+ "StreamConfig",
121
+ "StreamState",
122
+ "TimeBasedScreensaverMode",
123
+ "WebSound",
124
+ "WhenDarkScreensaverMode",
125
+ "Widget",
126
+ "__version__",
127
+ ]