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