nwp500-python 7.3.2__tar.gz → 7.3.4__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 (177) hide show
  1. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/CHANGELOG.rst +108 -0
  2. {nwp500_python-7.3.2/src/nwp500_python.egg-info → nwp500_python-7.3.4}/PKG-INFO +1 -1
  3. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/api/nwp500.rst +8 -0
  4. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/reservations.rst +32 -31
  5. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/protocol/data_conversions.rst +25 -17
  6. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/protocol/mqtt_protocol.rst +1 -1
  7. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/python_api/device_control.rst +8 -7
  8. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/python_api/mqtt_client.rst +12 -10
  9. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/combined_callbacks.py +4 -2
  10. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/demand_response.py +2 -1
  11. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/device_capabilities.py +3 -2
  12. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/device_status_debug.py +2 -1
  13. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/power_control.py +2 -1
  14. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/reconnection_demo.py +2 -1
  15. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/reservation_schedule.py +12 -6
  16. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/simple_auto_recovery.py +2 -1
  17. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/water_reservation.py +2 -1
  18. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/beginner/03_get_status.py +2 -1
  19. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/beginner/04_set_temperature.py +14 -9
  20. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/advanced_auth_patterns.py +4 -2
  21. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/device_status_callback.py +14 -8
  22. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/event_driven_control.py +8 -5
  23. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/improved_auth.py +3 -2
  24. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/mqtt_realtime_monitoring.py +4 -3
  25. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/periodic_requests.py +2 -1
  26. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/set_mode.py +2 -1
  27. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/vacation_mode.py +2 -1
  28. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/testing/periodic_device_info.py +2 -1
  29. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/testing/test_periodic_minimal.py +2 -1
  30. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/__init__.py +4 -0
  31. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/cli/handlers.py +3 -1
  32. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/cli/monitoring.py +4 -1
  33. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/cli/output_formatters.py +56 -0
  34. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/converters.py +39 -1
  35. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/encoding.py +23 -11
  36. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/events.py +4 -1
  37. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/models.py +69 -8
  38. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/control.py +28 -5
  39. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/subscriptions.py +4 -3
  40. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt_events.py +14 -8
  41. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/temperature.py +74 -0
  42. {nwp500_python-7.3.2 → nwp500_python-7.3.4/src/nwp500_python.egg-info}/PKG-INFO +1 -1
  43. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_api_helpers.py +4 -4
  44. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/.agent/workflows/pre-completion-testing.md +0 -0
  45. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/.coveragerc +0 -0
  46. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/.github/copilot-instructions.md +0 -0
  47. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/.github/workflows/ci.yml +0 -0
  48. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/.github/workflows/release.yml +0 -0
  49. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/.gitignore +0 -0
  50. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/.pre-commit-config.yaml +0 -0
  51. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/.readthedocs.yml +0 -0
  52. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/AUTHORS.rst +0 -0
  53. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/CONTRIBUTING.rst +0 -0
  54. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/LICENSE.txt +0 -0
  55. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/Makefile +0 -0
  56. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/README.rst +0 -0
  57. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/RELEASE.md +0 -0
  58. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/Makefile +0 -0
  59. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/_static/.gitignore +0 -0
  60. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/authors.rst +0 -0
  61. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/changelog.rst +0 -0
  62. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/conf.py +0 -0
  63. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/configuration.rst +0 -0
  64. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/development/contributing.rst +0 -0
  65. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/development/history.rst +0 -0
  66. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/enumerations.rst +0 -0
  67. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/advanced_features_explained.rst +0 -0
  68. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/authentication.rst +0 -0
  69. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/auto_recovery.rst +0 -0
  70. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/command_queue.rst +0 -0
  71. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/energy_monitoring.rst +0 -0
  72. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/event_system.rst +0 -0
  73. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/home_assistant_integration.rst +0 -0
  74. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/mqtt_diagnostics.rst +0 -0
  75. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/scheduling_features.rst +0 -0
  76. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/time_of_use.rst +0 -0
  77. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/guides/unit_conversion.rst +0 -0
  78. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/index.rst +0 -0
  79. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/installation.rst +0 -0
  80. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/license.rst +0 -0
  81. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/openapi.yaml +0 -0
  82. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/protocol/device_features.rst +0 -0
  83. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/protocol/device_status.rst +0 -0
  84. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/protocol/error_codes.rst +0 -0
  85. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/protocol/quick_reference.rst +0 -0
  86. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/protocol/rest_api.rst +0 -0
  87. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/python_api/api_client.rst +0 -0
  88. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/python_api/auth_client.rst +0 -0
  89. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/python_api/cli.rst +0 -0
  90. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/python_api/events.rst +0 -0
  91. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/python_api/exceptions.rst +0 -0
  92. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/python_api/models.rst +0 -0
  93. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/quickstart.rst +0 -0
  94. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/docs/requirements.txt +0 -0
  95. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/.ruff.toml +0 -0
  96. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/README.md +0 -0
  97. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/air_filter_reset.py +0 -0
  98. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/anti_legionella.py +0 -0
  99. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/auto_recovery.py +0 -0
  100. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/energy_analytics.py +0 -0
  101. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/error_code_demo.py +0 -0
  102. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/mqtt_diagnostics.py +0 -0
  103. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/recirculation_control.py +0 -0
  104. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/token_restoration.py +0 -0
  105. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/tou_openei.py +0 -0
  106. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/advanced/tou_schedule.py +0 -0
  107. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/beginner/01_authentication.py +0 -0
  108. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/beginner/02_list_devices.py +0 -0
  109. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/command_queue.py +0 -0
  110. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/error_handling.py +0 -0
  111. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/intermediate/legacy_auth_constructor.py +0 -0
  112. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/mask.py +0 -0
  113. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/testing/simple_periodic_info.py +0 -0
  114. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/testing/test_api_client.py +0 -0
  115. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/testing/test_mqtt_connection.py +0 -0
  116. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/examples/testing/test_mqtt_messaging.py +0 -0
  117. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/pyproject.toml +0 -0
  118. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/scripts/README.md +0 -0
  119. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/scripts/bump_version.py +0 -0
  120. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/scripts/diagnose_mqtt_connection.py +0 -0
  121. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/scripts/extract_changelog.py +0 -0
  122. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/scripts/format.py +0 -0
  123. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/scripts/lint.py +0 -0
  124. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/scripts/setup-dev.py +0 -0
  125. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/scripts/validate_version.py +0 -0
  126. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/setup.cfg +0 -0
  127. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/setup.py +0 -0
  128. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/api_client.py +0 -0
  129. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/auth.py +0 -0
  130. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/cli/__init__.py +0 -0
  131. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/cli/__main__.py +0 -0
  132. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/cli/commands.py +0 -0
  133. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/cli/rich_output.py +0 -0
  134. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/cli/token_storage.py +0 -0
  135. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/command_decorators.py +0 -0
  136. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/config.py +0 -0
  137. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/device_capabilities.py +0 -0
  138. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/device_info_cache.py +0 -0
  139. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/enums.py +0 -0
  140. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/exceptions.py +0 -0
  141. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/factory.py +0 -0
  142. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/field_factory.py +0 -0
  143. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/__init__.py +0 -0
  144. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/client.py +0 -0
  145. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/command_queue.py +0 -0
  146. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/connection.py +0 -0
  147. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/diagnostics.py +0 -0
  148. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/periodic.py +0 -0
  149. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/reconnection.py +0 -0
  150. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/mqtt/utils.py +0 -0
  151. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/py.typed +0 -0
  152. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/topic_builder.py +0 -0
  153. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/unit_system.py +0 -0
  154. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500/utils.py +0 -0
  155. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500_python.egg-info/SOURCES.txt +0 -0
  156. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  157. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  158. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  159. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500_python.egg-info/requires.txt +0 -0
  160. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/src/nwp500_python.egg-info/top_level.txt +0 -0
  161. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/conftest.py +0 -0
  162. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_auth.py +0 -0
  163. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_cli_basic.py +0 -0
  164. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_cli_commands.py +0 -0
  165. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_command_decorators.py +0 -0
  166. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_command_queue.py +0 -0
  167. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_device_capabilities.py +0 -0
  168. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_device_info_cache.py +0 -0
  169. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_events.py +0 -0
  170. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_exceptions.py +0 -0
  171. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_model_converters.py +0 -0
  172. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_models.py +0 -0
  173. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_mqtt_client_init.py +0 -0
  174. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_temperature_converters.py +0 -0
  175. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_unit_switching.py +0 -0
  176. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tests/test_utils.py +0 -0
  177. {nwp500_python-7.3.2 → nwp500_python-7.3.4}/tox.ini +0 -0
@@ -2,6 +2,114 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 7.3.4 (2026-01-27)
6
+ ==========================
7
+
8
+ Fixed
9
+ -----
10
+ - **Temperature Delta Conversions**: Fixed incorrect Fahrenheit conversion for differential temperature settings (heat pump and heater element on/off thresholds)
11
+
12
+ - Created new ``DeciCelsiusDelta`` class for temperature deltas that apply scale factor (9/5) but NOT the +32 offset
13
+ - Heat pump and heater element differential settings now use ``DeciCelsiusDelta`` instead of ``DeciCelsius``
14
+ - ``hp_upper_on_diff_temp_setting``, ``hp_lower_on_diff_temp_setting``, ``he_upper_on_diff_temp_setting``, ``he_lower_on_diff_temp_setting``, and related off settings now convert correctly to Fahrenheit
15
+ - Example: A device value of 5 (representing 0.5°C delta) now correctly converts to 0.9°F delta instead of 32.9°F
16
+
17
+ - **CLI Output**: Added display of heat pump and heater element differential temperature settings in device status output
18
+
19
+ Changed
20
+ -------
21
+ - **Internal API**: Added ``div_10_celsius_delta_to_preferred`` converter for temperature delta values in device models
22
+
23
+
24
+ Version 7.3.3 (2026-01-27)
25
+ ==========================
26
+
27
+ Breaking Changes
28
+ ----------------
29
+ - **Temperature Setpoint Limits**: Replaced hardcoded temperature limits with device-provided values
30
+
31
+ - ``set_dhw_temperature()`` now validates against device-specific ``dhw_temperature_min`` and ``dhw_temperature_max`` instead of hardcoded 95-150°F bounds
32
+ - ``build_reservation_entry()`` changed parameter name from ``temperature_f`` to ``temperature`` (unit-agnostic)
33
+ - Added optional ``temperature_min`` and ``temperature_max`` parameters to ``build_reservation_entry()`` for device-specific limit overrides
34
+ - Temperature parameters now accept values in the user's preferred unit (Celsius or Fahrenheit) based on global unit system context
35
+ - Fixes Home Assistant and other integrations that prefer Celsius unit display
36
+
37
+ **Migration guide:**
38
+
39
+ .. code-block:: python
40
+
41
+ # OLD (hardcoded 95-150°F)
42
+ await mqtt.control.set_dhw_temperature(device, temperature_f=140.0)
43
+ entry = build_reservation_entry(
44
+ enabled=True,
45
+ days=["Monday"],
46
+ hour=6,
47
+ minute=0,
48
+ mode_id=3,
49
+ temperature_f=140.0,
50
+ )
51
+
52
+ # NEW (device-provided limits, unit-aware)
53
+ # Temperature value automatically uses user's preferred unit
54
+ await mqtt.control.set_dhw_temperature(device, 140.0)
55
+
56
+ # Device features provide min/max in user's preferred unit
57
+ features = await device_info_cache.get(device.device_info.mac_address)
58
+ entry = build_reservation_entry(
59
+ enabled=True,
60
+ days=["Monday"],
61
+ hour=6,
62
+ minute=0,
63
+ mode_id=3,
64
+ temperature=140.0,
65
+ temperature_min=features.dhw_temperature_min,
66
+ temperature_max=features.dhw_temperature_max,
67
+ )
68
+
69
+ Added
70
+ -----
71
+ - **Reservation Temperature Conversion**: New ``reservation_param_to_preferred()`` utility function for unit-aware reservation display
72
+
73
+ - Converts device reservation parameters (half-degree Celsius) to user's preferred unit
74
+ - Respects global unit system context (metric/us_customary)
75
+ - Enables proper thermostat/reservation scheduling display in Home Assistant and other integrations
76
+ - Example usage:
77
+
78
+ .. code-block:: python
79
+
80
+ from nwp500 import reservation_param_to_preferred
81
+
82
+ # Display reservation temperature in user's preferred unit
83
+ param = 120 # Device raw value in half-Celsius
84
+ temp = reservation_param_to_preferred(param)
85
+ # Returns: 60.0 (Celsius) or 140.0 (Fahrenheit) based on unit context
86
+
87
+ - **Unit-Aware Temperature Conversion**: New ``preferred_to_half_celsius()`` utility function
88
+
89
+ - Converts temperature from user's preferred unit to half-degree Celsius for device commands
90
+ - Respects global unit system context (metric/us_customary)
91
+ - Replaces misleading ``fahrenheit_to_half_celsius()`` in unit-agnostic code paths
92
+ - Used internally by ``set_dhw_temperature()`` and ``build_reservation_entry()``
93
+
94
+ Changed
95
+ -------
96
+ - **Unit System Agnostic Display**: All logging and user-facing messages now respect global unit system context
97
+
98
+ - Temperature change logs dynamically show °C or °F based on user preference
99
+ - CLI monitoring and temperature setting messages use correct unit suffix
100
+ - Event listener documentation updated with unit-aware examples
101
+ - Reservation schedule examples now use ``reservation_param_to_preferred()`` for proper unit handling
102
+
103
+ Fixed
104
+ -----
105
+ - **Critical: Temperature Unit Bug in Set Operations**: Fixed incorrect temperature conversion when setting DHW temperature and reservations
106
+
107
+ - ``set_dhw_temperature()`` was calling ``fahrenheit_to_half_celsius()`` with unit-agnostic temperature parameter
108
+ - ``build_reservation_entry()`` had the same issue
109
+ - **Impact**: If user preferred Celsius, temperature would be interpreted as Fahrenheit, causing wrong setpoints
110
+ - **Fix**: Use new ``preferred_to_half_celsius()`` that respects unit system context
111
+ - This was a critical data correctness bug that would cause incorrect device behavior for Celsius users
112
+
5
113
  Version 7.3.2 (2026-01-25)
6
114
  ==========================
7
115
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 7.3.2
3
+ Version: 7.3.4
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
@@ -149,6 +149,14 @@ nwp500.topic\_builder module
149
149
  :show-inheritance:
150
150
  :undoc-members:
151
151
 
152
+ nwp500.unit\_system module
153
+ --------------------------
154
+
155
+ .. automodule:: nwp500.unit_system
156
+ :members:
157
+ :show-inheritance:
158
+ :undoc-members:
159
+
152
160
  nwp500.utils module
153
161
  -------------------
154
162
 
@@ -44,14 +44,14 @@ Here's a simple example that sets up a weekday morning reservation:
44
44
  device = await api.get_first_device()
45
45
 
46
46
  # Build reservation entry
47
- weekday_morning = NavienAPIClient.build_reservation_entry(
47
+ weekday_morning = build_reservation_entry(
48
48
  enabled=True,
49
49
  days=["Monday", "Tuesday", "Wednesday", "Thursday",
50
50
  "Friday"],
51
51
  hour=6,
52
52
  minute=30,
53
53
  mode_id=4, # High Demand
54
- temperature_f=140.0 # Temperature in Fahrenheit
54
+ temperature=140.0 # Temperature in user's preferred unit
55
55
  )
56
56
 
57
57
  # Send to device
@@ -149,8 +149,8 @@ Field Descriptions
149
149
  (e.g., ``98`` for 120°F) for consistency.
150
150
 
151
151
  **Note:** When using ``build_reservation_entry()``, you don't need to
152
- calculate the param value manually - just pass ``temperature_f`` in
153
- Fahrenheit and the conversion is handled automatically.
152
+ calculate the param value manually - just pass ``temperature`` in
153
+ your user's preferred unit and the conversion is handled automatically.
154
154
 
155
155
  Helper Functions
156
156
  ================
@@ -161,33 +161,33 @@ Building Reservation Entries
161
161
  -----------------------------
162
162
 
163
163
  Use ``build_reservation_entry()`` to create properly formatted entries.
164
- The function accepts temperature in Fahrenheit and handles the conversion
164
+ The function accepts temperature in your user's preferred unit and handles the conversion
165
165
  to the device's internal format automatically:
166
166
 
167
167
  .. code-block:: python
168
168
 
169
169
  from nwp500 import build_reservation_entry
170
170
 
171
- # Weekday morning - High Demand mode at 140°F
171
+ # Weekday morning - High Demand mode at 140 (°F or °C based on unit preference)
172
172
  entry = build_reservation_entry(
173
173
  enabled=True,
174
174
  days=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
175
175
  hour=6,
176
176
  minute=30,
177
177
  mode_id=4, # High Demand
178
- temperature_f=140.0 # Temperature in Fahrenheit
178
+ temperature=140.0 # Temperature in user's preferred unit
179
179
  )
180
180
  # Returns: {'enable': 1, 'week': 62, 'hour': 6, 'min': 30,
181
181
  # 'mode': 4, 'param': 120}
182
182
 
183
- # Weekend - Energy Saver mode at 120°F
183
+ # Weekend - Energy Saver mode at 120 (°F or °C)
184
184
  entry2 = build_reservation_entry(
185
185
  enabled=True,
186
186
  days=["Saturday", "Sunday"],
187
187
  hour=8,
188
188
  minute=0,
189
189
  mode_id=3, # Energy Saver
190
- temperature_f=120.0
190
+ temperature=120.0
191
191
  )
192
192
 
193
193
  # You can also use day indices (0=Sunday, 6=Saturday)
@@ -197,7 +197,7 @@ to the device's internal format automatically:
197
197
  hour=18,
198
198
  minute=0,
199
199
  mode_id=1, # Heat Pump Only
200
- temperature_f=130.0
200
+ temperature=130.0
201
201
  )
202
202
 
203
203
  Temperature Conversion Utility
@@ -269,7 +269,7 @@ Send a new reservation schedule to the device:
269
269
 
270
270
  # Build multiple reservation entries
271
271
  reservations = [
272
- # Weekday morning: High Demand at 140°F
272
+ # Weekday morning: High Demand at 140 (user's preferred unit)
273
273
  build_reservation_entry(
274
274
  enabled=True,
275
275
  days=["Monday", "Tuesday", "Wednesday", "Thursday",
@@ -277,9 +277,9 @@ Send a new reservation schedule to the device:
277
277
  hour=6,
278
278
  minute=30,
279
279
  mode_id=4,
280
- temperature_f=140.0
280
+ temperature=140.0
281
281
  ),
282
- # Weekday evening: Energy Saver at 130°F
282
+ # Weekday evening: Energy Saver at 130 (user's preferred unit)
283
283
  build_reservation_entry(
284
284
  enabled=True,
285
285
  days=["Monday", "Tuesday", "Wednesday", "Thursday",
@@ -287,16 +287,16 @@ Send a new reservation schedule to the device:
287
287
  hour=18,
288
288
  minute=0,
289
289
  mode_id=3,
290
- temperature_f=130.0
290
+ temperature=130.0
291
291
  ),
292
- # Weekend: Heat Pump Only at 120°F
292
+ # Weekend: Heat Pump Only at 120 (user's preferred unit)
293
293
  build_reservation_entry(
294
294
  enabled=True,
295
295
  days=["Saturday", "Sunday"],
296
296
  hour=8,
297
297
  minute=0,
298
298
  mode_id=1,
299
- temperature_f=120.0
299
+ temperature=120.0
300
300
  ),
301
301
  ]
302
302
 
@@ -439,7 +439,7 @@ Different settings for work days and weekends:
439
439
  hour=5,
440
440
  minute=30,
441
441
  mode_id=4, # High Demand
442
- temperature_f=140.0
442
+ temperature=140.0
443
443
  ),
444
444
  # Weekend morning: later start, energy saver
445
445
  build_reservation_entry(
@@ -448,7 +448,7 @@ Different settings for work days and weekends:
448
448
  hour=8,
449
449
  minute=0,
450
450
  mode_id=3, # Energy Saver
451
- temperature_f=130.0
451
+ temperature=130.0
452
452
  ),
453
453
  ]
454
454
 
@@ -467,7 +467,7 @@ Minimize energy use during peak hours:
467
467
  hour=6,
468
468
  minute=0,
469
469
  mode_id=4,
470
- temperature_f=140.0
470
+ temperature=140.0
471
471
  ),
472
472
  # Day: 9:00 AM - Switch to Energy Saver
473
473
  build_reservation_entry(
@@ -476,7 +476,7 @@ Minimize energy use during peak hours:
476
476
  hour=9,
477
477
  minute=0,
478
478
  mode_id=3,
479
- temperature_f=120.0
479
+ temperature=120.0
480
480
  ),
481
481
  # Evening: 5:00 PM - Heat Pump Only (before peak pricing)
482
482
  build_reservation_entry(
@@ -485,7 +485,7 @@ Minimize energy use during peak hours:
485
485
  hour=17,
486
486
  minute=0,
487
487
  mode_id=1,
488
- temperature_f=130.0
488
+ temperature=130.0
489
489
  ),
490
490
  # Night: 10:00 PM - Back to Energy Saver
491
491
  build_reservation_entry(
@@ -494,7 +494,7 @@ Minimize energy use during peak hours:
494
494
  hour=22,
495
495
  minute=0,
496
496
  mode_id=3,
497
- temperature_f=120.0
497
+ temperature=120.0
498
498
  ),
499
499
  ]
500
500
 
@@ -512,7 +512,7 @@ Automatically enable vacation mode during a trip:
512
512
  hour=20,
513
513
  minute=0,
514
514
  mode_id=5, # Vacation Mode
515
- temperature_f=120.0 # Temperature doesn't matter for vacation mode
515
+ temperature=120.0 # Temperature doesn't matter for vacation mode
516
516
  )
517
517
 
518
518
  # Return to normal operation when you get back
@@ -522,7 +522,7 @@ Automatically enable vacation mode during a trip:
522
522
  hour=14,
523
523
  minute=0,
524
524
  mode_id=3, # Energy Saver
525
- temperature_f=130.0
525
+ temperature=130.0
526
526
  )
527
527
 
528
528
  reservations = [start_vacation, end_vacation]
@@ -533,11 +533,12 @@ Important Notes
533
533
  Temperature Conversion
534
534
  -----------------------
535
535
 
536
- When using ``build_reservation_entry()``, pass temperatures in Fahrenheit
537
- using the ``temperature_f`` parameter. The function automatically converts
538
- to the device's internal format (half-degrees Celsius).
536
+ When using ``build_reservation_entry()``, pass temperatures in your user's
537
+ preferred unit (Celsius or Fahrenheit) using the ``temperature`` parameter.
538
+ The function automatically converts to the device's internal format
539
+ (half-degrees Celsius).
539
540
 
540
- The valid temperature range is 95°F to 150°F.
541
+ The valid temperature range is 35°C to 65.5°C (95°F to 150°F).
541
542
 
542
543
  For reading reservation responses from the device, the ``param`` field
543
544
  contains the raw half-degrees Celsius value. Convert to Fahrenheit with:
@@ -619,7 +620,7 @@ Full working example with error handling and response monitoring:
619
620
  hour=6,
620
621
  minute=30,
621
622
  mode_id=4, # High Demand
622
- temperature_f=140.0
623
+ temperature=140.0
623
624
  ),
624
625
  # Weekday day
625
626
  build_reservation_entry(
@@ -629,7 +630,7 @@ Full working example with error handling and response monitoring:
629
630
  hour=9,
630
631
  minute=0,
631
632
  mode_id=3, # Energy Saver
632
- temperature_f=120.0
633
+ temperature=120.0
633
634
  ),
634
635
  # Weekend morning
635
636
  build_reservation_entry(
@@ -638,7 +639,7 @@ Full working example with error handling and response monitoring:
638
639
  hour=8,
639
640
  minute=0,
640
641
  mode_id=3, # Energy Saver
641
- temperature_f=130.0
642
+ temperature=130.0
642
643
  ),
643
644
  ]
644
645
 
@@ -34,9 +34,17 @@ The device uses several encoding schemes to minimize transmission overhead:
34
34
  - Applied to decimal precision values
35
35
  - Formula: ``displayed_value = raw_value / 10.0``
36
36
  - Purpose: Preserve decimal precision in integer storage
37
- - Common for flow rates and differential temperatures
37
+ - Common for flow rates
38
38
  - Example: Raw 125 → 12.5 GPM
39
39
 
40
+ 3.5. **Temperature Delta (Decicelsius)** (div_10_celsius_delta)
41
+ - Applied to differential temperature settings (heat pump and element hysteresis)
42
+ - Formula: ``displayed_value_celsius = raw_value / 10.0``, ``displayed_value_fahrenheit = (raw_value / 10.0) * 9/5`` (NO +32 offset)
43
+ - Purpose: Represents temperature DIFFERENCES/DELTAS, not absolute temperatures
44
+ - Key difference from absolute temperature conversion: No +32 offset applied when converting to Fahrenheit
45
+ - Example: Raw 5 → 0.5°C delta → 0.9°F delta (NOT 32.9°F)
46
+ - Used for: Heat pump on/off differential, heating element on/off differential
47
+
40
48
  4. **Boolean Encoding** (device_bool)
41
49
  - Applied to all status flags
42
50
  - Formula: ``displayed_value = (raw_value == 2)``
@@ -181,21 +189,21 @@ Electric heating elements are controlled via thermostat ranges. Two sensors (upp
181
189
  - °F
182
190
  - **Lower element OFF threshold**. Lower tank temp rises above this to deactivate lower element.
183
191
  * - ``heUpperOnDiffTempSetting``
184
- - div_10
192
+ - div_10_celsius_delta
185
193
  - °F
186
- - **Upper element differential** (ON-OFF difference). Hysteresis width to prevent rapid cycling. Typically 2-5°F.
194
+ - **Upper element differential** (ON-OFF hysteresis width). Temperature delta to prevent rapid cycling. Typically 2-5°F. This is a DELTA value, not an absolute temperature (0 delta = 0°F difference).
187
195
  * - ``heUpperOffDiffTempSetting``
188
- - div_10
196
+ - div_10_celsius_delta
189
197
  - °F
190
- - **Upper element differential** variation (advanced tuning). May vary based on mode.
198
+ - **Upper element OFF differential** (advanced tuning). May vary based on mode. DELTA value.
191
199
  * - ``heLowerOnDiffTempSetting``
192
- - div_10
200
+ - div_10_celsius_delta
193
201
  - °F
194
- - **Lower element differential** (ON-OFF difference).
202
+ - **Lower element differential** (ON-OFF hysteresis width). DELTA value.
195
203
  * - ``heLowerOffDiffTempSetting``
196
- - div_10
204
+ - div_10_celsius_delta
197
205
  - °F
198
- - **Lower element differential** variation.
206
+ - **Lower element OFF differential** variation. DELTA value.
199
207
  * - ``heatMinOpTemperature``
200
208
  - HalfCelsiusToF
201
209
  - °F
@@ -231,21 +239,21 @@ Heat pump stages are similarly controlled via thermostat ranges:
231
239
  - °F
232
240
  - **Lower heat pump OFF**. Lower tank rises above this to stop lower tank heat pump operation.
233
241
  * - ``hpUpperOnDiffTempSetting``
234
- - div_10
242
+ - div_10_celsius_delta
235
243
  - °F
236
- - **Heat pump upper differential** (ON-OFF hysteresis). Prevents rapid cycling.
244
+ - **Heat pump upper differential** (ON-OFF hysteresis). Temperature delta to prevent rapid cycling. DELTA value.
237
245
  * - ``hpUpperOffDiffTempSetting``
238
- - div_10
246
+ - div_10_celsius_delta
239
247
  - °F
240
- - **Heat pump upper differential** variation.
248
+ - **Heat pump upper OFF differential** variation. DELTA value.
241
249
  * - ``hpLowerOnDiffTempSetting``
242
- - div_10
250
+ - div_10_celsius_delta
243
251
  - °F
244
- - **Heat pump lower differential**.
252
+ - **Heat pump lower ON differential**. DELTA value.
245
253
  * - ``hpLowerOffDiffTempSetting``
246
- - div_10
254
+ - div_10_celsius_delta
247
255
  - °F
248
- - **Heat pump lower differential** variation.
256
+ - **Heat pump lower OFF differential** variation. DELTA value.
249
257
 
250
258
  Freeze Protection Temperatures
251
259
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -422,7 +422,7 @@ DHW Temperature
422
422
  Temperature values are encoded in **half-degrees Celsius**.
423
423
  Use formula: ``fahrenheit = (param / 2.0) * 9/5 + 32``
424
424
  For 140°F, send ``param=120`` (which is 60°C × 2).
425
- Valid range: 95-150°F (70-150 raw value).
425
+ Valid range: Device-specific (see device features for ``dhw_temperature_min`` and ``dhw_temperature_max``).
426
426
 
427
427
  Anti-Legionella
428
428
  ---------------
@@ -220,7 +220,7 @@ Temperature Control
220
220
  set_dhw_temperature()
221
221
  ^^^^^^^^^^^^^^^^^^^^^
222
222
 
223
- .. py:method:: set_dhw_temperature(device, temperature_f)
223
+ .. py:method:: set_dhw_temperature(device, temperature)
224
224
 
225
225
  Set DHW target temperature.
226
226
 
@@ -228,24 +228,25 @@ set_dhw_temperature()
228
228
 
229
229
  :param device: Device object
230
230
  :type device: Device
231
- :param temperature_f: Target temperature in Fahrenheit (95-150°F)
232
- :type temperature_f: float
231
+ :param temperature: Target temperature in user's preferred unit (Celsius or Fahrenheit)
232
+ :type temperature: float
233
233
  :return: Publish packet ID
234
234
  :rtype: int
235
- :raises RangeValidationError: If temperature is outside 95-150°F range
235
+ :raises RangeValidationError: If temperature is outside valid range
236
236
  :raises DeviceCapabilityError: If device doesn't support temperature control
237
237
 
238
238
  The temperature is automatically converted to the device's internal format
239
- (half-degrees Celsius).
239
+ (half-degrees Celsius). The valid range depends on the device's
240
+ temperature preference and configuration.
240
241
 
241
242
  **Example:**
242
243
 
243
244
  .. code-block:: python
244
245
 
245
- # Set temperature to 140°F
246
+ # Set temperature (interpreted in device's preferred unit)
246
247
  await mqtt.control.set_dhw_temperature(device, 140.0)
247
248
 
248
- # Common temperatures
249
+ # Common temperatures (device-dependent units)
249
250
  await mqtt.control.set_dhw_temperature(device, 120.0) # Standard
250
251
  await mqtt.control.set_dhw_temperature(device, 130.0) # Medium
251
252
  await mqtt.control.set_dhw_temperature(device, 140.0) # Hot
@@ -49,8 +49,9 @@ Basic Monitoring
49
49
 
50
50
  # Subscribe to status updates
51
51
  def on_status(status):
52
- print(f"Water Temp: {status.dhw_temperature}°F")
53
- print(f"Target: {status.dhw_temperature_setting}°F")
52
+ unit = status.get_field_unit('dhw_temperature')
53
+ print(f"Water Temp: {status.dhw_temperature}{unit}")
54
+ print(f"Target: {status.dhw_temperature_setting}{unit}")
54
55
  print(f"Power: {status.current_inst_power}W")
55
56
  print(f"Mode: {status.dhw_operation_setting.name}")
56
57
 
@@ -87,7 +88,7 @@ control method reference, capability checking, and advanced features.
87
88
  # Control operations (with automatic capability checking)
88
89
  await mqtt.control.set_power(device, power_on=True)
89
90
  await mqtt.control.set_dhw_mode(device, mode_id=3) # Energy Saver
90
- await mqtt.control.set_dhw_temperature(device, 140.0)
91
+ await mqtt.control.set_dhw_temperature(device, 140.0) # Temperature in user's preferred unit
91
92
 
92
93
  await mqtt.disconnect()
93
94
 
@@ -442,29 +443,30 @@ set_dhw_mode()
442
443
  set_dhw_temperature()
443
444
  ^^^^^^^^^^^^^^^^^^^^^
444
445
 
445
- .. py:method:: set_dhw_temperature(device, temperature_f)
446
+ .. py:method:: set_dhw_temperature(device, temperature)
446
447
 
447
448
  Set target DHW temperature.
448
449
 
449
450
  :param device: Device object
450
451
  :type device: Device
451
- :param temperature_f: Temperature in Fahrenheit (95-150°F)
452
- :type temperature_f: float
452
+ :param temperature: Temperature in user's preferred unit (Celsius or Fahrenheit)
453
+ :type temperature: float
453
454
  :return: Publish packet ID
454
455
  :rtype: int
455
- :raises RangeValidationError: If temperature is outside 95-150°F range
456
+ :raises RangeValidationError: If temperature is outside valid range
456
457
 
457
458
  The temperature is automatically converted to the device's internal
458
- format (half-degrees Celsius).
459
+ format (half-degrees Celsius). The actual valid range depends on the
460
+ device's temperature preference and configuration.
459
461
 
460
462
  **Example:**
461
463
 
462
464
  .. code-block:: python
463
465
 
464
- # Set temperature to 140°F
466
+ # Set temperature (value interpreted in device's preferred unit)
465
467
  await mqtt.control.set_dhw_temperature(device, 140.0)
466
468
 
467
- # Common temperatures
469
+ # Common temperatures (device-dependent units)
468
470
  await mqtt.control.set_dhw_temperature(device, 120.0) # Standard
469
471
  await mqtt.control.set_dhw_temperature(device, 130.0) # Medium
470
472
  await mqtt.control.set_dhw_temperature(device, 140.0) # Hot
@@ -86,9 +86,10 @@ async def main():
86
86
  # Callback for status updates
87
87
  def on_status(status: DeviceStatus):
88
88
  counts["status"] += 1
89
+ unit = status.get_field_unit("dhw_temperature")
89
90
  print(f"\n📊 Status Update #{counts['status']}")
90
91
  print(f" Mode: {status.operation_mode.name}")
91
- print(f" DHW Temp: {status.dhw_temperature:.1f}°F")
92
+ print(f" DHW Temp: {status.dhw_temperature:.1f}{unit}")
92
93
  print(f" DHW Charge: {status.dhw_charge_per:.1f}%")
93
94
  print(f" Compressor: {'On' if status.comp_use else 'Off'}")
94
95
 
@@ -98,8 +99,9 @@ async def main():
98
99
  print(f"\n📋 Feature Info #{counts['feature']}")
99
100
  print(f" Serial: {feature.controller_serial_number}")
100
101
  print(f" FW Version: {feature.controller_sw_version}")
102
+ unit_suffix = feature.get_field_unit("dhw_temperature_min")
101
103
  print(
102
- f" Temp Range: {feature.dhw_temperature_min}-{feature.dhw_temperature_max}°F"
104
+ f" Temp Range: {feature.dhw_temperature_min}-{feature.dhw_temperature_max}{unit_suffix}"
103
105
  )
104
106
  print(
105
107
  f" Heat Pump: {'Yes' if feature.heatpump_use == OnOffFlag.ON else 'No'}"
@@ -48,8 +48,9 @@ async def demand_response_example():
48
48
  nonlocal current_status
49
49
  current_status = status
50
50
  logger.info(f"Current operation mode: {status.operation_mode.name}")
51
+ unit = status.get_field_unit("dhw_target_temperature_setting")
51
52
  logger.info(
52
- f"Current DHW temperature setting: {status.dhw_target_temperature_setting}°F"
53
+ f"Current DHW temperature setting: {status.dhw_target_temperature_setting}{unit}"
53
54
  )
54
55
 
55
56
  await mqtt_client.subscribe_device_status(device, on_current_status)
@@ -138,13 +138,14 @@ async def main():
138
138
  )
139
139
 
140
140
  print("\nConfiguration:")
141
+ unit_suffix = feature.get_field_unit("dhw_temperature_min")
141
142
  print(f" Temperature Unit: {feature.temperature_type.name}")
142
143
  print(f" Temp Formula Type: {feature.temp_formula_type}")
143
144
  print(
144
- f" DHW Temp Range: {feature.dhw_temperature_min}°F - {feature.dhw_temperature_max}°F"
145
+ f" DHW Temp Range: {feature.dhw_temperature_min}{unit_suffix} - {feature.dhw_temperature_max}{unit_suffix}"
145
146
  )
146
147
  print(
147
- f" Freeze Prot Range: {feature.freeze_protection_temp_min}°F - {feature.freeze_protection_temp_max}°F"
148
+ f" Freeze Prot Range: {feature.freeze_protection_temp_min}{unit_suffix} - {feature.freeze_protection_temp_max}{unit_suffix}"
148
149
  )
149
150
 
150
151
  print("\nFeature Support:")
@@ -129,10 +129,11 @@ async def main():
129
129
  def on_device_status(status: DeviceStatus):
130
130
  """Parsed status callback."""
131
131
  message_count["status"] += 1
132
+ unit = status.get_field_unit("dhw_temperature")
132
133
  print(
133
134
  f"\n[SUCCESS] PARSED Status Update #{message_count['status']}"
134
135
  )
135
- print(f" DHW Temperature: {status.dhw_temperature:.1f}°F")
136
+ print(f" DHW Temperature: {status.dhw_temperature:.1f}{unit}")
136
137
  print(f" Operation Mode: {status.operation_mode.name}")
137
138
  print(f" Compressor: {status.comp_use}")
138
139
 
@@ -48,7 +48,8 @@ async def power_control_example():
48
48
  nonlocal current_status
49
49
  current_status = status
50
50
  logger.info(f"Current operation mode: {status.operation_mode.name}")
51
- logger.info(f"Current DHW temperature: {status.dhw_temperature}°F")
51
+ unit = status.get_field_unit("dhw_temperature")
52
+ logger.info(f"Current DHW temperature: {status.dhw_temperature}{unit}")
52
53
 
53
54
  await mqtt_client.subscribe_device_status(device, on_current_status)
54
55
  await mqtt_client.control.request_device_status(device)
@@ -89,8 +89,9 @@ async def main():
89
89
  def on_status(status):
90
90
  nonlocal status_count
91
91
  status_count += 1
92
+ unit = status.get_field_unit("dhw_temperature")
92
93
  print(f"\n📊 Status update #{status_count}:")
93
- print(f" Temperature: {status.dhw_temperature}°F")
94
+ print(f" Temperature: {status.dhw_temperature}{unit}")
94
95
  print(f" Connected: {mqtt_client.is_connected}")
95
96
  if mqtt_client.is_reconnecting:
96
97
  print(f" Reconnecting: attempt {mqtt_client.reconnect_attempts}...")