trmnl 0.1.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.
trmnl-0.1.0/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joost Lekkerkerker
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.
trmnl-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.4
2
+ Name: trmnl
3
+ Version: 0.1.0
4
+ Summary: Asynchronous Python client for TRMNL.
5
+ License: MIT
6
+ License-File: LICENSE.md
7
+ Keywords: TRMNL,api,async,client
8
+ Author: Joost Lekkerkerker
9
+ Author-email: joostlek@outlook.com
10
+ Maintainer: Joost Lekkerkerker
11
+ Maintainer-email: joostlek@outlook.com
12
+ Requires-Python: >=3.13,<4.0
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Natural Language :: English
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Dist: aiohttp (>=3.0.0)
24
+ Requires-Dist: mashumaro (>=3.11,<4.0)
25
+ Requires-Dist: orjson (>=3.9.0)
26
+ Requires-Dist: yarl (>=1.6.0)
27
+ Project-URL: Bug Tracker, https://github.com/joostlek/python-trmnl/issues
28
+ Project-URL: Changelog, https://github.com/joostlek/python-trmnl/releases
29
+ Project-URL: Documentation, https://github.com/joostlek/python-trmnl
30
+ Project-URL: Homepage, https://github.com/joostlek/python-trmnl
31
+ Project-URL: Repository, https://github.com/joostlek/python-trmnl
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Python: TRMNL
35
+
36
+ [![GitHub Release][releases-shield]][releases]
37
+ [![Python Versions][python-versions-shield]][pypi]
38
+ ![Project Stage][project-stage-shield]
39
+ ![Project Maintenance][maintenance-shield]
40
+ [![License][license-shield]](LICENSE.md)
41
+
42
+ [![Build Status][build-shield]][build]
43
+ [![Code Coverage][codecov-shield]][codecov]
44
+
45
+ Asynchronous Python client for TRMNL.
46
+
47
+ ## About
48
+
49
+ This package allows you to control and fetch data from your TRMNL device.
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install trmnl
55
+ ```
56
+
57
+ ## Changelog & Releases
58
+
59
+ This repository keeps a change log using [GitHub's releases][releases]
60
+ functionality. The format of the log is based on
61
+ [Keep a Changelog][keepchangelog].
62
+
63
+ Releases are based on [Semantic Versioning][semver], and use the format
64
+ of `MAJOR.MINOR.PATCH`. In a nutshell, the version will be incremented
65
+ based on the following:
66
+
67
+ - `MAJOR`: Incompatible or major changes.
68
+ - `MINOR`: Backwards-compatible new features and enhancements.
69
+ - `PATCH`: Backwards-compatible bugfixes and package updates.
70
+
71
+ ## Contributing
72
+
73
+ This is an active open-source project. We are always open to people who want to
74
+ use the code or contribute to it.
75
+
76
+ We've set up a separate document for our
77
+ [contribution guidelines](.github/CONTRIBUTING.md).
78
+
79
+ Thank you for being involved! :heart_eyes:
80
+
81
+ ## Setting up development environment
82
+
83
+ This Python project is fully managed using the [Poetry][poetry] dependency manager. But also relies on the use of NodeJS for certain checks during development.
84
+
85
+ You need at least:
86
+
87
+ - Python 3.13+
88
+ - [Poetry][poetry-install]
89
+ - NodeJS 12+ (including NPM)
90
+
91
+ To install all packages, including all development requirements:
92
+
93
+ ```bash
94
+ npm install
95
+ poetry install
96
+ ```
97
+
98
+ As this repository uses the [pre-commit][pre-commit] framework, all changes
99
+ are linted and tested with each commit. You can run all checks and tests
100
+ manually, using the following command:
101
+
102
+ ```bash
103
+ poetry run pre-commit run --all-files
104
+ ```
105
+
106
+ To run just the Python tests:
107
+
108
+ ```bash
109
+ poetry run pytest
110
+ ```
111
+
112
+ ## Authors & contributors
113
+
114
+ The content is by [Joost Lekkerkerker][joostlek].
115
+
116
+ For a full list of all authors and contributors,
117
+ check [the contributor's page][contributors].
118
+
119
+ ## License
120
+
121
+ MIT License
122
+
123
+ Copyright (c) 2026 Joost Lekkerkerker
124
+
125
+ Permission is hereby granted, free of charge, to any person obtaining a copy
126
+ of this software and associated documentation files (the "Software"), to deal
127
+ in the Software without restriction, including without limitation the rights
128
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
129
+ copies of the Software, and to permit persons to whom the Software is
130
+ furnished to do so, subject to the following conditions:
131
+
132
+ The above copyright notice and this permission notice shall be included in all
133
+ copies or substantial portions of the Software.
134
+
135
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
136
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
137
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
138
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
139
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
140
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
141
+ SOFTWARE.
142
+
143
+ [build-shield]: https://github.com/joostlek/python-trmnl/actions/workflows/tests.yaml/badge.svg
144
+ [build]: https://github.com/joostlek/python-trmnl/actions
145
+ [codecov-shield]: https://codecov.io/gh/joostlek/python-trmnl/branch/master/graph/badge.svg
146
+ [codecov]: https://codecov.io/gh/joostlek/python-trmnl
147
+ [commits-shield]: https://img.shields.io/github/commit-activity/y/joostlek/python-trmnl.svg
148
+ [commits]: https://github.com/joostlek/python-trmnl/commits/master
149
+ [contributors]: https://github.com/joostlek/python-trmnl/graphs/contributors
150
+ [joostlek]: https://github.com/joostlek
151
+ [keepchangelog]: http://keepachangelog.com/en/1.0.0/
152
+ [license-shield]: https://img.shields.io/github/license/joostlek/python-trmnl.svg
153
+ [maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg
154
+ [poetry-install]: https://python-poetry.org/docs/#installation
155
+ [poetry]: https://python-poetry.org
156
+ [pre-commit]: https://pre-commit.com/
157
+ [project-stage-shield]: https://img.shields.io/badge/project%20stage-stable-green.svg
158
+ [python-versions-shield]: https://img.shields.io/pypi/pyversions/trmnl
159
+ [releases-shield]: https://img.shields.io/github/release/joostlek/python-trmnl.svg
160
+ [releases]: https://github.com/joostlek/python-trmnl/releases
161
+ [semver]: http://semver.org/spec/v2.0.0.html
162
+ [pypi]: https://pypi.org/project/trmnl/
163
+
trmnl-0.1.0/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # Python: TRMNL
2
+
3
+ [![GitHub Release][releases-shield]][releases]
4
+ [![Python Versions][python-versions-shield]][pypi]
5
+ ![Project Stage][project-stage-shield]
6
+ ![Project Maintenance][maintenance-shield]
7
+ [![License][license-shield]](LICENSE.md)
8
+
9
+ [![Build Status][build-shield]][build]
10
+ [![Code Coverage][codecov-shield]][codecov]
11
+
12
+ Asynchronous Python client for TRMNL.
13
+
14
+ ## About
15
+
16
+ This package allows you to control and fetch data from your TRMNL device.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install trmnl
22
+ ```
23
+
24
+ ## Changelog & Releases
25
+
26
+ This repository keeps a change log using [GitHub's releases][releases]
27
+ functionality. The format of the log is based on
28
+ [Keep a Changelog][keepchangelog].
29
+
30
+ Releases are based on [Semantic Versioning][semver], and use the format
31
+ of `MAJOR.MINOR.PATCH`. In a nutshell, the version will be incremented
32
+ based on the following:
33
+
34
+ - `MAJOR`: Incompatible or major changes.
35
+ - `MINOR`: Backwards-compatible new features and enhancements.
36
+ - `PATCH`: Backwards-compatible bugfixes and package updates.
37
+
38
+ ## Contributing
39
+
40
+ This is an active open-source project. We are always open to people who want to
41
+ use the code or contribute to it.
42
+
43
+ We've set up a separate document for our
44
+ [contribution guidelines](.github/CONTRIBUTING.md).
45
+
46
+ Thank you for being involved! :heart_eyes:
47
+
48
+ ## Setting up development environment
49
+
50
+ This Python project is fully managed using the [Poetry][poetry] dependency manager. But also relies on the use of NodeJS for certain checks during development.
51
+
52
+ You need at least:
53
+
54
+ - Python 3.13+
55
+ - [Poetry][poetry-install]
56
+ - NodeJS 12+ (including NPM)
57
+
58
+ To install all packages, including all development requirements:
59
+
60
+ ```bash
61
+ npm install
62
+ poetry install
63
+ ```
64
+
65
+ As this repository uses the [pre-commit][pre-commit] framework, all changes
66
+ are linted and tested with each commit. You can run all checks and tests
67
+ manually, using the following command:
68
+
69
+ ```bash
70
+ poetry run pre-commit run --all-files
71
+ ```
72
+
73
+ To run just the Python tests:
74
+
75
+ ```bash
76
+ poetry run pytest
77
+ ```
78
+
79
+ ## Authors & contributors
80
+
81
+ The content is by [Joost Lekkerkerker][joostlek].
82
+
83
+ For a full list of all authors and contributors,
84
+ check [the contributor's page][contributors].
85
+
86
+ ## License
87
+
88
+ MIT License
89
+
90
+ Copyright (c) 2026 Joost Lekkerkerker
91
+
92
+ Permission is hereby granted, free of charge, to any person obtaining a copy
93
+ of this software and associated documentation files (the "Software"), to deal
94
+ in the Software without restriction, including without limitation the rights
95
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
96
+ copies of the Software, and to permit persons to whom the Software is
97
+ furnished to do so, subject to the following conditions:
98
+
99
+ The above copyright notice and this permission notice shall be included in all
100
+ copies or substantial portions of the Software.
101
+
102
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
103
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
104
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
105
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
106
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
107
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
108
+ SOFTWARE.
109
+
110
+ [build-shield]: https://github.com/joostlek/python-trmnl/actions/workflows/tests.yaml/badge.svg
111
+ [build]: https://github.com/joostlek/python-trmnl/actions
112
+ [codecov-shield]: https://codecov.io/gh/joostlek/python-trmnl/branch/master/graph/badge.svg
113
+ [codecov]: https://codecov.io/gh/joostlek/python-trmnl
114
+ [commits-shield]: https://img.shields.io/github/commit-activity/y/joostlek/python-trmnl.svg
115
+ [commits]: https://github.com/joostlek/python-trmnl/commits/master
116
+ [contributors]: https://github.com/joostlek/python-trmnl/graphs/contributors
117
+ [joostlek]: https://github.com/joostlek
118
+ [keepchangelog]: http://keepachangelog.com/en/1.0.0/
119
+ [license-shield]: https://img.shields.io/github/license/joostlek/python-trmnl.svg
120
+ [maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg
121
+ [poetry-install]: https://python-poetry.org/docs/#installation
122
+ [poetry]: https://python-poetry.org
123
+ [pre-commit]: https://pre-commit.com/
124
+ [project-stage-shield]: https://img.shields.io/badge/project%20stage-stable-green.svg
125
+ [python-versions-shield]: https://img.shields.io/pypi/pyversions/trmnl
126
+ [releases-shield]: https://img.shields.io/github/release/joostlek/python-trmnl.svg
127
+ [releases]: https://github.com/joostlek/python-trmnl/releases
128
+ [semver]: http://semver.org/spec/v2.0.0.html
129
+ [pypi]: https://pypi.org/project/trmnl/
@@ -0,0 +1,163 @@
1
+ [tool.poetry]
2
+ name = "trmnl"
3
+ version = "0.1.0"
4
+ description = "Asynchronous Python client for TRMNL."
5
+ authors = ["Joost Lekkerkerker <joostlek@outlook.com>"]
6
+ maintainers = ["Joost Lekkerkerker <joostlek@outlook.com>"]
7
+ license = "MIT"
8
+ readme = "README.md"
9
+ homepage = "https://github.com/joostlek/python-trmnl"
10
+ repository = "https://github.com/joostlek/python-trmnl"
11
+ documentation = "https://github.com/joostlek/python-trmnl"
12
+ keywords = ["TRMNL", "api", "async", "client"]
13
+ classifiers = [
14
+ "Development Status :: 5 - Production/Stable",
15
+ "Framework :: AsyncIO",
16
+ "Intended Audience :: Developers",
17
+ "Natural Language :: English",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ ]
23
+ packages = [
24
+ { include = "trmnl", from = "src" },
25
+ ]
26
+
27
+ [tool.poetry.dependencies]
28
+ python = "^3.13"
29
+ aiohttp = ">=3.0.0"
30
+ yarl = ">=1.6.0"
31
+ mashumaro = "^3.11"
32
+ orjson = ">=3.9.0"
33
+
34
+ [tool.poetry.group.dev.dependencies]
35
+ codespell = "2.4.1"
36
+ covdefaults = "2.3.0"
37
+ coverage = {version = "7.13.4", extras = ["toml"]}
38
+ mypy = "1.19.1"
39
+ pre-commit = "4.5.1"
40
+ pre-commit-hooks = "6.0.0"
41
+ pylint = "4.0.5"
42
+ pytest = "9.0.2"
43
+ pytest-asyncio = "1.3.0"
44
+ pytest-cov = "7.0.0"
45
+ ruff = "0.15.2"
46
+ safety = "3.7.0"
47
+ yamllint = "1.38.0"
48
+ syrupy = "5.1.0"
49
+ aioresponses = "0.7.8"
50
+
51
+ [tool.poetry.urls]
52
+ "Bug Tracker" = "https://github.com/joostlek/python-trmnl/issues"
53
+ Changelog = "https://github.com/joostlek/python-trmnl/releases"
54
+
55
+ [tool.coverage.report]
56
+ show_missing = true
57
+ fail_under = 50
58
+
59
+ [tool.coverage.run]
60
+ plugins = ["covdefaults"]
61
+ source = ["trmnl"]
62
+
63
+ [tool.mypy]
64
+ # Specify the target platform details in config, so your developers are
65
+ # free to run mypy on Windows, Linux, or macOS and get consistent
66
+ # results.
67
+ platform = "linux"
68
+ python_version = "3.12"
69
+
70
+ # show error messages from unrelated files
71
+ follow_imports = "normal"
72
+
73
+ # suppress errors about unsatisfied imports
74
+ ignore_missing_imports = true
75
+
76
+ # be strict
77
+ check_untyped_defs = true
78
+ disallow_any_generics = true
79
+ disallow_incomplete_defs = true
80
+ disallow_subclassing_any = true
81
+ disallow_untyped_calls = true
82
+ disallow_untyped_decorators = true
83
+ disallow_untyped_defs = true
84
+ no_implicit_optional = true
85
+ strict_optional = true
86
+ warn_incomplete_stub = true
87
+ warn_no_return = true
88
+ warn_redundant_casts = true
89
+ warn_return_any = true
90
+ warn_unused_configs = true
91
+ warn_unused_ignores = true
92
+
93
+ [tool.pylint.MASTER]
94
+ ignore = [
95
+ "tests",
96
+ ]
97
+
98
+ [tool.pylint.BASIC]
99
+ good-names = [
100
+ "_",
101
+ "ex",
102
+ "fp",
103
+ "i",
104
+ "id",
105
+ "j",
106
+ "k",
107
+ "on",
108
+ "Run",
109
+ "T",
110
+ ]
111
+
112
+ [tool.pylint.DESIGN]
113
+ max-attributes = 8
114
+
115
+ [tool.pylint."MESSAGES CONTROL"]
116
+ disable = [
117
+ "duplicate-code",
118
+ "format",
119
+ "unsubscriptable-object",
120
+ "too-many-instance-attributes",
121
+ "wrong-import-order",
122
+ "too-many-arguments",
123
+ "too-many-positional-arguments",
124
+ ]
125
+
126
+ [tool.pylint.SIMILARITIES]
127
+ ignore-imports = true
128
+
129
+ [tool.pylint.FORMAT]
130
+ max-line-length = 88
131
+
132
+ [tool.pytest.ini_options]
133
+ addopts = "--cov"
134
+ asyncio_mode = "auto"
135
+
136
+ [tool.ruff.lint]
137
+ ignore = [
138
+ "ANN401", # Opinioated warning on disallowing dynamically typed expressions
139
+ "COM812", # Conflicts with other rules
140
+ "D203", # Conflicts with other rules
141
+ "D213", # Conflicts with other rules
142
+ "D417", # False positives in some occasions
143
+ "ISC001", # Conflicts with other rules
144
+ "PLR2004", # Just annoying, not really useful
145
+ ]
146
+ select = ["ALL"]
147
+
148
+ [tool.ruff.lint.flake8-pytest-style]
149
+ fixture-parentheses = false
150
+ mark-parentheses = false
151
+
152
+ [tool.ruff.lint.isort]
153
+ known-first-party = ["trmnl"]
154
+ force-sort-within-sections = true
155
+ split-on-trailing-comma = false
156
+ combine-as-imports = true
157
+
158
+ [tool.ruff.lint.mccabe]
159
+ max-complexity = 25
160
+
161
+ [build-system]
162
+ build-backend = "poetry.core.masonry.api"
163
+ requires = ["poetry-core>=1.0.0"]
@@ -0,0 +1,5 @@
1
+ """Asynchronous Python client for TRMNL."""
2
+
3
+ from .trmnl import TRMNLClient
4
+
5
+ __all__ = ["TRMNLClient"]
@@ -0,0 +1 @@
1
+ """Constants for TRMNL."""
@@ -0,0 +1,9 @@
1
+ """Asynchronous Python client for TRMNL."""
2
+
3
+
4
+ class TRMNLError(Exception):
5
+ """Generic exception."""
6
+
7
+
8
+ class TRMNLAuthenticationError(TRMNLError):
9
+ """Authentication error."""
@@ -0,0 +1,109 @@
1
+ """Models for TRMNL."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+
7
+ from mashumaro import field_options
8
+ from mashumaro.mixins.orjson import DataClassORJSONMixin
9
+
10
+
11
+ @dataclass
12
+ class PluginSetting(DataClassORJSONMixin):
13
+ """Plugin setting model."""
14
+
15
+ identifier: int = field(metadata=field_options(alias="id"))
16
+ name: str
17
+ plugin_id: int
18
+
19
+
20
+ @dataclass
21
+ class PluginSettingResponse(DataClassORJSONMixin):
22
+ """Plugin setting response."""
23
+
24
+ data: PluginSetting
25
+
26
+
27
+ @dataclass
28
+ class PluginSettingsResponse(DataClassORJSONMixin):
29
+ """Plugin settings list response."""
30
+
31
+ data: list[PluginSetting]
32
+
33
+
34
+ @dataclass
35
+ class Device(DataClassORJSONMixin):
36
+ """Device model."""
37
+
38
+ identifier: int = field(metadata=field_options(alias="id"))
39
+ name: str
40
+ friendly_id: str
41
+ mac_address: str
42
+ battery_voltage: float | None
43
+ rssi: int | None
44
+ sleep_mode_enabled: bool
45
+ sleep_start_time: int
46
+ sleep_end_time: int
47
+ percent_charged: float
48
+ wifi_strength: int
49
+
50
+
51
+ @dataclass
52
+ class DeviceResponse(DataClassORJSONMixin):
53
+ """Device response."""
54
+
55
+ data: Device
56
+
57
+
58
+ @dataclass
59
+ class DevicesResponse(DataClassORJSONMixin):
60
+ """Devices list response."""
61
+
62
+ data: list[Device]
63
+
64
+
65
+ @dataclass
66
+ class User(DataClassORJSONMixin):
67
+ """User model."""
68
+
69
+ identifier: int = field(metadata=field_options(alias="id"))
70
+ name: str
71
+ email: str
72
+ first_name: str
73
+ last_name: str
74
+ locale: str
75
+ time_zone: str
76
+ time_zone_iana: str
77
+ utc_offset: int
78
+ api_key: str
79
+
80
+
81
+ @dataclass
82
+ class UserResponse(DataClassORJSONMixin):
83
+ """User response."""
84
+
85
+ data: User
86
+
87
+
88
+ @dataclass
89
+ class PlaylistItem(DataClassORJSONMixin):
90
+ """Playlist item model."""
91
+
92
+ created_at: str
93
+ device_id: int
94
+ identifier: int = field(metadata=field_options(alias="id"))
95
+ mashup_id: int | None
96
+ mirror: bool
97
+ plugin_setting_id: int
98
+ rendered_at: str
99
+ row_order: int
100
+ updated_at: str
101
+ visible: bool
102
+ plugin_setting: PluginSetting
103
+
104
+
105
+ @dataclass
106
+ class PlaylistItemsResponse(DataClassORJSONMixin):
107
+ """Playlist items list response."""
108
+
109
+ data: list[PlaylistItem]
File without changes
@@ -0,0 +1,176 @@
1
+ """Asynchronous Python client for TRMNL."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from dataclasses import dataclass
7
+ import logging
8
+ from typing import Any, Self
9
+
10
+ from aiohttp import ClientSession
11
+ from aiohttp.hdrs import METH_DELETE, METH_GET, METH_PATCH, METH_POST
12
+ from yarl import URL
13
+
14
+ from trmnl.models import (
15
+ Device,
16
+ DeviceResponse,
17
+ DevicesResponse,
18
+ PlaylistItem,
19
+ PlaylistItemsResponse,
20
+ PluginSetting,
21
+ PluginSettingResponse,
22
+ PluginSettingsResponse,
23
+ User,
24
+ UserResponse,
25
+ )
26
+
27
+ VERSION = "1"
28
+
29
+ _LOGGER = logging.getLogger(__package__)
30
+
31
+ HOST = "trmnl.com"
32
+
33
+
34
+ @dataclass
35
+ class TRMNLClient:
36
+ """Main class for handling connections with the TRMNL API."""
37
+
38
+ token: str
39
+ session: ClientSession | None = None
40
+ request_timeout: int = 10
41
+ _close_session: bool = False
42
+
43
+ async def _request(
44
+ self,
45
+ uri: str,
46
+ *,
47
+ method: str = METH_GET,
48
+ data: dict[str, Any] | None = None,
49
+ ) -> str:
50
+ """Handle a request to the TRMNL API."""
51
+ url = URL.build(host=HOST, scheme="https").joinpath(f"api/{uri}")
52
+ headers = {
53
+ "User-Agent": f"python-trmnl/{VERSION}",
54
+ "Authorization": f"Bearer {self.token}",
55
+ }
56
+
57
+ if self.session is None:
58
+ self.session = ClientSession()
59
+ self._close_session = True
60
+
61
+ async with asyncio.timeout(self.request_timeout):
62
+ response = await self.session.request(
63
+ method,
64
+ url,
65
+ headers=headers,
66
+ json=data,
67
+ )
68
+
69
+ return await response.text()
70
+
71
+ async def _get(self, uri: str) -> str:
72
+ """Handle a GET request to the TRMNL API."""
73
+ return await self._request(uri, method=METH_GET)
74
+
75
+ async def _patch(self, uri: str, data: dict[str, Any]) -> str:
76
+ """Handle a PATCH request to the TRMNL API."""
77
+ return await self._request(uri, method=METH_PATCH, data=data)
78
+
79
+ async def _post(self, uri: str, data: dict[str, Any]) -> str:
80
+ """Handle a POST request to the TRMNL API."""
81
+ return await self._request(uri, method=METH_POST, data=data)
82
+
83
+ async def _delete(self, uri: str) -> None:
84
+ """Handle a DELETE request to the TRMNL API."""
85
+ await self._request(uri, method=METH_DELETE)
86
+
87
+ async def get_devices(self) -> list[Device]:
88
+ """Retrieve the list of devices."""
89
+ result = await self._get("devices")
90
+ return DevicesResponse.from_json(result).data
91
+
92
+ async def get_device(self, device_id: int) -> Device:
93
+ """Retrieve a single device by ID."""
94
+ result = await self._get(f"devices/{device_id}")
95
+ return DeviceResponse.from_json(result).data
96
+
97
+ async def update_device(
98
+ self,
99
+ device_id: int,
100
+ *,
101
+ sleep_mode_enabled: bool | None = None,
102
+ sleep_start_time: int | None = None,
103
+ sleep_end_time: int | None = None,
104
+ percent_charged: float | None = None,
105
+ ) -> None:
106
+ """Update a device."""
107
+ payload: dict[str, Any] = {}
108
+ if sleep_mode_enabled is not None:
109
+ payload["sleep_mode_enabled"] = sleep_mode_enabled
110
+ if sleep_start_time is not None:
111
+ payload["sleep_start_time"] = sleep_start_time
112
+ if sleep_end_time is not None:
113
+ payload["sleep_end_time"] = sleep_end_time
114
+ if percent_charged is not None:
115
+ payload["percent_charged"] = percent_charged
116
+ await self._patch(f"devices/{device_id}", payload)
117
+
118
+ async def get_me(self) -> User:
119
+ """Retrieve the current user."""
120
+ result = await self._get("me")
121
+ return UserResponse.from_json(result).data
122
+
123
+ async def get_playlist_items(self) -> list[PlaylistItem]:
124
+ """Retrieve all playlist items."""
125
+ result = await self._get("playlists/items")
126
+ return PlaylistItemsResponse.from_json(result).data
127
+
128
+ async def update_playlist_item(self, item_id: int, *, visible: bool) -> None:
129
+ """Update a playlist item's visibility."""
130
+ await self._patch(f"playlists/items/{item_id}", {"visible": visible})
131
+
132
+ async def get_plugin_settings(
133
+ self, plugin_id: int | None = None
134
+ ) -> list[PluginSetting]:
135
+ """Retrieve plugin settings, optionally filtered by plugin_id."""
136
+ uri = "plugin_settings"
137
+ if plugin_id is not None:
138
+ uri = f"{uri}?plugin_id={plugin_id}"
139
+ result = await self._get(uri)
140
+ return PluginSettingsResponse.from_json(result).data
141
+
142
+ async def create_plugin_setting(self, name: str, plugin_id: int) -> PluginSetting:
143
+ """Create a new plugin setting."""
144
+ result = await self._post(
145
+ "plugin_settings", {"name": name, "plugin_id": plugin_id}
146
+ )
147
+ return PluginSettingResponse.from_json(result).data
148
+
149
+ async def delete_plugin_setting(self, plugin_setting_id: int) -> None:
150
+ """Delete a plugin setting."""
151
+ await self._delete(f"plugin_settings/{plugin_setting_id}")
152
+
153
+ async def close(self) -> None:
154
+ """Close open client session."""
155
+ if self.session and self._close_session:
156
+ await self.session.close()
157
+
158
+ async def __aenter__(self) -> Self:
159
+ """Async enter.
160
+
161
+ Returns
162
+ -------
163
+ The TRMNLClient object.
164
+
165
+ """
166
+ return self
167
+
168
+ async def __aexit__(self, *_exc_info: object) -> None:
169
+ """Async exit.
170
+
171
+ Args:
172
+ ----
173
+ _exc_info: Exec type.
174
+
175
+ """
176
+ await self.close()