nwp500-python 9.2.1__tar.gz → 9.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 (230) hide show
  1. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/.gitignore +2 -0
  2. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/CHANGELOG.rst +263 -0
  3. {nwp500_python-9.2.1/src/nwp500_python.egg-info → nwp500_python-9.3.1}/PKG-INFO +1 -1
  4. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/explanation/index.rst +2 -0
  5. nwp500_python-9.3.1/docs/explanation/tank-energy.rst +439 -0
  6. nwp500_python-9.3.1/docs/explanation/unknown-values.rst +259 -0
  7. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/manage-units.rst +4 -4
  8. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/optimize-tou.rst +72 -57
  9. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/track-energy.rst +44 -30
  10. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/openapi.yaml +31 -1
  11. nwp500_python-9.3.1/docs/project/authors.rst +2 -0
  12. nwp500_python-9.3.1/docs/project/changelog.rst +2 -0
  13. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/project/history.rst +1 -1
  14. nwp500_python-9.3.1/docs/project/license.rst +8 -0
  15. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/index.rst +1 -0
  16. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/installation.rst +1 -1
  17. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/protocol/data_conversions.rst +18 -5
  18. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/protocol/device_status.rst +5 -5
  19. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/python_api/models.rst +63 -3
  20. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/python_api/mqtt_client.rst +4 -11
  21. nwp500_python-9.3.1/examples/.ruff.toml +31 -0
  22. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/air_filter_reset.py +1 -1
  23. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/anti_legionella.py +4 -4
  24. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/combined_callbacks.py +7 -1
  25. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/demand_response.py +2 -2
  26. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/firmware_payload_capture.py +18 -14
  27. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/mqtt_diagnostics.py +8 -5
  28. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/power_control.py +2 -2
  29. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/recirculation_control.py +3 -3
  30. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/token_restoration.py +2 -1
  31. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/tou_schedule.py +5 -5
  32. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/water_reservation.py +1 -1
  33. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/beginner/02_list_devices.py +6 -4
  34. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/beginner/03_get_status.py +2 -3
  35. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/beginner/04_set_temperature.py +1 -1
  36. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/event_driven_control.py +2 -1
  37. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/periodic_requests.py +1 -1
  38. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/set_mode.py +1 -1
  39. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/vacation_mode.py +1 -1
  40. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/mask.py +1 -1
  41. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/testing/simple_periodic_info.py +2 -3
  42. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/__init__.py +4 -0
  43. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/cli/__main__.py +74 -15
  44. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/cli/handlers.py +9 -5
  45. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/cli/presentation.py +25 -6
  46. nwp500_python-9.3.1/src/nwp500/converters.py +242 -0
  47. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/enums.py +6 -0
  48. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/models/__init__.py +6 -2
  49. nwp500_python-9.3.1/src/nwp500/models/device.py +101 -0
  50. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/models/feature.py +6 -1
  51. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/models/status.py +79 -16
  52. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/models/tou.py +6 -5
  53. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/_control_commands.py +0 -8
  54. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/_device_subscriptions.py +6 -5
  55. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/control.py +0 -32
  56. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/subscriptions.py +7 -6
  57. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt_events.py +1 -1
  58. {nwp500_python-9.2.1 → nwp500_python-9.3.1/src/nwp500_python.egg-info}/PKG-INFO +1 -1
  59. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500_python.egg-info/SOURCES.txt +4 -0
  60. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500_python.egg-info/scm_file_list.json +175 -171
  61. nwp500_python-9.3.1/src/nwp500_python.egg-info/scm_version.json +8 -0
  62. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/conftest.py +4 -2
  63. nwp500_python-9.3.1/tests/test_cli_commands.py +441 -0
  64. nwp500_python-9.3.1/tests/test_cli_tristate_presentation.py +126 -0
  65. nwp500_python-9.3.1/tests/test_device_rest_models.py +109 -0
  66. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_model_converters.py +120 -57
  67. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_models.py +210 -0
  68. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_public_api.py +13 -0
  69. nwp500_python-9.2.1/docs/project/authors.rst +0 -2
  70. nwp500_python-9.2.1/docs/project/changelog.rst +0 -2
  71. nwp500_python-9.2.1/docs/project/license.rst +0 -7
  72. nwp500_python-9.2.1/examples/.ruff.toml +0 -5
  73. nwp500_python-9.2.1/src/nwp500/converters.py +0 -154
  74. nwp500_python-9.2.1/src/nwp500/models/device.py +0 -57
  75. nwp500_python-9.2.1/src/nwp500_python.egg-info/scm_version.json +0 -8
  76. nwp500_python-9.2.1/tests/test_cli_commands.py +0 -220
  77. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/.coveragerc +0 -0
  78. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/.github/RESOLVING_PR_COMMENTS.md +0 -0
  79. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/.github/copilot-instructions.md +0 -0
  80. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/.github/workflows/ci.yml +0 -0
  81. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/.github/workflows/release.yml +0 -0
  82. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/.pre-commit-config.yaml +0 -0
  83. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/.readthedocs.yml +0 -0
  84. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/AUTHORS.rst +0 -0
  85. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/CONTRIBUTING.rst +0 -0
  86. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/LICENSE.txt +0 -0
  87. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/Makefile +0 -0
  88. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/README.rst +0 -0
  89. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/RELEASE.md +0 -0
  90. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/Makefile +0 -0
  91. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/_static/.gitignore +0 -0
  92. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/conf.py +0 -0
  93. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/explanation/advanced-features.rst +0 -0
  94. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/explanation/architecture.rst +0 -0
  95. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/authenticate.rst +0 -0
  96. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/auto-recovery.rst +0 -0
  97. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/diagnose-mqtt.rst +0 -0
  98. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/home-assistant.rst +0 -0
  99. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/index.rst +0 -0
  100. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/maintenance.rst +0 -0
  101. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/monitor-status.rst +0 -0
  102. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/queue-commands.rst +0 -0
  103. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/how-to/schedule-operation.rst +0 -0
  104. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/index.rst +0 -0
  105. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/project/contributing.rst +0 -0
  106. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/configuration.rst +0 -0
  107. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/enumerations.rst +0 -0
  108. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/protocol/device_features.rst +0 -0
  109. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/protocol/error_codes.rst +0 -0
  110. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/protocol/mqtt_protocol.rst +0 -0
  111. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/protocol/quick_reference.rst +0 -0
  112. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/protocol/rest_api.rst +0 -0
  113. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/python_api/api_client.rst +0 -0
  114. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/python_api/auth_client.rst +0 -0
  115. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/python_api/cli.rst +0 -0
  116. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/python_api/events.rst +0 -0
  117. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/reference/python_api/exceptions.rst +0 -0
  118. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/requirements.txt +0 -0
  119. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/docs/tutorials/getting-started.rst +0 -0
  120. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/README.md +0 -0
  121. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/auto_recovery.py +0 -0
  122. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/device_capabilities.py +0 -0
  123. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/device_status_debug.py +0 -0
  124. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/energy_analytics.py +0 -0
  125. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/error_code_demo.py +0 -0
  126. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/reconnection_demo.py +0 -0
  127. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/reservation_schedule.py +0 -0
  128. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/simple_auto_recovery.py +0 -0
  129. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/advanced/tou_openei.py +0 -0
  130. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/beginner/01_authentication.py +0 -0
  131. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/advanced_auth_patterns.py +0 -0
  132. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/command_queue.py +0 -0
  133. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/device_status_callback.py +0 -0
  134. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/error_handling.py +0 -0
  135. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/improved_auth.py +0 -0
  136. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/legacy_auth_constructor.py +0 -0
  137. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/intermediate/mqtt_realtime_monitoring.py +0 -0
  138. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/testing/periodic_device_info.py +0 -0
  139. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/testing/test_api_client.py +0 -0
  140. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/testing/test_mqtt_connection.py +0 -0
  141. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/testing/test_mqtt_messaging.py +0 -0
  142. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/examples/testing/test_periodic_minimal.py +0 -0
  143. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/pyproject.toml +0 -0
  144. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/scripts/README.md +0 -0
  145. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/scripts/bump_version.py +0 -0
  146. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/scripts/diagnose_mqtt_connection.py +0 -0
  147. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/scripts/extract_changelog.py +0 -0
  148. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/scripts/format.py +0 -0
  149. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/scripts/lint.py +0 -0
  150. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/scripts/setup-dev.py +0 -0
  151. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/scripts/validate_version.py +0 -0
  152. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/setup.cfg +0 -0
  153. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/setup.py +0 -0
  154. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/_base.py +0 -0
  155. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/api_client.py +0 -0
  156. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/auth.py +0 -0
  157. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/cli/__init__.py +0 -0
  158. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/cli/monitoring.py +0 -0
  159. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/cli/output_formatters.py +0 -0
  160. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/cli/rich_output.py +0 -0
  161. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/cli/token_storage.py +0 -0
  162. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/command_decorators.py +0 -0
  163. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/config.py +0 -0
  164. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/device_capabilities.py +0 -0
  165. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/device_info_cache.py +0 -0
  166. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/encoding.py +0 -0
  167. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/events.py +0 -0
  168. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/exceptions.py +0 -0
  169. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/factory.py +0 -0
  170. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/field_factory.py +0 -0
  171. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/models/_converters.py +0 -0
  172. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/models/energy.py +0 -0
  173. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/models/mqtt_models.py +0 -0
  174. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/models/schedule.py +0 -0
  175. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/__init__.py +0 -0
  176. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/client.py +0 -0
  177. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/command_queue.py +0 -0
  178. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/connection.py +0 -0
  179. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/diagnostics.py +0 -0
  180. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/periodic.py +0 -0
  181. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/reconnection.py +0 -0
  182. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/state_tracker.py +0 -0
  183. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/types.py +0 -0
  184. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/mqtt/utils.py +0 -0
  185. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/openei.py +0 -0
  186. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/py.typed +0 -0
  187. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/reservations.py +0 -0
  188. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/temperature.py +0 -0
  189. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/topic_builder.py +0 -0
  190. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/tou_schedule.py +0 -0
  191. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/unit_system.py +0 -0
  192. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500/utils.py +0 -0
  193. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  194. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  195. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  196. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500_python.egg-info/requires.txt +0 -0
  197. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/src/nwp500_python.egg-info/top_level.txt +0 -0
  198. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_api_helpers.py +0 -0
  199. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_auth.py +0 -0
  200. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_auth_session_lifecycle.py +0 -0
  201. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_bug_fixes.py +0 -0
  202. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_canonical_schedule.py +0 -0
  203. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_cli_basic.py +0 -0
  204. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_command_decorators.py +0 -0
  205. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_command_queue.py +0 -0
  206. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_device_capabilities.py +0 -0
  207. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_device_info_cache.py +0 -0
  208. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_events.py +0 -0
  209. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_exceptions.py +0 -0
  210. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_mqtt_clean_session_resume.py +0 -0
  211. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_mqtt_client_init.py +0 -0
  212. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_mqtt_events.py +0 -0
  213. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_mqtt_hypothesis.py +0 -0
  214. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_mqtt_reconnection.py +0 -0
  215. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_mqtt_reconnection_storm.py +0 -0
  216. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_mqtt_reliability.py +0 -0
  217. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_multi_device.py +0 -0
  218. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_openei.py +0 -0
  219. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_protocol_correctness.py +0 -0
  220. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_reservations.py +0 -0
  221. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_temperature_converters.py +0 -0
  222. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_threading_model.py +0 -0
  223. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_token_storage.py +0 -0
  224. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_tou_api.py +0 -0
  225. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_tou_schedule.py +0 -0
  226. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_unit_switching.py +0 -0
  227. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_unit_system_process_wide.py +0 -0
  228. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_utility_modules.py +0 -0
  229. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tests/test_utils.py +0 -0
  230. {nwp500_python-9.2.1 → nwp500_python-9.3.1}/tox.ini +0 -0
@@ -49,6 +49,8 @@ build/*
49
49
  dist/*
50
50
  sdist/*
51
51
  docs/api/*
52
+ docs/reference/api/*
53
+ .obsidian/
52
54
  docs/_rst/*
53
55
  docs/_build/*
54
56
  cover/*
@@ -5,6 +5,269 @@ Changelog
5
5
  Unreleased
6
6
  ==========
7
7
 
8
+ Version 9.3.1 (2026-08-29)
9
+ ==========================
10
+
11
+ ``NavienMqttClient.request_tou_settings()`` is removed. Not a breaking
12
+ change in practice: the device never answered it, so no caller can have
13
+ been getting a schedule out of it. Code that still names the method will
14
+ now fail with ``AttributeError`` rather than time out. See Removed below.
15
+
16
+ Added
17
+ -----
18
+ - **REST fields the cloud added since the models were written.**
19
+ ``/device/list`` now returns an ``error`` block (``errorCode``,
20
+ ``errorOccuredTime``) and the ``descaling`` block previously seen only
21
+ on ``/device/info``, and ``deviceInfo`` gained ``modelTypeCode`` and
22
+ ``installerId``. None of these were modelled, and ``NavienBaseModel``
23
+ ignores unknown keys, so all of them were silently discarded.
24
+ :class:`~nwp500.models.Device` gains optional ``error``
25
+ (:class:`~nwp500.models.DeviceErrorSummary`) and ``descaling``
26
+ (:class:`~nwp500.models.DescalingInfo`) sections, and
27
+ :class:`~nwp500.models.DeviceInfo` gains ``model_type_code`` and
28
+ ``installer_id``. ``error`` makes the device's last recorded fault
29
+ readable without an MQTT connection, including while the device is
30
+ offline. Every new field is optional, so responses that omit them - such
31
+ as ``/device/info``, which carries no ``error`` block - parse unchanged.
32
+ ``error_code`` is typed ``ErrorCode | int`` (validated left to right, so a
33
+ known code becomes an ``ErrorCode`` member and only an unknown one stays a
34
+ plain int) so an unrecognised code cannot make a whole listing unparseable. ``docs/openapi.yaml`` is
35
+ updated to match.
36
+
37
+ Removed
38
+ -------
39
+ - **``request_tou_settings()`` removed - the device has no MQTT read for its
40
+ TOU schedule.** The method published a ``CommandCode.TOU_RESERVATION``
41
+ message to ``ctrl/tou/rd`` carrying only ``controllerSerialNumber``, then
42
+ waited for a reply on ``res/tou/rd``. No reply ever comes: a live device
43
+ with TOU provisioned (``program_reservation_use`` true, a valid controller
44
+ serial, and a plan the REST API returns in full) stayed silent for 45
45
+ seconds. ``ctrl/tou/rd`` with that command code is the *write* - it is what
46
+ :meth:`~nwp500.NavienMqttClient.configure_tou_schedule` publishes and what
47
+ the vendor app publishes from its TOU editor
48
+ (``TouScheduleViewmodel.setPublishMgppControlTou``) - and the device answers
49
+ on ``res/tou/rd`` only to confirm such a write. The vendor app reads TOU
50
+ over REST, which is the only TOU read the protocol has.
51
+
52
+ Beyond returning nothing, the call published a write-shaped command with no
53
+ schedule attached. This device ignored it - ``touStatus`` was unchanged
54
+ across repeated calls - but a firmware that took it at face value could read
55
+ it as "store an empty TOU schedule".
56
+
57
+ **Migration**: use :meth:`~nwp500.NavienAPIClient.get_tou_info`, which
58
+ returns the stored plan - rate name, utility, ZIP code and the seasonal
59
+ pricing intervals. The read itself is pure REST; it is keyed by the
60
+ controller serial number, which only the MQTT device-info response
61
+ publishes, so fetch that once and cache it.
62
+ :meth:`~nwp500.NavienMqttClient.subscribe_tou_response` is unaffected and
63
+ still delivers write confirmations. Enabling and disabling TOU
64
+ (:meth:`~nwp500.NavienMqttClient.set_tou_enabled`, command codes
65
+ ``TOU_ON``/``TOU_OFF``) is a separate path and is unaffected.
66
+
67
+ Fixed
68
+ -----
69
+ - **CLI ``energy --months`` no longer duplicates ``--month`` output.**
70
+ ``handle_get_energy_request`` picked the view from the length of the
71
+ month list, so ``--months 5`` took the single-month branch and printed
72
+ the same daily breakdown as ``--month 5`` instead of the monthly
73
+ summary. The handler now takes an explicit ``daily`` flag set by the
74
+ option the user actually passed. ``energy`` also rejects ``--month``
75
+ and ``--months`` together rather than silently preferring ``--month``,
76
+ and validates the ``--months`` range and format the way ``--month``
77
+ already did. All of that validation now happens while Click parses the
78
+ arguments, so a bad invocation is a usage error (exit code 2) before any
79
+ authentication or MQTT connection is attempted - previously the command
80
+ body raised ``ClickException`` after connecting, where ``async_command``'s
81
+ catch-all reported it as an "Unexpected Error" with a traceback, and
82
+ anyone without working credentials saw an authentication failure instead
83
+ of the usage error. That catch-all now re-raises ``ClickException`` so any
84
+ command can report a usage error as itself.
85
+ - **``DeviceFeature.mixing_valve_use`` now reads the device's actual key.**
86
+ The field was aliased to ``mixingValveUse``, but the device sends
87
+ ``mixingValueUse`` - "Value", not "Valve" - and the base model ignores
88
+ unknown keys, so the field silently reported its ``False`` default on
89
+ every device regardless of whether a thermostatic mixing valve was
90
+ supported. The CLI's "Mixing Valve" capability row was wrong for the
91
+ same reason. The misspelling is Navien's own: it appears in the vendor
92
+ app's ``KDResponseMgppDid$Feature`` class and in
93
+ ``docs/reference/protocol/device_features.rst``, which documented the
94
+ wire spelling correctly all along.
95
+
96
+ - **``firmware_payload_capture.py`` captured almost nothing.** Its wildcards
97
+ covered ``cmd/{type}/{client_id}/res/#`` and the event topic, but the device
98
+ acknowledges control and query commands on
99
+ ``cmd/{type}/navilink-{mac}/{client_id}/res`` - which neither pattern
100
+ matches - so the device status and device info responses were missed, along
101
+ with every request published on the device path. A capture run recorded 2
102
+ payloads where it should have recorded 11. It now also subscribes to
103
+ ``cmd/{type}/navilink-{mac}/#``, which additionally picks up traffic from
104
+ other clients on the same device. Query results the device routes back to a
105
+ client-keyed topic, such as reservations and energy usage, were already
106
+ covered by the existing response wildcard.
107
+ Version 9.3.0 (2026-08-03)
108
+ ==========================
109
+
110
+ **BREAKING CHANGES**: two independent corrections land together.
111
+
112
+ Tank energy values were wrong in two ways - a 2.5x unit-scale error and
113
+ two actively misleading field names - and both are corrected. Reported
114
+ tank energy is now 2.5x smaller and two public field names are removed.
115
+
116
+ Separately, eight status flags change type from ``bool`` to
117
+ ``bool | None`` so the device's "unknown" state is no longer reported as
118
+ a definite OFF.
119
+
120
+ Changed
121
+ -------
122
+ - **Energy unit scale corrected.** ``totalEnergyCapacity`` and ``availableEnergyCapacity`` were
123
+ scaled by 10 on the assumption the device reported 10 Wh units. It does
124
+ not. Because ``totalEnergyCapacity`` is a whole-tank quantity, its slope
125
+ against the setpoint measures the quantum with no stratification
126
+ assumption: on a 65-gallon NWP500 that is 70.25 raw counts per Kelvin.
127
+ The field turns out to be bimodal - at a fixed setpoint it takes one of
128
+ two values exactly 2 degC apart - but both branches give the same slope
129
+ to within 0.2%, so the quantum is unaffected. Converting to Watt-hours needs a
130
+ water mass, and a "65 gallon" tank does not hold 65 gallons - so taking
131
+ the quantum to be round, as every other conversion in this protocol is,
132
+ 4 Wh/count is the only candidate implying a water volume below the
133
+ nameplate (241.7 L). Two further checks agree: 183 individual heating
134
+ recoveries give 4.11 Wh/count by a noisier route, and integrating
135
+ ``currentInstPower`` over them implies a heat-pump COP of 2.89 at the new
136
+ scale against 7.02 at the old, the latter being physically impossible.
137
+ **Reported tank energy is now 2.5x smaller.**
138
+ Historical series logged from earlier versions need rescaling by 0.4 to
139
+ be comparable.
140
+
141
+ - **Energy fields renamed.**
142
+ ``availableEnergyCapacity`` is not available energy - it is the energy
143
+ still *needed* to reach the setpoint. It falls as the tank heats and
144
+ reaches zero when the tank is fully charged, so code treating it as
145
+ stored energy had the signal backwards (regression against mean tank
146
+ temperature: negative slope, R-squared 0.93, zero crossing at the
147
+ setpoint). Likewise ``totalEnergyCapacity`` is not a fixed tank size but
148
+ the cost of a full recovery to the *current setpoint*, measured from the
149
+ device's own minimum setpoint of 104.9 degF; it moves by about 140 Wh
150
+ per 0.5 degC of setpoint change.
151
+
152
+ .. code-block:: python
153
+
154
+ # OLD (removed)
155
+ status.total_energy_capacity # 15800.0
156
+ status.available_energy_capacity # 11660.0
157
+
158
+ # NEW
159
+ status.full_recovery_energy # 6320.0
160
+ status.energy_to_setpoint # 4664.0
161
+
162
+ The protocol field names on the wire are unchanged. CLI rows are
163
+ relabelled from "Total Capacity"/"Available Capacity" to
164
+ "Full Recovery"/"Energy to Setpoint".
165
+
166
+ - **Status flags now preserve the device's unknown state.** The protocol
167
+ encodes these flags as ``0 = unknown, 1 = OFF, 2 = ON``, and the library
168
+ was collapsing 0 to ``False`` - inventing an OFF the device never claimed.
169
+ Confirmed against Navien's own NaviLink app (2.03.00, versionCode 141),
170
+ which decodes exactly this set of fields through an enum declared
171
+ ``UNKNOWN(0), OFF(1), ON(2)``; two sibling enums render their zero as
172
+ ``"-"`` and ``"Not Applied"`` rather than as an off state.
173
+
174
+ Affected: ``operation_busy``, ``comp_use``, ``anti_legionella_use``,
175
+ ``anti_legionella_operation_busy``, ``heat_upper_use``, ``heat_lower_use``,
176
+ ``air_filter_alarm_use``, ``recirc_reservation_use``.
177
+
178
+ ``None`` is falsy, so ``if status.comp_use:`` is unaffected. Code that
179
+ distinguishes ``is False`` from "not reported", or does arithmetic or
180
+ formatting on these fields, needs a ``None`` check. For Home Assistant
181
+ this is the wanted shape: ``None`` renders as "Unknown" instead of writing
182
+ a fabricated OFF into the recorder database.
183
+
184
+ - ``OnOffFlag`` gains the vendor's ``UNKNOWN = 0`` member. It previously
185
+ started at ``OFF = 1``, leaving the device's reserved value unrepresented.
186
+
187
+ - The CLI renders these flags as ``Unknown`` rather than ``No``. Both
188
+ affected rows are updated: "Busy" under OPERATION STATUS and
189
+ "Operation Busy" under ANTI-LEGIONELLA.
190
+
191
+ Added
192
+ -----
193
+ - **``DeviceStatus.usable_energy``**: drawable energy in Watt-hours,
194
+ computed as ``full_recovery_energy - energy_to_setpoint``. Both raw
195
+ fields are measured from the setpoint, so neither is a state of charge;
196
+ subtracting them cancels the setpoint and leaves the tank's heat above
197
+ the device's minimum operating temperature (104.9 degF), which is about
198
+ the lowest temperature usable for a shower. Robust despite
199
+ ``full_recovery_energy`` being bimodal, since both fields shift
200
+ together: the implied tank temperature tracks the thermistor mean to a
201
+ standard deviation of 0.57 degF over 12275 samples. Rendered by the CLI
202
+ as "Usable Energy".
203
+
204
+ - ``converters.device_tristate_to_python`` and
205
+ ``models.status.DeviceTriState`` for flags the device may decline to
206
+ report. ``converters.device_bool_to_python`` is unchanged and remains
207
+ correct for capability flags.
208
+ - New ``docs/explanation/unknown-values.rst`` recording which field families
209
+ use 0 as a sentinel and which do not, with the app evidence for each.
210
+
211
+ Removed
212
+ -------
213
+ - **Misnamed energy fields**: removed ``DeviceStatus.total_energy_capacity``
214
+ and ``DeviceStatus.available_energy_capacity`` outright rather than
215
+ aliasing them, so a missed rename fails with ``AttributeError`` instead
216
+ of silently returning a number 2.5x too large. Use
217
+ ``full_recovery_energy`` and ``energy_to_setpoint``.
218
+
219
+ - **Incorrect converter**: removed ``converters.mul_10``, which existed
220
+ only to apply the wrong 10 Wh scale. Use
221
+ ``converters.energy_count_to_wh`` and ``converters.WH_PER_ENERGY_COUNT``.
222
+
223
+ Fixed
224
+ -----
225
+ - **Documentation contradicted itself and the code on energy capacity.**
226
+ Three incompatible descriptions were published: Watt-hours with no
227
+ conversion (protocol reference), Watt-hours with a x10 scale (the code),
228
+ and a 0-100 percentage (``how-to/track-energy.rst``,
229
+ ``reference/python_api/models.rst``, ``project/history.rst``). The
230
+ percentage claim was never true of any library version. All are now
231
+ consistent.
232
+ - ``how-to/track-energy.rst`` documented four fields that do not exist on
233
+ ``DeviceStatus`` (``dhw_tank_upper_temp``, ``dhw_tank_lower_temp``,
234
+ ``comp_temp``, ``dhw_heatex_out_temp``); replaced with the real names.
235
+ - ``dhwTemperature`` is documented as an outlet temperature but is
236
+ measured inside the tank: it tracks ``tankUpperTemperature`` to within
237
+ one 0.5 degC step, and the device has no sensor downstream of itself.
238
+ Navien's own app agrees, labelling it "DHW Temp." beside the tank
239
+ thermistors and showing ``dischargeTemperature`` separately.
240
+ - ``docs/explanation/tank-energy.rst`` tabulated ``totalEnergyCapacity``
241
+ and ``availableEnergyCapacity`` at ten times their raw wire values,
242
+ under column headings naming the raw protocol fields. The series had
243
+ been logged through the pre-fix library, which multiplied by 10. The
244
+ prose beside the tables ("70.25 raw counts per Kelvin", "35 counts")
245
+ was already in true raw counts, so the page contradicted itself by
246
+ exactly the factor it exists to correct. Tables are now in raw counts;
247
+ 1580 counts at a 145.4 degF setpoint matches the test fixtures and the
248
+ 6320 Wh the CLI reports. No numeric conclusion changes.
249
+ - ``docs/explanation/tank-energy.rst`` referred to a field
250
+ ``hpUpperOnTemperatureSetting``; the protocol name is
251
+ ``hpUpperOnTempSetting``.
252
+ - The setpoint-per-0.5-degC figure is 140 Wh, not 143 Wh, which is what
253
+ the documented slope of 70.25 counts/K gives at 4 Wh/count. Corrected
254
+ in the changelog, the model field description and the protocol
255
+ reference.
256
+ - New ``docs/explanation/tank-energy.rst`` records what the two fields
257
+ actually measure and the calibration evidence behind the scale change,
258
+ including that Navien's own NaviLink app (2.03.00, versionCode 141)
259
+ reads neither field, so no vendor-side corroboration exists.
260
+
261
+ - Documented that **temperature fields carry no sentinel at all**. The app
262
+ has no out-of-band constant (no ``0xFFFF``/``-999``/``-1``), no zero-guard
263
+ in any display path, and formats whatever arrives - so a temperature of
264
+ zero means zero. This closes a recurring source of bugs where zero-as-none
265
+ was applied to temperature converters and reported working sensors as
266
+ missing during cold-weather operation.
267
+ - Documented that capability flags are a distinct case: the app hides a
268
+ feature's entire UI when its DID ``Use`` flag reads 0, so 0 there means
269
+ "not fitted" and the existing ``bool`` mapping is correct.
270
+
8
271
  Version 9.2.1 (2026-07-30)
9
272
  ==========================
10
273
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 9.2.1
3
+ Version: 9.3.1
4
4
  Summary: A library for controlling Navien NWP500 Water Heaters via NaviLink
5
5
  Home-page: https://github.com/eman/nwp500-python
6
6
  Author: Emmanuel Levijarvi
@@ -9,3 +9,5 @@ Understanding-oriented deep dives into the library's design and advanced feature
9
9
 
10
10
  advanced-features
11
11
  architecture
12
+ tank-energy
13
+ unknown-values