diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c9e30c2..e7dd7d6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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) ========================== diff --git a/docs/protocol/quick_reference.rst b/docs/protocol/quick_reference.rst index f83385e..cca69f8 100644 --- a/docs/protocol/quick_reference.rst +++ b/docs/protocol/quick_reference.rst @@ -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: diff --git a/src/nwp500/models.py b/src/nwp500/models.py index 469207a..4922d25 100644 --- a/src/nwp500/models.py +++ b/src/nwp500/models.py @@ -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)) diff --git a/tests/test_model_converters.py b/tests/test_model_converters.py index c6396a7..4eb5f5b 100644 --- a/tests/test_model_converters.py +++ b/tests/test_model_converters.py @@ -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 @@ -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.