nwp500-python 9.4.2__tar.gz → 9.4.3__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.
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/CHANGELOG.rst +136 -0
- {nwp500_python-9.4.2/src/nwp500_python.egg-info → nwp500_python-9.4.3}/PKG-INFO +1 -1
- nwp500_python-9.4.3/docs/explanation/heating-elements.rst +452 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/explanation/index.rst +2 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/explanation/tank-energy.rst +56 -12
- nwp500_python-9.4.3/docs/explanation/what-starts-a-recovery.rst +303 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/schedule-operation.rst +92 -2
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/track-energy.rst +8 -3
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/protocol/data_conversions.rst +44 -5
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/protocol/device_status.rst +17 -4
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/python_api/models.rst +2 -2
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/python_api/mqtt_client.rst +18 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/status.py +20 -10
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/control.py +155 -1
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/reservations.py +16 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3/src/nwp500_python.egg-info}/PKG-INFO +1 -1
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500_python.egg-info/SOURCES.txt +3 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500_python.egg-info/scm_file_list.json +3 -0
- nwp500_python-9.4.3/src/nwp500_python.egg-info/scm_version.json +8 -0
- nwp500_python-9.4.3/tests/test_reservation_validation.py +306 -0
- nwp500_python-9.4.2/src/nwp500_python.egg-info/scm_version.json +0 -8
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/.coveragerc +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/.github/RESOLVING_PR_COMMENTS.md +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/.github/copilot-instructions.md +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/.github/workflows/ci.yml +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/.github/workflows/release.yml +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/.gitignore +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/.pre-commit-config.yaml +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/.readthedocs.yml +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/AUTHORS.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/CONTRIBUTING.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/LICENSE.txt +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/Makefile +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/README.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/RELEASE.md +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/Makefile +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/_static/.gitignore +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/conf.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/explanation/advanced-features.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/explanation/architecture.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/explanation/tou-recovery-cap.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/explanation/unknown-values.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/authenticate.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/auto-recovery.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/diagnose-mqtt.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/home-assistant.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/index.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/maintenance.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/manage-units.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/monitor-status.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/optimize-tou.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/how-to/queue-commands.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/index.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/openapi.yaml +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/project/authors.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/project/changelog.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/project/contributing.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/project/history.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/project/license.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/configuration.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/enumerations.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/index.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/installation.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/protocol/device_features.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/protocol/error_codes.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/protocol/mqtt_protocol.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/protocol/quick_reference.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/protocol/rest_api.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/python_api/api_client.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/python_api/auth_client.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/python_api/cli.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/python_api/events.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/reference/python_api/exceptions.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/requirements.txt +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/docs/tutorials/getting-started.rst +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/.ruff.toml +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/README.md +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/air_filter_reset.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/anti_legionella.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/auto_recovery.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/combined_callbacks.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/demand_response.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/device_capabilities.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/device_status_debug.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/energy_analytics.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/error_code_demo.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/firmware_payload_capture.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/mqtt_diagnostics.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/power_control.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/recirculation_control.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/reconnection_demo.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/reservation_schedule.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/simple_auto_recovery.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/token_restoration.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/tou_openei.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/tou_schedule.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/advanced/water_reservation.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/beginner/01_authentication.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/beginner/02_list_devices.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/beginner/03_get_status.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/beginner/04_set_temperature.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/advanced_auth_patterns.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/command_queue.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/device_status_callback.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/error_handling.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/event_driven_control.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/improved_auth.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/legacy_auth_constructor.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/mqtt_realtime_monitoring.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/periodic_requests.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/set_mode.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/intermediate/vacation_mode.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/mask.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/testing/periodic_device_info.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/testing/simple_periodic_info.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/testing/test_api_client.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/testing/test_mqtt_connection.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/testing/test_mqtt_messaging.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/examples/testing/test_periodic_minimal.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/pyproject.toml +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/scripts/README.md +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/scripts/bump_version.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/scripts/diagnose_mqtt_connection.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/scripts/extract_changelog.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/scripts/format.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/scripts/lint.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/scripts/setup-dev.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/scripts/validate_version.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/setup.cfg +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/setup.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/__init__.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/_base.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/api_client.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/auth.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/cli/__init__.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/cli/__main__.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/cli/handlers.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/cli/monitoring.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/cli/output_formatters.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/cli/presentation.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/cli/rich_output.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/cli/token_storage.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/command_decorators.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/config.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/converters.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/device_capabilities.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/device_info_cache.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/encoding.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/enums.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/events.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/exceptions.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/factory.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/field_factory.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/__init__.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/_converters.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/device.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/diagnostics.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/energy.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/feature.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/firmware.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/mqtt_models.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/schedule.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/models/tou.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/__init__.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/_control_commands.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/_device_subscriptions.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/client.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/command_queue.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/connection.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/diagnostics.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/periodic.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/reconnection.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/state_tracker.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/subscriptions.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/types.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt/utils.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/mqtt_events.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/openei.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/py.typed +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/temperature.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/topic_builder.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/tou_schedule.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/unit_system.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500/utils.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500_python.egg-info/entry_points.txt +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500_python.egg-info/not-zip-safe +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500_python.egg-info/requires.txt +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/src/nwp500_python.egg-info/top_level.txt +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/conftest.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_api_helpers.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_app_queries.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_auth.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_auth_session_lifecycle.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_bug_fixes.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_canonical_schedule.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_cli_basic.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_cli_commands.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_cli_tristate_presentation.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_command_decorators.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_command_queue.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_device_capabilities.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_device_info_cache.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_device_rest_models.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_events.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_exceptions.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_model_converters.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_models.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_mqtt_clean_session_resume.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_mqtt_client_init.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_mqtt_events.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_mqtt_hypothesis.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_mqtt_reconnection.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_mqtt_reconnection_storm.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_mqtt_reliability.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_multi_device.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_openei.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_protocol_correctness.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_public_api.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_reservations.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_temperature_converters.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_threading_model.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_token_storage.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_tou_api.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_tou_schedule.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_unit_switching.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_unit_system_process_wide.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_utility_modules.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tests/test_utils.py +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/tox.ini +0 -0
- {nwp500_python-9.4.2 → nwp500_python-9.4.3}/uv.lock +0 -0
|
@@ -5,6 +5,142 @@ Changelog
|
|
|
5
5
|
Unreleased
|
|
6
6
|
==========
|
|
7
7
|
|
|
8
|
+
Version 9.4.3 (2026-10-01)
|
|
9
|
+
==========================
|
|
10
|
+
|
|
11
|
+
Changed
|
|
12
|
+
-------
|
|
13
|
+
- **Raw reservation writes are now validated** (#148).
|
|
14
|
+
``update_reservations()`` and ``update_reservations_confirmed()`` sent
|
|
15
|
+
their entry dicts unchecked, so an unknown mode such as 7, an hour of 99
|
|
16
|
+
or a setpoint of 200 (100 degC) went to the device as-is. A Python
|
|
17
|
+
``True`` for ``enable`` was worse: the confirmed helper read it as 1,
|
|
18
|
+
which means disabled. Every entry is now checked before anything is
|
|
19
|
+
sent, with the same rules as ``build_reservation_entry()``: the six
|
|
20
|
+
protocol fields present and plain integers, ``enable`` 1 or 2, ``week``
|
|
21
|
+
a day bitfield, ``hour`` 0-23, ``min`` 0-59 and ``mode`` a
|
|
22
|
+
``DhwOperationSetting`` id. ``param`` is held to the setpoint range the
|
|
23
|
+
heater reports in its feature data (``dhw_temperature_min_raw`` to
|
|
24
|
+
``dhw_temperature_max_raw``), which is requested if it is not cached;
|
|
25
|
+
clearing the schedule with an empty list needs none. A bad entry raises
|
|
26
|
+
``ParameterValidationError`` or ``RangeValidationError``, and missing
|
|
27
|
+
feature data raises ``DeviceCapabilityError``. Callers that wrote
|
|
28
|
+
entries outside these ranges and relied on the heater to clamp them now
|
|
29
|
+
get an error instead. The checks are also available on their own as
|
|
30
|
+
``nwp500.mqtt.control.validate_reservation_entries()``.
|
|
31
|
+
|
|
32
|
+
Documentation
|
|
33
|
+
-------------
|
|
34
|
+
- **New: what starts a recovery**
|
|
35
|
+
(``docs/explanation/what-starts-a-recovery.rst``). The lower-tank
|
|
36
|
+
turn-on setting is one trigger among several. Measured on one unit:
|
|
37
|
+
outside a TOU window the lower setting reads 104.9 degF at every
|
|
38
|
+
setpoint and never follows it; at a window's opening the upper tank
|
|
39
|
+
starts a cycle when it is well below the setpoint, and the lower trigger
|
|
40
|
+
waits for the close. **A control write re-evaluates**: outside a window,
|
|
41
|
+
in ``HEAT_PUMP`` with the compressor off, a setpoint write leaving the
|
|
42
|
+
upper tank below the new setpoint started the compressor within
|
|
43
|
+
2 minutes in 112 of 117 writes (a median 30 s later), lowerings
|
|
44
|
+
included. 46 of 81 mode writes made with the compressor off did the
|
|
45
|
+
same. A setpoint dropped well below the upper tank stopped a running
|
|
46
|
+
compressor within 5 s (one run). A lowered setpoint did not hold off a
|
|
47
|
+
draw-driven start (one run, in ``ENERGY_SAVER``). The page also covers
|
|
48
|
+
traps for anyone re-deriving this from history.
|
|
49
|
+
- **TOU window: the two open cases measured, and three more results.** In
|
|
50
|
+
``docs/how-to/schedule-operation.rst`` (one or two runs each): an entry
|
|
51
|
+
scheduled after a window fires on its minute; a mode held in a window
|
|
52
|
+
takes effect when the window ends naturally; writing ``HEAT_PUMP`` over
|
|
53
|
+
a held mode withdraws it; turning TOU back on inside a window drops a
|
|
54
|
+
running element mode; and the operation setting reports the configured
|
|
55
|
+
mode, not activity. The section's first measurement date is corrected to
|
|
56
|
+
local time (2026-09-19).
|
|
57
|
+
- **New: what the heating elements actually do**
|
|
58
|
+
(``docs/explanation/heating-elements.rst``). The four
|
|
59
|
+
``he*TempSetting`` fields describe the thermostat, and **on entry to
|
|
60
|
+
a mode the thermostat is not what runs the element**: in
|
|
61
|
+
Electric and High Demand the on and off settings are the same value,
|
|
62
|
+
and every ``he*DiffTempSetting`` reads 0. Measured on one unit by
|
|
63
|
+
commanding the modes and watching, the start differential is
|
|
64
|
+
**mode-dependent**, one bracket per mode: ``ELECTRIC`` at most
|
|
65
|
+
**0.3 degC** below the setpoint, ``HIGH_DEMAND`` within
|
|
66
|
+
**(0.2, 0.7] degC**, ``ENERGY_SAVER`` within **(0.8, 1.0] degC**.
|
|
67
|
+
Whether the first two share one threshold is an open question, not a
|
|
68
|
+
finding. At 0.7 degC short, High Demand runs the element and Energy
|
|
69
|
+
Saver does not. These
|
|
70
|
+
are **entry** thresholds, not thermostats: in High Demand's steady
|
|
71
|
+
state this unit's history has the element on in 1 of 47 minutes
|
|
72
|
+
0.2-0.3 degC short and 168 of 169 more than 2.2 degC short.
|
|
73
|
+
Neither differential is published anywhere in the status message.
|
|
74
|
+
|
|
75
|
+
**Later in a stint it depends on the mode.** From one
|
|
76
|
+
unit's recorded history, 17 of 23 mid-stint upper-element starts in
|
|
77
|
+
``ENERGY_SAVER`` have the probe in the 39.4-41.1 degC band where
|
|
78
|
+
``heUpperOnTempSetting`` rests, a median 0.2 degC below the field -
|
|
79
|
+
the element comes on as the probe reaches it. ``HIGH_DEMAND``, whose
|
|
80
|
+
ON setting tracks the setpoint, re-engages a median 2.1 degC *below*
|
|
81
|
+
it, which the field does not describe. So in ``ENERGY_SAVER`` entry is
|
|
82
|
+
the exception and the reference description is the rule; in
|
|
83
|
+
``HIGH_DEMAND`` it is not; ``ELECTRIC`` is uncharacterised.
|
|
84
|
+
``data_conversions.rst``'s warning now says which applies when.
|
|
85
|
+
A sampling note with it: the element runs in two very different
|
|
86
|
+
lengths. Of 115 upper-element runs over eight months, 20 lasted under
|
|
87
|
+
a minute (median 16 s), carrying 0.6 kWh against 93 kWh for the rest.
|
|
88
|
+
They are real elements, and not negligible to the probe - a 20 s burst
|
|
89
|
+
is ~0.3 degC. Because ``currentPower`` samples at a median 30 s, a
|
|
90
|
+
one-minute-resampled power series can miss one entirely, making a
|
|
91
|
+
5.5 kW element look like 0.5 kW. Resample power with a maximum.
|
|
92
|
+
|
|
93
|
+
Confirmed on the device, one unit, 2026-09-21: in ``ENERGY_SAVER``
|
|
94
|
+
the upper element engages near ``heUpperOnTempSetting`` rather than a
|
|
95
|
+
fixed gap below the setpoint (setpoint lowered to 42.0 degC; a gap
|
|
96
|
+
predicted 22 degC; it engaged at 39.8, three times). It then runs to
|
|
97
|
+
~0.9 degC under the setpoint-tracking ``heUpperOffTempSetting``, not
|
|
98
|
+
to a fixed point. Raising the setpoint in that mode starts the element
|
|
99
|
+
through the entry rule. And ``heUpperOnTempSetting`` rests at 40.5
|
|
100
|
+
degC reliably only in ``ENERGY_SAVER`` - in ``HEAT_PUMP`` about half
|
|
101
|
+
the time.
|
|
102
|
+
|
|
103
|
+
Also documents Energy Saver's entry behaviour (the element engages
|
|
104
|
+
although ``heUpperOnTempSetting`` rests 33 degC below the tank), Electric's
|
|
105
|
+
upper-to-lower handover and the upper probe **falling** 1.9 degC while
|
|
106
|
+
the lower element runs, measured element power of 5,219 W against the
|
|
107
|
+
5,000 W rating, and that a mode write may not take effect while a TOU
|
|
108
|
+
window is in force. ``device_status.rst``'s Heating Elements notes now
|
|
109
|
+
point at it.
|
|
110
|
+
- **currentInstPower includes the heating elements.** The field
|
|
111
|
+
table and *Power and Energy Fields* in the protocol reference, and the
|
|
112
|
+
``current_inst_power`` field description, said it excludes element
|
|
113
|
+
power; ``track-energy.rst`` said it includes it. On a 240 V NWP500-65
|
|
114
|
+
it includes it: element-only minutes read about 5.1 kW, compressor
|
|
115
|
+
and element together about 5.5 kW, and the tank's heat gain agrees.
|
|
116
|
+
All four now say so, with the measurement and its limits (one model,
|
|
117
|
+
240 V only). (#139)
|
|
118
|
+
- **TOU does not suspend reservations.** ``schedule-operation.rst``
|
|
119
|
+
said twice that it does, and its own priority table said otherwise. On
|
|
120
|
+
one NWP500, inside an active peak window, a reservation entry moved the
|
|
121
|
+
setpoint at its scheduled minute, twice. What the window holds back is
|
|
122
|
+
the entry's **mode**, as it does a direct ``set_operation_mode()``.
|
|
123
|
+
The mode is held, not discarded: a mode written in the window took
|
|
124
|
+
effect when TOU was switched off, and the upper element came on. The
|
|
125
|
+
priority table and Important Notes are corrected, and a new section
|
|
126
|
+
gives the measurements and their limits. ``heating-elements.rst`` now
|
|
127
|
+
links to it instead of to the issue. (#141)
|
|
128
|
+
- **tank-energy: what selects the second branch, and where usable_energy
|
|
129
|
+
under-reads.** ``tank-energy.rst`` left open what selects
|
|
130
|
+
``full_recovery_energy``'s second branch. On one NWP500-65 over 50,819
|
|
131
|
+
``HEAT_PUMP`` minutes, ``hpUpperOnTempSetting`` separates the branches
|
|
132
|
+
with no exceptions: 104.9 degF on the primary, raised to about the
|
|
133
|
+
setpoint during the afternoon TOU window on the secondary.
|
|
134
|
+
``energy_to_setpoint`` shifts by the same 2 degC. Because of that,
|
|
135
|
+
``usable_energy`` is not always robust to the branch. In the window it
|
|
136
|
+
caps at ``full_recovery_energy`` once ``energy_to_setpoint`` hits 0,
|
|
137
|
+
up to about 3.6 degF short of the setpoint, and under-reads by up to
|
|
138
|
+
561 Wh. The page and the ``usable_energy`` docstring now say so and
|
|
139
|
+
recommend the thermistors in that case. The ``full_recovery_energy``
|
|
140
|
+
and ``energy_to_setpoint`` descriptions in the model, the protocol
|
|
141
|
+
reference, the API reference and ``track-energy.rst`` now note the
|
|
142
|
+
window too. (#140)
|
|
143
|
+
|
|
8
144
|
Version 9.4.2 (2026-09-17)
|
|
9
145
|
==========================
|
|
10
146
|
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
====================
|
|
2
|
+
The Heating Elements
|
|
3
|
+
====================
|
|
4
|
+
|
|
5
|
+
The NWP500 publishes four element thermostat settings -
|
|
6
|
+
``heUpperOnTempSetting``, ``heUpperOffTempSetting`` and their lower-zone
|
|
7
|
+
pair - plus a differential for each. Read literally they describe a
|
|
8
|
+
thermostat, and how far that holds depends on the mode and the moment:
|
|
9
|
+
|
|
10
|
+
* **On entry to any element mode they do not** describe when the element
|
|
11
|
+
runs. It engages on a mode-dependent differential of its own, published
|
|
12
|
+
nowhere - the start thresholds below.
|
|
13
|
+
* **Later in a stint, in** ``ENERGY_SAVER`` **they do**: the element
|
|
14
|
+
engages as the probe reaches ``heUpperOnTempSetting``. That is from this
|
|
15
|
+
unit's history and confirmed by a commanded run.
|
|
16
|
+
* **Later in a stint, in** ``HIGH_DEMAND`` **they do not**: it re-engages
|
|
17
|
+
about 2.1 degC below its ON setting. That is from history only.
|
|
18
|
+
``ELECTRIC`` is uncharacterised.
|
|
19
|
+
|
|
20
|
+
The fields also look wrong as a thermostat taken literally: in Electric
|
|
21
|
+
and High Demand the on and off settings are **the same value**, and every
|
|
22
|
+
``he*DiffTempSetting`` reads 0.
|
|
23
|
+
|
|
24
|
+
**The start thresholds** here were measured on one unit by commanding
|
|
25
|
+
the modes and watching - not inferred from historical logs. That
|
|
26
|
+
distinction matters: two earlier values came from "the smallest deficit
|
|
27
|
+
ever seen to start an element" in a passive record, and both were wrong,
|
|
28
|
+
because such a figure measures where the tank happened to sit rather
|
|
29
|
+
than where the device decides.
|
|
30
|
+
|
|
31
|
+
Two things on this page are *not* from commanded runs, and are labelled
|
|
32
|
+
where they appear: High Demand's **steady-state** re-engagement, which
|
|
33
|
+
is this unit's history, and the mid-stint behaviour it is contrasted
|
|
34
|
+
with. Everything else is a bounded run.
|
|
35
|
+
|
|
36
|
+
.. contents::
|
|
37
|
+
:local:
|
|
38
|
+
:depth: 2
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
The short version
|
|
42
|
+
=================
|
|
43
|
+
|
|
44
|
+
.. list-table::
|
|
45
|
+
:header-rows: 1
|
|
46
|
+
:widths: 34 66
|
|
47
|
+
|
|
48
|
+
* - Observation
|
|
49
|
+
- Value
|
|
50
|
+
* - Element start differential
|
|
51
|
+
- **Mode-dependent, one bracket per mode.** ``ELECTRIC`` at most
|
|
52
|
+
**0.3 degC (0.54 degF)** below the setpoint; ``HIGH_DEMAND``
|
|
53
|
+
somewhere in **(0.2, 0.7] degC** ((0.36, 1.26] degF);
|
|
54
|
+
``ENERGY_SAVER`` in **(0.8, 1.0] degC** ((1.44, 1.80] degF).
|
|
55
|
+
Each bracket has a run on either side of it - except Electric's
|
|
56
|
+
lower edge, whose only negative sits *above* the setpoint.
|
|
57
|
+
* - Where that differential is published
|
|
58
|
+
- **Nowhere.** ``heUpperOnDiffTempSetting`` and its three siblings
|
|
59
|
+
read 0 on this unit while the behaviour above holds.
|
|
60
|
+
* - ``heUpperOnTempSetting`` by mode
|
|
61
|
+
- ``ELECTRIC`` and ``HIGH_DEMAND``: the setpoint, same as
|
|
62
|
+
``heUpperOffTempSetting``. ``ENERGY_SAVER``: 40.5 degC
|
|
63
|
+
(104.9 degF), the device minimum, in 96 % of recorded minutes on
|
|
64
|
+
this unit. ``HEAT_PUMP``: 40.5 degC only about **half** the time;
|
|
65
|
+
the rest are other values that match the current setpoint only
|
|
66
|
+
6 % of the time. Why is not known.
|
|
67
|
+
* - Energy Saver on entry
|
|
68
|
+
- Switching into ``ENERGY_SAVER`` engages the upper element even
|
|
69
|
+
though ``heUpperOnTempSetting`` rests 33 degC below the tank, so
|
|
70
|
+
**on entry** that field is not what drives it. Its bracket also
|
|
71
|
+
sits higher than the other two modes'. Why the two differ is
|
|
72
|
+
**not** established: the Energy Saver runs had the compressor
|
|
73
|
+
running and the others did not.
|
|
74
|
+
* - Energy Saver later in a stint
|
|
75
|
+
- Different rule. The element re-engages **as the upper probe
|
|
76
|
+
reaches** ``heUpperOnTempSetting`` - the field working exactly
|
|
77
|
+
as this reference describes it. Entry is the exception, not the
|
|
78
|
+
rule.
|
|
79
|
+
* - Electric ordering
|
|
80
|
+
- Upper element to the setpoint, then the lower element, never
|
|
81
|
+
both, as the protocol reference already states. The handover
|
|
82
|
+
takes one poll.
|
|
83
|
+
* - Element power
|
|
84
|
+
- 5,200-5,242 W over three measurements (two upper, one lower),
|
|
85
|
+
metered with the compressor off. The reference gives 5,000 W at
|
|
86
|
+
240 V.
|
|
87
|
+
* - Zone coupling
|
|
88
|
+
- While the lower element runs, the **upper** probe falls about
|
|
89
|
+
1.9 degC (3.4 degF) before recovering. The zones are not
|
|
90
|
+
independent.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
The start differential
|
|
94
|
+
======================
|
|
95
|
+
|
|
96
|
+
Three modes, three brackets, and no two of them the same width. Each
|
|
97
|
+
run holds a fixed deficit and watches for one to two minutes; the
|
|
98
|
+
setpoint quantises to 0.5 degC, so the deficit is set by choosing a
|
|
99
|
+
setpoint step relative to the tank.
|
|
100
|
+
|
|
101
|
+
``ENERGY_SAVER``
|
|
102
|
+
----------------
|
|
103
|
+
|
|
104
|
+
.. list-table::
|
|
105
|
+
:header-rows: 1
|
|
106
|
+
:widths: 30 30 40
|
|
107
|
+
|
|
108
|
+
* - Deficit below setpoint
|
|
109
|
+
- Upper element
|
|
110
|
+
- Poll at which it engaged
|
|
111
|
+
* - 0.3 degC (0.54 degF)
|
|
112
|
+
- **off** for the whole watch
|
|
113
|
+
- n/a
|
|
114
|
+
* - 0.8 degC (1.44 degF)
|
|
115
|
+
- **off** for the whole watch
|
|
116
|
+
- n/a
|
|
117
|
+
* - 1.0 degC (1.80 degF)
|
|
118
|
+
- **on**
|
|
119
|
+
- first poll after the mode landed
|
|
120
|
+
* - 1.2 degC (2.16 degF)
|
|
121
|
+
- **on**
|
|
122
|
+
- first poll after the mode landed
|
|
123
|
+
|
|
124
|
+
So the boundary lies in (0.8, 1.0] degC, and 1.0 degC is the device's
|
|
125
|
+
own storage granularity - settings are held in half-degree Celsius
|
|
126
|
+
steps - which makes a one-degree differential the natural parameter.
|
|
127
|
+
|
|
128
|
+
A further run put the element on at 2.0 degC (3.6 degF) short, so
|
|
129
|
+
nothing above the boundary has been seen to decline.
|
|
130
|
+
|
|
131
|
+
``ELECTRIC``
|
|
132
|
+
------------
|
|
133
|
+
|
|
134
|
+
.. list-table::
|
|
135
|
+
:header-rows: 1
|
|
136
|
+
:widths: 30 30 40
|
|
137
|
+
|
|
138
|
+
* - Deficit below setpoint
|
|
139
|
+
- Upper element
|
|
140
|
+
- Poll at which it engaged
|
|
141
|
+
* - 0.6 degC **above** the setpoint
|
|
142
|
+
- **off** for the whole watch
|
|
143
|
+
- n/a
|
|
144
|
+
* - 0.3 degC (0.54 degF)
|
|
145
|
+
- **on**
|
|
146
|
+
- first poll after the mode landed
|
|
147
|
+
* - 0.4 degC (0.72 degF)
|
|
148
|
+
- **on**
|
|
149
|
+
- first poll after the mode landed
|
|
150
|
+
|
|
151
|
+
``HIGH_DEMAND``
|
|
152
|
+
---------------
|
|
153
|
+
|
|
154
|
+
.. list-table::
|
|
155
|
+
:header-rows: 1
|
|
156
|
+
:widths: 30 30 40
|
|
157
|
+
|
|
158
|
+
* - Deficit below setpoint
|
|
159
|
+
- Upper element
|
|
160
|
+
- Poll at which it engaged
|
|
161
|
+
* - 0.2 degC (0.36 degF)
|
|
162
|
+
- **off** for the whole watch
|
|
163
|
+
- n/a
|
|
164
|
+
* - 0.7 degC (1.26 degF)
|
|
165
|
+
- **on**
|
|
166
|
+
- first poll after the mode landed
|
|
167
|
+
|
|
168
|
+
``heUpperOnTempSetting`` moved to the new setpoint in the same status
|
|
169
|
+
message the mode landed in, as it does in Electric.
|
|
170
|
+
|
|
171
|
+
Putting the three together
|
|
172
|
+
--------------------------
|
|
173
|
+
|
|
174
|
+
.. list-table::
|
|
175
|
+
:header-rows: 1
|
|
176
|
+
:widths: 24 26 24 26
|
|
177
|
+
|
|
178
|
+
* - Mode
|
|
179
|
+
- Element off at
|
|
180
|
+
- Element on at
|
|
181
|
+
- Bracket
|
|
182
|
+
* - ``ELECTRIC``
|
|
183
|
+
- 0.6 degC above setpoint
|
|
184
|
+
- 0.3, 0.4 degC
|
|
185
|
+
- at most 0.3 degC [#electric]_
|
|
186
|
+
* - ``HIGH_DEMAND``
|
|
187
|
+
- 0.2 degC
|
|
188
|
+
- 0.7 degC
|
|
189
|
+
- (0.2, 0.7] degC
|
|
190
|
+
* - ``ENERGY_SAVER``
|
|
191
|
+
- 0.3, 0.8 degC
|
|
192
|
+
- 1.0, 1.2 degC
|
|
193
|
+
- (0.8, 1.0] degC
|
|
194
|
+
|
|
195
|
+
**These are entry thresholds, not thermostats.** Every run measured what
|
|
196
|
+
happens within a poll of switching *into* a mode. In ``HIGH_DEMAND``'s
|
|
197
|
+
steady state this unit's history has the upper element on in 1 of 47
|
|
198
|
+
minutes 0.2-0.3 degC short and 168 of 169 minutes more than 2.2 degC
|
|
199
|
+
short - so a stint already running re-engages on something much larger
|
|
200
|
+
than the entry figure. Do not use one for the other.
|
|
201
|
+
|
|
202
|
+
.. [#electric] Electric's own negative is 0.6 degC *above* the setpoint,
|
|
203
|
+
so nothing at all was observed between there and 0.3 below it. Its
|
|
204
|
+
bracket is open at the bottom: no run has shown Electric declining
|
|
205
|
+
to heat a tank that is short of the setpoint by any amount.
|
|
206
|
+
|
|
207
|
+
**Electric and High Demand may share one entry threshold. That is an
|
|
208
|
+
inference, not a measurement.** Their brackets overlap in
|
|
209
|
+
(0.2, 0.3] degC and the two modes handle ``heUpperOnTempSetting``
|
|
210
|
+
identically, which is a real argument for one number covering both -
|
|
211
|
+
but High Demand has never been watched between 0.2 and 0.7 degC, so
|
|
212
|
+
nothing here measures where its boundary falls inside that span.
|
|
213
|
+
Energy Saver's band does not intersect either of them: at 0.7 degC
|
|
214
|
+
short, High Demand runs the element and Energy Saver does not. One
|
|
215
|
+
caveat on that last comparison: the Energy Saver runs had the
|
|
216
|
+
compressor running and the others did not, so mode is not the only
|
|
217
|
+
difference between them.
|
|
218
|
+
|
|
219
|
+
A consumer wanting one number per mode should take the **top of that
|
|
220
|
+
mode's own bracket** - 0.3 degC for ``ELECTRIC``, **0.7 degC** for
|
|
221
|
+
``HIGH_DEMAND``, 1.0 degC for ``ENERGY_SAVER``. Each is the **smallest
|
|
222
|
+
deficit at which that mode was seen to start an element**, so assuming
|
|
223
|
+
it contradicts no observation of that mode - where anything lower
|
|
224
|
+
contradicts that mode's own negative. Borrowing Electric's 0.3 degC for High Demand predicts an
|
|
225
|
+
element at, say, 0.4 degC short that High Demand has never been seen to
|
|
226
|
+
run; if your consumer is deciding whether hot water will be ready,
|
|
227
|
+
that is the expensive direction to be wrong in.
|
|
228
|
+
|
|
229
|
+
**The differential is not in the protocol.** A ``status --raw`` taken
|
|
230
|
+
between these runs shows::
|
|
231
|
+
|
|
232
|
+
"heUpperOnTempSetting": 81, # 40.5 degC
|
|
233
|
+
"heUpperOffTempSetting": 122, # 61.0 degC
|
|
234
|
+
"heUpperOnDiffTempSetting": 0,
|
|
235
|
+
"heUpperOffDiffTempSetting": 0,
|
|
236
|
+
"heLowerOnTDiffempSetting": 0,
|
|
237
|
+
"heLowerOffDiffTempSetting": 0,
|
|
238
|
+
|
|
239
|
+
A consumer that wants to predict when an element will run therefore
|
|
240
|
+
cannot read it from the status message; it has to assume the per-mode
|
|
241
|
+
figures above - 0.3 degC for ``ELECTRIC``, 0.7 for ``HIGH_DEMAND``,
|
|
242
|
+
1.0 for ``ENERGY_SAVER`` - and treat a tank inside the corresponding
|
|
243
|
+
bracket as a case where the device may go either way. High Demand's
|
|
244
|
+
bracket is the widest, so that "either way" span is widest there too.
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
What the on-setting does and does not tell you
|
|
248
|
+
==============================================
|
|
249
|
+
|
|
250
|
+
``heUpperOnTempSetting`` tracks the **mode**, not the moment:
|
|
251
|
+
|
|
252
|
+
* In ``ELECTRIC`` and ``HIGH_DEMAND`` it equals ``heUpperOffTempSetting``
|
|
253
|
+
and both equal the DHW setpoint. Taken literally that is a thermostat
|
|
254
|
+
with no hysteresis at all, which cannot be how the device behaves; on
|
|
255
|
+
entry the measured differential is at most 0.3 degC in Electric and
|
|
256
|
+
within (0.2, 0.7] degC in High Demand.
|
|
257
|
+
* In ``ENERGY_SAVER`` it rests at 40.5 degC, the device minimum, which
|
|
258
|
+
is 33 degC below a normally charged tank. An earlier version of this
|
|
259
|
+
page said the same of ``HEAT_PUMP``; on this unit that holds in only
|
|
260
|
+
48.5 % of Heat Pump minutes. The rest are other values - 63.0 degC
|
|
261
|
+
most often - which equal the *current* setpoint in only 6 % of Heat
|
|
262
|
+
Pump minutes, so this is not the field tracking the setpoint; it may
|
|
263
|
+
be holding a value over from an earlier mode. Do not rely on it in
|
|
264
|
+
Heat Pump mode.
|
|
265
|
+
|
|
266
|
+
The second case misleads **on entry only**. Switching into
|
|
267
|
+
``ENERGY_SAVER`` with the tank 1.0 degC or more short brought the upper
|
|
268
|
+
element on within one poll in both runs tested, while
|
|
269
|
+
``heUpperOnTempSetting`` dropped from the setpoint to 40.5 degC in the
|
|
270
|
+
*same* status message - 33 degC below the probe. For that moment the
|
|
271
|
+
field explains nothing.
|
|
272
|
+
|
|
273
|
+
That is as far as the entry observation goes. It does not explain *why*
|
|
274
|
+
Energy Saver's bracket sits higher than Electric's; the runs differ in
|
|
275
|
+
compressor state as well as mode, and nothing here separates the two.
|
|
276
|
+
|
|
277
|
+
**Later in the same stint the field is exactly what drives the
|
|
278
|
+
element.** In 23 mid-stint upper-element starts in ``ENERGY_SAVER``
|
|
279
|
+
drawn from one unit's recorded history, **17 have the upper probe
|
|
280
|
+
between 39.4 and 41.1 degC** - the band ``heUpperOnTempSetting`` rests
|
|
281
|
+
in for this mode - and where the field itself is recorded the median
|
|
282
|
+
start is **0.2 degC below it**. The element comes on as the probe
|
|
283
|
+
reaches the on-setting, which is what this reference has said all
|
|
284
|
+
along.
|
|
285
|
+
|
|
286
|
+
**Confirmed on the device.** The history above cannot prove it on its
|
|
287
|
+
own: with the setpoint fixed for almost the whole record, "at the
|
|
288
|
+
on-setting" and "a fixed gap below the setpoint" are the same
|
|
289
|
+
prediction. So the setpoint was lowered to 42.0 degC and the unit left
|
|
290
|
+
in ``ENERGY_SAVER``. With the on-setting still at 40.5 degC, a fixed gap
|
|
291
|
+
would have put the element near 22 degC; it came on three times with
|
|
292
|
+
the upper probe reading **39.8 degC**. Two cautions: all three came
|
|
293
|
+
while water was moving through the tank, and readings of 40.4, 40.1 and
|
|
294
|
+
40.0 degC did not trigger, so on this probe the working threshold may
|
|
295
|
+
sit a little under the published 40.5.
|
|
296
|
+
|
|
297
|
+
``HIGH_DEMAND`` behaves differently, and this is the clearest statement
|
|
298
|
+
of the difference on this page. There ``heUpperOnTempSetting`` tracks
|
|
299
|
+
the setpoint, and 12 mid-stint starts came a median **2.1 degC below
|
|
300
|
+
it**, none in the 39.4-41.1 degC band. That differential is real and is
|
|
301
|
+
published nowhere.
|
|
302
|
+
|
|
303
|
+
So a consumer needs both halves:
|
|
304
|
+
|
|
305
|
+
.. list-table::
|
|
306
|
+
:header-rows: 1
|
|
307
|
+
:widths: 26 37 37
|
|
308
|
+
|
|
309
|
+
* - Mode
|
|
310
|
+
- On entry
|
|
311
|
+
- Later in the stint
|
|
312
|
+
* - ``ENERGY_SAVER``
|
|
313
|
+
- element on, on-setting irrelevant (1.0 degC below setpoint)
|
|
314
|
+
- element on **near** ``heUpperOnTempSetting``
|
|
315
|
+
* - ``HIGH_DEMAND``
|
|
316
|
+
- element on, (0.2, 0.7] degC below setpoint
|
|
317
|
+
- element on ~2.1 degC **below** ``heUpperOnTempSetting``
|
|
318
|
+
|
|
319
|
+
Three more behaviours, from the same commanded run. Each is one unit
|
|
320
|
+
and one morning; the counts are given so they can be weighed.
|
|
321
|
+
|
|
322
|
+
* **The element runs to the setpoint, not to a fixed point.** It
|
|
323
|
+
stopped about 0.9 degC under ``heUpperOffTempSetting``, which follows
|
|
324
|
+
the setpoint. At a 42.0 degC setpoint that was 41.1 degC; historical
|
|
325
|
+
runs at a 60.5 degC setpoint stop around 59. Only the *engagement*
|
|
326
|
+
point is fixed.
|
|
327
|
+
* **Raising the setpoint in** ``ENERGY_SAVER`` **starts the element.**
|
|
328
|
+
With the tank at 41.7 degC, a setpoint raise to 61.0 degC switched the
|
|
329
|
+
heat source to heat pump plus element in the same second (n = 1). It
|
|
330
|
+
is the entry rule again, triggered by the setpoint rather than a mode
|
|
331
|
+
change. A controller writing setpoints in this mode can start a 5 kW
|
|
332
|
+
element.
|
|
333
|
+
* **Every engagement came with water moving through the tank** — one
|
|
334
|
+
shower and two runs of a recirculation pump on a cooled loop, which
|
|
335
|
+
the tank experiences as draws. Whether movement is *required*, or the
|
|
336
|
+
temperature alone suffices, is not separable from these.
|
|
337
|
+
|
|
338
|
+
.. warning::
|
|
339
|
+
|
|
340
|
+
These mid-stint figures come from **recorded history, not commanded
|
|
341
|
+
runs**, and they carry a sampling hazard worth repeating - one that
|
|
342
|
+
bit us.
|
|
343
|
+
|
|
344
|
+
**The element runs in two very different lengths.** Of 115 raw
|
|
345
|
+
upper-element runs on this unit over eight months, 20 (17 %) lasted
|
|
346
|
+
**under a minute** - median 16 s - and 95 ran a median 8 minutes.
|
|
347
|
+
The short ones are real elements at full power. They carry little
|
|
348
|
+
energy - 0.6 kWh across the record against 93 kWh for the rest -
|
|
349
|
+
but they are not negligible to the probe: a 20-second burst raises
|
|
350
|
+
the upper zone about 0.3 degC, three quanta of its 0.1 degC
|
|
351
|
+
resolution, and measured bursts of 41-58 s moved it 0.7-1.5 degC.
|
|
352
|
+
*(An earlier version said they "heat nothing", from dividing by the
|
|
353
|
+
whole tank's heat capacity rather than the upper zone's - three
|
|
354
|
+
times too large.)*
|
|
355
|
+
|
|
356
|
+
They are also easy to lose or to misread. ``currentPower`` samples
|
|
357
|
+
at a median 30 s, with 24 % of gaps over a minute, so **a
|
|
358
|
+
one-minute-resampled power series can miss a sub-minute burst
|
|
359
|
+
entirely** - taking the last sample at or before each minute makes a
|
|
360
|
+
real 5.5 kW element look like 0.5 kW. Resample power with a
|
|
361
|
+
**maximum** over the interval, or work from the raw edges, and
|
|
362
|
+
corroborate a run's duration rather than a single aligned sample.
|
|
363
|
+
|
|
364
|
+
For predicting recovery, filter on **duration or energy**, not on
|
|
365
|
+
the flag and not on one power reading: it is the sustained runs that
|
|
366
|
+
heat the tank.
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
Electric: the handover, and what happens to the upper zone
|
|
370
|
+
===========================================================
|
|
371
|
+
|
|
372
|
+
The protocol reference states that elements do not operate
|
|
373
|
+
simultaneously in Electric mode. Confirmed, and the order is upper
|
|
374
|
+
first: in a six-minute run the upper element stopped and the lower one
|
|
375
|
+
appeared in the next poll, with no overlap.
|
|
376
|
+
|
|
377
|
+
Two details a consumer modelling the tank should know:
|
|
378
|
+
|
|
379
|
+
**The upper probe falls while the lower element runs.** It peaked at
|
|
380
|
+
63.3 degC (145.9 degF), fell to 61.4 (142.5) over the following three
|
|
381
|
+
minutes, then recovered to 62.3 (144.1) as the tank mixed. Anything
|
|
382
|
+
that treats "upper zone reached X" as monotonic during an Electric
|
|
383
|
+
recovery will be wrong by about 1.9 degC for several minutes.
|
|
384
|
+
|
|
385
|
+
**The off point lags in both directions.** The upper element switched
|
|
386
|
+
off with the probe 0.8 degC (1.4 degF) *below* the setpoint, and the
|
|
387
|
+
probe then coasted 1.3 degC (2.3 degF) *past* it. The device's decision
|
|
388
|
+
and the probe are not the same instant.
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
Element power
|
|
392
|
+
=============
|
|
393
|
+
|
|
394
|
+
Metered at the whole-unit power reading with the compressor off, so it
|
|
395
|
+
is the element alone:
|
|
396
|
+
|
|
397
|
+
* upper element: 5,219 W on one run (5,205-5,242 over 33 samples) and
|
|
398
|
+
5,242 W on a second
|
|
399
|
+
* lower element: 5,200 W
|
|
400
|
+
|
|
401
|
+
The reference gives 3,755 W at 208 V or 5,000 W at 240 V. The measured
|
|
402
|
+
figure is 4 % above the 240 V rating on this unit, which is one unit at
|
|
403
|
+
one supply voltage - a nudge for anyone integrating energy, not a
|
|
404
|
+
correction to the specification.
|
|
405
|
+
|
|
406
|
+
For the record, the upper zone rose at 0.87-0.89 degC/min
|
|
407
|
+
(1.56-1.61 degF/min) end to end over 5.8 minutes on a 63.2 gal tank -
|
|
408
|
+
the spread is sampling resolution, the higher figure from 10-second
|
|
409
|
+
polls and the lower from a one-minute grid - and the lower zone at
|
|
410
|
+
0.66-0.86 degC/min depending on where the fit starts; the probe lags the
|
|
411
|
+
element for the first 40-60 seconds, which is most of the difference.
|
|
412
|
+
Those are tank-specific and are offered as an order of magnitude only.
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
Writing the mode while a TOU window is in force
|
|
416
|
+
===============================================
|
|
417
|
+
|
|
418
|
+
Relevant when planning any of this: with TOU scheduling enabled and a
|
|
419
|
+
window in force, a mode write may not take effect. Measured on the same
|
|
420
|
+
unit, four minutes with no change and nothing logged, twice - and the
|
|
421
|
+
command was not discarded either: it took effect one poll after the TOU
|
|
422
|
+
switch was turned off, with nothing else commanded.
|
|
423
|
+
|
|
424
|
+
Whether the device queues the command or accepts it and masks the
|
|
425
|
+
read-back is undetermined. Either way, **confirm a mode write by reading
|
|
426
|
+
the mode back, and do not treat an unconfirmed one as failed.**
|
|
427
|
+
|
|
428
|
+
Reservation entries are a separate case. Their target temperature
|
|
429
|
+
applies normally in a window, and only their mode is held back; see
|
|
430
|
+
:ref:`reservations and mode writes during a tou window`.
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
Limits
|
|
434
|
+
======
|
|
435
|
+
|
|
436
|
+
One unit, one supply voltage, 2026-09-19 and 2026-09-20. **Twenty-two**
|
|
437
|
+
bounded runs in all: thirteen on the elements and the thresholds, nine
|
|
438
|
+
on mode writes and reservation entries under TOU. Specifically **not**
|
|
439
|
+
established:
|
|
440
|
+
|
|
441
|
+
* **where inside (0.2, 0.7] degC ``HIGH_DEMAND``'s boundary falls.** It
|
|
442
|
+
has a run on each side, so it is bracketed - but that bracket is more
|
|
443
|
+
than twice Electric's width, and one run at 0.4 degC would halve it.
|
|
444
|
+
Until then, whether the two modes share a threshold is unresolved.
|
|
445
|
+
* **Electric's** behaviour later in a stint. Energy Saver's and High
|
|
446
|
+
Demand's are described above from history; Electric has only two
|
|
447
|
+
mid-stint starts on record, which characterises nothing.
|
|
448
|
+
* anything about runs longer than six minutes.
|
|
449
|
+
* whether either differential is firmware-dependent. The unit reported
|
|
450
|
+
``heUpperOnDiffTempSetting`` as 0 throughout; if another unit reports
|
|
451
|
+
it as non-zero, that field is the better answer and this page is a
|
|
452
|
+
description of one firmware's default.
|