Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
84300ef
Refactor models to use Pydantic
eman Nov 19, 2025
8ff7163
Fix linting errors
eman Nov 19, 2025
c87ebea
Apply ruff formatting
eman Nov 19, 2025
45d2c84
Update comment for API typo workaround
eman Nov 19, 2025
feaac72
Remove dead code in from_dict
eman Nov 19, 2025
7a37281
Fix EnergyUsageTotal aliases
eman Nov 19, 2025
ecb6bbc
fix: Complete Pydantic model refactoring and example file fixes
eman Nov 19, 2025
e2771a4
fix: Apply ruff linting and formatting fixes
eman Nov 19, 2025
4cd91aa
fix: Resolve mypy type errors and CLI attribute naming
eman Nov 19, 2025
956cf8f
refactor: improve MQTT client readability and fix token example
eman Nov 19, 2025
e6b2caf
docs: improve structure and add protocol disclaimers
eman Nov 19, 2025
6c53ff0
chore: fix linting and formatting issues
eman Nov 19, 2025
a019f23
docs: update Python code examples to use snake_case attributes
eman Nov 19, 2025
108732f
docs: fix reStructuredText heading hierarchy in advanced_features_exp…
eman Nov 19, 2025
52350e0
fix: remove duplicate @classmethod decorator in TOUInfo.model_validate
eman Nov 19, 2025
b16b6b6
docs: add advanced_features_explained to User Guides toctree
eman Nov 19, 2025
d2114b8
docs: update all remaining Python examples to use snake_case attributes
eman Nov 19, 2025
a4b5281
docs: convert field name listings in models.rst to snake_case
eman Nov 19, 2025
9f000dc
docs: convert remaining field names in models.rst to snake_case
eman Nov 19, 2025
0a60276
docs: convert code example attributes in models.rst to snake_case
eman Nov 19, 2025
5c62b1f
docs: normalize snake_case field names in mqtt_client examples
eman Nov 20, 2025
f86519b
docs: add :no-index: to package automodule to suppress duplicate warn…
eman Nov 20, 2025
9e3d471
docs: convert remaining camelCase to snake_case in mqtt examples
eman Nov 20, 2025
022bede
docs: fix remaining dhw_temperatureSetting and exclude duplicated mem…
eman Nov 20, 2025
d25fc6a
docs: finalize snake_case field and switch problematic JSON examples …
eman Nov 20, 2025
d3b4f9d
docs: finalize snake_case and switch json placeholders to text
eman Nov 20, 2025
8c6eec2
docs: update REST API and MQTT client docs (2025-11-20)
eman Nov 20, 2025
4dbb2a8
docs: update changelog with v6.0.3 model migration breaking changes
eman Nov 20, 2025
fb73625
clean-up readme
eman Nov 20, 2025
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
73 changes: 73 additions & 0 deletions .agent/workflows/pre-completion-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
description: Run linting and testing before completing tasks
---

# Pre-Completion Testing Workflow

Before marking any code-related task as complete, you MUST run the following checks:

## 1. Linting with Ruff

Run ruff to check for code style and quality issues:

```bash
ruff check src/ tests/ examples/
```

If there are any errors, fix them before proceeding. You can auto-fix many issues with:

```bash
ruff check --fix src/ tests/ examples/
```

## 2. Format Check with Ruff

Verify code formatting is correct:

```bash
ruff format --check src/ tests/ examples/
```

If formatting issues are found, apply formatting:

```bash
ruff format src/ tests/ examples/
```

## 3. Run Unit Tests

Execute the test suite to ensure no regressions:

```bash
pytest tests/
```

All tests must pass before completing the task.

## 4. Type Checking (Optional but Recommended)

If you've modified type annotations or core logic, run mypy:

```bash
mypy src/
```

## Summary

**Required before task completion:**
- ✅ Ruff linting passes (no errors)
- ✅ Ruff formatting check passes
- ✅ All pytest tests pass

**Recommended:**
- ✅ Mypy type checking passes (if types were modified)

## Quick Command

You can run all checks with:

```bash
ruff check src/ tests/ examples/ && ruff format --check src/ tests/ examples/ && pytest tests/
```

**IMPORTANT**: Do not claim a task is complete without running these checks. If any check fails, fix the issues and re-run the checks.
83 changes: 83 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,89 @@
Changelog
=========

Version 6.0.3 (2025-11-20)
==========================

**BREAKING CHANGES**: Migration from custom dataclass-based models to Pydantic BaseModel implementations with automatic field validation and alias handling.

Removed
-------

- Removed legacy dataclass implementations for models (DeviceInfo, Location, Device, FirmwareInfo, DeviceStatus, DeviceFeature, EnergyUsage*). All models now inherit from ``NavienBaseModel`` (Pydantic).
- Removed manual ``from_dict`` constructors relying on camelCase key mapping logic.
- Removed field metadata conversion system (``meta()`` + ``apply_field_conversions()``) in favor of Pydantic ``BeforeValidator`` pipeline.

Changed
-------

- Models now use snake_case attribute names consistently; camelCase keys from API/MQTT are mapped automatically via Pydantic ``alias_generator=to_camel``.
- Boolean device fields now validated via ``DeviceBool`` Annotated type (device value 2 -> True, 0/1 -> False) replacing manual conversion code.
- Temperature offset (+20), scale division (/10) and decicelsius-to-Fahrenheit conversions implemented with lightweight ``BeforeValidator`` functions (``Add20``, ``Div10``, ``DeciCelsiusToF``) instead of post-processing.
- Enum parsing now handled directly by Pydantic; unknown values default safely via explicit Field defaults instead of try/except conversion loops.
- Field names updated (examples & docs) to snake_case: e.g. ``operationMode`` -> ``operation_mode``, ``dhwTemperatureSetting`` -> ``dhw_temperature_setting``.
- API typo handled using Field alias (``heLowerOnTDiffempSetting`` -> ``he_lower_on_diff_temp_setting``) rather than custom dictionary mutation.
- DeviceStatus conversion now performed on parse instead of separate transformation step, improving performance and reducing memory copies.
- Improved validation error messages from Pydantic on malformed payloads.
- Simplified energy usage model accessors; removed manual percentage methods duplication.

Added
-----

- Introduced ``NavienBaseModel`` configuring alias generation, population by name, and ignoring unknown fields for forward compatibility.
- Added structured Annotated types: ``DeviceBool``, ``Add20``, ``Div10``, ``DeciCelsiusToF`` for declarative conversion definitions.
- Added consistent default enum values directly in field declarations (e.g. ``operation_mode=STANDBY``).

Migration Guide (v6.0.2 -> v6.0.3)
----------------------------------

1. Replace any imports of dataclass models with Pydantic versions (paths unchanged). No code change required if you only accessed attributes.
2. Remove calls to ``Model.from_dict(data)``: Either use ``Model.model_validate(data)`` or continue calling ``from_dict`` where still provided (thin wrapper for backward compatibility on some classes). Preferred: ``DeviceStatus.model_validate(raw_payload)``.
3. Update attribute access to snake_case. Common mappings:
- ``deviceInfo.macAddress`` -> ``device.device_info.mac_address``
- ``deviceStatus.operationMode`` -> ``status.operation_mode``
- ``deviceStatus.dhwTemperatureSetting`` -> ``status.dhw_temperature_setting``
- ``deviceStatus.currentInletTemperature`` -> ``status.current_inlet_temperature``
4. Remove manual conversion code. Raw numeric values are converted automatically; stop adding +20 or dividing by 10 in user code.
5. Stop performing boolean normalization (``value == 2``) manually; attributes already return proper bools.
6. For enum handling, remove try/except wrappers; rely on defaulted fields (e.g. ``operation_mode`` defaults to ``STANDBY``).
7. If you previously mutated raw payload keys to snake_case, eliminate that transformation step.
8. If you logged intermediate converted dictionaries, you can access ``model.model_dump()`` for a fully converted representation.
9. Replace any custom validation logic with Pydantic validators or continue using existing patterns; most prior validation code is now unnecessary.
10. Energy usage: Access percentages via properties unchanged; object types now Pydantic models.

Quick Example
~~~~~~~~~~~~~

.. code-block:: python

# OLD (v6.0.2)
raw = mqtt_payload["deviceStatus"]
converted = apply_field_conversions(DeviceStatus, raw)
status = DeviceStatus(**converted)
print(converted["dhwTemperatureSetting"] + 20) # manual offset

# NEW (v6.0.3)
status = DeviceStatus.model_validate(mqtt_payload["deviceStatus"])
print(status.dhw_temperature_setting) # already includes +20 offset

# OLD boolean and enum handling
is_heating = converted["currentHeatUse"] == 2
mode = CurrentOperationMode(converted["operationMode"]) if converted["operationMode"] in (0,32,64,96) else CurrentOperationMode.STANDBY

# NEW simplified
is_heating = status.current_heat_use
mode = status.operation_mode

Benefits
~~~~~~~~

- Declarative conversions reduce 400+ lines of imperative transformation logic.
- Improved performance (single parse vs copy + transform).
- Automatic camelCase key mapping; less brittle than manual dict key copying.
- Rich validation errors for debugging malformed device messages.
- Cleaner, shorter model definitions with clearer intent.
- Easier extension: add new fields with conversion by combining Annotated + validator.

Version 6.0.2 (2025-11-15)
==========================

Expand Down
116 changes: 11 additions & 105 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,15 @@ A Python library for monitoring and controlling the Navien NWP500 Heat Pump Wate

Features
========
* Monitor status (temperature, power, charge %)
* Set target water temperature
* Change operation mode
* Optional scheduling (reservations)
* Optional time-of-use settings
* Periodic high-temp cycle info
* Access detailed status fields

* **Device Monitoring**: Access real-time status information including temperatures, power consumption, and tank charge level
* **Temperature Control**: Set target water temperature (90-151°F)
* **Operation Mode Control**: Switch between Heat Pump, Energy Saver, High Demand, Electric, and Vacation modes
* **Reservation Management**: Schedule automatic temperature and mode changes
* **Time of Use (TOU)**: Configure energy pricing schedules for demand response
* **Anti-Legionella Protection**: Monitor periodic disinfection cycles (140°F heating)
* **Comprehensive Status Data**: Access to 70+ device status fields including compressor status, heater status, flow rates, and more
* **MQTT Protocol Support**: Low-level MQTT communication with Navien devices
* **Non-Blocking Async Operations**: Fully compatible with async event loops (Home Assistant safe)
* **Automatic Reconnection**: Reconnects automatically with exponential backoff during network interruptions
* **Command Queuing**: Commands sent while disconnected are queued and sent automatically when reconnected
* **Data Models**: Type-safe data classes with automatic unit conversions
* Async friendly

Quick Start
===========
Expand Down Expand Up @@ -119,8 +115,8 @@ The library includes a command line interface for quick monitoring and device in
**Available CLI Options:**

* ``--status``: Print current device status as JSON. Can be combined with control commands to see updated status.
* ``--device-info``: Print comprehensive device information (firmware, model, capabilities) via MQTT as JSON and exit
* ``--device-feature``: Print device capabilities and feature settings via MQTT as JSON and exit
* ``--device-info``: Print comprehensive device information (firmware, model, capabilities) as JSON and exit
* ``--device-feature``: Print device capabilities and feature settings as JSON and exit
* ``--power-on``: Turn the device on and display response
* ``--power-off``: Turn the device off and display response
* ``--set-mode MODE``: Set operation mode and display response. Valid modes: heat-pump, energy-saver, high-demand, electric, vacation, standby
Expand Down Expand Up @@ -161,63 +157,10 @@ The library provides access to comprehensive device status information:
* Cumulative operation time
* Flow rates

Operation Modes
===============

.. list-table:: Operation Modes
:header-rows: 1
:widths: 25 10 65

* - Mode
- ID
- Description
* - Heat Pump Mode
- 1
- Most energy-efficient mode using only the heat pump. Longest recovery time.
* - Electric Mode
- 2
- Fastest recovery using only electric heaters. Least energy-efficient.
* - Energy Saver Mode
- 3
- Default mode. Balances efficiency and recovery time using both heat pump and electric heater.
* - High Demand Mode
- 4
- Uses electric heater more frequently for faster recovery time.
* - Vacation Mode
- 5
- Suspends heating to save energy during extended absences.

**Important:** When you set a mode, you're configuring the ``dhwOperationSetting`` (what mode to use when heating). The device's current operational state is reported in ``operationMode`` (0=Standby, 32=Heat Pump active, 64=Energy Saver active, 96=High Demand active).

MQTT Protocol
=============

The library supports low-level MQTT communication with Navien devices:

**Control Topics**
* ``cmd/{deviceType}/{deviceId}/ctrl`` - Send control commands
* ``cmd/{deviceType}/{deviceId}/ctrl/rsv/rd`` - Manage reservations
* ``cmd/{deviceType}/{deviceId}/ctrl/tou/rd`` - Time of Use settings
* ``cmd/{deviceType}/{deviceId}/st`` - Request status updates

**Control Commands**
* Power control (on/off)
* DHW mode changes (including vacation mode)
* Temperature settings
* Reservation management (scheduled mode/temperature changes)
* Time of Use (TOU) pricing schedules

**Status Requests**
* Device information
* General device status
* Energy usage queries
* Reservation information
* TOU settings

Documentation
=============

For detailed information on device status fields, MQTT protocol, authentication, and more, see the complete documentation at https://nwp500-python.readthedocs.io/
Full docs: https://nwp500-python.readthedocs.io/

Data Models
===========
Expand All @@ -228,12 +171,6 @@ The library includes type-safe data models with automatic unit conversions:
* **DeviceFeature**: Device capabilities, firmware versions, and configuration limits
* **OperationMode**: Enumeration of available operation modes
* **TemperatureUnit**: Celsius/Fahrenheit handling
* **MqttRequest/MqttCommand**: MQTT message structures

Temperature conversions are handled automatically:
* DHW temperatures: ``raw_value + 20`` (°F)
* Heat pump temperatures: ``raw_value / 10.0`` (°F)
* Ambient temperature: ``(raw_value * 9/5) + 32`` (°F)

Requirements
============
Expand All @@ -245,37 +182,6 @@ Requirements
* pydantic >= 2.0.0
* awsiotsdk >= 1.21.0

Development
===========
To set up a development environment:

.. code-block:: bash

# Clone the repository
git clone https://github.com/eman/nwp500-python.git
cd nwp500-python

# Install in development mode
pip install -e .

# Run tests
pytest

**Linting and CI Consistency**

To ensure your local linting matches CI exactly:

.. code-block:: bash

# Install tox (recommended)
pip install tox

# Run linting exactly as CI does
tox -e lint

# Auto-fix and format
tox -e format

License
=======

Expand Down
Loading
Loading