diff --git a/docs/guides/advanced_features_explained.rst b/docs/guides/advanced_features_explained.rst index 9ac041b..9820280 100644 --- a/docs/guides/advanced_features_explained.rst +++ b/docs/guides/advanced_features_explained.rst @@ -441,24 +441,32 @@ Factors Affecting Stratification - Target: Warm tank from bottom, allow sufficient top recovery - Result: 140°F achieved in 45 min (slower due to DR, but cold ambient expected) -Formula Confirmation -==================== +Temperature Conversion Reference +================================ -**Formula**: +The NWP500 uses **half-degrees Celsius** encoding for temperature fields. -The temperature conversion formula is: +**Conversion Formulas**: .. code-block:: text - Formula: displayed_value = raw_value + 20 + Half-degrees Celsius to Fahrenheit: + fahrenheit = (raw_value / 2.0) * 9/5 + 32 - Application Evidence: - - Application handled by NaviLink - - Implementation in device status messages - - Fields: dhwTemperature, tankUpperTemperature, tankLowerTemperature, etc. - - Conversion: Applied uniformly to all add_20 type fields in device status - - Raw value range: 0-130 (representing -4°F to 150°F) - - Display range: 20-150°F + Examples: + - Raw 70 → (70 / 2.0) * 9/5 + 32 = 95°F + - Raw 98 → (98 / 2.0) * 9/5 + 32 ≈ 120°F + - Raw 120 → (120 / 2.0) * 9/5 + 32 = 140°F + - Raw 132 → (132 / 2.0) * 9/5 + 32 ≈ 150°F + + Inverse (Fahrenheit to raw): + raw_value = (fahrenheit - 32) * 5/9 * 2 + +**Field Types**: + +- **HalfCelsiusToF**: Most temperature fields (DHW, setpoints, freeze protection) +- **DeciCelsiusToF**: Sensor readings (tank sensors, refrigerant circuit) + - Formula: ``fahrenheit = (raw_value / 10.0) * 9/5 + 32`` **Related Documentation**: @@ -489,7 +497,7 @@ Summary and Recommendations See Also -------- -* :doc:`../protocol/data_conversions` - Temperature field conversions (add_20, decicelsius_to_f) +* :doc:`../protocol/data_conversions` - Temperature field conversions (HalfCelsiusToF, DeciCelsiusToF) * :doc:`../protocol/device_status` - Complete device status field reference * :doc:`scheduling_features` - Reservation and TOU integration points * :doc:`../python_api/models` - DeviceStatus model field definitions diff --git a/docs/guides/reservations.rst b/docs/guides/reservations.rst index a22f900..aa2a9bc 100644 --- a/docs/guides/reservations.rst +++ b/docs/guides/reservations.rst @@ -51,7 +51,7 @@ Here's a simple example that sets up a weekday morning reservation: hour=6, minute=30, mode_id=4, # High Demand - param=120 # 140°F display (120 + 20) + param=120 # 140°F (half-degrees Celsius: 60°C × 2) ) # Send to device @@ -131,21 +131,22 @@ Field Descriptions ``param`` (integer, required) Mode-specific parameter value. For temperature modes (1-4), this is the - target water temperature with a **20°F offset**: + target water temperature encoded in **half-degrees Celsius**: - * Display temperature = ``param + 20`` - * Message value = Display temperature - 20 + * Conversion formula: ``fahrenheit = (param / 2.0) * 9/5 + 32`` + * Inverse formula: ``param = (fahrenheit - 32) * 5/9 * 2`` **Temperature Examples:** - * 120°F display → ``param = 100`` - * 130°F display → ``param = 110`` - * 140°F display → ``param = 120`` - * 150°F display → ``param = 130`` + * 95°F display → ``param = 70`` (35°C × 2) + * 120°F display → ``param = 98`` (48.9°C × 2) + * 130°F display → ``param = 110`` (54.4°C × 2) + * 140°F display → ``param = 120`` (60°C × 2) + * 150°F display → ``param = 132`` (65.6°C × 2) For non-temperature modes (Vacation, Power Off), the param value is - typically ignored but should be set to a valid temperature offset - (e.g., ``100``) for consistency. + typically ignored but should be set to a valid temperature value + (e.g., ``98`` for 120°F) for consistency. Helper Functions ================ @@ -168,7 +169,7 @@ Use ``build_reservation_entry()`` to create properly formatted entries: hour=6, minute=30, mode_id=4, # High Demand - param=120 # 140°F (120 + 20) + param=120 # 140°F (half-degrees Celsius: 60°C × 2) ) # Returns: {'enable': 1, 'week': 62, 'hour': 6, 'min': 30, # 'mode': 4, 'param': 120} @@ -180,7 +181,7 @@ Use ``build_reservation_entry()`` to create properly formatted entries: hour=8, minute=0, mode_id=3, # Energy Saver - param=100 # 120°F (100 + 20) + param=98 # ~120°F (half-degrees Celsius: 48.9°C × 2) ) # You can also use day indices (0=Sunday, 6=Saturday) @@ -190,7 +191,7 @@ Use ``build_reservation_entry()`` to create properly formatted entries: hour=18, minute=0, mode_id=1, # Heat Pump Only - param=110 # 130°F (110 + 20) + param=110 # ~130°F (half-degrees Celsius: 54.4°C × 2) ) Encoding Week Bitfields @@ -334,13 +335,15 @@ Request the current reservation schedule from the device: hour = entry.get("hour", 0) minute = entry.get("min", 0) mode = entry.get("mode", 0) - display_temp = entry.get("param", 0) + 20 + # Convert from half-degrees Celsius to Fahrenheit + raw_param = entry.get("param", 0) + display_temp = (raw_param / 2.0) * 9/5 + 32 print(f"\nEntry {idx}:") print(f" Time: {hour:02d}:{minute:02d}") print(f" Days: {', '.join(days)}") print(f" Mode: {mode}") - print(f" Temp: {display_temp}°F") + print(f" Temp: {display_temp:.1f}°F") await mqtt.subscribe(response_topic, on_reservation_response) @@ -508,14 +511,15 @@ Automatically enable vacation mode during a trip: Important Notes =============== -Temperature Offset ------------------- +Temperature Encoding +-------------------- -The ``param`` field uses a **20°F offset** from the display temperature: +The ``param`` field uses **half-degrees Celsius** encoding: -* If you want the display to show 140°F, use ``param=120`` -* If you see ``param=100`` in a response, it means 120°F display -* This offset applies to all temperature-based modes (Heat Pump, Electric, +* Formula: ``fahrenheit = (param / 2.0) * 9/5 + 32`` +* If you want the display to show 140°F, use ``param=120`` (which is 60°C × 2) +* If you see ``param=98`` in a response, it means ~120°F display +* This encoding applies to all temperature-based modes (Heat Pump, Electric, Energy Saver, High Demand) Device Limits @@ -589,7 +593,7 @@ Full working example with error handling and response monitoring: hour=6, minute=30, mode_id=4, # High Demand - param=120 # 140°F + param=120 # 140°F (half-degrees Celsius: 60°C × 2) ), # Weekday day NavienAPIClient.build_reservation_entry( @@ -599,7 +603,7 @@ Full working example with error handling and response monitoring: hour=9, minute=0, mode_id=3, # Energy Saver - param=100 # 120°F + param=98 # ~120°F (half-degrees Celsius: 48.9°C × 2) ), # Weekend morning NavienAPIClient.build_reservation_entry( @@ -608,7 +612,7 @@ Full working example with error handling and response monitoring: hour=8, minute=0, mode_id=3, # Energy Saver - param=110 # 130°F + param=110 # ~130°F (half-degrees Celsius: 54.4°C × 2) ), ] @@ -637,9 +641,11 @@ Full working example with error handling and response monitoring: days = decode_week_bitfield( entry["week"] ) + # Convert from half-degrees Celsius to Fahrenheit + temp_f = (entry['param'] / 2.0) * 9/5 + 32 print(f"Entry {idx}: {entry['hour']:02d}:" f"{entry['min']:02d} - Mode {entry['mode']} - " - f"{entry['param'] + 20}°F - " + f"{temp_f:.1f}°F - " f"{', '.join(days)}") response_received.set() diff --git a/docs/guides/scheduling_features.rst b/docs/guides/scheduling_features.rst index 9ac7000..1b7f6e4 100644 --- a/docs/guides/scheduling_features.rst +++ b/docs/guides/scheduling_features.rst @@ -85,8 +85,9 @@ Each reservation entry controls one scheduled action: "hour": 6, # 0-23 (24-hour format) "min": 30, # 0-59 "mode": 3, # 1=Heat Pump, 2=Electric, 3=Energy Saver, 4=High Demand - "param": 120 # Temperature offset (raw value, add 20 to display) - # 120 raw = 140°F display + "param": 120 # Temperature in half-degrees Celsius + # Formula: fahrenheit = (param / 2.0) * 9/5 + 32 + # 120 = 60°C × 2 = 140°F } **Week Bitfield Encoding**: @@ -107,15 +108,19 @@ The ``week`` field uses 7 bits for days of week: **Temperature Parameter Encoding**: -The ``param`` field stores temperature with an offset of 20°F: +The ``param`` field stores temperature in **half-degrees Celsius**: .. code-block:: text - Display Temperature → Raw Parameter Value - 95°F → 75 (95-20) - 120°F → 100 (120-20) - 140°F → 120 (140-20) - 150°F → 130 (150-20) + Conversion: fahrenheit = (param / 2.0) * 9/5 + 32 + Inverse: param = (fahrenheit - 32) * 5/9 * 2 + + Temperature Examples: + 95°F → 70 (35°C × 2) + 120°F → 98 (48.9°C × 2) + 130°F → 110 (54.4°C × 2) + 140°F → 120 (60°C × 2) + 150°F → 132 (65.6°C × 2) **Mode Selection Strategy**: diff --git a/docs/protocol/device_features.rst b/docs/protocol/device_features.rst index 64c02e7..9b56866 100644 --- a/docs/protocol/device_features.rst +++ b/docs/protocol/device_features.rst @@ -103,12 +103,12 @@ The DeviceFeature data contains comprehensive device capabilities, configuration - int - °F - Minimum DHW temperature setting: 95°F (35°C) - safety and efficiency lower limit - - ``raw + 20`` + - HalfCelsiusToF * - ``dhwTemperatureMax`` - int - °F - Maximum DHW temperature setting: 150°F (65.5°C) - scald protection upper limit - - ``raw + 20`` + - HalfCelsiusToF * - ``smartDiagnosticUse`` - int - Boolean @@ -143,12 +143,12 @@ The DeviceFeature data contains comprehensive device capabilities, configuration - int - °F - Minimum freeze protection threshold: 43°F (6°C) - factory default activation temperature - - ``raw + 20`` + - HalfCelsiusToF * - ``freezeProtectionTempMax`` - int - °F - Maximum freeze protection threshold: typically 65°F - user-adjustable upper limit - - ``raw + 20`` + - HalfCelsiusToF * - ``mixingValueUse`` - int - Boolean @@ -292,15 +292,17 @@ Temperature Range Validation The reported temperature ranges align with official specifications and use the same conversion patterns as DeviceStatus fields: -* **DHW Range**: 95°F to 150°F (factory default: 120°F for safety) - uses ``raw + 20`` conversion -* **Freeze Protection**: Activates at 43°F, prevents tank freezing - uses ``raw + 20`` conversion +* **DHW Range**: 95°F to 150°F (factory default: 120°F for safety) - uses HalfCelsiusToF conversion +* **Freeze Protection**: Activates at 43°F, prevents tank freezing - uses HalfCelsiusToF conversion * **Anti-Legionella**: Heats to 140°F at programmed intervals (requires mixing valve) * **Scald Protection**: Built-in limits with recommendation for thermostatic mixing valves -**Conversion Pattern Consistency**: Temperature fields in DeviceFeature use the same ``raw + 20`` +**Conversion Pattern Consistency**: Temperature fields in DeviceFeature use the same HalfCelsiusToF conversion formula as corresponding fields in DeviceStatus, ensuring consistent temperature handling across all device data structures. +**HalfCelsiusToF Formula**: ``fahrenheit = (raw_value / 2.0) * 9/5 + 32`` + Usage Example ------------- diff --git a/docs/protocol/firmware_tracking.rst b/docs/protocol/firmware_tracking.rst index 5a8793a..dfb7c86 100644 --- a/docs/protocol/firmware_tracking.rst +++ b/docs/protocol/firmware_tracking.rst @@ -44,7 +44,7 @@ The following table tracks known fields that have been introduced in firmware up - Description * - ``heatMinOpTemperature`` - Controller: 184614912, WiFi: 34013184 - - ``raw + 20`` + - HalfCelsiusToF - Minimum heat pump operation temperature. Lowest tank temperature setpoint allowed in the current operating mode (95-113°F, default 95°F). Reporting New Fields @@ -123,7 +123,7 @@ Example entry in ``constants.py``: "newFieldName": { "introduced_in": "controller: 123, panel: 456, wifi: 789", "description": "What this field represents", - "conversion": "raw + 20", # or "raw / 10.0", "bool (1=OFF, 2=ON)", etc. + "conversion": "HalfCelsiusToF", # or "DeciCelsiusToF", "bool (1=OFF, 2=ON)", etc. }, } diff --git a/docs/protocol/mqtt_protocol.rst b/docs/protocol/mqtt_protocol.rst index ec376d6..ac84d1d 100644 --- a/docs/protocol/mqtt_protocol.rst +++ b/docs/protocol/mqtt_protocol.rst @@ -245,8 +245,9 @@ DHW Temperature } .. important:: - Temperature is 20°F less than display value. For 140°F display, - send 120°F. + Temperature values are encoded in **half-degrees Celsius**. + Use formula: ``fahrenheit = (param / 2.0) * 9/5 + 32`` + For 140°F, send ``param=120`` (which is 60°C × 2). Anti-Legionella --------------- @@ -349,7 +350,7 @@ Status Response **Field Conversions:** * Boolean fields: 1=false, 2=true -* Temperature fields: Add 20 to get display value +* Temperature fields: Use HalfCelsiusToF formula: ``fahrenheit = (raw / 2.0) * 9/5 + 32`` * Enum fields: Map integers to enum values See :doc:`device_status` for complete field reference. diff --git a/docs/python_api/constants.rst b/docs/python_api/constants.rst index ae15597..400ec1e 100644 --- a/docs/python_api/constants.rst +++ b/docs/python_api/constants.rst @@ -311,7 +311,7 @@ Some fields were introduced in specific firmware versions: "heatMinOpTemperature": { "introduced_in": "Controller: 184614912, WiFi: 34013184", "description": "Minimum heat pump operation temperature", - "conversion": "raw + 20" + "conversion": "HalfCelsiusToF" } } diff --git a/src/nwp500/models.py b/src/nwp500/models.py index c8f332c..e1e1b33 100644 --- a/src/nwp500/models.py +++ b/src/nwp500/models.py @@ -483,7 +483,7 @@ class DeviceStatus(NavienBaseModel): description="Recirculation reservation usage status" ) - # Temperature fields with offset (raw + 20) + # Temperature fields - encoded in half-degrees Celsius dhw_temperature: HalfCelsiusToF = Field( description="Current Domestic Hot Water (DHW) outlet temperature", json_schema_extra={