boneio 1.4.0.dev3__tar.gz → 1.4.0.dev5__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.
- {boneio-1.4.0.dev3/boneio.egg-info → boneio-1.4.0.dev5}/PKG-INFO +1 -1
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/cover/cover.py +6 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/cover/venetian.py +11 -4
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/irrigation/controller.py +101 -4
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/output/remote.py +224 -3
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/irrigation.py +23 -2
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/manager.py +130 -4
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/remote.py +58 -0
- boneio-1.4.0.dev5/boneio/core/messaging/lox.py +326 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/integration/homeassistant.py +84 -0
- boneio-1.4.0.dev5/boneio/integration/lox_template.py +367 -0
- boneio-1.4.0.dev5/boneio/modbus/devices/hvac/eht-topventil-plus.json +243 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/actions.yaml +11 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/remote_outputs.yaml +95 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/schema.yaml +22 -1
- boneio-1.4.0.dev5/boneio/version.py +2 -0
- boneio-1.4.0.dev3/boneio/webui/frontend-dist/assets/ConfigEditor-B7qJAsAx.js → boneio-1.4.0.dev5/boneio/webui/frontend-dist/assets/ConfigEditor-A8cWujvP.js +1 -1
- boneio-1.4.0.dev5/boneio/webui/frontend-dist/assets/aiConfig-DxFilBWK.js +2 -0
- boneio-1.4.0.dev3/boneio/webui/frontend-dist/assets/aiWizardPrompt-r-xmvrUB.js → boneio-1.4.0.dev5/boneio/webui/frontend-dist/assets/aiWizardPrompt-CrWwS7zb.js +1 -1
- boneio-1.4.0.dev3/boneio/webui/frontend-dist/assets/index-D-R9QzIP.css → boneio-1.4.0.dev5/boneio/webui/frontend-dist/assets/index-BtCN44rd.css +1 -1
- boneio-1.4.0.dev3/boneio/webui/frontend-dist/assets/index-BVRhWldF.js → boneio-1.4.0.dev5/boneio/webui/frontend-dist/assets/index-ChujUEJh.js +62 -6
- boneio-1.4.0.dev3/boneio/webui/frontend-dist/assets/useTranslation-VOFZRLzq.js → boneio-1.4.0.dev5/boneio/webui/frontend-dist/assets/useTranslation-CxUU9LHE.js +2 -2
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/index.html +4 -4
- {boneio-1.4.0.dev3/boneio/webui → boneio-1.4.0.dev5/boneio/webui/frontend-dist}/schema/binary_sensor.schema.json +44 -0
- {boneio-1.4.0.dev3/boneio/webui → boneio-1.4.0.dev5/boneio/webui/frontend-dist}/schema/config.schema.json +330 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/event.schema.json +154 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/irrigation.schema.json +9 -0
- {boneio-1.4.0.dev3/boneio/webui → boneio-1.4.0.dev5/boneio/webui/frontend-dist}/schema/modbus_devices.schema.json +3 -0
- boneio-1.4.0.dev5/boneio/webui/frontend-dist/schema/remote_outputs.schema.json +225 -0
- {boneio-1.4.0.dev3/boneio/webui → boneio-1.4.0.dev5/boneio/webui/frontend-dist}/schema/template.schema.json +9 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/sw.js +1 -1
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/config.py +26 -7
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/irrigation.py +217 -0
- {boneio-1.4.0.dev3/boneio/webui/frontend-dist → boneio-1.4.0.dev5/boneio/webui}/schema/binary_sensor.schema.json +44 -0
- {boneio-1.4.0.dev3/boneio/webui/frontend-dist → boneio-1.4.0.dev5/boneio/webui}/schema/config.schema.json +330 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/event.schema.json +154 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/irrigation.schema.json +9 -0
- {boneio-1.4.0.dev3/boneio/webui/frontend-dist → boneio-1.4.0.dev5/boneio/webui}/schema/modbus_devices.schema.json +3 -0
- boneio-1.4.0.dev5/boneio/webui/schema/remote_outputs.schema.json +225 -0
- {boneio-1.4.0.dev3/boneio/webui/frontend-dist → boneio-1.4.0.dev5/boneio/webui}/schema/template.schema.json +9 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5/boneio.egg-info}/PKG-INFO +1 -1
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio.egg-info/SOURCES.txt +7 -7
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/pyproject.toml +1 -0
- boneio-1.4.0.dev3/boneio/core/messaging/lox.py +0 -221
- boneio-1.4.0.dev3/boneio/core/messaging/loxone.py +0 -181
- boneio-1.4.0.dev3/boneio/integration/lox_template.py +0 -243
- boneio-1.4.0.dev3/boneio/version.py +0 -2
- boneio-1.4.0.dev3/boneio/webui/frontend-dist/assets/aiConfig-Dq5oh99L.js +0 -2
- boneio-1.4.0.dev3/boneio/webui/frontend-dist/schema/remote_outputs.schema.json +0 -114
- boneio-1.4.0.dev3/boneio/webui/schema/remote_outputs.schema.json +0 -114
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/LICENSE +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/MANIFEST.in +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/README.md +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.2/input.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.2/output_24_16.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.2/output_32_10.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.2/output_32_5.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.2/output_cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.2/output_cover_mix.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.3/input.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.3/output_24_16.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.3/output_32_10.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.3/output_32_5.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.3/output_cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.3/output_cover_mix.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.4/input.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.4/output_24_16.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.4/output_32_10.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.4/output_32_5.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.4/output_cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.4/output_cover_mix.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.5/input.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.5/output_24_16.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.5/output_32_10.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.5/output_cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.5/output_cover_mix.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.6/input.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.6/output_24_16.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.6/output_32_10.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.6/output_cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.6/output_cover_mix.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.7/input.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.7/output_24_16.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.7/output_32_10.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.7/output_48_4.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.7/output_cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.7/output_cover_mix.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.8/input.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.8/output_24_16.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.8/output_32_10.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.8/output_48_4.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.8/output_cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/boards/0.8/output_cover_mix.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/bonecli.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/cover/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/cover/time_based.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/group/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/group/output_group.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/input/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/input/binary_sensor.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/input/detectors.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/input/event.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/input/remote/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/input/remote/base.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/input/remote/esphome.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/irrigation/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/irrigation/water_source.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/output/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/output/basic.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/output/mcp.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/output/pca.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/output/pcf.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/sensor/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/sensor/virtual_energy.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/template/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/template/alarm_panel.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/template/gate_cover.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/components/template/thermostat.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/const.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/cloud/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/cloud/data/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/cloud/data/docker-compose-cloud.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/cloud/data/docker-compose.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/cloud/data/init-certs-cloud.sh +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/cloud/registration.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/cloud/secrets.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/config_helper.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/migrations/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/migrations/v1_proxy_port.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/migrations/v2_transition.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/package-lock.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/package.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/schema_converter.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/config/yaml_util.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/discovery.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/events/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/events/bus.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/action_conditions.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/canopen.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/covers.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/display.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/inputs.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/modbus.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/outputs.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/remote_input_registrar.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/sensors.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/templates/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/templates/alarm.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/templates/gate_cover.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/templates/thermostat.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/templates.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/manager/update.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/messaging/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/messaging/basic.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/messaging/basic_mqtt.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/messaging/composite.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/messaging/local.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/messaging/mqtt.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/messaging/queue.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/remote/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/remote/base.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/remote/can.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/remote/esphome.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/remote/mqtt.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/remote/wled.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/sensor/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/sensor/base.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/sensor/system.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/state/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/state/manager.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/system/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/system/host_data.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/system/monitor.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/utils/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/utils/async_updater.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/utils/conditions.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/utils/filter.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/utils/font_util.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/utils/logger.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/utils/timeperiod.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/core/utils/util.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/24x16/adc.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/24x16/binary_sensor.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/24x16/config.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/24x16/event.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/24x16/mqtt.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/24x16/output24x16A.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/32x10/adc.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/32x10/binary_sensor.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/32x10/config.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/32x10/event.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/32x10/mqtt.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/32x10/output32x10A.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/48x4/adc.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/48x4/binary_sensor.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/48x4/config.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/48x4/event.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/48x4/mqtt.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/48x4/output48x4A.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/can_master.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/can_slave.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover/adc.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover/binary_sensor.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover/config.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover/cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover/event.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover/mqtt.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover/outputCover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover_mix/adc.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover_mix/binary_sensor.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover_mix/config.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover_mix/cover.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover_mix/event.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover_mix/mqtt.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/cover_mix/outputCoverMix.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/adc.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/all_binary_sensor.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/binary_sensor.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/binary_sensor_v_0_7.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/config_24_16.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/event_all.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/event_v_0_7.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/led32x4A.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/output24x16A.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/output24x16A_v0.3.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/output32x10A.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/example_config/different_configs/output32x5A.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/exceptions.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/analog/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/analog/adc.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/can/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/can/bridge.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/can/client.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/can/interface.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/can/node.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/can/node_id.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/can/test_od.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/display/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/display/early_oled.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/display/fonts/danube__.ttf +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/display/oled.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/gpio/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/gpio/expanders/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/gpio/expanders/mcp23017.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/gpio/expanders/pca9685.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/gpio/expanders/pcf8575.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/gpio/input/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/gpio/input/base.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/gpio/input/manager.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/i2c/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/i2c/bus.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/i2c/ina219.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/i2c/ina219_driver.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/i2c/mcp9808.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/i2c/pct2075.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/onewire/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/onewire/bus.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/onewire/dallas.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/onewire/ds2482.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/sensor/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/sensor/temperature/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/sensor/temperature/base.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/sensor/temperature/mcp9808.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/hardware/sensor/temperature/pct2075.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/integration/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/integration/interlock.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/actions.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/MANIFEST.sha256 +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/docker/daemon.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/docker/nodered/docker-compose.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/docker/nodered/nginx/default.conf +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/docker/nodered/node-red/settings.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/journald/journald.conf +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/mosquitto/boneio.conf +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/sudoers/boneio +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/systemd/boneio-oled-boot.service +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/systemd/boneio.service +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/systemd/set-hostname-once.service +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/usr-local-bin/set-hostname-once.sh +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/usr-sbin/oled_boot_splash.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/usr-sbin/oled_boot_splash.sh +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/usr-sbin/oled_msg.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/assets/usr-sbin/oled_msg.sh +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/bootstrap/boneio-migrate +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/bootstrap/install-helper.sh +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/bootstrap/sudoers-migrate +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/runner.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/versions/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/versions/v1_3_0_baseline.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/versions/v1_3_1_fix_oled_boot.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/versions/v1_3_2_cleanup_can_sudoers.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/migrations/versions/v1_4_0_fix_mqtt_sudoers.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/cli.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/client.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/coordinator.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/energy_meters/le-03mw.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/energy_meters/le-03mwct.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/energy_meters/orno-or-we-517.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/energy_meters/sdm120.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/energy_meters/sdm630.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/energy_meters/socomec_e03.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/energy_meters/socomec_e23.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/hvac/fujitsu-ac.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/hvac/ventclear.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/inverters/sofar.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/inverters/thessla.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/other/esp32_relay_x4_modbus.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/other/n4dsc08.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/other/r4dcb08.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/other/wanas415.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/boneio-edge-temp.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/cwt.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/dts1964_3f.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/dyp-a12-ultrasonic.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/gdfs.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/gdfx.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/liquid-sensor.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/pt100.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/sht20.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/devices/sensors/sht30.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/base.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/derived/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/derived/numeric.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/derived/select.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/derived/switch.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/derived/text.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/sensor/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/sensor/binary.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/sensor/numeric.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/sensor/text.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/writeable/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/writeable/binary.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/entities/writeable/numeric.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/modbus/utils.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/models/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/models/actions.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/models/events.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/models/files.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/models/logs.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/models/mqtt.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/models/state.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/runner.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/actions_sensor.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/actions_switch.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/condition.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/filters.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/filters_adc.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/id.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/remote_devices.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/remote_inputs.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/temp_unit.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/schema/update_interval.yaml +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/action_validation.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/app.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/codicon-DCmgc-ay.ttf +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/editor.worker-BVjSvWeP.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/json.worker-CqOJL4FD.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/monaco-B9WiNxkw.css +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/monaco-CAZoGZNF.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/rolldown-runtime-Dw2cE7zH.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/vendor-BD5Ml_Uz.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/workbox-window.prod.es5-BJd1pjeW.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/assets/yaml.worker-nZQZSRZ7.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/boneio-192.png +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/boneio-512.png +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/boneio.svg +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/boneio_fav.svg +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/adc.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/areas.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/boneio.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/can.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/cover.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/dallas.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/ds2482.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/ina219.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/lm75.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/logger.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/lox_udp.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/mcp23017.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/mcp9808.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/modbus.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/mqtt.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/oled.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/output.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/output_group.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/pca9685.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/pcf8575.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/remote_devices.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/remote_inputs.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/sensor.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/virtual_energy_sensor.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/schema/web.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/frontend-dist/workbox-4eca85ba.js +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/middleware/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/middleware/auth.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/auth.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/caddy.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/can.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/covers.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/migrations.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/modbus.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/outputs.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/remote_devices.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/sensors.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/system.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/templates.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/timezone_sudoers.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/tools.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/routes/update.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/adc.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/areas.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/boneio.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/can.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/cover.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/dallas.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/ds2482.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/ina219.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/lm75.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/logger.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/lox_udp.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/mcp23017.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/mcp9808.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/modbus.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/mqtt.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/oled.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/output.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/output_group.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/pca9685.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/pcf8575.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/remote_devices.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/remote_inputs.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/sensor.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/virtual_energy_sensor.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/schema/web.schema.json +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/services/__init__.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/services/logs.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/web_server.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio/webui/websocket_manager.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio.egg-info/dependency_links.txt +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio.egg-info/entry_points.txt +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio.egg-info/requires.txt +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/boneio.egg-info/top_level.txt +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/setup.cfg +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/setup.py +0 -0
- {boneio-1.4.0.dev3 → boneio-1.4.0.dev5}/tests/test_py313_compatibility.py +0 -0
|
@@ -192,6 +192,12 @@ class BaseCover(BaseCoverABC, BasicMqtt):
|
|
|
192
192
|
self._movement_thread = None
|
|
193
193
|
self._stop_event = threading.Event()
|
|
194
194
|
|
|
195
|
+
# Flag set by action executor to signal this call originates from a button action.
|
|
196
|
+
# Used by VenetianCover to conditionally skip tilt restore.
|
|
197
|
+
self._from_action: bool = False
|
|
198
|
+
# When True, tilt restore is explicitly requested by the action definition.
|
|
199
|
+
self._action_tilt_restore: bool = False
|
|
200
|
+
|
|
195
201
|
self._event_bus.add_sigterm_listener(self.on_exit)
|
|
196
202
|
|
|
197
203
|
with suppress(RuntimeError):
|
|
@@ -35,7 +35,7 @@ class VenetianCover(BaseCover, BaseVenetianCoverABC):
|
|
|
35
35
|
actuator_activation_duration: Actuator activation time (unused).
|
|
36
36
|
restored_state: Persisted position/tilt from disk.
|
|
37
37
|
tilt_restore_after_close: If True, restore previous tilt
|
|
38
|
-
position after closing the cover.
|
|
38
|
+
position after closing the cover (UI/HA calls).
|
|
39
39
|
"""
|
|
40
40
|
self._tilt_duration = tilt_duration.total_milliseconds # Czas trwania ruchu lameli
|
|
41
41
|
self._initial_tilt_position = float(restored_state.get("tilt", DEFAULT_RESTORED_STATE["tilt"]))
|
|
@@ -300,10 +300,17 @@ class VenetianCover(BaseCover, BaseVenetianCoverABC):
|
|
|
300
300
|
def _save_tilt_for_restore(self) -> None:
|
|
301
301
|
"""Save current tilt position for later restoration.
|
|
302
302
|
|
|
303
|
-
Called before any closing movement
|
|
304
|
-
|
|
303
|
+
Called before any closing movement. Determines whether to save
|
|
304
|
+
based on the call source:
|
|
305
|
+
- UI/HA calls: uses tilt_restore_after_close setting
|
|
306
|
+
- Action calls: uses _action_tilt_restore flag (set by executor)
|
|
305
307
|
"""
|
|
306
|
-
if self.
|
|
308
|
+
if self._from_action:
|
|
309
|
+
should_restore = self._action_tilt_restore
|
|
310
|
+
else:
|
|
311
|
+
should_restore = self._tilt_restore_after_close
|
|
312
|
+
|
|
313
|
+
if should_restore and self._tilt_position > 0:
|
|
307
314
|
self._tilt_before_close = self._tilt_position
|
|
308
315
|
_LOGGER.debug(
|
|
309
316
|
"VenetianCover %s: saving tilt=%d%% before close",
|
|
@@ -75,6 +75,7 @@ class IrrigationController:
|
|
|
75
75
|
repeat: int = 0,
|
|
76
76
|
auto_advance: bool = True,
|
|
77
77
|
reverse: bool = False,
|
|
78
|
+
pause_timeout_s: int = 1800,
|
|
78
79
|
) -> None:
|
|
79
80
|
"""Initialize irrigation controller.
|
|
80
81
|
|
|
@@ -95,6 +96,8 @@ class IrrigationController:
|
|
|
95
96
|
repeat: Number of cycle repeats.
|
|
96
97
|
auto_advance: Automatically advance to next zone.
|
|
97
98
|
reverse: Run zones in reverse order.
|
|
99
|
+
pause_timeout_s: Auto-shutdown after this many seconds in PAUSED state.
|
|
100
|
+
Default 1800 (30 minutes). Set 0 to disable.
|
|
98
101
|
"""
|
|
99
102
|
self.id = id
|
|
100
103
|
self.name = name
|
|
@@ -119,6 +122,8 @@ class IrrigationController:
|
|
|
119
122
|
self._skip_next_run = False
|
|
120
123
|
|
|
121
124
|
self._zone_timer_cancel = None
|
|
125
|
+
self._pause_timer_cancel = None
|
|
126
|
+
self._pause_timeout_s = max(0, pause_timeout_s)
|
|
122
127
|
self._run_start_utc: datetime | None = None
|
|
123
128
|
self._active_zone_idx: int | None = None
|
|
124
129
|
self._active_zone_remaining_s: int | None = None
|
|
@@ -181,6 +186,10 @@ class IrrigationController:
|
|
|
181
186
|
def _schedule_skip_cmd_topic(self, idx: int) -> str:
|
|
182
187
|
return f"{self._topic_prefix}/cmd/{IRRIGATION}/{self.id}/schedule/{idx}/skip/set"
|
|
183
188
|
|
|
189
|
+
def _event_topic(self) -> str:
|
|
190
|
+
"""MQTT topic for HA event entity notifications."""
|
|
191
|
+
return f"{self._topic_prefix}/{IRRIGATION}/{self.id}/event"
|
|
192
|
+
|
|
184
193
|
def _state_key(self, suffix: str) -> str:
|
|
185
194
|
return f"{self.id}/{suffix}"
|
|
186
195
|
|
|
@@ -223,6 +232,24 @@ class IrrigationController:
|
|
|
223
232
|
_LOGGER.debug("Irrigation MQTT publish: topic='%s' payload=%s retain=%s", topic, payload, retain)
|
|
224
233
|
self._message_bus.send_message(topic=topic, payload=payload, retain=retain)
|
|
225
234
|
|
|
235
|
+
def _publish_event(self, event_type: str, **attributes: Any) -> None:
|
|
236
|
+
"""Publish an event to the HA event entity topic.
|
|
237
|
+
|
|
238
|
+
The payload follows the HA MQTT event entity format:
|
|
239
|
+
``{"event_type": "...", ...extra attributes}``.
|
|
240
|
+
|
|
241
|
+
Events are NOT retained — they are instantaneous notifications.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
event_type: One of the registered event types
|
|
245
|
+
(interlock_fault, cycle_complete, standby_blocked).
|
|
246
|
+
**attributes: Additional key-value pairs included in the event payload.
|
|
247
|
+
"""
|
|
248
|
+
import json
|
|
249
|
+
|
|
250
|
+
payload = {"event_type": event_type, **attributes}
|
|
251
|
+
self._publish(self._event_topic(), json.dumps(payload), retain=False)
|
|
252
|
+
|
|
226
253
|
def _ordered_zones(self) -> list[tuple[int, IrrigationZone]]:
|
|
227
254
|
indexed = list(enumerate(self._zones))
|
|
228
255
|
if self._reverse:
|
|
@@ -352,6 +379,7 @@ class IrrigationController:
|
|
|
352
379
|
"".join(traceback.format_stack(limit=5)),
|
|
353
380
|
)
|
|
354
381
|
self.stop_schedules()
|
|
382
|
+
self._cancel_pause_timer()
|
|
355
383
|
await self._stop_current_zone()
|
|
356
384
|
await self._handle_pump_stop_sequence()
|
|
357
385
|
self._state = ControllerState.IDLE
|
|
@@ -370,11 +398,13 @@ class IrrigationController:
|
|
|
370
398
|
self._cancel_zone_timer()
|
|
371
399
|
await self._stop_current_zone()
|
|
372
400
|
self._state = ControllerState.PAUSED
|
|
401
|
+
self._arm_pause_timer()
|
|
373
402
|
await self.publish_all_states()
|
|
374
403
|
|
|
375
404
|
async def resume(self) -> None:
|
|
376
405
|
if self._state != ControllerState.PAUSED or self._active_zone_idx is None:
|
|
377
406
|
return
|
|
407
|
+
self._cancel_pause_timer()
|
|
378
408
|
remaining = self._active_zone_remaining_s or self._current_zone_duration_seconds()
|
|
379
409
|
await self._start_zone(self._active_zone_idx, override_duration=remaining)
|
|
380
410
|
|
|
@@ -400,6 +430,11 @@ class IrrigationController:
|
|
|
400
430
|
)
|
|
401
431
|
if self._standby:
|
|
402
432
|
_LOGGER.info("Irrigation %s is in standby mode, not starting", self.id)
|
|
433
|
+
self._publish_event(
|
|
434
|
+
"standby_blocked",
|
|
435
|
+
controller=self.id,
|
|
436
|
+
message=f"Irrigation '{self.name}' start blocked: standby mode is active",
|
|
437
|
+
)
|
|
403
438
|
return
|
|
404
439
|
|
|
405
440
|
if self._state in (ControllerState.RUNNING, ControllerState.PAUSED):
|
|
@@ -428,6 +463,12 @@ class IrrigationController:
|
|
|
428
463
|
)
|
|
429
464
|
if self._standby:
|
|
430
465
|
_LOGGER.info("Irrigation %s is in standby mode, not starting zone", self.id)
|
|
466
|
+
self._publish_event(
|
|
467
|
+
"standby_blocked",
|
|
468
|
+
controller=self.id,
|
|
469
|
+
zone=zone_id,
|
|
470
|
+
message=f"Irrigation '{self.name}' zone '{zone_id}' blocked: standby mode is active",
|
|
471
|
+
)
|
|
431
472
|
return
|
|
432
473
|
|
|
433
474
|
match_idx = None
|
|
@@ -551,6 +592,11 @@ class IrrigationController:
|
|
|
551
592
|
await self._start_cycle_from_eligible()
|
|
552
593
|
return
|
|
553
594
|
|
|
595
|
+
self._publish_event(
|
|
596
|
+
"cycle_complete",
|
|
597
|
+
controller=self.id,
|
|
598
|
+
message=f"Irrigation '{self.name}' cycle completed",
|
|
599
|
+
)
|
|
554
600
|
await self.shutdown()
|
|
555
601
|
|
|
556
602
|
def _ordered_zone_indices(self) -> list[int]:
|
|
@@ -718,8 +764,10 @@ class IrrigationController:
|
|
|
718
764
|
async def _handle_interlock_fault(self, zone_id: str, source_id: str | None) -> None:
|
|
719
765
|
"""Handle interlock-blocked activation.
|
|
720
766
|
|
|
721
|
-
Shuts down the controller and publishes a fault
|
|
722
|
-
|
|
767
|
+
Shuts down the controller and publishes both a retained fault status
|
|
768
|
+
message and a non-retained HA event notification via the event entity.
|
|
769
|
+
Home Assistant automations can listen to the event entity to trigger
|
|
770
|
+
mobile push notifications, Telegram messages, etc.
|
|
723
771
|
|
|
724
772
|
Args:
|
|
725
773
|
zone_id: ID of the zone that was being started.
|
|
@@ -734,13 +782,15 @@ class IrrigationController:
|
|
|
734
782
|
source_info,
|
|
735
783
|
)
|
|
736
784
|
|
|
737
|
-
|
|
785
|
+
fault_message = f"Irrigation '{self.name}' stopped: output blocked by interlock (zone: {zone_id})"
|
|
786
|
+
|
|
787
|
+
# Publish retained fault status on MQTT (legacy topic)
|
|
738
788
|
fault_payload = {
|
|
739
789
|
"fault": "interlock_blocked",
|
|
740
790
|
"controller": self.id,
|
|
741
791
|
"zone": zone_id,
|
|
742
792
|
"source": source_id or "",
|
|
743
|
-
"message":
|
|
793
|
+
"message": fault_message,
|
|
744
794
|
}
|
|
745
795
|
self._publish(
|
|
746
796
|
f"{self._topic_prefix}/{IRRIGATION}/{self.id}/fault",
|
|
@@ -748,6 +798,15 @@ class IrrigationController:
|
|
|
748
798
|
retain=False,
|
|
749
799
|
)
|
|
750
800
|
|
|
801
|
+
# Publish HA event entity notification (non-retained, instantaneous)
|
|
802
|
+
self._publish_event(
|
|
803
|
+
"interlock_fault",
|
|
804
|
+
controller=self.id,
|
|
805
|
+
zone=zone_id,
|
|
806
|
+
source=source_id or "",
|
|
807
|
+
message=fault_message,
|
|
808
|
+
)
|
|
809
|
+
|
|
751
810
|
await self.shutdown()
|
|
752
811
|
await self.publish_all_states()
|
|
753
812
|
|
|
@@ -799,6 +858,44 @@ class IrrigationController:
|
|
|
799
858
|
async def _zone_timer_callback(self, _timestamp: datetime) -> None:
|
|
800
859
|
await self._advance_to_next_zone(force=False)
|
|
801
860
|
|
|
861
|
+
def _cancel_pause_timer(self) -> None:
|
|
862
|
+
"""Cancel the pause timeout timer if active."""
|
|
863
|
+
if self._pause_timer_cancel is not None:
|
|
864
|
+
self._pause_timer_cancel()
|
|
865
|
+
self._pause_timer_cancel = None
|
|
866
|
+
|
|
867
|
+
def _arm_pause_timer(self) -> None:
|
|
868
|
+
"""Arm an auto-shutdown timer for the PAUSED state.
|
|
869
|
+
|
|
870
|
+
If pause_timeout_s is 0, no timer is set (pause lasts indefinitely).
|
|
871
|
+
Otherwise, the controller will auto-shutdown after the configured timeout.
|
|
872
|
+
"""
|
|
873
|
+
self._cancel_pause_timer()
|
|
874
|
+
if self._pause_timeout_s <= 0:
|
|
875
|
+
return
|
|
876
|
+
point = utcnow() + timedelta(seconds=self._pause_timeout_s)
|
|
877
|
+
self._pause_timer_cancel = async_track_point_in_time(
|
|
878
|
+
loop=self._event_bus._loop,
|
|
879
|
+
job=self._pause_timeout_callback,
|
|
880
|
+
point_in_time=point,
|
|
881
|
+
)
|
|
882
|
+
_LOGGER.info(
|
|
883
|
+
"Irrigation %s: pause timeout armed for %d seconds",
|
|
884
|
+
self.id,
|
|
885
|
+
self._pause_timeout_s,
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
async def _pause_timeout_callback(self, _timestamp: datetime) -> None:
|
|
889
|
+
"""Handle pause timeout — auto-shutdown the controller."""
|
|
890
|
+
if self._state != ControllerState.PAUSED:
|
|
891
|
+
return
|
|
892
|
+
_LOGGER.warning(
|
|
893
|
+
"Irrigation %s: pause timeout expired after %d seconds, shutting down",
|
|
894
|
+
self.id,
|
|
895
|
+
self._pause_timeout_s,
|
|
896
|
+
)
|
|
897
|
+
await self.shutdown()
|
|
898
|
+
|
|
802
899
|
async def handle_main_command(self, payload: str) -> None:
|
|
803
900
|
cmd = payload.strip()
|
|
804
901
|
upper = cmd.upper()
|
|
@@ -10,15 +10,21 @@ The actual hardware control is delegated to the parent ``ESPHomeDeviceManager``
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import asyncio
|
|
13
14
|
import logging
|
|
14
15
|
import time
|
|
15
|
-
from typing import Any
|
|
16
|
+
from typing import TYPE_CHECKING, Any
|
|
16
17
|
|
|
17
18
|
from boneio.const import OFF, ON, SWITCH
|
|
18
|
-
from boneio.core.events import EventBus
|
|
19
|
+
from boneio.core.events import EventBus, async_track_point_in_time, utcnow
|
|
20
|
+
from boneio.core.utils import callback
|
|
21
|
+
from boneio.core.utils.timeperiod import TimePeriod, parse_time_to_seconds
|
|
19
22
|
from boneio.models import OutputState
|
|
20
23
|
from boneio.models.events import OutputEvent
|
|
21
24
|
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from boneio.integration.interlock import SoftwareInterlockManager
|
|
27
|
+
|
|
22
28
|
_LOGGER = logging.getLogger(__name__)
|
|
23
29
|
|
|
24
30
|
|
|
@@ -60,6 +66,16 @@ class RemoteOutputBase:
|
|
|
60
66
|
show_in_ha: bool = False,
|
|
61
67
|
area: str | None = None,
|
|
62
68
|
on_disconnect: str = "ignore",
|
|
69
|
+
interlock_manager: SoftwareInterlockManager | None = None,
|
|
70
|
+
interlock_groups: list[str] | None = None,
|
|
71
|
+
enforce_interlock: bool = False,
|
|
72
|
+
momentary_turn_on: TimePeriod | None = None,
|
|
73
|
+
momentary_turn_off: TimePeriod | None = None,
|
|
74
|
+
adjustable_duration: bool = False,
|
|
75
|
+
duration_default: float = 60.0,
|
|
76
|
+
duration_min: float = 1.0,
|
|
77
|
+
duration_max: float = 3600.0,
|
|
78
|
+
duration_unit: str = "s",
|
|
63
79
|
) -> None:
|
|
64
80
|
self._id = id
|
|
65
81
|
self._name = name
|
|
@@ -72,10 +88,32 @@ class RemoteOutputBase:
|
|
|
72
88
|
self.area: str | None = area
|
|
73
89
|
self._on_disconnect = on_disconnect
|
|
74
90
|
|
|
91
|
+
# Interlock support (shared with local BasicOutput instances)
|
|
92
|
+
self._interlock_manager: SoftwareInterlockManager | None = interlock_manager
|
|
93
|
+
self._interlock_groups: list[str] = interlock_groups or []
|
|
94
|
+
self._enforce_interlock: bool = enforce_interlock
|
|
95
|
+
|
|
96
|
+
# Momentary actions (auto-off / auto-on after duration)
|
|
97
|
+
self._momentary_turn_on: TimePeriod | None = momentary_turn_on
|
|
98
|
+
self._momentary_turn_off: TimePeriod | None = momentary_turn_off
|
|
99
|
+
self._momentary_action: Any = None
|
|
100
|
+
|
|
101
|
+
# Adjustable duration (HA number entity slider)
|
|
102
|
+
self._adjustable_duration_enabled: bool = adjustable_duration
|
|
103
|
+
self._duration_min: float = max(1.0, duration_min)
|
|
104
|
+
self._duration_max: float = max(self._duration_min, duration_max)
|
|
105
|
+
self._duration_default: float = duration_default
|
|
106
|
+
self._duration_unit: str = duration_unit
|
|
107
|
+
self._adjustable_duration: float = max(self._duration_min, min(self._duration_max, self._duration_default))
|
|
108
|
+
# If adjustable_duration is on, momentary_turn_on is managed by the slider
|
|
109
|
+
if self._adjustable_duration_enabled:
|
|
110
|
+
self._momentary_turn_on = None
|
|
111
|
+
|
|
75
112
|
self._state: str = OFF
|
|
76
113
|
self._last_timestamp: float = 0.0
|
|
77
114
|
self._available: bool = False
|
|
78
115
|
self._brightness: int | None = None # 0-255, None if not a dimmable light
|
|
116
|
+
self._loop: asyncio.AbstractEventLoop | None = None
|
|
79
117
|
|
|
80
118
|
# Reference to remote device manager — set by register_remote_outputs().
|
|
81
119
|
# May be None at startup if ESPHome devices load in background.
|
|
@@ -186,6 +224,34 @@ class RemoteOutputBase:
|
|
|
186
224
|
self._id,
|
|
187
225
|
)
|
|
188
226
|
|
|
227
|
+
# ------------------------------------------------------------------
|
|
228
|
+
# Interlock
|
|
229
|
+
# ------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
def set_interlock(
|
|
232
|
+
self,
|
|
233
|
+
interlock_manager: SoftwareInterlockManager,
|
|
234
|
+
interlock_groups: list[str],
|
|
235
|
+
) -> None:
|
|
236
|
+
"""Set interlock manager and groups.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
interlock_manager: Shared interlock manager instance.
|
|
240
|
+
interlock_groups: List of group names this output belongs to.
|
|
241
|
+
"""
|
|
242
|
+
self._interlock_manager = interlock_manager
|
|
243
|
+
self._interlock_groups = interlock_groups
|
|
244
|
+
|
|
245
|
+
def check_interlock(self) -> bool:
|
|
246
|
+
"""Check if this output can be turned on without violating interlocks.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
True if no interlock violation, False if blocked.
|
|
250
|
+
"""
|
|
251
|
+
if self._interlock_manager is not None and self._interlock_groups:
|
|
252
|
+
return self._interlock_manager.can_turn_on(self, self._interlock_groups)
|
|
253
|
+
return True
|
|
254
|
+
|
|
189
255
|
# ------------------------------------------------------------------
|
|
190
256
|
# Core control methods (duck-type compatible with BasicOutput)
|
|
191
257
|
# ------------------------------------------------------------------
|
|
@@ -193,12 +259,21 @@ class RemoteOutputBase:
|
|
|
193
259
|
async def async_turn_on(self, timestamp: float | None = None) -> bool:
|
|
194
260
|
"""Turn on the remote output.
|
|
195
261
|
|
|
262
|
+
Checks interlock before sending the ON command. If another output
|
|
263
|
+
in the same interlock group is already ON, the request is rejected.
|
|
264
|
+
After successful ON, schedules momentary auto-OFF if configured.
|
|
265
|
+
|
|
196
266
|
Args:
|
|
197
267
|
timestamp: Optional timestamp for state tracking.
|
|
198
268
|
|
|
199
269
|
Returns:
|
|
200
|
-
True if the output was turned on, False
|
|
270
|
+
True if the output was turned on, False if blocked or failed.
|
|
201
271
|
"""
|
|
272
|
+
can_turn_on = self.check_interlock()
|
|
273
|
+
if not can_turn_on:
|
|
274
|
+
_LOGGER.warning("Interlock active: cannot turn on remote output '%s'", self._id)
|
|
275
|
+
return False
|
|
276
|
+
|
|
202
277
|
if not self._resolve_device_manager():
|
|
203
278
|
_LOGGER.error("Remote output '%s' has no device manager, cannot turn on", self._id)
|
|
204
279
|
return False
|
|
@@ -211,6 +286,7 @@ class RemoteOutputBase:
|
|
|
211
286
|
self._state = ON
|
|
212
287
|
self._last_timestamp = timestamp or time.time()
|
|
213
288
|
self._emit_state_event()
|
|
289
|
+
self._execute_momentary_turn(ON)
|
|
214
290
|
_LOGGER.debug("Remote output '%s' turned ON", self._id)
|
|
215
291
|
else:
|
|
216
292
|
_LOGGER.warning("Failed to turn ON remote output '%s'", self._id)
|
|
@@ -219,6 +295,8 @@ class RemoteOutputBase:
|
|
|
219
295
|
async def async_turn_off(self, timestamp: float | None = None) -> None:
|
|
220
296
|
"""Turn off the remote output.
|
|
221
297
|
|
|
298
|
+
After successful OFF, schedules momentary auto-ON if configured.
|
|
299
|
+
|
|
222
300
|
Args:
|
|
223
301
|
timestamp: Optional timestamp for state tracking.
|
|
224
302
|
"""
|
|
@@ -234,6 +312,7 @@ class RemoteOutputBase:
|
|
|
234
312
|
self._state = OFF
|
|
235
313
|
self._last_timestamp = timestamp or time.time()
|
|
236
314
|
self._emit_state_event()
|
|
315
|
+
self._execute_momentary_turn(OFF)
|
|
237
316
|
_LOGGER.debug("Remote output '%s' turned OFF", self._id)
|
|
238
317
|
else:
|
|
239
318
|
_LOGGER.warning("Failed to turn OFF remote output '%s'", self._id)
|
|
@@ -259,6 +338,10 @@ class RemoteOutputBase:
|
|
|
259
338
|
Called by ESPHomeDeviceManager when it receives a state update
|
|
260
339
|
for this output's entity.
|
|
261
340
|
|
|
341
|
+
If ``enforce_interlock`` is enabled and the output was turned ON
|
|
342
|
+
externally while violating an interlock, boneIO will immediately
|
|
343
|
+
send a turn-off command.
|
|
344
|
+
|
|
262
345
|
Args:
|
|
263
346
|
new_state: ``True`` if the output is now ON.
|
|
264
347
|
brightness: Optional brightness value (0-255) for dimmable lights.
|
|
@@ -281,6 +364,15 @@ class RemoteOutputBase:
|
|
|
281
364
|
self._brightness,
|
|
282
365
|
)
|
|
283
366
|
|
|
367
|
+
# Enforce interlock: if turned ON externally and violating interlock,
|
|
368
|
+
# immediately send OFF command to the remote device.
|
|
369
|
+
if new_state and self._enforce_interlock and not self.check_interlock():
|
|
370
|
+
_LOGGER.warning(
|
|
371
|
+
"Remote output '%s' turned ON externally, violating interlock — forcing OFF",
|
|
372
|
+
self._id,
|
|
373
|
+
)
|
|
374
|
+
asyncio.ensure_future(self._enforce_interlock_off())
|
|
375
|
+
|
|
284
376
|
async def async_set_brightness(self, brightness: int, timestamp: float | None = None) -> None:
|
|
285
377
|
"""Set brightness on the remote light output.
|
|
286
378
|
|
|
@@ -386,9 +478,138 @@ class RemoteOutputBase:
|
|
|
386
478
|
area=self.area,
|
|
387
479
|
remote=True,
|
|
388
480
|
brightness=self._brightness,
|
|
481
|
+
interlock_groups=self._interlock_groups,
|
|
389
482
|
)
|
|
390
483
|
self._event_bus.trigger_event(OutputEvent(entity_id=self._id, state=output_state))
|
|
391
484
|
|
|
485
|
+
# ------------------------------------------------------------------
|
|
486
|
+
# Momentary actions (auto-off / auto-on after delay)
|
|
487
|
+
# ------------------------------------------------------------------
|
|
488
|
+
|
|
489
|
+
def _execute_momentary_turn(self, momentary_type: str) -> None:
|
|
490
|
+
"""Schedule momentary action (auto-off after ON, or auto-on after OFF).
|
|
491
|
+
|
|
492
|
+
If adjustable_duration is enabled and this is a turn-ON action,
|
|
493
|
+
the slider value is used instead of the static momentary_turn_on.
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
momentary_type: ON or OFF.
|
|
497
|
+
"""
|
|
498
|
+
if self._loop is None:
|
|
499
|
+
try:
|
|
500
|
+
self._loop = asyncio.get_running_loop()
|
|
501
|
+
except RuntimeError:
|
|
502
|
+
return
|
|
503
|
+
|
|
504
|
+
if self._momentary_action:
|
|
505
|
+
_LOGGER.debug("Cancelling momentary action for %s", self._name)
|
|
506
|
+
self._momentary_action()
|
|
507
|
+
|
|
508
|
+
if momentary_type == ON:
|
|
509
|
+
action = self.async_turn_off
|
|
510
|
+
if self._adjustable_duration_enabled:
|
|
511
|
+
delayed_action = TimePeriod(seconds=self._adjustable_duration)
|
|
512
|
+
else:
|
|
513
|
+
delayed_action = self._momentary_turn_on
|
|
514
|
+
else:
|
|
515
|
+
action = self.async_turn_on
|
|
516
|
+
delayed_action = self._momentary_turn_off
|
|
517
|
+
|
|
518
|
+
if delayed_action:
|
|
519
|
+
_LOGGER.debug(
|
|
520
|
+
"Scheduling momentary action for %s in %s",
|
|
521
|
+
self._name,
|
|
522
|
+
delayed_action.as_timedelta,
|
|
523
|
+
)
|
|
524
|
+
self._momentary_action = async_track_point_in_time(
|
|
525
|
+
loop=self._loop,
|
|
526
|
+
job=self._momentary_callback,
|
|
527
|
+
point_in_time=utcnow() + delayed_action.as_timedelta,
|
|
528
|
+
action=action,
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
@callback
|
|
532
|
+
async def _momentary_callback(self, timestamp: float, action: Any) -> None:
|
|
533
|
+
"""Execute the scheduled momentary turn-on or turn-off."""
|
|
534
|
+
_LOGGER.info("Momentary callback at %s for remote output %s", timestamp, self._name)
|
|
535
|
+
await action(timestamp=timestamp)
|
|
536
|
+
self._momentary_action = None
|
|
537
|
+
|
|
538
|
+
# -- Adjustable duration -------------------------------------------------
|
|
539
|
+
|
|
540
|
+
@property
|
|
541
|
+
def adjustable_duration_enabled(self) -> bool:
|
|
542
|
+
"""Whether this output has an adjustable duration (HA number entity)."""
|
|
543
|
+
return self._adjustable_duration_enabled
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def adjustable_duration(self) -> float:
|
|
547
|
+
"""Current adjustable duration value in seconds."""
|
|
548
|
+
return self._adjustable_duration
|
|
549
|
+
|
|
550
|
+
@property
|
|
551
|
+
def duration_min(self) -> float:
|
|
552
|
+
"""Minimum duration in seconds for HA slider."""
|
|
553
|
+
return self._duration_min
|
|
554
|
+
|
|
555
|
+
@property
|
|
556
|
+
def duration_max(self) -> float:
|
|
557
|
+
"""Maximum duration in seconds for HA slider."""
|
|
558
|
+
return self._duration_max
|
|
559
|
+
|
|
560
|
+
@property
|
|
561
|
+
def duration_unit(self) -> str:
|
|
562
|
+
"""Unit of measurement for HA number entity ('s' or 'min')."""
|
|
563
|
+
return self._duration_unit
|
|
564
|
+
|
|
565
|
+
def set_adjustable_duration(self, seconds: float) -> None:
|
|
566
|
+
"""Set the adjustable duration value.
|
|
567
|
+
|
|
568
|
+
Clamps to [duration_min, duration_max].
|
|
569
|
+
|
|
570
|
+
Args:
|
|
571
|
+
seconds: New duration in seconds.
|
|
572
|
+
"""
|
|
573
|
+
self._adjustable_duration = max(self._duration_min, min(self._duration_max, seconds))
|
|
574
|
+
_LOGGER.debug(
|
|
575
|
+
"Remote output '%s' adjustable duration set to %.1fs",
|
|
576
|
+
self.id,
|
|
577
|
+
self._adjustable_duration,
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
def restore_adjustable_duration(self, seconds: float) -> None:
|
|
581
|
+
"""Restore persisted duration value.
|
|
582
|
+
|
|
583
|
+
Args:
|
|
584
|
+
seconds: Persisted duration value.
|
|
585
|
+
"""
|
|
586
|
+
self._adjustable_duration = max(self._duration_min, min(self._duration_max, seconds))
|
|
587
|
+
|
|
588
|
+
async def _enforce_interlock_off(self) -> None:
|
|
589
|
+
"""Send OFF command to remote device after interlock violation.
|
|
590
|
+
|
|
591
|
+
Called when the remote device reports ON state that violates an
|
|
592
|
+
active interlock group. Small delay allows the state to settle.
|
|
593
|
+
"""
|
|
594
|
+
await asyncio.sleep(0.1) # Allow state to settle
|
|
595
|
+
if not self._resolve_device_manager():
|
|
596
|
+
_LOGGER.error(
|
|
597
|
+
"Cannot enforce interlock OFF on '%s': no device manager",
|
|
598
|
+
self._id,
|
|
599
|
+
)
|
|
600
|
+
return
|
|
601
|
+
success = await self._device_manager.control_output(
|
|
602
|
+
output_id=self._output_id,
|
|
603
|
+
action="OFF",
|
|
604
|
+
)
|
|
605
|
+
if success:
|
|
606
|
+
_LOGGER.info("Interlock enforced: remote output '%s' turned OFF", self._id)
|
|
607
|
+
else:
|
|
608
|
+
_LOGGER.error(
|
|
609
|
+
"Failed to enforce interlock OFF on remote output '%s'",
|
|
610
|
+
self._id,
|
|
611
|
+
)
|
|
612
|
+
|
|
392
613
|
# ------------------------------------------------------------------
|
|
393
614
|
# Properties (duck-type compatible with BasicOutput)
|
|
394
615
|
# ------------------------------------------------------------------
|
|
@@ -11,11 +11,13 @@ from boneio.const import NEXT_VALVE, ON, PAUSE, RESUME
|
|
|
11
11
|
from boneio.core.utils.timeperiod import parse_time_to_seconds
|
|
12
12
|
from boneio.integration.homeassistant import (
|
|
13
13
|
ha_irrigation_button_message,
|
|
14
|
+
ha_irrigation_event_message,
|
|
14
15
|
ha_irrigation_main_switch_message,
|
|
15
16
|
ha_irrigation_number_message,
|
|
16
17
|
ha_irrigation_select_message,
|
|
17
18
|
ha_irrigation_switch_message,
|
|
18
19
|
ha_irrigation_timestamp_sensor_message,
|
|
20
|
+
ha_irrigation_valve_message,
|
|
19
21
|
)
|
|
20
22
|
|
|
21
23
|
if TYPE_CHECKING:
|
|
@@ -90,6 +92,7 @@ class IrrigationManager:
|
|
|
90
92
|
repeat=int(cfg.get("repeat", 0)),
|
|
91
93
|
auto_advance=bool(cfg.get("auto_advance", True)),
|
|
92
94
|
reverse=bool(cfg.get("reverse", False)),
|
|
95
|
+
pause_timeout_s=int(parse_time_to_seconds(cfg.get("pause_timeout"), 1800)),
|
|
93
96
|
)
|
|
94
97
|
|
|
95
98
|
def _build_zone(self, zone_cfg: dict[str, Any]) -> IrrigationZone | None:
|
|
@@ -280,9 +283,10 @@ class IrrigationManager:
|
|
|
280
283
|
(f"{ctrl.id}_next_valve", "button"),
|
|
281
284
|
(f"{ctrl.id}_pause", "button"),
|
|
282
285
|
(f"{ctrl.id}_resume", "button"),
|
|
286
|
+
(f"{ctrl.id}_event", "event"),
|
|
283
287
|
]
|
|
284
288
|
for zone in ctrl.zones:
|
|
285
|
-
discovery_ids.append((f"{ctrl.id}_zone_{zone.id}", "
|
|
289
|
+
discovery_ids.append((f"{ctrl.id}_zone_{zone.id}", "valve"))
|
|
286
290
|
discovery_ids.append((f"{ctrl.id}_zone_{zone.id}_enabled", "switch"))
|
|
287
291
|
discovery_ids.append((f"{ctrl.id}_zone_{zone.id}_duration", "number"))
|
|
288
292
|
for idx in range(len(ctrl._schedule)):
|
|
@@ -506,10 +510,16 @@ class IrrigationManager:
|
|
|
506
510
|
)
|
|
507
511
|
|
|
508
512
|
for zone in ctrl.zones:
|
|
513
|
+
# Remove stale switch discovery (migration from switch → valve)
|
|
509
514
|
self._manager.publish_ha_discovery(
|
|
510
515
|
id=f"{ctrl.id}_zone_{zone.id}",
|
|
511
516
|
ha_type="switch",
|
|
512
|
-
payload=
|
|
517
|
+
payload="",
|
|
518
|
+
)
|
|
519
|
+
self._manager.publish_ha_discovery(
|
|
520
|
+
id=f"{ctrl.id}_zone_{zone.id}",
|
|
521
|
+
ha_type="valve",
|
|
522
|
+
payload=ha_irrigation_valve_message(
|
|
513
523
|
ctrl.id,
|
|
514
524
|
ctrl.name,
|
|
515
525
|
suffix=f"zone/{zone.id}",
|
|
@@ -584,3 +594,14 @@ class IrrigationManager:
|
|
|
584
594
|
config_helper=cfg,
|
|
585
595
|
),
|
|
586
596
|
)
|
|
597
|
+
|
|
598
|
+
# Event entity — fires on interlock faults, cycle completions, etc.
|
|
599
|
+
self._manager.publish_ha_discovery(
|
|
600
|
+
id=f"{ctrl.id}_event",
|
|
601
|
+
ha_type="event",
|
|
602
|
+
payload=ha_irrigation_event_message(
|
|
603
|
+
ctrl.id,
|
|
604
|
+
ctrl.name,
|
|
605
|
+
config_helper=cfg,
|
|
606
|
+
),
|
|
607
|
+
)
|