nwp500-python 9.7.0__tar.gz → 9.9.0__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 (345) hide show
  1. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/CHANGELOG.rst +84 -0
  2. {nwp500_python-9.7.0/src/nwp500_python.egg-info → nwp500_python-9.9.0}/PKG-INFO +1 -1
  3. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/simulator.rst +5 -1
  4. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/calibrate-simulator.rst +61 -6
  5. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/python_api/sim.rst +7 -3
  6. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/api.py +1 -5
  7. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/calibration.py +570 -132
  8. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/decay.py +29 -1
  9. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/params.py +17 -0
  10. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/physics/compressor.py +12 -2
  11. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/physics/tank.py +68 -0
  12. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/tank_physics.py +2 -0
  13. nwp500_python-9.9.0/src/nwp500/sim_calibrate.py +1245 -0
  14. {nwp500_python-9.7.0 → nwp500_python-9.9.0/src/nwp500_python.egg-info}/PKG-INFO +1 -1
  15. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500_python.egg-info/SOURCES.txt +1 -0
  16. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500_python.egg-info/scm_file_list.json +1 -0
  17. nwp500_python-9.9.0/src/nwp500_python.egg-info/scm_version.json +8 -0
  18. nwp500_python-9.9.0/tests/test_sim_bottom_loss.py +665 -0
  19. nwp500_python-9.9.0/tests/test_sim_calibrate.py +1388 -0
  20. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_change_history.py +45 -0
  21. nwp500_python-9.7.0/src/nwp500/sim_calibrate.py +0 -517
  22. nwp500_python-9.7.0/src/nwp500_python.egg-info/scm_version.json +0 -8
  23. nwp500_python-9.7.0/tests/test_sim_calibrate.py +0 -608
  24. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/.coveragerc +0 -0
  25. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/.github/RESOLVING_PR_COMMENTS.md +0 -0
  26. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/.github/copilot-instructions.md +0 -0
  27. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/.github/workflows/ci.yml +0 -0
  28. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/.github/workflows/release.yml +0 -0
  29. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/.gitignore +0 -0
  30. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/.pre-commit-config.yaml +0 -0
  31. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/.readthedocs.yml +0 -0
  32. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/AUTHORS.rst +0 -0
  33. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/CONTRIBUTING.rst +0 -0
  34. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/LICENSE.txt +0 -0
  35. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/Makefile +0 -0
  36. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/README.rst +0 -0
  37. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/RELEASE.md +0 -0
  38. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/Makefile +0 -0
  39. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/_static/.gitignore +0 -0
  40. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/conf.py +0 -0
  41. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/architecture.rst +0 -0
  42. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/draws.rst +0 -0
  43. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/heating-elements.rst +0 -0
  44. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/index.rst +0 -0
  45. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/recovery-speed.rst +0 -0
  46. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/standby-loss.rst +0 -0
  47. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/tank-energy.rst +0 -0
  48. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/tou-recovery-cap.rst +0 -0
  49. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/unknown-values.rst +0 -0
  50. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/explanation/what-starts-a-recovery.rst +0 -0
  51. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/authenticate.rst +0 -0
  52. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/auto-recovery.rst +0 -0
  53. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/bluetooth.rst +0 -0
  54. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/diagnose-mqtt.rst +0 -0
  55. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/home-assistant.rst +0 -0
  56. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/index.rst +0 -0
  57. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/maintenance.rst +0 -0
  58. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/manage-units.rst +0 -0
  59. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/monitor-status.rst +0 -0
  60. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/optimize-tou.rst +0 -0
  61. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/queue-commands.rst +0 -0
  62. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/schedule-operation.rst +0 -0
  63. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/simulate-heater.rst +0 -0
  64. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/how-to/track-energy.rst +0 -0
  65. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/index.rst +0 -0
  66. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/openapi.yaml +0 -0
  67. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/project/authors.rst +0 -0
  68. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/project/changelog.rst +0 -0
  69. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/project/contributing.rst +0 -0
  70. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/project/history.rst +0 -0
  71. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/project/license.rst +0 -0
  72. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/configuration.rst +0 -0
  73. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/enumerations.rst +0 -0
  74. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/index.rst +0 -0
  75. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/installation.rst +0 -0
  76. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/protocol/data_conversions.rst +0 -0
  77. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/protocol/device_features.rst +0 -0
  78. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/protocol/device_status.rst +0 -0
  79. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/protocol/error_codes.rst +0 -0
  80. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/protocol/mqtt_protocol.rst +0 -0
  81. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/protocol/quick_reference.rst +0 -0
  82. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/protocol/rest_api.rst +0 -0
  83. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/python_api/api_client.rst +0 -0
  84. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/python_api/auth_client.rst +0 -0
  85. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/python_api/cli.rst +0 -0
  86. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/python_api/events.rst +0 -0
  87. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/python_api/exceptions.rst +0 -0
  88. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/python_api/models.rst +0 -0
  89. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/python_api/mqtt_client.rst +0 -0
  90. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/reference/simulator-inputs.rst +0 -0
  91. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/requirements.txt +0 -0
  92. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/docs/tutorials/getting-started.rst +0 -0
  93. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/.ruff.toml +0 -0
  94. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/README.md +0 -0
  95. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/air_filter_reset.py +0 -0
  96. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/anti_legionella.py +0 -0
  97. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/auto_recovery.py +0 -0
  98. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/combined_callbacks.py +0 -0
  99. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/demand_response.py +0 -0
  100. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/device_capabilities.py +0 -0
  101. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/device_status_debug.py +0 -0
  102. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/energy_analytics.py +0 -0
  103. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/error_code_demo.py +0 -0
  104. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/firmware_payload_capture.py +0 -0
  105. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/mqtt_diagnostics.py +0 -0
  106. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/power_control.py +0 -0
  107. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/recirculation_control.py +0 -0
  108. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/reconnection_demo.py +0 -0
  109. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/reservation_schedule.py +0 -0
  110. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/simple_auto_recovery.py +0 -0
  111. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/token_restoration.py +0 -0
  112. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/tou_openei.py +0 -0
  113. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/tou_schedule.py +0 -0
  114. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/advanced/water_reservation.py +0 -0
  115. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/beginner/01_authentication.py +0 -0
  116. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/beginner/02_list_devices.py +0 -0
  117. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/beginner/03_get_status.py +0 -0
  118. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/beginner/04_set_temperature.py +0 -0
  119. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/advanced_auth_patterns.py +0 -0
  120. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/command_queue.py +0 -0
  121. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/device_status_callback.py +0 -0
  122. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/error_handling.py +0 -0
  123. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/event_driven_control.py +0 -0
  124. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/improved_auth.py +0 -0
  125. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/legacy_auth_constructor.py +0 -0
  126. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/mqtt_realtime_monitoring.py +0 -0
  127. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/periodic_requests.py +0 -0
  128. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/set_mode.py +0 -0
  129. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/intermediate/vacation_mode.py +0 -0
  130. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/mask.py +0 -0
  131. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/simulator/lower-setpoint.json +0 -0
  132. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/simulator/plan.json +0 -0
  133. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/simulator/scenario.json +0 -0
  134. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/testing/periodic_device_info.py +0 -0
  135. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/testing/simple_periodic_info.py +0 -0
  136. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/testing/test_api_client.py +0 -0
  137. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/testing/test_mqtt_connection.py +0 -0
  138. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/testing/test_mqtt_messaging.py +0 -0
  139. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/examples/testing/test_periodic_minimal.py +0 -0
  140. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/pyproject.toml +0 -0
  141. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/scripts/README.md +0 -0
  142. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/scripts/bump_version.py +0 -0
  143. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/scripts/diagnose_mqtt_connection.py +0 -0
  144. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/scripts/extract_changelog.py +0 -0
  145. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/scripts/format.py +0 -0
  146. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/scripts/lint.py +0 -0
  147. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/scripts/setup-dev.py +0 -0
  148. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/scripts/validate_version.py +0 -0
  149. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/setup.cfg +0 -0
  150. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/setup.py +0 -0
  151. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/__init__.py +0 -0
  152. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/_base.py +0 -0
  153. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/api_client.py +0 -0
  154. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/auth.py +0 -0
  155. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/ble/__init__.py +0 -0
  156. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/ble/__main__.py +0 -0
  157. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/ble/client.py +0 -0
  158. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/ble/mqtt_adapter.py +0 -0
  159. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/ble/protocol.py +0 -0
  160. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/__init__.py +0 -0
  161. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/__main__.py +0 -0
  162. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/ble_backend.py +0 -0
  163. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/calibration.py +0 -0
  164. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/handlers.py +0 -0
  165. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/monitoring.py +0 -0
  166. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/output_formatters.py +0 -0
  167. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/presentation.py +0 -0
  168. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/recording.py +0 -0
  169. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/rich_output.py +0 -0
  170. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/simulate.py +0 -0
  171. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/cli/token_storage.py +0 -0
  172. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/command_decorators.py +0 -0
  173. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/config.py +0 -0
  174. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/converters.py +0 -0
  175. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/device_capabilities.py +0 -0
  176. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/device_info_cache.py +0 -0
  177. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/encoding.py +0 -0
  178. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/enums.py +0 -0
  179. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/events.py +0 -0
  180. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/exceptions.py +0 -0
  181. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/factory.py +0 -0
  182. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/field_factory.py +0 -0
  183. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/__init__.py +0 -0
  184. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/_converters.py +0 -0
  185. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/account.py +0 -0
  186. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/device.py +0 -0
  187. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/diagnostics.py +0 -0
  188. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/energy.py +0 -0
  189. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/feature.py +0 -0
  190. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/firmware.py +0 -0
  191. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/maintenance.py +0 -0
  192. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/mqtt_models.py +0 -0
  193. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/schedule.py +0 -0
  194. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/status.py +0 -0
  195. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/models/tou.py +0 -0
  196. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/__init__.py +0 -0
  197. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/_control_commands.py +0 -0
  198. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/_device_subscriptions.py +0 -0
  199. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/client.py +0 -0
  200. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/command_queue.py +0 -0
  201. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/connection.py +0 -0
  202. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/control.py +0 -0
  203. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/diagnostics.py +0 -0
  204. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/periodic.py +0 -0
  205. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/reconnection.py +0 -0
  206. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/state_tracker.py +0 -0
  207. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/subscriptions.py +0 -0
  208. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/types.py +0 -0
  209. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt/utils.py +0 -0
  210. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/mqtt_events.py +0 -0
  211. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/openei.py +0 -0
  212. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/py.typed +0 -0
  213. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/reservations.py +0 -0
  214. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/__init__.py +0 -0
  215. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/attribution.py +0 -0
  216. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/book.py +0 -0
  217. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/circulation.py +0 -0
  218. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/codec.py +0 -0
  219. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/cold_supply.py +0 -0
  220. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/controller.py +0 -0
  221. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/defaults.py +0 -0
  222. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/delivery.py +0 -0
  223. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/draw_forecast.py +0 -0
  224. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/engine.py +0 -0
  225. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/extensions.py +0 -0
  226. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/heat_pump_recovery.py +0 -0
  227. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/heating_trajectory.py +0 -0
  228. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/inputs.py +0 -0
  229. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/location_air.py +0 -0
  230. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/meter.py +0 -0
  231. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/observed.py +0 -0
  232. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/physics/__init__.py +0 -0
  233. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/physics/draw.py +0 -0
  234. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/physics/element.py +0 -0
  235. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/physics/refrigerant.py +0 -0
  236. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/profile.py +0 -0
  237. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/ready.py +0 -0
  238. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/record.py +0 -0
  239. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/result.py +0 -0
  240. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/schedules.py +0 -0
  241. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/tou_triggers.py +0 -0
  242. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim/units.py +0 -0
  243. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim_adapters.py +0 -0
  244. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim_home_assistant.py +0 -0
  245. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/sim_live.py +0 -0
  246. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/status_history.py +0 -0
  247. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/temperature.py +0 -0
  248. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/topic_builder.py +0 -0
  249. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/tou_schedule.py +0 -0
  250. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/unit_system.py +0 -0
  251. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500/utils.py +0 -0
  252. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  253. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  254. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  255. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500_python.egg-info/requires.txt +0 -0
  256. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/src/nwp500_python.egg-info/top_level.txt +0 -0
  257. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/_sim_site.py +0 -0
  258. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/conftest.py +0 -0
  259. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/fixtures/sim/engine_golden.json +0 -0
  260. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/fixtures/sim/heater_params.json +0 -0
  261. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/fixtures/sim/heater_params_fitted.json +0 -0
  262. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_api_endpoints_201.py +0 -0
  263. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_api_helpers.py +0 -0
  264. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_app_queries.py +0 -0
  265. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_auth.py +0 -0
  266. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_auth_session_lifecycle.py +0 -0
  267. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_auth_session_refresh.py +0 -0
  268. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_ble_mqtt_adapter.py +0 -0
  269. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_ble_protocol.py +0 -0
  270. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_bug_fixes.py +0 -0
  271. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_canonical_schedule.py +0 -0
  272. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_cli_basic.py +0 -0
  273. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_cli_calibrate.py +0 -0
  274. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_cli_commands.py +0 -0
  275. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_cli_simulate.py +0 -0
  276. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_cli_tristate_presentation.py +0 -0
  277. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_command_decorators.py +0 -0
  278. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_command_queue.py +0 -0
  279. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_device_capabilities.py +0 -0
  280. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_device_info_cache.py +0 -0
  281. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_device_rest_models.py +0 -0
  282. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_events.py +0 -0
  283. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_exceptions.py +0 -0
  284. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_model_converters.py +0 -0
  285. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_models.py +0 -0
  286. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_clean_session_resume.py +0 -0
  287. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_client_init.py +0 -0
  288. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_control_fail.py +0 -0
  289. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_events.py +0 -0
  290. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_hypothesis.py +0 -0
  291. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_last_will.py +0 -0
  292. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_reconnection.py +0 -0
  293. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_reconnection_storm.py +0 -0
  294. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_mqtt_reliability.py +0 -0
  295. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_multi_device.py +0 -0
  296. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_navilink_201_enums.py +0 -0
  297. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_openei.py +0 -0
  298. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_protocol_correctness.py +0 -0
  299. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_public_api.py +0 -0
  300. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_reservation_validation.py +0 -0
  301. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_reservations.py +0 -0
  302. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_adapters.py +0 -0
  303. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_api.py +0 -0
  304. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_book.py +0 -0
  305. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_book_history.py +0 -0
  306. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_calibration.py +0 -0
  307. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_circulation.py +0 -0
  308. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_codec.py +0 -0
  309. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_cold_supply.py +0 -0
  310. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_controller.py +0 -0
  311. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_defaults.py +0 -0
  312. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_draw_forecast.py +0 -0
  313. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_draw_loss.py +0 -0
  314. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_element.py +0 -0
  315. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_element_fit.py +0 -0
  316. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_forecast.py +0 -0
  317. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_golden.py +0 -0
  318. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_home_assistant.py +0 -0
  319. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_hot_water.py +0 -0
  320. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_import_contract.py +0 -0
  321. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_live.py +0 -0
  322. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_location_air.py +0 -0
  323. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_meter.py +0 -0
  324. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_no_clock_no_state.py +0 -0
  325. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_observed.py +0 -0
  326. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_profile.py +0 -0
  327. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_readiness.py +0 -0
  328. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_readiness_fit.py +0 -0
  329. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_ready.py +0 -0
  330. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_ready_api.py +0 -0
  331. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_replay.py +0 -0
  332. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_schedules.py +0 -0
  333. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_sim_tank_physics.py +0 -0
  334. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_status_history.py +0 -0
  335. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_temperature_converters.py +0 -0
  336. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_threading_model.py +0 -0
  337. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_token_storage.py +0 -0
  338. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_tou_api.py +0 -0
  339. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_tou_schedule.py +0 -0
  340. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_unit_switching.py +0 -0
  341. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_unit_system_process_wide.py +0 -0
  342. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_utility_modules.py +0 -0
  343. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tests/test_utils.py +0 -0
  344. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/tox.ini +0 -0
  345. {nwp500_python-9.7.0 → nwp500_python-9.9.0}/uv.lock +0 -0
@@ -2,6 +2,90 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 9.9.0 (2026-10-22)
6
+ ==========================
7
+
8
+ Added
9
+ -----
10
+ - **A bottom loss in the standby model** (#187). Over one installation's 407
11
+ quiet windows across ten months, a window that opens stratified loses
12
+ heat at the bottom: its gap widens, its lower probe falls about 40 %
13
+ faster than a mixed tank's, and its upper probe hardly moves. One UA
14
+ on the whole tank could not represent it, and the fitted UA was a
15
+ blend that cooled a mixed tank too fast, as after a cold season's full
16
+ recoveries. ``LossParams`` gains ``bottom_loss_btu_hr`` and
17
+ ``bottom_gate_f``: heat leaving the lower zone, of which the lower
18
+ probe sees ``1 - exp(-gap / gate)`` (``sim.physics.tank.
19
+ bottom_loss_reach``). The standby walk (``decay.project_decay``) and
20
+ the recovery integrator charge it to the lower zone. A bottom loss of
21
+ 0.0, the default and what stored parameter sets carry, reproduces the
22
+ model before it.
23
+ - ``sim.calibration.fit_loss`` fits the UA law and the bottom loss
24
+ together, through the standby walk, on the quiet windows opening with
25
+ a gap of 10 °F or less (``loss_windows``); ``fit_standby`` uses it.
26
+ On the installation above, the lower probe's median error over ten
27
+ hours fell from 1.25 to 0.77 °F from March to October and from 1.67
28
+ to 1.05 °F over the winter (1.15 °F trained without the winter).
29
+ ``StandbyWindow`` carries each window's probes and air, and
30
+ ``standby_ends`` and ``standby_error`` walk a loss over windows.
31
+ - The report has the bottom loss and its gate, and ``keep_better``
32
+ judges them with the standby loss, on the probes' error in each
33
+ held-out window's fall, °F/h.
34
+
35
+ Changed
36
+ -------
37
+ - The standby loss is fitted only on quiet windows opening with a gap of
38
+ 10 °F or less: a wider gap is a draw's front closing, which the gap's
39
+ time constant describes.
40
+ - ``keep_better`` scores the standby group on the probes' error in each
41
+ held-out window's fall, walked as the simulation walks it, in place of
42
+ the error in log UA, so a loss with a bottom term and one without are
43
+ scored alike.
44
+ - The zone gap time constant is fitted with the bottom loss opening the
45
+ gaps it relaxes (``fit_gap_tau``'s ``loss``, ``gap_paths``), in turn
46
+ with the loss until it settles: a gap the bottom loss widens read as a
47
+ pure relaxation pushed the constant long. ``keep_better`` judges the
48
+ constant before the standby loss and fits and scores the loss on the
49
+ constant kept.
50
+
51
+ Version 9.8.0 (2026-10-21)
52
+ ==========================
53
+
54
+ Added
55
+ -----
56
+ - **Refine a fit without making it worse** (``keep_better=True`` on
57
+ ``sim_calibrate.calibrate`` and ``calibrate_changes``, #184). Each
58
+ group of terms holds out its latest evidence from its refit (the last
59
+ fifth, once it has 10 items) and keeps the existing values unless the
60
+ refit scores better there, by the error its own fit minimizes. The
61
+ terms in a group are kept or replaced together. ``Calibrated.judged``
62
+ records each judgement, and the report's reasons give both scores.
63
+ ``sim_calibrate.score(done, params)`` scores any parameters on that
64
+ held-out evidence.
65
+ - ``sim.calibration.split_recoveries``, ``recovery_minutes``,
66
+ ``power_cycles`` and ``cop_cycles``: the evidence the compressor and
67
+ power fits use, and their predictions, which ``fit_compressor``,
68
+ ``fit_power_scale`` and ``fit_cop_scale`` now take from.
69
+
70
+ Fixed
71
+ -----
72
+ - **The report names compressor runs without an air temperature.**
73
+ A recovery is timed against the air, so a run with none (no air
74
+ given, and no heater ambient reading while the compressor runs) is
75
+ left out of the recoveries. The report blamed the heater instead:
76
+ "0 recoveries reached their target". ``Calibrated`` now counts the
77
+ runs (``n_runs``, ``n_runs_without_air``), and the compressor term's
78
+ reason and the notes say how many had no air
79
+ (``sim_calibrate.air_unread_note``).
80
+ - ``sim.calibration.with_standby`` quotes the fitted UA at the ΔT the
81
+ law was fitted at (50 °F). It kept the ``ua_ref_delta_t_f`` of the
82
+ parameters it was given, so a refit applied over parameters quoted at
83
+ another ΔT ran as a different loss law.
84
+ - ``sim.calibration.fit_cop_scale`` takes the integrator's COP on the
85
+ heater's own tank (``HeaterParams.tank``), as the simulation does. It
86
+ used the default tank's mass, so a heater with another tank was fitted
87
+ against a COP the simulation does not run.
88
+
5
89
  Version 9.7.0 (2026-10-21)
6
90
  ==========================
7
91
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 9.7.0
3
+ Version: 9.9.0
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
@@ -78,7 +78,11 @@ zone (about half the tank's mass). Once the gap between the zones
78
78
  closes, the tank heats as one body. In standby the gap relaxes on a
79
79
  fitted time constant. Standby loss follows a jacket law,
80
80
  ``UA(dT) = ua_ref * (max(dT, 5) / 50) ^ n``, with the ambient as the
81
- other side.
81
+ other side, on the whole tank. A bottom loss ``Q_b`` leaves the lower
82
+ zone alone: the lower probe sees the share ``1 - exp(-gap / gate)`` of
83
+ it, so a stratified tank's lower zone falls faster and its gap opens,
84
+ while a mixed tank hides it below the probe. With ``Q_b = 0`` the loss
85
+ is the jacket law alone.
82
86
 
83
87
  **The elements.** They follow the device's own rules
84
88
  (:doc:`heating-elements`): per-mode entry thresholds, a dead time,
@@ -139,16 +139,23 @@ fitted or kept the base's value, and why:
139
139
 
140
140
  * - Term
141
141
  - Fitted from
142
- * - Standby loss and its exponent
142
+ * - Standby loss, its exponent, the bottom loss and its gate
143
143
  - Quiet windows of 3 h or more, starting 45 minutes after any
144
- heating or hot water in use. A window counts only if its fit
145
- tracks the probes within 2 °F. The exponent is fitted only when
146
- the windows span two 20 °F bands of tank-to-air difference; it
147
- is held at 0 (a constant loss) otherwise.
144
+ heating or hot water in use, that open with a gap of 10 °F or
145
+ less between the probes. A window counts only if its fit tracks
146
+ the probes within 2 °F. Each window's upper and lower probe are
147
+ walked from its start as the simulation walks them, and the
148
+ terms are fitted to where the probes ended. The exponent is
149
+ fitted only when the windows span two 20 °F bands of tank-to-air
150
+ difference; it is held at 0 (a constant loss) otherwise. The
151
+ bottom loss needs 10 windows that open with a gap of 1 °F or
152
+ more; with fewer it is 0, and the loss is the jacket law
153
+ alone.
148
154
  * - Zone gap time constant
149
155
  - How fast the upper and lower probes' gap closes over quiet
150
156
  windows; it needs 20 windows that open with a gap of 2 °F or
151
- more.
157
+ more. The bottom loss opens the gaps it relaxes, so the two are
158
+ fitted in turn until the constant settles.
152
159
  * - Compressor constant, evaporator approach
153
160
  - Heat-pump recoveries that reached their target, against the loss
154
161
  just fitted. The error is the mean absolute error of the
@@ -223,6 +230,48 @@ Assistant integration, can calibrate the same way:
223
230
  4. Store ``params_to_document(done.calibration.params)``, and show the
224
231
  ``report(done)`` rows and ``done.notes``.
225
232
 
233
+ Refine an existing fit
234
+ ----------------------
235
+
236
+ A fit made on months of history should not give way to one made on the
237
+ last ten days unless the new one predicts better. Pass the existing
238
+ fit as ``params`` with ``keep_better=True``:
239
+
240
+ .. code-block:: python
241
+
242
+ done = sim_calibrate.calibrate(
243
+ recorded(days), ambient=65.0, params=existing, keep_better=True
244
+ )
245
+ for j in done.judged:
246
+ print(j.group, j.kept, j.base_error, j.refit_error, j.why)
247
+
248
+ Each group of terms holds out its latest evidence from its refit:
249
+
250
+ - the standby loss, its exponent, the bottom loss and its gate: its
251
+ quiet windows, scored on the probes' error in each window's fall,
252
+ °F/h;
253
+ - the zone gap time constant: its windows that open a gap, judged
254
+ before the standby loss, which is then fitted and scored on the
255
+ constant kept;
256
+ - the compressor constant and the evaporator approach: its reached
257
+ recoveries;
258
+ - the power scale: the metered compressor cycles it can use;
259
+ - the COP terms: the cycles the COP fit can use (without a meter of
260
+ draws, those no water was drawn around).
261
+
262
+ Once a group has 10 items, the latest fifth (at least 2) is held out.
263
+ The refit replaces the existing values only where it scores better on
264
+ them, by the error its own fit minimizes. With fewer items the existing
265
+ values stay. The terms in a group are kept or replaced together, and the
266
+ report's reasons say which, with both scores.
267
+ ``sim_calibrate.score(done, params)`` gives those errors for any
268
+ parameters.
269
+
270
+ Use it over a fit of this heater. The defaults are another heater's fit,
271
+ and ``keep_better`` keeps them wherever there is too little evidence to
272
+ judge by. A group the existing fit lists as unfitted takes its refit, as
273
+ without ``keep_better``.
274
+
226
275
  ``sim.calibration.calibrate`` underneath takes the frame
227
276
  ``sim_adapters.history`` builds; ``sim_adapters.add_inputs`` adds the
228
277
  air, the humidity, a meter's draws and the circulation's runs to it.
@@ -294,6 +343,12 @@ leave out what your installation does not record. ``read_heater_history``:
294
343
  - notes a value it could not read, and an entity whose history starts
295
344
  after ``start``.
296
345
 
346
+ A recovery is timed against the air, so the compressor and power fits
347
+ need an air temperature over each compressor run: the air beside the
348
+ heater (``record=``, ``air`` or ``ambient``), or the heater's own reading
349
+ (``ambient`` in ``HeaterEntities``). Runs without one are no recovery,
350
+ and the report says how many there were.
351
+
297
352
  Give ``anti_legionella`` where it is recorded. Without it an
298
353
  anti-legionella cycle is not kept out of the recoveries, and the notes
299
354
  say so.
@@ -150,7 +150,8 @@ Hot water and the heat book
150
150
 
151
151
  .. automodule:: nwp500.sim.calibration
152
152
  :members: calibrate, Calibration, fit_standby, StandbyFit, StandbyWindow,
153
- fit_compressor, CompressorFit, clean_draws, CleanDraw, crossing,
153
+ fit_compressor, CompressorFit, split_recoveries, recovery_minutes,
154
+ clean_draws, CleanDraw, crossing,
154
155
  demonstrated, frontier, capacity, held_out, fit_evidence,
155
156
  EvidenceFit, cold_lower_cap, RecordedBook, fit_book_terms, BookFit,
156
157
  book_error_bands, book_error_btu, empty_point,
@@ -163,11 +164,13 @@ Hot water and the heat book
163
164
  element_off_points, element_power, element_grid, element_runs,
164
165
  fit_element_rates, ElementRateFit, day_ci,
165
166
  energy_saver_off_deficit, upper_start_edge, fit_power,
166
- PowerFit, fit_power_scale,
167
+ PowerFit, fit_power_scale, power_cycles, cop_cycles, CopCycle,
167
168
  fit_cop_scale, solve_cop_fit, solve_cop_scale, draw_free_withdrawn,
168
169
  metered_withdrawn, metered_lead,
169
170
  path_to_end, with_power, snapshots, recoveries, quiet_windows,
170
171
  busy_intervals, fit_window, fit_ua_law, fit_gap_tau, with_standby,
172
+ fit_loss, LossFit, loss_windows, standby_ends, standby_error,
173
+ walk_tau, gap_paths, gap_error,
171
174
  with_compressor, UA_SEARCH_BOUNDS, UA_EXPONENT_BOUNDS, DRAW_LEAD
172
175
 
173
176
  .. automodule:: nwp500.sim.profile
@@ -249,7 +252,7 @@ The pieces the entry points are built from.
249
252
  vapor_enthalpy, dew_point_f
250
253
 
251
254
  .. automodule:: nwp500.sim.physics.tank
252
- :members: ua_at
255
+ :members: ua_at, bottom_loss_reach, bottom_loss_drop_f
253
256
 
254
257
  .. automodule:: nwp500.sim.physics.element
255
258
  :members: mode_recovery, ModeRecovery, mode_plan, ModePlan,
@@ -299,6 +302,7 @@ Recorded history
299
302
 
300
303
  .. automodule:: nwp500.sim_calibrate
301
304
  :members: calibrate, calibrate_changes, Calibrated, report, Term, max_gap,
305
+ score, Judged, HeldOut, GROUPS, air_unread_note,
302
306
  CalibrationInputError, DEFAULT_HUMIDITY_PCT, GAP_SAMPLES, MIN_GAP,
303
307
  DEFAULT_BASE_NOTE, NO_AIR_NOTE
304
308
 
@@ -78,7 +78,7 @@ from nwp500.sim.inputs import (
78
78
  Schedules,
79
79
  Settings,
80
80
  )
81
- from nwp500.sim.params import HeaterParams
81
+ from nwp500.sim.params import TAU_GAP_PRIOR_H, HeaterParams
82
82
  from nwp500.sim.physics.compressor import MAX_MINUTES, predict_physics_detailed
83
83
  from nwp500.sim.physics.element import (
84
84
  DEVICE_MODE,
@@ -224,10 +224,6 @@ LOW_CONFIDENCE_FLOOR = 0.6
224
224
  #: start was never searched (the TOU-opening start still was), so an
225
225
  #: answer may miss a start the heater makes.
226
226
  HP_LOWER_ON_UNKNOWN = "hp_lower_on_unknown"
227
- #: The gap time constant served when the parameters carry none
228
- #: (``TankParams.stratification_gap_tau_h``), h: history puts it near
229
- #: 60 h on this product.
230
- TAU_GAP_PRIOR_H = 60.0
231
227
  #: Draws opened a gap that closes on that prior, not a fitted constant:
232
228
  #: under no draws it only shapes how an existing gap closes.
233
229
  TAU_GAP_PRIOR = "tau_gap_prior"