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.
Files changed (154) hide show
  1. {smartbox-2.6.2 → smartbox-3.0.0}/.gitignore +6 -0
  2. {smartbox-2.6.2 → smartbox-3.0.0}/CHANGELOG.md +82 -2
  3. smartbox-3.0.0/PKG-INFO +131 -0
  4. smartbox-3.0.0/README.md +111 -0
  5. {smartbox-2.6.2 → smartbox-3.0.0}/pyproject.toml +46 -23
  6. smartbox-3.0.0/src/smartbox/__init__.py +17 -0
  7. smartbox-3.0.0/src/smartbox/cli.py +615 -0
  8. smartbox-3.0.0/src/smartbox/core/__init__.py +37 -0
  9. smartbox-3.0.0/src/smartbox/core/deadlines.py +272 -0
  10. smartbox-3.0.0/src/smartbox/core/engine.py +1772 -0
  11. smartbox-3.0.0/src/smartbox/core/feed.py +198 -0
  12. smartbox-3.0.0/src/smartbox/core/merging.py +369 -0
  13. smartbox-3.0.0/src/smartbox/core/state.py +190 -0
  14. smartbox-3.0.0/src/smartbox/protocol/__init__.py +67 -0
  15. smartbox-3.0.0/src/smartbox/protocol/auth.py +415 -0
  16. smartbox-3.0.0/src/smartbox/protocol/client.py +421 -0
  17. smartbox-3.0.0/src/smartbox/protocol/errors.py +35 -0
  18. smartbox-3.0.0/src/smartbox/protocol/events.py +65 -0
  19. smartbox-3.0.0/src/smartbox/protocol/jwt.py +75 -0
  20. smartbox-3.0.0/src/smartbox/protocol/paths.py +31 -0
  21. {smartbox-2.6.2/src/smartbox → smartbox-3.0.0/src/smartbox/protocol}/reseller.py +54 -26
  22. smartbox-3.0.0/src/smartbox/protocol/retry.py +38 -0
  23. smartbox-3.0.0/src/smartbox/protocol/timing.py +24 -0
  24. smartbox-3.0.0/src/smartbox/protocol/transport.py +78 -0
  25. smartbox-3.0.0/src/smartbox/protocol/values.py +314 -0
  26. smartbox-3.0.0/src/smartbox/protocol/writes.py +740 -0
  27. smartbox-3.0.0/src/smartbox/protocol/ws_user.py +735 -0
  28. smartbox-3.0.0/tests/fake_api.py +393 -0
  29. smartbox-3.0.0/tests/fake_transport.py +77 -0
  30. smartbox-3.0.0/tests/fakes.py +253 -0
  31. smartbox-3.0.0/tests/fixtures/live/README.md +11 -0
  32. smartbox-3.0.0/tests/test_auth.py +597 -0
  33. smartbox-3.0.0/tests/test_cli.py +21 -0
  34. smartbox-3.0.0/tests/test_client.py +625 -0
  35. smartbox-3.0.0/tests/test_core_availability.py +1269 -0
  36. smartbox-3.0.0/tests/test_core_engine.py +1368 -0
  37. smartbox-3.0.0/tests/test_core_state.py +74 -0
  38. smartbox-3.0.0/tests/test_core_writes.py +1360 -0
  39. smartbox-3.0.0/tests/test_layering.py +30 -0
  40. smartbox-3.0.0/tests/test_reseller.py +40 -0
  41. smartbox-3.0.0/tests/test_retry.py +24 -0
  42. smartbox-3.0.0/tests/test_values.py +255 -0
  43. smartbox-3.0.0/tests/test_writes.py +790 -0
  44. smartbox-3.0.0/tests/test_ws_user_transport.py +1231 -0
  45. smartbox-3.0.0/tests/utils.py +54 -0
  46. smartbox-2.6.2/.github/FUNDING.yml +0 -3
  47. smartbox-2.6.2/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  48. smartbox-2.6.2/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  49. smartbox-2.6.2/.github/ISSUE_TEMPLATE/new-reseller.md +0 -18
  50. smartbox-2.6.2/.github/dependabot.yml +0 -19
  51. smartbox-2.6.2/.github/workflows/publish-to-pypi.yml +0 -31
  52. smartbox-2.6.2/.github/workflows/python-package.yml +0 -88
  53. smartbox-2.6.2/.pre-commit-config.yaml +0 -29
  54. smartbox-2.6.2/.vscode/settings.json +0 -12
  55. smartbox-2.6.2/CODEOWNERS +0 -5
  56. smartbox-2.6.2/PKG-INFO +0 -202
  57. smartbox-2.6.2/README.md +0 -182
  58. smartbox-2.6.2/ajtudela-pr-64-audit.md +0 -497
  59. smartbox-2.6.2/api-notes.md +0 -603
  60. smartbox-2.6.2/codecov.yml +0 -10
  61. smartbox-2.6.2/src/smartbox/__init__.py +0 -88
  62. smartbox-2.6.2/src/smartbox/cmd.py +0 -581
  63. smartbox-2.6.2/src/smartbox/error.py +0 -41
  64. smartbox-2.6.2/src/smartbox/models.py +0 -325
  65. smartbox-2.6.2/src/smartbox/session.py +0 -1869
  66. smartbox-2.6.2/src/smartbox/socket.py +0 -523
  67. smartbox-2.6.2/src/smartbox/update_manager.py +0 -764
  68. smartbox-2.6.2/tests/common.py +0 -28
  69. smartbox-2.6.2/tests/conftest.py +0 -142
  70. smartbox-2.6.2/tests/test_cmd.py +0 -521
  71. smartbox-2.6.2/tests/test_live_payloads.py +0 -133
  72. smartbox-2.6.2/tests/test_models.py +0 -334
  73. smartbox-2.6.2/tests/test_reseller.py +0 -65
  74. smartbox-2.6.2/tests/test_session.py +0 -2768
  75. smartbox-2.6.2/tests/test_session_reliability.py +0 -552
  76. smartbox-2.6.2/tests/test_socket.py +0 -501
  77. smartbox-2.6.2/tests/test_update_manager.py +0 -940
  78. smartbox-2.6.2/tools/api_functional_tests.py +0 -233
  79. smartbox-2.6.2/tools/away_probe.py +0 -109
  80. smartbox-2.6.2/tools/capture_prog.py +0 -84
  81. smartbox-2.6.2/tools/live_check.py +0 -137
  82. smartbox-2.6.2/tools/probe_extra_options.py +0 -145
  83. smartbox-2.6.2/tools/probe_modified_auto.py +0 -214
  84. smartbox-2.6.2/tools/probe_settle_window.py +0 -146
  85. smartbox-2.6.2/tools/prog_functional_tests.py +0 -143
  86. smartbox-2.6.2/tools/watch_status.py +0 -123
  87. smartbox-2.6.2/uv.lock +0 -1424
  88. smartbox-2.6.2/webapi-spec.md +0 -186
  89. {smartbox-2.6.2 → smartbox-3.0.0}/LICENSE +0 -0
  90. {smartbox-2.6.2 → smartbox-3.0.0}/src/smartbox/py.typed +0 -0
  91. {smartbox-2.6.2 → smartbox-3.0.0}/tests/__init__.py +0 -0
  92. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/acm/1/samples.json +0 -0
  93. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/acm/1/setup.json +0 -0
  94. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/acm/1/status.json +0 -0
  95. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/acm/1/version.json +0 -0
  96. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/connected.json +0 -0
  97. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr/0/samples.json +0 -0
  98. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr/0/setup.json +0 -0
  99. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr/0/status.json +0 -0
  100. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr/0/version.json +0 -0
  101. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/htr_system/power_limit.json +0 -0
  102. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/mgr/away_status.json +0 -0
  103. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/mgr/nodes.json +0 -0
  104. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/pmo/3/samples.json +0 -0
  105. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/pmo/3/setup.json +0 -0
  106. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/pmo/3/status.json +0 -0
  107. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device1/pmo/3/version.json +0 -0
  108. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/connected.json +0 -0
  109. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/0/samples.json +0 -0
  110. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/0/setup.json +0 -0
  111. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/0/status.json +0 -0
  112. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/0/version.json +0 -0
  113. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/1/samples.json +0 -0
  114. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/1/setup.json +0 -0
  115. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/1/status.json +0 -0
  116. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/1/version.json +0 -0
  117. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/2/samples.json +0 -0
  118. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/2/setup.json +0 -0
  119. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/2/status.json +0 -0
  120. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/2/version.json +0 -0
  121. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/3/samples.json +0 -0
  122. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/3/setup.json +0 -0
  123. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/3/status.json +0 -0
  124. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/3/version.json +0 -0
  125. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/4/samples.json +0 -0
  126. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/4/setup.json +0 -0
  127. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/4/status.json +0 -0
  128. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/4/version.json +0 -0
  129. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/5/samples.json +0 -0
  130. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/5/setup.json +0 -0
  131. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/5/status.json +0 -0
  132. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_mod/5/version.json +0 -0
  133. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/htr_system/power_limit.json +0 -0
  134. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/mgr/away_status.json +0 -0
  135. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs/device2/mgr/nodes.json +0 -0
  136. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/devs.json +0 -0
  137. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/grouped_devs.json +0 -0
  138. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/groups/test_home/guest_users.json +0 -0
  139. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr2.json +0 -0
  140. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr3.json +0 -0
  141. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr4.json +0 -0
  142. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr5.json +0 -0
  143. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_prog_addr6.json +0 -0
  144. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr2.json +0 -0
  145. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr3.json +0 -0
  146. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr4.json +0 -0
  147. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr5.json +0 -0
  148. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_addr6.json +0 -0
  149. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_setup_no_boost.json +0 -0
  150. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/htr_status_boost_off.json +0 -0
  151. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/socket_update_status_frame.json +0 -0
  152. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/socket_update_sync_lost_frame.json +0 -0
  153. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/live/socket_update_version_frame.json +0 -0
  154. {smartbox-2.6.2 → smartbox-3.0.0}/tests/fixtures/test_path.json +0 -0
@@ -73,3 +73,9 @@ junit.xml
73
73
  .DS_Store
74
74
 
75
75
  *.code-workspace
76
+
77
+ # ws_user probe token stores (contain live credentials)
78
+ *token*store*.json
79
+ wsu_store.json
80
+ *token*store*.json.tmp
81
+ wsu_store.json.tmp
@@ -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 vendor app
81
- bundle; all writes marked **unverified on live hardware** in docstrings.
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
@@ -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.
@@ -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 = "2.6.2"
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.urls]
29
- Homepage = "https://github.com/ajtudela/smartbox"
30
- Issues = "https://github.com/ajtudela/smartbox/issues"
27
+ [project.optional-dependencies]
28
+ cli = ["asyncclick"]
31
29
 
32
30
  [project.scripts]
33
- smartbox = "smartbox.cmd: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
- "pre-commit",
43
- "freezegun",
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
- "time-machine",
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 = 80
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/cmd.py" = ["T201", "ANN001"]
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"]