smartbox 2.5.1__tar.gz → 2.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {smartbox-2.5.1 → smartbox-2.6.2}/.github/FUNDING.yml +1 -2
- {smartbox-2.5.1 → smartbox-2.6.2}/.github/workflows/publish-to-pypi.yml +2 -2
- {smartbox-2.5.1 → smartbox-2.6.2}/.github/workflows/python-package.yml +7 -7
- {smartbox-2.5.1 → smartbox-2.6.2}/.gitignore +1 -1
- smartbox-2.6.2/.vscode/settings.json +12 -0
- smartbox-2.6.2/CHANGELOG.md +312 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/CODEOWNERS +1 -1
- {smartbox-2.5.1 → smartbox-2.6.2}/PKG-INFO +27 -15
- {smartbox-2.5.1 → smartbox-2.6.2}/README.md +22 -8
- smartbox-2.6.2/ajtudela-pr-64-audit.md +497 -0
- smartbox-2.6.2/api-notes.md +603 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/pyproject.toml +22 -18
- {smartbox-2.5.1 → smartbox-2.6.2}/src/smartbox/__init__.py +35 -1
- {smartbox-2.5.1 → smartbox-2.6.2}/src/smartbox/cmd.py +182 -31
- smartbox-2.6.2/src/smartbox/error.py +41 -0
- smartbox-2.6.2/src/smartbox/models.py +325 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/src/smartbox/reseller.py +5 -11
- smartbox-2.6.2/src/smartbox/session.py +1869 -0
- smartbox-2.6.2/src/smartbox/socket.py +523 -0
- smartbox-2.6.2/src/smartbox/update_manager.py +764 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/common.py +11 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/conftest.py +9 -1
- smartbox-2.6.2/tests/fixtures/devs/device1/acm/1/samples.json +2839 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/acm/1/setup.json +40 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/acm/1/status.json +24 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/acm/1/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/htr/0/samples.json +2839 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/htr/0/setup.json +40 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/htr/0/status.json +24 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/htr/0/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/htr_system/power_limit.json +3 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/mgr/away_status.json +5 -0
- smartbox-2.6.2/tests/fixtures/devs/device1/pmo/3/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/0/samples.json +2839 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/0/setup.json +40 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/0/status.json +24 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/0/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/1/setup.json +40 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/1/status.json +24 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/1/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/2/setup.json +40 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/2/status.json +24 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/2/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/3/setup.json +40 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/3/status.json +24 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/3/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/4/setup.json +40 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/4/status.json +24 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/4/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/5/setup.json +40 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/5/status.json +24 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_mod/5/version.json +6 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/htr_system/power_limit.json +3 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/mgr/away_status.json +5 -0
- smartbox-2.6.2/tests/fixtures/devs/device2/mgr/nodes.json +46 -0
- smartbox-2.6.2/tests/fixtures/devs.json +19 -0
- smartbox-2.6.2/tests/fixtures/grouped_devs.json +29 -0
- smartbox-2.6.2/tests/fixtures/live/htr_prog_addr2.json +187 -0
- smartbox-2.6.2/tests/fixtures/live/htr_prog_addr3.json +187 -0
- smartbox-2.6.2/tests/fixtures/live/htr_prog_addr4.json +187 -0
- smartbox-2.6.2/tests/fixtures/live/htr_prog_addr5.json +187 -0
- smartbox-2.6.2/tests/fixtures/live/htr_prog_addr6.json +187 -0
- smartbox-2.6.2/tests/fixtures/live/htr_setup_addr2.json +24 -0
- smartbox-2.6.2/tests/fixtures/live/htr_setup_addr3.json +24 -0
- smartbox-2.6.2/tests/fixtures/live/htr_setup_addr4.json +24 -0
- smartbox-2.6.2/tests/fixtures/live/htr_setup_addr5.json +24 -0
- smartbox-2.6.2/tests/fixtures/live/htr_setup_addr6.json +24 -0
- smartbox-2.6.2/tests/fixtures/live/htr_setup_no_boost.json +24 -0
- smartbox-2.6.2/tests/fixtures/live/htr_status_boost_off.json +25 -0
- smartbox-2.6.2/tests/fixtures/live/socket_update_status_frame.json +28 -0
- smartbox-2.6.2/tests/fixtures/live/socket_update_sync_lost_frame.json +6 -0
- smartbox-2.6.2/tests/fixtures/live/socket_update_version_frame.json +9 -0
- smartbox-2.6.2/tests/fixtures/test_path.json +3 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/test_cmd.py +137 -1
- smartbox-2.6.2/tests/test_live_payloads.py +133 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/test_models.py +51 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/test_reseller.py +14 -5
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/test_session.py +1406 -264
- smartbox-2.6.2/tests/test_session_reliability.py +552 -0
- smartbox-2.6.2/tests/test_socket.py +501 -0
- smartbox-2.6.2/tests/test_update_manager.py +940 -0
- smartbox-2.6.2/tools/api_functional_tests.py +233 -0
- smartbox-2.6.2/tools/away_probe.py +109 -0
- smartbox-2.6.2/tools/capture_prog.py +84 -0
- smartbox-2.6.2/tools/live_check.py +137 -0
- smartbox-2.6.2/tools/probe_extra_options.py +145 -0
- smartbox-2.6.2/tools/probe_modified_auto.py +214 -0
- smartbox-2.6.2/tools/probe_settle_window.py +146 -0
- smartbox-2.6.2/tools/prog_functional_tests.py +143 -0
- smartbox-2.6.2/tools/watch_status.py +123 -0
- smartbox-2.6.2/uv.lock +1424 -0
- smartbox-2.6.2/webapi-spec.md +186 -0
- smartbox-2.5.1/.vscode/settings.json +0 -7
- smartbox-2.5.1/CHANGELOG.md +0 -39
- smartbox-2.5.1/api-notes.md +0 -141
- smartbox-2.5.1/src/smartbox/error.py +0 -19
- smartbox-2.5.1/src/smartbox/models.py +0 -273
- smartbox-2.5.1/src/smartbox/session.py +0 -664
- smartbox-2.5.1/src/smartbox/socket.py +0 -313
- smartbox-2.5.1/src/smartbox/update_manager.py +0 -298
- smartbox-2.5.1/tests/fixtures/devs/device1/acm/1/samples.json +0 -2839
- smartbox-2.5.1/tests/fixtures/devs/device1/acm/1/setup.json +0 -40
- smartbox-2.5.1/tests/fixtures/devs/device1/acm/1/status.json +0 -24
- smartbox-2.5.1/tests/fixtures/devs/device1/acm/1/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device1/htr/0/samples.json +0 -2839
- smartbox-2.5.1/tests/fixtures/devs/device1/htr/0/setup.json +0 -40
- smartbox-2.5.1/tests/fixtures/devs/device1/htr/0/status.json +0 -24
- smartbox-2.5.1/tests/fixtures/devs/device1/htr/0/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device1/htr_system/power_limit.json +0 -1
- smartbox-2.5.1/tests/fixtures/devs/device1/mgr/away_status.json +0 -1
- smartbox-2.5.1/tests/fixtures/devs/device1/pmo/3/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/0/samples.json +0 -2839
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/0/setup.json +0 -40
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/0/status.json +0 -24
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/0/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/1/setup.json +0 -40
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/1/status.json +0 -24
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/1/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/2/setup.json +0 -40
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/2/status.json +0 -24
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/2/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/3/setup.json +0 -40
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/3/status.json +0 -24
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/3/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/4/setup.json +0 -40
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/4/status.json +0 -24
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/4/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/5/setup.json +0 -40
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/5/status.json +0 -24
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_mod/5/version.json +0 -6
- smartbox-2.5.1/tests/fixtures/devs/device2/htr_system/power_limit.json +0 -1
- smartbox-2.5.1/tests/fixtures/devs/device2/mgr/away_status.json +0 -1
- smartbox-2.5.1/tests/fixtures/devs/device2/mgr/nodes.json +0 -46
- smartbox-2.5.1/tests/fixtures/devs.json +0 -19
- smartbox-2.5.1/tests/fixtures/grouped_devs.json +0 -24
- smartbox-2.5.1/tests/fixtures/test_path.json +0 -1
- smartbox-2.5.1/tests/test_update_manager.py +0 -283
- smartbox-2.5.1/uv.lock +0 -1144
- {smartbox-2.5.1 → smartbox-2.6.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/.github/ISSUE_TEMPLATE/new-reseller.md +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/.github/dependabot.yml +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/.pre-commit-config.yaml +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/LICENSE +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/codecov.yml +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/src/smartbox/py.typed +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/__init__.py +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device1/connected.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device1/mgr/nodes.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device1/pmo/3/samples.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device1/pmo/3/setup.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device1/pmo/3/status.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device2/connected.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device2/htr_mod/1/samples.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device2/htr_mod/2/samples.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device2/htr_mod/3/samples.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device2/htr_mod/4/samples.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/devs/device2/htr_mod/5/samples.json +0 -0
- {smartbox-2.5.1 → smartbox-2.6.2}/tests/fixtures/groups/test_home/guest_users.json +0 -0
|
@@ -14,7 +14,7 @@ jobs:
|
|
|
14
14
|
permissions:
|
|
15
15
|
id-token: write
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
18
|
with:
|
|
19
19
|
fetch-depth: 0
|
|
20
20
|
- name: Install uv
|
|
@@ -22,7 +22,7 @@ jobs:
|
|
|
22
22
|
with:
|
|
23
23
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
24
24
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
|
25
|
-
uses: actions/setup-python@
|
|
25
|
+
uses: actions/setup-python@v7.0.0
|
|
26
26
|
with:
|
|
27
27
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
28
28
|
- name: Build a binary wheel and a source tarball
|
|
@@ -18,13 +18,13 @@ jobs:
|
|
|
18
18
|
matrix:
|
|
19
19
|
python-version: [3.14]
|
|
20
20
|
steps:
|
|
21
|
-
- uses: actions/checkout@
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
22
|
- name: Install uv
|
|
23
23
|
uses: astral-sh/setup-uv@v7
|
|
24
24
|
with:
|
|
25
25
|
python-version: ${{ matrix.python-version }}
|
|
26
26
|
- name: Set up Python ${{ matrix.python-version }}
|
|
27
|
-
uses: actions/setup-python@
|
|
27
|
+
uses: actions/setup-python@v7.0.0
|
|
28
28
|
with:
|
|
29
29
|
python-version: ${{ matrix.python-version }}
|
|
30
30
|
- name: Install the project
|
|
@@ -40,13 +40,13 @@ jobs:
|
|
|
40
40
|
python-version: [3.14]
|
|
41
41
|
|
|
42
42
|
steps:
|
|
43
|
-
- uses: actions/checkout@
|
|
43
|
+
- uses: actions/checkout@v7
|
|
44
44
|
- name: Install uv
|
|
45
45
|
uses: astral-sh/setup-uv@v7
|
|
46
46
|
with:
|
|
47
47
|
python-version: ${{ matrix.python-version }}
|
|
48
48
|
- name: Set up Python ${{ matrix.python-version }}
|
|
49
|
-
uses: actions/setup-python@
|
|
49
|
+
uses: actions/setup-python@v7.0.0
|
|
50
50
|
with:
|
|
51
51
|
python-version: ${{ matrix.python-version }}
|
|
52
52
|
- name: Install the project
|
|
@@ -62,14 +62,14 @@ jobs:
|
|
|
62
62
|
python-version: [3.14]
|
|
63
63
|
|
|
64
64
|
steps:
|
|
65
|
-
- uses: actions/checkout@
|
|
65
|
+
- uses: actions/checkout@v7
|
|
66
66
|
- name: Install uv
|
|
67
67
|
uses: astral-sh/setup-uv@v7
|
|
68
68
|
with:
|
|
69
69
|
python-version: ${{ matrix.python-version }}
|
|
70
70
|
|
|
71
71
|
- name: Set up Python ${{ matrix.python-version }}
|
|
72
|
-
uses: actions/setup-python@
|
|
72
|
+
uses: actions/setup-python@v7.0.0
|
|
73
73
|
with:
|
|
74
74
|
python-version: ${{ matrix.python-version }}
|
|
75
75
|
- name: Install the project
|
|
@@ -77,7 +77,7 @@ jobs:
|
|
|
77
77
|
uv sync --all-extras --dev
|
|
78
78
|
uv run tox -e py
|
|
79
79
|
- name: "Upload coverage to Codecov"
|
|
80
|
-
uses: codecov/codecov-action@
|
|
80
|
+
uses: codecov/codecov-action@v7.1.1
|
|
81
81
|
with:
|
|
82
82
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
83
83
|
fail_ci_if_error: true
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"[python]": {
|
|
3
|
+
"editor.codeActionsOnSave": {
|
|
4
|
+
"source.fixAll.ruff": "explicit",
|
|
5
|
+
"source.organizeImports.ruff": "explicit"
|
|
6
|
+
},
|
|
7
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
|
8
|
+
"editor.formatOnSave": true
|
|
9
|
+
},
|
|
10
|
+
"python.testing.pytestEnabled": true,
|
|
11
|
+
"python.testing.unittestEnabled": false
|
|
12
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.6.2
|
|
4
|
+
|
|
5
|
+
Port round of upstream PR #64 (ajtudela, `improvements/fix_bugs`, 19
|
|
6
|
+
commits) — full per-commit audit, verdicts and evidence in
|
|
7
|
+
ajtudela-pr-64-audit.md. Ten commits were already fixed by the 2.6.x
|
|
8
|
+
round (independent convergence); of the remainder, seven were ported
|
|
9
|
+
(each commit references the upstream source hash), one was declined by
|
|
10
|
+
decision (#7: typed-mode sparse statuses keep the rich
|
|
11
|
+
`SmartboxValidationError` rejection — the bare `{"sync_status": "lost"}`
|
|
12
|
+
dead-node frame is first-class there), and one is upstream-only (#18,
|
|
13
|
+
version bump).
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
* `set_node_setup` never serializes the read-modify-write GET through
|
|
17
|
+
the Pydantic models: the internal read is always raw, so keys the
|
|
18
|
+
setup models do not declare (e.g. `counter_offset` on pmo nodes) can
|
|
19
|
+
no longer be wiped on the device by a setup write (upstream `5618aa3`).
|
|
20
|
+
Completed by `extra="allow"` on `DefaultNodeSetup`/`PmoSetup`, so
|
|
21
|
+
typed (`raw_response=False`) reads keep unknown keys and round-trip
|
|
22
|
+
losslessly (upstream `5618aa3`, completion by decision).
|
|
23
|
+
* `InvalidAuthError`, `APIUnavailableError` and `ResellerNotExistError`
|
|
24
|
+
now derive from `SmartboxError` — one `except SmartboxError` catches
|
|
25
|
+
every library failure. `APIUnavailableError` keeps its
|
|
26
|
+
`aiohttp.ClientConnectionError` base for backwards compatibility; the
|
|
27
|
+
data-path 403 → `SmartboxError` mapping is kept deliberately
|
|
28
|
+
(upstream `62efcf8`).
|
|
29
|
+
* CLI: unknown `-d`/`-n` values raise `click.BadParameter` with a
|
|
30
|
+
`param_hint` (exit code 2, usage line, offending option named)
|
|
31
|
+
instead of `ClickException`; malformed wire node payloads stay
|
|
32
|
+
`ClickException` (upstream `6ad2014`).
|
|
33
|
+
|
|
34
|
+
### Build & Docs
|
|
35
|
+
* Core dependencies bounded to their current major: `aiohttp>=3,<4`,
|
|
36
|
+
`pydantic>=2,<3`, `python-socketio>=5,<6` (upstream `da3bb9c`; the
|
|
37
|
+
commit's unused-dependency removal was already done in 2.6.0).
|
|
38
|
+
* `.vscode/settings.json`: pylint/flake8 editor settings replaced with
|
|
39
|
+
the ruff formatter/code-actions setup (upstream `a78bc3a`; its
|
|
40
|
+
pyproject/ruff halves were already done in 2.6.0).
|
|
41
|
+
* CODEOWNERS co-owner and README badge reflow adopted from upstream
|
|
42
|
+
`2ec1ef0`.
|
|
43
|
+
|
|
44
|
+
### Tests
|
|
45
|
+
* `test_all_resellers` (real HTTP calls to twelve reseller servers)
|
|
46
|
+
moved behind a `network` marker, deselected by default; run on demand
|
|
47
|
+
with `pytest -m network`. Sessions in it are closed (upstream
|
|
48
|
+
`0bef64c`).
|
|
49
|
+
|
|
50
|
+
## 2.6.1
|
|
51
|
+
|
|
52
|
+
### Features
|
|
53
|
+
* Node reachability / write-confirmation tracking on `UpdateManager`:
|
|
54
|
+
`subscribe_to_node_availability` / `get_node_availability` report a
|
|
55
|
+
node Available/Unavailable from its status/setup frames — a bare
|
|
56
|
+
`{"sync_status": "lost"}` frame arms a uniform 6 s grace timer
|
|
57
|
+
(`unavailable_delay` knob, matches the vendor app's unreachable
|
|
58
|
+
window); any ok frame reports Available again. `expect_write_confirmation`
|
|
59
|
+
(knob `write_confirm_timeout`, 6 s) arms a window after an accepted
|
|
60
|
+
write: if no ok frame arrives, a one-shot discriminating GET decides
|
|
61
|
+
"applied (node alive)" vs "silently dropped (node unreachable)" —
|
|
62
|
+
writes to unreachable nodes are ACKed OK with no device ack (observed
|
|
63
|
+
live). Frame taps register lazily; both features share them, each
|
|
64
|
+
enables them independently. Backed by live probes 2026-09-26/30
|
|
65
|
+
(api-notes.md, "Node reachability").
|
|
66
|
+
|
|
67
|
+
## 2.6.0
|
|
68
|
+
|
|
69
|
+
### Features
|
|
70
|
+
* Full node programme (schedule) support: `get_node_prog` / `set_node_prog`
|
|
71
|
+
(+ typed `NodeProg` model, sync `Session.get_prog` / `set_prog` wrappers,
|
|
72
|
+
`subscribe_to_node_prog` websocket helper and `prog` / `set-prog` CLI
|
|
73
|
+
commands). Wire note: GET and POST both use the day-keyed object shape;
|
|
74
|
+
the array shape is rejected with 400 (observed live).
|
|
75
|
+
* Full management/fleet surface per webapi-spec §2–3: device discovery
|
|
76
|
+
GET/POST, device rtc/time, device/node/group rename, device/node delete,
|
|
77
|
+
device group move, node select/power, group geo_data/extra_data, invite
|
|
78
|
+
flows (invite/revoke/confirm — confirm is unauthenticated), quiet-home
|
|
79
|
+
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.
|
|
82
|
+
New base helpers: `_api_delete`, host-relative `_api_get` (params not
|
|
83
|
+
logged — may carry secrets) and `_api_post_path` (auth optional).
|
|
84
|
+
* Away-status writes default `enabled: true` to match the vendor app shape
|
|
85
|
+
(`{away, enabled}`); the server also honours `{away}` alone (D1 verified
|
|
86
|
+
live).
|
|
87
|
+
* Setup models widened for the fw-1.9 htr family: optional
|
|
88
|
+
`user_duty_factor` / `flash_version` / `extra_options`; new
|
|
89
|
+
`max_stemp_limit` / `priority` / `revision` and
|
|
90
|
+
`factory_options.operating_mode` / `power_factor` / `super_lock_available`.
|
|
91
|
+
Its `factory_options` carries no `prog_resolution` (schedule resolution
|
|
92
|
+
not readable from setup on this family).
|
|
93
|
+
|
|
94
|
+
### Bug Fixes
|
|
95
|
+
* `SocketSession.cancel()` is now bounded by a timeout (no more unbounded
|
|
96
|
+
hangs on a wedged websocket) and disconnects exactly once (cancel +
|
|
97
|
+
shutdown no longer double-disconnect).
|
|
98
|
+
* The websocket loop checks the exit flag before touching the REST API, so
|
|
99
|
+
cancel is no longer delayed by a slow auth refresh.
|
|
100
|
+
* Dangling background cleanup tasks are awaited (bounded) on shutdown
|
|
101
|
+
instead of dying as "Task was destroyed but it is pending" noise.
|
|
102
|
+
* Routine retry log downgraded from `_LOGGER.exception` to `_LOGGER.warning`.
|
|
103
|
+
* Status models widened for the newer htr firmware family (`error_code`
|
|
104
|
+
str|int; optional `act_duty` / `power_pcb_temp`); optional fields
|
|
105
|
+
`easy` / `runback` / `version` tolerated.
|
|
106
|
+
|
|
107
|
+
* Code-review round 3 (2026-09-27): request-timeouts and HTTP 5xx now map
|
|
108
|
+
to `APIUnavailableError` (aiohttp's total-timeout raises a plain
|
|
109
|
+
`TimeoutError` that previously escaped all error handling; 5xx GETs are
|
|
110
|
+
retried via the existing retry wrapper — 4xx still raises
|
|
111
|
+
`SmartboxError`); request bodies are redacted in debug logs (the
|
|
112
|
+
invite-confirmation `pass` leaked in plaintext); subscription user
|
|
113
|
+
callbacks are isolated — one raising callback can no longer starve the
|
|
114
|
+
other subscriptions or propagate into the socket loop, and jq-emitted
|
|
115
|
+
`null` node payloads no longer fire callbacks; the socket reconnect
|
|
116
|
+
backoff is capped at 30s per attempt (previously up to ~8.5 min of
|
|
117
|
+
futile retries before the token refresh that usually fixes the
|
|
118
|
+
disconnect); `_attempt_connection` also tolerates transient
|
|
119
|
+
`TimeoutError`/`OSError` from the websocket transport instead of
|
|
120
|
+
killing the run loop; the ping task is cancelled **and** reaped
|
|
121
|
+
(bounded) on cancel/shutdown; `get_device_power_limit` raises
|
|
122
|
+
`SmartboxError` instead of `KeyError` on an unexpected payload;
|
|
123
|
+
sync `Session` annotations aligned with the async surface (`set_setup`
|
|
124
|
+
→ `None`, `get_status`/`get_setup`/`get_prog` may return models);
|
|
125
|
+
CLI: non-numeric node addresses give a clean error, `resellers` prints
|
|
126
|
+
a readable list without credentials, and `-u`/`-p` accept the
|
|
127
|
+
`SMARTBOX_USERNAME`/`SMARTBOX_PASSWORD` env vars; `__version__` no
|
|
128
|
+
longer explodes when imported uninstalled.
|
|
129
|
+
|
|
130
|
+
* Code-review round 4 (2026-09-27): sync `Session` no longer fails on the
|
|
131
|
+
second consecutive call with "RuntimeError: Session is closed" (the
|
|
132
|
+
per-call cleanup now forgets the closed client session so the next
|
|
133
|
+
`asyncio.run` recreates it — regression test drives the real client
|
|
134
|
+
through a local HTTP server); sync `get_device_power_limit` /
|
|
135
|
+
`set_device_power_limit` regain the async `node` parameter (pmo devices
|
|
136
|
+
were unreachable from the sync API) and sync wrappers were added for
|
|
137
|
+
`health_check`, `api_version`, `get_device_connected`,
|
|
138
|
+
`get_node_samples` and `get_node_version`; CLI `-a/--api-name` defaults
|
|
139
|
+
to `api` and session/API failures (auth, network, unknown reseller)
|
|
140
|
+
surface as clean `ClickException` errors instead of tracebacks;
|
|
141
|
+
`set_node_prog` rejects array-shape / `prog`-less payloads with a clear
|
|
142
|
+
error instead of an `AttributeError` traceback or a silent no-op POST
|
|
143
|
+
(CLI guard included); a token endpoint 200 with a non-JSON body raises
|
|
144
|
+
`SmartboxError` instead of `InvalidAuthError` (it must not trigger the
|
|
145
|
+
consumer's reauth flow); `SocketSession.run` authenticates before the
|
|
146
|
+
first connect attempt (a cold CLI session no longer burns its
|
|
147
|
+
reconnect budget on an empty token); the CLI SIGINT handler is removed
|
|
148
|
+
at teardown and platforms without signal-handler support no longer die
|
|
149
|
+
on connect; package exports completed (`NodeVersion` and the remaining
|
|
150
|
+
public model types).
|
|
151
|
+
|
|
152
|
+
* Code-review round 5 (2026-09-27) — **contract changes**:
|
|
153
|
+
* `raw_response=True` no longer validates responses: `get_devices`,
|
|
154
|
+
`get_homes`, `get_grouped_devices`, `get_home_guests`,
|
|
155
|
+
`get_device_connected` and `get_device_away_status` return the wire
|
|
156
|
+
payload (with only the documented list extraction, e.g. `devs` +
|
|
157
|
+
`invited_to` flattened) instead of model-serialised dicts. Unknown
|
|
158
|
+
keys are kept (live: `grouped_devs` carries `extraData`, previously
|
|
159
|
+
dropped); a payload too malformed to extract from raises
|
|
160
|
+
`SmartboxError`. All raw getters now follow the same rule.
|
|
161
|
+
* New `SmartboxValidationError(SmartboxError)`, exported: with
|
|
162
|
+
`raw_response=False`, payload drift raises it (chained from the
|
|
163
|
+
pydantic error) instead of a bare `pydantic.ValidationError`.
|
|
164
|
+
* Every library call is bounded by a 30s whole-call deadline covering
|
|
165
|
+
token refresh, retries and backoff — also on caller-provided
|
|
166
|
+
websessions (previously aiohttp's 300s default applied there, and 5
|
|
167
|
+
retries could stretch one call to minutes). Expiry raises
|
|
168
|
+
`APIUnavailableError`; a backoff that would overrun it is skipped.
|
|
169
|
+
* A rejected refresh token falls back to one password login before
|
|
170
|
+
`InvalidAuthError` is raised; a mid-session 401 invalidates the
|
|
171
|
+
token and retries the request once after re-authenticating.
|
|
172
|
+
* Code-review round 5 — reliability fixes:
|
|
173
|
+
* The websocket loop survives transient auth-refresh failures
|
|
174
|
+
(`APIUnavailableError`/`SmartboxError` are waited out with capped
|
|
175
|
+
backoff; previously one network blip ended `run()` for good); only
|
|
176
|
+
rejected credentials end it.
|
|
177
|
+
* `cancel()` during reconnect backoff stops the reconnect loop
|
|
178
|
+
immediately (it used to keep attempting), and a connect completing
|
|
179
|
+
after `cancel()` is disconnected instead of leaking a live socket
|
|
180
|
+
that kept `run()` alive.
|
|
181
|
+
* The keepalive ping task survives a failed send (it died for the
|
|
182
|
+
rest of the session on one `BadNamespaceError`).
|
|
183
|
+
* Secrets: the INFO "Successfully connected" log no longer prints the
|
|
184
|
+
access token; error messages and logs no longer embed request query
|
|
185
|
+
strings (aiohttp's exception text leaked the wifi password of
|
|
186
|
+
`get_encrypted_wifi_credentials`).
|
|
187
|
+
* Truncated response bodies (`ClientPayloadError`) map to
|
|
188
|
+
`APIUnavailableError` (retried on GETs) and undecodable JSON to
|
|
189
|
+
`SmartboxError` — both previously escaped the library's error types.
|
|
190
|
+
* `retry_attempts=0` makes one attempt instead of raising `TypeError`.
|
|
191
|
+
* Update subscriptions anchor their paths (`/prog` no longer matches
|
|
192
|
+
`/prog_temps` frames, likewise status/setup/version).
|
|
193
|
+
* Hygiene: 45 unneeded `type: ignore`s removed, sync
|
|
194
|
+
`Session.set_device_away_status` annotated `-> None`, dead
|
|
195
|
+
`_basic_auth_credentials` attribute removed, `socket.py` imports
|
|
196
|
+
`urllib.parse` explicitly.
|
|
197
|
+
|
|
198
|
+
* Code-review round 6 (2026-09-27, every finding reproduced by a probe
|
|
199
|
+
before fixing): the token endpoint (`/client/token`) maps truncated
|
|
200
|
+
bodies (`ClientPayloadError`) and undecodable-JSON 200s to
|
|
201
|
+
`APIUnavailableError` / `SmartboxError` like the REST path — a
|
|
202
|
+
truncated token body previously escaped raw and **ended the websocket
|
|
203
|
+
loop for good** (end-to-end reproduced); `SocketSession` rejects
|
|
204
|
+
`ping_interval <= 0` (hot ping loop) and `reconnect_attempts < 1`
|
|
205
|
+
(endless refresh churn) with a `ValueError` at construction; the CLI
|
|
206
|
+
`set-status` command gives a clean error for `--stemp` without
|
|
207
|
+
`--units` instead of a raw `ValueError` traceback; the token endpoint
|
|
208
|
+
treats HTTP 429 as transient (`APIUnavailableError`) instead of bad
|
|
209
|
+
credentials — rate limiting no longer triggers the password-login
|
|
210
|
+
fallback, the consumer's reauth flow, or the end of the websocket
|
|
211
|
+
loop (400/401 rejections still surface as `InvalidAuthError`).
|
|
212
|
+
|
|
213
|
+
* Code-review round 6 follow-up (2026-09-27, **breaking** — 2.6.0 is the
|
|
214
|
+
alignment window; every fix probe-verified): `set_node_prog` raises
|
|
215
|
+
`ValueError` for a `prog`-less / array-shaped payload (was `TypeError`
|
|
216
|
+
— now aligned with `set_node_status`'s input validation); the sync
|
|
217
|
+
`Session` wrapper return annotations reflect `raw_response=False`
|
|
218
|
+
(e.g. `get_devices` may return a `Devices` model, `get_nodes` a
|
|
219
|
+
`list[Node]`); the sync `Session` no longer leaks "coroutine was never
|
|
220
|
+
awaited" `RuntimeWarning`s when misused inside a running event loop
|
|
221
|
+
(the pre-built coroutines are closed before re-raising); socket
|
|
222
|
+
teardown reaps cancelled disconnect tasks and cancelled pending
|
|
223
|
+
background tasks (bounded) — no more "Task was destroyed but it is
|
|
224
|
+
pending" noise in edge teardown timing.
|
|
225
|
+
|
|
226
|
+
* Dependency refresh (2026-09-27, all gates + live socket round green):
|
|
227
|
+
runtime aiohttp 3.14.0→3.14.3, pydantic 2.13.4→2.13.5,
|
|
228
|
+
python-socketio 5.16.2→5.17.0 (+engineio 4.14.0, bidict), asyncclick
|
|
229
|
+
8.3.0.7→8.4.2.1, jq 1.11.0→1.12.0; dev pytest 9.1.1, ruff 0.16.9,
|
|
230
|
+
mypy 2.3.1, time-machine 3.5.1, tox 4.64.3, pytest-randomly 5.0.0.
|
|
231
|
+
* Removed dead dependencies `requests` and `websocket-client` (imported
|
|
232
|
+
nowhere; legacy from the pre-asyncio era — the websocket path uses
|
|
233
|
+
python-socketio/simple-websocket, HTTP uses aiohttp).
|
|
234
|
+
* Lint config: ruff `target-version` aligned to the runtime floor
|
|
235
|
+
(py313 → py314); disabled CPY001 (no copyright headers in this
|
|
236
|
+
project) and PLR0917 (positional variant of the already-ignored
|
|
237
|
+
PLR0913); dropped dead mypy override modules (`freezegun`, `test`);
|
|
238
|
+
typing-only imports moved behind `TYPE_CHECKING` (ruff TC rules).
|
|
239
|
+
|
|
240
|
+
* Code-review round (2026-09-27, all gates + contract tests + live smoke
|
|
241
|
+
green): caller-provided websessions are no longer closed on context
|
|
242
|
+
exit (session-ownership model, `aclose_owned_session()`); REST requests
|
|
243
|
+
get bounded timeouts (30s total / 10s connect) on library-created
|
|
244
|
+
sessions; GETs retry on transient connection failures (uses the
|
|
245
|
+
previously dead `retry_attempts`/`backoff_factor` constructor config);
|
|
246
|
+
mid-session HTTP 401 now raises `InvalidAuthError` (consumers can
|
|
247
|
+
trigger reauth); access token is masked in debug logs; sync `Session`
|
|
248
|
+
closes its lazily-created client session per call (no more "Unclosed
|
|
249
|
+
client session" warnings); CLI: clean `ClickException` errors for
|
|
250
|
+
unknown device/node ids and invalid prog JSON, and fixed a latent bug
|
|
251
|
+
where set-* commands could never resolve a node (string vs int address
|
|
252
|
+
comparison); sample-window defaults are computed at call time (they
|
|
253
|
+
were frozen at import and drifted in long-lived processes); docstring
|
|
254
|
+
fixes (`Sample`/`PmoSample` were swapped, `subscribe_to_device_connected`
|
|
255
|
+
claimed to be a power-limit hook); removed redundant field
|
|
256
|
+
redeclarations in status subclasses and 1-tuple call hacks in the
|
|
257
|
+
update manager; dropped dead `[tool.uv.workspace]` and
|
|
258
|
+
`[tool.setuptools.*]` config.
|
|
259
|
+
|
|
260
|
+
* Code-review round 7 — final pre-release pass (2026-09-27, gates +
|
|
261
|
+
contract tests green): `SocketSession` can now be constructed outside
|
|
262
|
+
a running event loop (the REST websession binding moved from
|
|
263
|
+
`__init__` to `run()`, with `external_http=True` so engineio never
|
|
264
|
+
closes the session shared with REST traffic); the websocket run loop
|
|
265
|
+
no longer dies when the first `dev_data` emit races the server drop
|
|
266
|
+
(`BadNamespaceError` is a *sibling* of `ConnectionError` under
|
|
267
|
+
`SocketIOError` — the catch family was widened to `SocketIOError`);
|
|
268
|
+
a token-endpoint 200 with a wrong-shaped body raises `SmartboxError`
|
|
269
|
+
instead of `InvalidAuthError` (consistent with the undecodable-JSON /
|
|
270
|
+
non-JSON mapping: a server-side drift is not "credentials rejected");
|
|
271
|
+
`AsyncSession` rejects a negative `backoff_factor` at construction
|
|
272
|
+
(it previously silently disabled backoff); CLI node resolution gives
|
|
273
|
+
a clean error on malformed wire payloads (missing/non-numeric `addr`)
|
|
274
|
+
instead of a raw `KeyError`/`ValueError` traceback.
|
|
275
|
+
|
|
276
|
+
## 0.0.5 (alpha)
|
|
277
|
+
|
|
278
|
+
### Features
|
|
279
|
+
* Rename `away_status` to `device_away_status`
|
|
280
|
+
* Update API docs
|
|
281
|
+
* Add tox, tox and flake8 on github action
|
|
282
|
+
|
|
283
|
+
### Bug Fixes
|
|
284
|
+
* Pin dependency of python-socketio to match server
|
|
285
|
+
|
|
286
|
+
## 0.0.4 (alpha)
|
|
287
|
+
|
|
288
|
+
### Features
|
|
289
|
+
* Refactor socket session and implement reconnect
|
|
290
|
+
* Add note on basic auth credentials
|
|
291
|
+
|
|
292
|
+
## 0.0.3 (alpha)
|
|
293
|
+
|
|
294
|
+
### Features
|
|
295
|
+
* Fixed packaging
|
|
296
|
+
|
|
297
|
+
### Bug Fixes
|
|
298
|
+
* Fixed disconnect handling on token refresh
|
|
299
|
+
|
|
300
|
+
## 0.0.2 (alpha)
|
|
301
|
+
|
|
302
|
+
### Features
|
|
303
|
+
* Added `get_api_name` function
|
|
304
|
+
* Added basic tests for REST interactions
|
|
305
|
+
* Added token refresh support
|
|
306
|
+
* Added socket.io interface via `open_socket` function (no tests as yet)
|
|
307
|
+
* Added documentation for known REST and websocket endpoints
|
|
308
|
+
|
|
309
|
+
## 0.0.1 (alpha)
|
|
310
|
+
|
|
311
|
+
### Features
|
|
312
|
+
* Initial version supporting some REST endpoints
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: smartbox
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.2
|
|
4
4
|
Summary: Python API to control heating 'smart boxes'
|
|
5
5
|
Project-URL: Homepage, https://github.com/ajtudela/smartbox
|
|
6
6
|
Project-URL: Issues, https://github.com/ajtudela/smartbox/issues
|
|
@@ -11,22 +11,22 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Requires-Python: >=3.14.2
|
|
14
|
-
Requires-Dist: aiohttp
|
|
14
|
+
Requires-Dist: aiohttp<4,>=3
|
|
15
15
|
Requires-Dist: asyncclick
|
|
16
16
|
Requires-Dist: jq
|
|
17
|
-
Requires-Dist: pydantic
|
|
18
|
-
Requires-Dist: python-socketio
|
|
19
|
-
Requires-Dist: requests
|
|
20
|
-
Requires-Dist: websocket-client
|
|
17
|
+
Requires-Dist: pydantic<3,>=2
|
|
18
|
+
Requires-Dist: python-socketio<6,>=5
|
|
21
19
|
Description-Content-Type: text/markdown
|
|
22
20
|
|
|
23
|
-
# smartbox
|
|
21
|
+
# smartbox
|
|
22
|
+

|
|
23
|
+
[](https://badge.fury.io/py/smartbox)
|
|
24
|
+
[](https://codecov.io/gh/ajtudela/smartbox)
|
|
25
|
+
[](https://pypi.python.org/pypi/smartbox/)
|
|
26
|
+
[](https://pypi.python.org/pypi/smartbox/)
|
|
24
27
|
|
|
25
28
|
Python API to control heating 'smart boxes'
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
## Installation
|
|
29
|
-
|
|
30
30
|
## Install
|
|
31
31
|
|
|
32
32
|
To install smartbox simply run:
|
|
@@ -55,9 +55,9 @@ Verbose logging can be enabled with the `-v`/`--verbose` flag.
|
|
|
55
55
|
### Optional options
|
|
56
56
|
These options are useful if your reseller is not configured.
|
|
57
57
|
|
|
58
|
-
* `-b`/`--
|
|
58
|
+
* `-b`/`--basic-auth-creds`: An HTTP Basic Auth credential used to do initial
|
|
59
59
|
authentication with the server. Use the base64 encoded string directly. See
|
|
60
|
-
'Basic Auth Credential'
|
|
60
|
+
'Basic Auth Credential' in [api-notes.md](./api-notes.md) for more details.
|
|
61
61
|
* `-a`/`--api-name`: The API name for your heater vendor. This is visible in
|
|
62
62
|
the 'API Host' entry in the 'Version' menu item in the mobile app/web app. If
|
|
63
63
|
the host name is of the form `api-foo.xxxx` or `api.xxxx` use the values
|
|
@@ -97,6 +97,18 @@ node.
|
|
|
97
97
|
|
|
98
98
|
smartbox <auth options...> set-setup <-d/--device-id> <device id> <-n/--node-addr> <node> <name>=<value> [<name>=<value> ...]
|
|
99
99
|
|
|
100
|
+
### Getting node programme
|
|
101
|
+
The `prog` command lists the weekly schedule across all nodes and devices.
|
|
102
|
+
|
|
103
|
+
smartbox <auth options...> prog
|
|
104
|
+
|
|
105
|
+
### Setting node programme
|
|
106
|
+
The `set-prog` command can be used to set the weekly schedule (programme)
|
|
107
|
+
of a particular node from a JSON string. The schedule is day-level-merged
|
|
108
|
+
onto the node's current schedule before posting:
|
|
109
|
+
|
|
110
|
+
smartbox <auth options...> set-prog <-d/--device-id> <device id> <-n/--node-addr> <node> '{"prog": {"0": [0, 2, 2, ...], ...}}'
|
|
111
|
+
|
|
100
112
|
### Setting node samples
|
|
101
113
|
|
|
102
114
|
The `node-samples` command can be used to get the historical data (temperature and consumption) of a node.
|
|
@@ -148,7 +160,7 @@ See [api-notes.md](./api-notes.md) for notes on REST and socket.io endpoints.
|
|
|
148
160
|
Prerequisites:
|
|
149
161
|
|
|
150
162
|
uv
|
|
151
|
-
python >=3.
|
|
163
|
+
python >=3.14.2
|
|
152
164
|
|
|
153
165
|
Clone the repo, install dependencies and install pre-commit hooks:
|
|
154
166
|
|
|
@@ -177,7 +189,7 @@ Another way to run the tests is by using `tox`. This runs the tests against the
|
|
|
177
189
|
|
|
178
190
|
or by specifying a python version
|
|
179
191
|
|
|
180
|
-
tox -e
|
|
192
|
+
tox -e py314
|
|
181
193
|
|
|
182
194
|
# Support
|
|
183
195
|
[![Buy a coffee to ajtudela][buymeacoffee-shield]][buymeacoffee-ajtudela]
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
# smartbox
|
|
1
|
+
# smartbox
|
|
2
|
+

|
|
3
|
+
[](https://badge.fury.io/py/smartbox)
|
|
4
|
+
[](https://codecov.io/gh/ajtudela/smartbox)
|
|
5
|
+
[](https://pypi.python.org/pypi/smartbox/)
|
|
6
|
+
[](https://pypi.python.org/pypi/smartbox/)
|
|
2
7
|
|
|
3
8
|
Python API to control heating 'smart boxes'
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
## Installation
|
|
7
|
-
|
|
8
10
|
## Install
|
|
9
11
|
|
|
10
12
|
To install smartbox simply run:
|
|
@@ -33,9 +35,9 @@ Verbose logging can be enabled with the `-v`/`--verbose` flag.
|
|
|
33
35
|
### Optional options
|
|
34
36
|
These options are useful if your reseller is not configured.
|
|
35
37
|
|
|
36
|
-
* `-b`/`--
|
|
38
|
+
* `-b`/`--basic-auth-creds`: An HTTP Basic Auth credential used to do initial
|
|
37
39
|
authentication with the server. Use the base64 encoded string directly. See
|
|
38
|
-
'Basic Auth Credential'
|
|
40
|
+
'Basic Auth Credential' in [api-notes.md](./api-notes.md) for more details.
|
|
39
41
|
* `-a`/`--api-name`: The API name for your heater vendor. This is visible in
|
|
40
42
|
the 'API Host' entry in the 'Version' menu item in the mobile app/web app. If
|
|
41
43
|
the host name is of the form `api-foo.xxxx` or `api.xxxx` use the values
|
|
@@ -75,6 +77,18 @@ node.
|
|
|
75
77
|
|
|
76
78
|
smartbox <auth options...> set-setup <-d/--device-id> <device id> <-n/--node-addr> <node> <name>=<value> [<name>=<value> ...]
|
|
77
79
|
|
|
80
|
+
### Getting node programme
|
|
81
|
+
The `prog` command lists the weekly schedule across all nodes and devices.
|
|
82
|
+
|
|
83
|
+
smartbox <auth options...> prog
|
|
84
|
+
|
|
85
|
+
### Setting node programme
|
|
86
|
+
The `set-prog` command can be used to set the weekly schedule (programme)
|
|
87
|
+
of a particular node from a JSON string. The schedule is day-level-merged
|
|
88
|
+
onto the node's current schedule before posting:
|
|
89
|
+
|
|
90
|
+
smartbox <auth options...> set-prog <-d/--device-id> <device id> <-n/--node-addr> <node> '{"prog": {"0": [0, 2, 2, ...], ...}}'
|
|
91
|
+
|
|
78
92
|
### Setting node samples
|
|
79
93
|
|
|
80
94
|
The `node-samples` command can be used to get the historical data (temperature and consumption) of a node.
|
|
@@ -126,7 +140,7 @@ See [api-notes.md](./api-notes.md) for notes on REST and socket.io endpoints.
|
|
|
126
140
|
Prerequisites:
|
|
127
141
|
|
|
128
142
|
uv
|
|
129
|
-
python >=3.
|
|
143
|
+
python >=3.14.2
|
|
130
144
|
|
|
131
145
|
Clone the repo, install dependencies and install pre-commit hooks:
|
|
132
146
|
|
|
@@ -155,7 +169,7 @@ Another way to run the tests is by using `tox`. This runs the tests against the
|
|
|
155
169
|
|
|
156
170
|
or by specifying a python version
|
|
157
171
|
|
|
158
|
-
tox -e
|
|
172
|
+
tox -e py314
|
|
159
173
|
|
|
160
174
|
# Support
|
|
161
175
|
[![Buy a coffee to ajtudela][buymeacoffee-shield]][buymeacoffee-ajtudela]
|