From 4314d1a581e3f300b4352ed166d713a995e0c510 Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Mon, 13 Oct 2025 17:22:31 -0700 Subject: [PATCH 1/2] unit conversion fix --- docs/DEVICE_STATUS_FIELDS.rst | 2 +- src/nwp500/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DEVICE_STATUS_FIELDS.rst b/docs/DEVICE_STATUS_FIELDS.rst index 4f9c5ec..92f9c5a 100644 --- a/docs/DEVICE_STATUS_FIELDS.rst +++ b/docs/DEVICE_STATUS_FIELDS.rst @@ -147,7 +147,7 @@ This document lists the fields found in the ``status`` object of device status m - integer - °F - Target superheat value - the desired temperature difference ensuring complete refrigerant vaporization. - - ``raw / 10.0`` + - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit) * - ``compUse`` - bool - None diff --git a/src/nwp500/models.py b/src/nwp500/models.py index 55855dd..a0e49b0 100644 --- a/src/nwp500/models.py +++ b/src/nwp500/models.py @@ -380,7 +380,6 @@ def from_dict(cls, data: dict): # Convert fields with 'raw / 10.0' formula (non-temperature fields) div_10_fields = [ - "targetSuperHeat", "currentInletTemperature", "currentDhwFlowRate", "hpUpperOnDiffTempSetting", @@ -414,6 +413,7 @@ def from_dict(cls, data: dict): "evaporatorTemperature", "ambientTemperature", "currentSuperHeat", + "targetSuperHeat", ] for field_name in heat_pump_temp_fields: if field_name in converted_data: From 38125eb998d0a52a954b52ae10f97bc98e5fecd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Mon, 13 Oct 2025 18:35:29 -0700 Subject: [PATCH 2/2] doc updates for energy and available request methods --- docs/ENERGY_MONITORING.rst | 39 +++++++ docs/MQTT_CLIENT.rst | 218 ++++++++++++++++++++++++++++++++++++- 2 files changed, 253 insertions(+), 4 deletions(-) diff --git a/docs/ENERGY_MONITORING.rst b/docs/ENERGY_MONITORING.rst index d9f9eb4..d309026 100644 --- a/docs/ENERGY_MONITORING.rst +++ b/docs/ENERGY_MONITORING.rst @@ -79,6 +79,45 @@ Total upper electric heater runtime in minutes - ``heater2RunningMinuteTotal`` (int): Total lower electric heater runtime in minutes +Historical Energy Usage +----------------------- + +Request detailed daily energy usage data for specific months: + +.. code:: python + + from nwp500 import NavienMqttClient, EnergyUsageResponse + + def on_energy_usage(energy: EnergyUsageResponse): + print(f"Total Usage: {energy.total.total_usage} Wh") + print(f"Heat Pump: {energy.total.heat_pump_percentage:.1f}%") + print(f"Electric: {energy.total.heat_element_percentage:.1f}%") + + # Daily breakdown + for day in energy.daily: + print(f"Day {day.day}: {day.total_usage} Wh") + + # Subscribe to energy usage responses + await mqtt_client.subscribe_energy_usage(device, on_energy_usage) + + # Request energy usage for September 2025 + await mqtt_client.request_energy_usage(device, year=2025, months=[9]) + + # Request multiple months + await mqtt_client.request_energy_usage(device, year=2025, months=[7, 8, 9]) + +**Key Methods:** + +- ``request_energy_usage(device, year, months)``: Request historical data +- ``subscribe_energy_usage(device, callback)``: Subscribe to energy usage responses + +**Response Fields:** + +- ``total.total_usage`` (int): Total energy consumption in Wh +- ``total.heat_pump_percentage`` (float): Percentage from heat pump +- ``total.heat_element_percentage`` (float): Percentage from electric heaters +- ``daily`` (list): Daily breakdown of usage per day + Energy Capacity --------------- diff --git a/docs/MQTT_CLIENT.rst b/docs/MQTT_CLIENT.rst index e7603a3..3935f94 100644 --- a/docs/MQTT_CLIENT.rst +++ b/docs/MQTT_CLIENT.rst @@ -396,6 +396,41 @@ Publish a message to an MQTT topic. Device Command Methods ^^^^^^^^^^^^^^^^^^^^^^ +Complete MQTT API Reference +'''''''''''''''''''''''''''' + +This section provides a comprehensive reference of all available MQTT client methods for requesting data and controlling devices. + +**Request Methods & Corresponding Subscriptions** + ++------------------------------------+---------------------------------------+----------------------------------------+ +| Request Method | Subscribe Method | Response Type | ++====================================+=======================================+========================================+ +| ``request_device_status()`` | ``subscribe_device_status()`` | ``DeviceStatus`` object | ++------------------------------------+---------------------------------------+----------------------------------------+ +| ``request_device_info()`` | ``subscribe_device_feature()`` | ``DeviceFeature`` object | ++------------------------------------+---------------------------------------+----------------------------------------+ +| ``request_energy_usage()`` | ``subscribe_energy_usage()`` | ``EnergyUsageResponse`` object | ++------------------------------------+---------------------------------------+----------------------------------------+ +| ``set_power()`` | ``subscribe_device_status()`` | Updated ``DeviceStatus`` | ++------------------------------------+---------------------------------------+----------------------------------------+ +| ``set_dhw_mode()`` | ``subscribe_device_status()`` | Updated ``DeviceStatus`` | ++------------------------------------+---------------------------------------+----------------------------------------+ +| ``set_dhw_temperature()`` | ``subscribe_device_status()`` | Updated ``DeviceStatus`` | ++------------------------------------+---------------------------------------+----------------------------------------+ +| ``set_dhw_temperature_display()`` | ``subscribe_device_status()`` | Updated ``DeviceStatus`` | ++------------------------------------+---------------------------------------+----------------------------------------+ + +**Generic Subscriptions** + ++------------------------------------+---------------------------------------+----------------------------------------+ +| Method | Purpose | Response Type | ++====================================+=======================================+========================================+ +| ``subscribe_device()`` | Subscribe to all device messages | Raw ``dict`` (all message types) | ++------------------------------------+---------------------------------------+----------------------------------------+ +| ``subscribe()`` | Subscribe to any MQTT topic | Raw ``dict`` | ++------------------------------------+---------------------------------------+----------------------------------------+ + request_device_status() ''''''''''''''''''''''' @@ -403,12 +438,26 @@ request_device_status() await mqtt_client.request_device_status(device: Device) -> int -Request current device status. +Request current device status including temperatures, operation mode, power consumption, and error codes. **Command:** ``16777219`` **Topic:** ``cmd/{device_type}/navilink-{device_id}/st`` +**Response:** Subscribe with ``subscribe_device_status()`` to receive ``DeviceStatus`` objects + +**Example:** + +.. code:: python + + def on_status(status: DeviceStatus): + print(f"Water Temp: {status.dhwTemperature}°F") + print(f"Mode: {status.operationMode}") + print(f"Power: {status.currentInstPower}W") + + await mqtt_client.subscribe_device_status(device, on_status) + await mqtt_client.request_device_status(device) + request_device_info() ''''''''''''''''''''' @@ -416,12 +465,59 @@ request_device_info() await mqtt_client.request_device_info(device: Device) -> int -Request device information. +Request device information including firmware version, serial number, temperature limits, and capabilities. **Command:** ``16777217`` **Topic:** ``cmd/{device_type}/navilink-{device_id}/st/did`` +**Response:** Subscribe with ``subscribe_device_feature()`` to receive ``DeviceFeature`` objects + +**Example:** + +.. code:: python + + def on_feature(feature: DeviceFeature): + print(f"Firmware: {feature.controllerSwVersion}") + print(f"Serial: {feature.controllerSerialNumber}") + print(f"Temp Range: {feature.dhwTemperatureMin}-{feature.dhwTemperatureMax}°F") + + await mqtt_client.subscribe_device_feature(device, on_feature) + await mqtt_client.request_device_info(device) + +request_energy_usage() +'''''''''''''''''''''' + +.. code:: python + + await mqtt_client.request_energy_usage(device: Device, year: int, months: list[int]) -> int + +Request historical daily energy usage data for specified month(s). Returns heat pump and electric heating element consumption with daily breakdown. + +**Command:** ``16777225`` + +**Topic:** ``cmd/{device_type}/navilink-{device_id}/st/energy-usage-daily-query/rd`` + +**Response:** Subscribe with ``subscribe_energy_usage()`` to receive ``EnergyUsageResponse`` objects + +**Parameters:** + +- ``year``: Year to query (e.g., 2025) +- ``months``: List of months to query (1-12). Can request multiple months. + +**Example:** + +.. code:: python + + def on_energy(energy: EnergyUsageResponse): + print(f"Total Usage: {energy.total.total_usage} Wh") + print(f"Heat Pump: {energy.total.heat_pump_percentage:.1f}%") + for day in energy.daily: + print(f"Day {day.day}: {day.total_usage} Wh") + + await mqtt_client.subscribe_energy_usage(device, on_energy) + await mqtt_client.request_energy_usage(device, year=2025, months=[9]) + set_power() ''''''''''' @@ -435,6 +531,8 @@ Turn device on or off. **Mode:** ``power-on`` or ``power-off`` +**Response:** Device status is updated; subscribe with ``subscribe_device_status()`` to see changes + set_dhw_mode() '''''''''''''' @@ -456,6 +554,8 @@ Set DHW (Domestic Hot Water) operation mode. This sets the ``dhwOperationSetting * ``4``: High Demand (faster recovery - Hybrid: Boost) * ``5``: Vacation (suspend heating for 0-99 days) +**Response:** Device status is updated; subscribe with ``subscribe_device_status()`` to see changes + **Important:** Setting the mode updates ``dhwOperationSetting`` but does not immediately change ``operationMode``. The ``operationMode`` field reflects the device's current operational state and changes automatically when the device starts/stops heating. See :doc:`DEVICE_STATUS_FIELDS` for details on the relationship between these fields. set_dhw_temperature() @@ -465,13 +565,44 @@ set_dhw_temperature() await mqtt_client.set_dhw_temperature(device: Device, temperature: int) -> int -Set DHW target temperature. +Set DHW target temperature using the **MESSAGE value** (20°F lower than display). **Command:** ``33554433`` **Mode:** ``dhw-temperature`` -**Parameters:** - ``temperature``: Target temperature in Fahrenheit +**Parameters:** + +- ``temperature``: Target temperature in Fahrenheit (message value, not display value) + +**Response:** Device status is updated; subscribe with ``subscribe_device_status()`` to see changes + +**Important:** The temperature in the message is 20°F lower than what displays on the device/app: + +- Message value 120°F → Display shows 140°F +- Message value 130°F → Display shows 150°F + +set_dhw_temperature_display() +'''''''''''''''''''''''''''''' + +.. code:: python + + await mqtt_client.set_dhw_temperature_display(device: Device, display_temperature: int) -> int + +Set DHW target temperature using the **DISPLAY value** (what you see on device/app). This is a convenience method that automatically converts display temperature to message value. + +**Parameters:** + +- ``display_temperature``: Target temperature as shown on display/app (Fahrenheit) + +**Response:** Device status is updated; subscribe with ``subscribe_device_status()`` to see changes + +**Example:** + +.. code:: python + + # Set display temperature to 140°F (sends 120°F in message) + await mqtt_client.set_dhw_temperature_display(device, 140) signal_app_connection() ''''''''''''''''''''''' @@ -484,6 +615,85 @@ Signal that the app has connected. **Topic:** ``evt/{device_type}/navilink-{device_id}/app-connection`` +Subscription Methods +'''''''''''''''''''' + +subscribe_device_status() +......................... + +.. code:: python + + await mqtt_client.subscribe_device_status( + device: Device, + callback: Callable[[DeviceStatus], None] + ) -> int + +Subscribe to device status messages with automatic parsing into ``DeviceStatus`` objects. Use this after calling ``request_device_status()`` or any control commands to receive updates. + +**Emits Events:** + +- ``status_received``: Every status update (DeviceStatus) +- ``temperature_changed``: Temperature changed (old_temp, new_temp) +- ``mode_changed``: Operation mode changed (old_mode, new_mode) +- ``power_changed``: Power consumption changed (old_power, new_power) +- ``heating_started``: Device started heating (status) +- ``heating_stopped``: Device stopped heating (status) +- ``error_detected``: Error code detected (error_code, status) +- ``error_cleared``: Error code cleared (error_code) + +subscribe_device_feature() +.......................... + +.. code:: python + + await mqtt_client.subscribe_device_feature( + device: Device, + callback: Callable[[DeviceFeature], None] + ) -> int + +Subscribe to device feature/info messages with automatic parsing into ``DeviceFeature`` objects. Use this after calling ``request_device_info()`` to receive device capabilities and firmware info. + +**Emits Events:** + +- ``feature_received``: Feature/info received (DeviceFeature) + +subscribe_energy_usage() +........................ + +.. code:: python + + await mqtt_client.subscribe_energy_usage( + device: Device, + callback: Callable[[EnergyUsageResponse], None] + ) -> int + +Subscribe to energy usage query responses with automatic parsing into ``EnergyUsageResponse`` objects. Use this after calling ``request_energy_usage()`` to receive historical energy data. + +subscribe_device() +.................. + +.. code:: python + + await mqtt_client.subscribe_device( + device: Device, + callback: Callable[[str, dict], None] + ) -> int + +Subscribe to all messages from a device (no parsing). Receives all message types as raw dictionaries. Use the specific subscription methods above for automatic parsing. + +subscribe() +........... + +.. code:: python + + await mqtt_client.subscribe( + topic: str, + callback: Callable[[str, dict], None], + qos: mqtt.QoS = mqtt.QoS.AT_LEAST_ONCE + ) -> int + +Subscribe to any MQTT topic. Supports wildcards (``#``, ``+``). Receives raw dictionary messages. + Periodic Request Methods (Optional) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^