nwp500-python 6.0.3__tar.gz → 6.0.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/.github/workflows/release.yml +16 -1
  2. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/CHANGELOG.rst +54 -0
  3. {nwp500_python-6.0.3/src/nwp500_python.egg-info → nwp500_python-6.0.5}/PKG-INFO +1 -1
  4. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/protocol/data_conversions.rst +43 -43
  5. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/protocol/device_status.rst +23 -23
  6. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/python_api/models.rst +2 -14
  7. nwp500_python-6.0.5/scripts/extract_changelog.py +43 -0
  8. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/cli/commands.py +9 -6
  9. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/cli/output_formatters.py +1 -2
  10. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/models.py +36 -35
  11. {nwp500_python-6.0.3 → nwp500_python-6.0.5/src/nwp500_python.egg-info}/PKG-INFO +1 -1
  12. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/SOURCES.txt +2 -0
  13. nwp500_python-6.0.5/tests/test_models.py +132 -0
  14. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/.agent/workflows/pre-completion-testing.md +0 -0
  15. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/.coveragerc +0 -0
  16. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/.github/copilot-instructions.md +0 -0
  17. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/.github/workflows/ci.yml +0 -0
  18. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/.gitignore +0 -0
  19. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/.pre-commit-config.yaml +0 -0
  20. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/.readthedocs.yml +0 -0
  21. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/AUTHORS.rst +0 -0
  22. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/CONTRIBUTING.rst +0 -0
  23. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/LICENSE.txt +0 -0
  24. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/Makefile +0 -0
  25. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/README.rst +0 -0
  26. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/RELEASE.md +0 -0
  27. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/Makefile +0 -0
  28. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/_static/.gitignore +0 -0
  29. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/api/nwp500.rst +0 -0
  30. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/authors.rst +0 -0
  31. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/changelog.rst +0 -0
  32. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/conf.py +0 -0
  33. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/configuration.rst +0 -0
  34. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/development/contributing.rst +0 -0
  35. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/development/history.rst +0 -0
  36. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/guides/advanced_features_explained.rst +0 -0
  37. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/guides/auto_recovery.rst +0 -0
  38. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/guides/command_queue.rst +0 -0
  39. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/guides/energy_monitoring.rst +0 -0
  40. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/guides/event_system.rst +0 -0
  41. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/guides/reservations.rst +0 -0
  42. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/guides/scheduling_features.rst +0 -0
  43. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/guides/time_of_use.rst +0 -0
  44. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/index.rst +0 -0
  45. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/installation.rst +0 -0
  46. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/license.rst +0 -0
  47. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/openapi.yaml +0 -0
  48. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/protocol/device_features.rst +0 -0
  49. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/protocol/error_codes.rst +0 -0
  50. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/protocol/firmware_tracking.rst +0 -0
  51. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/protocol/mqtt_protocol.rst +0 -0
  52. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/protocol/rest_api.rst +0 -0
  53. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/python_api/api_client.rst +0 -0
  54. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/python_api/auth_client.rst +0 -0
  55. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/python_api/cli.rst +0 -0
  56. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/python_api/constants.rst +0 -0
  57. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/python_api/events.rst +0 -0
  58. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/python_api/exceptions.rst +0 -0
  59. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/python_api/mqtt_client.rst +0 -0
  60. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/quickstart.rst +0 -0
  61. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/docs/requirements.txt +0 -0
  62. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/.ruff.toml +0 -0
  63. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/README.md +0 -0
  64. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/anti_legionella_example.py +0 -0
  65. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/api_client_example.py +0 -0
  66. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/auth_constructor_example.py +0 -0
  67. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/authenticate.py +0 -0
  68. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/auto_recovery_example.py +0 -0
  69. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/combined_callbacks.py +0 -0
  70. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/command_queue_demo.py +0 -0
  71. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/device_feature_callback.py +0 -0
  72. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/device_status_callback.py +0 -0
  73. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/device_status_callback_debug.py +0 -0
  74. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/energy_usage_example.py +0 -0
  75. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/event_emitter_demo.py +0 -0
  76. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/exception_handling_example.py +0 -0
  77. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/improved_auth_pattern.py +0 -0
  78. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/mask.py +0 -0
  79. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/mqtt_client_example.py +0 -0
  80. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/periodic_device_info.py +0 -0
  81. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/periodic_requests.py +0 -0
  82. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/power_control_example.py +0 -0
  83. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/reconnection_demo.py +0 -0
  84. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/reservation_schedule_example.py +0 -0
  85. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/set_dhw_temperature_example.py +0 -0
  86. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/set_mode_example.py +0 -0
  87. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/simple_auto_recovery.py +0 -0
  88. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/simple_periodic_info.py +0 -0
  89. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/simple_periodic_status.py +0 -0
  90. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/test_api_client.py +0 -0
  91. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/test_mqtt_connection.py +0 -0
  92. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/test_mqtt_messaging.py +0 -0
  93. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/test_periodic_minimal.py +0 -0
  94. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/token_restoration_example.py +0 -0
  95. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/tou_openei_example.py +0 -0
  96. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/examples/tou_schedule_example.py +0 -0
  97. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/pyproject.toml +0 -0
  98. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/scripts/README.md +0 -0
  99. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/scripts/bump_version.py +0 -0
  100. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/scripts/format.py +0 -0
  101. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/scripts/lint.py +0 -0
  102. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/scripts/setup-dev.py +0 -0
  103. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/scripts/validate_version.py +0 -0
  104. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/setup.cfg +0 -0
  105. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/setup.py +0 -0
  106. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/__init__.py +0 -0
  107. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/api_client.py +0 -0
  108. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/auth.py +0 -0
  109. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/cli/__init__.py +0 -0
  110. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/cli/__main__.py +0 -0
  111. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/cli/monitoring.py +0 -0
  112. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/cli/token_storage.py +0 -0
  113. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/config.py +0 -0
  114. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/constants.py +0 -0
  115. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/encoding.py +0 -0
  116. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/events.py +0 -0
  117. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/exceptions.py +0 -0
  118. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/mqtt_client.py +0 -0
  119. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/mqtt_command_queue.py +0 -0
  120. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/mqtt_connection.py +0 -0
  121. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/mqtt_device_control.py +0 -0
  122. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/mqtt_periodic.py +0 -0
  123. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/mqtt_reconnection.py +0 -0
  124. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/mqtt_subscriptions.py +0 -0
  125. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/mqtt_utils.py +0 -0
  126. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/py.typed +0 -0
  127. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500/utils.py +0 -0
  128. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  129. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  130. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  131. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/requires.txt +0 -0
  132. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/top_level.txt +0 -0
  133. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/tests/conftest.py +0 -0
  134. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/tests/test_api_helpers.py +0 -0
  135. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/tests/test_auth.py +0 -0
  136. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/tests/test_command_queue.py +0 -0
  137. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/tests/test_events.py +0 -0
  138. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/tests/test_exceptions.py +0 -0
  139. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/tests/test_utils.py +0 -0
  140. {nwp500_python-6.0.3 → nwp500_python-6.0.5}/tox.ini +0 -0
@@ -77,9 +77,24 @@ jobs:
77
77
  steps:
78
78
  - uses: actions/checkout@v4
79
79
 
80
+ - name: Set up Python
81
+ uses: actions/setup-python@v5
82
+ with:
83
+ python-version: '3.10'
84
+
85
+ - name: Extract changelog for this version
86
+ id: changelog
87
+ run: |
88
+ VERSION=${GITHUB_REF#refs/tags/v}
89
+ NOTES=$(python3 scripts/extract_changelog.py CHANGELOG.rst "$VERSION")
90
+ # Use multiline output format for GitHub Actions
91
+ echo "notes<<EOF" >> $GITHUB_OUTPUT
92
+ echo "$NOTES" >> $GITHUB_OUTPUT
93
+ echo "EOF" >> $GITHUB_OUTPUT
94
+
80
95
  - name: Create Release
81
96
  uses: softprops/action-gh-release@v1
82
97
  with:
83
- generate_release_notes: true
98
+ body: ${{ steps.changelog.outputs.notes }}
84
99
  draft: false
85
100
  prerelease: false
@@ -2,6 +2,60 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 6.0.5 (2025-11-21)
6
+ ==========================
7
+
8
+ Fixed
9
+ -----
10
+
11
+ - **CRITICAL Temperature Conversion Bug**: Corrected temperature conversion formula for 8 sensor fields that were displaying values ~100°F higher than expected. The v6.0.4 change incorrectly used division by 5 (pentacelsius) instead of division by 10 (decicelsius) for these fields:
12
+
13
+ - ``tank_upper_temperature`` - Water tank upper sensor
14
+ - ``tank_lower_temperature`` - Water tank lower sensor
15
+ - ``discharge_temperature`` - Compressor discharge temperature (refrigerant)
16
+ - ``suction_temperature`` - Compressor suction temperature (refrigerant)
17
+ - ``evaporator_temperature`` - Evaporator coil temperature (refrigerant)
18
+ - ``ambient_temperature`` - Ambient air temperature at heat pump
19
+ - ``target_super_heat`` - Target superheat setpoint
20
+ - ``current_super_heat`` - Measured superheat value
21
+
22
+ **Impact**: These fields now correctly display temperatures in expected ranges:
23
+
24
+ - Tank temperatures: ~120°F (close to DHW temperature, not ~220°F)
25
+ - Discharge temperature: 120-180°F (not 220-280°F)
26
+ - Suction, evaporator, ambient: Now showing physically realistic values
27
+
28
+ **Technical details**: Changed from ``PentaCelsiusToF`` (÷5) back to ``DeciCelsiusToF`` (÷10). The correct formula is ``(raw_value / 10.0) * 9/5 + 32``.
29
+
30
+ Changed
31
+ -------
32
+
33
+ - **Documentation**: Updated ``data_conversions.rst`` and ``device_status.rst`` to reflect correct ``DeciCelsiusToF`` conversion for refrigerant circuit and tank temperature sensors
34
+
35
+ Version 6.0.4 (2025-11-21)
36
+ ==========================
37
+
38
+ Fixed
39
+ -----
40
+
41
+ - **Temperature Conversion Accuracy**: Corrected temperature conversion logic based on analysis of the decompiled mobile application. Previous conversions used approximations; new logic uses exact formulas from the app:
42
+
43
+ - Replaced ``Add20`` validator with ``HalfCelsiusToF`` for fields transmitted as half-degrees Celsius
44
+ - Replaced ``DeciCelsiusToF`` with ``PentaCelsiusToF`` for fields scaled by factor of 5
45
+ - Affects multiple temperature sensor readings for improved accuracy
46
+
47
+ - **CLI Output Formatting**: Fixed formatting issues in command-line interface output
48
+
49
+ Changed
50
+ -------
51
+
52
+ - **Documentation**: Updated temperature conversion documentation to use precise 9/5 fraction instead of 1.8 approximation for clarity
53
+
54
+ Added
55
+ -----
56
+
57
+ - **Test Coverage**: Added ``tests/test_models.py`` to verify temperature conversion correctness
58
+
5
59
  Version 6.0.3 (2025-11-20)
6
60
  ==========================
7
61
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 6.0.3
3
+ Version: 6.0.5
4
4
  Summary: A library for controlling Navien NWP500 Water Heaters via NaviLink
5
5
  Home-page: https://github.com/eman/nwp500-python
6
6
  Author: Emmanuel Levijarvi
@@ -18,26 +18,25 @@ Raw Encoding Strategies
18
18
 
19
19
  The device uses several encoding schemes to minimize transmission overhead:
20
20
 
21
- 1. **Offset Encoding** (add_20)
22
- - Applied to most temperature fields
23
- - Formula: ``displayed_value = raw_value + 20``
24
- - Purpose: Negative temperatures stored as positive integers
25
- - Range: Typically -4°F (-20°C) to 149°F (65°C)
26
- - Example: Raw 100 → 120°F display value
27
-
28
- 2. **Tenths Encoding** (div_10)
21
+ 1. **Half-degree Celsius to Fahrenheit** (HalfCelsiusToF)
22
+ - Applied to most temperature fields that are not scaled by other factors.
23
+ - Formula: ``displayed_value = (raw_value / 2.0) * 9/5 + 32``
24
+ - Purpose: Converts raw values, which are in half-degrees Celsius, to Fahrenheit.
25
+ - Example: Raw 122 -> (122 / 2) * 9/5 + 32 = 141.8°F
26
+
27
+ 2. **Decicelsius to Fahrenheit** (DeciCelsiusToF)
28
+ - Applied to refrigerant circuit and tank temperature sensors.
29
+ - Formula: ``displayed_value = (raw_value / 10.0) * 9/5 + 32``
30
+ - Purpose: Converts raw values, which are in tenths of degrees Celsius, to Fahrenheit.
31
+ - Example: Raw 489 -> (489 / 10) * 9/5 + 32 = 120.0°F
32
+
33
+ 3. **Tenths Encoding** (div_10)
29
34
  - Applied to decimal precision values
30
35
  - Formula: ``displayed_value = raw_value / 10.0``
31
36
  - Purpose: Preserve decimal precision in integer storage
32
37
  - Common for flow rates and differential temperatures
33
38
  - Example: Raw 125 → 12.5 GPM
34
39
 
35
- 3. **Decicelsius to Fahrenheit** (decicelsius_to_f)
36
- - Applied to refrigerant and evaporator temperatures
37
- - Formula: ``displayed_value = (raw_value / 10) * 9/5 + 32``
38
- - Purpose: Convert Celsius tenths to Fahrenheit
39
- - Example: Raw 250 (25°C) → 77°F
40
-
41
40
  4. **Boolean Encoding** (device_bool)
42
41
  - Applied to all status flags
43
42
  - Formula: ``displayed_value = (raw_value == 2)``
@@ -66,15 +65,15 @@ DHW (Domestic Hot Water) Temperatures
66
65
  - Display Unit
67
66
  - Description
68
67
  * - ``dhwTemperature``
69
- - add_20
68
+ - HalfCelsiusToF
70
69
  - °F
71
70
  - **Current outlet temperature** of hot water being delivered to fixtures. Real-time measurement. Typically 90-150°F.
72
71
  * - ``dhwTemperature2``
73
- - add_20
72
+ - HalfCelsiusToF
74
73
  - °F
75
74
  - **Secondary DHW temperature sensor** reading (redundancy/averaging). May differ slightly from primary sensor during temperature transitions.
76
75
  * - ``dhwTemperatureSetting``
77
- - add_20
76
+ - HalfCelsiusToF
78
77
  - °F
79
78
  - **User-configured target temperature** for DHW delivery. Adjustable range: 95-150°F. Default: 120°F. This is the setpoint users configure in the app.
80
79
  * - ``currentInletTemperature``
@@ -82,7 +81,7 @@ DHW (Domestic Hot Water) Temperatures
82
81
  - °F
83
82
  - **Cold water inlet temperature** to the water heater. Affects heating performance and recovery time. Typically 40-80°F depending on season and location.
84
83
  * - ``dhwTargetTemperatureSetting``
85
- - add_20
84
+ - HalfCelsiusToF
86
85
  - °F
87
86
  - **Duplicate of dhwTemperatureSetting** for legacy API compatibility.
88
87
 
@@ -98,11 +97,11 @@ Tank Temperature Sensors
98
97
  - Display Unit
99
98
  - Description
100
99
  * - ``tankUpperTemperature``
101
- - decicelsius_to_f
100
+ - DeciCelsiusToF
102
101
  - °F
103
102
  - **Upper tank sensor temperature**. Indicates stratification - hot water at top for quick delivery. Typically hottest point in tank.
104
103
  * - ``tankLowerTemperature``
105
- - decicelsius_to_f
104
+ - DeciCelsiusToF
106
105
  - °F
107
106
  - **Lower tank sensor temperature**. Indicates bulk tank temperature and heating progress. Typically cooler than upper sensor.
108
107
 
@@ -122,27 +121,27 @@ These temperatures monitor the heat pump refrigerant circuit health and performa
122
121
  - Display Unit
123
122
  - Description
124
123
  * - ``dischargeTemperature``
125
- - decicelsius_to_f
124
+ - DeciCelsiusToF
126
125
  - °F
127
126
  - **Compressor discharge temperature**. Temperature of refrigerant exiting the compressor. Typically 120-180°F. High values indicate high system pressure; low values indicate efficiency issues.
128
127
  * - ``suctionTemperature``
129
- - decicelsius_to_f
128
+ - DeciCelsiusToF
130
129
  - °F
131
130
  - **Compressor suction temperature**. Temperature of refrigerant entering the compressor. Typically 40-60°F. Affects superheat calculation.
132
131
  * - ``evaporatorTemperature``
133
- - decicelsius_to_f
132
+ - DeciCelsiusToF
134
133
  - °F
135
134
  - **Evaporator coil temperature**. Where heat is extracted from ambient air. Typically 20-50°F. Lower outdoor air temperature reduces evaporator efficiency.
136
135
  * - ``ambientTemperature``
137
- - decicelsius_to_f
136
+ - DeciCelsiusToF
138
137
  - °F
139
138
  - **Ambient air temperature** measured at heat pump inlet. Directly affects system performance. At freezing (32°F), heat pump efficiency drops significantly.
140
139
  * - ``targetSuperHeat``
141
- - decicelsius_to_f
140
+ - DeciCelsiusToF
142
141
  - °F
143
142
  - **Target superheat setpoint**. Desired temperature difference between suction and evaporator ensuring complete refrigerant vaporization. Typically 10-20°F.
144
143
  * - ``currentSuperHeat``
145
- - decicelsius_to_f
144
+ - DeciCelsiusToF
146
145
  - °F
147
146
  - **Measured superheat value**. Actual temperature difference. Deviation from target indicates EEV (Electronic Expansion Valve) control issues.
148
147
 
@@ -166,19 +165,19 @@ Electric heating elements are controlled via thermostat ranges. Two sensors (upp
166
165
  - Display Unit
167
166
  - Description
168
167
  * - ``heUpperOnTempSetting``
169
- - add_20
168
+ - HalfCelsiusToF
170
169
  - °F
171
170
  - **Upper element ON threshold**. Upper tank temp must fall below this to activate upper heating element.
172
171
  * - ``heUpperOffTempSetting``
173
- - add_20
172
+ - HalfCelsiusToF
174
173
  - °F
175
174
  - **Upper element OFF threshold**. Upper tank temp rises above this to deactivate upper element (hysteresis).
176
175
  * - ``heLowerOnTempSetting``
177
- - add_20
176
+ - HalfCelsiusToF
178
177
  - °F
179
178
  - **Lower element ON threshold**. Lower tank temp must fall below this to activate lower element.
180
179
  * - ``heLowerOffTempSetting``
181
- - add_20
180
+ - HalfCelsiusToF
182
181
  - °F
183
182
  - **Lower element OFF threshold**. Lower tank temp rises above this to deactivate lower element.
184
183
  * - ``heUpperOnDiffTempSetting``
@@ -198,7 +197,7 @@ Electric heating elements are controlled via thermostat ranges. Two sensors (upp
198
197
  - °F
199
198
  - **Lower element differential** variation.
200
199
  * - ``heatMinOpTemperature``
201
- - add_20
200
+ - HalfCelsiusToF
202
201
  - °F
203
202
  - **Minimum heat pump operation temperature**. Lowest tank temperature setpoint allowed in the current operating mode. Range: 95-113°F. Default: 95°F. When set, the user can only set the target tank temperature at or above this threshold, ensuring minimum system operating conditions.
204
203
 
@@ -216,19 +215,19 @@ Heat pump stages are similarly controlled via thermostat ranges:
216
215
  - Display Unit
217
216
  - Description
218
217
  * - ``hpUpperOnTempSetting``
219
- - add_20
218
+ - HalfCelsiusToF
220
219
  - °F
221
220
  - **Upper heat pump ON**. Upper tank falls below this to activate heat pump for upper tank heating.
222
221
  * - ``hpUpperOffTempSetting``
223
- - add_20
222
+ - HalfCelsiusToF
224
223
  - °F
225
224
  - **Upper heat pump OFF**. Upper tank rises above this to stop upper tank heat pump operation.
226
225
  * - ``hpLowerOnTempSetting``
227
- - add_20
226
+ - HalfCelsiusToF
228
227
  - °F
229
228
  - **Lower heat pump ON**. Lower tank falls below this to activate heat pump for lower tank heating.
230
229
  * - ``hpLowerOffTempSetting``
231
- - add_20
230
+ - HalfCelsiusToF
232
231
  - °F
233
232
  - **Lower heat pump OFF**. Lower tank rises above this to stop lower tank heat pump operation.
234
233
  * - ``hpUpperOnDiffTempSetting``
@@ -264,15 +263,15 @@ Freeze Protection Temperatures
264
263
  - Boolean
265
264
  - **Freeze protection enabled flag**. When True, triggers anti-freeze operation below threshold.
266
265
  * - ``freezeProtectionTemperature``
267
- - add_20
266
+ - HalfCelsiusToF
268
267
  - °F
269
268
  - **Freeze protection temperature setpoint**. Range: 43-50°F (6-10°C). Default: 43°F (6°C). When tank temperature drops below this, electric heating activates automatically to prevent freezing.
270
269
  * - ``freezeProtectionTempMin``
271
- - add_20
270
+ - HalfCelsiusToF
272
271
  - °F
273
272
  - **Minimum freeze protection temperature limit** (lower boundary). Fixed at 43°F (6°C).
274
273
  * - ``freezeProtectionTempMax``
275
- - add_20
274
+ - HalfCelsiusToF
276
275
  - °F
277
276
  - **Maximum freeze protection temperature limit** (upper boundary). Fixed at 50°F (10°C).
278
277
 
@@ -290,18 +289,19 @@ For systems with recirculation pumps (optional feature):
290
289
  - Display Unit
291
290
  - Description
292
291
  * - ``recircTemperature``
293
- - add_20
292
+ - HalfCelsiusToF
294
293
  - °F
295
294
  - **Recirculation loop current temperature**. Temperature of water being circulated back to tank.
296
295
  * - ``recircFaucetTemperature``
297
- - add_20
296
+ - HalfCelsiusToF
298
297
  - °F
299
298
  - **Recirculation faucet outlet temperature**. How hot water is at the furthest fixture during recirculation.
300
299
  * - ``recircTempSetting``
301
- - add_20
300
+ - HalfCelsiusToF
302
301
  - °F
303
302
  - **Recirculation target temperature**. What temperature to maintain in the recirculation line.
304
303
 
304
+
305
305
  Flow Rate Fields
306
306
  ----------------
307
307
 
@@ -613,8 +613,8 @@ Temperature Unit Notes
613
613
  * **Fahrenheit** conversions assume target display is °F as configured in the device
614
614
  * **Celsius calculations** can be derived by reversing the conversions:
615
615
 
616
- - From ``add_20`` fields: ``celsius = (fahrenheit - 20) * 5/9``
617
- - From ``decicelsius_to_f`` fields: ``celsius = (fahrenheit - 32) * 5/9``
616
+ - From ``HalfCelsiusToF`` fields: ``celsius = (fahrenheit - 32) * 5/9 * 2``
617
+ - From ``PentaCelsiusToF`` fields: ``celsius = (fahrenheit - 32) * 5/9 * 5``
618
618
  - From ``div_10`` fields: ``celsius = value_celsius / 10.0``
619
619
 
620
620
  * **Sensor Accuracy**: Typically ±2°F for tank sensors, ±3°F for refrigerant sensors
@@ -77,12 +77,12 @@ This document lists the fields found in the ``status`` object of device status m
77
77
  - integer
78
78
  - °F
79
79
  - Current Domestic Hot Water (DHW) outlet temperature.
80
- - ``raw + 20``
80
+ - HalfCelsiusToF
81
81
  * - ``dhwTemperatureSetting``
82
82
  - integer
83
83
  - °F
84
84
  - Target DHW temperature setting. Range: 95°F (35°C) to 150°F (65.5°C). Default: 120°F (49°C).
85
- - ``raw + 20``
85
+ - HalfCelsiusToF
86
86
  * - ``programReservationUse``
87
87
  - bool
88
88
  - None
@@ -117,42 +117,42 @@ This document lists the fields found in the ``status`` object of device status m
117
117
  - integer
118
118
  - °F
119
119
  - The target DHW temperature setting (same as dhwTemperatureSetting).
120
- - ``raw + 20``
120
+ - HalfCelsiusToF
121
121
  * - ``tankUpperTemperature``
122
122
  - integer
123
123
  - °F
124
124
  - Temperature of the upper part of the tank.
125
- - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit)
125
+ - DeciCelsiusToF
126
126
  * - ``tankLowerTemperature``
127
127
  - integer
128
128
  - °F
129
129
  - Temperature of the lower part of the tank.
130
- - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit)
130
+ - DeciCelsiusToF
131
131
  * - ``dischargeTemperature``
132
132
  - integer
133
133
  - °F
134
134
  - Compressor discharge temperature - temperature of refrigerant leaving the compressor.
135
- - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit)
135
+ - DeciCelsiusToF
136
136
  * - ``suctionTemperature``
137
137
  - integer
138
138
  - °F
139
139
  - Compressor suction temperature - temperature of refrigerant entering the compressor.
140
- - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit)
140
+ - DeciCelsiusToF
141
141
  * - ``evaporatorTemperature``
142
142
  - integer
143
143
  - °F
144
144
  - Evaporator temperature - temperature where heat is absorbed from ambient air.
145
- - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit)
145
+ - DeciCelsiusToF
146
146
  * - ``ambientTemperature``
147
147
  - integer
148
148
  - °F
149
149
  - Ambient air temperature measured at the heat pump air intake.
150
- - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit)
150
+ - DeciCelsiusToF
151
151
  * - ``targetSuperHeat``
152
152
  - integer
153
153
  - °F
154
154
  - Target superheat value - the desired temperature difference ensuring complete refrigerant vaporization.
155
- - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit)
155
+ - DeciCelsiusToF
156
156
  * - ``compUse``
157
157
  - bool
158
158
  - None
@@ -212,7 +212,7 @@ This document lists the fields found in the ``status`` object of device status m
212
212
  - integer
213
213
  - °F
214
214
  - Freeze protection temperature setpoint. Range: 43-50°F (6-10°C), Default: 43°F. When tank temperature drops below this, electric heating activates automatically to prevent freezing.
215
- - ``raw + 20``
215
+ - HalfCelsiusToF
216
216
  * - ``antiLegionellaUse``
217
217
  - bool
218
218
  - None
@@ -287,7 +287,7 @@ This document lists the fields found in the ``status`` object of device status m
287
287
  - integer
288
288
  - °F
289
289
  - Second DHW temperature reading.
290
- - ``raw + 20``
290
+ - HalfCelsiusToF
291
291
  * - ``currentDhwFlowRate``
292
292
  - float
293
293
  - GPM
@@ -307,7 +307,7 @@ This document lists the fields found in the ``status`` object of device status m
307
307
  - integer
308
308
  - °F
309
309
  - Current superheat value - actual temperature difference between suction and evaporator temperatures.
310
- - ``(raw / 10) * 9/5 + 32`` (decicelsius to Fahrenheit)
310
+ - DeciCelsiusToF
311
311
  * - ``heatUpperUse``
312
312
  - bool
313
313
  - None
@@ -357,42 +357,42 @@ This document lists the fields found in the ``status`` object of device status m
357
357
  - integer
358
358
  - °F
359
359
  - Heat pump upper on temperature setting.
360
- - ``raw + 20``
360
+ - HalfCelsiusToF
361
361
  * - ``hpUpperOffTempSetting``
362
362
  - integer
363
363
  - °F
364
364
  - Heat pump upper off temperature setting.
365
- - ``raw + 20``
365
+ - HalfCelsiusToF
366
366
  * - ``hpLowerOnTempSetting``
367
367
  - integer
368
368
  - °F
369
369
  - Heat pump lower on temperature setting.
370
- - ``raw + 20``
370
+ - HalfCelsiusToF
371
371
  * - ``hpLowerOffTempSetting``
372
372
  - integer
373
373
  - °F
374
374
  - Heat pump lower off temperature setting.
375
- - ``raw + 20``
375
+ - HalfCelsiusToF
376
376
  * - ``heUpperOnTempSetting``
377
377
  - integer
378
378
  - °F
379
379
  - Heater element upper on temperature setting.
380
- - ``raw + 20``
380
+ - HalfCelsiusToF
381
381
  * - ``heUpperOffTempSetting``
382
382
  - integer
383
383
  - °F
384
384
  - Heater element upper off temperature setting.
385
- - ``raw + 20``
385
+ - HalfCelsiusToF
386
386
  * - ``heLowerOnTempSetting``
387
387
  - integer
388
388
  - °F
389
389
  - Heater element lower on temperature setting.
390
- - ``raw + 20``
390
+ - HalfCelsiusToF
391
391
  * - ``heLowerOffTempSetting``
392
392
  - integer
393
393
  - °F
394
394
  - Heater element lower off temperature setting.
395
- - ``raw + 20``
395
+ - HalfCelsiusToF
396
396
  * - ``hpUpperOnDiffTempSetting``
397
397
  - float
398
398
  - °F
@@ -437,7 +437,7 @@ This document lists the fields found in the ``status`` object of device status m
437
437
  - float
438
438
  - °F
439
439
  - Minimum heat pump operation temperature. Lowest tank temperature setpoint allowed in the current operating mode (95-113°F, default 95°F). When set, users can only set the target tank temperature at or above this threshold.
440
- - ``raw + 20``
440
+ - HalfCelsiusToF
441
441
  * - ``drOverrideStatus``
442
442
  - integer
443
443
  - None
@@ -730,7 +730,7 @@ Technical Notes
730
730
 
731
731
  * Tank temperature sensors operate within -4°F to 149°F (-20°C to 65°C)
732
732
  * Outside normal range, system may operate with reduced capacity using opposite heating element
733
- * All tank temperature readings use conversion formula: ``display_temp = raw + 20``
733
+
734
734
 
735
735
  **Heating Elements:**
736
736
 
@@ -293,8 +293,7 @@ Complete real-time device status with 100+ fields.
293
293
  * ``ambient_temperature`` (float) - Ambient air temperature
294
294
 
295
295
  .. note::
296
- Temperature display values are 20°F higher than message values.
297
- Display: 140°F = Message: 120°F
296
+ Temperature values from the device are automatically converted from their raw (scaled Celsius) representation to Fahrenheit or Celsius based on the device's settings. The library handles these conversions transparently.
298
297
 
299
298
  **Key Power/Energy Fields:**
300
299
 
@@ -684,18 +683,7 @@ Best Practices
684
683
  # Device supports energy monitoring
685
684
  await mqtt.request_energy_usage(device, year, months)
686
685
 
687
- 3. **Handle temperature conversions:**
688
-
689
- .. code-block:: python
690
-
691
- # Display temperature is 20°F higher than message value
692
- display_temp = 140
693
- message_value = display_temp - 20 # 120
694
-
695
- # Or use convenience method
696
- await mqtt.set_dhw_temperature_display(device, 140)
697
-
698
- 4. **Monitor operation state:**
686
+ 3. **Monitor operation state:**
699
687
 
700
688
  .. code-block:: python
701
689
 
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python3
2
+ """Extract release notes from CHANGELOG.rst for a specific version."""
3
+
4
+ import re
5
+ import sys
6
+
7
+
8
+ def extract_version_notes(changelog_path: str, version: str) -> str:
9
+ """
10
+ Extract the changelog section for a specific version.
11
+
12
+ Args:
13
+ changelog_path: Path to CHANGELOG.rst
14
+ version: Version string (e.g., "6.0.4")
15
+
16
+ Returns:
17
+ The changelog content for that version
18
+ """
19
+ with open(changelog_path, "r") as f:
20
+ content = f.read()
21
+
22
+ # Match the version header and capture everything until the next version
23
+ # Pattern: "Version X.Y.Z (DATE)" followed by "===" line
24
+ version_pattern = rf"Version {re.escape(version)} \([^)]+\)\n=+\n(.*?)(?=\nVersion \d+\.\d+\.\d+ \([^)]+\)\n=+|$)"
25
+
26
+ match = re.search(version_pattern, content, re.DOTALL)
27
+ if not match:
28
+ return f"Release {version}"
29
+
30
+ notes = match.group(1).strip()
31
+ return notes
32
+
33
+
34
+ if __name__ == "__main__":
35
+ if len(sys.argv) != 3:
36
+ print("Usage: extract_changelog.py <changelog_path> <version>")
37
+ sys.exit(1)
38
+
39
+ changelog_path = sys.argv[1]
40
+ version = sys.argv[2].lstrip("v") # Remove 'v' prefix if present
41
+
42
+ notes = extract_version_notes(changelog_path, version)
43
+ print(notes)
@@ -3,7 +3,6 @@
3
3
  import asyncio
4
4
  import json
5
5
  import logging
6
- from dataclasses import asdict
7
6
  from typing import Any, Optional
8
7
 
9
8
  from nwp500 import Device, DeviceFeature, DeviceStatus, NavienMqttClient
@@ -54,7 +53,9 @@ async def handle_status_request(mqtt: NavienMqttClient, device: Device) -> None:
54
53
  if not future.done():
55
54
  print(
56
55
  json.dumps(
57
- asdict(status), indent=2, default=_json_default_serializer
56
+ status.model_dump(),
57
+ indent=2,
58
+ default=_json_default_serializer,
58
59
  )
59
60
  )
60
61
  future.set_result(None)
@@ -126,7 +127,9 @@ async def handle_device_info_request(
126
127
  if not future.done():
127
128
  print(
128
129
  json.dumps(
129
- asdict(info), indent=2, default=_json_default_serializer
130
+ info.model_dump(),
131
+ indent=2,
132
+ default=_json_default_serializer,
130
133
  )
131
134
  )
132
135
  future.set_result(None)
@@ -225,7 +228,7 @@ async def handle_set_mode_request(
225
228
  status = responses[0]
226
229
  print(
227
230
  json.dumps(
228
- asdict(status),
231
+ status.model_dump(),
229
232
  indent=2,
230
233
  default=_json_default_serializer,
231
234
  )
@@ -301,7 +304,7 @@ async def handle_set_dhw_temp_request(
301
304
  status = responses[0]
302
305
  print(
303
306
  json.dumps(
304
- asdict(status),
307
+ status.model_dump(),
305
308
  indent=2,
306
309
  default=_json_default_serializer,
307
310
  )
@@ -601,7 +604,7 @@ async def handle_set_tou_enabled_request(
601
604
  status = responses[0]
602
605
  print(
603
606
  json.dumps(
604
- asdict(status),
607
+ status.model_dump(),
605
608
  indent=2,
606
609
  default=_json_default_serializer,
607
610
  )
@@ -3,7 +3,6 @@
3
3
  import csv
4
4
  import json
5
5
  import logging
6
- from dataclasses import asdict
7
6
  from datetime import datetime
8
7
  from enum import Enum
9
8
  from pathlib import Path
@@ -43,7 +42,7 @@ def write_status_to_csv(file_path: str, status: DeviceStatus) -> None:
43
42
  """
44
43
  try:
45
44
  # Convert the entire dataclass to a dictionary to capture all fields
46
- status_dict = asdict(status)
45
+ status_dict = status.model_dump()
47
46
 
48
47
  # Add a timestamp to the beginning of the data
49
48
  status_dict["timestamp"] = datetime.now().isoformat()