nwp500-python 9.5.0__tar.gz → 9.5.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 (339) hide show
  1. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/CHANGELOG.rst +14 -0
  2. {nwp500_python-9.5.0/src/nwp500_python.egg-info → nwp500_python-9.5.1}/PKG-INFO +2 -4
  3. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/README.rst +1 -3
  4. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim_home_assistant.py +8 -4
  5. {nwp500_python-9.5.0 → nwp500_python-9.5.1/src/nwp500_python.egg-info}/PKG-INFO +2 -4
  6. nwp500_python-9.5.1/src/nwp500_python.egg-info/scm_version.json +8 -0
  7. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_home_assistant.py +15 -3
  8. nwp500_python-9.5.0/src/nwp500_python.egg-info/scm_version.json +0 -8
  9. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/.coveragerc +0 -0
  10. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/.github/RESOLVING_PR_COMMENTS.md +0 -0
  11. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/.github/copilot-instructions.md +0 -0
  12. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/.github/workflows/ci.yml +0 -0
  13. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/.github/workflows/release.yml +0 -0
  14. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/.gitignore +0 -0
  15. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/.pre-commit-config.yaml +0 -0
  16. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/.readthedocs.yml +0 -0
  17. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/AUTHORS.rst +0 -0
  18. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/CONTRIBUTING.rst +0 -0
  19. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/LICENSE.txt +0 -0
  20. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/Makefile +0 -0
  21. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/RELEASE.md +0 -0
  22. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/Makefile +0 -0
  23. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/_static/.gitignore +0 -0
  24. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/conf.py +0 -0
  25. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/architecture.rst +0 -0
  26. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/draws.rst +0 -0
  27. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/heating-elements.rst +0 -0
  28. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/index.rst +0 -0
  29. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/recovery-speed.rst +0 -0
  30. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/simulator.rst +0 -0
  31. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/standby-loss.rst +0 -0
  32. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/tank-energy.rst +0 -0
  33. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/tou-recovery-cap.rst +0 -0
  34. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/unknown-values.rst +0 -0
  35. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/explanation/what-starts-a-recovery.rst +0 -0
  36. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/authenticate.rst +0 -0
  37. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/auto-recovery.rst +0 -0
  38. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/bluetooth.rst +0 -0
  39. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/calibrate-simulator.rst +0 -0
  40. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/diagnose-mqtt.rst +0 -0
  41. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/home-assistant.rst +0 -0
  42. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/index.rst +0 -0
  43. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/maintenance.rst +0 -0
  44. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/manage-units.rst +0 -0
  45. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/monitor-status.rst +0 -0
  46. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/optimize-tou.rst +0 -0
  47. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/queue-commands.rst +0 -0
  48. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/schedule-operation.rst +0 -0
  49. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/simulate-heater.rst +0 -0
  50. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/how-to/track-energy.rst +0 -0
  51. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/index.rst +0 -0
  52. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/openapi.yaml +0 -0
  53. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/project/authors.rst +0 -0
  54. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/project/changelog.rst +0 -0
  55. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/project/contributing.rst +0 -0
  56. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/project/history.rst +0 -0
  57. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/project/license.rst +0 -0
  58. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/configuration.rst +0 -0
  59. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/enumerations.rst +0 -0
  60. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/index.rst +0 -0
  61. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/installation.rst +0 -0
  62. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/protocol/data_conversions.rst +0 -0
  63. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/protocol/device_features.rst +0 -0
  64. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/protocol/device_status.rst +0 -0
  65. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/protocol/error_codes.rst +0 -0
  66. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/protocol/mqtt_protocol.rst +0 -0
  67. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/protocol/quick_reference.rst +0 -0
  68. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/protocol/rest_api.rst +0 -0
  69. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/python_api/api_client.rst +0 -0
  70. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/python_api/auth_client.rst +0 -0
  71. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/python_api/cli.rst +0 -0
  72. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/python_api/events.rst +0 -0
  73. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/python_api/exceptions.rst +0 -0
  74. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/python_api/models.rst +0 -0
  75. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/python_api/mqtt_client.rst +0 -0
  76. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/python_api/sim.rst +0 -0
  77. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/reference/simulator-inputs.rst +0 -0
  78. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/requirements.txt +0 -0
  79. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/docs/tutorials/getting-started.rst +0 -0
  80. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/.ruff.toml +0 -0
  81. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/README.md +0 -0
  82. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/air_filter_reset.py +0 -0
  83. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/anti_legionella.py +0 -0
  84. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/auto_recovery.py +0 -0
  85. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/combined_callbacks.py +0 -0
  86. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/demand_response.py +0 -0
  87. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/device_capabilities.py +0 -0
  88. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/device_status_debug.py +0 -0
  89. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/energy_analytics.py +0 -0
  90. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/error_code_demo.py +0 -0
  91. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/firmware_payload_capture.py +0 -0
  92. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/mqtt_diagnostics.py +0 -0
  93. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/power_control.py +0 -0
  94. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/recirculation_control.py +0 -0
  95. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/reconnection_demo.py +0 -0
  96. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/reservation_schedule.py +0 -0
  97. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/simple_auto_recovery.py +0 -0
  98. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/token_restoration.py +0 -0
  99. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/tou_openei.py +0 -0
  100. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/tou_schedule.py +0 -0
  101. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/advanced/water_reservation.py +0 -0
  102. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/beginner/01_authentication.py +0 -0
  103. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/beginner/02_list_devices.py +0 -0
  104. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/beginner/03_get_status.py +0 -0
  105. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/beginner/04_set_temperature.py +0 -0
  106. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/advanced_auth_patterns.py +0 -0
  107. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/command_queue.py +0 -0
  108. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/device_status_callback.py +0 -0
  109. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/error_handling.py +0 -0
  110. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/event_driven_control.py +0 -0
  111. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/improved_auth.py +0 -0
  112. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/legacy_auth_constructor.py +0 -0
  113. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/mqtt_realtime_monitoring.py +0 -0
  114. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/periodic_requests.py +0 -0
  115. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/set_mode.py +0 -0
  116. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/intermediate/vacation_mode.py +0 -0
  117. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/mask.py +0 -0
  118. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/simulator/lower-setpoint.json +0 -0
  119. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/simulator/plan.json +0 -0
  120. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/simulator/scenario.json +0 -0
  121. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/testing/periodic_device_info.py +0 -0
  122. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/testing/simple_periodic_info.py +0 -0
  123. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/testing/test_api_client.py +0 -0
  124. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/testing/test_mqtt_connection.py +0 -0
  125. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/testing/test_mqtt_messaging.py +0 -0
  126. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/examples/testing/test_periodic_minimal.py +0 -0
  127. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/pyproject.toml +0 -0
  128. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/scripts/README.md +0 -0
  129. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/scripts/bump_version.py +0 -0
  130. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/scripts/diagnose_mqtt_connection.py +0 -0
  131. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/scripts/extract_changelog.py +0 -0
  132. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/scripts/format.py +0 -0
  133. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/scripts/lint.py +0 -0
  134. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/scripts/setup-dev.py +0 -0
  135. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/scripts/validate_version.py +0 -0
  136. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/setup.cfg +0 -0
  137. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/setup.py +0 -0
  138. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/__init__.py +0 -0
  139. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/_base.py +0 -0
  140. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/api_client.py +0 -0
  141. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/auth.py +0 -0
  142. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/ble/__init__.py +0 -0
  143. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/ble/__main__.py +0 -0
  144. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/ble/client.py +0 -0
  145. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/ble/mqtt_adapter.py +0 -0
  146. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/ble/protocol.py +0 -0
  147. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/__init__.py +0 -0
  148. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/__main__.py +0 -0
  149. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/ble_backend.py +0 -0
  150. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/calibration.py +0 -0
  151. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/handlers.py +0 -0
  152. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/monitoring.py +0 -0
  153. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/output_formatters.py +0 -0
  154. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/presentation.py +0 -0
  155. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/recording.py +0 -0
  156. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/rich_output.py +0 -0
  157. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/simulate.py +0 -0
  158. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/cli/token_storage.py +0 -0
  159. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/command_decorators.py +0 -0
  160. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/config.py +0 -0
  161. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/converters.py +0 -0
  162. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/device_capabilities.py +0 -0
  163. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/device_info_cache.py +0 -0
  164. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/encoding.py +0 -0
  165. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/enums.py +0 -0
  166. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/events.py +0 -0
  167. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/exceptions.py +0 -0
  168. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/factory.py +0 -0
  169. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/field_factory.py +0 -0
  170. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/__init__.py +0 -0
  171. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/_converters.py +0 -0
  172. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/account.py +0 -0
  173. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/device.py +0 -0
  174. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/diagnostics.py +0 -0
  175. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/energy.py +0 -0
  176. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/feature.py +0 -0
  177. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/firmware.py +0 -0
  178. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/maintenance.py +0 -0
  179. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/mqtt_models.py +0 -0
  180. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/schedule.py +0 -0
  181. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/status.py +0 -0
  182. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/models/tou.py +0 -0
  183. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/__init__.py +0 -0
  184. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/_control_commands.py +0 -0
  185. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/_device_subscriptions.py +0 -0
  186. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/client.py +0 -0
  187. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/command_queue.py +0 -0
  188. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/connection.py +0 -0
  189. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/control.py +0 -0
  190. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/diagnostics.py +0 -0
  191. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/periodic.py +0 -0
  192. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/reconnection.py +0 -0
  193. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/state_tracker.py +0 -0
  194. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/subscriptions.py +0 -0
  195. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/types.py +0 -0
  196. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt/utils.py +0 -0
  197. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/mqtt_events.py +0 -0
  198. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/openei.py +0 -0
  199. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/py.typed +0 -0
  200. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/reservations.py +0 -0
  201. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/__init__.py +0 -0
  202. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/api.py +0 -0
  203. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/attribution.py +0 -0
  204. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/book.py +0 -0
  205. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/calibration.py +0 -0
  206. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/circulation.py +0 -0
  207. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/codec.py +0 -0
  208. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/cold_supply.py +0 -0
  209. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/controller.py +0 -0
  210. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/decay.py +0 -0
  211. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/defaults.py +0 -0
  212. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/delivery.py +0 -0
  213. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/draw_forecast.py +0 -0
  214. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/engine.py +0 -0
  215. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/extensions.py +0 -0
  216. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/heat_pump_recovery.py +0 -0
  217. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/heating_trajectory.py +0 -0
  218. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/inputs.py +0 -0
  219. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/location_air.py +0 -0
  220. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/meter.py +0 -0
  221. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/observed.py +0 -0
  222. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/params.py +0 -0
  223. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/physics/__init__.py +0 -0
  224. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/physics/compressor.py +0 -0
  225. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/physics/draw.py +0 -0
  226. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/physics/element.py +0 -0
  227. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/physics/refrigerant.py +0 -0
  228. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/physics/tank.py +0 -0
  229. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/profile.py +0 -0
  230. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/ready.py +0 -0
  231. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/record.py +0 -0
  232. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/result.py +0 -0
  233. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/schedules.py +0 -0
  234. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/tank_physics.py +0 -0
  235. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/tou_triggers.py +0 -0
  236. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim/units.py +0 -0
  237. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim_adapters.py +0 -0
  238. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/sim_live.py +0 -0
  239. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/status_history.py +0 -0
  240. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/temperature.py +0 -0
  241. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/topic_builder.py +0 -0
  242. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/tou_schedule.py +0 -0
  243. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/unit_system.py +0 -0
  244. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500/utils.py +0 -0
  245. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500_python.egg-info/SOURCES.txt +0 -0
  246. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  247. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  248. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  249. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500_python.egg-info/requires.txt +0 -0
  250. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500_python.egg-info/scm_file_list.json +0 -0
  251. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/src/nwp500_python.egg-info/top_level.txt +0 -0
  252. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/_sim_site.py +0 -0
  253. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/conftest.py +0 -0
  254. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/fixtures/sim/engine_golden.json +0 -0
  255. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/fixtures/sim/heater_params.json +0 -0
  256. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/fixtures/sim/heater_params_fitted.json +0 -0
  257. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_api_endpoints_201.py +0 -0
  258. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_api_helpers.py +0 -0
  259. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_app_queries.py +0 -0
  260. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_auth.py +0 -0
  261. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_auth_session_lifecycle.py +0 -0
  262. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_auth_session_refresh.py +0 -0
  263. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_ble_mqtt_adapter.py +0 -0
  264. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_ble_protocol.py +0 -0
  265. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_bug_fixes.py +0 -0
  266. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_canonical_schedule.py +0 -0
  267. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_cli_basic.py +0 -0
  268. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_cli_calibrate.py +0 -0
  269. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_cli_commands.py +0 -0
  270. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_cli_simulate.py +0 -0
  271. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_cli_tristate_presentation.py +0 -0
  272. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_command_decorators.py +0 -0
  273. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_command_queue.py +0 -0
  274. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_device_capabilities.py +0 -0
  275. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_device_info_cache.py +0 -0
  276. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_device_rest_models.py +0 -0
  277. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_events.py +0 -0
  278. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_exceptions.py +0 -0
  279. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_model_converters.py +0 -0
  280. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_models.py +0 -0
  281. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_clean_session_resume.py +0 -0
  282. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_client_init.py +0 -0
  283. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_control_fail.py +0 -0
  284. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_events.py +0 -0
  285. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_hypothesis.py +0 -0
  286. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_last_will.py +0 -0
  287. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_reconnection.py +0 -0
  288. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_reconnection_storm.py +0 -0
  289. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_mqtt_reliability.py +0 -0
  290. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_multi_device.py +0 -0
  291. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_navilink_201_enums.py +0 -0
  292. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_openei.py +0 -0
  293. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_protocol_correctness.py +0 -0
  294. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_public_api.py +0 -0
  295. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_reservation_validation.py +0 -0
  296. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_reservations.py +0 -0
  297. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_adapters.py +0 -0
  298. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_api.py +0 -0
  299. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_book.py +0 -0
  300. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_book_history.py +0 -0
  301. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_calibration.py +0 -0
  302. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_circulation.py +0 -0
  303. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_codec.py +0 -0
  304. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_cold_supply.py +0 -0
  305. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_controller.py +0 -0
  306. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_defaults.py +0 -0
  307. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_draw_forecast.py +0 -0
  308. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_draw_loss.py +0 -0
  309. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_element.py +0 -0
  310. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_element_fit.py +0 -0
  311. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_forecast.py +0 -0
  312. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_golden.py +0 -0
  313. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_hot_water.py +0 -0
  314. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_import_contract.py +0 -0
  315. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_live.py +0 -0
  316. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_location_air.py +0 -0
  317. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_meter.py +0 -0
  318. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_no_clock_no_state.py +0 -0
  319. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_observed.py +0 -0
  320. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_profile.py +0 -0
  321. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_readiness.py +0 -0
  322. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_readiness_fit.py +0 -0
  323. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_ready.py +0 -0
  324. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_ready_api.py +0 -0
  325. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_replay.py +0 -0
  326. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_schedules.py +0 -0
  327. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_sim_tank_physics.py +0 -0
  328. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_status_history.py +0 -0
  329. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_temperature_converters.py +0 -0
  330. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_threading_model.py +0 -0
  331. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_token_storage.py +0 -0
  332. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_tou_api.py +0 -0
  333. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_tou_schedule.py +0 -0
  334. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_unit_switching.py +0 -0
  335. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_unit_system_process_wide.py +0 -0
  336. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_utility_modules.py +0 -0
  337. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tests/test_utils.py +0 -0
  338. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/tox.ini +0 -0
  339. {nwp500_python-9.5.0 → nwp500_python-9.5.1}/uv.lock +0 -0
@@ -2,6 +2,20 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 9.5.1 (2026-10-21)
6
+ ==========================
7
+
8
+ Fixed
9
+ -----
10
+ - **A steady Home Assistant sensor no longer goes stale in the
11
+ simulator** (#174). ``sim_home_assistant.parse_state`` dated a state by
12
+ ``last_updated``, which moves only when the value or its attributes
13
+ change. An air sensor holding the same value for over an hour failed
14
+ ``fresh()``, and the simulation fell back to the default air
15
+ temperature. A state is now dated by ``last_reported`` (Home Assistant
16
+ 2024.3 and later), then ``last_updated``, then ``last_changed``. The
17
+ history parsers still date each change row by its change.
18
+
5
19
  Version 9.5.0 (2026-10-20)
6
20
  ==========================
7
21
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 9.5.0
3
+ Version: 9.5.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
@@ -72,9 +72,7 @@ Features
72
72
  * **Monitoring:** Real-time tracking of temperature, power usage, tank charge, and component status.
73
73
  * **Control:** Remote control of target temperatures, operation modes, and vacation settings.
74
74
  * **Scheduling:** Reservations, time-of-use (TOU) schedules and anti-legionella cycles.
75
- * **Typed models:** Pydantic models with validation and unit handling.
76
- * **Async:** Built on asyncio.
77
- * **Simulator (optional):** A physics model of the heater, ``nwp500.sim``, that answers "when will the tank be hot?", "how much hot water is there?" and "what would this change do?", and calibrates from a heater's own history. ``nwp-cli simulate`` runs it on the heater as it is now or on a scenario file. Install it with ``pip install nwp500-python[sim]``.
75
+ * **Simulator (optional):** ``nwp500.sim``, a physics model of the heater. It estimates recovery time and available hot water, projects the effect of a change to the schedule or settings, and calibrates against a heater's own history. ``nwp-cli simulate`` runs it against the live heater or a scenario file. Install with ``pip install nwp500-python[sim]``.
78
76
 
79
77
  Getting Started
80
78
  ===============
@@ -18,9 +18,7 @@ Features
18
18
  * **Monitoring:** Real-time tracking of temperature, power usage, tank charge, and component status.
19
19
  * **Control:** Remote control of target temperatures, operation modes, and vacation settings.
20
20
  * **Scheduling:** Reservations, time-of-use (TOU) schedules and anti-legionella cycles.
21
- * **Typed models:** Pydantic models with validation and unit handling.
22
- * **Async:** Built on asyncio.
23
- * **Simulator (optional):** A physics model of the heater, ``nwp500.sim``, that answers "when will the tank be hot?", "how much hot water is there?" and "what would this change do?", and calibrates from a heater's own history. ``nwp-cli simulate`` runs it on the heater as it is now or on a scenario file. Install it with ``pip install nwp500-python[sim]``.
21
+ * **Simulator (optional):** ``nwp500.sim``, a physics model of the heater. It estimates recovery time and available hot water, projects the effect of a change to the schedule or settings, and calibrates against a heater's own history. ``nwp-cli simulate`` runs it against the live heater or a scenario file. Install with ``pip install nwp500-python[sim]``.
24
22
 
25
23
  Getting Started
26
24
  ===============
@@ -222,9 +222,11 @@ class State:
222
222
 
223
223
 
224
224
  def parse_state(doc: Any) -> State:
225
- """``/api/states/<entity>``'s answer. ``at`` is the entity's last
226
- state or attribute change (``last_updated``), as the reference
227
- implementation's change rows date a reading."""
225
+ """``/api/states/<entity>``'s answer. ``at`` is when the entity
226
+ last reported (``last_reported``), so a sensor that keeps reporting
227
+ the same value stays fresh. Home Assistant before 2024.3 has no
228
+ ``last_reported``; then ``at`` is the last state or attribute change
229
+ (``last_updated``, else ``last_changed``)."""
228
230
  if not isinstance(doc, Mapping) or "state" not in doc:
229
231
  raise HomeAssistantError("not an entity state")
230
232
  d = cast(Mapping[str, Any], doc)
@@ -233,7 +235,9 @@ def parse_state(doc: Any) -> State:
233
235
  if isinstance(attributes, Mapping):
234
236
  u = cast(Mapping[str, Any], attributes).get("unit_of_measurement")
235
237
  unit = u if isinstance(u, str) else None
236
- stamp = d.get("last_updated") or d.get("last_changed")
238
+ stamp = (
239
+ d.get("last_reported") or d.get("last_updated") or d.get("last_changed")
240
+ )
237
241
  return State(
238
242
  entity=str(d.get("entity_id", "")),
239
243
  value=_number(d.get("state")),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 9.5.0
3
+ Version: 9.5.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
@@ -72,9 +72,7 @@ Features
72
72
  * **Monitoring:** Real-time tracking of temperature, power usage, tank charge, and component status.
73
73
  * **Control:** Remote control of target temperatures, operation modes, and vacation settings.
74
74
  * **Scheduling:** Reservations, time-of-use (TOU) schedules and anti-legionella cycles.
75
- * **Typed models:** Pydantic models with validation and unit handling.
76
- * **Async:** Built on asyncio.
77
- * **Simulator (optional):** A physics model of the heater, ``nwp500.sim``, that answers "when will the tank be hot?", "how much hot water is there?" and "what would this change do?", and calibrates from a heater's own history. ``nwp-cli simulate`` runs it on the heater as it is now or on a scenario file. Install it with ``pip install nwp500-python[sim]``.
75
+ * **Simulator (optional):** ``nwp500.sim``, a physics model of the heater. It estimates recovery time and available hot water, projects the effect of a change to the schedule or settings, and calibrates against a heater's own history. ``nwp-cli simulate`` runs it against the live heater or a scenario file. Install with ``pip install nwp500-python[sim]``.
78
76
 
79
77
  Getting Started
80
78
  ===============
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "9.5.1",
3
+ "distance": 0,
4
+ "node": "gc7449be88920257c7be8f54c853a2586a5eb93b9",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-21"
8
+ }
@@ -46,15 +46,27 @@ class TestParsing:
46
46
  def test_no_value_is_none(self, value: str) -> None:
47
47
  assert ha.parse_state(_state("sensor.air", value, "°F")).value is None
48
48
 
49
- def test_freshness_is_the_last_update(self) -> None:
49
+ def test_freshness_is_the_last_report(self) -> None:
50
+ # A steady sensor: its value last changed 2 h ago, but it
51
+ # reported just now.
52
+ doc = _state("sensor.air", "61.5", "°F", timedelta(hours=2)) | {
53
+ "last_reported": NOW.isoformat(),
54
+ }
55
+ s = ha.parse_state(doc)
56
+ assert s.at == NOW
57
+ assert ha.fresh(s, NOW)
58
+
59
+ def test_without_a_report_the_last_update_then_change(self) -> None:
60
+ # Home Assistant before 2024.3 sends no last_reported.
50
61
  doc = _state("sensor.air", "61.5", "°F") | {
62
+ "last_changed": (NOW - timedelta(hours=3)).isoformat(),
51
63
  "last_updated": (NOW - timedelta(hours=2)).isoformat(),
52
- "last_reported": NOW.isoformat(),
53
64
  }
54
65
  s = ha.parse_state(doc)
55
66
  assert s.at == NOW - timedelta(hours=2)
56
67
  assert not ha.fresh(s, NOW)
57
- assert ha.fresh(ha.parse_state(_state("sensor.air", 1, "°F")), NOW)
68
+ del doc["last_updated"]
69
+ assert ha.parse_state(doc).at == NOW - timedelta(hours=3)
58
70
 
59
71
  def test_not_a_state(self) -> None:
60
72
  with pytest.raises(ha.HomeAssistantError):
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "9.5.0",
3
- "distance": 0,
4
- "node": "g6cedb29424f8b893710077157a5ba6539796ee3e",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-10-20"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes