Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4361ace
Phase 1: Foundation cleanup
eman May 6, 2026
3f5fe3e
Phase 2: Unit conversion redesign
eman May 6, 2026
3b75782
Phases 3+4: Topic consolidation and state tracker extraction
eman May 6, 2026
16e594b
Phase 5: Emit typed event dataclass instances
eman May 6, 2026
e033d03
Phases 5+6+7: Typed event payloads, new protocol models, missing cont…
eman May 6, 2026
9a92749
Phase 8: Typed subscriptions for reservation, weekly, and recirculati…
eman May 6, 2026
8dc9835
Phase 9a: Proxy all MqttDeviceController methods on NavienMqttClient
eman May 6, 2026
d8019ba
Phase 9b: Split models.py into models/ subpackage
eman May 6, 2026
7f73324
Documentation refactor: align with code refactor phases 5-9
eman May 6, 2026
0c01181
Remove LLM vocabulary and sentence patterns from documentation
eman May 6, 2026
b00236f
Update changelog for v8.0.0
eman May 6, 2026
680f06b
code review modifications
eman May 7, 2026
7d8a735
chore: fix CI issues, linting, and type safety
eman May 7, 2026
d99cf5e
fix(mqtt): use correct power attribute in state tracker
eman May 7, 2026
bbf3623
fix(mqtt): support both 'st/did' and 'status/feature' keys in device …
eman May 8, 2026
405a8e9
refactor(mqtt): extract get_response_data helper with correct key pre…
eman May 8, 2026
66ddcce
fix(mqtt): use sentinel in get_response_data to handle falsy values
eman May 8, 2026
ea02ba9
Merge pull request #83 from eman/fix/flexible-response-keys
eman May 8, 2026
43e2dfd
feat: add subscribe_tou_response() typed subscription
eman May 8, 2026
6d1aee6
fix: use TOUReservationSchedule for tou/rd MQTT responses
eman May 8, 2026
c70fa1e
fix: address review feedback on TOUPeriod and TOU subscription docs
eman May 8, 2026
ed38cf9
Merge pull request #84 from eman/feat/subscribe-tou-response
eman May 8, 2026
b6363d8
feat: add device identity to MQTT events and status models
eman May 8, 2026
c3a9cac
docs: update changelog for multi-device support enhancements
eman May 8, 2026
cd6e0c7
fix: resolve linting violations and formatting in multi-device support
eman May 8, 2026
48db415
style: fix formatting in multi-device tests to match ruff 0.15.12
eman May 8, 2026
355e5fc
docs: fix CI failures in documentation build
eman May 8, 2026
82a8ba2
docs: reorganize documentation into Diátaxis structure
eman May 8, 2026
5229bac
remove unnecessary comment
eman May 8, 2026
ac440a4
Fix on_connection_resumed callback missing connection parameter
eman May 12, 2026
37e8e5f
Update changelog for v8.1.1
eman May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.14'

- name: Install tox
run: |
Expand All @@ -37,7 +37,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.14'

- name: Install ruff
run: |
Expand All @@ -52,7 +52,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.13', '3.14']
python-version: ['3.14']

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.14'

- name: Install build dependencies
run: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.14'

- name: Install dependencies
run: |
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.14'

- name: Install build dependencies
run: |
Expand All @@ -80,7 +80,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.14'

- name: Extract changelog for this version
id: changelog
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ formats:
build:
os: ubuntu-22.04
tools:
python: "3.13"
python: "3.14"

python:
install:
Expand Down
144 changes: 142 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,146 @@
Changelog
=========

Version 8.1.1 (2026-05-12)
==========================

Bug Fixes
---------
- **Fix MQTT reconnection after unexpected AWS hangup**: The
``on_connection_resumed`` callback was missing the ``connection`` parameter
required by the AWS IoT SDK callback signature. The SDK calls
``on_connection_resumed(connection, return_code, session_present, **kwargs)``,
but the handler only accepted ``(return_code, session_present)``. The
mismatched signature caused the callback to fail silently (exception swallowed
by the AWS CRT C layer), so ``self._connected`` was never restored to ``True``
after an ``AWS_ERROR_MQTT_UNEXPECTED_HANGUP``. As a result, the
``connection_resumed`` event was never emitted, the reconnection loop ran
indefinitely, and device sensors became permanently unavailable until a manual
restart. (`#85 <https://github.com/eman/nwp500-python/issues/85>`_)

Version 8.1.0 (2026-05-07)
==========================

Features
--------
- **Multi-device support enhancements**: Improved support for accounts with multiple
Navilink devices by injecting device identity into models and events.

- Added ``mac_address`` field to ``DeviceStatus`` and ``DeviceFeature`` models.
- Added ``device_mac`` attribute to all device-specific MQTT events (temperature
changes, mode changes, power updates, errors, etc.).
- Updated ``DeviceStateTracker`` and ``MqttSubscriptionManager`` to propagate
device identity correctly.

Version 8.0.0 (2026-05-06)
===========================

**BREAKING CHANGES**: ``.on()`` event handler callbacks now receive a single typed
event dataclass instead of positional arguments. ``MqttDeviceController`` is no longer
accessible as ``.control`` on ``NavienMqttClient``; all control methods are now
available directly on the client.

Breaking Changes
----------------
- **Typed event payloads**: All ``.on()`` event handler callbacks now receive a single
typed event dataclass instance. The dataclasses are exported from
``nwp500.mqtt_events``.

.. code-block:: python

# OLD (removed)
mqtt.on("temperature_changed", lambda old, new: print(old, new))
mqtt.on("connection_resumed", lambda rc, sp: print(rc, sp))

# NEW
mqtt.on("temperature_changed", lambda e: print(e.old_temperature, e.new_temperature))
mqtt.on("connection_resumed", lambda e: print(e.return_code, e.session_present))

- **``MqttDeviceController`` no longer public**: ``NavienMqttClient`` no longer exposes
a ``.control`` attribute. All control methods are now available directly on the
client.

.. code-block:: python

# OLD (removed)
await mqtt.control.set_temperature(device, 50)

# NEW
await mqtt.set_temperature(device, 50)

Migration Guide (from 7.x.x)
----------------------------
The following steps are recommended for a smooth migration, particularly for complex
integrations like Home Assistant:

1. **Update Event Listeners**: Locate all ``mqtt.on()`` or ``mqtt.once()`` calls.
Update the callback signatures to accept a single argument (the event object) and
update the body to access fields via the object (e.g., ``event.new_temperature``
instead of ``new_val``).
2. **Refactor Control Calls**: Remove ``.control`` from all device command invocations.
Instead of ``await mqtt.control.set_power(...)``, use ``await mqtt.set_power(...)``.
3. **Handle Unit Conversions**: If your integration previously performed its own
conversions or relied on the library's eager conversion, note that
``DeviceStatus`` fields like ``dhw_temperature`` are now properties. They return
values based on the global unit system context (``us_customary`` by default).

* **Home Assistant Tip**: To ensure your state tracking is immune to unit system
toggles within the library, use the new ``*_raw`` fields (e.g.,
``status.dhw_temperature_raw``) for comparison logic, and use the properties
only for display or when a converted value is explicitly needed.
4. **Remove ``from_dict()`` Calls**: The ``from_dict()`` method has been removed
from all models. Use ``model_validate()`` instead.

* **Note**: ``AuthenticationResponse.model_validate()`` now automatically handles
the ``"data": { ... }`` wrapper found in raw API responses.
5. **Subpackage Imports**: While top-level imports from ``nwp500.models`` are
preserved, if you were importing from the internal ``nwp500.models`` module file
directly, you must update your imports to point to the new structured files
(e.g., ``nwp500.models.status``).

Added
-----
- **New control methods**: Nine previously unimplemented ``CommandCode`` values now have
full implementations: ``check_firmware``, ``commit_firmware``, ``reconnect_wifi``,
``reset_wifi``, ``set_freeze_protection_temperature``, ``run_smart_diagnostic``,
``enable_intelligent_reservation``, ``disable_intelligent_reservation``, and
``set_water_program_reservation``.
- **Typed subscription methods**: ``subscribe_reservation``,
``subscribe_weekly_reservation``, and ``subscribe_recirculation`` return typed
responses directly without requiring raw MQTT event handlers.
- **New protocol models**: ``WeeklyReservationSchedule``, ``WeeklyReservationEntry``,
``RecirculationSchedule``, ``RecirculationScheduleEntry``, and ``OtaCommitPayload``.
- **``DeviceStateTracker``**: State change detection extracted into a dedicated class
in ``nwp500.mqtt.state_tracker`` with per-device tracking keyed by MAC address.
- **``MQTT_PROTOCOL_VERSION`` constant**: Protocol version is now a named constant in
``nwp500.config`` rather than a hardcoded integer in the command payload builder.
- **``response_ack_topic()``**: New method on ``MqttTopicBuilder`` for control command
acknowledgement topics.

Changed
-------
- **Unit conversion redesign**: Temperature, flow rate, and volume fields in
``DeviceStatus`` and ``DeviceFeature`` now store raw device values as ``*_raw: int``
fields and expose converted values via lazy computed properties. Conversion happens at
access time rather than during Pydantic deserialization, preserving the original
device value in all cases.
- **Models split into subpackage**: ``nwp500.models`` is now a package
(``nwp500/models/``) with modules for status, schedule, TOU, and MQTT models. Public
imports from ``nwp500.models`` are unchanged.
- **Topic building centralised**: All MQTT topic construction now goes through
``MqttTopicBuilder``. Hardcoded topic strings removed from ``control.py``,
``reservations.py``, and ``cli/handlers.py``.
- **``set_vacation_days`` delegates to ``set_dhw_mode``**: The method now calls
``set_dhw_mode(device, DhwOperationSetting.VACATION, vacation_days=days)`` directly.
- **Per-device state tracking**: ``_previous_status`` changed from a single
``DeviceStatus | None`` to a ``dict[str, DeviceStatus]`` keyed by MAC address,
preventing spurious state-change events when multiple devices are connected.
- **Unit system stored as instance variable**: ``NavienAPIClient``,
``NavienMqttClient``, and ``NavienAuthClient`` no longer call ``set_unit_system()``
as a constructor side-effect.
- **``NavienBaseModel`` consolidated**: Duplicate definitions in ``auth.py`` and
``models.py`` merged into a single definition in ``nwp500._base``.

Version 7.4.10 (2026-04-13)
===========================

Expand Down Expand Up @@ -205,7 +345,7 @@ Breaking Changes
.. code-block:: python

# OLD (hardcoded 95-150°F)
await mqtt.control.set_dhw_temperature(device, temperature_f=140.0)
await mqtt.set_dhw_temperature(device, temperature_f=140.0)
entry = build_reservation_entry(
enabled=True,
days=["Monday"],
Expand All @@ -217,7 +357,7 @@ Breaking Changes

# NEW (device-provided limits, unit-aware)
# Temperature value automatically uses user's preferred unit
await mqtt.control.set_dhw_temperature(device, 140.0)
await mqtt.set_dhw_temperature(device, 140.0)

# Device features provide min/max in user's preferred unit
features = await device_info_cache.get(device.device_info.mac_address)
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Follow a clear exception hierarchy with consistent naming:
.. code-block:: python

try:
await mqtt_client.control.set_temperature(device, 150)
await mqtt_client.set_temperature(device, 150)
except MqttNotConnectedError:
# Handle specific case: not connected
print("Connect to device first")
Expand Down
Loading
Loading