nwp500-python 6.0.4__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.4 → nwp500_python-6.0.5}/.github/workflows/release.yml +16 -1
  2. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/CHANGELOG.rst +30 -0
  3. {nwp500_python-6.0.4/src/nwp500_python.egg-info → nwp500_python-6.0.5}/PKG-INFO +1 -1
  4. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/protocol/data_conversions.rst +13 -13
  5. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/protocol/device_status.rst +8 -8
  6. nwp500_python-6.0.5/scripts/extract_changelog.py +43 -0
  7. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/models.py +13 -15
  8. {nwp500_python-6.0.4 → nwp500_python-6.0.5/src/nwp500_python.egg-info}/PKG-INFO +1 -1
  9. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/SOURCES.txt +1 -0
  10. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/tests/test_models.py +4 -4
  11. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/.agent/workflows/pre-completion-testing.md +0 -0
  12. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/.coveragerc +0 -0
  13. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/.github/copilot-instructions.md +0 -0
  14. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/.github/workflows/ci.yml +0 -0
  15. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/.gitignore +0 -0
  16. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/.pre-commit-config.yaml +0 -0
  17. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/.readthedocs.yml +0 -0
  18. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/AUTHORS.rst +0 -0
  19. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/CONTRIBUTING.rst +0 -0
  20. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/LICENSE.txt +0 -0
  21. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/Makefile +0 -0
  22. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/README.rst +0 -0
  23. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/RELEASE.md +0 -0
  24. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/Makefile +0 -0
  25. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/_static/.gitignore +0 -0
  26. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/api/nwp500.rst +0 -0
  27. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/authors.rst +0 -0
  28. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/changelog.rst +0 -0
  29. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/conf.py +0 -0
  30. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/configuration.rst +0 -0
  31. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/development/contributing.rst +0 -0
  32. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/development/history.rst +0 -0
  33. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/guides/advanced_features_explained.rst +0 -0
  34. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/guides/auto_recovery.rst +0 -0
  35. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/guides/command_queue.rst +0 -0
  36. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/guides/energy_monitoring.rst +0 -0
  37. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/guides/event_system.rst +0 -0
  38. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/guides/reservations.rst +0 -0
  39. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/guides/scheduling_features.rst +0 -0
  40. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/guides/time_of_use.rst +0 -0
  41. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/index.rst +0 -0
  42. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/installation.rst +0 -0
  43. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/license.rst +0 -0
  44. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/openapi.yaml +0 -0
  45. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/protocol/device_features.rst +0 -0
  46. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/protocol/error_codes.rst +0 -0
  47. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/protocol/firmware_tracking.rst +0 -0
  48. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/protocol/mqtt_protocol.rst +0 -0
  49. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/protocol/rest_api.rst +0 -0
  50. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/python_api/api_client.rst +0 -0
  51. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/python_api/auth_client.rst +0 -0
  52. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/python_api/cli.rst +0 -0
  53. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/python_api/constants.rst +0 -0
  54. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/python_api/events.rst +0 -0
  55. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/python_api/exceptions.rst +0 -0
  56. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/python_api/models.rst +0 -0
  57. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/python_api/mqtt_client.rst +0 -0
  58. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/quickstart.rst +0 -0
  59. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/docs/requirements.txt +0 -0
  60. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/.ruff.toml +0 -0
  61. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/README.md +0 -0
  62. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/anti_legionella_example.py +0 -0
  63. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/api_client_example.py +0 -0
  64. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/auth_constructor_example.py +0 -0
  65. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/authenticate.py +0 -0
  66. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/auto_recovery_example.py +0 -0
  67. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/combined_callbacks.py +0 -0
  68. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/command_queue_demo.py +0 -0
  69. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/device_feature_callback.py +0 -0
  70. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/device_status_callback.py +0 -0
  71. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/device_status_callback_debug.py +0 -0
  72. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/energy_usage_example.py +0 -0
  73. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/event_emitter_demo.py +0 -0
  74. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/exception_handling_example.py +0 -0
  75. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/improved_auth_pattern.py +0 -0
  76. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/mask.py +0 -0
  77. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/mqtt_client_example.py +0 -0
  78. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/periodic_device_info.py +0 -0
  79. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/periodic_requests.py +0 -0
  80. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/power_control_example.py +0 -0
  81. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/reconnection_demo.py +0 -0
  82. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/reservation_schedule_example.py +0 -0
  83. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/set_dhw_temperature_example.py +0 -0
  84. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/set_mode_example.py +0 -0
  85. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/simple_auto_recovery.py +0 -0
  86. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/simple_periodic_info.py +0 -0
  87. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/simple_periodic_status.py +0 -0
  88. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/test_api_client.py +0 -0
  89. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/test_mqtt_connection.py +0 -0
  90. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/test_mqtt_messaging.py +0 -0
  91. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/test_periodic_minimal.py +0 -0
  92. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/token_restoration_example.py +0 -0
  93. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/tou_openei_example.py +0 -0
  94. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/examples/tou_schedule_example.py +0 -0
  95. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/pyproject.toml +0 -0
  96. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/scripts/README.md +0 -0
  97. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/scripts/bump_version.py +0 -0
  98. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/scripts/format.py +0 -0
  99. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/scripts/lint.py +0 -0
  100. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/scripts/setup-dev.py +0 -0
  101. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/scripts/validate_version.py +0 -0
  102. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/setup.cfg +0 -0
  103. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/setup.py +0 -0
  104. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/__init__.py +0 -0
  105. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/api_client.py +0 -0
  106. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/auth.py +0 -0
  107. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/cli/__init__.py +0 -0
  108. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/cli/__main__.py +0 -0
  109. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/cli/commands.py +0 -0
  110. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/cli/monitoring.py +0 -0
  111. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/cli/output_formatters.py +0 -0
  112. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/cli/token_storage.py +0 -0
  113. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/config.py +0 -0
  114. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/constants.py +0 -0
  115. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/encoding.py +0 -0
  116. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/events.py +0 -0
  117. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/exceptions.py +0 -0
  118. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/mqtt_client.py +0 -0
  119. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/mqtt_command_queue.py +0 -0
  120. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/mqtt_connection.py +0 -0
  121. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/mqtt_device_control.py +0 -0
  122. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/mqtt_periodic.py +0 -0
  123. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/mqtt_reconnection.py +0 -0
  124. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/mqtt_subscriptions.py +0 -0
  125. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/mqtt_utils.py +0 -0
  126. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/py.typed +0 -0
  127. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500/utils.py +0 -0
  128. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  129. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  130. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  131. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/requires.txt +0 -0
  132. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/src/nwp500_python.egg-info/top_level.txt +0 -0
  133. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/tests/conftest.py +0 -0
  134. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/tests/test_api_helpers.py +0 -0
  135. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/tests/test_auth.py +0 -0
  136. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/tests/test_command_queue.py +0 -0
  137. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/tests/test_events.py +0 -0
  138. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/tests/test_exceptions.py +0 -0
  139. {nwp500_python-6.0.4 → nwp500_python-6.0.5}/tests/test_utils.py +0 -0
  140. {nwp500_python-6.0.4 → 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,36 @@
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
+
5
35
  Version 6.0.4 (2025-11-21)
6
36
  ==========================
7
37
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 6.0.4
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
@@ -24,11 +24,11 @@ The device uses several encoding schemes to minimize transmission overhead:
24
24
  - Purpose: Converts raw values, which are in half-degrees Celsius, to Fahrenheit.
25
25
  - Example: Raw 122 -> (122 / 2) * 9/5 + 32 = 141.8°F
26
26
 
27
- 2. **Scaled Celsius to Fahrenheit** (PentaCelsiusToF)
28
- - Applied to refrigerant and evaporator temperatures.
29
- - Formula: ``displayed_value = (raw_value / 5.0) * 9/5 + 32``
30
- - Purpose: Converts raw values, which are scaled by a factor of 5, to Fahrenheit.
31
- - Example: Raw 250 -> (250 / 5) * 9/5 + 32 = 122°F
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
32
 
33
33
  3. **Tenths Encoding** (div_10)
34
34
  - Applied to decimal precision values
@@ -97,11 +97,11 @@ Tank Temperature Sensors
97
97
  - Display Unit
98
98
  - Description
99
99
  * - ``tankUpperTemperature``
100
- - PentaCelsiusToF
100
+ - DeciCelsiusToF
101
101
  - °F
102
102
  - **Upper tank sensor temperature**. Indicates stratification - hot water at top for quick delivery. Typically hottest point in tank.
103
103
  * - ``tankLowerTemperature``
104
- - PentaCelsiusToF
104
+ - DeciCelsiusToF
105
105
  - °F
106
106
  - **Lower tank sensor temperature**. Indicates bulk tank temperature and heating progress. Typically cooler than upper sensor.
107
107
 
@@ -121,27 +121,27 @@ These temperatures monitor the heat pump refrigerant circuit health and performa
121
121
  - Display Unit
122
122
  - Description
123
123
  * - ``dischargeTemperature``
124
- - PentaCelsiusToF
124
+ - DeciCelsiusToF
125
125
  - °F
126
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.
127
127
  * - ``suctionTemperature``
128
- - PentaCelsiusToF
128
+ - DeciCelsiusToF
129
129
  - °F
130
130
  - **Compressor suction temperature**. Temperature of refrigerant entering the compressor. Typically 40-60°F. Affects superheat calculation.
131
131
  * - ``evaporatorTemperature``
132
- - PentaCelsiusToF
132
+ - DeciCelsiusToF
133
133
  - °F
134
134
  - **Evaporator coil temperature**. Where heat is extracted from ambient air. Typically 20-50°F. Lower outdoor air temperature reduces evaporator efficiency.
135
135
  * - ``ambientTemperature``
136
- - PentaCelsiusToF
136
+ - DeciCelsiusToF
137
137
  - °F
138
138
  - **Ambient air temperature** measured at heat pump inlet. Directly affects system performance. At freezing (32°F), heat pump efficiency drops significantly.
139
139
  * - ``targetSuperHeat``
140
- - PentaCelsiusToF
140
+ - DeciCelsiusToF
141
141
  - °F
142
142
  - **Target superheat setpoint**. Desired temperature difference between suction and evaporator ensuring complete refrigerant vaporization. Typically 10-20°F.
143
143
  * - ``currentSuperHeat``
144
- - PentaCelsiusToF
144
+ - DeciCelsiusToF
145
145
  - °F
146
146
  - **Measured superheat value**. Actual temperature difference. Deviation from target indicates EEV (Electronic Expansion Valve) control issues.
147
147
 
@@ -122,37 +122,37 @@ This document lists the fields found in the ``status`` object of device status m
122
122
  - integer
123
123
  - °F
124
124
  - Temperature of the upper part of the tank.
125
- - PentaCelsiusToF
125
+ - DeciCelsiusToF
126
126
  * - ``tankLowerTemperature``
127
127
  - integer
128
128
  - °F
129
129
  - Temperature of the lower part of the tank.
130
- - PentaCelsiusToF
130
+ - DeciCelsiusToF
131
131
  * - ``dischargeTemperature``
132
132
  - integer
133
133
  - °F
134
134
  - Compressor discharge temperature - temperature of refrigerant leaving the compressor.
135
- - PentaCelsiusToF
135
+ - DeciCelsiusToF
136
136
  * - ``suctionTemperature``
137
137
  - integer
138
138
  - °F
139
139
  - Compressor suction temperature - temperature of refrigerant entering the compressor.
140
- - PentaCelsiusToF
140
+ - DeciCelsiusToF
141
141
  * - ``evaporatorTemperature``
142
142
  - integer
143
143
  - °F
144
144
  - Evaporator temperature - temperature where heat is absorbed from ambient air.
145
- - PentaCelsiusToF
145
+ - DeciCelsiusToF
146
146
  * - ``ambientTemperature``
147
147
  - integer
148
148
  - °F
149
149
  - Ambient air temperature measured at the heat pump air intake.
150
- - PentaCelsiusToF
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
- - PentaCelsiusToF
155
+ - DeciCelsiusToF
156
156
  * - ``compUse``
157
157
  - bool
158
158
  - None
@@ -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
- - PentaCelsiusToF
310
+ - DeciCelsiusToF
311
311
  * - ``heatUpperUse``
312
312
  - bool
313
313
  - None
@@ -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)
@@ -41,10 +41,10 @@ def _half_celsius_to_fahrenheit(v: Any) -> float:
41
41
  return float(v)
42
42
 
43
43
 
44
- def _penta_celsius_to_fahrenheit(v: Any) -> float:
45
- """Convert value scaled by 5 to Fahrenheit."""
44
+ def _deci_celsius_to_fahrenheit(v: Any) -> float:
45
+ """Convert decicelsius (tenths of Celsius) to Fahrenheit."""
46
46
  if isinstance(v, (int, float)):
47
- celsius = float(v) / 5.0
47
+ celsius = float(v) / 10.0
48
48
  return (celsius * 9 / 5) + 32
49
49
  return float(v)
50
50
 
@@ -53,9 +53,7 @@ def _penta_celsius_to_fahrenheit(v: Any) -> float:
53
53
  DeviceBool = Annotated[bool, BeforeValidator(_device_bool_validator)]
54
54
  Div10 = Annotated[float, BeforeValidator(_div_10_validator)]
55
55
  HalfCelsiusToF = Annotated[float, BeforeValidator(_half_celsius_to_fahrenheit)]
56
- PentaCelsiusToF = Annotated[
57
- float, BeforeValidator(_penta_celsius_to_fahrenheit)
58
- ]
56
+ DeciCelsiusToF = Annotated[float, BeforeValidator(_deci_celsius_to_fahrenheit)]
59
57
 
60
58
 
61
59
  class NavienBaseModel(BaseModel):
@@ -289,15 +287,15 @@ class DeviceStatus(NavienBaseModel):
289
287
  he_lower_off_diff_temp_setting: Div10
290
288
  recirc_dhw_flow_rate: Div10
291
289
 
292
- # Temperature fields with 1/5 scaling
293
- tank_upper_temperature: PentaCelsiusToF
294
- tank_lower_temperature: PentaCelsiusToF
295
- discharge_temperature: PentaCelsiusToF
296
- suction_temperature: PentaCelsiusToF
297
- evaporator_temperature: PentaCelsiusToF
298
- ambient_temperature: PentaCelsiusToF
299
- target_super_heat: PentaCelsiusToF
300
- current_super_heat: PentaCelsiusToF
290
+ # Temperature fields with decicelsius (÷10) to Fahrenheit conversion
291
+ tank_upper_temperature: DeciCelsiusToF
292
+ tank_lower_temperature: DeciCelsiusToF
293
+ discharge_temperature: DeciCelsiusToF
294
+ suction_temperature: DeciCelsiusToF
295
+ evaporator_temperature: DeciCelsiusToF
296
+ ambient_temperature: DeciCelsiusToF
297
+ target_super_heat: DeciCelsiusToF
298
+ current_super_heat: DeciCelsiusToF
301
299
 
302
300
  # Enum fields
303
301
  operation_mode: CurrentOperationMode = Field(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 6.0.4
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
@@ -92,6 +92,7 @@ examples/tou_openei_example.py
92
92
  examples/tou_schedule_example.py
93
93
  scripts/README.md
94
94
  scripts/bump_version.py
95
+ scripts/extract_changelog.py
95
96
  scripts/format.py
96
97
  scripts/lint.py
97
98
  scripts/setup-dev.py
@@ -118,11 +118,11 @@ def test_device_status_half_celsius_to_fahrenheit(default_status_data):
118
118
  assert status.dhw_temperature == pytest.approx(141.8)
119
119
 
120
120
 
121
- def test_device_status_penta_celsius_to_fahrenheit(default_status_data):
122
- """Test PentaCelsiusToF conversion."""
123
- default_status_data["tankUpperTemperature"] = 250
121
+ def test_device_status_deci_celsius_to_fahrenheit(default_status_data):
122
+ """Test DeciCelsiusToF conversion."""
123
+ default_status_data["tankUpperTemperature"] = 489
124
124
  status = DeviceStatus.model_validate(default_status_data)
125
- assert status.tank_upper_temperature == pytest.approx(122.0)
125
+ assert status.tank_upper_temperature == pytest.approx(120.0, abs=0.1)
126
126
 
127
127
 
128
128
  def test_device_status_div10(default_status_data):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes