Skip to content

Commit 566806f

Browse files
authored
Merge pull request #37 from eman/code-review-refactor
Refactor models to use Pydantic
2 parents 0d3eab5 + fb73625 commit 566806f

57 files changed

Lines changed: 1488 additions & 1789 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
description: Run linting and testing before completing tasks
3+
---
4+
5+
# Pre-Completion Testing Workflow
6+
7+
Before marking any code-related task as complete, you MUST run the following checks:
8+
9+
## 1. Linting with Ruff
10+
11+
Run ruff to check for code style and quality issues:
12+
13+
```bash
14+
ruff check src/ tests/ examples/
15+
```
16+
17+
If there are any errors, fix them before proceeding. You can auto-fix many issues with:
18+
19+
```bash
20+
ruff check --fix src/ tests/ examples/
21+
```
22+
23+
## 2. Format Check with Ruff
24+
25+
Verify code formatting is correct:
26+
27+
```bash
28+
ruff format --check src/ tests/ examples/
29+
```
30+
31+
If formatting issues are found, apply formatting:
32+
33+
```bash
34+
ruff format src/ tests/ examples/
35+
```
36+
37+
## 3. Run Unit Tests
38+
39+
Execute the test suite to ensure no regressions:
40+
41+
```bash
42+
pytest tests/
43+
```
44+
45+
All tests must pass before completing the task.
46+
47+
## 4. Type Checking (Optional but Recommended)
48+
49+
If you've modified type annotations or core logic, run mypy:
50+
51+
```bash
52+
mypy src/
53+
```
54+
55+
## Summary
56+
57+
**Required before task completion:**
58+
- ✅ Ruff linting passes (no errors)
59+
- ✅ Ruff formatting check passes
60+
- ✅ All pytest tests pass
61+
62+
**Recommended:**
63+
- ✅ Mypy type checking passes (if types were modified)
64+
65+
## Quick Command
66+
67+
You can run all checks with:
68+
69+
```bash
70+
ruff check src/ tests/ examples/ && ruff format --check src/ tests/ examples/ && pytest tests/
71+
```
72+
73+
**IMPORTANT**: Do not claim a task is complete without running these checks. If any check fails, fix the issues and re-run the checks.

CHANGELOG.rst

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,89 @@
22
Changelog
33
=========
44

5+
Version 6.0.3 (2025-11-20)
6+
==========================
7+
8+
**BREAKING CHANGES**: Migration from custom dataclass-based models to Pydantic BaseModel implementations with automatic field validation and alias handling.
9+
10+
Removed
11+
-------
12+
13+
- Removed legacy dataclass implementations for models (DeviceInfo, Location, Device, FirmwareInfo, DeviceStatus, DeviceFeature, EnergyUsage*). All models now inherit from ``NavienBaseModel`` (Pydantic).
14+
- Removed manual ``from_dict`` constructors relying on camelCase key mapping logic.
15+
- Removed field metadata conversion system (``meta()`` + ``apply_field_conversions()``) in favor of Pydantic ``BeforeValidator`` pipeline.
16+
17+
Changed
18+
-------
19+
20+
- Models now use snake_case attribute names consistently; camelCase keys from API/MQTT are mapped automatically via Pydantic ``alias_generator=to_camel``.
21+
- Boolean device fields now validated via ``DeviceBool`` Annotated type (device value 2 -> True, 0/1 -> False) replacing manual conversion code.
22+
- Temperature offset (+20), scale division (/10) and decicelsius-to-Fahrenheit conversions implemented with lightweight ``BeforeValidator`` functions (``Add20``, ``Div10``, ``DeciCelsiusToF``) instead of post-processing.
23+
- Enum parsing now handled directly by Pydantic; unknown values default safely via explicit Field defaults instead of try/except conversion loops.
24+
- Field names updated (examples & docs) to snake_case: e.g. ``operationMode`` -> ``operation_mode``, ``dhwTemperatureSetting`` -> ``dhw_temperature_setting``.
25+
- API typo handled using Field alias (``heLowerOnTDiffempSetting`` -> ``he_lower_on_diff_temp_setting``) rather than custom dictionary mutation.
26+
- DeviceStatus conversion now performed on parse instead of separate transformation step, improving performance and reducing memory copies.
27+
- Improved validation error messages from Pydantic on malformed payloads.
28+
- Simplified energy usage model accessors; removed manual percentage methods duplication.
29+
30+
Added
31+
-----
32+
33+
- Introduced ``NavienBaseModel`` configuring alias generation, population by name, and ignoring unknown fields for forward compatibility.
34+
- Added structured Annotated types: ``DeviceBool``, ``Add20``, ``Div10``, ``DeciCelsiusToF`` for declarative conversion definitions.
35+
- Added consistent default enum values directly in field declarations (e.g. ``operation_mode=STANDBY``).
36+
37+
Migration Guide (v6.0.2 -> v6.0.3)
38+
----------------------------------
39+
40+
1. Replace any imports of dataclass models with Pydantic versions (paths unchanged). No code change required if you only accessed attributes.
41+
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)``.
42+
3. Update attribute access to snake_case. Common mappings:
43+
- ``deviceInfo.macAddress`` -> ``device.device_info.mac_address``
44+
- ``deviceStatus.operationMode`` -> ``status.operation_mode``
45+
- ``deviceStatus.dhwTemperatureSetting`` -> ``status.dhw_temperature_setting``
46+
- ``deviceStatus.currentInletTemperature`` -> ``status.current_inlet_temperature``
47+
4. Remove manual conversion code. Raw numeric values are converted automatically; stop adding +20 or dividing by 10 in user code.
48+
5. Stop performing boolean normalization (``value == 2``) manually; attributes already return proper bools.
49+
6. For enum handling, remove try/except wrappers; rely on defaulted fields (e.g. ``operation_mode`` defaults to ``STANDBY``).
50+
7. If you previously mutated raw payload keys to snake_case, eliminate that transformation step.
51+
8. If you logged intermediate converted dictionaries, you can access ``model.model_dump()`` for a fully converted representation.
52+
9. Replace any custom validation logic with Pydantic validators or continue using existing patterns; most prior validation code is now unnecessary.
53+
10. Energy usage: Access percentages via properties unchanged; object types now Pydantic models.
54+
55+
Quick Example
56+
~~~~~~~~~~~~~
57+
58+
.. code-block:: python
59+
60+
# OLD (v6.0.2)
61+
raw = mqtt_payload["deviceStatus"]
62+
converted = apply_field_conversions(DeviceStatus, raw)
63+
status = DeviceStatus(**converted)
64+
print(converted["dhwTemperatureSetting"] + 20) # manual offset
65+
66+
# NEW (v6.0.3)
67+
status = DeviceStatus.model_validate(mqtt_payload["deviceStatus"])
68+
print(status.dhw_temperature_setting) # already includes +20 offset
69+
70+
# OLD boolean and enum handling
71+
is_heating = converted["currentHeatUse"] == 2
72+
mode = CurrentOperationMode(converted["operationMode"]) if converted["operationMode"] in (0,32,64,96) else CurrentOperationMode.STANDBY
73+
74+
# NEW simplified
75+
is_heating = status.current_heat_use
76+
mode = status.operation_mode
77+
78+
Benefits
79+
~~~~~~~~
80+
81+
- Declarative conversions reduce 400+ lines of imperative transformation logic.
82+
- Improved performance (single parse vs copy + transform).
83+
- Automatic camelCase key mapping; less brittle than manual dict key copying.
84+
- Rich validation errors for debugging malformed device messages.
85+
- Cleaner, shorter model definitions with clearer intent.
86+
- Easier extension: add new fields with conversion by combining Annotated + validator.
87+
588
Version 6.0.2 (2025-11-15)
689
==========================
790

README.rst

Lines changed: 11 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,15 @@ A Python library for monitoring and controlling the Navien NWP500 Heat Pump Wate
1313

1414
Features
1515
========
16+
* Monitor status (temperature, power, charge %)
17+
* Set target water temperature
18+
* Change operation mode
19+
* Optional scheduling (reservations)
20+
* Optional time-of-use settings
21+
* Periodic high-temp cycle info
22+
* Access detailed status fields
1623

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

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

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

164-
Operation Modes
165-
===============
166-
167-
.. list-table:: Operation Modes
168-
:header-rows: 1
169-
:widths: 25 10 65
170-
171-
* - Mode
172-
- ID
173-
- Description
174-
* - Heat Pump Mode
175-
- 1
176-
- Most energy-efficient mode using only the heat pump. Longest recovery time.
177-
* - Electric Mode
178-
- 2
179-
- Fastest recovery using only electric heaters. Least energy-efficient.
180-
* - Energy Saver Mode
181-
- 3
182-
- Default mode. Balances efficiency and recovery time using both heat pump and electric heater.
183-
* - High Demand Mode
184-
- 4
185-
- Uses electric heater more frequently for faster recovery time.
186-
* - Vacation Mode
187-
- 5
188-
- Suspends heating to save energy during extended absences.
189-
190-
**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).
191-
192-
MQTT Protocol
193-
=============
194-
195-
The library supports low-level MQTT communication with Navien devices:
196-
197-
**Control Topics**
198-
* ``cmd/{deviceType}/{deviceId}/ctrl`` - Send control commands
199-
* ``cmd/{deviceType}/{deviceId}/ctrl/rsv/rd`` - Manage reservations
200-
* ``cmd/{deviceType}/{deviceId}/ctrl/tou/rd`` - Time of Use settings
201-
* ``cmd/{deviceType}/{deviceId}/st`` - Request status updates
202-
203-
**Control Commands**
204-
* Power control (on/off)
205-
* DHW mode changes (including vacation mode)
206-
* Temperature settings
207-
* Reservation management (scheduled mode/temperature changes)
208-
* Time of Use (TOU) pricing schedules
209-
210-
**Status Requests**
211-
* Device information
212-
* General device status
213-
* Energy usage queries
214-
* Reservation information
215-
* TOU settings
216-
217160
Documentation
218161
=============
219162

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

222165
Data Models
223166
===========
@@ -228,12 +171,6 @@ The library includes type-safe data models with automatic unit conversions:
228171
* **DeviceFeature**: Device capabilities, firmware versions, and configuration limits
229172
* **OperationMode**: Enumeration of available operation modes
230173
* **TemperatureUnit**: Celsius/Fahrenheit handling
231-
* **MqttRequest/MqttCommand**: MQTT message structures
232-
233-
Temperature conversions are handled automatically:
234-
* DHW temperatures: ``raw_value + 20`` (°F)
235-
* Heat pump temperatures: ``raw_value / 10.0`` (°F)
236-
* Ambient temperature: ``(raw_value * 9/5) + 32`` (°F)
237174

238175
Requirements
239176
============
@@ -245,37 +182,6 @@ Requirements
245182
* pydantic >= 2.0.0
246183
* awsiotsdk >= 1.21.0
247184

248-
Development
249-
===========
250-
To set up a development environment:
251-
252-
.. code-block:: bash
253-
254-
# Clone the repository
255-
git clone https://github.com/eman/nwp500-python.git
256-
cd nwp500-python
257-
258-
# Install in development mode
259-
pip install -e .
260-
261-
# Run tests
262-
pytest
263-
264-
**Linting and CI Consistency**
265-
266-
To ensure your local linting matches CI exactly:
267-
268-
.. code-block:: bash
269-
270-
# Install tox (recommended)
271-
pip install tox
272-
273-
# Run linting exactly as CI does
274-
tox -e lint
275-
276-
# Auto-fix and format
277-
tox -e format
278-
279185
License
280186
=======
281187

0 commit comments

Comments
 (0)