pyremootio 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.
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .coverage
13
+ htmlcov/
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Remootio
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
13
+ all 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 WITHOUT LIMITATION 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
21
+ THE SOFTWARE.
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.5
2
+ Name: pyremootio
3
+ Version: 0.1.0
4
+ Summary: Async Python client for the Remootio Websocket API
5
+ Project-URL: Homepage, https://github.com/remootio/pyremootio
6
+ Project-URL: Documentation, https://github.com/remootio/remootio-api-documentation
7
+ Project-URL: Source, https://github.com/remootio/pyremootio
8
+ Project-URL: Issues, https://github.com/remootio/pyremootio/issues
9
+ Author-email: Remootio <hello@remootio.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: asyncio,garage,gate,remootio,websocket
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Home Automation
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.12
22
+ Requires-Dist: aiohttp>=3.9.0
23
+ Requires-Dist: cryptography>=42.0.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: mypy>=1.11.0; extra == 'dev'
26
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # pyremootio
32
+
33
+ Async Python client for the [Remootio](https://www.remootio.com) local Websocket API.
34
+
35
+ Enable the Websocket API in the Remootio app and copy the API Secret Key, API Auth Key, and device IP.
36
+
37
+ Protocol details: [Remootio Websocket API documentation](https://github.com/remootio/remootio-api-documentation).
38
+
39
+ ## Install
40
+
41
+ Python 3.12 or newer:
42
+
43
+ ```bash
44
+ pip install pyremootio
45
+ ```
46
+
47
+ From a clone of this repo (includes tests and examples):
48
+
49
+ ```bash
50
+ python3.12 -m venv .venv
51
+ .venv/bin/pip install -e ".[dev]"
52
+ ```
53
+
54
+ Run examples with `.venv/bin/python`.
55
+
56
+ ## Usage
57
+
58
+ ```python
59
+ import asyncio
60
+ import aiohttp
61
+ from pyremootio import RemootioClient
62
+
63
+ async def main() -> None:
64
+ async with aiohttp.ClientSession() as session:
65
+ client = RemootioClient(
66
+ "<ip_address>", # IP address of your Remootio device
67
+ "<secret_key>", # API Secret Key from the Remootio app
68
+ "<auth_key>", # API Auth Key from the Remootio app
69
+ session,
70
+ )
71
+ await client.connect()
72
+ try:
73
+ response = await client.trigger()
74
+ print("TRIGGER success=%s state=%s" % (response.success, response.state))
75
+ finally:
76
+ await client.disconnect()
77
+
78
+ asyncio.run(main())
79
+ ```
80
+
81
+ `connect()` runs HELLO, AUTH, and the first QUERY action to authenticate the client. After that, `listen()` receives device events.
82
+
83
+ Logging uses the standard `pyremootio` logger:
84
+
85
+ | Level | What you see |
86
+ | --- | --- |
87
+ | `DEBUG` | PING / PONG keepalive |
88
+ | `INFO` | connect, authenticate, disconnect (with reason) |
89
+ | `WARNING` | connection lost, reconnect failed |
90
+ | `ERROR` | unexpected receive-loop or listener failures |
91
+
92
+ ```python
93
+ import logging
94
+ logging.getLogger("pyremootio").setLevel(logging.DEBUG)
95
+ ```
96
+
97
+ ### Events
98
+
99
+ ```python
100
+ async def on_event(event):
101
+ print(event.type, event.state)
102
+
103
+ unsubscribe = client.listen(on_event)
104
+ ```
105
+
106
+ `StateChange` is sent when a sensor is installed. Enable API logging in the app for the full event set.
107
+
108
+ ### Actions
109
+
110
+ | Method | Device action |
111
+ | --- | --- |
112
+ | `query()` | Current door state |
113
+ | `open(duration_minutes=None)` | Open if closed (sensor required) |
114
+ | `close(duration_minutes=None)` | Close if open (sensor required) |
115
+ | `trigger(duration_minutes=None)` | Pulse the control output |
116
+ | `trigger_secondary(duration_minutes=None)` | Pulse the free relay output |
117
+ | `restart()` | Reboot the device (connection drops) |
118
+
119
+ `duration_minutes` holds the output active. It is rejected unless `api_version` is 3 or later. Failed actions raise `RemootioActionError`.
120
+
121
+ ## Examples
122
+
123
+ - [`examples/trigger.py`](examples/trigger.py) — connect, trigger, wait for `StateChange`
124
+ - [`examples/log_events.py`](examples/log_events.py) — log device events to stdout
125
+
126
+ ```bash
127
+ .venv/bin/python examples/trigger.py --host <ip_address> --secret-key <secret_key> --auth-key <auth_key>
128
+ ```
129
+
130
+ ## Tests
131
+
132
+ ```bash
133
+ pytest
134
+ ```
@@ -0,0 +1,104 @@
1
+ # pyremootio
2
+
3
+ Async Python client for the [Remootio](https://www.remootio.com) local Websocket API.
4
+
5
+ Enable the Websocket API in the Remootio app and copy the API Secret Key, API Auth Key, and device IP.
6
+
7
+ Protocol details: [Remootio Websocket API documentation](https://github.com/remootio/remootio-api-documentation).
8
+
9
+ ## Install
10
+
11
+ Python 3.12 or newer:
12
+
13
+ ```bash
14
+ pip install pyremootio
15
+ ```
16
+
17
+ From a clone of this repo (includes tests and examples):
18
+
19
+ ```bash
20
+ python3.12 -m venv .venv
21
+ .venv/bin/pip install -e ".[dev]"
22
+ ```
23
+
24
+ Run examples with `.venv/bin/python`.
25
+
26
+ ## Usage
27
+
28
+ ```python
29
+ import asyncio
30
+ import aiohttp
31
+ from pyremootio import RemootioClient
32
+
33
+ async def main() -> None:
34
+ async with aiohttp.ClientSession() as session:
35
+ client = RemootioClient(
36
+ "<ip_address>", # IP address of your Remootio device
37
+ "<secret_key>", # API Secret Key from the Remootio app
38
+ "<auth_key>", # API Auth Key from the Remootio app
39
+ session,
40
+ )
41
+ await client.connect()
42
+ try:
43
+ response = await client.trigger()
44
+ print("TRIGGER success=%s state=%s" % (response.success, response.state))
45
+ finally:
46
+ await client.disconnect()
47
+
48
+ asyncio.run(main())
49
+ ```
50
+
51
+ `connect()` runs HELLO, AUTH, and the first QUERY action to authenticate the client. After that, `listen()` receives device events.
52
+
53
+ Logging uses the standard `pyremootio` logger:
54
+
55
+ | Level | What you see |
56
+ | --- | --- |
57
+ | `DEBUG` | PING / PONG keepalive |
58
+ | `INFO` | connect, authenticate, disconnect (with reason) |
59
+ | `WARNING` | connection lost, reconnect failed |
60
+ | `ERROR` | unexpected receive-loop or listener failures |
61
+
62
+ ```python
63
+ import logging
64
+ logging.getLogger("pyremootio").setLevel(logging.DEBUG)
65
+ ```
66
+
67
+ ### Events
68
+
69
+ ```python
70
+ async def on_event(event):
71
+ print(event.type, event.state)
72
+
73
+ unsubscribe = client.listen(on_event)
74
+ ```
75
+
76
+ `StateChange` is sent when a sensor is installed. Enable API logging in the app for the full event set.
77
+
78
+ ### Actions
79
+
80
+ | Method | Device action |
81
+ | --- | --- |
82
+ | `query()` | Current door state |
83
+ | `open(duration_minutes=None)` | Open if closed (sensor required) |
84
+ | `close(duration_minutes=None)` | Close if open (sensor required) |
85
+ | `trigger(duration_minutes=None)` | Pulse the control output |
86
+ | `trigger_secondary(duration_minutes=None)` | Pulse the free relay output |
87
+ | `restart()` | Reboot the device (connection drops) |
88
+
89
+ `duration_minutes` holds the output active. It is rejected unless `api_version` is 3 or later. Failed actions raise `RemootioActionError`.
90
+
91
+ ## Examples
92
+
93
+ - [`examples/trigger.py`](examples/trigger.py) — connect, trigger, wait for `StateChange`
94
+ - [`examples/log_events.py`](examples/log_events.py) — log device events to stdout
95
+
96
+ ```bash
97
+ .venv/bin/python examples/trigger.py --host <ip_address> --secret-key <secret_key> --auth-key <auth_key>
98
+ ```
99
+
100
+ ## Tests
101
+
102
+ ```bash
103
+ pytest
104
+ ```
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ """Connect and print every event the device sends (enable API with logging).
3
+
4
+ Example (from this folder, after installing into .venv):
5
+
6
+ <ip_address> is the IP address of the Remootio device.
7
+ <secret_key> is the secret key of the Remootio device, you can find it in the Remootio app.
8
+ <auth_key> is the auth key of the Remootio device, you can find it in the Remootio app.
9
+
10
+ .venv/bin/python examples/log_events.py --host <ip_address> --secret-key <secret_key> --auth-key <auth_key>
11
+
12
+ for example:
13
+ .venv/bin/python examples/log_events.py --host 10.23.1.106 --secret-key D0A7E9B4E31AB7BCF2219C9A587ACA7DDB572EAB08459D0ACC52F7C83121B19C --auth-key 3C3B25A1227B9FA87E4A4E259B661F98E522531D2250BAE0C3F1FE3B02762183
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import asyncio
20
+ import logging
21
+
22
+ import aiohttp
23
+
24
+ from pyremootio import RemootioClient, RemootioEvent
25
+
26
+
27
+ async def main() -> None:
28
+ parser = argparse.ArgumentParser(description=__doc__)
29
+ parser.add_argument("--host", required=True)
30
+ parser.add_argument("--secret-key", required=True)
31
+ parser.add_argument("--auth-key", required=True)
32
+ parser.add_argument("--port", type=int, default=8080)
33
+ parser.add_argument(
34
+ "--log-level",
35
+ default="DEBUG",
36
+ choices=["DEBUG", "INFO", "WARNING", "ERROR"],
37
+ help="pyremootio log level (default: DEBUG, includes PING/PONG)",
38
+ )
39
+ args = parser.parse_args()
40
+
41
+ logging.basicConfig(
42
+ level=logging.INFO,
43
+ format="%(asctime)s [%(levelname)s] %(message)s",
44
+ )
45
+ logging.getLogger("pyremootio").setLevel(getattr(logging, args.log_level))
46
+
47
+ async def on_event(event: RemootioEvent) -> None:
48
+ logging.info("%s state=%s cnt=%s data=%s", event.type, event.state, event.cnt, event.data)
49
+
50
+ async with aiohttp.ClientSession() as session:
51
+ client = RemootioClient(
52
+ args.host,
53
+ args.secret_key,
54
+ args.auth_key,
55
+ session,
56
+ port=args.port,
57
+ )
58
+ client.listen(on_event)
59
+ await client.connect(reconnect=True)
60
+ logging.info(
61
+ "Listening (serial=%s, api=%s, state=%s)",
62
+ client.serial_number,
63
+ client.api_version,
64
+ client.state,
65
+ )
66
+ try:
67
+ await asyncio.Event().wait()
68
+ finally:
69
+ await client.disconnect()
70
+
71
+
72
+ if __name__ == "__main__":
73
+ asyncio.run(main())
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env python3
2
+ """Connect, trigger the control output, and disconnect after a StateChange.
3
+
4
+ Example (from this folder, after installing into .venv):
5
+
6
+ <ip_address> is the IP address of the Remootio device.
7
+ <secret_key> is the secret key of the Remootio device, you can find it in the Remootio app.
8
+ <auth_key> is the auth key of the Remootio device, you can find it in the Remootio app.
9
+
10
+ .venv/bin/python examples/trigger.py --host <ip_address> --secret-key <secret_key> --auth-key <auth_key>
11
+
12
+ for example:
13
+ .venv/bin/python examples/trigger.py --host 10.23.1.106 --secret-key D0A7E9B4E31AB7BCF2219C9A587ACA7DDB572EAB08459D0ACC52F7C83121B19C --auth-key 3C3B25A1227B9FA87E4A4E259B661F98E522531D2250BAE0C3F1FE3B02762183
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import asyncio
20
+ import logging
21
+
22
+ import aiohttp
23
+
24
+ from pyremootio import DoorState, EventType, RemootioClient, RemootioEvent
25
+
26
+
27
+ async def main() -> None:
28
+ parser = argparse.ArgumentParser(description=__doc__)
29
+ parser.add_argument("--host", required=True)
30
+ parser.add_argument("--secret-key", required=True)
31
+ parser.add_argument("--auth-key", required=True)
32
+ parser.add_argument("--port", type=int, default=8080)
33
+ args = parser.parse_args()
34
+
35
+ logging.basicConfig(level=logging.INFO)
36
+ done = asyncio.Event()
37
+
38
+ async def on_event(event: RemootioEvent) -> None:
39
+ if event.event_type is EventType.STATE_CHANGE:
40
+ logging.info("Door state changed to %s", event.state)
41
+ done.set()
42
+
43
+ async with aiohttp.ClientSession() as session:
44
+ client = RemootioClient(
45
+ args.host,
46
+ args.secret_key,
47
+ args.auth_key,
48
+ session,
49
+ port=args.port,
50
+ )
51
+ client.listen(on_event)
52
+ await client.connect()
53
+ try:
54
+ logging.info(
55
+ "Authenticated (serial=%s, api=%s, state=%s)",
56
+ client.serial_number,
57
+ client.api_version,
58
+ client.state,
59
+ )
60
+ response = await client.trigger()
61
+ logging.info("TRIGGER success=%s state=%s", response.success, response.state)
62
+ if response.state is DoorState.NO_SENSOR:
63
+ logging.info("No sensor installed; disconnecting without waiting")
64
+ return
65
+ await asyncio.wait_for(done.wait(), timeout=30)
66
+ finally:
67
+ await client.disconnect()
68
+
69
+
70
+ if __name__ == "__main__":
71
+ asyncio.run(main())
@@ -0,0 +1,95 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pyremootio"
7
+ dynamic = ["version"]
8
+ description = "Async Python client for the Remootio Websocket API"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.12"
12
+ authors = [
13
+ { name = "Remootio", email = "hello@remootio.com" },
14
+ ]
15
+ keywords = ["remootio", "asyncio", "garage", "gate", "websocket"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Framework :: AsyncIO",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Home Automation",
24
+ "Typing :: Typed",
25
+ ]
26
+ dependencies = [
27
+ "aiohttp>=3.9.0",
28
+ "cryptography>=42.0.0",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "pytest>=8.0.0",
34
+ "pytest-asyncio>=0.24.0",
35
+ "ruff>=0.6.0",
36
+ "mypy>=1.11.0",
37
+ ]
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/remootio/pyremootio"
41
+ Documentation = "https://github.com/remootio/remootio-api-documentation"
42
+ Source = "https://github.com/remootio/pyremootio"
43
+ Issues = "https://github.com/remootio/pyremootio/issues"
44
+
45
+ [tool.hatch.version]
46
+ path = "src/pyremootio/__init__.py"
47
+
48
+ [tool.hatch.build.targets.sdist]
49
+ include = [
50
+ "/src",
51
+ "/tests",
52
+ "/examples",
53
+ "/README.md",
54
+ "/LICENSE",
55
+ ]
56
+
57
+ [tool.pytest.ini_options]
58
+ asyncio_mode = "auto"
59
+ asyncio_default_fixture_loop_scope = "function"
60
+ testpaths = ["tests"]
61
+ pythonpath = ["src"]
62
+
63
+ [tool.ruff]
64
+ target-version = "py312"
65
+ line-length = 99
66
+ src = ["src", "tests", "examples"]
67
+
68
+ [tool.ruff.lint]
69
+ select = [
70
+ "B",
71
+ "C4",
72
+ "E",
73
+ "F",
74
+ "I",
75
+ "PERF",
76
+ "PLC",
77
+ "PLE",
78
+ "PLW",
79
+ "RUF",
80
+ "SIM",
81
+ "UP",
82
+ "W",
83
+ ]
84
+
85
+ [tool.ruff.lint.isort]
86
+ known-first-party = ["pyremootio"]
87
+
88
+ [tool.ruff.lint.per-file-ignores]
89
+ "examples/*.py" = ["E501"]
90
+
91
+ [tool.mypy]
92
+ python_version = "3.12"
93
+ strict = true
94
+ mypy_path = "src"
95
+ packages = ["pyremootio"]
@@ -0,0 +1,47 @@
1
+ """Async Python client for the Remootio Websocket API."""
2
+
3
+ from pyremootio.client import RemootioClient
4
+ from pyremootio.exceptions import (
5
+ RemootioActionError,
6
+ RemootioAuthenticationError,
7
+ RemootioConnectionError,
8
+ RemootioCryptoError,
9
+ RemootioError,
10
+ RemootioTimeoutError,
11
+ )
12
+ from pyremootio.models import (
13
+ ActionErrorCode,
14
+ ActionResponse,
15
+ ActionType,
16
+ ConnectionVia,
17
+ Credentials,
18
+ DeviceErrorMessage,
19
+ DoorState,
20
+ EventType,
21
+ KeyType,
22
+ RemootioEvent,
23
+ ServerHello,
24
+ )
25
+
26
+ __all__ = [
27
+ "ActionErrorCode",
28
+ "ActionResponse",
29
+ "ActionType",
30
+ "ConnectionVia",
31
+ "Credentials",
32
+ "DeviceErrorMessage",
33
+ "DoorState",
34
+ "EventType",
35
+ "KeyType",
36
+ "RemootioActionError",
37
+ "RemootioAuthenticationError",
38
+ "RemootioClient",
39
+ "RemootioConnectionError",
40
+ "RemootioCryptoError",
41
+ "RemootioError",
42
+ "RemootioEvent",
43
+ "RemootioTimeoutError",
44
+ "ServerHello",
45
+ ]
46
+
47
+ __version__ = "0.1.0"