nwp500-python 7.2.3__tar.gz → 7.3.1__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 (180) hide show
  1. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/CHANGELOG.rst +206 -114
  2. {nwp500_python-7.2.3/src/nwp500_python.egg-info → nwp500_python-7.3.1}/PKG-INFO +1 -1
  3. nwp500_python-7.3.1/docs/guides/home_assistant_integration.rst +371 -0
  4. nwp500_python-7.3.1/docs/guides/unit_conversion.rst +663 -0
  5. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/index.rst +8 -6
  6. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/protocol/device_status.rst +20 -20
  7. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/__init__.py +9 -0
  8. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/api_client.py +15 -4
  9. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/auth.py +23 -10
  10. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/cli/__main__.py +27 -3
  11. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/cli/handlers.py +3 -2
  12. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/cli/output_formatters.py +49 -14
  13. nwp500_python-7.3.1/src/nwp500/converters.py +481 -0
  14. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/encoding.py +3 -0
  15. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/events.py +3 -2
  16. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/field_factory.py +27 -9
  17. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/models.py +252 -115
  18. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/client.py +24 -9
  19. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/connection.py +23 -9
  20. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/subscriptions.py +12 -2
  21. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/utils.py +2 -1
  22. nwp500_python-7.3.1/src/nwp500/temperature.py +373 -0
  23. nwp500_python-7.3.1/src/nwp500/unit_system.py +128 -0
  24. {nwp500_python-7.2.3 → nwp500_python-7.3.1/src/nwp500_python.egg-info}/PKG-INFO +1 -1
  25. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500_python.egg-info/SOURCES.txt +4 -0
  26. nwp500_python-7.3.1/tests/conftest.py +118 -0
  27. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_mqtt_client_init.py +41 -37
  28. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_temperature_converters.py +24 -0
  29. nwp500_python-7.3.1/tests/test_unit_switching.py +609 -0
  30. nwp500_python-7.2.3/src/nwp500/converters.py +0 -165
  31. nwp500_python-7.2.3/src/nwp500/temperature.py +0 -187
  32. nwp500_python-7.2.3/tests/conftest.py +0 -10
  33. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/.agent/workflows/pre-completion-testing.md +0 -0
  34. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/.coveragerc +0 -0
  35. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/.github/copilot-instructions.md +0 -0
  36. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/.github/workflows/ci.yml +0 -0
  37. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/.github/workflows/release.yml +0 -0
  38. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/.gitignore +0 -0
  39. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/.pre-commit-config.yaml +0 -0
  40. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/.readthedocs.yml +0 -0
  41. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/AUTHORS.rst +0 -0
  42. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/CONTRIBUTING.rst +0 -0
  43. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/LICENSE.txt +0 -0
  44. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/Makefile +0 -0
  45. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/README.rst +0 -0
  46. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/RELEASE.md +0 -0
  47. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/Makefile +0 -0
  48. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/_static/.gitignore +0 -0
  49. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/api/nwp500.rst +0 -0
  50. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/authors.rst +0 -0
  51. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/changelog.rst +0 -0
  52. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/conf.py +0 -0
  53. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/configuration.rst +0 -0
  54. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/development/contributing.rst +0 -0
  55. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/development/history.rst +0 -0
  56. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/enumerations.rst +0 -0
  57. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/advanced_features_explained.rst +0 -0
  58. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/authentication.rst +0 -0
  59. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/auto_recovery.rst +0 -0
  60. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/command_queue.rst +0 -0
  61. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/energy_monitoring.rst +0 -0
  62. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/event_system.rst +0 -0
  63. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/mqtt_diagnostics.rst +0 -0
  64. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/reservations.rst +0 -0
  65. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/scheduling_features.rst +0 -0
  66. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/guides/time_of_use.rst +0 -0
  67. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/installation.rst +0 -0
  68. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/license.rst +0 -0
  69. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/openapi.yaml +0 -0
  70. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/protocol/data_conversions.rst +0 -0
  71. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/protocol/device_features.rst +0 -0
  72. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/protocol/error_codes.rst +0 -0
  73. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/protocol/mqtt_protocol.rst +0 -0
  74. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/protocol/quick_reference.rst +0 -0
  75. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/protocol/rest_api.rst +0 -0
  76. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/python_api/api_client.rst +0 -0
  77. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/python_api/auth_client.rst +0 -0
  78. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/python_api/cli.rst +0 -0
  79. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/python_api/device_control.rst +0 -0
  80. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/python_api/events.rst +0 -0
  81. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/python_api/exceptions.rst +0 -0
  82. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/python_api/models.rst +0 -0
  83. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/python_api/mqtt_client.rst +0 -0
  84. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/quickstart.rst +0 -0
  85. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/docs/requirements.txt +0 -0
  86. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/.ruff.toml +0 -0
  87. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/README.md +0 -0
  88. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/air_filter_reset.py +0 -0
  89. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/anti_legionella.py +0 -0
  90. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/auto_recovery.py +0 -0
  91. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/combined_callbacks.py +0 -0
  92. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/demand_response.py +0 -0
  93. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/device_capabilities.py +0 -0
  94. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/device_status_debug.py +0 -0
  95. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/energy_analytics.py +0 -0
  96. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/error_code_demo.py +0 -0
  97. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/mqtt_diagnostics.py +0 -0
  98. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/power_control.py +0 -0
  99. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/recirculation_control.py +0 -0
  100. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/reconnection_demo.py +0 -0
  101. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/reservation_schedule.py +0 -0
  102. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/simple_auto_recovery.py +0 -0
  103. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/token_restoration.py +0 -0
  104. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/tou_openei.py +0 -0
  105. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/tou_schedule.py +0 -0
  106. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/advanced/water_reservation.py +0 -0
  107. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/beginner/01_authentication.py +0 -0
  108. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/beginner/02_list_devices.py +0 -0
  109. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/beginner/03_get_status.py +0 -0
  110. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/beginner/04_set_temperature.py +0 -0
  111. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/advanced_auth_patterns.py +0 -0
  112. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/command_queue.py +0 -0
  113. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/device_status_callback.py +0 -0
  114. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/error_handling.py +0 -0
  115. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/event_driven_control.py +0 -0
  116. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/improved_auth.py +0 -0
  117. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/legacy_auth_constructor.py +0 -0
  118. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/mqtt_realtime_monitoring.py +0 -0
  119. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/periodic_requests.py +0 -0
  120. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/set_mode.py +0 -0
  121. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/intermediate/vacation_mode.py +0 -0
  122. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/mask.py +0 -0
  123. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/testing/periodic_device_info.py +0 -0
  124. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/testing/simple_periodic_info.py +0 -0
  125. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/testing/test_api_client.py +0 -0
  126. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/testing/test_mqtt_connection.py +0 -0
  127. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/testing/test_mqtt_messaging.py +0 -0
  128. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/examples/testing/test_periodic_minimal.py +0 -0
  129. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/pyproject.toml +0 -0
  130. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/scripts/README.md +0 -0
  131. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/scripts/bump_version.py +0 -0
  132. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/scripts/diagnose_mqtt_connection.py +0 -0
  133. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/scripts/extract_changelog.py +0 -0
  134. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/scripts/format.py +0 -0
  135. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/scripts/lint.py +0 -0
  136. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/scripts/setup-dev.py +0 -0
  137. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/scripts/validate_version.py +0 -0
  138. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/setup.cfg +0 -0
  139. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/setup.py +0 -0
  140. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/cli/__init__.py +0 -0
  141. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/cli/commands.py +0 -0
  142. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/cli/monitoring.py +0 -0
  143. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/cli/rich_output.py +0 -0
  144. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/cli/token_storage.py +0 -0
  145. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/command_decorators.py +0 -0
  146. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/config.py +0 -0
  147. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/device_capabilities.py +0 -0
  148. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/device_info_cache.py +0 -0
  149. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/enums.py +0 -0
  150. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/exceptions.py +0 -0
  151. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/factory.py +0 -0
  152. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/__init__.py +0 -0
  153. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/command_queue.py +0 -0
  154. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/control.py +0 -0
  155. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/diagnostics.py +0 -0
  156. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/periodic.py +0 -0
  157. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt/reconnection.py +0 -0
  158. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/mqtt_events.py +0 -0
  159. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/py.typed +0 -0
  160. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/topic_builder.py +0 -0
  161. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500/utils.py +0 -0
  162. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  163. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  164. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  165. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500_python.egg-info/requires.txt +0 -0
  166. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/src/nwp500_python.egg-info/top_level.txt +0 -0
  167. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_api_helpers.py +0 -0
  168. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_auth.py +0 -0
  169. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_cli_basic.py +0 -0
  170. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_cli_commands.py +0 -0
  171. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_command_decorators.py +0 -0
  172. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_command_queue.py +0 -0
  173. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_device_capabilities.py +0 -0
  174. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_device_info_cache.py +0 -0
  175. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_events.py +0 -0
  176. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_exceptions.py +0 -0
  177. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_model_converters.py +0 -0
  178. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_models.py +0 -0
  179. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tests/test_utils.py +0 -0
  180. {nwp500_python-7.2.3 → nwp500_python-7.3.1}/tox.ini +0 -0
@@ -2,21 +2,113 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 7.3.1 (2026-01-25)
6
+ ==========================
7
+
8
+ Fixed
9
+ -----
10
+ - **MQTT Client Initialization**: Removed overly strict token validity check from ``NavienMqttClient.__init__()``
11
+
12
+ - The strict token validity check prevented creating MQTT clients with restored tokens that may have expired between application restarts
13
+ - However, ``NavienMqttClient.connect()`` already handles token refresh automatically, making the check redundant
14
+ - This change allows integrations to create MQTT clients with expired tokens and let connect() handle validation
15
+ - Simplifies application code by removing duplicate token refresh calls
16
+ - Enables proper handling of restored authentication sessions
17
+ - Fixes MQTT connection failures when using stored tokens across application restarts
18
+
19
+ Version 7.3.0 (2026-01-19)
20
+ ==========================
21
+
22
+ Added
23
+ -----
24
+ - **Dynamic Unit Conversion**: All temperature, flow, and volume measurements now dynamically convert based on user's region preference (Metric/Imperial)
25
+
26
+ - Temperature fields convert between Celsius and Fahrenheit using standard formulas
27
+ - Flow rate fields convert between LPM (Liters Per Minute) and GPM (Gallons Per Minute)
28
+ - Volume fields convert between Liters and Gallons
29
+ - Available devices and regions determine conversion support (validated at runtime)
30
+ - ``get_field_unit()`` method retrieves the correct unit suffix for any field
31
+ - All conversions use Pydantic ``WrapValidator`` for transparent, automatic conversion
32
+ - Comprehensive documentation in ``docs/guides/unit_conversion.rst``
33
+ - Example usage:
34
+
35
+ .. code-block:: python
36
+
37
+ # Get converted value with unit
38
+ temp_f = device_status.flow_rate_target # Returns Fahrenheit if region prefers imperial
39
+ unit = device_status.get_field_unit("flow_rate_target") # Returns "°F" or "°C"
40
+
41
+ # All conversions are transparent - values automatically convert to preferred units
42
+ flow_gpm = device_status.flow_rate_current # GPM if imperial, LPM if metric
43
+ volume_gal = device_status.tank_volume # Gallons if imperial, Liters if metric
44
+
45
+ - Supported conversion fields:
46
+
47
+ - **Temperature**: ``flow_rate_target``, ``flow_rate_current``, ``in_water_temp``, ``out_water_temp``, ``set_temp``, ``in_temp``, ``out_temp``, etc.
48
+ - **Flow Rate**: ``flow_rate_target``, ``flow_rate_current``
49
+ - **Volume**: ``tank_volume`` and related storage fields
50
+
51
+ - Integration patterns for Home Assistant, CLI, and custom integrations documented
52
+
53
+ - **Unit System Override**: Allow applications and CLI users to override the device's temperature preference and explicitly specify Metric or Imperial units
54
+
55
+ - Library-level: Add optional ``unit_system`` parameter to ``NavienAuthClient``, ``NavienMqttClient``, and ``NavienAPIClient`` initialization
56
+ - Set once at initialization; applies to all subsequent data conversions
57
+ - Accepts: ``"metric"`` (Celsius/LPM/Liters), ``"imperial"`` (Fahrenheit/GPM/Gallons), or ``None`` (auto-detect from device)
58
+ - Decouples unit preference from device configuration - users can override what the device is set to
59
+ - Uses context variables for thread-safe and async-safe unit system management
60
+ - Example usage:
61
+
62
+ .. code-block:: python
63
+
64
+ # Library initialization
65
+ from nwp500 import NavienAuthClient, set_unit_system
66
+ auth = NavienAuthClient(email, password, unit_system="metric")
67
+
68
+ # Or set after initialization
69
+ set_unit_system("imperial")
70
+ device_status = await mqtt.request_device_status(device)
71
+ # Values now in F, GPM, gallons regardless of device setting
72
+
73
+ - CLI-level: Add ``--unit-system`` flag for per-command override
74
+ - Example: ``nwp-cli status --unit-system metric``
75
+ - Defaults to device's setting if not specified
76
+ - New exported functions:
77
+ - ``set_unit_system(unit_system)`` - Set the preferred unit system
78
+ - ``get_unit_system()`` - Get the current unit system preference
79
+ - ``reset_unit_system()`` - Reset to auto-detect mode
80
+
81
+ Fixed
82
+ -----
83
+ - **MQTT Unit System Override Bug**: Fixed unit_system CLI flag not applying to device status values
84
+
85
+ - **Issue**: Running ``nwp-cli --unit-system metric status`` displayed values in device's native format (imperial) with metric unit strings (e.g., "104.9 °C" instead of "40.5 °C")
86
+ - **Root Cause**: MQTT callbacks from AWS CRT execute on different threads where context variables are not set. Since Python's context variables are task-local, the unit_system preference was not visible to validators
87
+ - **Solution**: Store unit_system in ``NavienMqttClient`` and ``MqttSubscriptionManager``. Before parsing MQTT messages, explicitly set the context variable in message handlers to ensure validators use the correct unit system regardless of thread context
88
+ - **Result**: Values and units now correctly convert when ``--unit-system`` override is specified
89
+ - **Testing**: All 393 tests pass including new unit system context override tests
90
+
91
+ - **Type Annotation Quotes**: Removed unnecessary quoted type annotations (UP037 violations)
92
+
93
+ - With ``from __future__ import annotations``, explicit string quotes are redundant
94
+ - Updated ``from_dict()`` methods in ``UserInfo``, ``AuthTokens``, ``AuthenticationResponse``, ``DeviceStatus``, ``DeviceFeature``, and ``EnergyUsageResponse``
95
+ - Improves code clarity and passes modern linting standards
96
+
5
97
  Version 7.2.3 (2026-01-15)
6
98
  ==========================
7
99
 
8
100
  Added
9
101
  -----
10
102
  - **Daily Energy Breakdown by Month**: New ``--month`` option for energy command to show daily energy data for a specific month
11
-
103
+
12
104
  .. code-block:: bash
13
-
105
+
14
106
  # Daily breakdown for a single month
15
107
  nwp-cli energy --year 2025 --month 12
16
-
108
+
17
109
  # Monthly summary for multiple months (existing)
18
110
  nwp-cli energy --year 2025 --months 10,11,12
19
-
111
+
20
112
  - Displays daily energy consumption, efficiency, and heat source breakdown
21
113
  - Rich formatted output with progress bars and color-coded efficiency percentages
22
114
  - Plain text fallback for non-Rich environments
@@ -25,25 +117,25 @@ Added
25
117
  Fixed
26
118
  -----
27
119
  - **Documentation**: Fixed all warnings and broken cross-references in documentation
28
-
120
+
29
121
  - Fixed docstring formatting in field_factory.py module
30
122
  - Fixed broken cross-reference links in enumerations.rst, mqtt_diagnostics.rst, cli.rst, and models.rst
31
123
  - Fixed invalid JSON syntax in code examples (removed invalid [...] and ... tokens)
32
124
  - Suppressed duplicate object description warnings from re-exported classes
33
-
125
+
34
126
  - **CLI Documentation**: Updated documentation for all 19 CLI commands
35
-
127
+
36
128
  - Added missing device-info command documentation
37
129
  - Added --raw flag documentation for status, info, and device-info commands
38
130
  - Added --month option documentation to energy command
39
131
  - Clarified mutually exclusive options (--months vs --month)
40
-
132
+
41
133
  - **RST Title Hierarchy**: Fixed title level inconsistencies in device_control.rst
42
134
 
43
135
  - **Read the Docs Configuration**: Updated Python version requirement to 3.13 in Read the Docs config
44
136
 
45
137
  - **CI Test Failures**: Fixed ``ModuleNotFoundError`` when running tests without CLI dependencies installed
46
-
138
+
47
139
  - Wrapped CLI module imports in try-except blocks in test modules
48
140
  - Tests are skipped gracefully when optional dependencies (click, rich) are not installed
49
141
  - Allows pytest to run without CLI extra, while supporting full test suite with tox
@@ -59,7 +151,7 @@ Version 7.2.2 (2025-12-25)
59
151
  Fixed
60
152
  -----
61
153
  - **TOU Status Always Showing False**: Fixed ``touStatus`` field always reporting ``False`` regardless of actual device state
62
-
154
+
63
155
  - Root cause: Version 7.2.1 incorrectly changed ``touStatus`` to use device-specific 1/2 encoding, but the device uses standard 0/1 encoding
64
156
  - Solution: Use Python's built-in ``bool()`` for ``touStatus`` field (handles 0=False, 1=True naturally)
65
157
  - Updated documentation in ``docs/protocol/quick_reference.rst`` to note ``touStatus`` exception
@@ -72,21 +164,21 @@ Version 7.2.1 (2025-12-25)
72
164
  Added
73
165
  -----
74
166
  - **CLI Command**: New ``device-info`` command to retrieve basic device information from REST API
75
-
167
+
76
168
  .. code-block:: bash
77
-
169
+
78
170
  # Get basic device info (DeviceInfo model)
79
171
  python3 -m nwp500.cli device-info
80
172
  python3 -m nwp500.cli device-info --raw
81
173
 
82
174
  - **ConnectionStatus Enum**: New ``ConnectionStatus`` enum for device cloud connection state
83
-
175
+
84
176
  - ``ConnectionStatus.DISCONNECTED`` = 1 - Device offline/not connected
85
177
  - ``ConnectionStatus.CONNECTED`` = 2 - Device online and reachable
86
178
  - Used in ``DeviceInfo.connected`` field with automatic validation
87
179
 
88
180
  - **InstallType Enum**: New ``InstallType`` enum for device installation classification
89
-
181
+
90
182
  - ``InstallType.RESIDENTIAL`` = "R" - Residential use
91
183
  - ``InstallType.COMMERCIAL`` = "C" - Commercial use
92
184
  - Used in ``DeviceInfo.install_type`` field with automatic validation
@@ -96,7 +188,7 @@ Added
96
188
 
97
189
  Changed
98
190
  -------
99
- - **DeviceInfo Model**:
191
+ - **DeviceInfo Model**:
100
192
  - ``connected`` field now uses ``ConnectionStatus`` enum instead of plain int
101
193
  - ``install_type`` field now uses ``InstallType`` enum instead of plain string
102
194
 
@@ -112,12 +204,12 @@ Changed
112
204
  Removed
113
205
  -------
114
206
  - **constants.py Module**: Removed empty ``constants.py`` module. ``CommandCode`` enum was already moved to ``enums.py`` in version 4.2.0.
115
-
207
+
116
208
  .. code-block:: python
117
-
209
+
118
210
  # OLD (removed)
119
211
  from nwp500.constants import CommandCode
120
-
212
+
121
213
  # NEW (use this)
122
214
  from nwp500.enums import CommandCode
123
215
 
@@ -140,20 +232,20 @@ Removed
140
232
 
141
233
  # OLD (removed)
142
234
  from nwp500 import DeviceCapabilityChecker, DeviceInfoCache
143
-
235
+
144
236
  # NEW
145
237
  from nwp500 import MqttDeviceCapabilityChecker, MqttDeviceInfoCache
146
238
 
147
- **Rationale**: The original names were too generic. These classes are specifically designed
148
- for MQTT client functionality (auto-fetching device info, caching, capability checking).
149
- The new names make it clear they're MQTT-specific implementations, leaving room for future
239
+ **Rationale**: The original names were too generic. These classes are specifically designed
240
+ for MQTT client functionality (auto-fetching device info, caching, capability checking).
241
+ The new names make it clear they're MQTT-specific implementations, leaving room for future
150
242
  REST API versions if needed.
151
243
 
152
244
  **Migration**: Simple find-and-replace:
153
-
245
+
154
246
  - ``DeviceCapabilityChecker`` → ``MqttDeviceCapabilityChecker``
155
247
  - ``DeviceInfoCache`` → ``MqttDeviceInfoCache``
156
-
248
+
157
249
  All functionality remains identical - only the class names changed.
158
250
 
159
251
  Added
@@ -165,7 +257,7 @@ Added
165
257
 
166
258
  # Create both API and MQTT clients in one call
167
259
  from nwp500 import create_navien_clients
168
-
260
+
169
261
  async with create_navien_clients(email, password) as (api_client, mqtt_client):
170
262
  devices = await api_client.get_devices()
171
263
  await mqtt_client.connect()
@@ -183,10 +275,10 @@ Added
183
275
  .. code-block:: python
184
276
 
185
277
  from nwp500 import VolumeCode
186
-
278
+
187
279
  # Enum values: VOLUME_50GAL = 65, VOLUME_65GAL = 66, VOLUME_80GAL = 67
188
280
  # Human-readable text available in VOLUME_CODE_TEXT dict
189
-
281
+
190
282
  - Maps device codes to actual tank capacities (50, 65, 80 gallons)
191
283
  - Used in ``DeviceFeature.volume_code`` field with automatic validation
192
284
  - Exported from main package for convenience
@@ -254,7 +346,7 @@ Changed
254
346
  from nwp500.mqtt_client import NavienMqttClient
255
347
  from nwp500.mqtt_diagnostics import MqttDiagnosticsCollector
256
348
  from nwp500.mqtt_utils import MqttConnectionConfig
257
-
349
+
258
350
  # NEW imports (preferred)
259
351
  from nwp500.mqtt import NavienMqttClient, MqttDiagnosticsCollector, MqttConnectionConfig
260
352
  # OR import from main package (recommended)
@@ -285,12 +377,12 @@ Changed
285
377
  - Renamed and moved 35+ example scripts for better discoverability
286
378
  - Updated ``examples/README.md`` with 'Getting Started' guide and categorized index
287
379
  - Added 01-04 beginner series for smooth onboarding:
288
-
380
+
289
381
  - ``beginner/01_authentication.py`` - Basic authentication patterns
290
382
  - ``beginner/02_list_devices.py`` - Retrieving device information
291
383
  - ``beginner/03_get_status.py`` - Getting device status
292
384
  - ``beginner/04_set_temperature.py`` - Basic device control
293
-
385
+
294
386
  - Intermediate examples: event-driven control, error handling, MQTT monitoring
295
387
  - Advanced examples: demand response, recirculation, TOU schedules, diagnostics
296
388
  - Testing examples: connection testing, periodic updates, minimal examples
@@ -427,18 +519,18 @@ Fixed
427
519
  Version 7.0.0 (2025-12-17)
428
520
  ==========================
429
521
 
430
- **BREAKING CHANGES**:
522
+ **BREAKING CHANGES**:
431
523
  - Minimum Python version raised to 3.13
432
524
  - Enumerations refactored for type safety and consistency
433
525
 
434
526
  Removed
435
527
  -------
436
528
  - **Python 3.9-3.12 Support**: Minimum Python version is now 3.13
437
-
529
+
438
530
  Home Assistant has deprecated Python 3.12 support, making Python 3.13 the de facto minimum for this ecosystem.
439
-
531
+
440
532
  Python 3.13 features and improvements:
441
-
533
+
442
534
  - **Experimental free-threaded mode** (PEP 703): Optional GIL removal for true parallelism
443
535
  - **JIT compiler** (PEP 744): Just-in-time compilation for performance improvements
444
536
  - **Better error messages**: Enhanced suggestions for NameError, AttributeError, and import errors
@@ -447,16 +539,16 @@ Removed
447
539
  - PEP 695: New type parameter syntax for generics
448
540
  - PEP 701: f-string improvements
449
541
  - Built-in ``datetime.UTC`` constant
450
-
542
+
451
543
  If you need Python 3.12 support, use version 6.1.x of this library.
452
544
 
453
545
  - **CommandCode moved**: Import from ``nwp500.enums`` instead of ``nwp500.constants``
454
-
546
+
455
547
  .. code-block:: python
456
-
548
+
457
549
  # OLD (removed)
458
550
  from nwp500.constants import CommandCode
459
-
551
+
460
552
  # NEW
461
553
  from nwp500.enums import CommandCode
462
554
  # OR
@@ -466,14 +558,14 @@ Added
466
558
  -----
467
559
 
468
560
  - **Python 3.12+ Optimizations**: Leverage latest Python features
469
-
561
+
470
562
  - PEP 695: New type parameter syntax (``def func[T](...)`` instead of ``TypeVar``)
471
563
  - Use ``datetime.UTC`` constant instead of ``datetime.timezone.utc``
472
564
  - Native union syntax (``X | Y`` instead of ``Union[X, Y]``)
473
565
  - Cleaner generic type annotations throughout codebase
474
566
 
475
567
  - **Enumerations Module (``src/nwp500/enums.py``)**: Comprehensive type-safe enums for device control and status
476
-
568
+
477
569
  - Status value enums: ``OnOffFlag``, ``Operation``, ``DhwOperationSetting``, ``CurrentOperationMode``, ``HeatSource``, ``DREvent``, ``WaterLevel``, ``FilterChange``, ``RecirculationMode``
478
570
  - Time of Use enums: ``TouWeekType``, ``TouRateType``
479
571
  - Device capability enums: ``CapabilityFlag``, ``TemperatureType``, ``DeviceType``
@@ -488,7 +580,7 @@ Changed
488
580
  -------
489
581
 
490
582
  - **Command Code Constants**: Migrated from ``constants.py`` to ``CommandCode`` enum in ``enums.py``
491
-
583
+
492
584
  - ``ANTI_LEGIONELLA_ENABLE`` → ``CommandCode.ANTI_LEGIONELLA_ON``
493
585
  - ``ANTI_LEGIONELLA_DISABLE`` → ``CommandCode.ANTI_LEGIONELLA_OFF``
494
586
  - ``TOU_ENABLE`` → ``CommandCode.TOU_ON``
@@ -497,19 +589,19 @@ Changed
497
589
  - All command constants now use consistent naming in ``CommandCode`` enum
498
590
 
499
591
  - **Model Enumerations**: Updated type annotations for clarity and type safety
500
-
592
+
501
593
  - ``TemperatureUnit`` → ``TemperatureType`` (matches device protocol field names)
502
594
  - All capability flags (e.g., ``power_use``, ``dhw_use``) now use ``CapabilityFlag`` type
503
595
  - ``MqttRequest.device_type`` now accepts ``Union[DeviceType, int]`` for flexibility
504
596
 
505
597
  - **Model Serialization**: Enums automatically serialize to human-readable names
506
-
598
+
507
599
  - `model_dump()` converts enums to names (e.g., `DhwOperationSetting.HEAT_PUMP` → `"HEAT_PUMP"`)
508
600
  - CLI and other consumers benefit from automatic enum name serialization
509
601
  - Text mappings available for custom formatting (e.g., `DHW_OPERATION_TEXT[enum]` → "Heat Pump Only")
510
602
 
511
603
  - **Documentation**: Comprehensive updates across protocol and API documentation
512
-
604
+
513
605
  - ``docs/guides/time_of_use.rst``: Clarified TOU override status behavior (1=OFF/override active, 2=ON/normal operation)
514
606
  - ``docs/protocol/data_conversions.rst``: Updated TOU field descriptions with correct enum values
515
607
  - ``docs/protocol/device_features.rst``: Added capability flag pattern explanation (2=supported, 1=not supported)
@@ -517,14 +609,14 @@ Changed
517
609
  - ``docs/python_api/models.rst``: Updated model field type annotations
518
610
 
519
611
  - **Examples**: Updated to use new enums for type-safe device control
520
-
612
+
521
613
  - ``examples/anti_legionella_example.py``: Uses ``CommandCode`` enum
522
614
  - ``examples/device_feature_callback.py``: Uses capability enums
523
615
  - ``examples/event_emitter_demo.py``: Uses status enums
524
616
  - ``examples/mqtt_diagnostics_example.py``: Uses command enums
525
617
 
526
618
  - **CLI Code Cleanup**: Refactored JSON formatting to use shared utility function
527
-
619
+
528
620
  - Extracted repeated `json.dumps()` calls to `format_json_output()` helper
529
621
  - Cleaner code with consistent formatting across all commands
530
622
 
@@ -572,7 +664,7 @@ Changed
572
664
 
573
665
  # OLD (removed)
574
666
  build_reservation_entry(..., param=120)
575
-
667
+
576
668
  # NEW
577
669
  build_reservation_entry(..., temperature_f=140.0)
578
670
 
@@ -583,7 +675,7 @@ Changed
583
675
 
584
676
  # OLD (removed)
585
677
  await mqtt.set_dhw_temperature(device, 120)
586
-
678
+
587
679
  # NEW
588
680
  await mqtt.set_dhw_temperature(device, 140.0)
589
681
 
@@ -604,7 +696,7 @@ Added
604
696
  .. code-block:: python
605
697
 
606
698
  from nwp500 import fahrenheit_to_half_celsius
607
-
699
+
608
700
  param = fahrenheit_to_half_celsius(140.0) # Returns 120
609
701
 
610
702
  Fixed
@@ -644,7 +736,7 @@ Fixed
644
736
 
645
737
  - **Example Code**: Fixed ``device_status_callback.py`` example to use snake_case attribute names consistently
646
738
  - **Field Descriptions**: Clarified distinctions between similar fields:
647
-
739
+
648
740
  - ``dhw_temperature_setting`` vs ``dhw_target_temperature_setting`` descriptions
649
741
  - ``freeze_protection_temp`` descriptions differ between DeviceStatus and DeviceFeature
650
742
  - ``eco_use`` descriptions differ between DeviceStatus (current state) and DeviceFeature (capability)
@@ -656,7 +748,7 @@ Fixed
656
748
  -----
657
749
 
658
750
  - **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:
659
-
751
+
660
752
  - ``tank_upper_temperature`` - Water tank upper sensor
661
753
  - ``tank_lower_temperature`` - Water tank lower sensor
662
754
  - ``discharge_temperature`` - Compressor discharge temperature (refrigerant)
@@ -665,13 +757,13 @@ Fixed
665
757
  - ``ambient_temperature`` - Ambient air temperature at heat pump
666
758
  - ``target_super_heat`` - Target superheat setpoint
667
759
  - ``current_super_heat`` - Measured superheat value
668
-
760
+
669
761
  **Impact**: These fields now correctly display temperatures in expected ranges:
670
-
762
+
671
763
  - Tank temperatures: ~120°F (close to DHW temperature, not ~220°F)
672
764
  - Discharge temperature: 120-180°F (not 220-280°F)
673
765
  - Suction, evaporator, ambient: Now showing physically realistic values
674
-
766
+
675
767
  **Technical details**: Changed from ``PentaCelsiusToF`` (÷5) back to ``DeciCelsiusToF`` (÷10). The correct formula is ``(raw_value / 10.0) * 9/5 + 32``.
676
768
 
677
769
  Changed
@@ -686,7 +778,7 @@ Fixed
686
778
  -----
687
779
 
688
780
  - **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:
689
-
781
+
690
782
  - Replaced ``Add20`` validator with ``HalfCelsiusToF`` for fields transmitted as half-degrees Celsius
691
783
  - Replaced ``DeciCelsiusToF`` with ``PentaCelsiusToF`` for fields scaled by factor of 5
692
784
  - Affects multiple temperature sensor readings for improved accuracy
@@ -818,32 +910,32 @@ Removed
818
910
  -------
819
911
 
820
912
  - **Constructor Callbacks**: Removed ``on_connection_interrupted`` and ``on_connection_resumed`` constructor parameters from ``NavienMqttClient``
821
-
913
+
822
914
  .. code-block:: python
823
-
915
+
824
916
  # OLD (removed in v6.0.0)
825
917
  mqtt_client = NavienMqttClient(
826
918
  auth_client,
827
919
  on_connection_interrupted=on_interrupted,
828
920
  on_connection_resumed=on_resumed,
829
921
  )
830
-
922
+
831
923
  # NEW (use event emitter pattern)
832
924
  mqtt_client = NavienMqttClient(auth_client)
833
925
  mqtt_client.on("connection_interrupted", on_interrupted)
834
926
  mqtt_client.on("connection_resumed", on_resumed)
835
927
 
836
928
  - **Backward Compatibility Re-exports**: Removed exception re-exports from ``api_client`` and ``auth`` modules
837
-
929
+
838
930
  .. code-block:: python
839
-
931
+
840
932
  # OLD (removed in v6.0.0)
841
933
  from nwp500.api_client import APIError
842
934
  from nwp500.auth import AuthenticationError, TokenRefreshError
843
-
935
+
844
936
  # NEW (import from exceptions module)
845
937
  from nwp500.exceptions import APIError, AuthenticationError, TokenRefreshError
846
-
938
+
847
939
  # OR (import from package root - recommended)
848
940
  from nwp500 import APIError, AuthenticationError, TokenRefreshError
849
941
 
@@ -854,7 +946,7 @@ Changed
854
946
  -------
855
947
 
856
948
  - **Migration Benefits**:
857
-
949
+
858
950
  - Multiple listeners per event (not just one callback)
859
951
  - Consistent API with other events (temperature_changed, mode_changed, etc.)
860
952
  - Dynamic listener management (add/remove listeners at runtime)
@@ -877,7 +969,7 @@ Fixed
877
969
  -----
878
970
 
879
971
  - **MQTT Future Cancellation**: Fixed InvalidStateError exceptions during disconnect
880
-
972
+
881
973
  - Added asyncio.shield() to protect concurrent.futures.Future objects from cancellation
882
974
  - Applied consistent cancellation handling across all MQTT operations (connect, disconnect, subscribe, unsubscribe, publish)
883
975
  - AWS CRT callbacks can now complete independently without raising InvalidStateError
@@ -905,7 +997,7 @@ Changed
905
997
  Version 5.0.0 (2025-10-27)
906
998
  ==========================
907
999
 
908
- **BREAKING CHANGES**: This release introduces a comprehensive enterprise exception architecture.
1000
+ **BREAKING CHANGES**: This release introduces a comprehensive enterprise exception architecture.
909
1001
  See migration guide below for details on updating your code.
910
1002
 
911
1003
  Added
@@ -915,10 +1007,10 @@ Added
915
1007
 
916
1008
  - Created ``exceptions.py`` module with comprehensive exception hierarchy
917
1009
  - Added ``Nwp500Error`` as base exception for all library errors
918
- - Added MQTT-specific exceptions: ``MqttError``, ``MqttConnectionError``, ``MqttNotConnectedError``,
1010
+ - Added MQTT-specific exceptions: ``MqttError``, ``MqttConnectionError``, ``MqttNotConnectedError``,
919
1011
  ``MqttPublishError``, ``MqttSubscriptionError``, ``MqttCredentialsError``
920
1012
  - Added validation exceptions: ``ValidationError``, ``ParameterValidationError``, ``RangeValidationError``
921
- - Added device exceptions: ``DeviceError``, ``DeviceNotFoundError``, ``DeviceOfflineError``,
1013
+ - Added device exceptions: ``DeviceError``, ``DeviceNotFoundError``, ``DeviceOfflineError``,
922
1014
  ``DeviceOperationError``
923
1015
  - All exceptions now include ``error_code``, ``details``, and ``retriable`` attributes
924
1016
  - Added ``to_dict()`` method to all exceptions for structured logging
@@ -946,7 +1038,7 @@ Migration Guide (v4.x to v5.0)
946
1038
 
947
1039
  **Breaking Changes Summary**:
948
1040
 
949
- The library now uses specific exception types instead of generic ``RuntimeError`` and ``ValueError``.
1041
+ The library now uses specific exception types instead of generic ``RuntimeError`` and ``ValueError``.
950
1042
  This improves error handling but requires updates to exception handling code.
951
1043
 
952
1044
  **1. MQTT Connection Errors**
@@ -962,7 +1054,7 @@ This improves error handling but requires updates to exception handling code.
962
1054
 
963
1055
  # NEW CODE (v5.0+)
964
1056
  from nwp500 import MqttNotConnectedError, MqttError
965
-
1057
+
966
1058
  try:
967
1059
  await mqtt_client.request_device_status(device)
968
1060
  except MqttNotConnectedError:
@@ -985,7 +1077,7 @@ This improves error handling but requires updates to exception handling code.
985
1077
 
986
1078
  # NEW CODE (v5.0+)
987
1079
  from nwp500 import RangeValidationError, ValidationError
988
-
1080
+
989
1081
  try:
990
1082
  set_vacation_mode(device, days=35)
991
1083
  except RangeValidationError as e:
@@ -1009,7 +1101,7 @@ This improves error handling but requires updates to exception handling code.
1009
1101
 
1010
1102
  # NEW CODE (v5.0+)
1011
1103
  from nwp500 import MqttCredentialsError
1012
-
1104
+
1013
1105
  try:
1014
1106
  mqtt_client = NavienMqttClient(auth_client)
1015
1107
  except MqttCredentialsError as e:
@@ -1023,14 +1115,14 @@ This improves error handling but requires updates to exception handling code.
1023
1115
 
1024
1116
  # NEW CODE (v5.0+) - catch all library exceptions
1025
1117
  from nwp500 import Nwp500Error
1026
-
1118
+
1027
1119
  try:
1028
1120
  # Any library operation
1029
1121
  await mqtt_client.request_device_status(device)
1030
1122
  except Nwp500Error as e:
1031
1123
  # All nwp500 exceptions inherit from Nwp500Error
1032
1124
  logger.error(f"Library error: {e.to_dict()}")
1033
-
1125
+
1034
1126
  # Check if retriable
1035
1127
  if e.retriable:
1036
1128
  await retry_operation()
@@ -1042,7 +1134,7 @@ All exceptions now include structured information:
1042
1134
  .. code-block:: python
1043
1135
 
1044
1136
  from nwp500 import MqttPublishError
1045
-
1137
+
1046
1138
  try:
1047
1139
  await mqtt_client.publish(topic, payload)
1048
1140
  except MqttPublishError as e:
@@ -1055,10 +1147,10 @@ All exceptions now include structured information:
1055
1147
  # 'details': {},
1056
1148
  # 'retriable': True
1057
1149
  # }
1058
-
1150
+
1059
1151
  # Log for monitoring/alerting
1060
1152
  logger.error("Publish failed", extra=error_info)
1061
-
1153
+
1062
1154
  # Implement retry logic
1063
1155
  if e.retriable:
1064
1156
  await asyncio.sleep(1)
@@ -1116,7 +1208,7 @@ Added
1116
1208
  -----
1117
1209
 
1118
1210
  - **MQTT Reconnection**: Two-tier reconnection strategy with unlimited retries
1119
-
1211
+
1120
1212
  - Implemented quick reconnection (attempts 1-9) for fast recovery from transient network issues
1121
1213
  - Implemented deep reconnection (every 10th attempt) with full connection rebuild and credential refresh
1122
1214
  - Changed default ``max_reconnect_attempts`` from 10 to -1 (unlimited retries)
@@ -1132,7 +1224,7 @@ Improved
1132
1224
  --------
1133
1225
 
1134
1226
  - **Exception Handling**: Replaced 25 catch-all exception handlers with specific exception types
1135
-
1227
+
1136
1228
  - ``mqtt_client.py``: Uses ``AwsCrtError``, ``AuthenticationError``, ``TokenRefreshError``, ``RuntimeError``, ``ValueError``, ``TypeError``, ``AttributeError``
1137
1229
  - ``mqtt_reconnection.py``: Uses ``AwsCrtError``, ``RuntimeError``, ``ValueError``, ``TypeError``
1138
1230
  - ``mqtt_connection.py``: Uses ``AwsCrtError``, ``RuntimeError``, ``ValueError``
@@ -1142,7 +1234,7 @@ Improved
1142
1234
  - Added exception handling guidelines to ``.github/copilot-instructions.md``
1143
1235
 
1144
1236
  - **Code Quality**: Multiple readability and safety improvements
1145
-
1237
+
1146
1238
  - Simplified nested conditions by extracting to local variables
1147
1239
  - Added ``hasattr()`` checks before accessing ``AwsCrtError.name`` attribute
1148
1240
  - Optimized ``resubscribe_all()`` to break after first failure per topic (reduces redundant error logs)
@@ -1153,7 +1245,7 @@ Fixed
1153
1245
  -----
1154
1246
 
1155
1247
  - **MQTT Reconnection**: Eliminated duplicate "Connection interrupted" log messages
1156
-
1248
+
1157
1249
  - Removed duplicate logging from ``mqtt_client.py`` (kept in ``mqtt_reconnection.py``)
1158
1250
 
1159
1251
  Version 3.1.4 (2025-10-26)
@@ -1163,7 +1255,7 @@ Fixed
1163
1255
  -----
1164
1256
 
1165
1257
  - **MQTT Reconnection**: Fixed MQTT reconnection failures due to expired AWS credentials
1166
-
1258
+
1167
1259
  - Added AWS credential expiration tracking (``_aws_expires_at`` field in ``AuthTokens``)
1168
1260
  - Added ``are_aws_credentials_expired`` property to check AWS credential validity
1169
1261
  - Modified ``ensure_valid_token()`` to prioritize AWS credential expiration check
@@ -1180,7 +1272,7 @@ Fixed
1180
1272
  -----
1181
1273
 
1182
1274
  - **MQTT Reconnection**: Improved MQTT reconnection reliability with active reconnection
1183
-
1275
+
1184
1276
  - **Breaking Internal Change**: ``MqttReconnectionHandler`` now requires ``reconnect_func`` parameter (not Optional)
1185
1277
  - Implemented active reconnection that always recreates MQTT connection on interruption
1186
1278
  - Removed unreliable passive fallback to AWS IoT SDK automatic reconnection
@@ -1199,7 +1291,7 @@ Fixed
1199
1291
  -----
1200
1292
 
1201
1293
  - **Authentication**: Fixed 401 authentication errors with automatic token refresh
1202
-
1294
+
1203
1295
  - Add automatic token refresh on 401 Unauthorized responses in API client
1204
1296
  - Preserve AWS credentials when refreshing tokens (required for MQTT)
1205
1297
  - Save refreshed tokens to cache after successful API calls
@@ -1216,7 +1308,7 @@ Fixed
1216
1308
  -----
1217
1309
 
1218
1310
  - **MQTT Client**: Fixed connection interrupted callback signature for AWS SDK
1219
-
1311
+
1220
1312
  - Updated callback to match latest AWS IoT SDK signature: ``(connection, error, **kwargs)``
1221
1313
  - Fixed type annotations in ``MqttConnection`` for proper type checking
1222
1314
  - Resolves mypy type checking errors and ensures AWS SDK compatibility
@@ -1228,14 +1320,14 @@ Version 3.0.0 (Unreleased)
1228
1320
  **Breaking Changes**
1229
1321
 
1230
1322
  - **REMOVED**: ``OperationMode`` enum has been removed
1231
-
1323
+
1232
1324
  - This enum was deprecated in v2.0.0 and has now been fully removed
1233
1325
  - Use ``DhwOperationSetting`` for user-configured mode preferences (values 1-6)
1234
1326
  - Use ``CurrentOperationMode`` for real-time operational states (values 0, 32, 64, 96)
1235
1327
  - Migration was supported throughout the v2.x series
1236
1328
 
1237
1329
  - **REMOVED**: Migration helper functions and deprecation infrastructure
1238
-
1330
+
1239
1331
  - Removed ``migrate_operation_mode_usage()`` function
1240
1332
  - Removed ``enable_deprecation_warnings()`` function
1241
1333
  - Removed migration documentation files (MIGRATION.md, BREAKING_CHANGES_V3.md)
@@ -1248,7 +1340,7 @@ Version 2.0.0 (Unreleased)
1248
1340
 
1249
1341
  - **DEPRECATION**: ``OperationMode`` enum is deprecated and will be removed in v3.0.0
1250
1342
 
1251
-
1343
+
1252
1344
  - Use ``DhwOperationSetting`` for user-configured mode preferences (values 1-6)
1253
1345
  - Use ``CurrentOperationMode`` for real-time operational states (values 0, 32, 64, 96)
1254
1346
  - See ``MIGRATION.md`` for detailed migration guide
@@ -1325,7 +1417,7 @@ Added
1325
1417
  - Standardized ruff configuration across all environments
1326
1418
  - Eliminates "passes locally but fails in CI" issues
1327
1419
  - Cross-platform support (Linux, macOS, Windows, containers)
1328
-
1420
+
1329
1421
  - All MQTT operations (connect, disconnect, subscribe, unsubscribe, publish) use ``asyncio.wrap_future()`` to convert AWS SDK Futures to asyncio Futures
1330
1422
  - Eliminates "blocking I/O detected" warnings in Home Assistant and other async applications
1331
1423
  - Fully compatible with async event loops without blocking other operations
@@ -1336,7 +1428,7 @@ Added
1336
1428
  - Updated documentation with non-blocking implementation details
1337
1429
 
1338
1430
  - **Event Emitter Pattern (Phase 1)**: Event-driven architecture for device state changes
1339
-
1431
+
1340
1432
  - ``EventEmitter`` base class with multiple listeners per event
1341
1433
  - Async and sync handler support
1342
1434
  - Priority-based execution order (higher priority executes first)
@@ -1354,7 +1446,7 @@ Added
1354
1446
  - Documentation: ``EVENT_EMITTER.rst``, ``EVENT_QUICK_REFERENCE.rst``, ``EVENT_ARCHITECTURE.rst``
1355
1447
 
1356
1448
  - **Authentication**: Simplified constructor-based authentication
1357
-
1449
+
1358
1450
  - ``NavienAuthClient`` now requires ``user_id`` and ``password`` in constructor
1359
1451
  - Automatic authentication when entering async context manager
1360
1452
  - No need to call ``sign_in()`` manually
@@ -1363,7 +1455,7 @@ Added
1363
1455
  - Updated all documentation with new authentication examples
1364
1456
 
1365
1457
  - **MQTT Command Queue**: Automatic command queuing when disconnected
1366
-
1458
+
1367
1459
  - Commands sent while disconnected are automatically queued
1368
1460
  - Queue processed in FIFO order when connection is restored
1369
1461
  - Configurable queue size (default: 100 commands)
@@ -1376,7 +1468,7 @@ Added
1376
1468
  - Documentation: ``COMMAND_QUEUE.rst``
1377
1469
 
1378
1470
  - **MQTT Reconnection**: Automatic reconnection with exponential backoff
1379
-
1471
+
1380
1472
  - Automatic reconnection on connection interruption
1381
1473
  - Configurable exponential backoff (default: 1s, 2s, 4s, 8s, ... up to 120s)
1382
1474
  - Configurable max attempts (default: 10)
@@ -1388,7 +1480,7 @@ Added
1388
1480
  - Documentation: Added reconnection section to MQTT_CLIENT.rst
1389
1481
 
1390
1482
  - **MQTT Client**: Complete implementation of real-time device communication
1391
-
1483
+
1392
1484
  - WebSocket MQTT connection to AWS IoT Core
1393
1485
  - Device subscription and message handling
1394
1486
  - Status request methods (device info, device status)
@@ -1397,7 +1489,7 @@ Added
1397
1489
  - Connection lifecycle management (connect, disconnect, reconnect)
1398
1490
 
1399
1491
  - **Device Control**: Fully implemented and verified control commands
1400
-
1492
+
1401
1493
  - Power control (on/off) with correct command codes
1402
1494
  - DHW mode control (Heat Pump, Electric, Energy Saver, High Demand)
1403
1495
  - DHW temperature control with 20°F offset handling
@@ -1405,7 +1497,7 @@ Added
1405
1497
  - Helper method for display-value temperature control
1406
1498
 
1407
1499
  - **Typed Callbacks**: 100% coverage of all MQTT response types
1408
-
1500
+
1409
1501
  - ``subscribe_device_status()`` - Automatic parsing of status messages into ``DeviceStatus`` objects
1410
1502
  - ``subscribe_device_feature()`` - Automatic parsing of feature messages into ``DeviceFeature`` objects
1411
1503
  - ``subscribe_energy_usage()`` - Automatic parsing of energy usage responses into ``EnergyUsageResponse`` objects
@@ -1414,7 +1506,7 @@ Added
1414
1506
  - Example scripts demonstrating usage patterns
1415
1507
 
1416
1508
  - **Energy Usage API (EMS)**: Historical energy consumption data
1417
-
1509
+
1418
1510
  - ``request_energy_usage()`` - Query daily energy usage for specified month(s)
1419
1511
  - ``EnergyUsageResponse`` dataclass with daily breakdown
1420
1512
  - ``EnergyUsageTotal`` with percentage calculations
@@ -1426,7 +1518,7 @@ Added
1426
1518
  - Efficiency percentage calculations
1427
1519
 
1428
1520
  - **Data Models**: Comprehensive type-safe models
1429
-
1521
+
1430
1522
  - ``DeviceStatus`` dataclass with 125 sensor and operational fields
1431
1523
  - ``DeviceFeature`` dataclass with 46 capability and configuration fields
1432
1524
  - ``EnergyUsageResponse`` dataclass for historical energy data
@@ -1439,7 +1531,7 @@ Added
1439
1531
  - Authentication tokens and user info
1440
1532
 
1441
1533
  - **API Client**: High-level REST API client
1442
-
1534
+
1443
1535
  - Device listing and information retrieval
1444
1536
  - Firmware information queries
1445
1537
  - Time-of-Use (TOU) schedule management
@@ -1448,7 +1540,7 @@ Added
1448
1540
  - Automatic session management
1449
1541
 
1450
1542
  - **Authentication**: AWS Cognito integration
1451
-
1543
+
1452
1544
  - Sign-in with email/password
1453
1545
  - Access token management
1454
1546
  - Token refresh functionality
@@ -1456,7 +1548,7 @@ Added
1456
1548
  - Async context manager support
1457
1549
 
1458
1550
  - **Documentation**: Complete protocol and API documentation
1459
-
1551
+
1460
1552
  - MQTT message format specifications
1461
1553
  - Energy usage query API documentation (EMS data)
1462
1554
  - API client usage guide
@@ -1468,7 +1560,7 @@ Added
1468
1560
  - Complete energy data reference (ENERGY_DATA_SUMMARY.md)
1469
1561
 
1470
1562
  - **Examples**: Production-ready example scripts
1471
-
1563
+
1472
1564
  - ``device_status_callback.py`` - Real-time status monitoring with typed callbacks
1473
1565
  - ``device_feature_callback.py`` - Device capabilities and firmware info
1474
1566
  - ``combined_callbacks.py`` - Both status and feature callbacks together
@@ -1481,7 +1573,7 @@ Changed
1481
1573
  -------
1482
1574
 
1483
1575
  - **Breaking**: Python version requirement updated to 3.9+
1484
-
1576
+
1485
1577
  - Minimum Python version is now 3.9 (was 3.8)
1486
1578
  - Migrated to native type hints (PEP 585): ``dict[str, Any]`` instead of ``Dict[str, Any]``
1487
1579
  - Removed ``typing.Dict``, ``typing.List``, ``typing.Deque`` imports
@@ -1490,7 +1582,7 @@ Changed
1490
1582
  - Updated ruff target-version to py39
1491
1583
 
1492
1584
  - **Breaking**: ``NavienAuthClient`` constructor signature
1493
-
1585
+
1494
1586
  - Now requires ``user_id`` and ``password`` as first parameters
1495
1587
  - Old: ``NavienAuthClient()`` then ``await client.sign_in(email, password)``
1496
1588
  - New: ``NavienAuthClient(email, password)`` - authentication is automatic
@@ -1499,7 +1591,7 @@ Changed
1499
1591
  - All documentation updated with new examples
1500
1592
 
1501
1593
  - **Documentation**: Major updates across all files
1502
-
1594
+
1503
1595
  - Fixed all RST formatting issues (title underlines, tables)
1504
1596
  - Updated authentication examples in 8 documentation files
1505
1597
  - Fixed broken documentation links (local file paths)
@@ -1514,7 +1606,7 @@ Fixed
1514
1606
  -----
1515
1607
 
1516
1608
  - **Critical Bug**: Thread-safe reconnection task creation from MQTT callbacks
1517
-
1609
+
1518
1610
  - Fixed ``RuntimeError: no running event loop`` when connection is interrupted
1519
1611
  - Fixed ``RuntimeWarning: coroutine '_reconnect_with_backoff' was never awaited``
1520
1612
  - Connection interruption callbacks run in separate threads without event loops
@@ -1524,7 +1616,7 @@ Fixed
1524
1616
  - Ensures reconnection tasks are properly awaited and executed
1525
1617
 
1526
1618
  - **Critical Bug**: Thread-safe event emission from MQTT callbacks
1527
-
1619
+
1528
1620
  - Fixed ``RuntimeError: no running event loop in thread 'Dummy-1'``
1529
1621
  - MQTT callbacks run in separate threads created by AWS IoT SDK
1530
1622
  - Implemented ``_schedule_coroutine()`` method for thread-safe scheduling
@@ -1534,7 +1626,7 @@ Fixed
1534
1626
  - All event emissions now work correctly from any thread
1535
1627
 
1536
1628
  - **Bug**: Incorrect method parameter passing in temperature control
1537
-
1629
+
1538
1630
  - Fixed ``set_dhw_temperature_display()`` calling ``set_dhw_temperature()`` with wrong parameters
1539
1631
  - Was passing individual parameters (``device_id``, ``device_type``, ``additional_value``)
1540
1632
  - Now correctly passes ``Device`` object as expected by method signature
@@ -1542,14 +1634,14 @@ Fixed
1542
1634
  - Updated docstrings to match actual method signatures
1543
1635
 
1544
1636
  - **Enhancement**: Anonymized MAC addresses in documentation
1545
-
1637
+
1546
1638
  - Replaced all occurrences of real MAC address (``04786332fca0``) with placeholder (``aabbccddeeff``)
1547
1639
  - Updated ``API_CLIENT.rst``, ``MQTT_CLIENT.rst``, ``MQTT_MESSAGES.rst``
1548
1640
  - Updated built HTML documentation files
1549
1641
  - Protects privacy in public documentation
1550
1642
 
1551
1643
  - **Critical Bug**: Device control command codes
1552
-
1644
+
1553
1645
  - Fixed incorrect command code usage causing unintended power-off
1554
1646
  - Power-off now uses command code ``33554433``
1555
1647
  - Power-on now uses command code ``33554434``
@@ -1557,7 +1649,7 @@ Fixed
1557
1649
  - Discovered through network traffic analysis of official app
1558
1650
 
1559
1651
  - **Critical Bug**: MQTT topic pattern matching with wildcards
1560
-
1652
+
1561
1653
  - Fixed ``_topic_matches_pattern()`` to correctly handle ``#`` wildcard
1562
1654
  - Topics now match when message arrives on base topic (e.g., ``cmd/52/device/res``)
1563
1655
  - Topics also match subtopics (e.g., ``cmd/52/device/res/extra``)
@@ -1565,21 +1657,21 @@ Fixed
1565
1657
  - Enables callbacks to receive messages correctly
1566
1658
 
1567
1659
  - **Bug**: Missing ``OperationMode.STANDBY`` enum value
1568
-
1660
+
1569
1661
  - Added ``STANDBY = 0`` to ``OperationMode`` enum
1570
1662
  - Device reports mode 0 when tank is fully charged and no heating is needed
1571
1663
  - Added graceful fallback for unknown enum values
1572
1664
  - Prevents ``ValueError`` when parsing device status
1573
1665
 
1574
1666
  - **Bug**: Insufficient topic subscriptions
1575
-
1667
+
1576
1668
  - Examples now subscribe to broader topic patterns
1577
1669
  - Subscribe to ``cmd/{device_type}/{device_topic}/#`` to catch all command messages
1578
1670
  - Subscribe to ``evt/{device_type}/{device_topic}/#`` to catch all event messages
1579
1671
  - Ensures all device responses are received
1580
1672
 
1581
1673
  - **Enhancement**: Robust enum conversion with fallbacks
1582
-
1674
+
1583
1675
  - Added try/except blocks for all enum conversions in ``DeviceStatus.from_dict()``
1584
1676
  - Added try/except blocks for all enum conversions in ``DeviceFeature.from_dict()``
1585
1677
  - Unknown operation modes default to ``STANDBY``
@@ -1592,7 +1684,7 @@ Verified
1592
1684
  --------
1593
1685
 
1594
1686
  - **Device Control**: Real-world testing with Navien NWP500 device
1595
-
1687
+
1596
1688
  - Successfully changed DHW mode from Heat Pump to Energy Saver
1597
1689
  - Successfully changed DHW mode from Energy Saver to High Demand
1598
1690
  - Successfully changed DHW temperature (discovered 20°F offset between message and display)