Add N/A value support for unavailable sensors and features#69
Add N/A value support for unavailable sensors and features#69
Conversation
Support proper N/A display for device features that report 0 to indicate 'not available' rather than actual zero readings. This addresses confusion where missing sensors showed misleading values like 0.0°C/32.0°F instead of N/A. Three categories of N/A support: - Temperature sensors (outside, inlet, recirculation, etc.) - when sensor doesn't exist - Optional feature settings (mixing valve, heating element lower) - when feature not present or not applicable to current mode - Recirculation pump status - when no pump installed Key changes: - Modified 4 temperature converters to return float | None (return None when value is 0) - Created new converters for optional feature values (int_with_zero_as_none, float_with_zero_as_none, etc.) - Updated ~25 fields in DeviceStatus model to support optional values - CLI now displays 'N/A' instead of misleading zero values - Added comprehensive test coverage (6 new tests) - Documented all changes in CHANGES.md
- Use ternary operator for N/A value formatting (SIM108) - Shorten test docstrings to meet line length limit (E501) - Add None check for recirc_operation_mode before accessing attributes to fix type errors in handlers.py (reportOptionalMemberAccess)
There was a problem hiding this comment.
Pull request overview
This PR implements comprehensive N/A value support for device features that use 0 to indicate unavailability rather than actual zero readings. The changes distinguish between missing sensors (hardware-dependent), optional features (model-dependent), and mode-dependent features (operating mode).
Changes:
- Modified temperature converters to return
float | Nonewhen value is0 - Added new converters for optional features that treat
0as None - Updated ~25 model field definitions to support optional values
- Enhanced CLI display to show "N/A" instead of misleading zero values
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
tests/test_models.py |
Added 6 comprehensive tests validating N/A behavior for temperature sensors, mixing valve rate, heating element settings, and recirculation status fields |
src/nwp500/models.py |
Updated type annotations and field definitions to support optional values for sensors and features that can be unavailable |
src/nwp500/converters.py |
Modified temperature converters to return None for zero values and added new converters for optional feature support |
src/nwp500/cli/output_formatters.py |
Updated display logic to show "N/A" for None values instead of attempting to format them |
src/nwp500/cli/handlers.py |
Added None check for recirculation operation mode before accessing its value |
CHANGES.md |
Comprehensive documentation of implementation details, design decisions, and usage examples |
CHANGELOG.rst |
Version 7.4.0 changelog entry documenting breaking changes and new features |
tests/test_models.py
Outdated
| default_status_data["recircPumpOperationStatus"] = 1 | ||
| default_status_data["recircHotBtnReady"] = 5 | ||
| default_status_data["recircOperationReason"] = 3 | ||
| default_status_data["recircErrorStatus"] = 0 # Should be None |
There was a problem hiding this comment.
The inline comment '# Should be None' is misleading because the test is setting the value to 0, not None. This comment describes the expected outcome after validation, not the input value. Consider either removing the comment or clarifying it to say '# Will be converted to None' or '# 0 should become None'.
| default_status_data["recircErrorStatus"] = 0 # Should be None | |
| default_status_data["recircErrorStatus"] = 0 # 0 should become None |
Changed '# Should be None' to '# 0 will become None' to accurately describe that the input value of 0 will be converted to None during validation, addressing review feedback from Copilot.
|
✅ Addressed review feedback: Fixed misleading comment in test_models.py line 246 Changed from: default_status_data["recircErrorStatus"] = 0 # Should be NoneTo: default_status_data["recircErrorStatus"] = 0 # 0 will become NoneThis clarifies that the comment describes the conversion behavior (0 → None during validation) rather than describing the input value itself. Fixed in commit: 8fc4b73 |
BREAKING CHANGE: Heat pump sensors now correctly report 0°C/32°F instead of None for freezing temperatures. Problem: The previous implementation treated 0 as a universal sentinel for "N/A", causing heat pump compressor/refrigerant sensors to incorrectly report "unavailable" when measuring 0°C during normal operation. Solution: - Removed zero-as-none from deci_celsius_to_preferred() converter (used for heat pump sensors with 0.1°C precision) - Kept zero-as-none for half_celsius_to_preferred() converter (used for optional sensors and mode-dependent settings) - Kept zero-as-none for raw_celsius_to_preferred() converter (used for optional outside sensor) Protocol Analysis: The device protocol uses different temperature precision for different sensor types with different semantics: - DeciCelsius (0.1°C): Heat pump sensors - 0 = valid 0°C reading - HalfCelsius (0.5°C): Optional/mode-dependent - 0 = N/A - RawCelsius: Optional external sensor - 0 = not installed Type Safety: - Added None-handling to _format_number() in CLI formatters - Updated example code to safely format optional temperatures - All tests passing (410 passed, 3 skipped) Fixes issue identified in reference/na_evaluation.txt
Critical Bug Fix Applied (2026-02-13)Problem IdentifiedAfter reviewing Heat pump compressor/refrigerant sensors were incorrectly treating 0°C as "sensor unavailable", causing false "Unknown" states during cold weather operation. Root CauseThe initial implementation used 0 as a universal sentinel for "N/A" across all temperature converters. However, the device protocol uses different semantics for different field types:
Protocol EvidenceFrom HAR file analysis ( "ambientTemperature": 258, // 25.8°C - valid reading
"ambientTemperature": 266, // 26.6°C - valid reading
"outsideTemperature": 0, // Always 0 - sensor not present
"currentInletTemperature": 0, // Always 0 - no flow
"mixingRate": 0, // Always 0 - feature not presentHeat pump sensors ( Solution AppliedFixed # BEFORE (incorrect)
def deci_celsius_to_preferred(...) -> float | None:
if value == 0:
return None # ❌ Wrong! Heat pump can measure 0°C
return DeciCelsius(value).to_preferred(is_celsius)
# AFTER (correct)
def deci_celsius_to_preferred(...) -> float | None:
# Heat pump sensors can measure 0°C - don't treat as None
return DeciCelsius(value).to_preferred(is_celsius)Kept zero-as-none for Impact
Validation✅ All 410 tests passing The fix ensures we distinguish between:
|
Summary
This PR implements proper N/A value support for device features that report
0to indicate "not available" rather than actual zero readings. UPDATED (2026-02-13): Fixed critical bug where heat pump sensors incorrectly treated 0°C as "unavailable".Problem
The device protocol uses
0to indicate different scenarios depending on the field type:Original Issue: All zero values showed as actual measurements, making it impossible to distinguish unavailable features.
Bug Fixed in Latest Commit: Heat pump compressor/refrigerant sensors incorrectly returned
Nonefor 0°C readings, causing false "sensor unavailable" reports during cold weather operation.Solution
Implemented field-type-specific N/A support with correct zero handling:
1. Heat Pump Sensors (DeciCelsius - 0.1°C precision)
deci_celsius_to_preferred()- Removed zero-as-none logictank_upper_temperature,tank_lower_temperature,discharge_temperature,suction_temperature,evaporator_temperature,ambient_temperature,target_super_heat,current_super_heat2. Optional/Mode-Dependent Sensors (HalfCelsius - 0.5°C precision)
half_celsius_to_preferred()- Keeps zero-as-none logicdhw_temperature,dhw_temperature2,current_inlet_temperature,recirc_temperature,recirc_faucet_temperature,he_lower_on_temp_setting,he_lower_off_temp_setting3. Optional External Sensor (RawCelsius)
raw_celsius_to_preferred()- Keeps zero-as-none logicoutside_temperature4. Optional Feature Settings
float_with_zero_as_none()converter5. Recirculation Pump Status (7 fields)
int_with_zero_as_none(),enum_with_zero_as_none_validator(),device_bool_with_zero_as_none()Changes
Files Modified:
src/nwp500/converters.py- Fixeddeci_celsius_to_preferred()zero handling, added comprehensive documentationsrc/nwp500/models.py- Updated type annotation comments to clarify zero semanticssrc/nwp500/cli/output_formatters.py- Enhanced_format_number()to handle None gracefullyexamples/intermediate/device_status_callback.py- Added safe None handling for temperature displaytests/test_models.py- Updated tests to reflect correct zero behavior for each field typeCHANGELOG.rst- Added Version 7.5.0 with breaking change documentationTest Coverage:
Protocol Analysis Evidence
From HAR file inspection (
reference/HTTPToolkit_*.har):outsideTemperature: Always 0 → Optional sensor not present ✅ambientTemperature: 258, 266 → Valid temps (25.8°C, 26.6°C) ✅mixingRate: Always 0 → Feature not present ✅currentInletTemperature: Always 0 → Optional sensor ✅dhwTemperature2: Always 0 → Secondary sensor not present ✅heLowerOnTempSetting: 70 → Has value when applicable ✅This confirms the protocol uses 0 differently for different field categories.
Type System Changes
Created clear distinction based on sensor type:
Breaking Changes
Nonefor 0°C will now return32.0(°F) or0.0(°C).Impact:
DeciCelsiusToPreferredfields (heat pump compressor/refrigerant sensors)Migration:
Documentation
See
CHANGELOG.rstVersion 7.5.0 for:Fix Validation
Addressed issues identified in
reference/na_evaluation.txt:Testing with Real Protocol Data:
Future Enhancements
Potential follow-ups: