nwp500-python 9.12.0__tar.gz → 9.12.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.12.0 → nwp500_python-9.12.1}/CHANGELOG.rst +29 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/CONTRIBUTING.rst +1 -1
- {nwp500_python-9.12.0/src/nwp500_python.egg-info → nwp500_python-9.12.1}/PKG-INFO +6 -17
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/conf.py +1 -2
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/simulate-heater.rst +20 -1
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/requirements.txt +1 -1
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/pyproject.toml +72 -2
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/scripts/README.md +1 -1
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/scripts/validate_version.py +21 -13
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/setup.cfg +0 -65
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/__main__.py +16 -3
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/simulate.py +121 -7
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim_home_assistant.py +5 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1/src/nwp500_python.egg-info}/PKG-INFO +6 -17
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500_python.egg-info/SOURCES.txt +0 -2
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500_python.egg-info/requires.txt +2 -11
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500_python.egg-info/scm_file_list.json +1 -3
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500_python.egg-info/scm_version.json +2 -2
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_cli_simulate.py +136 -1
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_home_assistant.py +4 -0
- nwp500_python-9.12.0/setup.py +0 -22
- nwp500_python-9.12.0/uv.lock +0 -3
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/.coveragerc +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/.github/RESOLVING_PR_COMMENTS.md +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/.github/copilot-instructions.md +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/.github/workflows/ci.yml +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/.github/workflows/release.yml +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/.gitignore +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/.pre-commit-config.yaml +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/.readthedocs.yml +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/AUTHORS.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/LICENSE.txt +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/Makefile +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/README.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/RELEASE.md +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/Makefile +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/_static/.gitignore +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/architecture.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/draws.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/heating-elements.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/index.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/recovery-speed.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/simulator.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/standby-loss.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/tank-energy.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/tou-recovery-cap.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/unknown-values.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/explanation/what-starts-a-recovery.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/authenticate.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/auto-recovery.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/bluetooth.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/calibrate-simulator.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/diagnose-mqtt.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/home-assistant.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/index.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/maintenance.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/manage-units.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/monitor-status.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/optimize-tou.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/queue-commands.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/schedule-operation.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/how-to/track-energy.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/index.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/openapi.yaml +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/project/authors.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/project/changelog.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/project/contributing.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/project/history.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/project/license.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/configuration.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/enumerations.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/index.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/installation.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/protocol/data_conversions.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/protocol/device_features.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/protocol/device_status.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/protocol/error_codes.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/protocol/mqtt_protocol.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/protocol/quick_reference.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/protocol/rest_api.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/python_api/api_client.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/python_api/auth_client.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/python_api/cli.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/python_api/events.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/python_api/exceptions.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/python_api/models.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/python_api/mqtt_client.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/python_api/sim.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/reference/simulator-inputs.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/docs/tutorials/getting-started.rst +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/.ruff.toml +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/README.md +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/air_filter_reset.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/anti_legionella.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/auto_recovery.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/combined_callbacks.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/demand_response.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/device_capabilities.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/device_status_debug.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/energy_analytics.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/error_code_demo.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/firmware_payload_capture.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/mqtt_diagnostics.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/power_control.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/recirculation_control.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/reconnection_demo.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/reservation_schedule.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/simple_auto_recovery.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/token_restoration.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/tou_openei.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/tou_schedule.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/advanced/water_reservation.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/beginner/01_authentication.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/beginner/02_list_devices.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/beginner/03_get_status.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/beginner/04_set_temperature.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/advanced_auth_patterns.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/command_queue.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/device_status_callback.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/error_handling.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/event_driven_control.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/improved_auth.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/legacy_auth_constructor.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/mqtt_realtime_monitoring.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/periodic_requests.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/set_mode.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/intermediate/vacation_mode.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/mask.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/simulator/lower-setpoint.json +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/simulator/plan.json +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/simulator/scenario.json +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/testing/periodic_device_info.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/testing/simple_periodic_info.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/testing/test_api_client.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/testing/test_mqtt_connection.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/testing/test_mqtt_messaging.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/examples/testing/test_periodic_minimal.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/scripts/bump_version.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/scripts/diagnose_mqtt_connection.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/scripts/extract_changelog.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/scripts/format.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/scripts/lint.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/scripts/setup-dev.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/__init__.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/_base.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/api_client.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/auth.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/ble/__init__.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/ble/__main__.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/ble/client.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/ble/mqtt_adapter.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/ble/protocol.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/__init__.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/ble_backend.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/calibration.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/handlers.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/monitoring.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/output_formatters.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/presentation.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/recording.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/rich_output.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/cli/token_storage.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/command_decorators.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/config.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/converters.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/device_capabilities.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/device_info_cache.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/encoding.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/enums.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/events.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/exceptions.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/factory.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/field_factory.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/__init__.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/_converters.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/account.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/device.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/diagnostics.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/energy.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/feature.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/firmware.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/maintenance.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/mqtt_models.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/schedule.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/status.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/models/tou.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/__init__.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/_control_commands.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/_device_subscriptions.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/client.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/command_queue.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/connection.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/control.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/diagnostics.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/periodic.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/reconnection.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/state_tracker.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/subscriptions.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/types.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt/utils.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/mqtt_events.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/openei.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/py.typed +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/reservations.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/__init__.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/api.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/attribution.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/book.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/calibration.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/circulation.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/codec.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/cold_supply.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/controller.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/decay.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/defaults.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/delivery.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/draw_forecast.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/engine.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/extensions.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/heat_pump_recovery.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/heating_trajectory.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/inputs.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/location_air.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/observed.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/params.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/physics/__init__.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/physics/compressor.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/physics/draw.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/physics/element.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/physics/refrigerant.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/physics/tank.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/profile.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/ready.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/record.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/result.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/schedules.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/tank_physics.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/tou_triggers.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim/units.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim_adapters.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim_calibrate.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/sim_live.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/status_history.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/temperature.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/topic_builder.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/tou_schedule.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/unit_system.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500/utils.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500_python.egg-info/entry_points.txt +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500_python.egg-info/not-zip-safe +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/src/nwp500_python.egg-info/top_level.txt +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/_sim_site.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/conftest.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/fixtures/sim/engine_golden.json +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/fixtures/sim/heater_params.json +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/fixtures/sim/heater_params_fitted.json +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_api_endpoints_201.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_api_helpers.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_app_queries.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_auth.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_auth_session_lifecycle.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_auth_session_refresh.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_ble_mqtt_adapter.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_ble_protocol.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_bug_fixes.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_canonical_schedule.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_cli_basic.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_cli_calibrate.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_cli_commands.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_cli_tristate_presentation.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_command_decorators.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_command_queue.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_device_capabilities.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_device_info_cache.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_device_rest_models.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_events.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_exceptions.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_model_converters.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_models.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_clean_session_resume.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_client_init.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_control_fail.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_events.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_hypothesis.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_last_will.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_reconnection.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_reconnection_storm.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_mqtt_reliability.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_multi_device.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_navilink_201_enums.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_openei.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_protocol_correctness.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_public_api.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_reservation_validation.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_reservations.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_adapters.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_api.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_book.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_book_history.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_bottom_loss.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_calibrate.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_calibration.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_change_history.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_circulation.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_codec.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_cold_supply.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_controller.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_defaults.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_documents.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_draw_forecast.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_draw_loss.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_element.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_element_fit.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_fit_book.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_forecast.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_golden.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_hot_water.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_import_contract.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_live.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_location_air.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_no_clock_no_state.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_observed.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_overlay.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_profile.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_readiness.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_readiness_fit.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_ready.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_ready_api.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_replay.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_schedules.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_sim_tank_physics.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_status_history.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_temperature_converters.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_threading_model.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_token_storage.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_tou_api.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_tou_schedule.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_unit_switching.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_unit_system_process_wide.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_utility_modules.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tests/test_utils.py +0 -0
- {nwp500_python-9.12.0 → nwp500_python-9.12.1}/tox.ini +0 -0
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
Version 9.12.1 (2026-10-23)
|
|
6
|
+
===========================
|
|
7
|
+
|
|
8
|
+
Fixed
|
|
9
|
+
-----
|
|
10
|
+
|
|
11
|
+
- **simulate run books the tank from Home Assistant.** Given the
|
|
12
|
+
sources' ``[home_assistant.heater]`` table and the new
|
|
13
|
+
``draw_flow`` entity, the run walks from the tank's booked heat
|
|
14
|
+
(``HomeAssistantBook``), and "deliverable now" reads it: a tank with
|
|
15
|
+
its probes apart read 0 gal from the probes alone, the history
|
|
16
|
+
unused.
|
|
17
|
+
|
|
18
|
+
Added
|
|
19
|
+
-----
|
|
20
|
+
|
|
21
|
+
- **simulate run's bath and showers.** The table gives the bath and
|
|
22
|
+
the whole showers the tank is ready for (``sim.time_to_ready``), and
|
|
23
|
+
when the heat pump has the bath ready; ``--bath`` and ``--shower``
|
|
24
|
+
give the volumes.
|
|
25
|
+
|
|
26
|
+
Changed
|
|
27
|
+
-------
|
|
28
|
+
|
|
29
|
+
- **The package's metadata is in pyproject.toml.** Name, dependencies,
|
|
30
|
+
extras, classifiers and the ``nwp-cli`` entry point moved from
|
|
31
|
+
``setup.cfg`` to the standard ``[project]`` table, and ``setup.py`` is
|
|
32
|
+
gone. The built package installs the same files and requirements.
|
|
33
|
+
|
|
5
34
|
Version 9.12.0 (2026-10-23)
|
|
6
35
|
===========================
|
|
7
36
|
|
|
@@ -265,7 +265,7 @@ package:
|
|
|
265
265
|
potentially in the root of your project.
|
|
266
266
|
|
|
267
267
|
#. Sometimes |tox|_ misses out when new dependencies are added, especially to
|
|
268
|
-
``
|
|
268
|
+
``pyproject.toml`` and ``docs/requirements.txt``. If you find any problems with
|
|
269
269
|
missing dependencies when running a command with |tox|_, try to recreate the
|
|
270
270
|
``tox`` environment using the ``-r`` flag. For example, instead of::
|
|
271
271
|
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nwp500-python
|
|
3
|
-
Version: 9.12.
|
|
3
|
+
Version: 9.12.1
|
|
4
4
|
Summary: A library for controlling Navien NWP500 Water Heaters via NaviLink
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
License: MIT
|
|
5
|
+
Author-email: Emmanuel Levijarvi <emansl@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/eman/nwp500-python
|
|
9
8
|
Project-URL: Documentation, https://nwp500-python.readthedocs.io/en/latest/
|
|
10
9
|
Project-URL: Source, https://github.com/eman/nwp500-python
|
|
11
10
|
Project-URL: Tracker, https://github.com/eman/nwp500-python/issues
|
|
12
|
-
Platform: any
|
|
13
11
|
Classifier: Development Status :: 4 - Beta
|
|
14
12
|
Classifier: Programming Language :: Python
|
|
15
13
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -31,9 +29,7 @@ Requires-Dist: numpy>=1.24.0; extra == "sim"
|
|
|
31
29
|
Requires-Dist: pandas>=2.0.0; extra == "sim"
|
|
32
30
|
Requires-Dist: scipy>=1.11.0; extra == "sim"
|
|
33
31
|
Provides-Extra: testing
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist: pandas>=2.0.0; extra == "testing"
|
|
36
|
-
Requires-Dist: scipy>=1.11.0; extra == "testing"
|
|
32
|
+
Requires-Dist: nwp500-python[sim]; extra == "testing"
|
|
37
33
|
Requires-Dist: setuptools; extra == "testing"
|
|
38
34
|
Requires-Dist: pytest; extra == "testing"
|
|
39
35
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
@@ -42,14 +38,7 @@ Requires-Dist: hypothesis; extra == "testing"
|
|
|
42
38
|
Provides-Extra: dev
|
|
43
39
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
44
40
|
Requires-Dist: pyright>=1.1.0; extra == "dev"
|
|
45
|
-
Requires-Dist:
|
|
46
|
-
Requires-Dist: pandas>=2.0.0; extra == "dev"
|
|
47
|
-
Requires-Dist: scipy>=1.11.0; extra == "dev"
|
|
48
|
-
Requires-Dist: setuptools; extra == "dev"
|
|
49
|
-
Requires-Dist: pytest; extra == "dev"
|
|
50
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
51
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
52
|
-
Requires-Dist: hypothesis; extra == "dev"
|
|
41
|
+
Requires-Dist: nwp500-python[testing]; extra == "dev"
|
|
53
42
|
Dynamic: license-file
|
|
54
43
|
|
|
55
44
|
=============
|
|
@@ -24,8 +24,7 @@ sys.path.insert(0, os.path.join(__location__, "../src"))
|
|
|
24
24
|
# This hack is necessary since RTD does not issue `sphinx-apidoc` before running
|
|
25
25
|
# `sphinx-build -b html . _build/html`. See Issue:
|
|
26
26
|
# https://github.com/readthedocs/readthedocs.org/issues/1139
|
|
27
|
-
#
|
|
28
|
-
# setup.py install" in the RTD Advanced Settings.
|
|
27
|
+
# RTD installs the project itself (.readthedocs.yml, python.install).
|
|
29
28
|
# Additionally it helps us to avoid running apidoc manually
|
|
30
29
|
|
|
31
30
|
try: # for Sphinx >= 1.7
|
|
@@ -141,6 +141,7 @@ pass it with ``--sources`` (or set ``NWP500_SOURCES``):
|
|
|
141
141
|
demand = "binary_sensor.hot_water_demand" # on while hot water is drawn
|
|
142
142
|
circulation_speed = "sensor.circulation_pump_speed"
|
|
143
143
|
weather = "weather.forecast_home"
|
|
144
|
+
draw_flow = "sensor.hot_water_draw_flow" # the household's draws
|
|
144
145
|
|
|
145
146
|
[home_assistant.heater] # optional: the heater's own entities
|
|
146
147
|
heard = "sensor.water_heater_last_status"
|
|
@@ -183,7 +184,25 @@ give:
|
|
|
183
184
|
``demand`` entity: a binary sensor that is on while the household
|
|
184
185
|
draws hot water, with the circulation's water not among its draws. A
|
|
185
186
|
circulation pump with no speed reading leaves no estimate, and a draw
|
|
186
|
-
before its first reading is skipped
|
|
187
|
+
before its first reading is skipped;
|
|
188
|
+
- **the heat book:** with the ``[home_assistant.heater]`` table and a
|
|
189
|
+
``draw_flow`` entity (the household's hot water draw flow, the
|
|
190
|
+
circulation's water not in it), the tank's heat booked from the last
|
|
191
|
+
72 hours of the heater's readings and the draws
|
|
192
|
+
(``sim_home_assistant.HomeAssistantBook``). The run walks from it,
|
|
193
|
+
and "deliverable now" reads it (``energy``), whatever the gap between
|
|
194
|
+
the two probes. Without it, the probes alone answer only a full tank,
|
|
195
|
+
and a tank with the probes apart reads 0 (``probes``). With no full
|
|
196
|
+
tank in the 72 hours, or a scenario that doesn't start now, the run
|
|
197
|
+
walks without the book, and a warning says why.
|
|
198
|
+
|
|
199
|
+
Under "deliverable now", the table gives the bath and the showers the
|
|
200
|
+
tank is ready for (``sim.time_to_ready``): the bath ready with its
|
|
201
|
+
margin, or how short it is and when the heat pump has it ready; and the
|
|
202
|
+
whole showers, as a shower the tank can't finish isn't counted. Both are
|
|
203
|
+
delivered volumes, past the mixing valve. ``--bath`` and ``--shower``
|
|
204
|
+
give them in the preferred unit; without them, the parameters'
|
|
205
|
+
(``ReadinessParams.bath_gal``, 28.7 gal, and ``shower_gal``, 12 gal).
|
|
187
206
|
|
|
188
207
|
A reading's age is from when Home Assistant last heard the sensor
|
|
189
208
|
report (``last_reported``). Over the REST API, Home Assistant serves
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Requirements file for ReadTheDocs, check .readthedocs.yml.
|
|
2
2
|
# To build the module reference correctly, make sure every external package
|
|
3
|
-
# under `
|
|
3
|
+
# under `dependencies` in `pyproject.toml` is also listed here!
|
|
4
4
|
sphinx>=3.2.1
|
|
5
5
|
# sphinx_rtd_theme
|
|
6
6
|
sphinxcontrib-openapi>=0.8.0
|
|
@@ -1,8 +1,78 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
|
|
3
|
-
requires = ["setuptools>=46.1.0", "setuptools_scm[toml]>=5"]
|
|
2
|
+
requires = ["setuptools>=77", "setuptools_scm>=8"]
|
|
4
3
|
build-backend = "setuptools.build_meta"
|
|
5
4
|
|
|
5
|
+
[project]
|
|
6
|
+
name = "nwp500-python"
|
|
7
|
+
description = "A library for controlling Navien NWP500 Water Heaters via NaviLink"
|
|
8
|
+
readme = { file = "README.rst", content-type = "text/x-rst; charset=UTF-8" }
|
|
9
|
+
authors = [{ name = "Emmanuel Levijarvi", email = "emansl@gmail.com" }]
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE.txt"]
|
|
12
|
+
requires-python = ">=3.14"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Programming Language :: Python",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.14",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
]
|
|
20
|
+
# The version comes from the git tag (setuptools_scm).
|
|
21
|
+
dynamic = ["version"]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"aiohttp>=3.13.5",
|
|
24
|
+
"awsiotsdk>=1.29.0",
|
|
25
|
+
"pydantic>=2.0.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
# Command line interface with optional rich formatting
|
|
30
|
+
cli = [
|
|
31
|
+
"click>=8.3.0",
|
|
32
|
+
"rich>=14.3.0",
|
|
33
|
+
]
|
|
34
|
+
# Bluetooth LE access to the heater's controller (installer data)
|
|
35
|
+
ble = [
|
|
36
|
+
"bleak>=0.22.0",
|
|
37
|
+
]
|
|
38
|
+
# The optional heater simulator (nwp500.sim)
|
|
39
|
+
sim = [
|
|
40
|
+
"numpy>=1.24.0",
|
|
41
|
+
"pandas>=2.0.0",
|
|
42
|
+
"scipy>=1.11.0",
|
|
43
|
+
]
|
|
44
|
+
testing = [
|
|
45
|
+
"nwp500-python[sim]",
|
|
46
|
+
"setuptools",
|
|
47
|
+
"pytest",
|
|
48
|
+
"pytest-cov",
|
|
49
|
+
"pytest-asyncio",
|
|
50
|
+
"hypothesis",
|
|
51
|
+
]
|
|
52
|
+
dev = [
|
|
53
|
+
"ruff>=0.1.0",
|
|
54
|
+
"pyright>=1.1.0",
|
|
55
|
+
"nwp500-python[testing]",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[project.scripts]
|
|
59
|
+
nwp-cli = "nwp500.cli.__main__:run"
|
|
60
|
+
|
|
61
|
+
[project.urls]
|
|
62
|
+
Homepage = "https://github.com/eman/nwp500-python"
|
|
63
|
+
Documentation = "https://nwp500-python.readthedocs.io/en/latest/"
|
|
64
|
+
Source = "https://github.com/eman/nwp500-python"
|
|
65
|
+
Tracker = "https://github.com/eman/nwp500-python/issues"
|
|
66
|
+
|
|
67
|
+
[tool.setuptools]
|
|
68
|
+
zip-safe = false
|
|
69
|
+
include-package-data = true
|
|
70
|
+
|
|
71
|
+
[tool.setuptools.packages.find]
|
|
72
|
+
where = ["src"]
|
|
73
|
+
exclude = ["tests"]
|
|
74
|
+
namespaces = true
|
|
75
|
+
|
|
6
76
|
[tool.setuptools_scm]
|
|
7
77
|
# For smarter version schemes and other configuration options,
|
|
8
78
|
# check out https://github.com/pypa/setuptools_scm
|
|
@@ -49,7 +49,7 @@ python3 scripts/validate_version.py
|
|
|
49
49
|
**Checks:**
|
|
50
50
|
- Verifies `setup.cfg` `[pyscaffold]` version hasn't been modified (should be 4.6)
|
|
51
51
|
- Ensures no hardcoded `__version__` strings in source code
|
|
52
|
-
- Confirms `
|
|
52
|
+
- Confirms `pyproject.toml` takes the version from `setuptools_scm`
|
|
53
53
|
|
|
54
54
|
**When to run:**
|
|
55
55
|
- Before creating a release
|
|
@@ -5,13 +5,15 @@ Version validation script for nwp500-python.
|
|
|
5
5
|
This script checks for common version-related mistakes:
|
|
6
6
|
1. Verifies that setup.cfg [pyscaffold] version hasn't been changed
|
|
7
7
|
2. Ensures no hardcoded version strings exist in source code
|
|
8
|
-
3. Validates that
|
|
8
|
+
3. Validates that pyproject.toml takes the version from git tags
|
|
9
|
+
(setuptools_scm)
|
|
9
10
|
|
|
10
11
|
Run this as part of CI/CD or pre-commit hooks.
|
|
11
12
|
"""
|
|
12
13
|
|
|
13
14
|
import re
|
|
14
15
|
import sys
|
|
16
|
+
import tomllib
|
|
15
17
|
from pathlib import Path
|
|
16
18
|
|
|
17
19
|
|
|
@@ -120,21 +122,27 @@ def check_hardcoded_versions() -> bool:
|
|
|
120
122
|
return True
|
|
121
123
|
|
|
122
124
|
|
|
123
|
-
def
|
|
124
|
-
"""Verify
|
|
125
|
-
|
|
125
|
+
def check_scm_version() -> bool:
|
|
126
|
+
"""Verify pyproject.toml takes the version from setuptools_scm."""
|
|
127
|
+
pyproject = Path("pyproject.toml")
|
|
126
128
|
|
|
127
|
-
if not
|
|
128
|
-
print("
|
|
129
|
-
return
|
|
129
|
+
if not pyproject.exists():
|
|
130
|
+
print("Error: pyproject.toml not found", file=sys.stderr)
|
|
131
|
+
return False
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
config = tomllib.loads(pyproject.read_text())
|
|
134
|
+
project = config.get("project", {})
|
|
132
135
|
|
|
133
|
-
if "
|
|
134
|
-
print("
|
|
135
|
-
|
|
136
|
+
if "version" in project:
|
|
137
|
+
print("pyproject.toml hardcodes [project] version", file=sys.stderr)
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
if "version" not in project.get("dynamic", []) or (
|
|
141
|
+
"setuptools_scm" not in config.get("tool", {})
|
|
142
|
+
):
|
|
136
143
|
print(
|
|
137
|
-
"
|
|
144
|
+
"pyproject.toml should list version in [project] dynamic and "
|
|
145
|
+
"have a [tool.setuptools_scm] table",
|
|
138
146
|
file=sys.stderr,
|
|
139
147
|
)
|
|
140
148
|
return False
|
|
@@ -150,7 +158,7 @@ def main() -> int:
|
|
|
150
158
|
all_checks = [
|
|
151
159
|
("PyScaffold version", check_pyscaffold_version),
|
|
152
160
|
("Hardcoded versions", check_hardcoded_versions),
|
|
153
|
-
("
|
|
161
|
+
("setuptools_scm configuration", check_scm_version),
|
|
154
162
|
]
|
|
155
163
|
|
|
156
164
|
results = []
|
|
@@ -1,68 +1,3 @@
|
|
|
1
|
-
[metadata]
|
|
2
|
-
name = nwp500-python
|
|
3
|
-
description = A library for controlling Navien NWP500 Water Heaters via NaviLink
|
|
4
|
-
author = Emmanuel Levijarvi
|
|
5
|
-
author_email = emansl@gmail.com
|
|
6
|
-
license = MIT
|
|
7
|
-
license_files = LICENSE.txt
|
|
8
|
-
long_description = file: README.rst
|
|
9
|
-
long_description_content_type = text/x-rst; charset=UTF-8
|
|
10
|
-
url = https://github.com/eman/nwp500-python
|
|
11
|
-
project_urls =
|
|
12
|
-
Documentation = https://nwp500-python.readthedocs.io/en/latest/
|
|
13
|
-
Source = https://github.com/eman/nwp500-python
|
|
14
|
-
Tracker = https://github.com/eman/nwp500-python/issues
|
|
15
|
-
platforms = any
|
|
16
|
-
classifiers =
|
|
17
|
-
Development Status :: 4 - Beta
|
|
18
|
-
Programming Language :: Python
|
|
19
|
-
Programming Language :: Python :: 3
|
|
20
|
-
Programming Language :: Python :: 3.14
|
|
21
|
-
Programming Language :: Python :: 3 :: Only
|
|
22
|
-
|
|
23
|
-
[options]
|
|
24
|
-
zip_safe = False
|
|
25
|
-
packages = find_namespace:
|
|
26
|
-
include_package_data = True
|
|
27
|
-
package_dir =
|
|
28
|
-
=src
|
|
29
|
-
python_requires = >=3.14
|
|
30
|
-
install_requires =
|
|
31
|
-
aiohttp>=3.13.5
|
|
32
|
-
awsiotsdk>=1.29.0
|
|
33
|
-
pydantic>=2.0.0
|
|
34
|
-
|
|
35
|
-
[options.packages.find]
|
|
36
|
-
where = src
|
|
37
|
-
exclude =
|
|
38
|
-
tests
|
|
39
|
-
|
|
40
|
-
[options.extras_require]
|
|
41
|
-
cli =
|
|
42
|
-
click>=8.3.0
|
|
43
|
-
rich>=14.3.0
|
|
44
|
-
ble =
|
|
45
|
-
bleak>=0.22.0
|
|
46
|
-
sim =
|
|
47
|
-
numpy>=1.24.0
|
|
48
|
-
pandas>=2.0.0
|
|
49
|
-
scipy>=1.11.0
|
|
50
|
-
testing =
|
|
51
|
-
%(sim)s
|
|
52
|
-
setuptools
|
|
53
|
-
pytest
|
|
54
|
-
pytest-cov
|
|
55
|
-
pytest-asyncio
|
|
56
|
-
hypothesis
|
|
57
|
-
dev =
|
|
58
|
-
ruff>=0.1.0
|
|
59
|
-
pyright>=1.1.0
|
|
60
|
-
%(testing)s
|
|
61
|
-
|
|
62
|
-
[options.entry_points]
|
|
63
|
-
console_scripts =
|
|
64
|
-
nwp-cli = nwp500.cli.__main__:run
|
|
65
|
-
|
|
66
1
|
[tool:pytest]
|
|
67
2
|
addopts =
|
|
68
3
|
--cov nwp500 --cov-report term-missing
|
|
@@ -1132,6 +1132,18 @@ _tz_option = click.option(
|
|
|
1132
1132
|
help="A HeaterParams document (default the tank size's defaults)",
|
|
1133
1133
|
)
|
|
1134
1134
|
@_sources_option
|
|
1135
|
+
@click.option(
|
|
1136
|
+
"--bath",
|
|
1137
|
+
type=float,
|
|
1138
|
+
help="The bath readiness is judged against, delivered, preferred unit "
|
|
1139
|
+
"(default the parameters', 28.7 gal)",
|
|
1140
|
+
)
|
|
1141
|
+
@click.option(
|
|
1142
|
+
"--shower",
|
|
1143
|
+
type=float,
|
|
1144
|
+
help="A shower, delivered, preferred unit (default the parameters', "
|
|
1145
|
+
"12 gal)",
|
|
1146
|
+
)
|
|
1135
1147
|
@click.option(
|
|
1136
1148
|
"--format",
|
|
1137
1149
|
"output_format",
|
|
@@ -1146,9 +1158,9 @@ def simulate_run(ctx: click.Context, output_format: str, **given: Any) -> None:
|
|
|
1146
1158
|
inputs = _read_inputs(sim_cli, **given)
|
|
1147
1159
|
now = datetime.now(UTC)
|
|
1148
1160
|
|
|
1149
|
-
def walk(live: Any) -> None:
|
|
1161
|
+
def walk(live: Any, keeper: Any = None) -> None:
|
|
1150
1162
|
try:
|
|
1151
|
-
sim_cli.run(inputs, live, now, output_format)
|
|
1163
|
+
sim_cli.run(inputs, live, now, output_format, keeper=keeper)
|
|
1152
1164
|
except sim_cli.InputError as e:
|
|
1153
1165
|
raise click.UsageError(str(e)) from None
|
|
1154
1166
|
|
|
@@ -1163,9 +1175,10 @@ def simulate_run(ctx: click.Context, output_format: str, **given: Any) -> None:
|
|
|
1163
1175
|
live = await sim_cli.compose_live(
|
|
1164
1176
|
mqtt, ctx.obj["api"], device, inputs, now
|
|
1165
1177
|
)
|
|
1178
|
+
keeper = await sim_cli.read_book(inputs, now)
|
|
1166
1179
|
except sim_cli.InputError as e:
|
|
1167
1180
|
raise click.UsageError(str(e)) from None
|
|
1168
|
-
walk(live)
|
|
1181
|
+
walk(live, keeper)
|
|
1169
1182
|
|
|
1170
1183
|
ctx.exit(asyncio.run(_with_device(ctx, command)))
|
|
1171
1184
|
|
|
@@ -11,6 +11,7 @@ This module needs the ``sim`` extra; ``__main__`` imports it only when a
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
import csv
|
|
14
|
+
import dataclasses
|
|
14
15
|
import json
|
|
15
16
|
import logging
|
|
16
17
|
import os
|
|
@@ -42,6 +43,7 @@ if TYPE_CHECKING:
|
|
|
42
43
|
from nwp500.api_client import NavienAPIClient
|
|
43
44
|
from nwp500.models import Device
|
|
44
45
|
from nwp500.mqtt import NavienMqttClient
|
|
46
|
+
from nwp500.sim_home_assistant import HomeAssistantBook
|
|
45
47
|
|
|
46
48
|
__author__ = "Emmanuel Levijarvi"
|
|
47
49
|
__copyright__ = "Emmanuel Levijarvi"
|
|
@@ -128,12 +130,20 @@ class Inputs:
|
|
|
128
130
|
#: Where the air, its humidity and the inlet are read live
|
|
129
131
|
#: (``sim_home_assistant.Sources``), when the heater is.
|
|
130
132
|
sources: Any = None
|
|
133
|
+
#: The bath and the shower readiness is judged against, delivered,
|
|
134
|
+
#: in the preferred unit; None: the parameters'.
|
|
135
|
+
bath: float | None = None
|
|
136
|
+
shower: float | None = None
|
|
131
137
|
|
|
132
138
|
def __post_init__(self) -> None:
|
|
133
139
|
if self.ambient is not None and self.environment is not None:
|
|
134
140
|
raise InputError("give --ambient or --environment, not both")
|
|
135
141
|
if self.start is not None and self.start.tzinfo is None:
|
|
136
142
|
raise InputError("--start needs its UTC offset")
|
|
143
|
+
for name in ("bath", "shower"):
|
|
144
|
+
value = getattr(self, name)
|
|
145
|
+
if value is not None and not value > 0.0:
|
|
146
|
+
raise InputError(f"--{name} must be a positive volume")
|
|
137
147
|
if self.tz is not None:
|
|
138
148
|
zone_name(self.tz)
|
|
139
149
|
if self.scenario is not None:
|
|
@@ -268,6 +278,54 @@ async def compose_live(
|
|
|
268
278
|
return composed
|
|
269
279
|
|
|
270
280
|
|
|
281
|
+
async def read_book(inputs: Inputs, now: datetime) -> HomeAssistantBook | None:
|
|
282
|
+
"""The heat book's keeper (``sim_home_assistant.HomeAssistantBook``)
|
|
283
|
+
with the history read to ``now``, when the sources name the heater's
|
|
284
|
+
entities and the draw flow; None otherwise. Its ``book`` makes no
|
|
285
|
+
request, so it books after the client is closed."""
|
|
286
|
+
src = inputs.sources
|
|
287
|
+
if src is None or src.heater is None or src.entities.draw_flow is None:
|
|
288
|
+
return None
|
|
289
|
+
from nwp500 import sim_home_assistant as ha
|
|
290
|
+
|
|
291
|
+
async with ha.HomeAssistantClient(src.url, src.token) as client:
|
|
292
|
+
keeper = ha.HomeAssistantBook(
|
|
293
|
+
client, src.heater, src.entities.draw_flow, src.entities
|
|
294
|
+
)
|
|
295
|
+
await keeper.read(now)
|
|
296
|
+
return keeper
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def booked(
|
|
300
|
+
keeper: HomeAssistantBook | None, built: Built, now: datetime
|
|
301
|
+
) -> sim.Booked | None:
|
|
302
|
+
"""The tank's booked heat at the scenario's start, from ``keeper``
|
|
303
|
+
(``read_book``): only for a scenario that starts ``now``, whose
|
|
304
|
+
state is the heater's now. Its gallons convert at the scenario's
|
|
305
|
+
inlet where one is given, as the walk takes it. Its notes are
|
|
306
|
+
logged."""
|
|
307
|
+
if keeper is None:
|
|
308
|
+
return None
|
|
309
|
+
start = built.scenario.start
|
|
310
|
+
if start != now:
|
|
311
|
+
_logger.warning(
|
|
312
|
+
"book: the scenario starts at %s, not now: walked without "
|
|
313
|
+
"the heat book",
|
|
314
|
+
start.isoformat(),
|
|
315
|
+
)
|
|
316
|
+
return None
|
|
317
|
+
answer = keeper.book(now, built.params)
|
|
318
|
+
for note in answer.notes:
|
|
319
|
+
if answer.booked is None:
|
|
320
|
+
_logger.warning(note)
|
|
321
|
+
else:
|
|
322
|
+
_logger.info(note)
|
|
323
|
+
inlet = built.scenario.environment.inlet_f
|
|
324
|
+
if answer.booked is None or inlet is None:
|
|
325
|
+
return answer.booked
|
|
326
|
+
return dataclasses.replace(answer.booked, inlet_f=inlet)
|
|
327
|
+
|
|
328
|
+
|
|
271
329
|
def window(inputs: Inputs, now: datetime) -> tuple[datetime, timedelta]:
|
|
272
330
|
"""The start and horizon ``build`` will give the scenario: a flag,
|
|
273
331
|
then the scenario document's, then ``now`` and ``DEFAULT_HOURS``.
|
|
@@ -349,20 +407,66 @@ def energy_totals(
|
|
|
349
407
|
return compressor, element
|
|
350
408
|
|
|
351
409
|
|
|
410
|
+
def hot_water_lines(
|
|
411
|
+
built: Built,
|
|
412
|
+
book: sim.Booked | None,
|
|
413
|
+
bath_gal: float | None = None,
|
|
414
|
+
shower_gal: float | None = None,
|
|
415
|
+
) -> list[str]:
|
|
416
|
+
"""The bath and the showers the tank is ready for now
|
|
417
|
+
(``sim.time_to_ready``): the bath's margin, or when the heat pump
|
|
418
|
+
has it ready; the whole showers. Volumes in gallons, None for the
|
|
419
|
+
parameters'."""
|
|
420
|
+
try:
|
|
421
|
+
r = sim.time_to_ready(
|
|
422
|
+
built.scenario,
|
|
423
|
+
built.params,
|
|
424
|
+
book=book,
|
|
425
|
+
bath_gal=bath_gal,
|
|
426
|
+
shower_gal=shower_gal,
|
|
427
|
+
)
|
|
428
|
+
except SimulationError as e:
|
|
429
|
+
return [f"Bath and showers: not computed ({e})"]
|
|
430
|
+
bath = r.bath
|
|
431
|
+
if bath.ready:
|
|
432
|
+
state = f"ready, {_volume(bath.margin_gal)} to spare"
|
|
433
|
+
elif bath.ready_at is not None:
|
|
434
|
+
state = (
|
|
435
|
+
f"not ready, {_volume(-bath.margin_gal)} short; the heat pump "
|
|
436
|
+
f"has it ready at {_local(bath.ready_at, built.scenario.tz)}"
|
|
437
|
+
)
|
|
438
|
+
else:
|
|
439
|
+
state = f"not ready, {_volume(-bath.margin_gal)} short"
|
|
440
|
+
showers = r.showers
|
|
441
|
+
return [
|
|
442
|
+
f"Bath ({_volume(bath.volume_gal)}): {state}",
|
|
443
|
+
f"Showers ({_volume(showers.volume_gal)}): "
|
|
444
|
+
f"{showers.ready_for_whole} ready",
|
|
445
|
+
]
|
|
446
|
+
|
|
447
|
+
|
|
352
448
|
def print_table(
|
|
353
|
-
built: Built,
|
|
449
|
+
built: Built,
|
|
450
|
+
result: sim.SimulationResult,
|
|
451
|
+
console: Console,
|
|
452
|
+
book: sim.Booked | None = None,
|
|
453
|
+
bath_gal: float | None = None,
|
|
454
|
+
shower_gal: float | None = None,
|
|
354
455
|
) -> None:
|
|
355
|
-
"""The heating intervals, the energy by source, the readiness
|
|
356
|
-
|
|
456
|
+
"""The heating intervals, the energy by source, the readiness (from
|
|
457
|
+
``book`` when given) with the bath and the showers, the draw windows
|
|
458
|
+
(and which ran cold), the reasons and the defaults."""
|
|
357
459
|
s = built.scenario
|
|
358
460
|
tz = s.tz
|
|
359
|
-
ready = sim.readiness(s.state, s.settings, built.params)
|
|
461
|
+
ready = sim.readiness(s.state, s.settings, built.params, book=book)
|
|
360
462
|
console.print(
|
|
361
463
|
f"[bold]{_local(s.start, tz)} to {_local(s.until, tz)}[/bold] ({tz}): "
|
|
362
464
|
f"tank {_temp(s.state.upper_f)} upper, {_temp(s.state.lower_f)} "
|
|
363
465
|
f"lower, setpoint {_temp(s.settings.setpoint_f)}; "
|
|
364
466
|
f"{_volume(ready.gal)} deliverable now ({ready.basis})"
|
|
365
467
|
)
|
|
468
|
+
for line in hot_water_lines(built, book, bath_gal, shower_gal):
|
|
469
|
+
console.print(line)
|
|
366
470
|
|
|
367
471
|
heating = Table(title="Heating")
|
|
368
472
|
for col in ("Start", "End", "Source", "Why", "Target", "Stop", "Wh"):
|
|
@@ -457,13 +561,16 @@ def run(
|
|
|
457
561
|
now: datetime,
|
|
458
562
|
output_format: str,
|
|
459
563
|
out: TextIO | None = None,
|
|
564
|
+
keeper: HomeAssistantBook | None = None,
|
|
460
565
|
) -> sim.SimulationResult:
|
|
461
|
-
"""Build the scenario, walk it and print the result
|
|
566
|
+
"""Build the scenario, walk it and print the result: from the tank's
|
|
567
|
+
booked heat when ``keeper`` (``read_book``) books one."""
|
|
462
568
|
built = build(inputs, live, now)
|
|
463
569
|
for note in built.notes:
|
|
464
570
|
_logger.warning(note)
|
|
571
|
+
book = booked(keeper, built, now)
|
|
465
572
|
try:
|
|
466
|
-
result = sim.simulate(built.scenario, built.params)
|
|
573
|
+
result = sim.simulate(built.scenario, built.params, book=book)
|
|
467
574
|
except SimulationError as e:
|
|
468
575
|
raise InputError(f"the scenario cannot be simulated: {e}") from None
|
|
469
576
|
stream = out or sys.stdout
|
|
@@ -473,5 +580,12 @@ def run(
|
|
|
473
580
|
elif output_format == "csv":
|
|
474
581
|
write_csv(result, stream)
|
|
475
582
|
else:
|
|
476
|
-
print_table(
|
|
583
|
+
print_table(
|
|
584
|
+
built,
|
|
585
|
+
result,
|
|
586
|
+
Console(file=stream),
|
|
587
|
+
book,
|
|
588
|
+
None if inputs.bath is None else sa.to_sim_volume(inputs.bath),
|
|
589
|
+
None if inputs.shower is None else sa.to_sim_volume(inputs.shower),
|
|
590
|
+
)
|
|
477
591
|
return result
|
|
@@ -137,6 +137,10 @@ class Entities:
|
|
|
137
137
|
#: A weather entity with an hourly forecast, its ``temperature``
|
|
138
138
|
#: attribute the outdoor air.
|
|
139
139
|
weather: str | None = None
|
|
140
|
+
#: The household's hot water draw flow, the circulation's water not
|
|
141
|
+
#: in it, such as a draw detector's (``HomeAssistantBook``'s
|
|
142
|
+
#: ``flow``): with the heater's entities, the heat book.
|
|
143
|
+
draw_flow: str | None = None
|
|
140
144
|
|
|
141
145
|
|
|
142
146
|
@dataclass(frozen=True)
|
|
@@ -169,6 +173,7 @@ def sources_from(doc: Mapping[str, Any], environ: Mapping[str, str]) -> Sources:
|
|
|
169
173
|
demand = "binary_sensor.hot_water_demand"
|
|
170
174
|
circulation_speed = "sensor.circulation_pump_speed"
|
|
171
175
|
weather = "weather.forecast_home"
|
|
176
|
+
draw_flow = "sensor.hot_water_draw_flow"
|
|
172
177
|
|
|
173
178
|
[home_assistant.heater] # optional: the heater's own
|
|
174
179
|
heard = "sensor.water_heater_last_status"
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nwp500-python
|
|
3
|
-
Version: 9.12.
|
|
3
|
+
Version: 9.12.1
|
|
4
4
|
Summary: A library for controlling Navien NWP500 Water Heaters via NaviLink
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
License: MIT
|
|
5
|
+
Author-email: Emmanuel Levijarvi <emansl@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/eman/nwp500-python
|
|
9
8
|
Project-URL: Documentation, https://nwp500-python.readthedocs.io/en/latest/
|
|
10
9
|
Project-URL: Source, https://github.com/eman/nwp500-python
|
|
11
10
|
Project-URL: Tracker, https://github.com/eman/nwp500-python/issues
|
|
12
|
-
Platform: any
|
|
13
11
|
Classifier: Development Status :: 4 - Beta
|
|
14
12
|
Classifier: Programming Language :: Python
|
|
15
13
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -31,9 +29,7 @@ Requires-Dist: numpy>=1.24.0; extra == "sim"
|
|
|
31
29
|
Requires-Dist: pandas>=2.0.0; extra == "sim"
|
|
32
30
|
Requires-Dist: scipy>=1.11.0; extra == "sim"
|
|
33
31
|
Provides-Extra: testing
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist: pandas>=2.0.0; extra == "testing"
|
|
36
|
-
Requires-Dist: scipy>=1.11.0; extra == "testing"
|
|
32
|
+
Requires-Dist: nwp500-python[sim]; extra == "testing"
|
|
37
33
|
Requires-Dist: setuptools; extra == "testing"
|
|
38
34
|
Requires-Dist: pytest; extra == "testing"
|
|
39
35
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
@@ -42,14 +38,7 @@ Requires-Dist: hypothesis; extra == "testing"
|
|
|
42
38
|
Provides-Extra: dev
|
|
43
39
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
44
40
|
Requires-Dist: pyright>=1.1.0; extra == "dev"
|
|
45
|
-
Requires-Dist:
|
|
46
|
-
Requires-Dist: pandas>=2.0.0; extra == "dev"
|
|
47
|
-
Requires-Dist: scipy>=1.11.0; extra == "dev"
|
|
48
|
-
Requires-Dist: setuptools; extra == "dev"
|
|
49
|
-
Requires-Dist: pytest; extra == "dev"
|
|
50
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
51
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
52
|
-
Requires-Dist: hypothesis; extra == "dev"
|
|
41
|
+
Requires-Dist: nwp500-python[testing]; extra == "dev"
|
|
53
42
|
Dynamic: license-file
|
|
54
43
|
|
|
55
44
|
=============
|