Repository navigation
Releases: loebi-ch/aionanoleaf2
Releases · loebi-ch/aionanoleaf2
Release list
v1.2.1
What's Changed in v1.2.1
- Quieter event stream logging. An outage of the SSE event stream is now logged once at
WARNING(first failure only). Repeated reconnect attempts during the same outage are logged atDEBUG, and a singleINFOmessage is emitted when the stream has recovered. Previously, every reconnect attempt with an HTTP error produced aWARNING. - Trusted Publishing. Releases are now tested (Python 3.12 – 3.14), built and published to PyPI by a GitHub Actions workflow using PyPI Trusted Publishing; no API token is stored in the repository.
No API changes; fully compatible with 1.2.0.
v1.2.0
What's Changed in v1.2.0
A stability and integration release. It aligns the library with the Home Assistant Nanoleaf integration: one clear exception hierarchy, a hardened event listener, and no hidden network state.
Highlights
- New exception hierarchy below
NanoleafError, so applications can map errors one-to-one:AuthError(InvalidToken, HTTP 401) is the only error that should trigger re-authentication.Unavailable(NanoleafConnectionError,NanoleafTimeoutError) means the device is unreachable or unusable.Unauthorized(pairing mode not active) is deliberately not anAuthError.- New:
NoAuthToken,InvalidResponseError,EndpointNotFoundError(HTTP 404, with.path).
- Hardened
listen_events()- Automatic reconnect with exponential backoff (1-30 s) and jitter. The delay resets after an event or a stable connection.
- HTTP 401 raises
InvalidTokenand HTTP 404 raisesEndpointNotFoundErrorinstead of reconnecting forever. - Malformed events are skipped, and sync and async callbacks are both supported.
- An exception in a callback is logged and never stops the listener.
- Cancelling the task cleans up callback tasks and the UDP socket.
- UDP touch stream fix: callbacks of the touch stream can now be plain functions or coroutine functions. Async callbacks are tracked and cancelled together with the listener, so they can no longer be garbage-collected while running.
- Better error messages: for HTTP errors the
Unavailablemessage now contains the status code, the path and the (truncated) response text of the device. - No hidden network state: the library never creates an
aiohttp.ClientSession. You pass one in (in Home Assistant:async_get_clientsession). - Fully typed (
py.typed), Python 3.12-3.14,aiohttp>=3.9.0. - New README, a test suite of 149 tests, and smoke-test scripts for real devices in
scripts/(not part of the PyPI package).
Behavior changes (migrating from 1.1.0)
- Python 3.12 or newer is required (1.1.0 supported 3.8 and newer), and
aiohttpmust be 3.9.0 or newer. Older Python versions keep installing 1.1.0 automatically. get_info()and the otherget_*methods now raise on timeouts and connection errors. Only HTTP 404 on optional endpoints (for example on Essentials) is tolerated. CatchNanoleafErroraround refreshes.- HTTP status mapping: 401 ->
InvalidToken, 404 ->EndpointNotFoundError, any other status >= 400 ->Unavailable. - Requests are retried (default: 3 attempts) only on timeouts and connection errors. HTTP error responses are never retried.
Nanoleaf.hostis normalized idempotently and can be passed back into the constructor unchanged.NanoleafExceptionstill works as an alias ofNanoleafError, but is deprecated and will be removed in 2.0.0.
Tested on real hardware
| Device | Firmware | Result |
|---|---|---|
| Nanoleaf Elements / Hexagons (NL52) | 12.3.2, 12.4.1 | Info, control and event listener OK |
| Nanoleaf Shapes (NL42) | 12.3.2, 12.4.1 | Info, control and event listener OK |
| Nanoleaf Essentials (NL72K1) | 4.0.11 | Info and control OK (no event stream on this device) |
| Nanoleaf 4D (NL69) | 12.4.1 | Info, control, event listener and Screen Mirroring OK |
Known device behavior
These are quirks of the devices, not of the library. They were observed on a few real devices and may differ on other models or firmware versions.
- Essentials (NL72K1, firmware 4.0.11): The local API has no working
identifyendpoint: the device answers with HTTP 400 and does not react, which the library raises asUnavailable. The device has no event stream (supports_eventsisFalse). Aturn_off()sent immediately afterset_effect()was ignored by the device; waiting about half a second in between avoids it. - Value rounding: The firmware may report slightly different values than you set, for example 4331 K as 4347 K.
- Elements (NL52, firmware 12.3.2):
set_brightness()on a switched-off device turned it on. - Sporadic HTTP 400: In a few smoke-test runs, the first write to a switched-off NL52 or NL69 was answered with HTTP 400. On the NL69 the device was switched on afterwards. It could not be reproduced in 24 controlled on/off cycles. The library raises
Unavailable, and its message now contains the device's response text.
Full Changelog: v1.1.0...v1.2.0
v1.1.0
What's Changed in v1.1.0
Highlights
- Connection leak fixes: all HTTP requests now use async response context managers, so connections are always released.
- Hardened SSE streaming:
listen_events()reconnects after network drops, propagatesasyncio.CancelledErrorcleanly, and a failingget_emersion()no longer aborts the stream. - Touch UDP streaming: packets are now parsed with
struct.unpack(">HBH")(the oldbin()-based parsing could misread panel IDs), and the sender check handles bracketed IPv6 hosts and zone IDs. - Typing:
py.typedmarker (PEP 561),__all__exports, strict mypy issues resolved.
⚠️ Behavior changes / upgrade notes
Nanoleaf._request()(private) now returns the decoded body (dict/list/str/None) instead ofaiohttp.ClientResponse.- Request failures, including non-2xx responses and failures in
authorize(), are now raised asUnavailableinstead ofaiohttp.ClientResponseError.401still raisesInvalidToken,403duringauthorize()raisesUnauthorized. - Request bodies are sent as
application/json(previouslytext/plain). get_info()no longer raisesKeyErrorfor missing fields (needed for Essentials); defaults are used instead.get_emersion()no longer raises on failure and reports"Unknown".- Unknown event attribute IDs are reported as
unknown_<id>instead of raisingKeyError. TouchStreamEvent.panel_id_2now returnsNonefor the "no second panel" value (0xFFFF). Previously the check never matched (2 ^ 16is XOR = 18).- Type hints: the package is now typed, so mypy/pyright in downstream projects may report new findings.
InfoDataandEffectDetailsDataaretotal=False. - New:
Nanoleaf.raw_host(unbracketed IP without zone ID).
Fixes
- Wrong
panel_id_2sentinel check (2 ^ 16). - Fragile
bin()-based UDP touch packet parsing. - Touch packets from IPv6 devices were dropped because of bracketed/zoned host comparison.
authorize()now has a request timeout and clearer error guidance (API toggle in the Nanoleaf app first, hardware button only for legacy Light Panels).
Packaging, docs & tests
py.typed+package_data, fixed documentation URL, updated README and repository URLs.- Extended live integration script
test.py(SSE state events, cancellation). Requires a real device; not part of an automated test suite.
v1.0.5
v1.0.4
What's Changed
- Add support for retrieving effect details by @hgregorian in #5
- Infer event streaming support from the presence of panelLayout. by @hgregorian in #6
New Contributors
- @hgregorian made their first contribution in #5
Full Changelog: v1.0.3...v1.0.4
v1.0.3
What's Changed
- fix: handle Essentials devices missing state in root info payload by @jeroen-meijer in #8
New Contributors
- @jeroen-meijer made their first contribution in #8
Full Changelog: v1.0.2...v1.0.3
v1.0.2
v1.0.1
v1.0.0
This async Python wrapper for the Nanoleaf API replaces the no longer maintained aioNanoleaf package.
The original aioNanoleaf has been modified to:
- add support for Nanoleaf Essentials devices.
- add support for Screen Mirroring emersion modes (1D, 2D, 3D, 4D).