smartbox
Python API to control heating 'smart boxes' (Helki and its resellers: Elnur, Haverland, Climastar, Technotherm, HJM, ...). It wraps the REST API and the socket.io channel, and ships a smartbox command line tool.
Installation
pip install smartbox
The package is importable as smartbox and ships py.typed.
Library usage
AsyncSmartboxSession is the entry point. Use it as an async context manager so its HTTP client is closed for you; when you already have an aiohttp.ClientSession (for example Home Assistant's shared one) pass it as websession= and it will not be closed on exit.
import asyncio
from smartbox import AsyncSmartboxSession
async def main() -> None:
async with AsyncSmartboxSession(
api_name="api", # or SMARTBOX_API_NAME
username="you@example.com",
password="secret",
raw_response=False, # return Pydantic models instead of dicts
) as session:
for device in await session.get_devices():
for node in await session.get_nodes(device.dev_id):
status = await session.get_node_status(device.dev_id, node.model_dump())
print(device.name, node.name, status.mtemp, status.stemp)
asyncio.run(main())
With raw_response=True (the default) every method returns the raw dict/list from the API. With raw_response=False they return models from smartbox.models (HtrNodeStatus, DefaultNodeSetup, ...); the models are lenient (unknown keys kept, fields optional) because the API is undocumented and varies by reseller.
Constructor options worth knowing: retry_attempts/backoff_factor (transient 5xx / connection errors are retried with exponential backoff), x_referer / x_serial_id / basic_auth_credentials for a reseller that is not built in.
For live updates over socket.io use UpdateManager:
from smartbox import AsyncSmartboxSession, UpdateManager
async with AsyncSmartboxSession(...) as session:
manager = UpdateManager(session, device_id)
manager.subscribe_to_node_status(
lambda node_type, addr, status: print(node_type, addr, status)
)
await manager.run() # runs until cancelled
Websocket transports
Status updates arrive over one of two transports; both are implemented in this library and verified live (api-notes.md, "Transports — socket_io vs ws_user"):
socket_io(default): a per-device socket.io connection. TheUpdateManagerowns it;run()runs the loop,cancel()is terminal.ws_user(the vendor web apps' channel): ONE plain-WebSocket connection (WsUserSocketSession) serves every device of the account. Its server-side proxy PINGs keep the session supervised, and sockets are cut by the server exactly at the access token's 4 h expiry (close code 1006) — the reconnect with a fresh token is mandatory and part of the steady state.
Caller contract for the shared ws_user socket:
socket_ = WsUserSocketSession(session)
# The caller owns run() — and must SUPERVISE it: run() exits on any
# failure (it is re-runnable; only cancel() is terminal), and a
# parked UpdateManager never observes a shared-socket death, so
# without a supervisor the transport dies silently. Exceptions
# surface when the task is awaited, so keep and await it.
task = asyncio.create_task(supervise(socket_.run()))
socket_.add_device(dev_id, dev_data_cb, update_cb) # register (any time)
socket_.remove_device(dev_id) # unregister
await socket_.cancel() # TERMINAL: later run() calls no-op
run() raises InvalidAuthError (rejected credentials — start a
reauthentication flow) and WsUserUnsupportedError (deterministic
404/410 — the host does not serve ws_user; fall back to socket_io).
UpdateManager(session, dev_id, ws_user_socket=socket_) switches an
individual manager onto the shared connection: run() registers the
manager's device and parks until cancel() (the shared socket's own
restarts are handled by its supervisor, not by per-device consumers).
Pass-through SocketSession kwargs are ignored in this mode (warned).
smartbox command line tool
Use the smartbox tool to read status information from your heaters (nodes) and change settings.
Options
-u/--username and -p/--password (your mobile-app / web-app credentials) are required for every command except resellers. -v/--verbose enables debug logging.
These are only needed when your reseller is not built into the package:
-a/--api-name: the API name for your heater vendor, from the 'API Host' entry under the 'Version' menu of the mobile/web app. For a hostapi-foo.xxxxorapi.xxxxuseapi-fooorapi.-b/--basic-auth-creds: the HTTP Basic Auth credential used for the initial authentication, as a base64 string. See Basic Auth Credential in api-notes.md for how to capture it.-r/--x-referer: thex-refererheader value.-i/--x-serial-id: thex-serialidheader value.
Configuration via environment variables / .env
Every option above can be supplied through an environment variable instead of being typed on each invocation. The command line always takes precedence over the environment.
| Option | Environment variable |
|---|---|
-a/--api-name |
SMARTBOX_API_NAME |
-b/--basic-auth-creds |
SMARTBOX_BASIC_AUTH_CREDS |
-u/--username |
SMARTBOX_USERNAME |
-p/--password |
SMARTBOX_PASSWORD |
-r/--x-referer |
SMARTBOX_X_REFERER |
-i/--x-serial-id |
SMARTBOX_X_SERIAL_ID |
The smartbox command also reads a .env file (searched for in the working directory and its parents) before parsing options, so the usual workflow is to copy .env.example to .env, fill it in once, and then run commands without any auth flags:
cp .env.example .env
# edit .env
smartbox devices
Commands
In the examples below <auth options...> stands for the options above (or nothing, when they come from .env).
Read-only, across every device/node:
| Command | What it shows |
|---|---|
devices |
the devices the account can see |
homes |
devices grouped into homes |
nodes |
the nodes of each device |
status |
live status of every node |
setup |
configuration of every node |
prog |
weekly heating schedule of every node |
device-away-status |
away status of every device |
device-connected-status |
whether each device is online |
device-power-limit |
power limit (watts) of every device |
health-check |
whether the API is alive |
api-version |
API build info |
resellers |
resellers with a built-in configuration |
smartbox <auth options...> status
smartbox <auth options...> prog
guests needs a home id:
smartbox <auth options...> guests -h <home id>
node-samples reads the temperature/consumption history of one node (-s/-e default to one hour before/after now):
smartbox <auth options...> node-samples -d <device id> -n <node addr> [-s <start unix ts>] [-e <end unix ts>]
Writes take named options, one per field to change:
smartbox <auth options...> set-status -d <device id> -n <node addr> [--mode auto] [--stemp 21.5 --units C] [--locked false]
smartbox <auth options...> set-setup -d <device id> -n <node addr> [--control-mode 1] [--offset 0.0] [--units C] [--true-radiant-enabled true] [--window-mode-enabled false] [--priority low]
smartbox <auth options...> set-prog -d <device id> -n <node addr> '{"prog": {"0": [2, 2, ...], ...}}'
smartbox <auth options...> set-device-away-status -d <device id> [--away true] [--enabled true] [--forced false]
smartbox <auth options...> set-device-power-limit -d <device id> <watts>
socket opens a long-lived socket.io connection and prints dev_data and update events until interrupted:
smartbox <auth options...> socket -d <device id>
See api-notes.md for notes on the REST and socket.io endpoints.
Development
Prerequisites:
uv
python >=3.14.2
Clone the repo, install dependencies and install pre-commit hooks:
git clone https://github.com/ajtudela/smartbox
cd smartbox
uv sync
pre-commit install
Testing
Run the full suite:
uv run pytest
Generate a coverage XML (e.g. for use in an editor):
uv run pytest --cov-report xml:cov.xml --cov smartbox --cov-append tests/
tox runs the tests against the installed package:
uv run tox
uv run tox -e py # the single environment declared in pyproject.toml
Changelog
Release notes are kept in CHANGELOG.md.
Support
This is a community project, maintained on a best-effort basis and provided without warranty.
Getting help and reporting problems
Open an issue at https://github.com/ajtudela/smartbox/issues. Templates are provided for the common cases:
- Bug report — a command or method misbehaves. Include the
smartboxcommand you ran (or the code), the full output with-v/--verbose, and your reseller. Redact the access token and any Basic Auth credential before pasting logs. - Feature request — a missing endpoint or option.
- New reseller — your heater vendor is not in the built-in list. The template asks for
api-name,x-referer,x-serial-idand the Basic Auth credential; see "Capturing it from the reseller's web app" in api-notes.md for how to read them from the login request. Once added, everyone using that vendor benefits.
For questions about the API itself rather than this library, see api-notes.md.
Supporting the maintainers
If this library is useful to you, you can buy the maintainers a coffee:
Metadata
Release files for smartbox 2.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| smartbox-2.7.0.tar.gz | 253.6 kB | With provenance | Details for smartbox-2.7.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| smartbox-2.7.0-py3-none-any.whl | Python 3 | none | any | Details for smartbox-2.7.0-py3-none-any.whl |
Total release size: 311.0 kB
Release files / smartbox-2.7.0.tar.gz
| Download URL | smartbox-2.7.0.tar.gz |
|---|---|
| Size | 253.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
66ef74b5a933b72cb49a1c103c8b1f64f3824d97925e50ce330025b54b6c2792
|
|
BLAKE2b-256 checksum How to use checksums |
27f1410f8a3f8116bbe299c01156d9d01b26cb8b585ab8cd6759e751a309eb21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logRelease files / smartbox-2.7.0-py3-none-any.whl
| Download URL | smartbox-2.7.0-py3-none-any.whl |
|---|---|
| Size | 57.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e6e988c5252646dbf66a4369f728d918baceb71fec13957ea78d1056d8ee2a9d
|
|
BLAKE2b-256 checksum How to use checksums |
8860b95bf32ae2a53e62d209c9c2a4fab8ff57980aaa416c9cde03aa3dea5b6d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logSecurity for smartbox 2.7.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for smartbox-2.7.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.