Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
Changelog
=========

Version 7.2.2 (2025-12-25)
==========================

Fixed
-----
- **TOU Status Always Showing False**: Fixed ``touStatus`` field always reporting ``False`` regardless of actual device state

- Root cause: Version 7.2.1 incorrectly changed ``touStatus`` to use device-specific 1/2 encoding, but the device uses standard 0/1 encoding
- Solution: Use Python's built-in ``bool()`` for ``touStatus`` field (handles 0=False, 1=True naturally)
- Updated documentation in ``docs/protocol/quick_reference.rst`` to note ``touStatus`` exception
- Added tests verifying built-in ``bool()`` handles 0/1 encoding correctly
- Device encoding: 0=OFF/disabled, 1=ON/enabled (standard Python truthiness)

Version 7.2.1 (2025-12-25)
==========================

Expand Down
4 changes: 3 additions & 1 deletion docs/protocol/quick_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ The device uses non-standard boolean encoding in many status fields:
- Notes
* - **1**
- OFF / False
- Standard: False value. Used for power, TOU status, and most feature flags.
- Standard: False value. Used for power and most feature flags.
* - **2**
- ON / True
- Standard: True value.

**Exception:** The ``touStatus`` field uses 0/1 encoding (0=disabled, 1=enabled) instead of the standard 1/2 encoding.

**Why 1 & 2?**
This likely stems from legacy firmware design where:

Expand Down
2 changes: 1 addition & 1 deletion src/nwp500/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
Div10 = Annotated[float, BeforeValidator(div_10)]
HalfCelsiusToF = Annotated[float, BeforeValidator(half_celsius_to_fahrenheit)]
DeciCelsiusToF = Annotated[float, BeforeValidator(deci_celsius_to_fahrenheit)]
TouStatus = Annotated[bool, BeforeValidator(device_bool_to_python)]
TouStatus = Annotated[bool, BeforeValidator(bool)]
TouOverride = Annotated[bool, BeforeValidator(tou_override_to_python)]
VolumeCodeField = Annotated[
VolumeCode, BeforeValidator(enum_validator(VolumeCode))
Expand Down
35 changes: 33 additions & 2 deletions tests/test_model_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
- div_10 (divide by 10 converter)
- enum_validator (enum validation and conversion)

Note: tou_status field uses device_bool_to_python
(same as all other OnOffFlag fields)
Note: touStatus field uses built-in bool() for standard 0/1 encoding
"""

import pytest
Expand Down Expand Up @@ -91,6 +90,38 @@ def test_off_value_variations(self, off_value):
assert device_bool_to_python(off_value) is False


class TestBuiltinBoolForTouStatus:
"""Test built-in bool() for TOU status field (0/1 encoding).

The touStatus field uses standard 0/1 encoding, so Python's built-in
bool() is sufficient - no custom converter needed.
"""

def test_enabled_state(self):
"""TOU enabled: 1 = True."""
assert bool(1) is True

def test_disabled_state(self):
"""TOU disabled: 0 = False."""
assert bool(0) is False

def test_nonzero_is_true(self):
"""Any non-zero value is truthy."""
assert bool(2) is True
assert bool(99) is True
assert bool(-1) is True

@pytest.mark.parametrize("value", [0, 0.0, "", [], {}, None])
def test_falsy_values(self, value):
"""Test various falsy values."""
assert bool(value) is False

@pytest.mark.parametrize("value", [1, 2, -1, "text", [1], {1: 1}])
def test_truthy_values(self, value):
"""Test various truthy values."""
assert bool(value) is True


class TestTouOverrideConverter:
"""Test tou_override_to_python converter.

Expand Down