smartbox 2.6.2__tar.gz → 3.0.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.
- {smartbox-2.6.2 → smartbox-3.0.0}/.gitignore +6 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/CHANGELOG.md +82 -2
- smartbox-3.0.0/PKG-INFO +131 -0
- smartbox-3.0.0/README.md +111 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/pyproject.toml +46 -23
- smartbox-3.0.0/src/smartbox/__init__.py +17 -0
- smartbox-3.0.0/src/smartbox/cli.py +615 -0
- smartbox-3.0.0/src/smartbox/core/__init__.py +37 -0
- smartbox-3.0.0/src/smartbox/core/deadlines.py +272 -0
- smartbox-3.0.0/src/smartbox/core/engine.py +1772 -0
- smartbox-3.0.0/src/smartbox/core/feed.py +198 -0
- smartbox-3.0.0/src/smartbox/core/merging.py +369 -0
- smartbox-3.0.0/src/smartbox/core/state.py +190 -0
- smartbox-3.0.0/src/smartbox/protocol/__init__.py +67 -0
- smartbox-3.0.0/src/smartbox/protocol/auth.py +415 -0
- smartbox-3.0.0/src/smartbox/protocol/client.py +421 -0
- smartbox-3.0.0/src/smartbox/protocol/errors.py +35 -0
- smartbox-3.0.0/src/smartbox/protocol/events.py +65 -0
- smartbox-3.0.0/src/smartbox/protocol/jwt.py +75 -0
- smartbox-3.0.0/src/smartbox/protocol/paths.py +31 -0
- {smartbox-2.6.2/src/smartbox → smartbox-3.0.0/src/smartbox/protocol}/reseller.py +54 -26
- smartbox-3.0.0/src/smartbox/protocol/retry.py +38 -0
- smartbox-3.0.0/src/smartbox/protocol/timing.py +24 -0
- smartbox-3.0.0/src/smartbox/protocol/transport.py +78 -0
- smartbox-3.0.0/src/smartbox/protocol/values.py +314 -0
- smartbox-3.0.0/src/smartbox/protocol/writes.py +740 -0
- smartbox-3.0.0/src/smartbox/protocol/ws_user.py +735 -0
- smartbox-3.0.0/tests/fake_api.py +393 -0
- smartbox-3.0.0/tests/fake_transport.py +77 -0
- smartbox-3.0.0/tests/fakes.py +253 -0
- smartbox-3.0.0/tests/fixtures/live/README.md +11 -0
- smartbox-3.0.0/tests/test_auth.py +597 -0
- smartbox-3.0.0/tests/test_cli.py +21 -0
- smartbox-3.0.0/tests/test_client.py +625 -0
- smartbox-3.0.0/tests/test_core_availability.py +1269 -0
- smartbox-3.0.0/tests/test_core_engine.py +1368 -0
- smartbox-3.0.0/tests/test_core_state.py +74 -0
- smartbox-3.0.0/tests/test_core_writes.py +1360 -0
- smartbox-3.0.0/tests/test_layering.py +30 -0
- smartbox-3.0.0/tests/test_reseller.py +40 -0
- smartbox-3.0.0/tests/test_retry.py +24 -0
- smartbox-3.0.0/tests/test_values.py +255 -0
- smartbox-3.0.0/tests/test_writes.py +790 -0
- smartbox-3.0.0/tests/test_ws_user_transport.py +1231 -0
- smartbox-3.0.0/tests/utils.py +54 -0
- smartbox-2.6.2/.github/FUNDING.yml +0 -3
- smartbox-2.6.2/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- smartbox-2.6.2/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- smartbox-2.6.2/.github/ISSUE_TEMPLATE/new-reseller.md +0 -18
- smartbox-2.6.2/.github/dependabot.yml +0 -19
- smartbox-2.6.2/.github/workflows/publish-to-pypi.yml +0 -31
- smartbox-2.6.2/.github/workflows/python-package.yml +0 -88
- smartbox-2.6.2/.pre-commit-config.yaml +0 -29
- smartbox-2.6.2/.vscode/settings.json +0 -12
- smartbox-2.6.2/CODEOWNERS +0 -5
- smartbox-2.6.2/PKG-INFO +0 -202
- smartbox-2.6.2/README.md +0 -182
- smartbox-2.6.2/ajtudela-pr-64-audit.md +0 -497
- smartbox-2.6.2/api-notes.md +0 -603
- smartbox-2.6.2/codecov.yml +0 -10
- smartbox-2.6.2/src/smartbox/__init__.py +0 -88
- smartbox-2.6.2/src/smartbox/cmd.py +0 -581
- smartbox-2.6.2/src/smartbox/error.py +0 -41
- smartbox-2.6.2/src/smartbox/models.py +0 -325
- smartbox-2.6.2/src/smartbox/session.py +0 -1869
- smartbox-2.6.2/src/smartbox/socket.py +0 -523
- smartbox-2.6.2/src/smartbox/update_manager.py +0 -764
- smartbox-2.6.2/tests/common.py +0 -28
- smartbox-2.6.2/tests/conftest.py +0 -142
- smartbox-2.6.2/tests/test_cmd.py +0 -521
- smartbox-2.6.2/tests/test_live_payloads.py +0 -133
- smartbox-2.6.2/tests/test_models.py +0 -334
- smartbox-2.6.2/tests/test_reseller.py +0 -65
- smartbox-2.6.2/tests/test_session.py +0 -2768
- smartbox-2.6.2/tests/test_session_reliability.py +0 -552
- smartbox-2.6.2/tests/test_socket.py +0 -501
- smartbox-2.6.2/tests/test_update_manager.py +0 -940
- smartbox-2.6.2/tools/api_functional_tests.py +0 -233
- smartbox-2.6.2/tools/away_probe.py +0 -109
- smartbox-2.6.2/tools/capture_prog.py +0 -84
- smartbox-2.6.2/tools/live_check.py +0 -137
- smartbox-2.6.2/tools/probe_extra_options.py +0 -145
- smartbox-2.6.2/tools/probe_modified_auto.py +0 -214
- smartbox-2.6.2/tools/probe_settle_window.py +0 -146
- smartbox-2.6.2/tools/prog_functional_tests.py +0 -143
- smartbox-2.6.2/tools/watch_status.py +0 -123
- smartbox-2.6.2/uv.lock +0 -1424
- smartbox-2.6.2/webapi-spec.md +0 -186
- {smartbox-2.6.2 → smartbox-3.0.0}/LICENSE +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/src/smartbox/py.typed +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/__init__.py +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/acm/1/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/acm/1/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/acm/1/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/acm/1/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/connected.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr/0/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr/0/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr/0/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr/0/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr_system/power_limit.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/mgr/away_status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/mgr/nodes.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/pmo/3/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/pmo/3/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/pmo/3/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/pmo/3/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/connected.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/0/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/0/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/0/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/0/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/1/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/1/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/1/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/1/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/2/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/2/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/2/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/2/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/3/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/3/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/3/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/3/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/4/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/4/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/4/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/4/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/5/samples.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/5/setup.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/5/status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/5/version.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_system/power_limit.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/mgr/away_status.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/mgr/nodes.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/grouped_devs.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/groups/test_home/guest_users.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr2.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr3.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr4.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr5.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr6.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr2.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr3.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr4.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr5.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr6.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_no_boost.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_status_boost_off.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/socket_update_status_frame.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/socket_update_sync_lost_frame.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/socket_update_version_frame.json +0 -0
- {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/test_path.json +0 -0
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
First stable release of the standalone protocol/core library used by the 3.x
|
|
6
|
+
Home Assistant integration.
|
|
7
|
+
|
|
8
|
+
### Breaking
|
|
9
|
+
|
|
10
|
+
* The 2.7 session/update-manager API is replaced by `smartbox.protocol`
|
|
11
|
+
(`SmartboxClient`, auth, `ws_user` transport, named writes) and
|
|
12
|
+
`smartbox.core` (`SmartboxEngine`, state, deadlines, feed).
|
|
13
|
+
* The library is Home Assistant-free by construction; the integration depends
|
|
14
|
+
on it instead of vendoring the layers.
|
|
15
|
+
* The CLI now uses the new client and `ws_user` transport. The old per-device
|
|
16
|
+
`socket` command is replaced by `listen`; `set-setup` exposes the granular
|
|
17
|
+
setup writes; boost and node power-limit commands are added.
|
|
18
|
+
|
|
19
|
+
### New
|
|
20
|
+
|
|
21
|
+
* The CLI remains available as the optional `cli` extra:
|
|
22
|
+
`pip install "smartbox[cli] ..."`.
|
|
23
|
+
* `SmartboxClient.get_home_guests()` and the CLI `guests` command.
|
|
24
|
+
* `WriteApi.set_setup_value()` as the CLI/debug escape hatch for setup keys
|
|
25
|
+
without a named setter.
|
|
26
|
+
|
|
27
|
+
See GitHub release notes: https://github.com/jandado/smartbox/releases
|
|
28
|
+
|
|
29
|
+
## 2.7.0
|
|
30
|
+
|
|
31
|
+
### New
|
|
32
|
+
* Per-user `ws_user` websocket transport (`WsUserSocketSession`,
|
|
33
|
+
`check_ws_user_support`): one connection serves every device of the
|
|
34
|
+
account, replacing N per-device `socket_io` sessions. Wire protocol
|
|
35
|
+
and every failure mode verified live (api-notes.md, "Transports —
|
|
36
|
+
socket_io vs ws_user"). Selected via the new `ws_user_socket=` kwarg
|
|
37
|
+
on `UpdateManager`; the default transport is unchanged.
|
|
38
|
+
* `WsUserUnsupportedError` + the fallback doctrine: only deterministic
|
|
39
|
+
handshake rejections (HTTP 401/403/404/410 in the one-shot probe on a
|
|
40
|
+
fresh token; 404/410 in the run loop, or 401/403 that keep rejecting
|
|
41
|
+
freshly minted tokens) signal "endpoint unsupported"; transient
|
|
42
|
+
trouble is retried and never demotes.
|
|
43
|
+
* In `ws_user` mode `UpdateManager` ignores the SocketSession
|
|
44
|
+
pass-through kwargs (warned at construction) — the shared socket's
|
|
45
|
+
liveness is heartbeat-driven, not knob-driven.
|
|
46
|
+
|
|
47
|
+
### Breaking
|
|
48
|
+
* `UpdateManager.socket_session` now returns `None` when the manager
|
|
49
|
+
runs in `ws_user` mode (previously always a `SocketSession`); use the
|
|
50
|
+
new `ws_user_socket` accessor to distinguish modes.
|
|
51
|
+
|
|
52
|
+
### Features
|
|
53
|
+
* CLI: every authentication option also resolves from a `SMARTBOX_*`
|
|
54
|
+
environment variable, and the `smartbox` console script loads a `.env`
|
|
55
|
+
file (searched upward from the working directory) before parsing
|
|
56
|
+
options. Precedence: command line > shell environment > `.env`. See
|
|
57
|
+
`.env.example`.
|
|
58
|
+
|
|
59
|
+
### Fixes
|
|
60
|
+
* CLI: `.env` parsing now follows python-dotenv semantics — an inline
|
|
61
|
+
`#` starts a comment only when it is preceded by whitespace and lies
|
|
62
|
+
outside quotes, and quoted values unquote even when a trailing
|
|
63
|
+
comment follows them. Previously inline comments silently became
|
|
64
|
+
part of the value and quote-stripping was defeated, corrupting
|
|
65
|
+
credentials written in the conventional quoted style.
|
|
66
|
+
|
|
67
|
+
### Refactor
|
|
68
|
+
* `UpdateManager`: `subscribe_to_node_status`/`_setup`/`_version`/`_prog`
|
|
69
|
+
share one `_subscribe_to_node_key` implementation.
|
|
70
|
+
|
|
71
|
+
### Tests & CI
|
|
72
|
+
* Socket tests for the namespace callbacks, websocket cleanup, SIGINT
|
|
73
|
+
handler and cancellation during connect; coverage floor of 95%
|
|
74
|
+
(`fail_under`) enforced.
|
|
75
|
+
* ruff and mypy run over `tests/` in CI; `socket.py` is no longer
|
|
76
|
+
excluded from codecov.
|
|
77
|
+
|
|
78
|
+
### Docs
|
|
79
|
+
* README rewritten: library usage, command table, `.env` section and an
|
|
80
|
+
actionable Support section. api-notes documents how to capture the
|
|
81
|
+
Basic Auth credential and the new-reseller issue template points to it.
|
|
82
|
+
|
|
3
83
|
## 2.6.2
|
|
4
84
|
|
|
5
85
|
Port round of upstream PR #64 (ajtudela, `improvements/fix_bugs`, 19
|
|
@@ -77,8 +157,8 @@ version bump).
|
|
|
77
157
|
device group move, node select/power, group geo_data/extra_data, invite
|
|
78
158
|
flows (invite/revoke/confirm — confirm is unauthenticated), quiet-home
|
|
79
159
|
notification config GET/POST + test, encrypted-wifi-credentials and
|
|
80
|
-
geocoding helpers. Read-only-verified paths pinned from the
|
|
81
|
-
|
|
160
|
+
geocoding helpers. Read-only-verified paths pinned from the official
|
|
161
|
+
vendor app; all writes marked **unverified on live hardware** in docstrings.
|
|
82
162
|
New base helpers: `_api_delete`, host-relative `_api_get` (params not
|
|
83
163
|
logged — may carry secrets) and `_api_post_path` (auth optional).
|
|
84
164
|
* Away-status writes default `enabled: true` to match the vendor app shape
|
smartbox-3.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: smartbox
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Python API to control heating 'smart boxes'
|
|
5
|
+
Project-URL: Homepage, https://github.com/jandado/smartbox
|
|
6
|
+
Project-URL: Issues, https://github.com/jandado/smartbox/issues
|
|
7
|
+
Project-URL: Upstream, https://github.com/ajtudela/smartbox
|
|
8
|
+
Author-email: Alberto Tudela <ajtudela@gmail.com>, Delmael <delmael@outlook.com>, graham33 <graham@grahambennett.org>
|
|
9
|
+
Maintainer: Jan Dado
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Requires-Python: >=3.14.2
|
|
16
|
+
Requires-Dist: aiohttp<4,>=3
|
|
17
|
+
Provides-Extra: cli
|
|
18
|
+
Requires-Dist: asyncclick; extra == 'cli'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# smartbox 3.0
|
|
22
|
+
|
|
23
|
+
Python library for Smartbox heating systems (Helki and its resellers: Elnur,
|
|
24
|
+
Haverland, Climastar, Technotherm, HJM, …).
|
|
25
|
+
|
|
26
|
+
This is the 3.0 rewrite. It exposes the protocol and domain layers that the
|
|
27
|
+
Home Assistant integration uses, without importing Home Assistant. The 2.7
|
|
28
|
+
API remains available on the `main` branch and the `v2.7.0` tag.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
From the tagged source:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pip install "smartbox @ https://github.com/jandado/smartbox/archive/refs/tags/v3.0.0.tar.gz"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
With the optional command line tool:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pip install "smartbox[cli] @ https://github.com/jandado/smartbox/archive/refs/tags/v3.0.0.tar.gz"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For development:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
uv sync --all-extras --dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Library
|
|
51
|
+
|
|
52
|
+
`SmartboxClient` owns the REST API and the token lifecycle. `smartbox.core`
|
|
53
|
+
contains the Home Assistant-free state engine, write confirmation and feed
|
|
54
|
+
supervision.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import asyncio
|
|
58
|
+
|
|
59
|
+
from smartbox.core import SmartboxEngine
|
|
60
|
+
from smartbox.protocol import API_HOST, AuthConfig, ManagedTokenSource, SmartboxClient
|
|
61
|
+
|
|
62
|
+
CONFIG = AuthConfig(
|
|
63
|
+
api_host=API_HOST,
|
|
64
|
+
basic_auth_creds="<reseller Basic auth>",
|
|
65
|
+
serial_id="1",
|
|
66
|
+
username="you@example.com",
|
|
67
|
+
password="secret",
|
|
68
|
+
referer="https://app.helki.com/",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
async def main() -> None:
|
|
72
|
+
client = SmartboxClient(CONFIG)
|
|
73
|
+
try:
|
|
74
|
+
print(await client.get_devices())
|
|
75
|
+
finally:
|
|
76
|
+
await client.close()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The lower layers are Home Assistant-free and are tested without the HA test
|
|
80
|
+
harness.
|
|
81
|
+
|
|
82
|
+
## CLI
|
|
83
|
+
|
|
84
|
+
The CLI is an optional extra; the base package only depends on `aiohttp`.
|
|
85
|
+
|
|
86
|
+
Authentication options are `-a/--api-name`, `-b/--basic-auth-creds`,
|
|
87
|
+
`-u/--username`, `-p/--password`, `-r/--x-referer`, `-i/--x-serial-id`.
|
|
88
|
+
They also resolve from `SMARTBOX_*` environment variables, and the script
|
|
89
|
+
loads the nearest `.env` file. Command line values win over the environment,
|
|
90
|
+
which wins over `.env`.
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
smartbox -a api -u you@example.com -p secret devices
|
|
94
|
+
smartbox -a api -u you@example.com -p secret nodes
|
|
95
|
+
smartbox -a api -u you@example.com -p secret status
|
|
96
|
+
smartbox -a api -u you@example.com -p secret set-status -d DEV -n 0 --mode auto
|
|
97
|
+
smartbox -a api -u you@example.com -p secret set-boost-mode -d DEV -n 0 \
|
|
98
|
+
--active --mode runback --temperature 21 --time-minutes 60
|
|
99
|
+
smartbox -a api -u you@example.com -p secret listen
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`smartbox --help` lists every command. Reads print the raw wire payloads;
|
|
103
|
+
writes print the posted path and body. `listen` follows the shared `ws_user`
|
|
104
|
+
feed and can filter updates with `--device-id`.
|
|
105
|
+
|
|
106
|
+
## Layout
|
|
107
|
+
|
|
108
|
+
- `smartbox.protocol` — auth, REST client, `ws_user` transport, wire values
|
|
109
|
+
and named writes.
|
|
110
|
+
- `smartbox.core` — versioned state store, merges, write confirmation,
|
|
111
|
+
deadlines and feed supervision.
|
|
112
|
+
- `smartbox.cli` — the optional debugging CLI.
|
|
113
|
+
|
|
114
|
+
The canonical wire record lives in the sibling `hass-smartbox` repository
|
|
115
|
+
under `docs/` (`api-contract.md`, `api-ws-user.md`, `webapi-spec.md`);
|
|
116
|
+
`webapi-spec.md` in this repo is a pointer.
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
uv run pytest
|
|
122
|
+
uv run ruff check src tests
|
|
123
|
+
uv run mypy src
|
|
124
|
+
uv run tox
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Credits
|
|
128
|
+
|
|
129
|
+
The original library and the 2.7.x line are by Alberto Tudela, Delmael,
|
|
130
|
+
graham33 and the upstream contributors. This fork carries the 3.0 rewrite;
|
|
131
|
+
upstream collaboration is welcome.
|
smartbox-3.0.0/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# smartbox 3.0
|
|
2
|
+
|
|
3
|
+
Python library for Smartbox heating systems (Helki and its resellers: Elnur,
|
|
4
|
+
Haverland, Climastar, Technotherm, HJM, …).
|
|
5
|
+
|
|
6
|
+
This is the 3.0 rewrite. It exposes the protocol and domain layers that the
|
|
7
|
+
Home Assistant integration uses, without importing Home Assistant. The 2.7
|
|
8
|
+
API remains available on the `main` branch and the `v2.7.0` tag.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
From the tagged source:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pip install "smartbox @ https://github.com/jandado/smartbox/archive/refs/tags/v3.0.0.tar.gz"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
With the optional command line tool:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pip install "smartbox[cli] @ https://github.com/jandado/smartbox/archive/refs/tags/v3.0.0.tar.gz"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For development:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
uv sync --all-extras --dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Library
|
|
31
|
+
|
|
32
|
+
`SmartboxClient` owns the REST API and the token lifecycle. `smartbox.core`
|
|
33
|
+
contains the Home Assistant-free state engine, write confirmation and feed
|
|
34
|
+
supervision.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import asyncio
|
|
38
|
+
|
|
39
|
+
from smartbox.core import SmartboxEngine
|
|
40
|
+
from smartbox.protocol import API_HOST, AuthConfig, ManagedTokenSource, SmartboxClient
|
|
41
|
+
|
|
42
|
+
CONFIG = AuthConfig(
|
|
43
|
+
api_host=API_HOST,
|
|
44
|
+
basic_auth_creds="<reseller Basic auth>",
|
|
45
|
+
serial_id="1",
|
|
46
|
+
username="you@example.com",
|
|
47
|
+
password="secret",
|
|
48
|
+
referer="https://app.helki.com/",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
async def main() -> None:
|
|
52
|
+
client = SmartboxClient(CONFIG)
|
|
53
|
+
try:
|
|
54
|
+
print(await client.get_devices())
|
|
55
|
+
finally:
|
|
56
|
+
await client.close()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The lower layers are Home Assistant-free and are tested without the HA test
|
|
60
|
+
harness.
|
|
61
|
+
|
|
62
|
+
## CLI
|
|
63
|
+
|
|
64
|
+
The CLI is an optional extra; the base package only depends on `aiohttp`.
|
|
65
|
+
|
|
66
|
+
Authentication options are `-a/--api-name`, `-b/--basic-auth-creds`,
|
|
67
|
+
`-u/--username`, `-p/--password`, `-r/--x-referer`, `-i/--x-serial-id`.
|
|
68
|
+
They also resolve from `SMARTBOX_*` environment variables, and the script
|
|
69
|
+
loads the nearest `.env` file. Command line values win over the environment,
|
|
70
|
+
which wins over `.env`.
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
smartbox -a api -u you@example.com -p secret devices
|
|
74
|
+
smartbox -a api -u you@example.com -p secret nodes
|
|
75
|
+
smartbox -a api -u you@example.com -p secret status
|
|
76
|
+
smartbox -a api -u you@example.com -p secret set-status -d DEV -n 0 --mode auto
|
|
77
|
+
smartbox -a api -u you@example.com -p secret set-boost-mode -d DEV -n 0 \
|
|
78
|
+
--active --mode runback --temperature 21 --time-minutes 60
|
|
79
|
+
smartbox -a api -u you@example.com -p secret listen
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`smartbox --help` lists every command. Reads print the raw wire payloads;
|
|
83
|
+
writes print the posted path and body. `listen` follows the shared `ws_user`
|
|
84
|
+
feed and can filter updates with `--device-id`.
|
|
85
|
+
|
|
86
|
+
## Layout
|
|
87
|
+
|
|
88
|
+
- `smartbox.protocol` — auth, REST client, `ws_user` transport, wire values
|
|
89
|
+
and named writes.
|
|
90
|
+
- `smartbox.core` — versioned state store, merges, write confirmation,
|
|
91
|
+
deadlines and feed supervision.
|
|
92
|
+
- `smartbox.cli` — the optional debugging CLI.
|
|
93
|
+
|
|
94
|
+
The canonical wire record lives in the sibling `hass-smartbox` repository
|
|
95
|
+
under `docs/` (`api-contract.md`, `api-ws-user.md`, `webapi-spec.md`);
|
|
96
|
+
`webapi-spec.md` in this repo is a pointer.
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
uv run pytest
|
|
102
|
+
uv run ruff check src tests
|
|
103
|
+
uv run mypy src
|
|
104
|
+
uv run tox
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Credits
|
|
108
|
+
|
|
109
|
+
The original library and the 2.7.x line are by Alberto Tudela, Delmael,
|
|
110
|
+
graham33 and the upstream contributors. This fork carries the 3.0 rewrite;
|
|
111
|
+
upstream collaboration is welcome.
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "smartbox"
|
|
3
|
-
version = "
|
|
3
|
+
version = "3.0.0"
|
|
4
4
|
requires-python = ">=3.14.2"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name="Alberto Tudela", email="ajtudela@gmail.com" },
|
|
7
7
|
{ name="Delmael", email="delmael@outlook.com" },
|
|
8
8
|
{ name="graham33", email="graham@grahambennett.org" },
|
|
9
9
|
]
|
|
10
|
+
maintainers = [
|
|
11
|
+
{ name="Jan Dado" },
|
|
12
|
+
]
|
|
10
13
|
description = "Python API to control heating 'smart boxes'"
|
|
11
14
|
readme = "README.md"
|
|
12
15
|
|
|
@@ -19,38 +22,44 @@ license = "MIT"
|
|
|
19
22
|
license-files = ["LICEN[CS]E*"]
|
|
20
23
|
dependencies = [
|
|
21
24
|
"aiohttp>=3,<4",
|
|
22
|
-
"asyncclick",
|
|
23
|
-
"jq",
|
|
24
|
-
"pydantic>=2,<3",
|
|
25
|
-
"python-socketio>=5,<6",
|
|
26
25
|
]
|
|
27
26
|
|
|
28
|
-
[project.
|
|
29
|
-
|
|
30
|
-
Issues = "https://github.com/ajtudela/smartbox/issues"
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
cli = ["asyncclick"]
|
|
31
29
|
|
|
32
30
|
[project.scripts]
|
|
33
|
-
smartbox = "smartbox.
|
|
31
|
+
smartbox = "smartbox.cli:cli"
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/jandado/smartbox"
|
|
35
|
+
Issues = "https://github.com/jandado/smartbox/issues"
|
|
36
|
+
Upstream = "https://github.com/ajtudela/smartbox"
|
|
34
37
|
|
|
35
38
|
[build-system]
|
|
36
39
|
requires = ["hatchling"]
|
|
37
40
|
build-backend = "hatchling.build"
|
|
38
41
|
|
|
42
|
+
[tool.hatch.build.targets.sdist]
|
|
43
|
+
include = [
|
|
44
|
+
"/src/smartbox",
|
|
45
|
+
"/tests",
|
|
46
|
+
"/README.md",
|
|
47
|
+
"/LICENSE",
|
|
48
|
+
"/CHANGELOG.md",
|
|
49
|
+
]
|
|
50
|
+
|
|
39
51
|
|
|
40
52
|
[dependency-groups]
|
|
41
53
|
dev = [
|
|
42
|
-
"
|
|
43
|
-
"
|
|
54
|
+
"asyncclick",
|
|
55
|
+
"multidict",
|
|
44
56
|
"pytest",
|
|
45
57
|
"pytest-asyncio",
|
|
46
58
|
"pytest-cov",
|
|
47
|
-
"pytest-mock",
|
|
48
|
-
"pytest-randomly",
|
|
49
|
-
"requests-mock",
|
|
50
|
-
"tox",
|
|
51
59
|
"ruff",
|
|
52
60
|
"mypy",
|
|
53
|
-
"
|
|
61
|
+
"tox",
|
|
62
|
+
"yarl",
|
|
54
63
|
]
|
|
55
64
|
|
|
56
65
|
[tool.pytest.ini_options]
|
|
@@ -61,22 +70,27 @@ markers = [
|
|
|
61
70
|
"network: tests that make real HTTP calls to reseller servers (deselected by default; run with -m network)",
|
|
62
71
|
]
|
|
63
72
|
|
|
73
|
+
[tool.coverage.run]
|
|
74
|
+
# The CLI is an optional extra; it is exercised manually against the live API
|
|
75
|
+
# and by the `--help`/reseller smoke tests, not by the core coverage gate.
|
|
76
|
+
omit = ["src/smartbox/cli.py"]
|
|
77
|
+
|
|
78
|
+
[tool.coverage.report]
|
|
79
|
+
fail_under = 95
|
|
80
|
+
exclude_also = ["if __name__ == .__main__.:"]
|
|
81
|
+
|
|
64
82
|
[tool.mypy]
|
|
65
83
|
warn_unused_configs = true
|
|
66
84
|
|
|
67
85
|
[[tool.mypy.overrides]]
|
|
68
|
-
module = [
|
|
69
|
-
"jq",
|
|
70
|
-
"socketio",
|
|
71
|
-
"asyncio",
|
|
72
|
-
]
|
|
86
|
+
module = ["asyncio"]
|
|
73
87
|
ignore_missing_imports = true
|
|
74
88
|
disable_error_code = ["arg-type"]
|
|
75
89
|
|
|
76
90
|
[tool.ruff]
|
|
77
91
|
target-version = "py314"
|
|
78
92
|
fix = true
|
|
79
|
-
line-length =
|
|
93
|
+
line-length = 88
|
|
80
94
|
extend-exclude = ["tools"]
|
|
81
95
|
|
|
82
96
|
[tool.ruff.lint]
|
|
@@ -136,8 +150,17 @@ max-complexity = 25
|
|
|
136
150
|
"S106",
|
|
137
151
|
"PLR2004",
|
|
138
152
|
"B018",
|
|
153
|
+
"PT011",
|
|
154
|
+
]
|
|
155
|
+
"src/smartbox/cli.py" = [
|
|
156
|
+
"ANN401",
|
|
157
|
+
"D10",
|
|
158
|
+
"EM101",
|
|
159
|
+
"EM102",
|
|
160
|
+
"PLR2004",
|
|
161
|
+
"TRY003",
|
|
139
162
|
]
|
|
140
|
-
"src/smartbox/
|
|
163
|
+
"src/smartbox/core/**" = ["TID252"]
|
|
141
164
|
"tools/*" = ["T201", "ANN401", "INP001", "PLR0911", "PLR0915", "S108", "ASYNC240"]
|
|
142
165
|
|
|
143
166
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Smartbox protocol and domain layers.
|
|
2
|
+
|
|
3
|
+
The package is Home Assistant-agnostic: ``smartbox.protocol`` owns the
|
|
4
|
+
wire and the ``smartbox.core`` package owns the domain state. Both are
|
|
5
|
+
usable without the HA test harness.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
9
|
+
|
|
10
|
+
from . import core, protocol
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
__version__ = version("smartbox")
|
|
14
|
+
except PackageNotFoundError: # pragma: no cover - vendored/undist import
|
|
15
|
+
__version__ = "0.0.0"
|
|
16
|
+
|
|
17
|
+
__all__ = ["__version__", "core", "protocol"]
|