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