Skip to content

Releases: loebi-ch/aionanoleaf2

v1.2.1

Choose a tag to compare

@loebi-ch loebi-ch released this 19 Oct 19:20

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 at DEBUG, and a single INFO message is emitted when the stream has recovered. Previously, every reconnect attempt with an HTTP error produced a WARNING.
  • 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

Choose a tag to compare

@loebi-ch loebi-ch released this 14 Oct 14:17

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 an AuthError.
    • 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 InvalidToken and HTTP 404 raises EndpointNotFoundError instead 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 Unavailable message 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 aiohttp must be 3.9.0 or newer. Older Python versions keep installing 1.1.0 automatically.
  • get_info() and the other get_* methods now raise on timeouts and connection errors. Only HTTP 404 on optional endpoints (for example on Essentials) is tolerated. Catch NanoleafError around 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.host is normalized idempotently and can be passed back into the constructor unchanged.
  • NanoleafException still works as an alias of NanoleafError, 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 identify endpoint: the device answers with HTTP 400 and does not react, which the library raises as Unavailable. The device has no event stream (supports_events is False). A turn_off() sent immediately after set_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

Choose a tag to compare

@loebi-ch loebi-ch released this 12 Oct 23:45

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, propagates asyncio.CancelledError cleanly, and a failing get_emersion() no longer aborts the stream.
  • Touch UDP streaming: packets are now parsed with struct.unpack(">HBH") (the old bin()-based parsing could misread panel IDs), and the sender check handles bracketed IPv6 hosts and zone IDs.
  • Typing: py.typed marker (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 of aiohttp.ClientResponse.
  • Request failures, including non-2xx responses and failures in authorize(), are now raised as Unavailable instead of aiohttp.ClientResponseError. 401 still raises InvalidToken, 403 during authorize() raises Unauthorized.
  • Request bodies are sent as application/json (previously text/plain).
  • get_info() no longer raises KeyError for 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 raising KeyError.
  • TouchStreamEvent.panel_id_2 now returns None for the "no second panel" value (0xFFFF). Previously the check never matched (2 ^ 16 is XOR = 18).
  • Type hints: the package is now typed, so mypy/pyright in downstream projects may report new findings. InfoData and EffectDetailsData are total=False.
  • New: Nanoleaf.raw_host (unbracketed IP without zone ID).

Fixes

  • Wrong panel_id_2 sentinel 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

Choose a tag to compare

@loebi-ch loebi-ch released this 10 Oct 14:00
31a8177

What's Changed

  • fix(nanoleaf): handle read-after-write consistency on firmware 5.x.x by @feixm1 in #10

New Contributors

Full Changelog: v1.0.4...v1.0.5

v1.0.4

Choose a tag to compare

@loebi-ch loebi-ch released this 06 Oct 23:36
9afa9b7

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

Full Changelog: v1.0.3...v1.0.4

v1.0.3

Choose a tag to compare

@loebi-ch loebi-ch released this 06 Oct 23:14
48a783c

What's Changed

  • fix: handle Essentials devices missing state in root info payload by @jeroen-meijer in #8

New Contributors

Full Changelog: v1.0.2...v1.0.3

v1.0.2

Choose a tag to compare

@loebi-ch loebi-ch released this 29 Mar 16:23

Solves a problem with IPv6 literals.

v1.0.1

Choose a tag to compare

@loebi-ch loebi-ch released this 26 Nov 08:13

Adds support for IPv6.

Closes the issue #1.

v1.0.0

Choose a tag to compare

@loebi-ch loebi-ch released this 25 Nov 10:12

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).