Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ce47535
Add comprehensive enumerations module for type-safe device control
eman Dec 17, 2025
276a9f5
Fix changelog: clarify enum serialization happens in model not CLI
eman Dec 17, 2025
0e7c55b
Fix documentation: replace non-existent OperationMode with correct enums
eman Dec 17, 2025
9416fb7
Update changelog with documentation fix
eman Dec 17, 2025
237b03e
Clarify use_enum_values comment: affects validation not serialization
eman Dec 17, 2025
c603e41
Fix comment: replace invalid HYBRID with ENERGY_SAVER enum value
eman Dec 17, 2025
0c85b44
Fix mode descriptions in event_emitter_demo for accuracy
eman Dec 17, 2025
f4b8143
Fix line length and move enum comment to docstring
eman Dec 17, 2025
2b3ee28
Fix backwards OnOffFlag logic in device_feature_callback
eman Dec 17, 2025
6af1d7a
Fix regression: restore DhwOperationSetting enum usage
eman Dec 17, 2025
8933fe1
Replace all remaining magic numbers with enum values
eman Dec 17, 2025
55bb1a6
Relocate CommandCode enum to enums.py module
eman Dec 17, 2025
1c254db
Remove backward compatibility for CommandCode import
eman Dec 17, 2025
5011d52
Fix anti_legionella_example import after CommandCode relocation
eman Dec 17, 2025
bdbc871
Fix documentation: replace DeviceControl with CommandCode
eman Dec 17, 2025
df04e50
Add cycle detection to _convert_enums_to_names method
eman Dec 17, 2025
ba14843
Use asyncio.Event for thread-safe shutdown handling
eman Dec 17, 2025
c2d92c7
Use explicit enum.value in format strings
eman Dec 17, 2025
f0a1092
Fix remaining self.running references to use shutdown_event
eman Dec 17, 2025
6e2c358
Remove unnecessary temporary variable in _convert_enums_to_names
eman Dec 17, 2025
571c408
Fix RST escape sequence in documentation
eman Dec 17, 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
84 changes: 83 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,88 @@
Changelog
=========

Version 6.2.0 (2025-12-17)
==========================

**BREAKING CHANGES**: Enumerations refactored for type safety and consistency

- **CommandCode moved**: Import from ``nwp500.enums`` instead of ``nwp500.constants``

.. code-block:: python

# OLD (removed)
from nwp500.constants import CommandCode

# NEW
from nwp500.enums import CommandCode
# OR
from nwp500 import CommandCode # Still works

Added
-----

- **Enumerations Module (``src/nwp500/enums.py``)**: Comprehensive type-safe enums for device control and status

- Status value enums: ``OnOffFlag``, ``Operation``, ``DhwOperationSetting``, ``CurrentOperationMode``, ``HeatSource``, ``DREvent``, ``WaterLevel``, ``FilterChange``, ``RecirculationMode``
- Time of Use enums: ``TouWeekType``, ``TouRateType``
- Device capability enums: ``CapabilityFlag``, ``TemperatureType``, ``DeviceType``
- Device control command enum: ``CommandCode`` (all MQTT command codes)
- Error code enum: ``ErrorCode`` with complete error code mappings
- Human-readable text mappings for all enums (e.g., ``DHW_OPERATION_SETTING_TEXT``, ``ERROR_CODE_TEXT``)
- Exported from main package: ``from nwp500 import OnOffFlag, ErrorCode, CommandCode``
- Comprehensive documentation in ``docs/enumerations.rst``
- Example usage in ``examples/error_code_demo.py``

Changed
-------

- **Command Code Constants**: Migrated from ``constants.py`` to ``CommandCode`` enum in ``enums.py``

- ``ANTI_LEGIONELLA_ENABLE`` → ``CommandCode.ANTI_LEGIONELLA_ON``
- ``ANTI_LEGIONELLA_DISABLE`` → ``CommandCode.ANTI_LEGIONELLA_OFF``
- ``TOU_ENABLE`` → ``CommandCode.TOU_ON``
- ``TOU_DISABLE`` → ``CommandCode.TOU_OFF``
- ``TOU_SETTINGS`` → ``CommandCode.TOU_RESERVATION``
- All command constants now use consistent naming in ``CommandCode`` enum

- **Model Enumerations**: Updated type annotations for clarity and type safety

- ``TemperatureUnit`` → ``TemperatureType`` (matches device protocol field names)
- All capability flags (e.g., ``power_use``, ``dhw_use``) now use ``CapabilityFlag`` type
- ``MqttRequest.device_type`` now accepts ``Union[DeviceType, int]`` for flexibility

- **Model Serialization**: Enums automatically serialize to human-readable names

- `model_dump()` converts enums to names (e.g., `DhwOperationSetting.HEAT_PUMP` → `"HEAT_PUMP"`)
- CLI and other consumers benefit from automatic enum name serialization
- Text mappings available for custom formatting (e.g., `DHW_OPERATION_TEXT[enum]` → "Heat Pump Only")

- **Documentation**: Comprehensive updates across protocol and API documentation

- ``docs/guides/time_of_use.rst``: Clarified TOU override status behavior (1=OFF/override active, 2=ON/normal operation)
- ``docs/protocol/data_conversions.rst``: Updated TOU field descriptions with correct enum values
- ``docs/protocol/device_features.rst``: Added capability flag pattern explanation (2=supported, 1=not supported)
- ``docs/protocol/mqtt_protocol.rst``: Updated command code references to use new enum names
- ``docs/python_api/models.rst``: Updated model field type annotations

- **Examples**: Updated to use new enums for type-safe device control

- ``examples/anti_legionella_example.py``: Uses ``CommandCode`` enum
- ``examples/device_feature_callback.py``: Uses capability enums
- ``examples/event_emitter_demo.py``: Uses status enums
- ``examples/mqtt_diagnostics_example.py``: Uses command enums

- **CLI Code Cleanup**: Refactored JSON formatting to use shared utility function

- Extracted repeated `json.dumps()` calls to `format_json_output()` helper
- Cleaner code with consistent formatting across all commands

Fixed
-----

- **Temperature Conversion Test**: Corrected ``test_device_status_div10`` to use ``HalfCelsiusToF`` conversion (100 → 122°F, not 50.0)
- **Documentation**: Fixed references to non-existent ``OperationMode`` enum - replaced with correct ``DhwOperationSetting`` and ``CurrentOperationMode`` enums

Version 6.1.1 (2025-12-08)
==========================

Expand Down Expand Up @@ -238,7 +320,7 @@ Quick Example

# 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
mode = OperationMode(converted["operationMode"]) if converted["operationMode"] in (0,32,64,96) else OperationMode.STANDBY

# NEW simplified
is_heating = status.current_heat_use
Expand Down
293 changes: 293 additions & 0 deletions docs/enumerations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
Enumerations Reference
======================

This document provides a comprehensive reference for all enumerations used in
the Navien NWP500 protocol.

Device Control Commands
-----------------------

.. autoclass:: nwp500.enums.CommandCode
:members:
:undoc-members:

These command IDs are used in MQTT control messages to change device settings
and trigger actions. The most commonly used commands include:

- **Power Control**: ``POWER_ON``, ``POWER_OFF``
- **Temperature**: ``DHW_TEMPERATURE``
- **Operation Mode**: ``DHW_MODE``
- **TOU**: ``TOU_ON``, ``TOU_OFF``
- **Maintenance**: ``AIR_FILTER_RESET``, ``ANTI_LEGIONELLA_ON``

Example usage::

from nwp500 import CommandCode

# Send temperature command
command = CommandCode.DHW_TEMPERATURE
params = [120] # 60°C in half-degree units

Status Value Enumerations
--------------------------

OnOffFlag
~~~~~~~~~

.. autoclass:: nwp500.enums.OnOffFlag
:members:
:undoc-members:

Generic on/off flag used throughout status fields for power status, TOU status,
recirculation status, vacation mode, anti-legionella, and other boolean settings.

**Note**: Device uses ``1=OFF, 2=ON`` (not standard 0/1 boolean).

Operation
~~~~~~~~~

.. autoclass:: nwp500.enums.Operation
:members:
:undoc-members:

Device operation state indicating overall device activity.

DhwOperationSetting
~~~~~~~~~~~~~~~~~~~

.. autoclass:: nwp500.enums.DhwOperationSetting
:members:
:undoc-members:

User-configured DHW heating mode preference. This determines which heat source(s)
the device will use when heating is needed:

- **HEAT_PUMP**: Most efficient but slower heating
- **ELECTRIC**: Fastest but uses most energy
- **ENERGY_SAVER**: Hybrid mode - balanced efficiency
- **HIGH_DEMAND**: Hybrid mode - maximum heating capacity
- **VACATION**: Energy-saving mode for extended absences
- **POWER_OFF**: Device powered off

Example::

from nwp500 import DhwOperationSetting
from nwp500.enums import DHW_OPERATION_SETTING_TEXT

mode = DhwOperationSetting.ENERGY_SAVER
print(f"Current mode: {DHW_OPERATION_SETTING_TEXT[mode]}") # "Hybrid: Efficiency"

CurrentOperationMode
~~~~~~~~~~~~~~~~~~~~

.. autoclass:: nwp500.enums.CurrentOperationMode
:members:
:undoc-members:

Real-time operational state (read-only). This reflects what the device is actually
doing right now, which may differ from the configured mode setting:

- **STANDBY**: Device idle, not actively heating
- **HEAT_PUMP_MODE**: Heat pump actively running
- **HYBRID_EFFICIENCY_MODE**: Actively heating in Energy Saver mode
- **HYBRID_BOOST_MODE**: Actively heating in High Demand mode

Example::

from nwp500 import CurrentOperationMode
from nwp500.enums import CURRENT_OPERATION_MODE_TEXT

mode = CurrentOperationMode.HEAT_PUMP_MODE
print(f"Device state: {CURRENT_OPERATION_MODE_TEXT[mode]}") # "Heat Pump"

HeatSource
~~~~~~~~~~

.. autoclass:: nwp500.enums.HeatSource
:members:
:undoc-members:

Currently active heat source (read-only status). This reflects what the device
is *currently* using, not what mode it's set to. In Hybrid mode, this field
shows which heat source(s) are active at any moment.

DREvent
~~~~~~~

.. autoclass:: nwp500.enums.DREvent
:members:
:undoc-members:

Demand Response event status. Allows utilities to manage grid load by signaling
water heaters to reduce consumption (shed) or pre-heat (load up) before peak periods.

WaterLevel
~~~~~~~~~~

.. autoclass:: nwp500.enums.WaterLevel
:members:
:undoc-members:

Hot water level indicator displayed as gauge in app. IDs are non-sequential,
likely represent bit positions for multi-level displays.

FilterChange
~~~~~~~~~~~~

.. autoclass:: nwp500.enums.FilterChange
:members:
:undoc-members:

Air filter status for heat pump models. Indicates when air filter maintenance
is needed.

RecirculationMode
~~~~~~~~~~~~~~~~~

.. autoclass:: nwp500.enums.RecirculationMode
:members:
:undoc-members:

Recirculation pump operation mode:

- **ALWAYS**: Pump continuously runs
- **BUTTON**: Manual activation only (hot button)
- **SCHEDULE**: Runs on configured schedule
- **TEMPERATURE**: Activates when pipe temp drops below setpoint

Time of Use (TOU) Enumerations
-------------------------------

TouWeekType
~~~~~~~~~~~

.. autoclass:: nwp500.enums.TouWeekType
:members:
:undoc-members:

Day grouping for TOU schedules. Allows separate schedules for weekdays and
weekends to account for different electricity rates and usage patterns.

TouRateType
~~~~~~~~~~~

.. autoclass:: nwp500.enums.TouRateType
:members:
:undoc-members:

Electricity rate period type. Device behavior can be configured for each period:

- **OFF_PEAK**: Lowest rates - device heats aggressively
- **MID_PEAK**: Medium rates - device heats normally
- **ON_PEAK**: Highest rates - device minimizes heating

Temperature and Unit Enumerations
----------------------------------

TemperatureType
~~~~~~~~~~~~~~~

.. autoclass:: nwp500.enums.TemperatureType
:members:
:undoc-members:

Temperature display unit preference (Celsius or Fahrenheit).

**Alias**: ``TemperatureUnit`` in models.py for backward compatibility.

TempFormulaType
~~~~~~~~~~~~~~~

.. autoclass:: nwp500.enums.TempFormulaType
:members:
:undoc-members:

Temperature conversion formula type. Different device models use slightly different
rounding algorithms when converting internal Celsius values to Fahrenheit:

- **ASYMMETRIC** (Type 0): Special rounding based on raw value remainder
- **STANDARD** (Type 1): Simple round to nearest integer

This ensures the mobile app matches the device's built-in display exactly.

Device Type Enumerations
-------------------------

UnitType
~~~~~~~~

.. autoclass:: nwp500.enums.UnitType
:members:
:undoc-members:

Navien device/unit model types. Common values:

- **NPF** (513): Heat pump water heater (primary model for this library)
- **NPE**: Tankless water heater
- **NCB**: Condensing boiler
- **NPN**: Condensing water heater

Values with ``CAS_`` prefix indicate cascading systems where multiple units
work together.

DeviceType
~~~~~~~~~~

.. autoclass:: nwp500.enums.DeviceType
:members:
:undoc-members:

Communication device type (WiFi module model).

FirmwareType
~~~~~~~~~~~~

.. autoclass:: nwp500.enums.FirmwareType
:members:
:undoc-members:

Firmware component types. Devices may have multiple firmware components that
can be updated independently.

Display Text Helpers
--------------------

The enums module also provides dictionaries for converting enum values to
user-friendly display text:

.. code-block:: python

from nwp500.enums import (
DHW_OPERATION_SETTING_TEXT,
CURRENT_OPERATION_MODE_TEXT,
HEAT_SOURCE_TEXT,
DR_EVENT_TEXT,
RECIRC_MODE_TEXT,
TOU_RATE_TEXT,
FILTER_STATUS_TEXT,
ERROR_CODE_TEXT,
)

# Usage examples
from nwp500 import DhwOperationSetting, CurrentOperationMode, ErrorCode

# User-configured mode
mode = DhwOperationSetting.ENERGY_SAVER
print(DHW_OPERATION_SETTING_TEXT[mode]) # "Hybrid: Efficiency"

# Current operational state
state = CurrentOperationMode.HEAT_PUMP_MODE
print(CURRENT_OPERATION_MODE_TEXT[state]) # "Heat Pump"

# Error codes
error = ErrorCode.E407_DHW_TEMP_SENSOR
print(ERROR_CODE_TEXT[error]) # "Abnormal DHW Temperature Sensor"

Related Documentation
---------------------

For detailed protocol documentation, see:

- :doc:`protocol/device_status` - Status field definitions
- :doc:`guides/time_of_use` - TOU scheduling and rate types
- :doc:`protocol/control_commands` - Control command usage
Loading