nwp500-python 9.3.2__tar.gz → 9.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/CHANGELOG.rst +173 -0
- {nwp500_python-9.3.2/src/nwp500_python.egg-info → nwp500_python-9.4.1}/PKG-INFO +1 -1
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/explanation/index.rst +1 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/explanation/tank-energy.rst +7 -7
- nwp500_python-9.4.1/docs/explanation/tou-recovery-cap.rst +188 -0
- nwp500_python-9.4.1/docs/how-to/maintenance.rst +139 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/schedule-operation.rst +36 -69
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/track-energy.rst +43 -15
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/protocol/data_conversions.rst +3 -3
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/protocol/device_status.rst +2 -2
- nwp500_python-9.4.1/docs/reference/protocol/mqtt_protocol.rst +1230 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/protocol/quick_reference.rst +61 -29
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/python_api/cli.rst +133 -24
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/python_api/events.rst +3 -1
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/python_api/models.rst +94 -58
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/python_api/mqtt_client.rst +174 -66
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/error_code_demo.py +3 -1
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/__init__.py +12 -4
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/cli/__main__.py +205 -16
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/cli/handlers.py +283 -1
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/cli/output_formatters.py +62 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/cli/presentation.py +251 -8
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/cli/rich_output.py +55 -10
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/converters.py +2 -2
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/enums.py +40 -16
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/__init__.py +13 -4
- nwp500_python-9.4.1/src/nwp500/models/diagnostics.py +147 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/energy.py +35 -3
- nwp500_python-9.4.1/src/nwp500/models/firmware.py +47 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/schedule.py +105 -157
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/status.py +13 -1
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/_control_commands.py +42 -33
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/_device_subscriptions.py +105 -17
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/client.py +76 -3
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/control.py +435 -157
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/reconnection.py +7 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/subscriptions.py +327 -157
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/utils.py +9 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/topic_builder.py +49 -6
- {nwp500_python-9.3.2 → nwp500_python-9.4.1/src/nwp500_python.egg-info}/PKG-INFO +1 -1
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500_python.egg-info/SOURCES.txt +4 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500_python.egg-info/scm_file_list.json +4 -0
- nwp500_python-9.4.1/src/nwp500_python.egg-info/scm_version.json +8 -0
- nwp500_python-9.4.1/tests/test_app_queries.py +901 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_cli_commands.py +434 -1
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_mqtt_reliability.py +39 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_protocol_correctness.py +353 -132
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_public_api.py +59 -0
- nwp500_python-9.3.2/docs/how-to/maintenance.rst +0 -119
- nwp500_python-9.3.2/docs/reference/protocol/mqtt_protocol.rst +0 -929
- nwp500_python-9.3.2/src/nwp500_python.egg-info/scm_version.json +0 -8
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/.coveragerc +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/.github/RESOLVING_PR_COMMENTS.md +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/.github/copilot-instructions.md +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/.github/workflows/ci.yml +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/.github/workflows/release.yml +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/.gitignore +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/.pre-commit-config.yaml +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/.readthedocs.yml +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/AUTHORS.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/CONTRIBUTING.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/LICENSE.txt +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/Makefile +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/README.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/RELEASE.md +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/Makefile +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/_static/.gitignore +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/conf.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/explanation/advanced-features.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/explanation/architecture.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/explanation/unknown-values.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/authenticate.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/auto-recovery.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/diagnose-mqtt.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/home-assistant.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/index.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/manage-units.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/monitor-status.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/optimize-tou.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/how-to/queue-commands.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/index.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/openapi.yaml +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/project/authors.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/project/changelog.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/project/contributing.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/project/history.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/project/license.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/configuration.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/enumerations.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/index.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/installation.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/protocol/device_features.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/protocol/error_codes.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/protocol/rest_api.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/python_api/api_client.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/python_api/auth_client.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/reference/python_api/exceptions.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/requirements.txt +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/docs/tutorials/getting-started.rst +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/.ruff.toml +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/README.md +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/air_filter_reset.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/anti_legionella.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/auto_recovery.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/combined_callbacks.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/demand_response.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/device_capabilities.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/device_status_debug.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/energy_analytics.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/firmware_payload_capture.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/mqtt_diagnostics.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/power_control.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/recirculation_control.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/reconnection_demo.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/reservation_schedule.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/simple_auto_recovery.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/token_restoration.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/tou_openei.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/tou_schedule.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/advanced/water_reservation.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/beginner/01_authentication.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/beginner/02_list_devices.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/beginner/03_get_status.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/beginner/04_set_temperature.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/advanced_auth_patterns.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/command_queue.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/device_status_callback.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/error_handling.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/event_driven_control.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/improved_auth.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/legacy_auth_constructor.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/mqtt_realtime_monitoring.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/periodic_requests.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/set_mode.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/intermediate/vacation_mode.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/mask.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/testing/periodic_device_info.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/testing/simple_periodic_info.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/testing/test_api_client.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/testing/test_mqtt_connection.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/testing/test_mqtt_messaging.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/examples/testing/test_periodic_minimal.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/pyproject.toml +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/scripts/README.md +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/scripts/bump_version.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/scripts/diagnose_mqtt_connection.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/scripts/extract_changelog.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/scripts/format.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/scripts/lint.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/scripts/setup-dev.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/scripts/validate_version.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/setup.cfg +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/setup.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/_base.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/api_client.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/auth.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/cli/__init__.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/cli/monitoring.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/cli/token_storage.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/command_decorators.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/config.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/device_capabilities.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/device_info_cache.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/encoding.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/events.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/exceptions.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/factory.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/field_factory.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/_converters.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/device.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/feature.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/mqtt_models.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/models/tou.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/__init__.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/command_queue.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/connection.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/diagnostics.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/periodic.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/state_tracker.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt/types.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/mqtt_events.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/openei.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/py.typed +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/reservations.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/temperature.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/tou_schedule.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/unit_system.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500/utils.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500_python.egg-info/entry_points.txt +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500_python.egg-info/not-zip-safe +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500_python.egg-info/requires.txt +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/src/nwp500_python.egg-info/top_level.txt +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/conftest.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_api_helpers.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_auth.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_auth_session_lifecycle.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_bug_fixes.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_canonical_schedule.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_cli_basic.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_cli_tristate_presentation.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_command_decorators.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_command_queue.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_device_capabilities.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_device_info_cache.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_device_rest_models.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_events.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_exceptions.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_model_converters.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_models.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_mqtt_clean_session_resume.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_mqtt_client_init.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_mqtt_events.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_mqtt_hypothesis.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_mqtt_reconnection.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_mqtt_reconnection_storm.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_multi_device.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_openei.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_reservations.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_temperature_converters.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_threading_model.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_token_storage.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_tou_api.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_tou_schedule.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_unit_switching.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_unit_system_process_wide.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_utility_modules.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tests/test_utils.py +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/tox.ini +0 -0
- {nwp500_python-9.3.2 → nwp500_python-9.4.1}/uv.lock +0 -0
|
@@ -5,6 +5,178 @@ Changelog
|
|
|
5
5
|
Unreleased
|
|
6
6
|
==========
|
|
7
7
|
|
|
8
|
+
Version 9.4.1 (2026-09-17)
|
|
9
|
+
==========================
|
|
10
|
+
|
|
11
|
+
Fixed
|
|
12
|
+
-----
|
|
13
|
+
- **The reconnect attempt counter never reset after a successful quick or
|
|
14
|
+
deep reconnect.** Both build a new connection, so the SDK never calls
|
|
15
|
+
``on_connection_resumed``, the only place the counter was reset. Each
|
|
16
|
+
later interruption started where the last one stopped: its first delay
|
|
17
|
+
was longer and it reached the deep-reconnect threshold sooner. A
|
|
18
|
+
long-running client saw the first delay grow from about 1 s to 12 s
|
|
19
|
+
over five daily AWS IoT disconnects. The counter now resets whenever the
|
|
20
|
+
backoff loop ends connected.
|
|
21
|
+
|
|
22
|
+
Version 9.4.0 (2026-09-12)
|
|
23
|
+
==========================
|
|
24
|
+
|
|
25
|
+
This release realigns the MQTT command set with what the NaviLink app
|
|
26
|
+
actually sends. The app (version 2.03.00) was decompiled and its request
|
|
27
|
+
builder compared with the library; every query it sends now has a
|
|
28
|
+
method, six commands it never sends were removed, and the recirculation
|
|
29
|
+
schedule write was corrected. Breaking changes are listed under
|
|
30
|
+
*Removed* and *Changed*.
|
|
31
|
+
|
|
32
|
+
Added
|
|
33
|
+
-----
|
|
34
|
+
- **Installer diagnostics query.** ``request_diagnostics()`` /
|
|
35
|
+
``subscribe_diagnostics()`` and the ``nwp-cli diagnostics`` command
|
|
36
|
+
return the lifetime counters the app's installer screen shows: heat
|
|
37
|
+
pump and element energy, days since installation, fault event counts,
|
|
38
|
+
demand-response operation times, hot-water draw statistics and
|
|
39
|
+
component run times and start counts, as
|
|
40
|
+
:class:`~nwp500.models.DeviceDiagnostics`. The device answers the
|
|
41
|
+
``st/td/rd`` query with packed hex; the NaviLink cloud decodes it into
|
|
42
|
+
JSON only when the reply topic has the app's five-segment form, so the
|
|
43
|
+
library requests that form. Verified live: the energies and compressor
|
|
44
|
+
and element run times match the energy query's lifetime totals.
|
|
45
|
+
- **Monthly energy query.** ``request_energy_usage_monthly()`` /
|
|
46
|
+
``subscribe_energy_usage_monthly()`` and ``nwp-cli energy --years``
|
|
47
|
+
return one entry per year with twelve per-month items. Verified live.
|
|
48
|
+
- **Hourly energy query.** ``request_energy_usage_hourly()`` /
|
|
49
|
+
``subscribe_energy_usage_hourly()`` send the app's per-hour query. The
|
|
50
|
+
NWP500 tested never answered it in four attempts, so it has no CLI
|
|
51
|
+
command.
|
|
52
|
+
- **Recirculation schedule read.** ``request_recirculation_schedule()``
|
|
53
|
+
and ``nwp-cli recirc-schedule get``; the write is exposed as
|
|
54
|
+
``nwp-cli recirc-schedule set``, which checks the JSON before
|
|
55
|
+
connecting (known keys only, integers in range, at most 20 entries).
|
|
56
|
+
The read was verified live on a unit that reports recirculation
|
|
57
|
+
scheduling but has no pump (empty, disabled schedule); the write and
|
|
58
|
+
its echo are unverified. The CLI reports a write as updated only when
|
|
59
|
+
the echo matches what was written. ``RecirculationSchedule`` and
|
|
60
|
+
``RecirculationScheduleEntry`` fields are strict integers, so ``True``
|
|
61
|
+
or ``6.0`` is rejected instead of coerced.
|
|
62
|
+
- **Firmware download info.** ``request_firmware_download_info()`` /
|
|
63
|
+
``subscribe_firmware_download_info()`` and ``nwp-cli firmware info``
|
|
64
|
+
return :class:`~nwp500.models.FirmwareDownloadInfo`. The device replies
|
|
65
|
+
on its own topic rather than a client-keyed one. Verified live.
|
|
66
|
+
- **Session end.** ``end_session()`` sends the ``st/end`` query the app
|
|
67
|
+
sends whenever it leaves a device. No reply to it has been observed.
|
|
68
|
+
Unlike other commands it is never queued while disconnected (it raises
|
|
69
|
+
``MqttNotConnectedError``), since a session end replayed after a
|
|
70
|
+
reconnect would end the new session.
|
|
71
|
+
- ``MqttConnectionConfig.send_session_end_on_disconnect`` (default
|
|
72
|
+
``True``) controls whether ``disconnect()`` sends it.
|
|
73
|
+
- ``subscribe_firmware_commit_response()`` receives the reply to
|
|
74
|
+
``commit_firmware_update()``, which is requested on the app-form
|
|
75
|
+
``res/commit-ota`` topic that no other subscription covers.
|
|
76
|
+
- **Controls with app builder cases that had no method:**
|
|
77
|
+
``set_vacation_duration(days)`` (``goout-day``; the app has a builder
|
|
78
|
+
case but no screen that calls it; verified live to change the day count
|
|
79
|
+
without changing the operation mode),
|
|
80
|
+
``set_air_filter_life(hours)`` (``air-filter-life``; the wire value is
|
|
81
|
+
hours divided by 500, matching the app's 0 or 1000-10000 picker;
|
|
82
|
+
verified live) and ``reset_condenser_fault()`` (``cond-fault-reset``,
|
|
83
|
+
installer-level in the app; acknowledged live, clearing effect not
|
|
84
|
+
observable without a fault). CLI: ``vacation-duration``,
|
|
85
|
+
``filter-life``, ``reset-condenser-fault``.
|
|
86
|
+
- **Command codes** for every member of the app's ``DeviceControlMGPP``
|
|
87
|
+
enum, with the declared-but-never-sent ones annotated.
|
|
88
|
+
- ``EnergyUsageResponse.type_of_usage``, ``get_year_data()``,
|
|
89
|
+
``MonthlyEnergyData.day`` and ``EnergyUsageDay.ep_usage`` /
|
|
90
|
+
``water_usage`` (declared by the app; not reported by the NWP500).
|
|
91
|
+
|
|
92
|
+
Changed
|
|
93
|
+
-------
|
|
94
|
+
- **Recirculation schedule models take the app's entry shape** (breaking).
|
|
95
|
+
:class:`~nwp500.models.RecirculationScheduleEntry` is now
|
|
96
|
+
``enable``/``week``/``hour``/``min``/``mode``/``param`` like a
|
|
97
|
+
reservation entry (``mode`` is the entry's pump on/off toggle in the
|
|
98
|
+
app's editor) and :class:`~nwp500.models.RecirculationSchedule` carries
|
|
99
|
+
``reservation_use`` plus ``reservation`` instead of ``schedule``. The
|
|
100
|
+
previous start/end window fields were not what the device accepts.
|
|
101
|
+
- ``MonthlyEnergyData.month`` is optional: monthly-query entries have no
|
|
102
|
+
month.
|
|
103
|
+
- **``NavienMqttClient.disconnect()`` now sends ``st/end``** to every
|
|
104
|
+
device that was passed to any subscription, as the NaviLink app does
|
|
105
|
+
when it leaves a device. The publishes go straight to the connection,
|
|
106
|
+
never into the offline command queue, stop if the connection drops,
|
|
107
|
+
are bounded by a short timeout and never fail the disconnect. What it
|
|
108
|
+
does to other clients connected to the same device (for example a Home
|
|
109
|
+
Assistant integration) is not known; turn it off with
|
|
110
|
+
``MqttConnectionConfig(send_session_end_on_disconnect=False)``.
|
|
111
|
+
- **CLI energy summaries total the requested periods.** ``energy
|
|
112
|
+
--month``, ``--months`` and ``--years`` printed the device's lifetime
|
|
113
|
+
total under a per-month or per-year heading. Each summary now covers
|
|
114
|
+
only the rows shown, and the lifetime total is printed once under its
|
|
115
|
+
own heading. (The response's ``total`` is lifetime whatever is
|
|
116
|
+
requested; checked live.)
|
|
117
|
+
- **Parameter validation.** ``set_air_filter_life()``,
|
|
118
|
+
``set_vacation_duration()`` and the daily, monthly and hourly energy
|
|
119
|
+
queries reject non-integers (including ``True`` and floats) and
|
|
120
|
+
out-of-range values; years must be 2000-2099 and ``--years`` drops
|
|
121
|
+
duplicates. ``filter-life``, ``--year`` and ``--years`` reject bad
|
|
122
|
+
input before connecting. ``request_energy_usage()`` now rejects an
|
|
123
|
+
empty month list.
|
|
124
|
+
|
|
125
|
+
Fixed
|
|
126
|
+
-----
|
|
127
|
+
- **``configure_recirculation_schedule()`` published to the wrong topic
|
|
128
|
+
with the wrong keys.** It sent ``{"schedule": [...]}`` on the bare
|
|
129
|
+
``ctrl`` topic; the app publishes RECIR_RESERVATION on
|
|
130
|
+
``ctrl/recirc-rsv/rd`` with ``reservationUse`` and ``reservation`` and
|
|
131
|
+
asks for the echo on ``recirc-rsv/rd``.
|
|
132
|
+
- **``commit_firmware_update()`` published to the bare ``ctrl`` topic.**
|
|
133
|
+
The app publishes OTA_COMMIT on ``ctrl/commit-ota`` and asks for the
|
|
134
|
+
reply on ``res/commit-ota``; the library now does the same. Not
|
|
135
|
+
exercised against a device.
|
|
136
|
+
- **Unsubscribing could remove the wrong handler.** When one callback was
|
|
137
|
+
registered for more than one device-wildcard subscription (status,
|
|
138
|
+
feature, firmware info), unsubscribing one removed whichever handler
|
|
139
|
+
came first. Handlers are now matched on subscription kind as well.
|
|
140
|
+
- **Query replies could reach another device's callback.** Client-keyed
|
|
141
|
+
reply topics are shared by every device a client queries, so with two
|
|
142
|
+
devices subscribed a reservation, energy, TOU, diagnostics or
|
|
143
|
+
recirculation reply reached both callbacks. Typed subscriptions now
|
|
144
|
+
ignore replies whose ``macAddress`` names a different device.
|
|
145
|
+
- Recirculation read-backs in hex carry the unused ``param`` as ``0xFF``
|
|
146
|
+
and in JSON as ``-1``; the model normalizes to ``-1`` so the two compare
|
|
147
|
+
equal.
|
|
148
|
+
- **Protocol documentation** rewritten from the decompiled request
|
|
149
|
+
builder and live captures: real topic structure, the anti-legionella
|
|
150
|
+
modes (``anti-leg-on [days]`` / ``anti-leg-off``, not
|
|
151
|
+
``anti-legionella-setting``), the message example that paired the
|
|
152
|
+
weekly-reservation code with ``dhw-temperature``, the undocumented
|
|
153
|
+
16777226 reservation write, response shapes for every query, and the
|
|
154
|
+
quick reference's invented ``header``/``body`` format with 0x11/0x21/0x31
|
|
155
|
+
codes that never existed. Stale ``docs/protocol/`` paths in code
|
|
156
|
+
comments and docstrings now point at ``docs/reference/protocol/``.
|
|
157
|
+
- ``models/schedule.py`` docstrings cited 33554444 (the hot button) for
|
|
158
|
+
the recirculation reservation; it is 33554440.
|
|
159
|
+
- CLI reference: token cache path is ``~/.nwp500_tokens.json``, ``-vv``
|
|
160
|
+
is a repeated ``-v`` (there was no ``--very-verbose``), and the
|
|
161
|
+
``--unit-system`` option and ``tou rates``/``plan``/``apply`` commands
|
|
162
|
+
are documented.
|
|
163
|
+
|
|
164
|
+
Removed
|
|
165
|
+
-------
|
|
166
|
+
- **Six MQTT commands the NaviLink app never sends** (breaking):
|
|
167
|
+
``check_firmware_update()``, ``reconnect_wifi()``, ``reset_wifi()``,
|
|
168
|
+
``set_freeze_protection_temperature()``, ``run_smart_diagnostic()`` and
|
|
169
|
+
``update_weekly_reservation()`` with
|
|
170
|
+
``subscribe_weekly_reservation_response()`` and the
|
|
171
|
+
``WeeklyReservationEntry`` / ``WeeklyReservationSchedule`` models. The
|
|
172
|
+
app's enum declares the first five codes but its request builder has
|
|
173
|
+
no case for them, so the library's ``ota-check``, ``wifi-reconnect``,
|
|
174
|
+
``wifi-reset``, ``frez-temp`` and ``smart-diagnostic`` mode strings were
|
|
175
|
+
invented. The app's weekly schedule is the ordinary reservation
|
|
176
|
+
program: it writes with 16777226 on ``ctrl/rsv/rd``
|
|
177
|
+
(``update_reservations()``), never with 33554438 or a ``rsv-weekly/rd``
|
|
178
|
+
topic. The ``CommandCode`` members stay, annotated as declared only.
|
|
179
|
+
|
|
8
180
|
Version 9.3.2 (2026-09-01)
|
|
9
181
|
==========================
|
|
10
182
|
|
|
@@ -129,6 +301,7 @@ Fixed
|
|
|
129
301
|
other clients on the same device. Query results the device routes back to a
|
|
130
302
|
client-keyed topic, such as reservations and energy usage, were already
|
|
131
303
|
covered by the existing response wildcard.
|
|
304
|
+
|
|
132
305
|
Version 9.3.0 (2026-08-03)
|
|
133
306
|
==========================
|
|
134
307
|
|
|
@@ -3,7 +3,7 @@ Tank Energy
|
|
|
3
3
|
===========
|
|
4
4
|
|
|
5
5
|
The NWP500 reports two energy figures, ``totalEnergyCapacity`` and
|
|
6
|
-
``availableEnergyCapacity``. Both names mislead, and before
|
|
6
|
+
``availableEnergyCapacity``. Both names mislead, and before v9.3.0 this
|
|
7
7
|
library also scaled them wrongly. This page explains what they
|
|
8
8
|
actually measure and shows the evidence for the correction.
|
|
9
9
|
|
|
@@ -33,7 +33,7 @@ The short version
|
|
|
33
33
|
whenever the setpoint moves.
|
|
34
34
|
|
|
35
35
|
Both are raw counts of 4 Wh each, not Watt-hours. Library versions before
|
|
36
|
-
|
|
36
|
+
9.3.0 multiplied by 10, overstating tank energy by 2.5x.
|
|
37
37
|
|
|
38
38
|
Both are also measured **from the setpoint**, so both describe potential
|
|
39
39
|
rather than content: move the setpoint and both change while the water in
|
|
@@ -226,7 +226,7 @@ coefficient of performance:
|
|
|
226
226
|
* - 4 Wh/count (corrected)
|
|
227
227
|
- 2.89
|
|
228
228
|
- Normal for a heat pump water heater
|
|
229
|
-
* - 10 Wh/count (pre-
|
|
229
|
+
* - 10 Wh/count (pre-9.3.0)
|
|
230
230
|
- 7.02
|
|
231
231
|
- Physically impossible
|
|
232
232
|
|
|
@@ -236,7 +236,7 @@ A heat pump water heater in a 72 degF room runs at a COP of roughly 2 to
|
|
|
236
236
|
This argument is worth stating separately because it needs no tank
|
|
237
237
|
volume, specific heat or stratification model - only the device's own
|
|
238
238
|
reported energy and its own reported power. It cannot tell you what the
|
|
239
|
-
quantum *is*, but it rules out the pre-
|
|
239
|
+
quantum *is*, but it rules out the pre-9.3.0 value regardless of anything
|
|
240
240
|
assumed elsewhere on this page.
|
|
241
241
|
|
|
242
242
|
.. _two branches:
|
|
@@ -403,8 +403,8 @@ the heat capacity is 156 Wh per degF:
|
|
|
403
403
|
drawable_Wh = 156 * (tank_mean_temperature - your_floor_degF)
|
|
404
404
|
|
|
405
405
|
|
|
406
|
-
Migrating from before
|
|
407
|
-
|
|
406
|
+
Migrating from before v9.3.0
|
|
407
|
+
============================
|
|
408
408
|
|
|
409
409
|
.. list-table::
|
|
410
410
|
:header-rows: 1
|
|
@@ -429,7 +429,7 @@ rename that is missed fails immediately with ``AttributeError`` instead
|
|
|
429
429
|
of silently returning a number 2.5x too large.
|
|
430
430
|
|
|
431
431
|
If you logged these values historically, the stored series needs
|
|
432
|
-
rescaling by 0.4 to be comparable with values from
|
|
432
|
+
rescaling by 0.4 to be comparable with values from v9.3.0 onward.
|
|
433
433
|
|
|
434
434
|
|
|
435
435
|
See also
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
==========================
|
|
2
|
+
The TOU Recovery Cap
|
|
3
|
+
==========================
|
|
4
|
+
|
|
5
|
+
When a Time-of-Use schedule is active, the NWP500 stops a heat-pump
|
|
6
|
+
recovery at about **90 % charge** instead of running to the setpoint. The
|
|
7
|
+
tank finishes roughly 1.9 degC below ``hp_lower_off_temp_setting`` and
|
|
8
|
+
stays there. Nothing in the protocol announces this, and no error is
|
|
9
|
+
raised - the compressor simply stops early.
|
|
10
|
+
|
|
11
|
+
This matters for anything that waits for the tank to reach its setpoint:
|
|
12
|
+
under an active TOU window that instant never arrives.
|
|
13
|
+
|
|
14
|
+
.. contents::
|
|
15
|
+
:local:
|
|
16
|
+
:depth: 2
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
The short version
|
|
20
|
+
=================
|
|
21
|
+
|
|
22
|
+
.. list-table::
|
|
23
|
+
:header-rows: 1
|
|
24
|
+
:widths: 30 70
|
|
25
|
+
|
|
26
|
+
* - Observation
|
|
27
|
+
- Value
|
|
28
|
+
* - Charge at termination
|
|
29
|
+
- ``dhw_charge_per`` = 90 % (p10 89, p90 91). Never reaches 100.
|
|
30
|
+
* - Shortfall below setpoint
|
|
31
|
+
- ``tank_lower_temperature`` finishes 1.9 degC (3.42 degF) below
|
|
32
|
+
``hp_lower_off_temp_setting``
|
|
33
|
+
* - Precondition
|
|
34
|
+
- An expensive TOU period in force, un-overridden. ``tou_status``
|
|
35
|
+
alone is **not** the test - see below.
|
|
36
|
+
* - Frequency
|
|
37
|
+
- 59 of 333 recoveries (18 %) over 260 days
|
|
38
|
+
|
|
39
|
+
A normal recovery, for contrast, ends with ``dhw_charge_per`` at 100 %
|
|
40
|
+
(p10 99) and the lower probe at or above the setpoint.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Evidence
|
|
44
|
+
========
|
|
45
|
+
|
|
46
|
+
Measured across 333 heat-pump recovery cycles of 15-480 minutes, from
|
|
47
|
+
2025-12-28 to 2026-09-06, on one unit. 196 reached the lower-off
|
|
48
|
+
setpoint; 137 did not.
|
|
49
|
+
|
|
50
|
+
The shortfall of the 137 is **bimodal**, not a spread:
|
|
51
|
+
|
|
52
|
+
.. code:: text
|
|
53
|
+
|
|
54
|
+
shortfall below hp_lower_off_temp_setting, cycles that never reached it
|
|
55
|
+
|
|
56
|
+
min 0.18 p10 1.66 p25 3.42 median 3.42 p75 4.32
|
|
57
|
+
p90 31.03 max 58.86 (degF)
|
|
58
|
+
|
|
59
|
+
most common values: 3.42 degF x 59 cycles 3.24 x 6 3.60 x 6
|
|
60
|
+
|
|
61
|
+
The spike at 3.42 degF is 1.9 degC exactly, and 19 steps of the 0.1 degC
|
|
62
|
+
tank-probe quantum. It holds across eight different setpoints from 140.0
|
|
63
|
+
to 147.2 degF and appears in all nine months observed, so it is an
|
|
64
|
+
offset in the control logic rather than a proportional effect or a
|
|
65
|
+
seasonal one. The long tail beyond 8 degF is ordinary interruption -
|
|
66
|
+
a draw starting mid-recovery, a mode change - and is unrelated.
|
|
67
|
+
|
|
68
|
+
``tou_status`` looks like a clean separator, and is not one:
|
|
69
|
+
|
|
70
|
+
.. list-table::
|
|
71
|
+
:header-rows: 1
|
|
72
|
+
:widths: 34 14 26 26
|
|
73
|
+
|
|
74
|
+
* - Population
|
|
75
|
+
- n
|
|
76
|
+
- ``tou_status`` True
|
|
77
|
+
- ``tou_override_status`` True
|
|
78
|
+
* - Stopped 3.42 degF short
|
|
79
|
+
- 59
|
|
80
|
+
- **59 / 59 (100 %)**
|
|
81
|
+
- 0 / 59
|
|
82
|
+
* - Other unreached
|
|
83
|
+
- 78
|
|
84
|
+
- 49 / 78 (63 %)
|
|
85
|
+
- 6 / 78
|
|
86
|
+
* - Reached setpoint
|
|
87
|
+
- 196
|
|
88
|
+
- 98 / 196 (50 %)
|
|
89
|
+
- 27 / 196
|
|
90
|
+
|
|
91
|
+
All 59 ran in ``HEAT_PUMP`` mode throughout, so this is not
|
|
92
|
+
``ENERGY_SAVER`` or ``VACATION`` behaviour.
|
|
93
|
+
|
|
94
|
+
But read that table carefully: 98 cycles reached the setpoint with
|
|
95
|
+
``tou_status`` True. **The flag only reports that TOU scheduling is
|
|
96
|
+
enabled.** It says nothing about whether the recovery ran inside an
|
|
97
|
+
expensive period, and the cap bites only when it did - outside one, an
|
|
98
|
+
enabled schedule does nothing at all.
|
|
99
|
+
|
|
100
|
+
The device marks a period itself. While inside one it applies non-zero
|
|
101
|
+
``*_diff_temp_setting`` offsets and reverts them at the end; on the unit
|
|
102
|
+
measured this toggles at 21:00 and 04:00 UTC daily. Using that as the
|
|
103
|
+
in-period test, among ``tou_status`` True cycles:
|
|
104
|
+
|
|
105
|
+
.. list-table::
|
|
106
|
+
:header-rows: 1
|
|
107
|
+
:widths: 34 22 22 22
|
|
108
|
+
|
|
109
|
+
* - At cycle end
|
|
110
|
+
- Capped
|
|
111
|
+
- Reached
|
|
112
|
+
- Other
|
|
113
|
+
* - Inside a period
|
|
114
|
+
- **42 (62 %)**
|
|
115
|
+
- 12
|
|
116
|
+
- 14
|
|
117
|
+
* - Outside a period
|
|
118
|
+
- 16 (12 %)
|
|
119
|
+
- 86
|
|
120
|
+
- 34
|
|
121
|
+
|
|
122
|
+
Test it at the moment the cycle *ended*, not when it began: a recovery
|
|
123
|
+
that starts off-peak and runs into a period is still capped, and testing
|
|
124
|
+
the start catches 17 of 59 against 43 testing the end.
|
|
125
|
+
|
|
126
|
+
Two plausible explanations were tested and **refuted**:
|
|
127
|
+
|
|
128
|
+
- *The upper zone satisfied its own cut-out first.* No -
|
|
129
|
+
``tank_upper_temperature`` reached ``hp_upper_off_temp_setting`` on
|
|
130
|
+
only 2 of 137 unreached cycles, against 179 of 196 reached ones. The
|
|
131
|
+
upper probe finishes short too, by a median of 4.5 degF.
|
|
132
|
+
- *The device terminates on outlet temperature.* No -
|
|
133
|
+
``dhw_outlet_temperature`` reached the setpoint on only 2 of 136.
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
What is not yet established
|
|
137
|
+
===========================
|
|
138
|
+
|
|
139
|
+
Being inside a period is a strong predictor but not a deterministic one:
|
|
140
|
+
12 cycles reached the setpoint from inside one, and 16 were capped from
|
|
141
|
+
outside. Some of that is the marker's own resolution - it is sampled at
|
|
142
|
+
the cycle end, and a period boundary crossed mid-recovery is not captured
|
|
143
|
+
- but it has not been reconciled against the schedule the device was
|
|
144
|
+
actually holding, which ``configure_tou_schedule_confirmed`` can read
|
|
145
|
+
back.
|
|
146
|
+
|
|
147
|
+
The 90 % figure has been observed on one unit under one TOU schedule. It
|
|
148
|
+
is not known whether the ceiling is fixed in firmware, derived from the
|
|
149
|
+
schedule, or configurable.
|
|
150
|
+
|
|
151
|
+
A note on reading ``tou_status`` over time: the field emits brief
|
|
152
|
+
``unknown`` values on integration reconnect - sub-second blips that
|
|
153
|
+
return immediately to the value they interrupted - so a raw state history
|
|
154
|
+
contains entries that are not transitions. Stores that keep only boolean
|
|
155
|
+
values drop these, which is the right behaviour and leaves a faithful
|
|
156
|
+
record of the genuine changes.
|
|
157
|
+
|
|
158
|
+
Do not read a low record count as lost data. It means the state was
|
|
159
|
+
*held*: on the unit measured, ``tou_status`` logged 165 changes in one
|
|
160
|
+
month and 5 in another, and the sparse month was the one in which TOU was
|
|
161
|
+
active nearly continuously. Forward-filling the genuine transitions is
|
|
162
|
+
correct. It can be corroborated against the ``*_diff_temp_setting``
|
|
163
|
+
fields, which toggle away from zero at the start of each TOU window and
|
|
164
|
+
back at the end.
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
Working with the cap
|
|
168
|
+
====================
|
|
169
|
+
|
|
170
|
+
**Do not wait for the setpoint.** Code that treats "tank reached
|
|
171
|
+
``hp_lower_off_temp_setting``" as the completion signal will block
|
|
172
|
+
indefinitely on a capped recovery. Watch ``dhw_charge_per`` plateauing,
|
|
173
|
+
or the compressor stopping, instead.
|
|
174
|
+
|
|
175
|
+
**Do not read a capped cycle as a fault or as degraded capacity.** The
|
|
176
|
+
appliance is doing what the schedule told it to. A recovery that ends at
|
|
177
|
+
90 % charge with ``tou_status`` True is a normal outcome.
|
|
178
|
+
|
|
179
|
+
**When measuring recovery duration, treat capped cycles separately.**
|
|
180
|
+
They are not censored observations of a full recovery - they are
|
|
181
|
+
complete observations of a different, shorter target. Pooling them with
|
|
182
|
+
uncapped recoveries biases any duration estimate downward; discarding
|
|
183
|
+
them biases it upward, because capped cycles are systematically shorter
|
|
184
|
+
(median 161 minutes against 235 for cycles that ran to setpoint).
|
|
185
|
+
|
|
186
|
+
**To force a full recovery,** set ``tou_override_status`` - none of the
|
|
187
|
+
59 capped cycles had it set, and 27 of the cycles that ran to setpoint
|
|
188
|
+
under an active TOU schedule did.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
==================
|
|
2
|
+
Device Maintenance
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
Maintenance commands cover firmware update information, the installer
|
|
6
|
+
diagnostics counters, air filter service intervals and fault resets. All
|
|
7
|
+
of them mirror what the NaviLink app sends; the CLI equivalents are noted
|
|
8
|
+
with each one.
|
|
9
|
+
|
|
10
|
+
.. contents:: On This Page
|
|
11
|
+
:local:
|
|
12
|
+
:depth: 2
|
|
13
|
+
|
|
14
|
+
Before You Start
|
|
15
|
+
================
|
|
16
|
+
|
|
17
|
+
Some maintenance operations are gated on device capabilities. Request
|
|
18
|
+
device features first so the client can inspect capability flags.
|
|
19
|
+
|
|
20
|
+
.. code-block:: python
|
|
21
|
+
|
|
22
|
+
await mqtt.subscribe_device_feature(device, lambda feature: print(feature))
|
|
23
|
+
await mqtt.request_device_info(device)
|
|
24
|
+
|
|
25
|
+
Installer Diagnostics
|
|
26
|
+
=====================
|
|
27
|
+
|
|
28
|
+
The NaviLink app's installer screen shows lifetime counters the device
|
|
29
|
+
keeps: heat pump and element energy, days since installation, fault
|
|
30
|
+
event counts, demand-response operation times, hot-water draw statistics
|
|
31
|
+
and component run times and start counts. The gate is in the app, so a
|
|
32
|
+
consumer account's unit answers the query.
|
|
33
|
+
|
|
34
|
+
.. code-block:: python
|
|
35
|
+
|
|
36
|
+
from nwp500 import DeviceDiagnostics
|
|
37
|
+
|
|
38
|
+
def on_diagnostics(diag: DeviceDiagnostics) -> None:
|
|
39
|
+
ts, ta = diag.ts_data, diag.ta_data
|
|
40
|
+
print(f"Installed {ts.days_since_installation} days ago")
|
|
41
|
+
print(f"Heat pump: {ts.cumulated_pwr_hp} Wh over "
|
|
42
|
+
f"{ta.cumulated_op_time_comp} h, {ta.cumulated_op_num_comp} starts")
|
|
43
|
+
print(f"Condensate overflows: {ts.cumulated_occ_num_con_ovr_flow}")
|
|
44
|
+
|
|
45
|
+
await mqtt.subscribe_diagnostics(device, on_diagnostics)
|
|
46
|
+
await mqtt.request_diagnostics(device)
|
|
47
|
+
|
|
48
|
+
The two energies are watt-hours and ``cumulated_op_time_*`` values are
|
|
49
|
+
hours (both cross-checked against the energy query); the remaining
|
|
50
|
+
counters have no documented unit. See
|
|
51
|
+
:class:`~nwp500.models.DeviceDiagnostics` for every field.
|
|
52
|
+
|
|
53
|
+
CLI: ``nwp-cli diagnostics`` (add ``--json`` for the raw model).
|
|
54
|
+
|
|
55
|
+
Firmware Download Information
|
|
56
|
+
=============================
|
|
57
|
+
|
|
58
|
+
The device reports which firmware components it has downloaded for an
|
|
59
|
+
over-the-air update. A unit with nothing pending reports one all-zero
|
|
60
|
+
entry.
|
|
61
|
+
|
|
62
|
+
.. code-block:: python
|
|
63
|
+
|
|
64
|
+
from nwp500 import FirmwareDownloadInfo
|
|
65
|
+
|
|
66
|
+
def on_firmware(info: FirmwareDownloadInfo) -> None:
|
|
67
|
+
for entry in info.download_sw_info:
|
|
68
|
+
print(entry.component_name, entry.sw_version, entry.status)
|
|
69
|
+
|
|
70
|
+
await mqtt.subscribe_firmware_download_info(device, on_firmware)
|
|
71
|
+
await mqtt.request_firmware_download_info(device)
|
|
72
|
+
|
|
73
|
+
To apply a downloaded update, call
|
|
74
|
+
:meth:`nwp500.mqtt.client.NavienMqttClient.commit_firmware_update` with an
|
|
75
|
+
:class:`~nwp500.models.OtaCommitPayload` naming the component code and
|
|
76
|
+
version from the entry above.
|
|
77
|
+
|
|
78
|
+
.. warning::
|
|
79
|
+
|
|
80
|
+
Committing firmware reboots the device. Heating and MQTT connectivity
|
|
81
|
+
will be interrupted until the upgrade completes.
|
|
82
|
+
|
|
83
|
+
.. code-block:: python
|
|
84
|
+
|
|
85
|
+
from nwp500 import OtaCommitPayload
|
|
86
|
+
|
|
87
|
+
payload = OtaCommitPayload(swCode=1, swVersion=1234)
|
|
88
|
+
await mqtt.commit_firmware_update(device, payload)
|
|
89
|
+
|
|
90
|
+
CLI: ``nwp-cli firmware info``.
|
|
91
|
+
|
|
92
|
+
Air Filter
|
|
93
|
+
==========
|
|
94
|
+
|
|
95
|
+
The air filter timer is reset after cleaning or replacing the filter. The
|
|
96
|
+
service interval is set in evaporator-fan hours: ``0`` disables the alarm,
|
|
97
|
+
otherwise 1000 to 10000 hours in 500-hour steps, which are the values the
|
|
98
|
+
NaviLink app offers. The device reports the interval back as
|
|
99
|
+
``DeviceStatus.air_filter_alarm_period``.
|
|
100
|
+
|
|
101
|
+
.. code-block:: python
|
|
102
|
+
|
|
103
|
+
await mqtt.set_air_filter_life(device, 3000)
|
|
104
|
+
await mqtt.reset_air_filter(device)
|
|
105
|
+
|
|
106
|
+
CLI: ``nwp-cli filter-life 3000`` and ``nwp-cli reset-filter``.
|
|
107
|
+
|
|
108
|
+
Condenser Fault Reset
|
|
109
|
+
=====================
|
|
110
|
+
|
|
111
|
+
Clears a condenser fault. The NaviLink app exposes this on its status
|
|
112
|
+
screen to installer accounts only; the gate is in the app. A consumer
|
|
113
|
+
account's unit acknowledges the command; the clearing effect could not be
|
|
114
|
+
observed because no fault was present.
|
|
115
|
+
|
|
116
|
+
.. code-block:: python
|
|
117
|
+
|
|
118
|
+
await mqtt.reset_condenser_fault(device)
|
|
119
|
+
|
|
120
|
+
CLI: ``nwp-cli reset-condenser-fault``.
|
|
121
|
+
|
|
122
|
+
Commands the App Never Sends
|
|
123
|
+
============================
|
|
124
|
+
|
|
125
|
+
The NaviLink app's command enum also declares an OTA check, WiFi reconnect
|
|
126
|
+
and reset, a freeze-protection temperature and a smart diagnostic trigger,
|
|
127
|
+
but its request builder has no case for any of them, so no NaviLink client
|
|
128
|
+
publishes those codes and their payloads are unknown. Earlier versions of
|
|
129
|
+
this library sent made-up payloads for them; those methods were removed.
|
|
130
|
+
The freeze-protection range and the smart diagnostic result remain
|
|
131
|
+
readable from :class:`~nwp500.models.DeviceFeature` and
|
|
132
|
+
:class:`~nwp500.models.DeviceStatus`.
|
|
133
|
+
|
|
134
|
+
Related Documentation
|
|
135
|
+
=====================
|
|
136
|
+
|
|
137
|
+
* :doc:`../reference/python_api/mqtt_client` - Full MQTT client API reference
|
|
138
|
+
* :doc:`schedule-operation` - Reservations, recirculation schedules, and intelligent scheduling
|
|
139
|
+
* :doc:`diagnose-mqtt` - Connection troubleshooting and diagnostics
|