nwp500-python 7.1.0__tar.gz → 7.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/.github/copilot-instructions.md +9 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/.github/workflows/ci.yml +2 -2
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/CHANGELOG.rst +237 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/CONTRIBUTING.rst +63 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/PKG-INFO +33 -1
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/README.rst +28 -0
- nwp500_python-7.2.0/docs/guides/authentication.rst +304 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/guides/event_system.rst +62 -4
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/index.rst +2 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/protocol/device_features.rst +1 -1
- nwp500_python-7.2.0/docs/protocol/quick_reference.rst +161 -0
- nwp500_python-7.2.0/examples/README.md +96 -0
- nwp500_python-7.1.0/examples/auto_recovery_example.py → nwp500_python-7.2.0/examples/advanced/auto_recovery.py +3 -2
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/advanced}/combined_callbacks.py +3 -2
- nwp500_python-7.1.0/examples/device_feature_callback.py → nwp500_python-7.2.0/examples/advanced/device_capabilities.py +4 -3
- nwp500_python-7.1.0/examples/device_status_callback_debug.py → nwp500_python-7.2.0/examples/advanced/device_status_debug.py +5 -4
- nwp500_python-7.1.0/examples/mqtt_diagnostics_example.py → nwp500_python-7.2.0/examples/advanced/mqtt_diagnostics.py +1 -1
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/advanced}/reconnection_demo.py +3 -2
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/advanced}/simple_auto_recovery.py +3 -2
- nwp500_python-7.1.0/examples/authenticate.py → nwp500_python-7.2.0/examples/beginner/01_authentication.py +2 -1
- nwp500_python-7.1.0/examples/api_client_example.py → nwp500_python-7.2.0/examples/beginner/02_list_devices.py +5 -4
- nwp500_python-7.1.0/examples/simple_periodic_status.py → nwp500_python-7.2.0/examples/beginner/03_get_status.py +2 -1
- nwp500_python-7.2.0/examples/intermediate/advanced_auth_patterns.py +179 -0
- nwp500_python-7.1.0/examples/command_queue_demo.py → nwp500_python-7.2.0/examples/intermediate/command_queue.py +1 -1
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/intermediate}/device_status_callback.py +4 -3
- nwp500_python-7.1.0/examples/exception_handling_example.py → nwp500_python-7.2.0/examples/intermediate/error_handling.py +2 -1
- nwp500_python-7.1.0/examples/event_emitter_demo.py → nwp500_python-7.2.0/examples/intermediate/event_driven_control.py +31 -23
- nwp500_python-7.1.0/examples/mqtt_client_example.py → nwp500_python-7.2.0/examples/intermediate/mqtt_realtime_monitoring.py +5 -4
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/intermediate}/periodic_requests.py +3 -2
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/testing}/periodic_device_info.py +3 -2
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/testing}/simple_periodic_info.py +2 -1
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/testing}/test_api_client.py +1 -1
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/testing}/test_mqtt_connection.py +1 -1
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/testing}/test_mqtt_messaging.py +3 -3
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/testing}/test_periodic_minimal.py +2 -1
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/pyproject.toml +39 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/scripts/lint.py +17 -3
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/setup.cfg +4 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/__init__.py +19 -9
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/auth.py +25 -10
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/cli/__init__.py +1 -1
- nwp500_python-7.2.0/src/nwp500/cli/__main__.py +348 -0
- nwp500_python-7.2.0/src/nwp500/cli/commands.py +89 -0
- nwp500_python-7.1.0/src/nwp500/cli/commands.py → nwp500_python-7.2.0/src/nwp500/cli/handlers.py +12 -6
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/cli/output_formatters.py +368 -327
- nwp500_python-7.2.0/src/nwp500/cli/rich_output.py +522 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/command_decorators.py +3 -3
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/constants.py +1 -1
- nwp500_python-7.2.0/src/nwp500/converters.py +154 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/device_capabilities.py +2 -2
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/device_info_cache.py +3 -3
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/encoding.py +2 -3
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/enums.py +21 -1
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/events.py +2 -4
- nwp500_python-7.2.0/src/nwp500/factory.py +82 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/models.py +56 -52
- nwp500_python-7.2.0/src/nwp500/mqtt/__init__.py +31 -0
- nwp500_python-7.1.0/src/nwp500/mqtt_client.py → nwp500_python-7.2.0/src/nwp500/mqtt/client.py +47 -40
- nwp500_python-7.1.0/src/nwp500/mqtt_command_queue.py → nwp500_python-7.2.0/src/nwp500/mqtt/command_queue.py +2 -2
- nwp500_python-7.1.0/src/nwp500/mqtt_connection.py → nwp500_python-7.2.0/src/nwp500/mqtt/connection.py +20 -22
- nwp500_python-7.1.0/src/nwp500/mqtt_device_control.py → nwp500_python-7.2.0/src/nwp500/mqtt/control.py +27 -17
- nwp500_python-7.1.0/src/nwp500/mqtt_periodic.py → nwp500_python-7.2.0/src/nwp500/mqtt/periodic.py +2 -2
- nwp500_python-7.1.0/src/nwp500/mqtt_reconnection.py → nwp500_python-7.2.0/src/nwp500/mqtt/reconnection.py +1 -1
- nwp500_python-7.1.0/src/nwp500/mqtt_subscriptions.py → nwp500_python-7.2.0/src/nwp500/mqtt/subscriptions.py +8 -8
- nwp500_python-7.1.0/src/nwp500/mqtt_utils.py → nwp500_python-7.2.0/src/nwp500/mqtt/utils.py +1 -1
- nwp500_python-7.2.0/src/nwp500/mqtt_events.py +344 -0
- nwp500_python-7.2.0/src/nwp500/temperature.py +187 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500_python.egg-info/PKG-INFO +33 -1
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500_python.egg-info/SOURCES.txt +61 -49
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500_python.egg-info/requires.txt +5 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_cli_commands.py +1 -1
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_command_decorators.py +13 -13
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_command_queue.py +2 -2
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_device_capabilities.py +27 -19
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_device_info_cache.py +26 -26
- nwp500_python-7.2.0/tests/test_model_converters.py +365 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_mqtt_client_init.py +1 -1
- nwp500_python-7.2.0/tests/test_temperature_converters.py +360 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tox.ini +10 -4
- nwp500_python-7.1.0/examples/README.md +0 -158
- nwp500_python-7.1.0/src/nwp500/cli/__main__.py +0 -290
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/.agent/workflows/pre-completion-testing.md +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/.coveragerc +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/.github/workflows/release.yml +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/.gitignore +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/.pre-commit-config.yaml +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/.readthedocs.yml +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/AUTHORS.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/LICENSE.txt +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/Makefile +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/RELEASE.md +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/Makefile +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/_static/.gitignore +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/api/nwp500.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/authors.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/changelog.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/conf.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/configuration.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/development/contributing.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/development/history.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/enumerations.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/guides/advanced_features_explained.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/guides/auto_recovery.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/guides/command_queue.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/guides/energy_monitoring.rst +0 -0
- /nwp500_python-7.1.0/docs/MQTT_DIAGNOSTICS.rst → /nwp500_python-7.2.0/docs/guides/mqtt_diagnostics.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/guides/reservations.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/guides/scheduling_features.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/guides/time_of_use.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/installation.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/license.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/openapi.yaml +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/protocol/data_conversions.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/protocol/device_status.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/protocol/error_codes.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/protocol/firmware_tracking.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/protocol/mqtt_protocol.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/protocol/rest_api.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/api_client.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/auth_client.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/cli.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/constants.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/device_control.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/events.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/exceptions.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/models.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/python_api/mqtt_client.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/quickstart.rst +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/docs/requirements.txt +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/examples/.ruff.toml +0 -0
- /nwp500_python-7.1.0/examples/air_filter_reset_example.py → /nwp500_python-7.2.0/examples/advanced/air_filter_reset.py +0 -0
- /nwp500_python-7.1.0/examples/anti_legionella_example.py → /nwp500_python-7.2.0/examples/advanced/anti_legionella.py +0 -0
- /nwp500_python-7.1.0/examples/demand_response_example.py → /nwp500_python-7.2.0/examples/advanced/demand_response.py +0 -0
- /nwp500_python-7.1.0/examples/energy_usage_example.py → /nwp500_python-7.2.0/examples/advanced/energy_analytics.py +0 -0
- {nwp500_python-7.1.0/examples → nwp500_python-7.2.0/examples/advanced}/error_code_demo.py +0 -0
- /nwp500_python-7.1.0/examples/power_control_example.py → /nwp500_python-7.2.0/examples/advanced/power_control.py +0 -0
- /nwp500_python-7.1.0/examples/recirculation_control_example.py → /nwp500_python-7.2.0/examples/advanced/recirculation_control.py +0 -0
- /nwp500_python-7.1.0/examples/reservation_schedule_example.py → /nwp500_python-7.2.0/examples/advanced/reservation_schedule.py +0 -0
- /nwp500_python-7.1.0/examples/token_restoration_example.py → /nwp500_python-7.2.0/examples/advanced/token_restoration.py +0 -0
- /nwp500_python-7.1.0/examples/tou_openei_example.py → /nwp500_python-7.2.0/examples/advanced/tou_openei.py +0 -0
- /nwp500_python-7.1.0/examples/tou_schedule_example.py → /nwp500_python-7.2.0/examples/advanced/tou_schedule.py +0 -0
- /nwp500_python-7.1.0/examples/water_program_reservation_example.py → /nwp500_python-7.2.0/examples/advanced/water_reservation.py +0 -0
- /nwp500_python-7.1.0/examples/set_dhw_temperature_example.py → /nwp500_python-7.2.0/examples/beginner/04_set_temperature.py +0 -0
- /nwp500_python-7.1.0/examples/improved_auth_pattern.py → /nwp500_python-7.2.0/examples/intermediate/improved_auth.py +0 -0
- /nwp500_python-7.1.0/examples/auth_constructor_example.py → /nwp500_python-7.2.0/examples/intermediate/legacy_auth_constructor.py +0 -0
- /nwp500_python-7.1.0/examples/set_mode_example.py → /nwp500_python-7.2.0/examples/intermediate/set_mode.py +0 -0
- /nwp500_python-7.1.0/examples/vacation_mode_example.py → /nwp500_python-7.2.0/examples/intermediate/vacation_mode.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/examples/mask.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/scripts/README.md +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/scripts/bump_version.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/scripts/diagnose_mqtt_connection.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/scripts/extract_changelog.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/scripts/format.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/scripts/setup-dev.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/scripts/validate_version.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/setup.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/api_client.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/cli/monitoring.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/cli/token_storage.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/config.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/exceptions.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/field_factory.py +0 -0
- /nwp500_python-7.1.0/src/nwp500/mqtt_diagnostics.py → /nwp500_python-7.2.0/src/nwp500/mqtt/diagnostics.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/py.typed +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/topic_builder.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500/utils.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500_python.egg-info/entry_points.txt +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500_python.egg-info/not-zip-safe +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/src/nwp500_python.egg-info/top_level.txt +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/conftest.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_api_helpers.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_auth.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_cli_basic.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_events.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_exceptions.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_models.py +0 -0
- {nwp500_python-7.1.0 → nwp500_python-7.2.0}/tests/test_utils.py +0 -0
|
@@ -45,6 +45,15 @@ The version bump script:
|
|
|
45
45
|
|
|
46
46
|
**Validation**: Run `make validate-version` to check for version-related mistakes before committing.
|
|
47
47
|
|
|
48
|
+
### Review Comments
|
|
49
|
+
|
|
50
|
+
When working on pull requests, use the GitHub CLI to access review comments:
|
|
51
|
+
- **List review comments**: `gh pr review-comment list --repo=<owner>/<repo>`
|
|
52
|
+
- **Get PR details with reviews**: `gh pr view <number> --repo=<owner>/<repo>`
|
|
53
|
+
- **Apply review feedback** before final submission
|
|
54
|
+
|
|
55
|
+
This ensures you can address all feedback from code reviewers systematically.
|
|
56
|
+
|
|
48
57
|
### Before Committing Changes
|
|
49
58
|
Always run these checks before finalizing changes to ensure your code will pass CI:
|
|
50
59
|
1. **Linting**: `make ci-lint` - Ensures code style matches CI requirements
|
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
- name: Set up Python
|
|
19
19
|
uses: actions/setup-python@v5
|
|
20
20
|
with:
|
|
21
|
-
python-version: '3.
|
|
21
|
+
python-version: '3.13'
|
|
22
22
|
|
|
23
23
|
- name: Install tox
|
|
24
24
|
run: |
|
|
@@ -68,7 +68,7 @@ jobs:
|
|
|
68
68
|
- name: Set up Python
|
|
69
69
|
uses: actions/setup-python@v5
|
|
70
70
|
with:
|
|
71
|
-
python-version: '3.
|
|
71
|
+
python-version: '3.13'
|
|
72
72
|
|
|
73
73
|
- name: Install build dependencies
|
|
74
74
|
run: |
|
|
@@ -2,6 +2,243 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
Version 7.2.0 (2025-12-23)
|
|
6
|
+
==========================
|
|
7
|
+
|
|
8
|
+
**BREAKING CHANGES**: Class names renamed for consistency with MQTT-specific functionality
|
|
9
|
+
|
|
10
|
+
Removed
|
|
11
|
+
-------
|
|
12
|
+
|
|
13
|
+
- **Renamed Classes**: Updated class names to clarify MQTT-specific implementations
|
|
14
|
+
|
|
15
|
+
.. code-block:: python
|
|
16
|
+
|
|
17
|
+
# OLD (removed)
|
|
18
|
+
from nwp500 import DeviceCapabilityChecker, DeviceInfoCache
|
|
19
|
+
|
|
20
|
+
# NEW
|
|
21
|
+
from nwp500 import MqttDeviceCapabilityChecker, MqttDeviceInfoCache
|
|
22
|
+
|
|
23
|
+
**Rationale**: The original names were too generic. These classes are specifically designed
|
|
24
|
+
for MQTT client functionality (auto-fetching device info, caching, capability checking).
|
|
25
|
+
The new names make it clear they're MQTT-specific implementations, leaving room for future
|
|
26
|
+
REST API versions if needed.
|
|
27
|
+
|
|
28
|
+
**Migration**: Simple find-and-replace:
|
|
29
|
+
|
|
30
|
+
- ``DeviceCapabilityChecker`` → ``MqttDeviceCapabilityChecker``
|
|
31
|
+
- ``DeviceInfoCache`` → ``MqttDeviceInfoCache``
|
|
32
|
+
|
|
33
|
+
All functionality remains identical - only the class names changed.
|
|
34
|
+
|
|
35
|
+
Added
|
|
36
|
+
-----
|
|
37
|
+
|
|
38
|
+
- **Factory Function**: New ``create_navien_clients()`` factory for streamlined client initialization
|
|
39
|
+
|
|
40
|
+
.. code-block:: python
|
|
41
|
+
|
|
42
|
+
# Create both API and MQTT clients in one call
|
|
43
|
+
from nwp500 import create_navien_clients
|
|
44
|
+
|
|
45
|
+
async with create_navien_clients(email, password) as (api_client, mqtt_client):
|
|
46
|
+
devices = await api_client.get_devices()
|
|
47
|
+
await mqtt_client.connect()
|
|
48
|
+
# Both clients ready to use
|
|
49
|
+
|
|
50
|
+
- Automatic auth client management (created internally, shared by both clients)
|
|
51
|
+
- Simplified initialization for common use case (API + MQTT)
|
|
52
|
+
- Proper async context manager support
|
|
53
|
+
- Reduces boilerplate in application code
|
|
54
|
+
- Comprehensive documentation in ``docs/guides/authentication.rst``
|
|
55
|
+
- Example: ``examples/intermediate/advanced_auth_patterns.py``
|
|
56
|
+
|
|
57
|
+
- **VolumeCode Enum**: Tank capacity identification with gallon values
|
|
58
|
+
|
|
59
|
+
.. code-block:: python
|
|
60
|
+
|
|
61
|
+
from nwp500 import VolumeCode
|
|
62
|
+
|
|
63
|
+
# Enum values: VOLUME_50GAL = 65, VOLUME_65GAL = 66, VOLUME_80GAL = 67
|
|
64
|
+
# Human-readable text available in VOLUME_CODE_TEXT dict
|
|
65
|
+
|
|
66
|
+
- Maps device codes to actual tank capacities (50, 65, 80 gallons)
|
|
67
|
+
- Used in ``DeviceFeature.volume_code`` field with automatic validation
|
|
68
|
+
- Exported from main package for convenience
|
|
69
|
+
- Includes ``VOLUME_CODE_TEXT`` mapping for display purposes
|
|
70
|
+
|
|
71
|
+
- **Temperature Conversion Classes**: Type-safe temperature handling with clear precision
|
|
72
|
+
|
|
73
|
+
- ``HalfCelsius`` class: 0.5°C precision (value / 2.0)
|
|
74
|
+
- ``DeciCelsius`` class: 0.1°C precision (value / 10.0)
|
|
75
|
+
- Base ``Temperature`` ABC with ``to_celsius()`` and ``to_fahrenheit()`` methods
|
|
76
|
+
- ``from_fahrenheit()`` class methods for reverse conversions
|
|
77
|
+
- Validator functions for Pydantic integration
|
|
78
|
+
- Centralized in new ``temperature.py`` module
|
|
79
|
+
- Better type safety and clearer intent than raw number conversions
|
|
80
|
+
|
|
81
|
+
- **Protocol Converters Module**: Centralized device protocol conversion logic
|
|
82
|
+
|
|
83
|
+
- ``device_bool_to_python()``: Convert device boolean (1=False, 2=True)
|
|
84
|
+
- ``device_bool_from_python()``: Reverse conversion
|
|
85
|
+
- ``tou_status_to_python()``: Time of Use status conversion
|
|
86
|
+
- ``tou_override_to_python()``: TOU override status conversion
|
|
87
|
+
- ``div_10()``: Divide by 10.0 utility
|
|
88
|
+
- ``enum_validator()``: Generic enum factory
|
|
89
|
+
- Comprehensive documentation explaining device protocol quirks
|
|
90
|
+
- New ``converters.py`` module replacing scattered validators
|
|
91
|
+
|
|
92
|
+
- **MQTT Event System**: Structured event handling for MQTT operations
|
|
93
|
+
|
|
94
|
+
- ``MqttClientEvents`` class with type-safe event definitions
|
|
95
|
+
- Feature monitoring and capability detection events
|
|
96
|
+
- Enhanced device capability monitoring in MQTT control module
|
|
97
|
+
- New ``mqtt_events.py`` module for event infrastructure
|
|
98
|
+
- Improved separation of concerns for event-driven architectures
|
|
99
|
+
|
|
100
|
+
- **Pyright Type Checking**: Static type analysis integrated into CI/CD
|
|
101
|
+
|
|
102
|
+
- Added pyright>=1.1.0 to dev dependencies
|
|
103
|
+
- Configured in ``pyproject.toml`` with strict mode for ``src/nwp500``
|
|
104
|
+
- Integrated into tox lint environment and CI workflows
|
|
105
|
+
- Runs automatically with ``make ci-lint`` or ``python3 scripts/lint.py``
|
|
106
|
+
- All source code now passes strict type checking (0 errors)
|
|
107
|
+
- Improved type annotations across codebase
|
|
108
|
+
|
|
109
|
+
- **Dynamic Unit Extraction in CLI**: CLI output now dynamically extracts units from DeviceStatus model metadata
|
|
110
|
+
|
|
111
|
+
- New helper functions: ``_get_unit_suffix()`` and ``_add_numeric_item()``
|
|
112
|
+
- Eliminates hardcoded units in output formatter
|
|
113
|
+
- Single source of truth: model metadata drives CLI display
|
|
114
|
+
|
|
115
|
+
- **Comprehensive Protocol Documentation**: Complete protocol reference documentation
|
|
116
|
+
|
|
117
|
+
- New ``docs/protocol/quick_reference.rst`` with command codes, field formats, and conversions
|
|
118
|
+
- Converted protocol documentation to RST format for Sphinx integration
|
|
119
|
+
- Added protocol reference links in source code comments
|
|
120
|
+
- Improved cross-referencing between code and documentation
|
|
121
|
+
|
|
122
|
+
Changed
|
|
123
|
+
-------
|
|
124
|
+
|
|
125
|
+
- **MQTT Module Reorganization**: Consolidated 9 separate modules into cohesive ``mqtt`` package
|
|
126
|
+
|
|
127
|
+
.. code-block:: python
|
|
128
|
+
|
|
129
|
+
# OLD imports (still work via compatibility layer)
|
|
130
|
+
from nwp500.mqtt_client import NavienMqttClient
|
|
131
|
+
from nwp500.mqtt_diagnostics import MqttDiagnosticsCollector
|
|
132
|
+
from nwp500.mqtt_utils import MqttConnectionConfig
|
|
133
|
+
|
|
134
|
+
# NEW imports (preferred)
|
|
135
|
+
from nwp500.mqtt import NavienMqttClient, MqttDiagnosticsCollector, MqttConnectionConfig
|
|
136
|
+
# OR import from main package (recommended)
|
|
137
|
+
from nwp500 import NavienMqttClient, MqttDiagnosticsCollector, MqttConnectionConfig
|
|
138
|
+
|
|
139
|
+
- Created ``src/nwp500/mqtt/`` package with organized submodules
|
|
140
|
+
- Better package organization and structure
|
|
141
|
+
- Clearer public vs internal APIs
|
|
142
|
+
- New ``mqtt/__init__.py`` with clean public API exports
|
|
143
|
+
- Backward compatibility maintained via main package exports
|
|
144
|
+
- All 209 tests pass with zero type checking errors
|
|
145
|
+
|
|
146
|
+
- **CLI Framework Migration**: Migrated from argparse to Click framework
|
|
147
|
+
|
|
148
|
+
- Implemented ``async_command`` decorator for automatic loop and connection management
|
|
149
|
+
- Added support for command groups (reservations, tou)
|
|
150
|
+
- Improved argument and option parsing with built-in validation
|
|
151
|
+
- Enhanced help text and version reporting
|
|
152
|
+
- Centralized command registry in ``src/nwp500/cli/commands.py``
|
|
153
|
+
- Reorganized CLI handlers into ``src/nwp500/cli/handlers.py``
|
|
154
|
+
- Better separation of concerns between CLI framework and business logic
|
|
155
|
+
- Industry-standard CLI framework with better maintainability
|
|
156
|
+
- Added click>=8.0.0 dependency
|
|
157
|
+
|
|
158
|
+
- **Examples Reorganization**: Restructured examples into beginner/intermediate/advanced/testing categories
|
|
159
|
+
|
|
160
|
+
- Created structured hierarchy in ``examples/`` directory
|
|
161
|
+
- Renamed and moved 35+ example scripts for better discoverability
|
|
162
|
+
- Updated ``examples/README.md`` with 'Getting Started' guide and categorized index
|
|
163
|
+
- Added 01-04 beginner series for smooth onboarding:
|
|
164
|
+
|
|
165
|
+
- ``beginner/01_authentication.py`` - Basic authentication patterns
|
|
166
|
+
- ``beginner/02_list_devices.py`` - Retrieving device information
|
|
167
|
+
- ``beginner/03_get_status.py`` - Getting device status
|
|
168
|
+
- ``beginner/04_set_temperature.py`` - Basic device control
|
|
169
|
+
|
|
170
|
+
- Intermediate examples: event-driven control, error handling, MQTT monitoring
|
|
171
|
+
- Advanced examples: demand response, recirculation, TOU schedules, diagnostics
|
|
172
|
+
- Testing examples: connection testing, periodic updates, minimal examples
|
|
173
|
+
- All examples updated with correct imports for new package structure
|
|
174
|
+
|
|
175
|
+
- **Authentication Documentation**: Major improvements to authentication guide
|
|
176
|
+
|
|
177
|
+
- Complete rewrite of ``docs/guides/authentication.rst``
|
|
178
|
+
- Added factory function patterns and examples
|
|
179
|
+
- Improved context manager documentation
|
|
180
|
+
- Added best practices and common patterns
|
|
181
|
+
- More comprehensive code examples
|
|
182
|
+
|
|
183
|
+
- **Model Refactoring**: Updated to use new converter modules
|
|
184
|
+
|
|
185
|
+
- Replaced 53 lines of scattered validators with imports
|
|
186
|
+
- Updated ``fahrenheit_to_half_celsius()`` to use ``HalfCelsius`` class
|
|
187
|
+
- Cleaner model definitions with centralized conversion logic
|
|
188
|
+
- No breaking changes to public API
|
|
189
|
+
|
|
190
|
+
- **CLI Output Formatter Refactoring**: Restructured ``print_device_status()`` to use dynamic unit extraction
|
|
191
|
+
|
|
192
|
+
- Reduced code duplication by ~400 lines
|
|
193
|
+
- Improved maintainability: field additions automatically get correct units
|
|
194
|
+
- No breaking changes to CLI output format or behavior
|
|
195
|
+
|
|
196
|
+
- **Type Annotations**: Improved type safety across entire codebase
|
|
197
|
+
|
|
198
|
+
- Fixed datetime imports to use ``datetime.UTC`` (Python 3.13)
|
|
199
|
+
- Fixed type annotations in ``rich_output.py`` for optional dependencies
|
|
200
|
+
- Fixed type narrowing issues in ``encoding.py``
|
|
201
|
+
- Updated ``MqttConnection`` callback signature to use ``AwsCrtError``
|
|
202
|
+
- Added public properties and setters where needed for type checking
|
|
203
|
+
|
|
204
|
+
Fixed
|
|
205
|
+
-----
|
|
206
|
+
|
|
207
|
+
- **Superheat Temperature Units**: Target and Current SuperHeat now correctly display in °F instead of °C
|
|
208
|
+
|
|
209
|
+
- Both fields use ``DeciCelsiusToF`` conversion, now properly reflected in CLI output
|
|
210
|
+
- Fields were displaying inconsistent units compared to all other temperature readings
|
|
211
|
+
|
|
212
|
+
- **Missing CLI Output Units**: Multiple fields now display with proper units from model metadata
|
|
213
|
+
|
|
214
|
+
- ``current_dhw_flow_rate``: Now shows GPM unit
|
|
215
|
+
- ``total_energy_capacity``: Now shows Wh unit
|
|
216
|
+
- ``available_energy_capacity``: Now shows Wh unit
|
|
217
|
+
- ``dr_override_status``: Now shows hours unit
|
|
218
|
+
- ``vacation_day_setting``: Now shows days unit
|
|
219
|
+
- ``vacation_day_elapsed``: Now shows days unit
|
|
220
|
+
- ``anti_legionella_period``: Fixed to show days unit (was incorrectly h)
|
|
221
|
+
- ``wifi_rssi``: Now shows dBm unit
|
|
222
|
+
|
|
223
|
+
- **Invalid MQTT Topic Filter**: Fixed ``reservations get`` command subscription topic
|
|
224
|
+
|
|
225
|
+
- Changed invalid topic pattern ``cmd/52/navilink-+/#`` to valid ``cmd/52/+/#``
|
|
226
|
+
- AWS IoT Core MQTT does not support wildcards within topic segments
|
|
227
|
+
- Affected: ``handle_get_reservations_request()`` in commands.py
|
|
228
|
+
|
|
229
|
+
- **DeviceFeature Documentation**: Clarified field descriptions and fixed documentation errors
|
|
230
|
+
|
|
231
|
+
- Fixed ``country_code`` documentation (actual value is 3, not 1 as previously noted)
|
|
232
|
+
- Clarified ``model_type_code``, ``control_type_code``, ``recirc_model_type_code`` field purposes
|
|
233
|
+
- Updated ``volume_code`` to use new ``VolumeCode`` enum with validation
|
|
234
|
+
|
|
235
|
+
- **Type Checking Errors**: Resolved all pyright type checking errors in source code
|
|
236
|
+
|
|
237
|
+
- Fixed datetime imports and type annotations
|
|
238
|
+
- Added missing public properties and setters
|
|
239
|
+
- Removed unused imports and variables
|
|
240
|
+
- All source code now passes strict type checking
|
|
241
|
+
|
|
5
242
|
Version 7.1.0 (2025-12-22)
|
|
6
243
|
==========================
|
|
7
244
|
|
|
@@ -85,6 +85,69 @@ The project is organized around a modular architecture:
|
|
|
85
85
|
|
|
86
86
|
Design principles include separation of concerns, clear public APIs, and extensibility for new device features. Contributors should review the `src/nwp500/` directory for module structure and refer to the documentation for details on each component.
|
|
87
87
|
|
|
88
|
+
Naming Conventions
|
|
89
|
+
------------------
|
|
90
|
+
|
|
91
|
+
Consistent naming conventions improve code readability and maintainability. Follow these patterns when adding new classes, methods, and exceptions.
|
|
92
|
+
|
|
93
|
+
**Classes**
|
|
94
|
+
|
|
95
|
+
- **Client classes**: Use ``Navien<Component>Client`` format for main client classes (e.g., ``NavienAuthClient``, ``NavienAPIClient``, ``NavienMqttClient``). This prefix clearly indicates these are the primary library clients.
|
|
96
|
+
- **Manager/Controller classes**: Use ``<Domain><Responsibility>`` format for classes managing specific functionality (e.g., ``MqttConnectionManager``, ``DeviceInfoCache``). Avoid Navien prefix for utility/internal classes.
|
|
97
|
+
- **Utility classes**: Use ``<Domain><Feature>`` or ``<Domain>Utilities`` format for helper classes (e.g., ``MqttDiagnostics``, ``DeviceCapabilityChecker``).
|
|
98
|
+
|
|
99
|
+
**Methods**
|
|
100
|
+
|
|
101
|
+
Follow these patterns for consistent method naming:
|
|
102
|
+
|
|
103
|
+
- **Getters**: Use ``get_<resource>()`` for single item retrieval (e.g., ``get_device_info()``, ``get_firmware_info()``)
|
|
104
|
+
- **Listers**: Use ``list_<resources>()`` for collection retrieval (e.g., ``list_devices()``)
|
|
105
|
+
- **Setters**: Use ``set_<field>(value)`` for direct assignment or ``configure_<feature>()`` for complex configuration (e.g., ``set_power()``, ``set_dhw_temperature()``)
|
|
106
|
+
- **Actions**: Use ``<action>_<resource>()`` format for operations that modify state (e.g., ``reset_filter()``, ``enable_anti_legionella()``)
|
|
107
|
+
- **Requesters**: Use ``request_<data>()`` for async data fetching from devices (e.g., ``request_device_status()``, ``request_device_info()``)
|
|
108
|
+
|
|
109
|
+
**Enums**
|
|
110
|
+
|
|
111
|
+
- **Enum names**: Use descriptive names that indicate the device state or protocol value (e.g., ``OnOffFlag``, ``CurrentOperationMode``, ``HeatSource``). These names should reflect the Navien protocol directly.
|
|
112
|
+
- **Enum values**: Document device protocol mappings in code comments. For example:
|
|
113
|
+
|
|
114
|
+
.. code-block:: python
|
|
115
|
+
|
|
116
|
+
class OnOffFlag(IntEnum):
|
|
117
|
+
"""Device on/off state per Navien protocol."""
|
|
118
|
+
OFF = 0 # 0 = False per Navien protocol
|
|
119
|
+
ON = 1 # 1 = True per Navien protocol
|
|
120
|
+
|
|
121
|
+
**Exceptions**
|
|
122
|
+
|
|
123
|
+
Follow a clear exception hierarchy with consistent naming:
|
|
124
|
+
|
|
125
|
+
- **Pattern**: ``<Domain><Situation>Error`` (e.g., ``MqttConnectionError``, ``AuthenticationError``, ``DeviceCapabilityError``)
|
|
126
|
+
- **Grouping**: Group related errors under a base class that consumers can catch for broad error handling:
|
|
127
|
+
|
|
128
|
+
- ``AuthenticationError`` - Base for all authentication failures (covers ``InvalidCredentialsError``, ``TokenExpiredError``, ``TokenRefreshError``)
|
|
129
|
+
- ``MqttError`` - Base for all MQTT operations (covers ``MqttConnectionError``, ``MqttNotConnectedError``, ``MqttPublishError``, etc.)
|
|
130
|
+
- ``ValidationError`` - Base for all validation failures (covers ``ParameterValidationError``, ``RangeValidationError``)
|
|
131
|
+
- ``DeviceError`` - Base for all device operations (covers ``DeviceNotFoundError``, ``DeviceOfflineError``, ``DeviceCapabilityError``, etc.)
|
|
132
|
+
|
|
133
|
+
- **Example usage**:
|
|
134
|
+
|
|
135
|
+
.. code-block:: python
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
await mqtt_client.control.set_temperature(device, 150)
|
|
139
|
+
except MqttNotConnectedError:
|
|
140
|
+
# Handle specific case: not connected
|
|
141
|
+
print("Connect to device first")
|
|
142
|
+
except MqttError:
|
|
143
|
+
# Handle other MQTT errors
|
|
144
|
+
print("MQTT operation failed")
|
|
145
|
+
except RangeValidationError as e:
|
|
146
|
+
print(f"Invalid {e.field}: must be {e.min_value}-{e.max_value}")
|
|
147
|
+
except ValidationError:
|
|
148
|
+
# Handle other validation errors
|
|
149
|
+
print("Invalid parameter")
|
|
150
|
+
|
|
88
151
|
Submit an issue
|
|
89
152
|
---------------
|
|
90
153
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nwp500-python
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.2.0
|
|
4
4
|
Summary: A library for controlling Navien NWP500 Water Heaters via NaviLink
|
|
5
5
|
Home-page: https://github.com/eman/nwp500-python
|
|
6
6
|
Author: Emmanuel Levijarvi
|
|
@@ -21,6 +21,9 @@ License-File: LICENSE.txt
|
|
|
21
21
|
Requires-Dist: aiohttp>=3.8.0
|
|
22
22
|
Requires-Dist: awsiotsdk>=1.27.0
|
|
23
23
|
Requires-Dist: pydantic>=2.0.0
|
|
24
|
+
Requires-Dist: click>=8.0.0
|
|
25
|
+
Provides-Extra: cli
|
|
26
|
+
Requires-Dist: rich>=13.0.0; extra == "cli"
|
|
24
27
|
Provides-Extra: testing
|
|
25
28
|
Requires-Dist: setuptools; extra == "testing"
|
|
26
29
|
Requires-Dist: pytest; extra == "testing"
|
|
@@ -28,6 +31,7 @@ Requires-Dist: pytest-cov; extra == "testing"
|
|
|
28
31
|
Requires-Dist: pytest-asyncio; extra == "testing"
|
|
29
32
|
Provides-Extra: dev
|
|
30
33
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pyright>=1.1.0; extra == "dev"
|
|
31
35
|
Requires-Dist: setuptools; extra == "dev"
|
|
32
36
|
Requires-Dist: pytest; extra == "dev"
|
|
33
37
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -98,6 +102,34 @@ Basic Usage
|
|
|
98
102
|
# Change operation mode
|
|
99
103
|
await api_client.set_device_mode(device, "heat_pump")
|
|
100
104
|
|
|
105
|
+
For more detailed authentication information, see the `Authentication & Session Management <https://nwp500-python.readthedocs.io/en/latest/guides/authentication.html>`_ guide.
|
|
106
|
+
|
|
107
|
+
MQTT Real-Time Monitoring
|
|
108
|
+
--------------------------
|
|
109
|
+
|
|
110
|
+
Monitor your device in real-time using MQTT:
|
|
111
|
+
|
|
112
|
+
.. code-block:: python
|
|
113
|
+
|
|
114
|
+
from nwp500 import NavienAuthClient, NavienMqttClient
|
|
115
|
+
|
|
116
|
+
async with NavienAuthClient("your_email@example.com", "your_password") as auth_client:
|
|
117
|
+
# Create MQTT client
|
|
118
|
+
mqtt_client = NavienMqttClient(auth_client=auth_client)
|
|
119
|
+
await mqtt_client.connect()
|
|
120
|
+
|
|
121
|
+
# Subscribe to device status updates
|
|
122
|
+
def on_status(status):
|
|
123
|
+
print(f"Temperature: {status.dhw_temperature}°F")
|
|
124
|
+
print(f"Mode: {status.operation_mode}")
|
|
125
|
+
|
|
126
|
+
device = (await api_client.list_devices())[0]
|
|
127
|
+
await mqtt_client.subscribe_device_status(device, on_status)
|
|
128
|
+
|
|
129
|
+
# Keep the connection alive
|
|
130
|
+
await mqtt_client.wait()
|
|
131
|
+
|
|
132
|
+
|
|
101
133
|
Command Line Interface
|
|
102
134
|
======================
|
|
103
135
|
|
|
@@ -62,6 +62,34 @@ Basic Usage
|
|
|
62
62
|
# Change operation mode
|
|
63
63
|
await api_client.set_device_mode(device, "heat_pump")
|
|
64
64
|
|
|
65
|
+
For more detailed authentication information, see the `Authentication & Session Management <https://nwp500-python.readthedocs.io/en/latest/guides/authentication.html>`_ guide.
|
|
66
|
+
|
|
67
|
+
MQTT Real-Time Monitoring
|
|
68
|
+
--------------------------
|
|
69
|
+
|
|
70
|
+
Monitor your device in real-time using MQTT:
|
|
71
|
+
|
|
72
|
+
.. code-block:: python
|
|
73
|
+
|
|
74
|
+
from nwp500 import NavienAuthClient, NavienMqttClient
|
|
75
|
+
|
|
76
|
+
async with NavienAuthClient("your_email@example.com", "your_password") as auth_client:
|
|
77
|
+
# Create MQTT client
|
|
78
|
+
mqtt_client = NavienMqttClient(auth_client=auth_client)
|
|
79
|
+
await mqtt_client.connect()
|
|
80
|
+
|
|
81
|
+
# Subscribe to device status updates
|
|
82
|
+
def on_status(status):
|
|
83
|
+
print(f"Temperature: {status.dhw_temperature}°F")
|
|
84
|
+
print(f"Mode: {status.operation_mode}")
|
|
85
|
+
|
|
86
|
+
device = (await api_client.list_devices())[0]
|
|
87
|
+
await mqtt_client.subscribe_device_status(device, on_status)
|
|
88
|
+
|
|
89
|
+
# Keep the connection alive
|
|
90
|
+
await mqtt_client.wait()
|
|
91
|
+
|
|
92
|
+
|
|
65
93
|
Command Line Interface
|
|
66
94
|
======================
|
|
67
95
|
|