From cfd0d40c8e75e96d9924d844ed0dabc0239e7e23 Mon Sep 17 00:00:00 2001 From: Home Assistant Version Control Date: Wed, 7 Oct 2026 02:49:21 +0000 Subject: [PATCH] 208 files --- .../__pycache__/__init__.cpython-314.pyc | Bin 7390 -> 0 bytes .../__pycache__/binary_sensor.cpython-314.pyc | Bin 1199 -> 0 bytes .../spook/__pycache__/button.cpython-314.pyc | Bin 1176 -> 0 bytes .../__pycache__/config_flow.cpython-314.pyc | Bin 6379 -> 0 bytes .../spook/__pycache__/const.cpython-314.pyc | Bin 1092 -> 0 bytes .../spook/__pycache__/entity.cpython-314.pyc | Bin 1596 -> 0 bytes .../entity_filtering.cpython-314.pyc | Bin 31764 -> 0 bytes .../spook/__pycache__/event.cpython-314.pyc | Bin 1173 -> 0 bytes .../integration_linking.cpython-314.pyc | Bin 2933 -> 0 bytes .../__pycache__/listeners.cpython-314.pyc | Bin 2862 -> 0 bytes .../spook/__pycache__/number.cpython-314.pyc | Bin 1176 -> 0 bytes .../spook/__pycache__/repairs.cpython-314.pyc | Bin 79724 -> 0 bytes .../spook/__pycache__/select.cpython-314.pyc | Bin 1176 -> 0 bytes .../spook/__pycache__/sensor.cpython-314.pyc | Bin 1176 -> 0 bytes .../__pycache__/services.cpython-314.pyc | Bin 38784 -> 0 bytes .../__pycache__/setup_helpers.cpython-314.pyc | Bin 7347 -> 0 bytes .../spook/__pycache__/switch.cpython-314.pyc | Bin 1177 -> 0 bytes .../spook/__pycache__/time.cpython-314.pyc | Bin 1170 -> 0 bytes custom_components/spook/conditions.yaml | 22 + .../__pycache__/__init__.cpython-314.pyc | Bin 187 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 198 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 206 -> 0 bytes .../unknown_area_references.cpython-314.pyc | Bin 2633 -> 0 bytes .../unknown_device_references.cpython-314.pyc | Bin 4957 -> 0 bytes .../unknown_entity_references.cpython-314.pyc | Bin 19621 -> 0 bytes .../unknown_floor_references.cpython-314.pyc | Bin 2589 -> 0 bytes .../unknown_label_references.cpython-314.pyc | Bin 2589 -> 0 bytes ...unknown_service_references.cpython-314.pyc | Bin 3210 -> 0 bytes .../repairs/unknown_area_references.py | 9 +- .../repairs/unknown_device_references.py | 13 +- .../repairs/unknown_entity_references.py | 25 + .../repairs/unknown_floor_references.py | 9 +- .../repairs/unknown_label_references.py | 9 +- .../__pycache__/__init__.cpython-314.pyc | Bin 197 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 206 -> 0 bytes .../__pycache__/importer.cpython-314.pyc | Bin 3825 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 193 -> 0 bytes .../cloud/__pycache__/entity.cpython-314.pyc | Bin 2571 -> 0 bytes .../cloud/__pycache__/switch.cpython-314.pyc | Bin 9098 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 193 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 201 -> 0 bytes .../unknown_members.cpython-314.pyc | Bin 3763 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 201 -> 0 bytes .../__pycache__/button.cpython-314.pyc | Bin 4139 -> 0 bytes .../__pycache__/device.cpython-314.pyc | Bin 4566 -> 0 bytes .../__pycache__/entity.cpython-314.pyc | Bin 1754 -> 0 bytes .../__pycache__/sensor.cpython-314.pyc | Bin 32394 -> 0 bytes .../homeassistant/services/__init__.py | 36 +- .../__pycache__/__init__.cpython-314.pyc | Bin 2298 -> 0 bytes .../add_alias_to_area.cpython-314.pyc | Bin 2471 -> 0 bytes .../add_alias_to_floor.cpython-314.pyc | Bin 2482 -> 0 bytes .../add_area_to_floor.cpython-314.pyc | Bin 2495 -> 0 bytes .../add_device_to_area.cpython-314.pyc | Bin 2526 -> 0 bytes .../add_entity_to_area.cpython-314.pyc | Bin 2500 -> 0 bytes .../add_label_to_area.cpython-314.pyc | Bin 2770 -> 0 bytes .../add_label_to_device.cpython-314.pyc | Bin 2810 -> 0 bytes .../add_label_to_entity.cpython-314.pyc | Bin 2784 -> 0 bytes .../__pycache__/create_area.cpython-314.pyc | Bin 2103 -> 0 bytes .../__pycache__/create_floor.cpython-314.pyc | Bin 2296 -> 0 bytes .../__pycache__/create_label.cpython-314.pyc | Bin 2692 -> 0 bytes ...lete_all_orphaned_entities.cpython-314.pyc | Bin 2083 -> 0 bytes .../__pycache__/delete_area.cpython-314.pyc | Bin 1744 -> 0 bytes .../__pycache__/delete_floor.cpython-314.pyc | Bin 1750 -> 0 bytes .../__pycache__/delete_label.cpython-314.pyc | Bin 1744 -> 0 bytes .../disable_config_entry.cpython-314.pyc | Bin 2050 -> 0 bytes .../disable_device.cpython-314.pyc | Bin 1941 -> 0 bytes .../disable_entity.cpython-314.pyc | Bin 2019 -> 0 bytes .../disable_polling.cpython-314.pyc | Bin 2109 -> 0 bytes .../__pycache__/disable_user.cpython-314.pyc | Bin 2494 -> 0 bytes .../enable_config_entry.cpython-314.pyc | Bin 1929 -> 0 bytes .../__pycache__/enable_device.cpython-314.pyc | Bin 1927 -> 0 bytes .../__pycache__/enable_entity.cpython-314.pyc | Bin 1927 -> 0 bytes .../enable_polling.cpython-314.pyc | Bin 2106 -> 0 bytes .../__pycache__/enable_user.cpython-314.pyc | Bin 2490 -> 0 bytes .../__pycache__/hide_entity.cpython-314.pyc | Bin 2022 -> 0 bytes .../ignore_all_discovered.cpython-314.pyc | Bin 3921 -> 0 bytes ...orphaned_database_entities.cpython-314.pyc | Bin 3335 -> 0 bytes .../remove_alias_from_area.cpython-314.pyc | Bin 2491 -> 0 bytes .../remove_alias_from_floor.cpython-314.pyc | Bin 2500 -> 0 bytes .../remove_area_from_floor.cpython-314.pyc | Bin 1922 -> 0 bytes .../remove_device_from_area.cpython-314.pyc | Bin 1961 -> 0 bytes .../remove_entity_from_area.cpython-314.pyc | Bin 1936 -> 0 bytes .../remove_label_from_area.cpython-314.pyc | Bin 2798 -> 0 bytes .../remove_label_from_device.cpython-314.pyc | Bin 2838 -> 0 bytes .../remove_label_from_entity.cpython-314.pyc | Bin 2812 -> 0 bytes .../__pycache__/rename_entity.cpython-314.pyc | Bin 2049 -> 0 bytes .../__pycache__/restart.cpython-314.pyc | Bin 2778 -> 0 bytes .../set_area_aliases.cpython-314.pyc | Bin 2349 -> 0 bytes .../set_floor_aliases.cpython-314.pyc | Bin 2355 -> 0 bytes .../__pycache__/unhide_entity.cpython-314.pyc | Bin 1925 -> 0 bytes .../update_entity_id.cpython-314.pyc | Bin 1938 -> 0 bytes .../services/disable_config_entry.py | 8 +- .../services/enable_config_entry.py | 8 +- .../__pycache__/__init__.cpython-314.pyc | Bin 200 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 3883 -> 0 bytes .../__pycache__/create.cpython-314.pyc | Bin 1321 -> 0 bytes .../__pycache__/decrement.cpython-314.pyc | Bin 2264 -> 0 bytes .../__pycache__/delete.cpython-314.pyc | Bin 725 -> 0 bytes .../__pycache__/increment.cpython-314.pyc | Bin 2264 -> 0 bytes .../services/__pycache__/max.cpython-314.pyc | Bin 1487 -> 0 bytes .../services/__pycache__/min.cpython-314.pyc | Bin 1487 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 200 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 209 -> 0 bytes .../__pycache__/random.cpython-314.pyc | Bin 790 -> 0 bytes .../__pycache__/shuffle.cpython-314.pyc | Bin 1674 -> 0 bytes .../services/__pycache__/sort.cpython-314.pyc | Bin 1630 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 199 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 207 -> 0 bytes .../unknown_source.cpython-314.pyc | Bin 1718 -> 0 bytes .../integration/repairs/unknown_source.py | 34 - .../__pycache__/__init__.cpython-314.pyc | Bin 196 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 204 -> 0 bytes .../unknown_entity_references.cpython-314.pyc | Bin 20949 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 194 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 2652 -> 0 bytes .../__pycache__/decrement.cpython-314.pyc | Bin 2483 -> 0 bytes .../__pycache__/increment.cpython-314.pyc | Bin 2483 -> 0 bytes .../services/__pycache__/max.cpython-314.pyc | Bin 1732 -> 0 bytes .../services/__pycache__/min.cpython-314.pyc | Bin 1732 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 194 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 203 -> 0 bytes .../add_device_tracker.cpython-314.pyc | Bin 2965 -> 0 bytes .../remove_device_tracker.cpython-314.pyc | Bin 2999 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 197 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 205 -> 0 bytes .../unknown_ignored_zones.cpython-314.pyc | Bin 3210 -> 0 bytes .../unknown_tracked_entities.cpython-314.pyc | Bin 3238 -> 0 bytes .../__pycache__/unknown_zone.cpython-314.pyc | Bin 2723 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 196 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 205 -> 0 bytes .../import_statistics.cpython-314.pyc | Bin 4221 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 195 -> 0 bytes .../__pycache__/button.cpython-314.pyc | Bin 3561 -> 0 bytes .../__pycache__/entity.cpython-314.pyc | Bin 1722 -> 0 bytes .../repairs/__pycache__/event.cpython-314.pyc | Bin 3600 -> 0 bytes .../__pycache__/sensor.cpython-314.pyc | Bin 6516 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 204 -> 0 bytes .../__pycache__/create.cpython-314.pyc | Bin 2828 -> 0 bytes .../__pycache__/ignore_all.cpython-314.pyc | Bin 1600 -> 0 bytes .../__pycache__/remove.cpython-314.pyc | Bin 1748 -> 0 bytes .../__pycache__/unignore_all.cpython-314.pyc | Bin 1606 -> 0 bytes .../ectoplasms/repairs/services/ignore_all.py | 18 +- .../repairs/services/unignore_all.py | 18 +- .../__pycache__/__init__.cpython-314.pyc | Bin 193 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 201 -> 0 bytes .../unknown_entity_references.cpython-314.pyc | Bin 3387 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 194 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 202 -> 0 bytes .../unknown_area_references.cpython-314.pyc | Bin 3523 -> 0 bytes .../unknown_device_references.cpython-314.pyc | Bin 3607 -> 0 bytes .../unknown_entity_references.cpython-314.pyc | Bin 8336 -> 0 bytes .../unknown_floor_references.cpython-314.pyc | Bin 3539 -> 0 bytes .../unknown_label_references.cpython-314.pyc | Bin 3539 -> 0 bytes ...unknown_service_references.cpython-314.pyc | Bin 2737 -> 0 bytes .../script/repairs/unknown_area_references.py | 9 +- .../repairs/unknown_device_references.py | 13 +- .../repairs/unknown_entity_references.py | 119 +- .../repairs/unknown_floor_references.py | 9 +- .../repairs/unknown_label_references.py | 9 +- .../__pycache__/__init__.cpython-314.pyc | Bin 194 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 203 -> 0 bytes .../__pycache__/random.cpython-314.pyc | Bin 2057 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 193 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 202 -> 0 bytes .../services/__pycache__/boo.cpython-314.pyc | Bin 1305 -> 0 bytes .../__pycache__/random_fail.cpython-314.pyc | Bin 1455 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 199 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 207 -> 0 bytes .../unknown_source.cpython-314.pyc | Bin 1550 -> 0 bytes .../switch_as_x/repairs/unknown_source.py | 30 - .../__pycache__/__init__.cpython-314.pyc | Bin 193 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 202 -> 0 bytes .../__pycache__/set_duration.cpython-314.pyc | Bin 2940 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 193 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 201 -> 0 bytes .../unknown_source.cpython-314.pyc | Bin 1657 -> 0 bytes .../trend/repairs/unknown_source.py | 33 - .../__pycache__/__init__.cpython-314.pyc | Bin 201 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 209 -> 0 bytes .../unknown_source.cpython-314.pyc | Bin 1688 -> 0 bytes .../utility_meter/repairs/unknown_source.py | 33 - .../zone/__pycache__/__init__.cpython-314.pyc | Bin 192 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 201 -> 0 bytes .../__pycache__/create.cpython-314.pyc | Bin 1901 -> 0 bytes .../__pycache__/delete.cpython-314.pyc | Bin 3077 -> 0 bytes .../__pycache__/update.cpython-314.pyc | Bin 3153 -> 0 bytes .../spook/ectoplasms/zone/services/update.py | 58 +- custom_components/spook/entity_filtering.py | 32 +- custom_components/spook/icons.json | 18 + .../spook_calibration/manifest.json | 2 +- .../spook_calibration/translations/hu.json | 29 +- .../integrations/spook_inverse/manifest.json | 2 +- .../spook_inverse/translations/de.json | 42 +- .../spook_inverse/translations/es.json | 51 +- .../spook_inverse/translations/hu.json | 32 + .../spook_time_in_state/manifest.json | 2 +- custom_components/spook/manifest.json | 2 +- custom_components/spook/services.yaml | 117 +- custom_components/spook/translations/de.json | 463 +- custom_components/spook/translations/en.json | 104 +- custom_components/spook/translations/es.json | 37 +- custom_components/spook/translations/nl.json | 197 +- .../spook/translations/sr-Latn.json | 148 + custom_components/spook/translations/sr.json | 148 + custom_components/spook/triggers.yaml | 45 + .../sensor-bar-card-plus.js | 4298 ++++++++++++++--- .../sensor-bar-card-plus.js.gz | Bin 69174 -> 99441 bytes zigbee2mqtt/state.json | 44 +- 208 files changed, 5434 insertions(+), 901 deletions(-) delete mode 100644 custom_components/spook/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/binary_sensor.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/button.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/config_flow.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/const.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/entity.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/entity_filtering.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/event.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/integration_linking.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/listeners.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/number.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/repairs.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/select.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/sensor.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/services.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/setup_helpers.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/switch.cpython-314.pyc delete mode 100644 custom_components/spook/__pycache__/time.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/automation/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/automation/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_area_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_device_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_entity_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_floor_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_label_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_service_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/blueprint/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/blueprint/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/blueprint/services/__pycache__/importer.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/cloud/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/cloud/__pycache__/entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/cloud/__pycache__/switch.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/group/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/group/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/group/repairs/__pycache__/unknown_members.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/__pycache__/button.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/__pycache__/device.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/__pycache__/entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/__pycache__/sensor.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_alias_to_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_alias_to_floor.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_area_to_floor.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_device_to_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_entity_to_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_label_to_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_label_to_device.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_label_to_entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/create_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/create_floor.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/create_label.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/delete_all_orphaned_entities.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/delete_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/delete_floor.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/delete_label.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_config_entry.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_device.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_polling.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_user.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_config_entry.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_device.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_polling.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_user.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/hide_entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/ignore_all_discovered.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/list_orphaned_database_entities.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_alias_from_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_alias_from_floor.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_area_from_floor.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_device_from_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_entity_from_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_label_from_area.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_label_from_device.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_label_from_entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/rename_entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/restart.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/set_area_aliases.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/set_floor_aliases.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/unhide_entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/homeassistant/services/__pycache__/update_entity_id.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_number/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_number/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_number/services/__pycache__/create.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_number/services/__pycache__/decrement.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_number/services/__pycache__/delete.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_number/services/__pycache__/increment.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_number/services/__pycache__/max.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_number/services/__pycache__/min.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_select/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_select/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_select/services/__pycache__/random.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_select/services/__pycache__/shuffle.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/input_select/services/__pycache__/sort.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/integration/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/integration/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/integration/repairs/__pycache__/unknown_source.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/integration/repairs/unknown_source.py delete mode 100644 custom_components/spook/ectoplasms/lovelace/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/lovelace/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/lovelace/repairs/__pycache__/unknown_entity_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/number/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/number/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/number/services/__pycache__/decrement.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/number/services/__pycache__/increment.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/number/services/__pycache__/max.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/number/services/__pycache__/min.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/person/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/person/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/person/services/__pycache__/add_device_tracker.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/person/services/__pycache__/remove_device_tracker.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/proximity/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/proximity/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_ignored_zones.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_tracked_entities.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_zone.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/recorder/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/recorder/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/recorder/services/__pycache__/import_statistics.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/__pycache__/button.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/__pycache__/entity.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/__pycache__/event.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/__pycache__/sensor.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/services/__pycache__/create.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/services/__pycache__/ignore_all.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/services/__pycache__/remove.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/repairs/services/__pycache__/unignore_all.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/scene/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/scene/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/scene/repairs/__pycache__/unknown_entity_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/script/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/script/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_area_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_device_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_entity_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_floor_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_label_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_service_references.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/select/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/select/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/select/services/__pycache__/random.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/spook/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/spook/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/spook/services/__pycache__/boo.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/spook/services/__pycache__/random_fail.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/switch_as_x/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/switch_as_x/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/switch_as_x/repairs/__pycache__/unknown_source.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/switch_as_x/repairs/unknown_source.py delete mode 100644 custom_components/spook/ectoplasms/timer/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/timer/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/timer/services/__pycache__/set_duration.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/trend/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/trend/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/trend/repairs/__pycache__/unknown_source.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/trend/repairs/unknown_source.py delete mode 100644 custom_components/spook/ectoplasms/utility_meter/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/utility_meter/repairs/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/utility_meter/repairs/__pycache__/unknown_source.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/utility_meter/repairs/unknown_source.py delete mode 100644 custom_components/spook/ectoplasms/zone/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/zone/services/__pycache__/__init__.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/zone/services/__pycache__/create.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/zone/services/__pycache__/delete.cpython-314.pyc delete mode 100644 custom_components/spook/ectoplasms/zone/services/__pycache__/update.cpython-314.pyc diff --git a/custom_components/spook/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 00f417106aae1c5e94a7b2c3f6a5fd7c80e0372b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7390 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 diff --git a/custom_components/spook/__pycache__/button.cpython-314.pyc b/custom_components/spook/__pycache__/button.cpython-314.pyc deleted file mode 100644 index 32ebd99649338b6bfc7c09c8708221e6d8499c35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1176 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 I?D0GP8z&%#oB#j- diff --git a/custom_components/spook/__pycache__/const.cpython-314.pyc b/custom_components/spook/__pycache__/const.cpython-314.pyc deleted file mode 100644 index 93b930a27b253178fc8f08f66babc9dcd9867fc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1092 zcmah{%}*0i5TCc*zOscP2_J<@q@q!(vE`r%24gI+K$CV`w+n`Zve~qyRNCEU_i?~e zNldu#2YB{>@ecqFke6sohzD$48&u?h}AB z5p+apJ6AJ!VnGvhe;C;CG%(PCkqyvEN@5aPDdk(1nLGjL`%?a8%IE~>G7A(C*(MWE zka7c^vVZJdC@4>{wh$I9(A>zie^VnmdNaQE5sh?3$Gf6;x}wpp=ww&)epmFtzi3=x zLpU`NQ|sg&#OS-he8X|Jqfer1&W;=1a_ZG`T#FIzU#!|z4V|7`&0?F5TjzdHq!z_a z+Is;ReII^TTd10_3LDbQdw>#Rnc<1wd>^m@aw%Qo{8t%1d{$Ks7$-e-2|m9bbKvPA`N8dVccVC)da ze)aRW*ZCZ6z~@Sv0Sv)p?9NY56xPV}6;-Mk=Fnx{_N+ z8mikP{wfBKkyh)i6~1zNMISvc>P~5=R(|P<7v0*-E;_%?eT0xNFm?w0pJDVHOnn^) zH|a5iPnCfaC32)hKFl5~51aIyf}A?1=Yzo^ Nsj2)Bdv-(k_uqTx;06Ey diff --git a/custom_components/spook/__pycache__/entity.cpython-314.pyc b/custom_components/spook/__pycache__/entity.cpython-314.pyc deleted file mode 100644 index 2382e23ee3430871f2c24baab52c4962925ea18e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1596 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 diff --git a/custom_components/spook/__pycache__/event.cpython-314.pyc b/custom_components/spook/__pycache__/event.cpython-314.pyc deleted file mode 100644 index e7e5c2f570d5e441a04ff4cb1fe081ca163e1f7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1173 zcmZWoU279T6up!E$R?Y_Xp5y%NT^sFt4SXe3W`ula2F-iRSHUVST|c6O?Jb~L`|QH z`r?!SL7)5~{)f0&5TuB2DJc}+yfcXbbs%@n+?lz1&zX~zLOu&PK3@9#Y>)!@E*F0W zuH_)3J&gO{fq=*05tleAkt#`?ay3U6y26@kIHoWW6Hj;3jwP%TJkl?!8UW21fCYKS za5GL;WYrzhwVj;EDV%omPC*n(;8}CvW#*t=6sNrGd8pWXGxxf2{Jean{2=bJ@^0K| zh1Cj4Y(I)(;fq!raa`u_KiI6(4X3_w+il#!gtgiB#ZJsRh*y2yj{=ITmp*IK?qr9C zkzjqwL(%JUDqvnoVU2}BY}yT^=1<{TcQ>64Zq!f z;s?(IA|;!Uws6*@g3Ac@VGACo*50BPm|K9mWS4lvOL>~7dxmFvY0s+Z$T#y-Dxc`P zKIbegp`p5Dro?B)MEqt`_O8Comcxh>goxwRdJyu+KWb79~rbE?M~RH2PTej#i!c=-(qO9GmUJ-y4&< zRb{sR^pB%Fr*vRN?2paLpyaY{bmC^O9o}SR%*m(a7cdMpLdX}GKZM0YC?CS|A>96< T6%X{a1AX;CcTY^Tiz@RQkAfw! diff --git a/custom_components/spook/__pycache__/integration_linking.cpython-314.pyc b/custom_components/spook/__pycache__/integration_linking.cpython-314.pyc deleted file mode 100644 index f3fde78500b61b844366b3ff7d2b67bc67ca0d04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2933 zcmchZO>7fK6o6;F>-B$Z#|{Z0Z8rP_oPdqgv_%0bMFe8Y!8QxEK%}f3dt)2yU2AqN zl^`UmstQ4<2KA6u>H+oE8@IxV_E7a2i%_R4kXjXldZ=m?MMypM&DtBg{D`zv9m#Lr z{J-~h-sc(Za@i4-uSUK<`;8f)UrEC#rlvxzVo)lGM=Cmlcq&4LO{!^-=%xrAW>iMk z>4-VZsx0Uf&qOR?t7;9~RNEl>fZf^JF=Ih!fUGk|>|uxM7(_hVi+D>fDtJ^UZw1|^ z>n`5D4Fw$Y?;bD7@;Pn~cS1=opowc#1diG|z-gV88h{ z5qujal_Rh)y?gCYZXS3 z?jBJ{feS#~Gq2{r@CusIJBdiNuEKf;C_``+##(`a4{0h`ZrW+bdePrFVy z+Pb0fw6Vg2j{J^L$@EGqn55~2yE~0N+x1w>k3rP7HHi`=SkIIM3F0%wu#7kF?y!90 zr+2O@(MdYtd*%RWj-@kp3(~BDFqbbt%4uvVg&|Ef zGtL&|S&flWMNwm$$*kF%wUEhUjTJuxt|*wKie_)2OHk#2850m|mUJ#9Wkp4^8ZKx| z1{~371*a9VYM6t9-DGkPL4W`e6FN=B+h9!3|6f3wRli)p^Ca)U)r_t~;UIq562NHXd8H#_zj* z*CrRE*Q0g+NXrDjMjQa?|OExbnjTBDev&Z?!Gm%$-BA1BHz%Oh4v0LtjM=zF?2mt?;fso z53ku6@6HB|T%JZ3a(S!qh4|-(mYv+nrv58aPd1~T1l8zAPG2R4z2N2RP#W7{13mgW zq%p~Djfnwwu?y4yh4ox6~*J;q0B*9MUX zQS^%@wRkg7i=BV-eTn4XOPCa#=bGb$TrcE~f{AtW+g>q^;0Y6A#{v!=L3k51bO2$3 zTHHs(W+KS{J>EjZ01@?-~oQuBRga1$~#l0DM3-H~;_u diff --git a/custom_components/spook/__pycache__/listeners.cpython-314.pyc b/custom_components/spook/__pycache__/listeners.cpython-314.pyc deleted file mode 100644 index 442c6ebacf21dcb234965e71918b4f6d0c263e25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2862 zcmbVO&2JM&6rcUJ9Xqj|K++JJ%|`)~V7Cp32t}w#1aV3l(G5yf@v(C3H8D85*6bKH zRu8FKRcQ~ED2H;adV(8A{(;`1ph8xas8kT*=E#)-RS$h{)^?JnP}-4p=FRNPn|bg3 zelu^VH4-9FemMB!rK2h#zhS3QL|+hUUxKhg48q6-VhGbhT4Z7`%Hp(?mYK|DXiERH86FLLJN(fVKh8> zG2^;4h+@dXbD0eES2~(oodrwI>!0c_qh6p`_f%!Hal*4_`Ak0R{IK3UE)ERqyqa}v z3pMc8Q>n$u)TSD2d6R%uDtLP-!(23{-y2S49XnsRn97!2<}8_6XQ>41ZRVz2j7X|+ z1UFe)_QIxFkB()Uv=cN$;d9@CYK3f&{X)+=Ru4m+RSDW(qk~li+Nw(0ri7}7Vt}aa z>p_reu!v;AY0V;o1bwCUCkPoaqXJ|FM?KeYJed;@*X1SvLFfS(canK=UL66q1JnY= zXi1tUr|a-<>IdPpvbE>SAGIH@&veuelIMU#elg^xR>jwPxc;Xk{|D_Io7%VNRzbHq zA|O5RyiLu@yaHeeRHl5A=#HJWI6pGB&hesW?40h$6hys{vmjtVC#IKex4htHX<@!6m$TT5n!|f#DqY1LE}S;?go)# z!%Jn{Wjky75HiselTo0Yl}nBl9XO+VJh>H7-KYdB1d|->U&rc3rHak%9PiOC_|0vC zmKQ|OHAKxQ2xDSD>4oKh8*xtP19CPHGX-OpVD2ZviZr8yV3r{a5RJ^JN2G#~5dY{- zgJ+AD&K523x^;*ZmMq`p$ysjYO^Nov7BmhO(rU@MV!62czLgbjw71Lj4dxE3n{82V z^mW|uPb-`Lc>WA*0v7G=UOl-M@4kNITIxZ#?|!)Ng&T$geBRnETotZIcndxk1wTd;0)}Nm`2HA1X+pR)==XQVNrp9m8>RL(2xFojqfKrb5U&PP zw#LnmQ$nITESs(LyyPO}mmS~`*-QJN+8&uU^KUVE_jO{jea#?h#gIIQSOj=1ehEkK z#8S7q?uU=vKJs&TeADX2orO0GSS{2LIxk~f3XljEBHK8%=w6imEfPtX&13#MKOO$XcS*E8p8?+ zP6x2UXNC@A#jU_Mbl<+buIumlgnwg)w#EfV5#{`G{0xK_fS52DJg{=|vDSTk^no_` zlQy^^H?BDN7vKF|*VV2Ev7x)Mp&Reti_Lze{vIHq_=bqA^0`%$V4&&`>|BvvRUa#n z4H2Rb6V=u#O4T?C_uP^9a7WB0Iv^}&&dHjl7c$L!88QQW$T2#eRjbeQif71EM| zeUTz-DL$HY9gdgPaj30PGU1YwQzJt6&1$o@xU c=q?$0N@nhnnWs|pkv#H5KDwboy1_O51?*y(cK`qY diff --git a/custom_components/spook/__pycache__/number.cpython-314.pyc b/custom_components/spook/__pycache__/number.cpython-314.pyc deleted file mode 100644 index 4a626d425eb5c90b23db1249b76fbe1bafc72c70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1176 zcmZWoU279T6up!E$R?Y#(W<3VXlSt-t4SXe1VzwNx=U-Qs}?HNVcl$P+GID(OwjbH zs4qVGAL^4o#Q#ti3xX8!Ez&~q%{#McK^@4QGk0e0-gD+;W~i749J^OPZzdUl?{aZ6 za4iQp+{U;A9te029&w425t#{zGp^?7LRVOG4aXEFV&ds;*0F>&0nhZyss=z~1Ylg= zG2EP!7kPEZbZw^~3JPc4qBA6hCcv{s!OM+8t0acK{1jB|?U6^FINmB>FR#X3R^Eu) z&9GV_iS0*GEPT<7BaX}BKM7ML4{hh&3z#LIY^r+bEHdRfn^>Bu+tQ!1b6 z8$RbOE1{vfWTwPtQzCw&A$wQfX7W3U$&dt#61^Tr;U~cUOv&9BT(&crRBC~uQwzFW z#BCbH?GB!d1g~)^Z!PM!UxcjM=_Lh92hxU=GJH8WgFiltVF!-ECgv|0_VS~B4d-w6 zg}#Av--NMi{j5Y5nE78hM2C0>0wjDoB#XBI#^F;0lQ4lL*G>dOiB`x;W^G_SfMxyk z-YK{-$St55H?i_-vZE~-d937-DagW-c~fhWN@n}~qfnH)opS1)lpe3vD;hJf7?WiZ zv;O44{do^_w#*e|C}QBU<>~2Zb{6O4Oe(svNopB3ig~3<1;I}waZ~{N_MTPPvxBTU0H(<%%m4rY diff --git a/custom_components/spook/__pycache__/repairs.cpython-314.pyc b/custom_components/spook/__pycache__/repairs.cpython-314.pyc deleted file mode 100644 index 6beb7c874af65314df3fb548d29acbb7a6ebabe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79724 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 + } + if (marker.type === "target" || marker.type === "floor") { + const markerClass = `${marker.type}-marker`; + return ` +
+
+
+ +
`; + } + return ` +
+
+
+ +
`; + } + _patchMarker(markerEl, marker) { + var _a, _b, _c, _d, _e; + if (!markerEl || !marker) return; + if (!marker.visible) this._clearMarkerHover(markerEl); + const defaultShape = marker.type === "generic" ? "circle" : marker.type === "peak" || marker.type === "floor" ? "triangle" : "diamond"; + const shape = normalizeMarkerShape(marker.shape, defaultShape); + this._setDatasetIfChanged(markerEl, "shape", shape); + this._setDatasetIfChanged(markerEl, "lane", (_a = marker.lane) != null ? _a : marker.type === "peak" ? "above" : "below"); + this._setDatasetIfChanged(markerEl, "direction", (_b = marker.direction) != null ? _b : "inward"); + if (marker.type === "generic") { + this._setDatasetIfChanged(markerEl, "showMarker", marker.showMarker === false ? "false" : "true"); + } + const shapeSvg = (_c = markerEl.querySelector) == null ? void 0 : _c.call(markerEl, ".marker-shape-svg"); + if (shapeSvg) { + this._setDatasetIfChanged(shapeSvg, "shape", shape); + this._setDatasetIfChanged(shapeSvg, "lane", (_d = marker.lane) != null ? _d : marker.type === "peak" ? "above" : "below"); + this._setDatasetIfChanged(shapeSvg, "direction", (_e = marker.direction) != null ? _e : "inward"); + } + this._setStyleIfChanged(markerEl, "display", marker.visible ? "" : "none"); + if (marker.visible && Number.isFinite(marker.position)) { + this._setStyleIfChanged(markerEl, "left", `${marker.position}%`); + } + const markerColor = this._getEffectiveMarkerColor(marker); + this._setStyleIfChanged(markerEl, "--marker-color", markerColor); + this._setStyleIfChanged(markerEl, "--marker-contrast-color", this._getMarkerContrastColor(markerColor)); + } + _patchMarkerLabelAppearance(labelEl, marker) { + if (!labelEl || !marker) return; + const markerColor = this._getEffectiveMarkerColor(marker); + this._setStyleIfChanged(labelEl, "--marker-color", markerColor); + this._setStyleIfChanged(labelEl, "--marker-contrast-color", this._getMarkerContrastColor(markerColor)); + } + _buildRowViewModel(entityCfg, ecfg, stateObj) { + var _a; + const rowViewModel = buildRowViewModel({ hass: this._hass, cardConfig: this._config, entityConfig: ecfg, entityState: stateObj, - peaks: this._peaks - }) : null; + extrema: (_a = this._extrema.get(entityCfg)) != null ? _a : null, + previousScale: this._rowScales.get(entityCfg) + }); + this._rowScales.set(entityCfg, { min: rowViewModel.min, max: rowViewModel.max }); + return rowViewModel; + } + _buildRow(entityCfg, stateDisplay, unit, pct, color, peakPct, peakDisplay, targetPct, targetDisplay, peakColor, targetColor, minValue, maxValue, rowIndex = ((_b) => (_b = ((_a) => (_a = this._config.entities) == null ? void 0 : _a.indexOf(entityCfg))()) != null ? _b : -1)()) { + var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E; + const ecfg = this._resolve(entityCfg); + const stateObj = (_c = (_b2 = (_a2 = this._hass) == null ? void 0 : _a2.states) == null ? void 0 : _b2[entityCfg.entity]) != null ? _c : null; + if (stateObj) this._updateExtrema(entityCfg, ecfg, stateObj); + const rowViewModel = stateObj ? this._buildRowViewModel(entityCfg, ecfg, stateObj) : null; const layout = ecfg.layout; const bar = ecfg.bar; - const targetMarkerCfg = ecfg.target_marker; - const peakMarkerCfg = ecfg.peak_marker; const safeMin = Number.isFinite(minValue) ? minValue : 0; const safeMax = Number.isFinite(maxValue) ? maxValue : 100; const baselinePct = (_d = rowViewModel == null ? void 0 : rowViewModel.baselinePercent) != null ? _d : this._resolveBaselinePct(ecfg, safeMin, safeMax); @@ -3695,31 +5361,45 @@ const name = (_j = (_i = (_g = rowViewModel == null ? void 0 : rowViewModel.name) != null ? _g : ecfg.name) != null ? _i : (_h = stateObj == null ? void 0 : stateObj.attributes) == null ? void 0 : _h.friendly_name) != null ? _j : entityCfg.entity; const escapedEntityId = escapeHtml((_k = rowViewModel == null ? void 0 : rowViewModel.entityId) != null ? _k : entityCfg.entity); const escapedName = escapeHtml(name); - const targetEnabled = (targetMarkerCfg == null ? void 0 : targetMarkerCfg.enabled) !== false; - const peakMarkerColor = peakColor || "#888"; - const targetMarkerColor = targetColor || "#888"; - const peakContrastColor = this._getMarkerContrastColor(peakMarkerColor); - const targetContrastColor = this._getMarkerContrastColor(targetMarkerColor); - const rawValue = (_l = rowViewModel == null ? void 0 : rowViewModel.numericValue) != null ? _l : this._getFiniteNumber(stateDisplay); - const needleState = (_m = rowViewModel == null ? void 0 : rowViewModel.needle) != null ? _m : this._getNeedleRenderState(rawValue, ecfg, safeMin, safeMax, baselinePct); + const markerModels = this._getRowMarkerModels(rowViewModel, ecfg, peakPct, peakDisplay, targetPct, targetDisplay, peakColor, targetColor); + const targetMarkerModel = this._getMarkerModel(markerModels, "target"); + const peakMarkerModel = this._getMarkerModel(markerModels, "peak"); + const floorMarkerModel = this._getMarkerModel(markerModels, "floor"); + const genericMarkerModels = markerModels.filter((marker) => marker.type === "generic"); + const markerLaneOccupancy = (_l = rowViewModel == null ? void 0 : rowViewModel.markerLaneOccupancy) != null ? _l : getMarkerLaneOccupancy(ecfg); + const markerLabelLaneOccupancy = (_m = rowViewModel == null ? void 0 : rowViewModel.markerLabelLaneOccupancy) != null ? _m : getMarkerLabelLaneOccupancy(ecfg); + const rawValue = (_n = rowViewModel == null ? void 0 : rowViewModel.numericValue) != null ? _n : this._getFiniteNumber(stateDisplay); + const needleState = (_o = rowViewModel == null ? void 0 : rowViewModel.needle) != null ? _o : this._getNeedleRenderState(rawValue, ecfg, safeMin, safeMax, baselinePct); const fillState = this._getFillRenderState(pct, "var(--sbcp-row-height)", ecfg, color, targetPct, baselinePct, safeMin, safeMax, needleState.show); - const peakMarker = peakMarkerCfg.show && peakPct !== null ? ` -
-
-
+ const baselineAt = (_p = ecfg.baseline) == null ? void 0 : _p.at; + const baselineConfigured = ((_q = ecfg.baseline) == null ? void 0 : _q.enabled) !== false && (Number.isFinite(baselinePct) || Boolean(baselineAt == null ? void 0 : baselineAt.entity) || (baselineAt == null ? void 0 : baselineAt.fixed) !== null && (baselineAt == null ? void 0 : baselineAt.fixed) !== void 0 || Number.isFinite(baselineAt == null ? void 0 : baselineAt.percent)); + const baselineIndicator = baselineConfigured ? `` : ""; + const peakMarker = this._renderMarker(peakMarkerModel); + const targetMarker = this._renderMarker(targetMarkerModel); + const floorMarker = this._renderMarker(floorMarkerModel); + const targetValueLabel = (targetMarkerModel == null ? void 0 : targetMarkerModel.labelVisible) ? ` +
+ ${((_s = targetMarkerModel.label) == null ? void 0 : _s.text) ? escapeHtml(targetMarkerModel.label.text) : ""}
` : ""; - const targetMarker = ` -
-
-
+ const peakValueLabel = (peakMarkerModel == null ? void 0 : peakMarkerModel.labelVisible) ? ` +
+ ${peakMarkerModel.visible && ((_u = peakMarkerModel.label) == null ? void 0 : _u.text) ? escapeHtml(peakMarkerModel.label.text) : ""} +
` : ""; + const floorValueLabel = (floorMarkerModel == null ? void 0 : floorMarkerModel.labelVisible) ? ` +
+ ${floorMarkerModel.visible && ((_w = floorMarkerModel.label) == null ? void 0 : _w.text) ? escapeHtml(floorMarkerModel.label.text) : ""} +
` : ""; + const genericMarkers = genericMarkerModels.map((marker) => this._renderMarker(marker)).join(""); + const genericValueLabels = genericMarkerModels.filter((marker) => marker.labelVisible).map((marker) => { + var _a3, _b3; + return ` +
+ ${marker.visible && ((_b3 = marker.label) == null ? void 0 : _b3.text) ? escapeHtml(marker.label.text) : ""}
`; - const targetValueLabel = targetEnabled && targetMarkerCfg.show_label ? ` -
- ${targetDisplay !== null ? escapeHtml(targetDisplay) : ""} -
` : ""; - const needleMarker = ((_o = (_n = ecfg.bar) == null ? void 0 : _n.needle) == null ? void 0 : _o.show) && !Number.isFinite(baselinePct) ? ` + }).join(""); + const needleMarker = ((_y = (_x = ecfg.bar) == null ? void 0 : _x.needle) == null ? void 0 : _y.show) && !Number.isFinite(baselinePct) ? `
-
+
` : ""; const paintLayers = fillState.paintLayers.map((layer) => `
`).join(""); @@ -3731,7 +5411,7 @@ ${this._formatAboveValueMarkup(stateDisplay, unit, false)}
` : ""; - const heroSize = (_q = layout.hero.size) != null ? _q : "small"; + const heroSize = (_A = layout.hero.size) != null ? _A : "small"; const heroFontSize = layout.hero.value_size; const heroHeader = lp === "hero" ? `
@@ -3751,12 +5431,12 @@ const escapedIcon = ecfg.icon && ecfg.icon !== false ? escapeHtml(ecfg.icon) : ""; const mainIcon = escapedIcon && lp !== "hero" ? `
` : ""; return ` -
+
${aboveLabel} ${heroHeader} ${topRightValue} -
+
${mainIcon} ${leftLabel}
@@ -3764,47 +5444,67 @@
${paintLayers}
+ ${baselineIndicator} ${innerLabel} ${peakMarker} ${targetMarker} + ${floorMarker} + ${genericMarkers} ${needleMarker}
+ ${peakValueLabel} ${targetValueLabel} + ${floorValueLabel} + ${genericValueLabels}
${rightValue}
`; } - _patchRow(row, entityCfg, stateObj) { - var _a; + _patchRow(row, entityCfg, stateObj, previousHass = null) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; if (!row || !stateObj) return; const ecfg = this._resolve(entityCfg); - const rowViewModel = buildRowViewModel({ - hass: this._hass, - cardConfig: this._config, - entityConfig: ecfg, - entityState: stateObj, - peaks: this._peaks - }); - const rawVal = rowViewModel.numericValue; + this._updateExtrema(entityCfg, ecfg, stateObj); + const previousScale = this._rowScales.get(entityCfg); + const rowViewModel = this._buildRowViewModel(entityCfg, ecfg, stateObj); const safeMin = rowViewModel.min; const safeMax = rowViewModel.max; - const targetVal = rowViewModel.target; const pct = rowViewModel.percent; const color = this._getColor(pct, ecfg, safeMin, safeMax); - const display = rowViewModel.displayValue; - const displayUnit = rowViewModel.displayUnit; + const display = rowViewModel.primaryPresentation.number; + const displayUnit = rowViewModel.primaryPresentation.unit; const fillReveal = row.querySelector(".bar-fill-reveal"); + const baselineIndicator = row.querySelector(".baseline-indicator"); const paintLayer = row.querySelector('.bar-paint-layer[data-layer="base"]'); const liveTargetPct = rowViewModel.targetPercent; const liveBaselinePct = rowViewModel.baselinePercent; const needleState = rowViewModel.needle; const fillState = this._getFillRenderState(pct, "var(--sbcp-row-height)", ecfg, color, liveTargetPct, liveBaselinePct, safeMin, safeMax, needleState.show); + const previousStateObj = (_b = (_a = previousHass == null ? void 0 : previousHass.states) == null ? void 0 : _a[entityCfg.entity]) != null ? _b : null; + const previousViewModel = previousStateObj ? buildRowViewModel({ + hass: previousHass, + cardConfig: this._config, + entityConfig: ecfg, + entityState: previousStateObj, + extrema: (_c = this._extrema.get(entityCfg)) != null ? _c : null, + previousScale + }) : null; + const revealDuration = this._getRevealTransitionDuration( + previousViewModel && Number.isFinite(previousViewModel.numericValue) ? { valuePercent: previousViewModel.percent, baselinePercent: previousViewModel.baselinePercent } : null, + Number.isFinite(rowViewModel.numericValue) ? { valuePercent: pct, baselinePercent: liveBaselinePct } : null + ); if (fillReveal) { - this._setStyleTextIfChanged(fillReveal, fillState.revealStyle); + this._setStyleTextIfChanged(fillReveal, `${fillState.revealStyle};--sbcp-reveal-duration:${revealDuration}ms`); this._setClassNameIfChanged(fillReveal, `bar-fill-reveal${ecfg.bar.animated ? "" : " no-anim"}`); } + if (baselineIndicator) { + this._setStyleIfChanged(baselineIndicator, "display", Number.isFinite(liveBaselinePct) ? "block" : "none"); + if (Number.isFinite(liveBaselinePct)) { + this._setStyleIfChanged(baselineIndicator, "left", `${liveBaselinePct}%`); + } + } if (paintLayer) { const baseLayerState = fillState.paintLayers.find((layer) => layer.id === "base"); if (baseLayerState) { @@ -3821,7 +5521,7 @@ ${paintLayers} const needleEl = row.querySelector(".needle-marker"); if (needleEl) { this._setStyleIfChanged(needleEl, "display", needleState.show ? "block" : "none"); - this._setStyleIfChanged(needleEl, "left", `${(_a = needleState.pct) != null ? _a : 0}%`); + this._setStyleIfChanged(needleEl, "left", `${(_d = needleState.pct) != null ? _d : 0}%`); this._setStyleIfChanged(needleEl, "--needle-color", needleState.color); this._setStyleIfChanged(needleEl, "--needle-border-color", needleState.borderColor); this._setDatasetIfChanged(needleEl, "edge", needleState.edge); @@ -3868,88 +5568,96 @@ ${paintLayers} if (aboveLabel) { aboveLabel.innerHTML = `${escapeHtml(rowViewModel.name)}${this._formatAboveValueMarkup(display, displayUnit, false)}`; } - if (ecfg.peak_marker.show && Number.isFinite(rawVal)) { - const key = entityCfg.entity; - if (this._peaks[key] === void 0 || rawVal > this._peaks[key]) { - this._peaks[key] = rawVal; - } - const peakVal = this._peaks[key]; - const peakPct = this._toScalePct(peakVal, safeMin, safeMax); - const peakEl = row.querySelector(".peak-marker"); - if (peakEl) { - if (Number.isFinite(peakPct)) { - this._setStyleIfChanged(peakEl, "left", `${peakPct}%`); - } - this._setStyleIfChanged(peakEl, "--marker-color", ecfg.peak_marker.color); - this._setStyleIfChanged(peakEl, "--marker-contrast-color", this._getMarkerContrastColor(ecfg.peak_marker.color)); - } - } const targetEl = row.querySelector(".target-marker"); const targetLabelEl = row.querySelector(".target-value-label"); - if (targetVal !== null) { - const targetPct = rowViewModel.targetPercent; - if (targetEl) { - this._setStyleIfChanged(targetEl, "display", ""); - this._setStyleIfChanged(targetEl, "left", `${targetPct}%`); - this._setStyleIfChanged(targetEl, "--marker-color", ecfg.target_marker.color); - this._setStyleIfChanged(targetEl, "--marker-contrast-color", this._getMarkerContrastColor(ecfg.target_marker.color)); + const peakLabelEl = row.querySelector(".peak-value-label"); + const floorLabelEl = row.querySelector(".floor-value-label"); + const markerModels = (_e = rowViewModel.markers) != null ? _e : []; + this._patchMarker(targetEl, this._getMarkerModel(markerModels, "target")); + this._patchMarker(row.querySelector(".peak-marker"), this._getMarkerModel(markerModels, "peak")); + this._patchMarker(row.querySelector(".floor-marker"), this._getMarkerModel(markerModels, "floor")); + ((_g = (_f = row.querySelectorAll) == null ? void 0 : _f.call(row, ".generic-marker[data-marker-id]")) != null ? _g : []).forEach((markerEl) => { + var _a2, _b2, _c2, _d2, _e2; + const markerId = markerEl.dataset.markerId; + const marker = this._getMarkerModel(markerModels, markerId); + this._patchMarker(markerEl, marker); + const labelEl = [...(_b2 = (_a2 = row.querySelectorAll) == null ? void 0 : _a2.call(row, ".generic-value-label[data-marker-id]")) != null ? _b2 : []].find((label) => label.dataset.markerId === markerId); + if (!labelEl) return; + this._setDatasetIfChanged(labelEl, "showMarker", (marker == null ? void 0 : marker.showMarker) === false ? "false" : "true"); + this._patchMarkerLabelAppearance(labelEl, marker); + if ((marker == null ? void 0 : marker.labelVisible) && marker.visible && ((_c2 = marker.label) == null ? void 0 : _c2.text)) { + this._setTextIfChanged(labelEl, (_e2 = (_d2 = marker.label) == null ? void 0 : _d2.text) != null ? _e2 : null); + this._setStyleIfChanged(labelEl, "visibility", "visible"); + this._setStyleIfChanged(labelEl, "left", `${Number.isFinite(marker.position) ? marker.position : 0}%`); + } else { + this._setStyleIfChanged(labelEl, "visibility", "hidden"); } - if (targetLabelEl) { - this._setTextIfChanged(targetLabelEl, rowViewModel.targetDisplay); + }); + const targetMarkerModel = this._getMarkerModel(markerModels, "target"); + this._patchMarkerLabelAppearance(targetLabelEl, targetMarkerModel); + if (targetLabelEl) { + if ((targetMarkerModel == null ? void 0 : targetMarkerModel.labelVisible) && targetMarkerModel.visible && ((_h = targetMarkerModel.label) == null ? void 0 : _h.text)) { + this._setTextIfChanged(targetLabelEl, (_j = (_i = targetMarkerModel.label) == null ? void 0 : _i.text) != null ? _j : null); + } else { + this._setStyleIfChanged(targetLabelEl, "visibility", "hidden"); } - } else { - if (targetEl) this._setStyleIfChanged(targetEl, "display", "none"); - if (targetLabelEl) this._setStyleIfChanged(targetLabelEl, "visibility", "hidden"); } + const patchValueLabel = (labelEl, markerType) => { + var _a2, _b2, _c2; + if (!labelEl) return; + const marker = this._getMarkerModel(markerModels, markerType); + this._patchMarkerLabelAppearance(labelEl, marker); + if ((marker == null ? void 0 : marker.labelVisible) && marker.visible && ((_a2 = marker.label) == null ? void 0 : _a2.text)) { + this._setTextIfChanged(labelEl, (_c2 = (_b2 = marker.label) == null ? void 0 : _b2.text) != null ? _c2 : null); + this._setStyleIfChanged(labelEl, "visibility", "visible"); + this._setStyleIfChanged(labelEl, "left", `${Number.isFinite(marker.position) ? marker.position : 0}%`); + } else { + this._setStyleIfChanged(labelEl, "visibility", "hidden"); + } + }; + patchValueLabel(peakLabelEl, "peak"); + patchValueLabel(floorLabelEl, "floor"); } - _update() { + _update(previousHass = null) { + var _a, _b, _c, _d; if (!this._hass || !this._config) return; const rowsEl = this.shadowRoot.querySelector(".rows"); if (!rowsEl) return; const entities = this._config.entities; - if (!this._rendered) { + const presence = entities.map((entityCfg) => !!this._hass.states[entityCfg.entity]); + const presenceChanged = presence.some((present, index) => present !== this._rowPresence[index]); + if (!this._rendered || presenceChanged) { + this._rowGeneration += 1; + this._rowPresence = presence; let html = ""; for (let entityIndex = 0; entityIndex < entities.length; entityIndex++) { const entityCfg = entities[entityIndex]; const stateObj = this._hass.states[entityCfg.entity]; if (!stateObj) { - html += `
Entity not found: ${escapeHtml(entityCfg.entity)}
`; + html += `
Entity not found: ${escapeHtml(entityCfg.entity)}
`; continue; } const ecfg = this._resolve(entityCfg); - const rowViewModel = buildRowViewModel({ - hass: this._hass, - cardConfig: this._config, - entityConfig: ecfg, - entityState: stateObj, - peaks: this._peaks - }); - const rawVal = rowViewModel.numericValue; + this._updateExtrema(entityCfg, ecfg, stateObj); + const rowViewModel = this._buildRowViewModel(entityCfg, ecfg, stateObj); const safeMin = rowViewModel.min; const safeMax = rowViewModel.max; - const targetVal = rowViewModel.target; const pct = rowViewModel.percent; const color = this._getColor(pct, ecfg, safeMin, safeMax); - const display = rowViewModel.displayValue; - const displayUnit = rowViewModel.displayUnit; + const display = rowViewModel.primaryPresentation.number; + const displayUnit = rowViewModel.primaryPresentation.unit; const targetPct = rowViewModel.targetPercent; - const targetDisplay = rowViewModel.targetDisplay; - let peakPct = null, peakDisplay = null; - if (ecfg.peak_marker.show && Number.isFinite(rawVal)) { - if (this._peaks[entityCfg.entity] === void 0 || rawVal > this._peaks[entityCfg.entity]) { - this._peaks[entityCfg.entity] = rawVal; - } - const peakVal = this._peaks[entityCfg.entity]; - peakPct = this._toScalePct(peakVal, safeMin, safeMax); - peakDisplay = this._formatNumericDisplay(peakVal, ecfg.formatting.decimal); - } - html += this._buildRow(entityCfg, display, displayUnit, pct, color, peakPct, peakDisplay, targetPct, targetDisplay, ecfg.peak_marker.color, ecfg.target_marker.color, safeMin, safeMax); + const targetDisplay = (_b = (_a = rowViewModel.targetPresentation) == null ? void 0 : _a.text) != null ? _b : null; + const peakPct = rowViewModel.peakPercent; + const peakDisplay = (_d = (_c = rowViewModel.peakPresentation) == null ? void 0 : _c.number) != null ? _d : null; + html += this._buildRow(entityCfg, display, displayUnit, pct, color, peakPct, peakDisplay, targetPct, targetDisplay, ecfg.peak_marker.color, ecfg.target_marker.color, safeMin, safeMax, entityIndex); } + this._clearMarkerHover(); rowsEl.innerHTML = html; this._rendered = true; const builtRows = rowsEl.querySelectorAll(".row[data-entity]"); - builtRows.forEach((row, idx) => { - const entityCfg = entities[idx]; + builtRows.forEach((row) => { + const entityCfg = entities[Number(row.dataset.rowIndex)]; const stateObj = entityCfg ? this._hass.states[entityCfg.entity] : null; if (entityCfg && stateObj) { this._patchRow(row, entityCfg, stateObj); @@ -3966,20 +5674,12 @@ ${paintLayers} return; } const rows = rowsEl.querySelectorAll(".row[data-entity]"); - let rowIdx = 0; - for (const entityCfg of entities) { + for (const row of rows) { + const entityCfg = entities[Number(row.dataset.rowIndex)]; + if (!entityCfg) continue; const stateObj = this._hass.states[entityCfg.entity]; - if (!stateObj) { - rowIdx++; - continue; - } - const row = rows[rowIdx]; - if (!row) { - rowIdx++; - continue; - } - this._patchRow(row, entityCfg, stateObj); - rowIdx++; + if (!stateObj) continue; + this._patchRow(row, entityCfg, stateObj, previousHass); } this._runPostLayoutPasses(rows); } @@ -3991,6 +5691,8 @@ ${paintLayers} var SensorBarCardPlusEditor; var init_SensorBarCardPlusEditor = __esm({ "src/editor/SensorBarCardPlusEditor.js"() { + init_normalize(); + init_resolve(); SensorBarCardPlusEditor = class extends HTMLElement { constructor() { super(); @@ -4006,6 +5708,9 @@ ${paintLayers} this._expandedEntityOverrides = /* @__PURE__ */ new Set(); this._expandedOverrideGroups = /* @__PURE__ */ new Set(); this._expandedCardGroups = /* @__PURE__ */ new Set(); + this._genericMarkerUiIds = /* @__PURE__ */ new Map(); + this._expandedGenericMarkerUiIds = /* @__PURE__ */ new Set(); + this._nextGenericMarkerUiId = 0; this._gradientStopsDrafts = /* @__PURE__ */ new Map(); this._gradientStopsUiRows = /* @__PURE__ */ new Map(); this._gradientStopPosTexts = /* @__PURE__ */ new Map(); @@ -4035,7 +5740,16 @@ ${paintLayers} this._config = nextConfig; return; } + if (nextConfigJson === this._lastEmittedConfigJson) { + this._config = nextConfig; + return; + } + if (this._getEmittedConfigJson(nextConfig) === this._getEmittedConfigJson(this._draftConfig)) { + this._config = nextConfig; + return; + } const shouldRender = !((_a = this.shadowRoot) == null ? void 0 : _a.innerHTML) || nextConfigJson !== this._lastRenderedConfigJson; + this._lastEmittedConfigJson = null; this._config = nextConfig; this._draftConfig = this._cloneDeep(nextConfig); this._gradientStopsDrafts = /* @__PURE__ */ new Map(); @@ -4047,6 +5761,8 @@ ${paintLayers} this._segmentBoundaryTexts = /* @__PURE__ */ new Map(); this._targetAboveFillDrafts = /* @__PURE__ */ new Map(); this._baselineColorDrafts = /* @__PURE__ */ new Map(); + this._genericMarkerUiIds.clear(); + this._expandedGenericMarkerUiIds.clear(); if (shouldRender) { this._render(); } @@ -4091,6 +5807,9 @@ ${paintLayers} }; return JSON.stringify(normalize(value != null ? value : null)); } + _getEmittedConfigJson(config) { + return this._serializeConfig(config); + } _isObject(value) { return !!value && typeof value === "object" && !Array.isArray(value); } @@ -4350,6 +6069,11 @@ ${paintLayers} this._refreshEntityDerivedUi(); } _refreshCardDerivedUi() { + this._setElementText("card-group-marker-target-summary", this._getCardTargetMarkerSummary()); + this._setElementText("card-group-marker-peak-summary", this._getMarkerResetSummary("peak")); + this._setElementText("card-group-marker-floor-summary", this._getMarkerResetSummary("floor")); + this._setElementText("card-group-baseline-summary", this._getCardBaselineSummary()); + this._setElementText("card-group-generic-markers-summary", this._getGenericMarkersSummary({ type: "card" })); this._setElementText("card-group-segments-summary", this._getSegmentsSummary({ type: "card" })); this._setElementText("card-group-gradient-stops-summary", this._getGradientStopsSummary({ type: "card" })); this._setElementChecked("target-above-fill-enabled", this._isTargetAboveFillEnabled({ type: "card" })); @@ -4366,6 +6090,8 @@ ${paintLayers} this._setElementChecked(`entity-${index}-baseline-inherit`, !this._hasBaselineOverride(scope)); this._setElementChecked(`entity-${index}-needle-inherit`, !this._hasNeedleOverride(scope)); this._setElementChecked(`entity-${index}-peak-inherit`, !this._hasPeakOverride(scope)); + this._setElementChecked(`entity-${index}-floor-inherit`, !this._hasExtremumOverride(scope, "floor")); + this._setElementChecked(`entity-${index}-markers-inherit`, !this._hasMarkersOverride(scope)); this._setElementChecked(`entity-${index}-bar-inherit`, !this._hasEntityBarAppearanceOverride(scope)); this._setElementChecked(`entity-${index}-segments-inherit`, !this._hasSegmentsOverride(scope)); this._setElementChecked(`entity-${index}-gradient-stops-inherit`, !this._hasGradientStopsOverride(scope)); @@ -4379,6 +6105,8 @@ ${paintLayers} this._setElementText(`entity-${index}-group-baseline-summary`, this._getBaselineOverrideSummary(scope)); this._setElementText(`entity-${index}-group-needle-summary`, this._getNeedleSummary(scope)); this._setElementText(`entity-${index}-group-peak-summary`, this._getPeakSummary(scope)); + this._setElementText(`entity-${index}-group-floor-summary`, this._getFloorSummary(scope)); + this._setElementText(`entity-${index}-group-markers-summary`, this._getGenericMarkersSummary(scope)); this._setElementText(`entity-${index}-group-bar-summary`, this._getBarAppearanceSummary(scope)); this._setElementText(`entity-${index}-group-segments-summary`, this._getSegmentsSummary(scope)); this._setElementText(`entity-${index}-group-gradient-stops-summary`, this._getGradientStopsSummary(scope)); @@ -4502,23 +6230,11 @@ ${paintLayers} return nextTarget; } _cleanupResolvableValueForEmit(value) { - const nextValue = {}; - if (this._isObject(value)) { - const fixed = this._normalizeNumberValue(value.fixed); - const entity = this._normalizeTextValue(value.entity).trim(); - if (fixed !== null) { - nextValue.fixed = fixed; - } - if (entity) { - nextValue.entity = entity; - } - } else { - const fixed = this._normalizeNumberValue(value); - if (fixed !== null) { - nextValue.fixed = fixed; - } + if (this._isObject(value) || value === null || typeof value === "string" && this._normalizeNumberValue(value) === null) { + return this._cloneDeep(value); } - return Object.keys(nextValue).length ? nextValue : null; + const fixed = this._normalizeNumberValue(value); + return fixed === null ? null : { fixed }; } _cleanupScaleForEmit(target) { if (!this._isObject(target) || !this._isObject(target.scale)) { @@ -4531,7 +6247,7 @@ ${paintLayers} return; } const cleanedValue = this._cleanupResolvableValueForEmit(nextScale[key]); - if (cleanedValue) { + if (cleanedValue || nextScale[key] === null) { nextScale[key] = cleanedValue; delete nextTarget[key]; delete nextTarget[`${key}_entity`]; @@ -4629,8 +6345,8 @@ ${paintLayers} } return nextTarget; } - _cleanupTargetForEmit(target) { - var _a, _b; + _cleanupTargetForEmit(target, scope = { type: "card" }) { + var _a; if (!this._isObject(target) || !this._isObject(target.target)) { return target; } @@ -4638,12 +6354,15 @@ ${paintLayers} const nextMarker = this._cloneDeep(nextTarget.target); const cleanedAt = this._cleanupResolvableValueForEmit(nextMarker.at); const color = this._normalizeTextValue(nextMarker.color).trim(); - const labelShow = ((_a = nextMarker.label) == null ? void 0 : _a.show) === true; - const fillColor = this._normalizeTextValue((_b = nextMarker.when_exceeded) == null ? void 0 : _b.fill_color).trim(); + const label = this._cleanBuiltinMarkerLabelForEmit(nextMarker.label, scope, "target"); + const fillColor = this._normalizeTextValue((_a = nextMarker.when_exceeded) == null ? void 0 : _a.fill_color).trim(); + const hasShape = Object.prototype.hasOwnProperty.call(nextMarker, "shape"); + const shape = hasShape ? normalizeTargetMarkerShape(nextMarker.shape) : null; + const direction = Object.prototype.hasOwnProperty.call(nextMarker, "direction") ? normalizeMarkerDirection(nextMarker.direction) : null; if (typeof nextMarker.enabled !== "boolean") { delete nextMarker.enabled; } - if (cleanedAt) { + if (cleanedAt || nextMarker.at === null) { nextMarker.at = cleanedAt; delete nextTarget.target_entity; } else { @@ -4655,11 +6374,12 @@ ${paintLayers} } else { delete nextMarker.color; } - if (labelShow) { - nextMarker.label = { ...this._isObject(nextMarker.label) ? nextMarker.label : {}, show: true }; - delete nextTarget.show_target_label; + if (Object.keys(label).length) { + nextMarker.label = label; + if (label.show === true) delete nextTarget.show_target_label; } else { delete nextMarker.label; + delete nextTarget.show_target_label; } if (fillColor) { nextMarker.when_exceeded = { @@ -4670,6 +6390,18 @@ ${paintLayers} } else { delete nextMarker.when_exceeded; } + if ((scope == null ? void 0 : scope.type) === "card" && shape === "diamond") { + delete nextMarker.shape; + } else if (shape) { + nextMarker.shape = shape; + } else { + delete nextMarker.shape; + } + if (direction === "inward" && ((scope == null ? void 0 : scope.type) !== "entity" || direction === this._getEffectiveMarkerDirection({ type: "card" }, "target"))) { + delete nextMarker.direction; + } else if (direction) { + nextMarker.direction = direction; + } if (Object.keys(nextMarker).length) { nextTarget.target = nextMarker; } else { @@ -4687,7 +6419,7 @@ ${paintLayers} if (typeof nextBaseline.enabled !== "boolean") { delete nextBaseline.enabled; } - if (cleanedAt) { + if (cleanedAt || nextBaseline.at === null) { nextBaseline.at = cleanedAt; } else { delete nextBaseline.at; @@ -4711,22 +6443,33 @@ ${paintLayers} } return nextTarget; } - _cleanupPeakForEmit(target) { + _cleanupPeakForEmit(target, scope = { type: "card" }) { if (!this._isObject(target) || !this._isObject(target.peak)) { return target; } const nextTarget = this._cloneDeep(target); const nextPeak = this._cloneDeep(nextTarget.peak); const color = this._normalizeTextValue(nextPeak.color).trim(); + const direction = Object.prototype.hasOwnProperty.call(nextPeak, "direction") ? normalizeMarkerDirection(nextPeak.direction) : null; if (typeof nextPeak.enabled !== "boolean") { delete nextPeak.enabled; } + if ((scope == null ? void 0 : scope.type) !== "entity" && nextPeak.reset === "never") { + delete nextPeak.reset; + } + if (direction === "inward" && ((scope == null ? void 0 : scope.type) !== "entity" || direction === this._getEffectiveMarkerDirection({ type: "card" }, "peak"))) { + delete nextPeak.direction; + } else if (direction) { + nextPeak.direction = direction; + } if (color && this._normalizeColorComparisonValue(color) !== this._normalizeColorComparisonValue("#888")) { nextPeak.color = color; delete nextTarget.peak_color; } else { delete nextPeak.color; } + nextPeak.label = this._cleanBuiltinMarkerLabelForEmit(nextPeak.label, scope, "peak"); + if (!Object.keys(nextPeak.label).length) delete nextPeak.label; if (Object.keys(nextPeak).length) { nextTarget.peak = nextPeak; } else { @@ -4734,18 +6477,92 @@ ${paintLayers} } return nextTarget; } - _cleanupBarForEmit(target) { + _cleanupFloorForEmit(target, scope = { type: "card" }) { + if (!this._isObject(target) || !this._isObject(target.floor)) { + return target; + } + const nextTarget = this._cloneDeep(target); + const nextFloor = this._cloneDeep(nextTarget.floor); + const color = this._normalizeTextValue(nextFloor.color).trim(); + const direction = Object.prototype.hasOwnProperty.call(nextFloor, "direction") ? normalizeMarkerDirection(nextFloor.direction) : null; + if (typeof nextFloor.enabled !== "boolean") delete nextFloor.enabled; + if (color && this._normalizeColorComparisonValue(color) !== this._normalizeColorComparisonValue("#888888")) { + nextFloor.color = color; + } else { + delete nextFloor.color; + } + if (this._isObject(nextFloor.label)) { + nextFloor.label = this._cleanBuiltinMarkerLabelForEmit(nextFloor.label, scope, "floor"); + if (!Object.keys(nextFloor.label).length) delete nextFloor.label; + } + if ((scope == null ? void 0 : scope.type) !== "entity" && nextFloor.reset === "never" || nextFloor.reset === void 0 || nextFloor.reset === null || nextFloor.reset === "") { + delete nextFloor.reset; + } + if (direction === "inward" && ((scope == null ? void 0 : scope.type) !== "entity" || direction === this._getEffectiveMarkerDirection({ type: "card" }, "floor"))) { + delete nextFloor.direction; + } else if (direction) { + nextFloor.direction = direction; + } + if (Object.keys(nextFloor).length) nextTarget.floor = nextFloor; + else delete nextTarget.floor; + return nextTarget; + } + _cleanBuiltinMarkerLabelForEmit(rawLabel, scope, key) { + var _a, _b, _c; + const label = this._isObject(rawLabel) ? this._cloneDeep(rawLabel) : {}; + const cardLabel = (scope == null ? void 0 : scope.type) === "entity" ? (_a = this._getScopedValue({ type: "card" }, [key, "label"])) != null ? _a : {} : {}; + if (label.show === false && (scope == null ? void 0 : scope.type) !== "entity") delete label.show; + if (label.show === false && (scope == null ? void 0 : scope.type) === "entity" && cardLabel.show !== true) delete label.show; + if (label.show_value === true && ((scope == null ? void 0 : scope.type) !== "entity" || cardLabel.show_value !== false)) delete label.show_value; + if (label.show_unit === true && ((scope == null ? void 0 : scope.type) !== "entity" || cardLabel.show_unit !== false)) delete label.show_unit; + if (typeof label.text === "string") { + label.text = label.text.replace(/\s+/g, " ").trim(); + if (!label.text && (scope == null ? void 0 : scope.type) !== "entity") delete label.text; + } + const precision = this._normalizeDecimalValue((_b = label.precision) != null ? _b : label.decimal); + delete label.decimal; + if (precision === null) delete label.precision; + else if ((scope == null ? void 0 : scope.type) === "entity" && precision === ((_c = cardLabel.precision) != null ? _c : cardLabel.decimal)) delete label.precision; + else label.precision = precision; + return label; + } + _cleanupBarForEmit(target, scope = { type: "card" }, cardConfig = null, inheritedSegmentSpace = null) { if (!this._isObject(target) || !this._isObject(target.bar)) { return target; } const nextTarget = this._cloneDeep(target); const nextBar = this._cloneDeep(nextTarget.bar); + const scopedSegmentSpace = ["percent", "scale"].includes(nextBar.segment_space) ? nextBar.segment_space : (scope == null ? void 0 : scope.type) === "entity" && ["percent", "scale"].includes(inheritedSegmentSpace) ? inheritedSegmentSpace : null; + const legacySegmentSpace = scopedSegmentSpace; + if (legacySegmentSpace === "percent" && Array.isArray(nextBar.segments)) { + const asLegacyPercent = (boundary) => { + if (typeof boundary === "number" && Number.isFinite(boundary)) return `${boundary}%`; + if (typeof boundary === "string" && boundary.trim() !== "" && !boundary.includes("%") && Number.isFinite(Number(boundary.trim()))) { + return `${Number(boundary.trim())}%`; + } + return boundary; + }; + nextBar.segments = nextBar.segments.map((segment) => ({ + ...segment, + from: asLegacyPercent(segment == null ? void 0 : segment.from), + ...Object.prototype.hasOwnProperty.call(segment != null ? segment : {}, "to") ? { to: asLegacyPercent(segment.to) } : {} + })); + } + delete nextBar.segment_space; const fillStyle = this._normalizeTextValue(nextBar.fill_style).trim(); const color = this._normalizeTextValue(nextBar.color).trim(); const segments = Array.isArray(nextBar.segments) ? nextBar.segments : null; const gradientStops = Array.isArray(nextBar.gradient_stops) ? nextBar.gradient_stops : null; - if (fillStyle && fillStyle !== "bands") { - nextBar.fill_style = fillStyle; + if (fillStyle) { + const withoutLocalFillStyle = this._cloneDeep(nextTarget); + delete withoutLocalFillStyle.bar.fill_style; + const inheritedStyle = normalizeBarConfig( + withoutLocalFillStyle, + (scope == null ? void 0 : scope.type) === "entity" ? cardConfig : null, + { isCardScope: (scope == null ? void 0 : scope.type) !== "entity" } + ).fill_style; + if (fillStyle === "bands" && inheritedStyle === "bands") delete nextBar.fill_style; + else nextBar.fill_style = fillStyle; delete nextTarget.color_mode; } else { delete nextBar.fill_style; @@ -4761,14 +6578,14 @@ ${paintLayers} } else { delete nextBar.solid_fill; } - if (segments && segments.length && !this._segmentsEqualForEditor(segments, this._getDefaultSegments())) { + if (segments && (!segments.length || legacySegmentSpace || !this._segmentsEqualForEditor(segments, this._getDefaultSegments()))) { nextBar.segments = segments; delete nextTarget.segments; delete nextTarget.severity; } else { delete nextBar.segments; } - if (gradientStops && gradientStops.length >= 2 && !this._isDefaultGradientStops(gradientStops)) { + if (gradientStops && (!gradientStops.length || gradientStops.length >= 2 && !this._isDefaultGradientStops(gradientStops))) { nextBar.gradient_stops = this._sanitizeGradientStopsForEmit(gradientStops); delete nextTarget.gradient_stops; } else { @@ -4785,9 +6602,9 @@ ${paintLayers} const pathKey = path.join("."); switch (pathKey) { case "": - return ["type", "title", "entities", "scale", "target", "baseline", "peak", "layout", "formatting", "bar"]; + return ["type", "title", "entities", "scale", "target", "baseline", "peak", "floor", "layout", "formatting", "bar"]; case "entities.*": - return ["entity", "name", "icon", "scale", "target", "baseline", "peak", "layout", "formatting", "bar"]; + return ["entity", "name", "icon", "scale", "target", "baseline", "peak", "floor", "layout", "formatting", "bar"]; case "scale": return ["min", "max"]; case "scale.min": @@ -4796,9 +6613,9 @@ ${paintLayers} case "baseline.at": return ["fixed", "entity"]; case "target": - return ["enabled", "at", "color", "label", "when_exceeded"]; + return ["enabled", "at", "shape", "color", "label", "when_exceeded"]; case "target.label": - return ["show"]; + return ["show", "decimal"]; case "target.when_exceeded": return ["fill_color"]; case "baseline": @@ -4807,7 +6624,12 @@ ${paintLayers} case "baseline.below": return ["color"]; case "peak": - return ["enabled", "color"]; + return ["enabled", "color", "reset", "label"]; + case "floor": + return ["enabled", "color", "reset", "label"]; + case "peak.label": + case "floor.label": + return ["show", "decimal"]; case "layout": return ["height", "label", "hero"]; case "layout.label": @@ -4852,35 +6674,70 @@ ${paintLayers} return orderedValue; } _cleanupEditorEmittedConfig(config) { + var _a; if (!this._isObject(config)) { return config; } + const inheritedSegmentSpace = (_a = config.bar) == null ? void 0 : _a.segment_space; let nextConfig = this._cleanupEntityIdentityForEmit(config); nextConfig = this._cleanupScaleForEmit(nextConfig); - nextConfig = this._cleanupTargetForEmit(nextConfig); + nextConfig = this._cleanupTargetForEmit(nextConfig, { type: "card" }); nextConfig = this._cleanupBaselineForEmit(nextConfig); - nextConfig = this._cleanupPeakForEmit(nextConfig); + nextConfig = this._cleanupPeakForEmit(nextConfig, { type: "card" }); + nextConfig = this._cleanupFloorForEmit(nextConfig, { type: "card" }); + nextConfig = this._cleanupGenericMarkersForEmit(nextConfig); nextConfig = this._cleanupLayoutForEmit(nextConfig); nextConfig = this._cleanupFormattingForEmit(nextConfig); nextConfig = this._cleanupNeedleForEmit(nextConfig, { type: "card" }); - nextConfig = this._cleanupBarForEmit(nextConfig); + nextConfig = this._cleanupBarForEmit(nextConfig, { type: "card" }); if (Array.isArray(nextConfig.entities)) { - nextConfig.entities = nextConfig.entities.map((entry) => { + nextConfig.entities = nextConfig.entities.map((entry, index) => { if (!this._isObject(entry)) { return entry; } let cleanedEntry = this._cleanupEntityIdentityForEmit(entry); cleanedEntry = this._cleanupScaleForEmit(cleanedEntry); - cleanedEntry = this._cleanupTargetForEmit(cleanedEntry); + cleanedEntry = this._cleanupTargetForEmit(cleanedEntry, { type: "entity", index }); cleanedEntry = this._cleanupBaselineForEmit(cleanedEntry); - cleanedEntry = this._cleanupPeakForEmit(cleanedEntry); + cleanedEntry = this._cleanupPeakForEmit(cleanedEntry, { type: "entity", index }); + cleanedEntry = this._cleanupFloorForEmit(cleanedEntry, { type: "entity", index }); + cleanedEntry = this._cleanupGenericMarkersForEmit(cleanedEntry); cleanedEntry = this._cleanupLayoutForEmit(cleanedEntry); cleanedEntry = this._cleanupFormattingForEmit(cleanedEntry); cleanedEntry = this._cleanupNeedleForEmit(cleanedEntry, { type: "entity" }); - cleanedEntry = this._cleanupBarForEmit(cleanedEntry); + cleanedEntry = this._cleanupBarForEmit(cleanedEntry, { type: "entity", index }, nextConfig, inheritedSegmentSpace); return cleanedEntry; }); } + const meaning = (raw) => { + const normalized = normalizeCardConfig({ ...raw, ...!raw.entities && !raw.entity ? { entities: [] } : {} }); + const featureKeys = ["layout", "scale", "bar", "baseline", "formatting", "target_marker", "peak_marker", "floor_marker", "generic_markers"]; + const scope = (value) => ({ + ...Object.fromEntries(featureKeys.map((key) => [key, value[key]])), + scale: Object.fromEntries(["min", "max"].map((key) => [key, { + ...value.scale[key], + fixed_explicit: value.scale[key].fixed_explicit !== false + }])) + }); + const canonical = (value, key = "") => { + if (Array.isArray(value)) return value.map((entry) => canonical(entry)); + if (this._isObject(value)) return Object.fromEntries(Object.entries(value).filter(([name]) => !["severity", "segment_space", "label_precision_key"].includes(name) && !/invalid/i.test(name)).map(([name, entry]) => [name, canonical(entry, name)])); + if (key === "fixed") return getNumericValue(null, value); + if (/color/i.test(key) && typeof value === "string") return this._normalizeColorComparisonValue(value); + return value; + }; + return this._serializeConfig(canonical({ card: scope(normalized), entities: normalized.entities.map((row) => ({ + entity: row.entity, + name: row.name, + icon: row.icon, + ...scope(row) + })) })); + }; + try { + if (meaning(config) !== meaning(nextConfig)) nextConfig = this._cloneDeep(config); + } catch (_error) { + nextConfig = this._cloneDeep(config); + } return this._orderEditorConfigKeys(nextConfig); } _getScopedPath(scope, keyPath) { @@ -5100,18 +6957,26 @@ ${paintLayers} return this._setCanonicalScopedValue(scope, canonicalPath, normalizedValue, options); } _getResolvablePartsFromTarget(target, field, options = {}) { - var _a, _b, _c, _d; + var _a, _b, _c, _d, _e, _f; const canonicalBasePath = (_a = options.canonicalBasePath) != null ? _a : ["scale", field]; const legacyFixedPath = (_b = options.legacyFixedPath) != null ? _b : [field]; const legacyEntityPath = (_c = options.legacyEntityPath) != null ? _c : [`${field}_entity`]; const structuredValue = this._getPathValue(target, canonicalBasePath); const legacyFixedValue = this._getPathValue(target, legacyFixedPath); const legacyEntityValue = this._getPathValue(target, legacyEntityPath); - const structuredFixedValue = this._isObject(structuredValue) ? structuredValue == null ? void 0 : structuredValue.fixed : structuredValue; - const structuredEntityValue = this._isObject(structuredValue) ? structuredValue == null ? void 0 : structuredValue.entity : void 0; + if (structuredValue !== void 0) { + const source = normalizeStructuredResolvableValue(structuredValue, (_d = options.inheritedSource) != null ? _d : null, null, { + allowPercent: ["target", "baseline"].includes(canonicalBasePath[0]) + }); + return { + fixed: (_e = source.fixed) != null ? _e : "", + entity: (_f = source.entity) != null ? _f : "", + ...Number.isFinite(source.percent) ? { percent: source.percent } : {} + }; + } return { - fixed: structuredFixedValue != null ? structuredFixedValue : !this._isObject(legacyFixedValue) && legacyFixedValue !== void 0 ? legacyFixedValue : "", - entity: (_d = structuredEntityValue != null ? structuredEntityValue : legacyEntityValue) != null ? _d : "" + fixed: !this._isObject(legacyFixedValue) && legacyFixedValue !== void 0 ? legacyFixedValue : "", + entity: legacyEntityValue != null ? legacyEntityValue : "" }; } _getResolvableScopedValue(scope, field, options = {}) { @@ -5119,11 +6984,19 @@ ${paintLayers} return this._getResolvablePartsFromTarget(target != null ? target : {}, field, options); } _getEffectiveResolvableScopedValue(scope, field, options = {}) { + var _a, _b; const localValue = this._getResolvableScopedValue(scope, field, options); if ((scope == null ? void 0 : scope.type) !== "entity") { return localValue; } const inheritedValue = this._getResolvableScopedValue({ type: "card" }, field, options); + const target = this._getEntityRawEntries()[scope.index]; + if (this._getPathValue(target, (_a = options.canonicalBasePath) != null ? _a : ["scale", field]) !== void 0) { + return this._getResolvablePartsFromTarget(target, field, { + ...options, + inheritedSource: { fixed: inheritedValue.fixed === "" ? null : inheritedValue.fixed, entity: inheritedValue.entity || null, percent: (_b = inheritedValue.percent) != null ? _b : null } + }); + } return { fixed: this._hasExplicitOverrideValue(localValue.fixed) ? localValue.fixed : inheritedValue.fixed, entity: this._hasExplicitOverrideValue(localValue.entity) ? localValue.entity : inheritedValue.entity @@ -5163,10 +7036,12 @@ ${paintLayers} nextTarget = this._deletePathValue(nextTarget, canonicalBasePath); const hasFixed = nextParts.fixed !== void 0 && nextParts.fixed !== null && nextParts.fixed !== ""; const hasEntity = nextParts.entity !== void 0 && nextParts.entity !== null && nextParts.entity !== ""; - if (hasFixed || hasEntity) { + const hasPercent = Number.isFinite(nextParts.percent); + if (hasFixed || hasEntity || hasPercent) { const nextValue = {}; if (hasFixed) nextValue.fixed = nextParts.fixed; if (hasEntity) nextValue.entity = nextParts.entity; + if (hasPercent) nextValue.percent = nextParts.percent; nextTarget = this._setPathValue(nextTarget, canonicalBasePath, nextValue); } prunePaths.forEach((path) => { @@ -5469,7 +7344,8 @@ ${paintLayers} ]; } _normalizeGradientStopPosValue(rawValue) { - const numericValue = this._normalizeNumberValue(rawValue); + const percent = parsePercentLiteral(rawValue); + const numericValue = Number.isFinite(percent) ? percent : this._normalizeNumberValue(rawValue); if (numericValue === null || !Number.isFinite(numericValue)) { return null; } @@ -5804,11 +7680,11 @@ ${paintLayers} _hasPeakOverride(scope) { var _a, _b; const peakValue = (_a = this._getScopedValue(scope, ["peak"])) != null ? _a : {}; - if (this._isObject(peakValue) && (Object.prototype.hasOwnProperty.call(peakValue, "enabled") || Object.prototype.hasOwnProperty.call(peakValue, "color"))) { + if (this._isObject(peakValue) && (Object.prototype.hasOwnProperty.call(peakValue, "enabled") || Object.prototype.hasOwnProperty.call(peakValue, "color") || Object.prototype.hasOwnProperty.call(peakValue, "reset") || Object.prototype.hasOwnProperty.call(peakValue, "label") || Object.prototype.hasOwnProperty.call(peakValue, "direction"))) { return true; } const peakMarkerValue = (_b = this._getScopedValue(scope, ["peak_marker"])) != null ? _b : {}; - if (this._isObject(peakMarkerValue) && (Object.prototype.hasOwnProperty.call(peakMarkerValue, "show") || Object.prototype.hasOwnProperty.call(peakMarkerValue, "color"))) { + if (this._isObject(peakMarkerValue) && (Object.prototype.hasOwnProperty.call(peakMarkerValue, "show") || Object.prototype.hasOwnProperty.call(peakMarkerValue, "color") || Object.prototype.hasOwnProperty.call(peakMarkerValue, "direction"))) { return true; } return this._getScopedValue(scope, ["show_peak"]) !== void 0 || this._getScopedValue(scope, ["peak_color"]) !== void 0; @@ -5825,6 +7701,9 @@ ${paintLayers} return this._applyScopedMutation(scope, (target) => { let nextTarget = this._deletePathValue(target, ["peak", "enabled"]); nextTarget = this._deletePathValue(nextTarget, ["peak", "color"]); + nextTarget = this._deletePathValue(nextTarget, ["peak", "reset"]); + nextTarget = this._deletePathValue(nextTarget, ["peak", "label"]); + nextTarget = this._deletePathValue(nextTarget, ["peak", "direction"]); nextTarget = this._deletePathValue(nextTarget, ["show_peak"]); nextTarget = this._deletePathValue(nextTarget, ["peak_color"]); nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]); @@ -5891,6 +7770,295 @@ ${paintLayers} return nextTarget; }); } + _getScopedMarkerExtras(scope, key) { + var _a, _b, _c, _d; + const raw = this._getScopedValue(scope, [key]); + const marker = this._isObject(raw) ? raw : {}; + const label = this._isObject(marker.label) ? marker.label : {}; + return { + reset: Object.prototype.hasOwnProperty.call(marker, "reset") ? this._normalizeTextValue(marker.reset).trim().toLowerCase() : null, + labelShow: typeof label.show === "boolean" ? label.show : null, + labelText: typeof label.text === "string" ? label.text.replace(/\s+/g, " ").trim() : null, + labelShowValue: typeof label.show_value === "boolean" ? label.show_value : null, + labelShowUnit: typeof label.show_unit === "boolean" ? label.show_unit : null, + labelPrecision: ((_a = label.precision) != null ? _a : label.decimal) === void 0 || ((_b = label.precision) != null ? _b : label.decimal) === null || ((_c = label.precision) != null ? _c : label.decimal) === "" ? null : this._normalizeNumberValue((_d = label.precision) != null ? _d : label.decimal) + }; + } + _hasExtremumOverride(scope, key) { + const marker = this._getScopedValue(scope, [key]); + if (!this._isObject(marker)) return false; + return ["enabled", "color", "reset", "label", "direction"].some((field) => Object.prototype.hasOwnProperty.call(marker, field)); + } + _getEffectiveMarkerExtras(scope, key) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r; + const local = this._getScopedMarkerExtras(scope, key); + if ((scope == null ? void 0 : scope.type) !== "entity") { + return { + reset: (_a = local.reset) != null ? _a : "never", + labelShow: (_b = local.labelShow) != null ? _b : false, + labelText: local.labelText, + labelShowValue: (_c = local.labelShowValue) != null ? _c : true, + labelShowUnit: (_d = local.labelShowUnit) != null ? _d : true, + labelPrecision: local.labelPrecision + }; + } + const card = this._getScopedMarkerExtras({ type: "card" }, key); + if (!this._hasExtremumOverride(scope, key)) { + return { + reset: (_e = card.reset) != null ? _e : "never", + labelShow: (_f = card.labelShow) != null ? _f : false, + labelText: card.labelText, + labelShowValue: (_g = card.labelShowValue) != null ? _g : true, + labelShowUnit: (_h = card.labelShowUnit) != null ? _h : true, + labelPrecision: card.labelPrecision + }; + } + return { + reset: (_j = (_i = local.reset) != null ? _i : card.reset) != null ? _j : "never", + labelShow: (_l = (_k = local.labelShow) != null ? _k : card.labelShow) != null ? _l : false, + labelText: Object.prototype.hasOwnProperty.call((_m = this._getScopedValue(scope, [key, "label"])) != null ? _m : {}, "text") ? local.labelText : card.labelText, + labelShowValue: (_o = (_n = local.labelShowValue) != null ? _n : card.labelShowValue) != null ? _o : true, + labelShowUnit: (_q = (_p = local.labelShowUnit) != null ? _p : card.labelShowUnit) != null ? _q : true, + labelPrecision: (_r = local.labelPrecision) != null ? _r : card.labelPrecision + }; + } + _getScopedFloorConfig(scope) { + const raw = this._getScopedValue(scope, ["floor"]); + const marker = this._isObject(raw) ? raw : {}; + let mode = (scope == null ? void 0 : scope.type) === "entity" ? "inherit" : "disabled"; + if (marker.enabled === true) mode = "enabled"; + if (marker.enabled === false) mode = "disabled"; + const color = marker.color && this._normalizeColorComparisonValue(marker.color) !== this._normalizeColorComparisonValue("#888888") ? marker.color : ""; + return { mode, color }; + } + _getEffectiveScopedFloorConfig(scope) { + const local = this._getScopedFloorConfig(scope); + const extras = this._getEffectiveMarkerExtras(scope, "floor"); + if ((scope == null ? void 0 : scope.type) !== "entity") return { ...local, ...extras }; + if (!this._hasExtremumOverride(scope, "floor")) { + return { ...this._getScopedFloorConfig({ type: "card" }), ...extras }; + } + const card = this._getEffectiveScopedFloorConfig({ type: "card" }); + return { + mode: local.mode === "inherit" ? card.mode : local.mode, + color: local.color || card.color, + ...extras + }; + } + _getFloorSummary(scope) { + if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasExtremumOverride(scope, "floor")) return "Inherited"; + const floor = this._getEffectiveScopedFloorConfig(scope); + if (floor.mode === "enabled") return floor.color ? "Enabled \u2022 Custom color" : "Enabled"; + if (floor.color) return "Disabled \u2022 Custom color"; + return "Disabled"; + } + _getMarkerResetSummary(key) { + var _a; + const scope = { type: "card" }; + const marker = key === "peak" ? this._getScopedPeakConfig(scope) : this._getEffectiveScopedFloorConfig(scope); + const enabled = marker.mode === "enabled"; + const reset = (_a = this._getEffectiveMarkerExtras(scope, key).reset) != null ? _a : "never"; + return `${enabled ? "Enabled" : "Disabled"} \xB7 ${reset === "never" ? "no reset" : `${reset} reset`}`; + } + _getCardTargetMarkerSummary() { + const mode = this._getTargetMode({ type: "card" }); + if (mode === "disabled") return "Disabled"; + const target = this._getTargetResolvableValue({ type: "card" }); + const parts = []; + if (mode === "enabled") parts.push("Enabled"); + const hasTargetValue = target.fixed !== "" && target.fixed !== void 0 || !!target.entity; + if (target.fixed !== "" && target.fixed !== void 0) parts.push(String(target.fixed)); + if (target.entity) parts.push(target.entity); + if (hasTargetValue || this._hasTargetShape({ type: "card" })) { + parts.push(this._getEffectiveTargetShapeValue({ type: "card" }) === "triangle" ? "Triangle" : "Diamond"); + } + return parts.length ? parts.join(" \xB7 ") : "Automatic"; + } + _setScopedExtremumEnabled(scope, key, value) { + const boolValue = !!value; + const defaultColor = "#888888"; + return this._applyScopedMutation(scope, (target) => { + var _a; + let nextTarget = this._cloneDeep(target); + const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {}; + const currentColor = (_a = current.color) != null ? _a : defaultColor; + if ((scope == null ? void 0 : scope.type) === "entity" || boolValue) current.enabled = boolValue; + else delete current.enabled; + if (currentColor && this._normalizeColorComparisonValue(currentColor) !== this._normalizeColorComparisonValue(defaultColor)) { + current.color = currentColor; + } else delete current.color; + if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current); + else nextTarget = this._deletePathValue(nextTarget, [key]); + return nextTarget; + }); + } + _setScopedExtremumColor(scope, key, rawValue) { + const normalizedValue = this._normalizeTextValue(rawValue).trim(); + const defaultColor = "#888888"; + return this._applyScopedMutation(scope, (target) => { + let nextTarget = this._cloneDeep(target); + const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {}; + delete current.color; + if (normalizedValue && this._normalizeColorComparisonValue(normalizedValue) !== this._normalizeColorComparisonValue(defaultColor)) { + current.color = normalizedValue; + } + const mode = this._getScopedFloorConfig(scope).mode; + if (key === "floor" && ((scope == null ? void 0 : scope.type) === "entity" && mode !== "inherit" || (scope == null ? void 0 : scope.type) !== "entity" && mode === "enabled")) { + current.enabled = mode === "enabled"; + } + if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current); + else nextTarget = this._deletePathValue(nextTarget, [key]); + if (key === "peak") { + nextTarget = this._deletePathValue(nextTarget, ["show_peak"]); + nextTarget = this._deletePathValue(nextTarget, ["peak_color"]); + nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]); + } + return nextTarget; + }); + } + _setScopedExtremumReset(scope, key, value) { + const normalized = this._normalizeTextValue(value).trim().toLowerCase(); + return this._applyScopedMutation(scope, (target) => { + let nextTarget = this._cloneDeep(target); + const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {}; + if (normalized && ((scope == null ? void 0 : scope.type) === "entity" || normalized !== "never")) current.reset = normalized; + else delete current.reset; + if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current); + else nextTarget = this._deletePathValue(nextTarget, [key]); + if (key === "peak") { + nextTarget = this._deletePathValue(nextTarget, ["show_peak"]); + nextTarget = this._deletePathValue(nextTarget, ["peak_color"]); + nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]); + } + return nextTarget; + }); + } + _setScopedExtremumLabelShow(scope, key, value) { + const enabled = !!value; + return this._applyScopedMutation(scope, (target) => { + let nextTarget = this._cloneDeep(target); + const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {}; + const label = this._isObject(current.label) ? this._cloneDeep(current.label) : {}; + if ((scope == null ? void 0 : scope.type) === "entity" || enabled) label.show = enabled; + else delete label.show; + if (Object.keys(label).length) current.label = label; + else delete current.label; + if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current); + else nextTarget = this._deletePathValue(nextTarget, [key]); + if (key === "peak") { + nextTarget = this._deletePathValue(nextTarget, ["show_peak"]); + nextTarget = this._deletePathValue(nextTarget, ["peak_color"]); + nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]); + } + return nextTarget; + }); + } + _setScopedExtremumLabelDecimal(scope, key, value) { + const decimal = this._normalizeNumberValue(value); + return this._applyScopedMutation(scope, (target) => { + let nextTarget = this._cloneDeep(target); + const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {}; + const label = this._isObject(current.label) ? this._cloneDeep(current.label) : {}; + if (decimal !== null) label.decimal = decimal; + else delete label.decimal; + if (Object.keys(label).length) current.label = label; + else delete current.label; + if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current); + else nextTarget = this._deletePathValue(nextTarget, [key]); + return nextTarget; + }); + } + _getBuiltinMarkerLabelOptions(scope, key) { + var _a; + const marker = this._getScopedValue(scope, [key]); + const label = this._isObject(marker == null ? void 0 : marker.label) ? marker.label : {}; + const cardLabel = (scope == null ? void 0 : scope.type) === "entity" ? (_a = this._getScopedValue({ type: "card" }, [key, "label"])) != null ? _a : {} : {}; + const own = (name) => Object.prototype.hasOwnProperty.call(label, name); + const inheritedValue = (name, fallback) => { + var _a2; + return own(name) ? label[name] : (_a2 = cardLabel[name]) != null ? _a2 : fallback; + }; + const precision = inheritedValue("precision", inheritedValue("decimal", "")); + return { + show: key === "target" ? this._getEffectiveTargetLabelShowValue(scope) : this._getEffectiveMarkerExtras(scope, key).labelShow, + text: inheritedValue("text", ""), + showValue: inheritedValue("show_value", true) !== false, + showUnit: inheritedValue("show_unit", true) !== false, + precision: precision === null ? "" : precision + }; + } + _renderBuiltinMarkerLabelControls(scope, key, title) { + var _a; + const options = this._getBuiltinMarkerLabelOptions(scope, key); + const isEntity = (scope == null ? void 0 : scope.type) === "entity"; + const prefix = isEntity ? `entity-${scope.index}-${key}` : key; + const attr = isEntity ? `data-kind="entity-${key}-label-` : `data-field="${key}-label-`; + const suffix = isEntity ? `" data-index="${scope.index}"` : '"'; + return ` +
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
`; + } + _setBuiltinMarkerLabelField(scope, key, field, value) { + const publicField = field === "showValue" ? "show_value" : field === "showUnit" ? "show_unit" : field === "precision" ? "precision" : field; + const isText = field === "text"; + const normalized = isText ? this._normalizeTextValue(value).replace(/\s+/g, " ").trim() : field === "show" || field === "showValue" || field === "showUnit" ? value === true : this._normalizeDecimalValue(value); + if (field === "precision" && value !== "" && normalized === null) return false; + return this._applyScopedMutation(scope, (target) => { + var _a, _b, _c, _d; + let next = this._cloneDeep(target); + if (key === "target" && field === "show") next = this._deletePathValue(next, ["show_target_label"]); + const marker = this._isObject(this._getPathValue(next, [key])) ? this._cloneDeep(this._getPathValue(next, [key])) : {}; + const label = this._isObject(marker.label) ? this._cloneDeep(marker.label) : {}; + const inheritedLabel = (scope == null ? void 0 : scope.type) === "entity" ? (_a = this._getScopedValue({ type: "card" }, [key, "label"])) != null ? _a : {} : {}; + const inheritedValue = field === "precision" ? (_c = (_b = inheritedLabel.precision) != null ? _b : inheritedLabel.decimal) != null ? _c : null : (_d = inheritedLabel[publicField]) != null ? _d : field === "show" ? false : field === "showValue" || field === "showUnit" ? true : void 0; + if (normalized === null || isText && !normalized) { + if (isText && (scope == null ? void 0 : scope.type) === "entity" && typeof inheritedLabel.text === "string" && inheritedLabel.text.trim()) { + label[publicField] = ""; + } else { + delete label[publicField]; + } + } else if ((scope == null ? void 0 : scope.type) === "entity" && normalized === inheritedValue) { + delete label[publicField]; + } else if ((field === "show" || field === "showValue" || field === "showUnit") && (scope == null ? void 0 : scope.type) !== "entity" && normalized === (field === "show" ? false : true)) { + delete label[publicField]; + } else { + label[publicField] = normalized; + } + if (field === "precision") delete label.decimal; + if (Object.keys(label).length) marker.label = label; + else delete marker.label; + if (Object.keys(marker).length) next = this._setPathValue(next, [key], marker); + else next = this._deletePathValue(next, [key]); + return next; + }); + } + _clearFloorOverride(scope) { + return this._applyScopedMutation(scope, (target) => { + let nextTarget = this._cloneDeep(target); + const floor = this._isObject(this._getPathValue(nextTarget, ["floor"])) ? this._cloneDeep(this._getPathValue(nextTarget, ["floor"])) : {}; + ["enabled", "color", "reset", "label", "direction"].forEach((key) => delete floor[key]); + if (Object.keys(floor).length) nextTarget = this._setPathValue(nextTarget, ["floor"], floor); + else nextTarget = this._deletePathValue(nextTarget, ["floor"]); + nextTarget = this._deletePathValue(nextTarget, ["floor_marker"]); + return nextTarget; + }, { rerender: true }); + } _setFixedMarkerValue(rootKey, enabled, value) { const numericValue = this._normalizeNumberValue(value); if (!enabled || numericValue === null) { @@ -6561,7 +8729,7 @@ ${paintLayers} return this._setScopedGradientStops(scope, nextStops, { rerender: true }); } _getFillStyleValue() { - return this._getScopedFillStyleValue({ type: "card" }); + return this._getEffectiveFillStyleValue({ type: "card" }); } _getFillStyleFromColorMode(colorMode) { switch (colorMode) { @@ -6585,14 +8753,11 @@ ${paintLayers} return this._getFillStyleFromColorMode(colorMode); } _getEffectiveScopedFillStyleValue(scope) { - if ((scope == null ? void 0 : scope.type) !== "entity") { - return this._getScopedFillStyleValue(scope); - } return this._getEffectiveFillStyleValue(scope); } _setScopedBarFillStyle(scope, rawValue) { const normalizedValue = this._normalizeTextValue(rawValue).trim(); - if (!normalizedValue || normalizedValue === "bands") { + if (!normalizedValue) { return this._removeCanonicalScopedValue(scope, ["bar", "fill_style"], { deprecatedKeys: [["color_mode"]], prunePaths: [["bar"]] @@ -6809,6 +8974,51 @@ ${paintLayers} if (enabled === false) return "disabled"; return "auto"; } + _getTargetShapeValue(scope) { + var _a; + return (_a = this._getScopedValue(scope, ["target", "shape"])) != null ? _a : ""; + } + _getEffectiveMarkerDirection(scope, key) { + const canonical = this._getScopedValue(scope, [key, "direction"]); + const local = canonical !== void 0 ? canonical : this._getScopedValue(scope, [`${key}_marker`, "direction"]); + if ((scope == null ? void 0 : scope.type) === "entity" && local === void 0) { + return this._getEffectiveMarkerDirection({ type: "card" }, key); + } + return normalizeMarkerDirection(local); + } + _setMarkerDirection(scope, key, rawValue) { + const direction = normalizeMarkerDirection(rawValue); + const cardDirection = this._getEffectiveMarkerDirection({ type: "card" }, key); + if ((scope == null ? void 0 : scope.type) !== "entity" && direction === "inward" || (scope == null ? void 0 : scope.type) === "entity" && direction === cardDirection) { + return this._removeCanonicalScopedValue(scope, [key, "direction"], { + prunePaths: [[key]] + }); + } + return this._setCanonicalScopedTextOverride(scope, [key, "direction"], direction, { + prunePaths: [[key]] + }); + } + _hasTargetShape(scope) { + const target = this._getScopedValue(scope, ["target"]); + return this._isObject(target) && Object.prototype.hasOwnProperty.call(target, "shape"); + } + _getEffectiveTargetShapeValue(scope) { + if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasTargetShape(scope)) { + return this._getEffectiveTargetShapeValue({ type: "card" }); + } + return normalizeTargetMarkerShape(this._getTargetShapeValue(scope)); + } + _setTargetShape(scope, rawValue) { + const normalizedShape = normalizeTargetMarkerShape(rawValue); + if ((scope == null ? void 0 : scope.type) !== "entity" && normalizedShape === "diamond") { + return this._removeCanonicalScopedValue(scope, ["target", "shape"], { + prunePaths: [["target"]] + }); + } + return this._setCanonicalScopedTextOverride(scope, ["target", "shape"], normalizedShape, { + prunePaths: [["target"]] + }); + } _getEffectiveTargetMode(scope) { const mode = this._getTargetMode(scope); if ((scope == null ? void 0 : scope.type) !== "entity" || mode !== "inherit") { @@ -6849,7 +9059,10 @@ ${paintLayers} nextTarget = this._deletePathValue(nextTarget, ["target", "enabled"]); nextTarget = this._deletePathValue(nextTarget, ["target", "at"]); nextTarget = this._deletePathValue(nextTarget, ["target", "color"]); + nextTarget = this._deletePathValue(nextTarget, ["target", "shape"]); + nextTarget = this._deletePathValue(nextTarget, ["target", "direction"]); nextTarget = this._deletePathValue(nextTarget, ["target", "label", "show"]); + nextTarget = this._deletePathValue(nextTarget, ["target", "label", "decimal"]); nextTarget = this._deletePathValue(nextTarget, ["target", "when_exceeded", "fill_color"]); } else { nextTarget = this._deletePathValue(nextTarget, ["target"]); @@ -6921,6 +9134,28 @@ ${paintLayers} prunePaths: [["target", "label"], ["target"]] }); } + _getTargetLabelDecimalValue(scope) { + var _a, _b; + return (_b = (_a = this._getScopedValue(scope, ["target", "label", "precision"])) != null ? _a : this._getScopedValue(scope, ["target", "label", "decimal"])) != null ? _b : ""; + } + _getEffectiveTargetLabelDecimalValue(scope) { + const local = this._getTargetLabelDecimalValue(scope); + return local !== "" && local !== null && local !== void 0 ? local : (scope == null ? void 0 : scope.type) === "entity" ? this._getTargetLabelDecimalValue({ type: "card" }) : ""; + } + _setTargetLabelDecimal(scope, rawValue) { + const normalizedValue = this._normalizeDecimalValue(rawValue); + if (rawValue === "" || rawValue === null || rawValue === void 0) { + return this._removeCanonicalScopedValue(scope, ["target", "label", "decimal"], { + prunePaths: [["target", "label"], ["target"]] + }); + } + if (normalizedValue === null) { + return false; + } + return this._setCanonicalScopedValue(scope, ["target", "label", "decimal"], normalizedValue, { + prunePaths: [["target", "label"], ["target"]] + }); + } _getTargetAboveFillColorValue(scope) { var _a, _b; return (_b = (_a = this._getScopedValue(scope, ["target", "when_exceeded", "fill_color"])) != null ? _a : this._getScopedValue(scope, ["above_target_color"])) != null ? _b : ""; @@ -7027,7 +9262,7 @@ ${paintLayers} return ["target_entity", "target_color", "show_target_label", "above_target_color"].some((key) => { const value = this._getScopedValue(scope, [key]); return value !== void 0 && value !== null && value !== "" && value !== false; - }); + }) || this._getTargetLabelDecimalValue(scope) !== ""; } _getBaselineResolvableValue(scope) { return this._getResolvableScopedValue(scope, "baseline", { @@ -7163,6 +9398,9 @@ ${paintLayers} return nextTarget; }, { rerender: true }); } + _removeBaseline(scope) { + return this._removeScopedValue(scope, ["baseline"], { rerender: true }); + } _hasBaselineOverride(scope) { const baselineValue = this._getScopedValue(scope, ["baseline"]); if (this._isObject(baselineValue) && Object.keys(baselineValue).length) { @@ -7208,6 +9446,14 @@ ${paintLayers} } this._render(); } + _toggleGenericMarkerExpanded(markerUiId) { + if (this._expandedGenericMarkerUiIds.has(markerUiId)) { + this._expandedGenericMarkerUiIds.delete(markerUiId); + } else { + this._expandedGenericMarkerUiIds.add(markerUiId); + } + this._render(); + } _getOverrideGroupKey(index, group) { return `${index}:${group}`; } @@ -7256,8 +9502,13 @@ ${paintLayers} const target = this._getTargetResolvableValue(scope); if (target.fixed !== "" && target.fixed !== void 0) parts.push(`Target ${target.fixed}`); if (target.entity) parts.push("Entity"); + if (this._hasTargetShape(scope)) { + parts.push(this._getEffectiveTargetShapeValue(scope) === "triangle" ? "Triangle" : "Diamond"); + } if (this._hasCustomTargetColor(scope)) parts.push("Custom color"); if (this._getTargetLabelShowValue(scope)) parts.push("Label"); + const labelDecimal = this._getTargetLabelDecimalValue(scope); + if (labelDecimal !== "") parts.push(`Label ${labelDecimal} ${Number(labelDecimal) === 1 ? "decimal" : "decimals"}`); if (this._getTargetAboveFillColorValue(scope)) parts.push("Above"); return parts.length ? parts.join(" \u2022 ") : "Inherited"; } @@ -7272,6 +9523,14 @@ ${paintLayers} if (this._getBaselineDirectionalColorValue(scope, "below")) parts.push("Below"); return parts.length ? parts.join(" \u2022 ") : "Inherited"; } + _getCardBaselineSummary() { + const mode = this._getBaselineMode({ type: "card" }); + if (mode === "disabled") return "Disabled"; + const baseline = this._getBaselineResolvableValue({ type: "card" }); + const origin = baseline.entity || (baseline.fixed !== "" && baseline.fixed !== void 0 ? baseline.fixed : ""); + const modeLabel = mode === "enabled" ? "Enabled" : "Auto"; + return origin !== "" ? `${modeLabel} \xB7 ${origin}` : modeLabel; + } _getBarAppearanceSummary(scope) { var _a; const parts = []; @@ -7309,13 +9568,13 @@ ${paintLayers} } _getEffectiveFillStyleValue(scope) { if ((scope == null ? void 0 : scope.type) === "entity") { - const hasEntityFillStyle = this._getScopedValue(scope, ["bar", "fill_style"]) !== void 0 || this._getScopedValue(scope, ["bar", "color_mode"]) !== void 0 || this._getScopedValue(scope, ["color_mode"]) !== void 0; - if (hasEntityFillStyle) { - return this._getScopedFillStyleValue(scope); - } - return this._getFillStyleValue(); + return normalizeBarConfig( + this._getEntityRawEntries()[scope.index], + this._draftConfig, + { isCardScope: false } + ).fill_style; } - return this._getScopedFillStyleValue(scope); + return normalizeBarConfig(this._draftConfig, null, { isCardScope: true }).fill_style; } _getScopedGradientStopsValue(scope) { const localRows = this._getGradientStopsUiRows(scope); @@ -7484,11 +9743,424 @@ ${paintLayers} } return ``; } - _renderEntitySourceInput(kind, index, value, placeholder = "sensor.example") { + _renderEntitySourceInput(kind, index, value, placeholder = "sensor.example", extraDataset = {}) { + const extraAttrs = Object.entries(extraDataset).map(([key, entry]) => `data-${key}="${this._escapeAttribute(entry)}"`).join(" "); if (customElements.get("ha-entity-picker")) { - return ``; + return ``; } - return ``; + return ``; + } + _hasMarkersOverride(scope) { + return (scope == null ? void 0 : scope.type) === "entity" && this._getScopedValue(scope, ["markers"]) !== void 0; + } + _getGenericMarkers(scope, effective = true) { + const local = this._getScopedValue(scope, ["markers"]); + if ((scope == null ? void 0 : scope.type) === "entity" && local === void 0 && effective) { + const cardMarkers = this._getScopedValue({ type: "card" }, ["markers"]); + return Array.isArray(cardMarkers) ? this._cloneDeep(cardMarkers) : []; + } + return Array.isArray(local) ? this._cloneDeep(local) : []; + } + _getGenericMarkersSummary(scope) { + if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasMarkersOverride(scope)) return "Inherited"; + const count = this._getGenericMarkers(scope, false).length; + return count === 0 ? "No reference markers" : `${count} reference marker${count === 1 ? "" : "s"}`; + } + _getGenericMarkerScopeKey(scope) { + return (scope == null ? void 0 : scope.type) === "entity" ? `entity:${scope.index}` : "card"; + } + _getGenericMarkerUiIds(scope, count) { + var _a; + const key = this._getGenericMarkerScopeKey(scope); + const ids = (_a = this._genericMarkerUiIds.get(key)) != null ? _a : []; + while (ids.length < count) { + ids.push(`marker-${++this._nextGenericMarkerUiId}`); + } + ids.length = count; + this._genericMarkerUiIds.set(key, ids); + return ids; + } + _resetGenericMarkerUiScope(scope) { + var _a; + const key = this._getGenericMarkerScopeKey(scope); + const ids = (_a = this._genericMarkerUiIds.get(key)) != null ? _a : []; + ids.forEach((id) => this._expandedGenericMarkerUiIds.delete(id)); + this._genericMarkerUiIds.delete(key); + } + _getGenericMarkerSummary(marker) { + var _a; + const source = this._getGenericMarkerSource(marker); + const lane = (marker == null ? void 0 : marker.lane) === "above" ? "Above" : "Below"; + const shape = (_a = marker == null ? void 0 : marker.shape) != null ? _a : "circle"; + let sourceSummary; + if (source.mode === "percent") { + sourceSummary = source.percent === "" ? "Percentage" : `${source.percent}%`; + } else if (source.mode === "entity" || source.mode === "entity-fallback") { + sourceSummary = source.entity || "Entity"; + } else { + sourceSummary = source.fixed === "" ? "Fixed value" : String(source.fixed); + } + return `${lane} \xB7 ${shape.charAt(0).toUpperCase()}${shape.slice(1)} \xB7 ${sourceSummary}`; + } + _refreshGenericMarkerSummary(scope, markerIndex) { + const markers = this._getGenericMarkers(scope); + const marker = markers[markerIndex]; + const uiId = this._getGenericMarkerUiIds(scope, markers.length)[markerIndex]; + const summary = this._getShadowElementById(`generic-${uiId}-summary`); + if (marker && summary) { + const text = this._getGenericMarkerSummary(marker); + summary.textContent = text; + summary.setAttribute("title", text); + } + } + _getGenericMarkerSource(marker) { + var _a, _b, _c; + const at = marker == null ? void 0 : marker.at; + if (typeof at === "string" && /^\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*%\s*$/.test(at)) { + return { mode: "percent", percent: at.replace(/%/g, "").trim() }; + } + if (typeof at === "string" && /^[a-z0-9_]+\.[a-z0-9_]+$/i.test(at.trim())) { + return { mode: "entity", entity: at.trim(), fixed: "" }; + } + const source = this._isObject(at) ? at : {}; + if (Object.prototype.hasOwnProperty.call(source, "entity")) { + return { + mode: source.fixed !== void 0 && source.fixed !== null && source.fixed !== "" ? "entity-fallback" : "entity", + entity: (_a = source.entity) != null ? _a : "", + fixed: (_b = source.fixed) != null ? _b : "" + }; + } + return { mode: "fixed", fixed: (_c = source.fixed) != null ? _c : "" }; + } + _renderGenericMarkersEditor(scope) { + const scopeType = scope.type; + const scopeIndex = scopeType === "entity" ? scope.index : "card"; + const markers = this._getGenericMarkers(scope); + const markerUiIds = this._getGenericMarkerUiIds(scope, markers.length); + const override = this._hasMarkersOverride(scope); + const rows = markers.map((marker, markerIndex) => { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n; + const source = this._getGenericMarkerSource(marker); + const markerUiId = markerUiIds[markerIndex]; + const rowId = `${scopeType}-${scopeIndex}-generic-${markerUiId}`; + const expanded = this._expandedGenericMarkerUiIds.has(markerUiId); + const entitySource = source.mode === "entity" || source.mode === "entity-fallback" ? this._renderEntitySourceInput("generic-marker-entity", scopeIndex, source.entity, "sensor.reference", { + "scope-type": scopeType, + "marker-index": markerIndex + }) : ""; + const labelEntitySource = this._renderEntitySourceInput( + "generic-marker-label-entity", + scopeIndex, + (_b = (_a = marker == null ? void 0 : marker.label) == null ? void 0 : _a.entity) != null ? _b : "", + "sensor.information", + { + "scope-type": scopeType, + "marker-index": markerIndex + } + ); + return ` +
+
+ +
+ + + +
+
+
+
+ + +
+ ${source.mode === "fixed" ? ` +
+ + +
` : ""} + ${source.mode === "entity" || source.mode === "entity-fallback" ? ` +
+ + ${entitySource} +
` : ""} + ${source.mode === "entity-fallback" ? ` +
+ + +
` : ""} + ${source.mode === "percent" ? ` +
+ + % +
` : ""} +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + ${this._renderColorInput({ + id: `${rowId}-color`, + kind: "generic-marker-color", + index: scopeIndex, + value: (_e = marker == null ? void 0 : marker.color) != null ? _e : "#888888", + fallbackHex: "#888888", + placeholder: "#888888", + extraDataset: { "scope-type": scopeType, "marker-index": markerIndex } + })} +
+
+ + +
+
+ + +
+
+ +
+
+ + ${labelEntitySource} +
+
+
+ + +
+
+ + +
+
+ +
+
+
+
`; + }).join(""); + const inheritControl = scopeType === "entity" ? ` +
+
+ + +
+
` : ""; + const overrideNote = scopeType === "entity" && !override ? '
Enable the override to replace the card marker list. An empty override clears all card markers.
' : ""; + return ` + ${inheritControl} + ${overrideNote} + ${scopeType === "card" || override ? ` +
Up to four markers render in each lane, including Peak, Floor, and Target. Excess generic markers remain editable and show a warning. Unresolved markers still reserve a slot and lane.
+
${rows}
+ ` : ""} + `; + } + _getGenericMarkerScope(target) { + var _a; + return ((_a = target == null ? void 0 : target.dataset) == null ? void 0 : _a.scopeType) === "entity" ? { type: "entity", index: Number(target.dataset.index) } : { type: "card" }; + } + _setGenericMarkerList(scope, markers, options = {}) { + return this._setScopedValue(scope, ["markers"], markers, { rerender: false, ...options }); + } + _updateGenericMarker(scope, markerIndex, update, options = {}) { + var _a; + const markers = this._getGenericMarkers(scope); + if (!markers[markerIndex]) return false; + const marker = this._isObject(markers[markerIndex]) ? this._cloneDeep(markers[markerIndex]) : {}; + const nextMarker = (_a = update(marker)) != null ? _a : marker; + markers[markerIndex] = nextMarker; + const changed = this._setGenericMarkerList(scope, markers, options); + if (changed) this._refreshGenericMarkerSummary(scope, markerIndex); + return changed; + } + _setGenericMarkerSourceMode(scope, markerIndex, mode) { + return this._updateGenericMarker(scope, markerIndex, (marker) => { + if (mode === "percent") { + marker.at = "50%"; + return marker; + } + const oldAt = this._isObject(marker.at) ? this._cloneDeep(marker.at) : {}; + if (mode === "fixed") { + delete oldAt.entity; + delete oldAt.percent; + if (oldAt.fixed === void 0) oldAt.fixed = 50; + } else { + delete oldAt.percent; + if (!oldAt.entity) oldAt.entity = ""; + if (mode === "entity") delete oldAt.fixed; + if (mode === "entity-fallback" && oldAt.fixed === void 0) oldAt.fixed = 50; + } + marker.at = oldAt; + return marker; + }, { rerender: true }); + } + _setGenericMarkerField(scope, markerIndex, kind, value) { + var _a, _b; + const atLeaf = (key, nextValue) => this._updateGenericMarker(scope, markerIndex, (marker) => { + const at = this._isObject(marker.at) ? this._cloneDeep(marker.at) : {}; + if (nextValue === void 0) delete at[key]; + else at[key] = nextValue; + marker.at = Object.keys(at).length ? at : null; + return marker; + }); + if (kind === "generic-marker-source-mode") return this._setGenericMarkerSourceMode(scope, markerIndex, value); + if (kind === "generic-marker-show-marker") { + return this._updateGenericMarker(scope, markerIndex, (marker) => { + if (value === false) marker.show_marker = false; + else delete marker.show_marker; + return marker; + }); + } + if (kind === "generic-marker-fixed") { + return atLeaf("fixed", (_a = this._normalizeNumberValue(value)) != null ? _a : void 0); + } + if (kind === "generic-marker-fallback") { + return atLeaf("fixed", (_b = this._normalizeNumberValue(value)) != null ? _b : void 0); + } + if (kind === "generic-marker-entity") { + return atLeaf("entity", this._normalizeTextValue(value).trim() || void 0); + } + if (kind === "generic-marker-percent") { + const percent = this._normalizeNumberValue(value); + return this._updateGenericMarker(scope, markerIndex, (marker) => { + marker.at = percent === null ? null : `${percent}%`; + return marker; + }); + } + if (kind === "generic-marker-lane" || kind === "generic-marker-shape" || kind === "generic-marker-direction") { + return this._updateGenericMarker(scope, markerIndex, (marker) => { + const key = kind === "generic-marker-lane" ? "lane" : kind === "generic-marker-shape" ? "shape" : "direction"; + marker[key] = key === "direction" ? normalizeMarkerDirection(value) : value; + return marker; + }); + } + if (kind === "generic-marker-color") { + return this._updateGenericMarker(scope, markerIndex, (marker) => { + marker.color = this._normalizeTextValue(value).trim(); + return marker; + }); + } + if (kind.startsWith("generic-marker-label-")) { + return this._updateGenericMarker(scope, markerIndex, (marker) => { + const label = this._isObject(marker.label) ? this._cloneDeep(marker.label) : {}; + if (kind === "generic-marker-label-show") label.show = value === true; + if (kind === "generic-marker-label-text") { + const text = this._normalizeTextValue(value).replace(/\s+/g, " ").trim(); + if (text) label.text = text; + else delete label.text; + } + if (kind === "generic-marker-label-entity") { + const entity = this._normalizeTextValue(value).trim(); + if (entity) label.entity = entity; + else delete label.entity; + } + if (kind === "generic-marker-label-show-value") label.show_value = value === true; + if (kind === "generic-marker-label-show-unit") label.show_unit = value === true; + if (kind === "generic-marker-label-precision") { + const precision = this._normalizeDecimalValue(value); + if (precision === null) { + delete label.precision; + delete label.decimal; + } else { + label.precision = precision; + delete label.decimal; + } + } + marker.label = label; + return marker; + }); + } + return false; + } + _cleanupGenericMarkersForEmit(target) { + if (!this._isObject(target) || !Array.isArray(target.markers)) return target; + const nextTarget = this._cloneDeep(target); + nextTarget.markers = nextTarget.markers.map((rawMarker) => { + var _a; + if (!this._isObject(rawMarker)) return rawMarker; + const marker = this._cloneDeep(rawMarker); + if (marker.show_marker === true) delete marker.show_marker; + if (this._isObject(marker.at)) { + const at = this._cloneDeep(marker.at); + const fixed = this._normalizeNumberValue(at.fixed); + const entity = this._normalizeTextValue(at.entity).trim(); + if (fixed !== null) at.fixed = fixed; + else delete at.fixed; + if (entity) at.entity = entity; + else delete at.entity; + if (Object.keys(at).length) marker.at = at; + else delete marker.at; + } + if (marker.lane === "below") delete marker.lane; + if (marker.shape === "circle") delete marker.shape; + if (marker.direction === "inward") delete marker.direction; + if (this._normalizeColorComparisonValue(marker.color) === this._normalizeColorComparisonValue("#888888")) delete marker.color; + if (this._isObject(marker.label)) { + const label = this._cloneDeep(marker.label); + if (label.show === false) delete label.show; + if (label.show_value === true) delete label.show_value; + if (label.show_unit === true) delete label.show_unit; + if (typeof label.text === "string") { + label.text = label.text.replace(/\s+/g, " ").trim(); + if (!label.text) delete label.text; + } + const precision = this._normalizeDecimalValue((_a = label.precision) != null ? _a : label.decimal); + delete label.decimal; + if (precision === null) delete label.precision; + else label.precision = precision; + delete label.unit; + if (Object.keys(label).length) marker.label = label; + else delete marker.label; + } + return marker; + }); + return nextTarget; + } + _renderResetOptions(value) { + const selected = this._normalizeTextValue(value).trim().toLowerCase() || "never"; + const options = [ + "never", + "quarterly", + "hourly", + "daily", + "weekly", + "monthly", + "yearly", + ...Array.from({ length: 59 }, (_, index) => `${index + 1}m`), + ...Array.from({ length: 23 }, (_, index) => `${index + 1}h`) + ]; + return options.map((option) => ``).join(""); } _escapeAttribute(value) { return this._normalizeTextValue(value).replace(/&/g, "&").replace(/"/g, """).replace(//g, ">"); @@ -7559,12 +10231,17 @@ ${paintLayers} const baselineBelowColor = this._getBaselineDirectionalColorValue({ type: "card" }, "below"); const target = this._getTargetResolvableValue({ type: "card" }); const targetMode = this._getTargetMode({ type: "card" }); + const targetShape = this._getEffectiveTargetShapeValue({ type: "card" }); + const targetDirection = this._getEffectiveMarkerDirection({ type: "card" }, "target"); const targetColor = this._getTargetColorValue({ type: "card" }); const targetLabelShow = this._getTargetLabelShowValue({ type: "card" }); + const targetLabelDecimal = this._getTargetLabelDecimalValue({ type: "card" }); const targetAboveFillColor = this._getTargetAboveFillColorValue({ type: "card" }); const formattingUnit = this._getScopedFormattingValue({ type: "card" }, "unit"); const formattingDecimal = this._getScopedFormattingValue({ type: "card" }, "decimal"); const cardPeak = this._getScopedPeakConfig({ type: "card" }); + const cardPeakExtras = this._getEffectiveMarkerExtras({ type: "card" }, "peak"); + const cardFloor = this._getEffectiveScopedFloorConfig({ type: "card" }); const scaleMin = this._getScaleFixedValue("min", "min"); const scaleMax = this._getScaleFixedValue("max", "max"); const scaleMinEntity = this._getScaleEntityValue("min"); @@ -7680,6 +10357,79 @@ ${paintLayers} align-items: end; min-width: 0; } + .generic-marker-list { + gap: 8px; + } + .generic-marker-item { + min-width: 0; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--divider-color, #888) 32%, transparent); + border-radius: 10px; + background: color-mix(in srgb, var(--card-background-color, #fff) 82%, transparent); + } + .generic-marker-item[data-expanded="true"] { + border-color: color-mix(in srgb, var(--accent-color, var(--primary-color, #03a9f4)) 38%, var(--divider-color, #888)); + } + .generic-marker-header { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + padding: 6px 8px; + } + .generic-marker-toggle { + display: grid; + grid-template-columns: minmax(0, auto) minmax(0, 1fr); + align-items: center; + gap: 4px 10px; + flex: 1 1 auto; + min-width: 0; + min-height: 40px; + padding: 4px 6px; + border: 0; + background: transparent; + text-align: left; + } + .generic-marker-toggle:hover, + .generic-marker-toggle:focus { + background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 72%, transparent); + } + .generic-marker-title { + font-weight: 700; + white-space: nowrap; + } + .generic-marker-summary { + min-width: 0; + overflow: hidden; + color: var(--secondary-text-color, #666); + text-overflow: ellipsis; + white-space: nowrap; + } + .generic-marker-actions { + display: flex; + flex: 0 0 auto; + gap: 4px; + align-items: center; + } + .generic-marker-actions button { + min-width: 38px; + min-height: 38px; + padding: 6px; + } + .generic-marker-body { + gap: 12px; + padding: 12px; + border-top: 1px solid color-mix(in srgb, var(--divider-color, #888) 25%, transparent); + background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 78%, transparent); + } + .generic-marker-pair, + .generic-marker-options { + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + align-items: end; + } + .generic-marker-precision { + max-width: 220px; + } .list-row.triple { grid-template-columns: repeat(3, minmax(120px, 1fr)) auto; } @@ -8042,6 +10792,22 @@ ${paintLayers} .list-row { grid-template-columns: minmax(0, 1fr); } + .generic-marker-header { + flex-wrap: wrap; + } + .generic-marker-toggle { + flex-basis: 100%; + } + .generic-marker-actions { + width: 100%; + } + .generic-marker-actions button { + flex: 1 1 auto; + } + .generic-marker-pair, + .generic-marker-options { + grid-template-columns: minmax(0, 1fr); + } .list-row.triple, .list-row.gradient-stop-row { grid-template-columns: minmax(0, 1fr); @@ -8109,6 +10875,7 @@ ${paintLayers}
Overrides replace card defaults only for this entity.
${(() => { + var _a3, _b2; const scope = { type: "entity", index }; const minParts = this._getEffectiveResolvableScopedValue(scope, "min"); const maxParts = this._getEffectiveResolvableScopedValue(scope, "max"); @@ -8121,6 +10888,8 @@ ${paintLayers} const targetInherited = !this._hasTargetOverride(scope); const targetParts = this._getEffectiveTargetResolvableValue(scope); const targetMode2 = this._getEffectiveTargetMode(scope); + const targetShape2 = this._getEffectiveTargetShapeValue(scope); + const targetDirection2 = this._getEffectiveMarkerDirection(scope, "target"); const formattingInherited = !this._hasFormattingOverride(scope); const layoutInherited = !this._hasLayoutOverride(scope); const peakInherited = !this._hasPeakOverride(scope); @@ -8128,6 +10897,9 @@ ${paintLayers} const segmentsInherited = !this._hasSegmentsOverride(scope); const scaleInherited = !this._hasResolvableOverride(this._getResolvableScopedValue(scope, "min")) && !this._hasResolvableOverride(this._getResolvableScopedValue(scope, "max")); const entityPeak = this._getEffectiveScopedPeakConfig(scope); + const entityPeakExtras = this._getEffectiveMarkerExtras(scope, "peak"); + const floorInherited = !this._hasExtremumOverride(scope, "floor"); + const entityFloor = this._getEffectiveScopedFloorConfig(scope); const entityGradientStops = this._getScopedGradientStopsValue(scope); const entityGradientDraft = this._getGradientStopsDraftState(scope); const entityGradientDraftMessage = this._getGradientDraftValidationMessage(scope); @@ -8335,8 +11107,74 @@ ${paintLayers} placeholder: "inherit card default" })}
+
+ + +
+
+ + +
+ ${this._renderBuiltinMarkerLabelControls(scope, "peak", "Peak")} ` }); + const floorGroup = this._renderOverrideGroup({ + index, + group: "floor", + title: "Floor", + summary: this._getFloorSummary(scope), + content: ` +
+
+ + +
+
+
+
+ + +
+
+
+ + ${this._renderColorInput({ + id: `entity-${index}-floor-color`, + kind: "entity-floor-color", + index, + value: entityFloor.color, + fallbackHex: "#888888", + placeholder: "inherit card default" + })} +
+
+ + +
+
+ + +
+ ${this._renderBuiltinMarkerLabelControls(scope, "floor", "Floor")} + ` + }); + const markersGroup = this._renderOverrideGroup({ + index, + group: "markers", + title: "Reference markers", + summary: this._getGenericMarkersSummary(scope), + content: this._renderGenericMarkersEditor(scope) + }); const segmentsGroup = this._renderOverrideGroup({ index, group: "segments", @@ -8355,13 +11193,13 @@ ${paintLayers}
${this._renderListRows(entitySegments, (segment, segmentIndex) => { - var _a3; + var _a4; return `
- +
@@ -8402,15 +11240,15 @@ ${paintLayers}
${this._renderListRows(entityGradientStops, (stop, stopIndex) => { - var _a3, _b2; + var _a4, _b3; return `
- + ${this._renderColorInput({ id: `entity-${index}-gradient-color-${stopIndex}`, kind: "entity-gradient-color", index, - value: (_b2 = stop == null ? void 0 : stop.color) != null ? _b2 : "#4a9eff", + value: (_b3 = stop == null ? void 0 : stop.color) != null ? _b3 : "#4a9eff", fallbackHex: "#4CAF50", placeholder: "CSS color value", extraDataset: { "stop-index": stopIndex } @@ -8447,9 +11285,12 @@ ${paintLayers}
- +
+
+ +
+
+ + +
+
+ + +
${this._renderEntitySourceInput("entity-target-entity-source", index, targetParts.entity, "inherit card default")} @@ -8539,12 +11394,7 @@ ${paintLayers} placeholder: "inherit card default" })}
-
-
- - -
-
+ ${this._renderBuiltinMarkerLabelControls(scope, "target", "Target")}
@@ -8565,12 +11415,14 @@ ${paintLayers} ` }); return ` - ${scaleGroup} - ${targetGroup} - ${baselineGroup} - ${needleGroup} - ${peakGroup} - ${barGroup} + ${scaleGroup} + ${targetGroup} + ${peakGroup} + ${floorGroup} + ${markersGroup} + ${barGroup} + ${baselineGroup} + ${needleGroup} ${segmentsGroup} ${gradientStopsGroup} ${layoutGroup} @@ -8612,11 +11464,17 @@ ${paintLayers}
-
+

Markers

-
Target, baseline, and peak markers help compare the current value against reference points.
+
Configure Target, Peak, Floor, and custom reference markers.
+ ${this._renderCardGroup({ + group: "marker-target", + title: "Target", + summary: this._getCardTargetMarkerSummary(), + content: ` +
+
+ + +
+
+ + +
${this._renderEntitySourceInput("target-entity-source", "card", target.entity)} @@ -8636,19 +11508,14 @@ ${paintLayers}
${this._renderColorInput({ - id: "target-color", - field: "target-color", - value: targetColor, - fallbackHex: "#888", - placeholder: "#888" - })} -
-
-
- - -
+ id: "target-color", + field: "target-color", + value: targetColor, + fallbackHex: "#888", + placeholder: "#888" + })}
+ ${this._renderBuiltinMarkerLabelControls({ type: "card" }, "target", "Target")}
@@ -8658,11 +11525,140 @@ ${paintLayers}
${this._renderColorInput({ - id: "target-above-fill-color", - field: "target-above-fill-color", - value: targetAboveFillColor, - fallbackHex: "#000000" + id: "target-above-fill-color", + field: "target-above-fill-color", + value: targetAboveFillColor, + fallbackHex: "#000000" + })} +
+
` })} + ${this._renderCardGroup({ + group: "marker-peak", + title: "Peak", + summary: this._getMarkerResetSummary("peak"), + content: ` +
+
+
+ + +
+
+
+ + ${this._renderColorInput({ + id: "peak-color", + field: "peak-color", + value: cardPeak.color, + fallbackHex: "#888", + placeholder: "#888" + })} +
+
+ + +
+
+ + +
+ ${this._renderBuiltinMarkerLabelControls({ type: "card" }, "peak", "Peak")} +
` + })} + ${this._renderCardGroup({ + group: "marker-floor", + title: "Floor", + summary: this._getMarkerResetSummary("floor"), + content: ` +
+
+
+ + +
+
+
+ + ${this._renderColorInput({ + id: "floor-color", + field: "floor-color", + value: cardFloor.color, + fallbackHex: "#888888", + placeholder: "#888888" + })} +
+
+ + +
+
+ + +
+ ${this._renderBuiltinMarkerLabelControls({ type: "card" }, "floor", "Floor")} +
` + })} + ${this._renderCardGroup({ + group: "generic-markers", + title: "Generic Reference Markers", + summary: this._getGenericMarkersSummary({ type: "card" }), + content: this._renderGenericMarkersEditor({ type: "card" }) + })} +
+
+ +
+
+

Bar Appearance

+
Choose the bar rendering mode and base bar colors.
+
+
+
+ + +
+
+
+ + +
+
+
+ + ${this._renderColorInput({ + id: "bar-color", + field: "bar-color", + value: barColor, + fallbackHex: "#4a9eff", + placeholder: "#4a9eff" + })} +
+
+ ${this._renderCardGroup({ + group: "baseline", + title: "Baseline", + summary: this._getCardBaselineSummary(), + content: ` +
+
+
@@ -8692,11 +11688,11 @@ ${paintLayers}
${this._renderColorInput({ - id: "baseline-above-color", - field: "baseline-above-color", - value: baselineAboveColor, - fallbackHex: "#000000" - })} + id: "baseline-above-color", + field: "baseline-above-color", + value: baselineAboveColor, + fallbackHex: "#000000" + })}
@@ -8707,63 +11703,15 @@ ${paintLayers}
${this._renderColorInput({ - id: "baseline-below-color", - field: "baseline-below-color", - value: baselineBelowColor, - fallbackHex: "#000000" + id: "baseline-below-color", + field: "baseline-below-color", + value: baselineBelowColor, + fallbackHex: "#000000" + })} +
+
` })} -
-
-
- - -
-
-
- - ${this._renderColorInput({ - id: "peak-color", - field: "peak-color", - value: cardPeak.color, - fallbackHex: "#888", - placeholder: "#888" - })} -
-
-
- -
-
-

Bar Appearance

-
Choose the bar rendering mode and base bar colors.
-
-
-
- - -
-
-
- - -
-
-
- - ${this._renderColorInput({ - id: "bar-color", - field: "bar-color", - value: barColor, - fallbackHex: "#4a9eff", - placeholder: "#4a9eff" - })} -
+

^3sKhQzj$mEssVJpqhedQzqeCEfa7OG+IM@ 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 diff --git a/custom_components/spook/__pycache__/sensor.cpython-314.pyc b/custom_components/spook/__pycache__/sensor.cpython-314.pyc deleted file mode 100644 index 557015ef6a4f8b5d950bf714ef160d2ba51b4b54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1176 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 bGIGYed&UWyVMm&y5Zl_A_aRGQnCkxltX}`8 diff --git a/custom_components/spook/__pycache__/setup_helpers.cpython-314.pyc b/custom_components/spook/__pycache__/setup_helpers.cpython-314.pyc deleted file mode 100644 index b63b848d536e005c25c94f8fd340573129ff6f3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7347 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 diff --git a/custom_components/spook/__pycache__/switch.cpython-314.pyc b/custom_components/spook/__pycache__/switch.cpython-314.pyc deleted file mode 100644 index 0fa615e5993f7bbf7e0ba3c29b4a6e34add77b40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1177 zcmZWoU279T6up!E$R?Y#sTGS-XsB3?)uay!1x1ih+{IGrRw_z$SU1~dHQ5a_6KeWY z)EA%p5BlT}@juknf*?iu7O7Bt^UiE6r~|ok=FZIBd(NE9kClsnzRAU( zfonO)(GJF4@Ib&5@Q6#CoXE{coO3lt7rMflYdEGb5fe{$^NuB~8F;K;R5buvlK|85 zj^P%ZqA036rfWMTQBpYXmYp#%HUpkD1zuqaIu$YQ70*M>-kDtMCCT&Z)#}5f&#LXD z8-?{6No_xl6XA;}i8(II_aCk<(WbN1yz8#q!GyKi@x^Asx`-EiK8OQ~t8JgPXm7Mb z!&tBZ<)P^JI28#E1Cih+mv`)D5^qLZOY(@U?lrMEq7u_O8Cosmx=xt%j>p{VwI)yzE^y^z;eY8o@J8k2QW zbM1k9zv*DkmbsD)Wei;QJU2JTPT`zRWU4ElWS(JDm{+pY5F8?lpCzzw>{+EftGs8O z`(jnzj=vc{Dj2qT42EI;l;IbbA9z`vC+|p2f1jTxE450Rr?iy>l%_>WH~aXCQg&qm zQw(}hyv1ZQ=_EQoL{nr2tmAWXk&!58jkUv0FJxS?OQ-)kjb2v0qg_W{`u7M6Gs9i@ zdt+3$t_(Lg`SU2>DIHh^`(vkNP;yx}yGg6x32(6~=H%=03mAqPA>to diff --git a/custom_components/spook/__pycache__/time.cpython-314.pyc b/custom_components/spook/__pycache__/time.cpython-314.pyc deleted file mode 100644 index 85a123f634e62cd3f83f1723b3213afcfa495f8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1170 zcmZWoU279T6up!E$R?ZAv=vLG(9qJ_SWWt%P*4OZrMtA2x|V{X9oEgZ-8R_`GZQp@ zD(Z_*{s(>XhxiwyE*1nS(zi$p#W(LvVnH3qoilf4?%s3eWOksK2OJ-+f7<<=0r)N# ze+I7QAgB8n55NNfo8S?bI2n-{mpJ2UjxKbCHP>)VVIn4;?q(fJSmW?azou#c)Q17a zD!IeC#+cTCrI3ZkHJ)-5^%VqhFRYXrR92sBFKl9!)^ioHMlsFfsp<(uWlNt=~- zl4cZEDKHUgf$V*`MeVc6jv{NR;R80 z4h>_$I+TZ^-QrXvGz>(7n_S+p7m|1@+Fq1LWPK-ThW9y-xbS0viQ)V8I&MZH3i*QH zXgv3Wy?{u``lKzKHL2hXf&#}$CZ6?2ynhZ&>IMr8^IQ$6MpDDTfg3ER$(rdLq(WwP( zE|Mk{@?(gLM@VM9t8z8ehUkI!K^fOD{k`BR3&{76^B`I~*E zYv9~9Vf02fE0G0e{$~!+Dc*qq>0S)U;WdD9`UJrQjAO~Cf8;`eHpoWiY2SDNYx>2# z3vjESTS6=DV&#|QKwB~LSji)kkcAcVwib~}22B%+a=TUTc}J4t)zyl|3@pWDkyKxI zA1q?VmYISKMGRcFJ3T$khHy>?dvYs-^vuFWFs~G;AUHt+X9ckD99o4#t9WQ#{c4ro z72g(5bB1l6gJGCwGW_E51Fy=n^W`f?~Q)l zs&ZQA;$Nc-r*vQ??2lcRLCIy^Z6@`0BfQ7Tn3E67r!aIiLdX{wJ%)*6C?CVjF)aVk TN=N$qkv?~%yXPjFMV0vtGjt?z diff --git a/custom_components/spook/conditions.yaml b/custom_components/spook/conditions.yaml index 891fce02..ef71b89b 100644 --- a/custom_components/spook/conditions.yaml +++ b/custom_components/spook/conditions.yaml @@ -54,6 +54,28 @@ repair_issue_present: - critical - error - warning +is_at_target_temperature: + target: + entity: + - domain: climate + - domain: water_heater + fields: + behavior: + required: true + default: any + selector: + automation_behavior: + mode: condition + tolerance: + required: false + default: 0 + example: 0.5 + selector: + number: + min: 0 + max: 10 + step: 0.1 + mode: box quota: fields: limit: diff --git a/custom_components/spook/ectoplasms/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 20445849e0361d9c3f46b5437d9746e228fe60d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DPO4oI8_-0M VlZrWj#0O?ZM#kF=l0_^)4gipSH2VMm diff --git a/custom_components/spook/ectoplasms/automation/repairs/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/automation/repairs/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index c3f47b2be4a6e0aa4ccdaff85f353381eef5a415..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206 zcmdPq&d*EBOxI5?EiTE=jZekeFFitREkrnU`4-AFo$Xd5gm)H$SB` dC)KWq4QMjRX~i5s;sY}yBjarb$s!gY2LOg7I4A%B diff --git a/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_area_references.cpython-314.pyc b/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_area_references.cpython-314.pyc deleted file mode 100644 index 6bdf3c52eb6aa135a3d8665770f91e7026198096..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2633 zcma)8TW=dh6rQ!$_Qr`XN$Xtdq~0bFWSYdSXax@y!X$2OwROTaiKrl%Y}T7(sk7_O zjEfS4WC4Qm#NU7y1iZi_KOs>il8#g<60NGfrAncKC(g_|&ZVuyNPBi>&&=69-*+y1 zHXe&27+?Q5|H;D;LVt?I{(!X@%HCa&4P+pO=8>V4lwyDdGGZPm1&b`#nauHA3d`8^&8Y$& zFBZnHmdaP)LDZ}>Z&m7r+9bA#iM{MlM%J zWHC6?|Du*93>AP5u}&k*x{S!&2}2u(ZAKfA5$!?MBs*!e!@NVzQ$~z+8*!i|!P_MQ zzK84MK1W3Qt25%*RA-*PPg$N1pE3O5h=(6>&~X`;f{ z@sKpIK9sXOcgb1K9URQr6^G?MtGmwax{aO6=l^QdW*-4(b3Ad3W=jEE;u0eO7m%Xt z36vUW7R?9V`3hncp7o;-loiELj6hBS*Mm>QULGbks}q;2W#Ik?1SE%-JOTkng~>tw z;r_>2bqaxqt^FvWIVu7=XIspxRZY4|4}ZT8AeB_Ye13gE9P+oZ|<@e0L?|}wM2*gAT5tCmc6x(|rCW9yg2S4722LPE8ZXX*Ln)+Gf zwzLL-bTQ~C*}!^k+_Y9~U1o(OT`x^g9bnSgiX#Gm)&K>_VBK1?s`gkp8zfL15LZ`B zN=X8yfU8RF>XJltm zA6oxERE0NuF_2M?M2C0?@F;saOkNcoM$k}m0f>+g>4a@O3?%`|18IW=5eZA;LYxGV zRY(eMeyB*NNRQCOA|)lgt!%C6i);GAf{((&!WdLttyN;oRQNvJ42Lt#a1a?*XLhT0w5(7*`jWtS^>)d?S$KmC zk;I$Z9m(%X-8;erW@=Paw zY~KNHDDT`~doFays%KVsdd4;Hm`>GPtlIfjA1%mu=Dqa;CHC3H78EkdJDMvvXIFWm zMF936Bt>z?byCGOlR?{cp(O8@GSSAt=>dR_ybgk=Fs7otTNw5dXAzhc8$|ZV@b|N( zT`YwV8An$@e1q<**LPw)Td~udvD1yq+p+VF%R5@vmNv4fjcjYtEp2#H8{Q2CyTgt0 zu7>&sHq_KkCcBj>Y-S1@YXA0$Oyk;i_dsKEyXVq|dU7WjG_$!XJp~vay4H;4_^%s$Q|HHJeZ( z{>sQXaX#4u*Z*Y^W@X06dKqc94WfzdvfxN$j-ti2OMVo-OY*Q!)NVQoVmGKL%6-)L n2n{|$`XiKkfG$2p(+|+q$HC+S_0+R)zY^W(D*TP$F8%%wZ^giS diff --git a/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_device_references.cpython-314.pyc b/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_device_references.cpython-314.pyc deleted file mode 100644 index 7b32502e9027e9bdfbcd8311c64b241c1c5a18cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4957 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 diff --git a/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_entity_references.cpython-314.pyc b/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_entity_references.cpython-314.pyc deleted file mode 100644 index d78137ce0997a36453b4e500a15a8238a99bb0d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19621 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 KYeodG@IM2Psffk^ diff --git a/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_floor_references.cpython-314.pyc b/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_floor_references.cpython-314.pyc deleted file mode 100644 index e90f01b81adc9a73b2f87ddb31b7ce20a684e461..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2589 zcma)8&2Jk;6rZ&>{!09jw)tr5#M=Z~Syk+|6w0AOq={QwEl${`ks?SYoAo+by4iJS z#zhW7vH(Fj@el9^z=d-;!HsL8N+cbrQY5IV-cqGd!HGAsj-8~f#7H~)-oBZ)`+o2D z-t5^#JceL={nNrHzjYz>hfwwg=wc|l_h4=!9Wk_kbfu&e11yjca-b9}s!Ww~u+&uy zv5=J2Qn(mlQIJ)=s}w7Cv+iOK>ydh>6fY)NB7^4DGtxgol`ukS;V%sSk}R1)edrX@ zBLhf}&L=Jg{bekr$3{`Md+q4Wn&;it#oJ4bo=ZWE&&*91 z@IcpkyTTDB=8jckCY4cc%`T(uo$5q#VAK6SCb$gWU6 z;9Hk1h7HH*7{JwA<4fSeFq!o@=@6mMcB!R@EYwYg$ydYKBclG>@19m?n)_%aLY0T!8$P9J;Oc1~g4dXV+N z&J!|D`$VsuIdN;^DQa+}~S)ZPO93*+SjMa~2 zZPHH&k&$VP*cR28r&VpYqSgKQHL?dlMU$XX&33g;Mre+Yb~N1D)q=Ubm(CCszK@4| z7i*(=({ro#a=x=QZ&hrT|E%uXcj^|lE1&-xp%(iH5;V_KhqG!b;7wd&1mJ=cD7$G8 znrId+1m67$$O+HV=tE^i(G@+AS0Mc0Q?ZtZh{fu}O`hEd^igwO4L zomr>AJZ$c9g664UaNaVRS91)yO7{og!$|Y5!ys2{@E(j?Y_~AxgZq+^+iw|x@1XrM zoEIq2HK9C{^AF6w?}7zM41^$tpx~DX#doiOFoH6G!^eC003y>O?nC2Z%RY*sGunrAK zwD5Ik=*lHQ4;by_UeIL)w2NGprOa=;WGmWILt9+*VOU(uL8Fa!x@z)Jo6V|3yqTPY ziT8G#_>G{&h5!*2N;H_Li2rnp5~o^}5Or2(R=bI`-B3RE3Ua)Dxy-^@c!Q)#^6XYm z>bugnrLF$-W`Ay@Kev_EHq+-f(&wLt)l_T;snJ-AJ7akT(kr-I&i3;N#;&m{Dm8}j zs#mEyLWUZ?Q#YKJC5hqWA(S~boEjdO!x&dQ6XRCLo1FS!yj13)3LIg!D|(MOMtGn~ zc)BoKD9_-lH>Rd^tQW48ZqDd)_}28~_)KAvCt9_Et-Fu}%6s>8&xO;^@r(*TdCN8K z8n$CBIo5dlkSs{%^1*t?i2XET6Pgy~JuMCPSq@LOVaEQ0l<35`P8?h_7#yiChZy&r z(Gc1Y07Tw|iT7bl#o29QSWE0B$RC9TqD~~y{rcz@s+dG=Q4G^J=%HHPjt^|chd1KG z&8u7S3(c$Bk=SPB%tqwQPB73PYL<6G==jjO+P9s_Ze|J_nZml7-a49T-q`9NZcc3t zTwYg?ZTAm9P>26gMVEXL*(*8!-QtbqY%dX15GkzIq3=TqiC`ZOu|~~ymx(A2J_x@f z7Jvgk6^6@lotE#XvG1UH#d2yEp+x*?k-XTSYGLaCg$TE@9%Mrdw@wBU5zA#qk$@Zo ziAOH`6nl@n59>sCreiSe1QkVjh>kx-Bae~x8082;(6$#5?en~ K_#44p#{D1q)Vg~B diff --git a/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_label_references.cpython-314.pyc b/custom_components/spook/ectoplasms/automation/repairs/__pycache__/unknown_label_references.cpython-314.pyc deleted file mode 100644 index 2f5a1e2f8276e0d4d48935ea65c4be4cc278d982..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2589 zcma)8&ulYm;w5Q87K#m&mL5>mkQi)1O}d0gQJYLQ>rJv{vukF? zR8CdNYO9u>`VaIEXfHj}Q+w+*5mFOILW!0R6`K=3~KZLS7Ko>*Vxd(F#>4>2@q$?Gr9AcrIkVBPlS!Jq}!SVHQNO0t|{sT`VBPfPy@)w&VN3V+?;FU`_9)Q?Ug zJvxB&*lg-T*k8ta_4o+N_pBbiS@*o#+Nd_;HHfzCt=Lu}ukaqjbv#)9QwVlCN}k!C)`vj%OtCSJ51W)Xbb^*(j6zsRmp zKHyuIEQSro=@`J(TVo60!Z4Zqu9C}a)*Sa%>Yi)4?3OD%>DHn}EZ4NCZq*H&z_#$1 z+cX0NJ!w4<#k&cFgT>;GPy>yDG@O?bu zyI375nx4C8FBLmmi&o8M#m^e9eWzhzyY~6NQEIV|fS_64dyrLA0dL|GBLo*vpzLHp zXrXB|7kc+ANKSZ`MIS25imvFPq5|QEpNh3SLM+xGE?29-^p6NAjVyWuGK>nB!+d7< z>&ym)%){nxPS7G18Co6$Pn>>MWknI-6d~gpLg}s(x_zs$p z;k-bhu5sm=oPS{ceFrQ^Vju)DL<)Y1P;%!o2*W4`IDEW|4=C#TM)c`1sE!!dsXdQ5XqSef%=~#toK1`q=Ag(SOl#(=v zfUHU_XHnvMiim*t0QL(K$Zgbd#|t0X2ga)xwQ+e00sg67ys!Qe`#JVNyL?amJ$m9F zs=^z-IFVEK<%f7E^FssSb@`!(b+s6R9O)u`u#88b1wcC>F;K`MF{Q*LN;R*Lez^I` zB7-6W!Zrw#s6r|#wuN4xNLT!snC_d~;`ZyZ)W}gVn{CKJiNGbvTL9tzkvFYOim(n0 zAX@l3Fm&ajNDmloa?k6s0@_6`%Tn^&F4>y4(A4JVeHiBF3(#m|ovxZb&}Q>05pO2P zVd8xqCw?OcvLQr7g%S-WD&jvCq{PV}B}APynAL6~Z8wyUz9KnZzg)6#2HqgkBzQqcxf|n}XJQ<5yonnhj8&>UQiCJRc17)`+w{rQ7TxmU5T2r%|hjXp#o0*~3jm?2e zYwD4$%+LdM=r2`t$rsVxlJnm!-dN4|5s?al!o>#keV~wt?BfyEtlRDq5yim=;djIW zaNwuHa9N?#@_icn_nVh3r*08S#Ge)^iv7I-rv6`ua4YLUHpK4WWFS$oTy_)*$bOJ` z^pc-q?~(Umo#@VV6sGO4q9_m1(Z^``G14BR;v@9V6Eyt@U3(JleWV_H9yzYW*AA8b JMsSyL{|BsXH_IhWIYx^PurqJw zy_uc)z0YrsWzz)OuYY&vr%sBHzvCooiI7mvehWm07(|de#87HVH6~&Oq+_*sRTZkF zI==8?9zUrkWMm>X;alYsuPpH79Zf(onzN`)d2wmr)Zrb2;xwxFi#_GRG0-zjf%!AwiY_f|aNi2YT6ujPBTCvJQ4 zM~1y)GdzbIcFS}aY>St@eXED89Mo{4oc#fa4rvgffL>#FW<~r}(5+A#iS^9$8$w#7 zjF`}Xci}}|Bi$HnWWJ?}tPvMu@Qs6<{-T_iFjUZ6LQEP-anVRM@`iQ=#EcQpZF-ux zIWc970-cfcw2=jROwu#PxR^C^pz(=dR3Z?*@Zd(MekR|m3+C8d7rwsZcul<>qN1}1 zmZlzQLU%lUU;%tN_}XAbd;22vX@5W8WC&lNMaLX0l`P-eaqg7{+e>!S5v8BEJqL73 zo#rq84dUV*P+lV#KW7k@0>|NpEe0Q0w{kWQM2BpVJF)NoijXF(&6A%fyNaP0v62E9 z#6L&bAjxdeW?rDy!3aJjpzY+2&%mfT9(g%Hl@MLb2?dmp0f-$;ZyQK&qyp$R-VZI6A_MQz#$R^B(R}> z|B{gG*$;uZObUR+yAeWw$vVFLT)WoeZxOH0H2|lTxT6$f2c=ch+O>7*Ap-*dq;#?C zaIgg40w5qz=+?gF+ROD~oMB81)LoNvh6WGNs@!&WB(PW5EOMu5=&PXIb_<+1Iwe>d z!s4ri`mlTs@B2q#f>@A*@BpELp-;0suBh9-@3Ltiiwc{8Av72HDs~CQ=AipFGH})*sJKPc*WxD| zr#L*^lrx5gYV{$92`U0^m^}+OZYk@qaK6Fal!Hqf_nnsBbhx?g+RY(f1h7-KC)YoX zu>x#oOVDxr(Ul(fQDN#q#Uvh-|gEA?{6mU5VDD~o{K%>+%_b_^!IBOD(D4ng zVOPS33Oy5Eq`)b*3>^l^6aD7)CX(am1# z+&IxL9BZ#1X|H#+#jd7zwT07>#8k3VKh2O=UprLw?#%q5nm;KNj|-KfLgi4M?@ksv zH@j1MXRSN^#-V!gB){-TU3jWu&U>ms(0ryv&K=$dZ&5E!vK1K70(RPv%)tT}#vrfSLm@^u+QYkB3$;d7kY-v2jk%xgOqOU5Y157ens~lU=Hp_Sq;d z$!sCP8U`@X-Uu!AP4**@!(hqZgzhx1D9UGK_A$BqnCOp5=~+xwVowNk&oUA9G)Vq!wZ#v5f~*P{{gpWL`eVu diff --git a/custom_components/spook/ectoplasms/automation/repairs/unknown_area_references.py b/custom_components/spook/ectoplasms/automation/repairs/unknown_area_references.py index a12cf641..27388fac 100644 --- a/custom_components/spook/ectoplasms/automation/repairs/unknown_area_references.py +++ b/custom_components/spook/ectoplasms/automation/repairs/unknown_area_references.py @@ -8,7 +8,10 @@ from homeassistant.components import automation from homeassistant.helpers import area_registry as ar from ....entity_filtering import async_filter_known_area_ids, async_get_all_area_ids -from ....reference_extraction import extract_targets_from_config +from ....reference_extraction import ( + extract_targets_from_config, + only_in_disabled_steps, +) from . import AbstractSpookAutomationReferencesRepair if TYPE_CHECKING: @@ -45,6 +48,10 @@ class SpookRepair(AbstractSpookAutomationReferencesRepair): # references nested in some step types, like repeat sequences. if raw_config := getattr(entity, "raw_config", None): area_ids.update(extract_targets_from_config(raw_config).area_ids) + # A disabled step does nothing, so what only it names is left out. + area_ids -= only_in_disabled_steps( + raw_config, lambda found: extract_targets_from_config(found).area_ids + ) return async_filter_known_area_ids( self.hass, diff --git a/custom_components/spook/ectoplasms/automation/repairs/unknown_device_references.py b/custom_components/spook/ectoplasms/automation/repairs/unknown_device_references.py index 7d95f33a..c170f616 100644 --- a/custom_components/spook/ectoplasms/automation/repairs/unknown_device_references.py +++ b/custom_components/spook/ectoplasms/automation/repairs/unknown_device_references.py @@ -8,7 +8,10 @@ from homeassistant.components import automation from homeassistant.helpers import device_registry as dr from ....entity_filtering import async_filter_known_device_ids, async_get_all_device_ids -from ....reference_extraction import extract_targets_from_config +from ....reference_extraction import ( + extract_targets_from_config, + only_in_disabled_steps, +) from ....template_extraction import extract_device_ids_from_config from . import AbstractSpookAutomationReferencesRepair @@ -91,6 +94,14 @@ class SpookRepair(AbstractSpookAutomationReferencesRepair): ) # Devices referenced via device_entities() in templates. device_ids.update(extract_device_ids_from_config(entity.raw_config)) + # A disabled step does nothing, so what only it names is left out. + device_ids -= only_in_disabled_steps( + entity.raw_config, + lambda found: ( + extract_targets_from_config(found).device_ids + | extract_device_ids_from_config(found) + ), + ) return async_filter_known_device_ids( self.hass, diff --git a/custom_components/spook/ectoplasms/automation/repairs/unknown_entity_references.py b/custom_components/spook/ectoplasms/automation/repairs/unknown_entity_references.py index e1b6d6df..cc073ceb 100644 --- a/custom_components/spook/ectoplasms/automation/repairs/unknown_entity_references.py +++ b/custom_components/spook/ectoplasms/automation/repairs/unknown_entity_references.py @@ -13,6 +13,7 @@ from ....action_extraction import ( async_extract_entities_from_value, ) from ....entity_filtering import async_get_all_entity_ids, async_get_all_services +from ....reference_extraction import without_disabled_steps from ....template_extraction import ( KNOWN_DOMAINS, async_extract_entities_from_config, @@ -291,6 +292,21 @@ class SpookRepair(AbstractSpookAutomationReferencesRepair): ) self._known_services = async_get_all_services(self.hass) + async def _async_named_in(self, config: dict[str, Any]) -> set[str]: + """Return the entities a configuration names, the way this repair reads it. + + The same reading the report is built from, structure and templates + alike, so comparing it with and without the disabled steps leaves out + exactly what only those name. + """ + named = await extract_entities_from_automation_config( + self.hass, config, self._known_services + ) + named |= await async_extract_entities_from_config( + self.hass, config, self._known_services + ) + return named + async def _async_compute_unknown_references(self, entity: Any) -> set[str]: """Return unknown entity IDs referenced by ``entity`` (incl. templates).""" all_entities = set(entity.referenced_entities) @@ -314,6 +330,15 @@ class SpookRepair(AbstractSpookAutomationReferencesRepair): ) ) + # Home Assistant's own list includes disabled steps, triggers and + # conditions too. Something parked that way does nothing, so what only + # it names is left out of the report: whatever this repair finds in + # the configuration, and no longer finds once those are pruned. + if isinstance(raw_config := getattr(entity, "raw_config", None), dict): + named = await self._async_named_in(raw_config) + still_named = await self._async_named_in(without_disabled_steps(raw_config)) + all_entities -= named - still_named + return await async_filter_known_entity_ids_with_templates( self.hass, entity_ids=all_entities, diff --git a/custom_components/spook/ectoplasms/automation/repairs/unknown_floor_references.py b/custom_components/spook/ectoplasms/automation/repairs/unknown_floor_references.py index 70c0e7a4..a39a2405 100644 --- a/custom_components/spook/ectoplasms/automation/repairs/unknown_floor_references.py +++ b/custom_components/spook/ectoplasms/automation/repairs/unknown_floor_references.py @@ -8,7 +8,10 @@ from homeassistant.components import automation from homeassistant.helpers import floor_registry as fr from ....entity_filtering import async_filter_known_floor_ids, async_get_all_floor_ids -from ....reference_extraction import extract_targets_from_config +from ....reference_extraction import ( + extract_targets_from_config, + only_in_disabled_steps, +) from . import AbstractSpookAutomationReferencesRepair if TYPE_CHECKING: @@ -44,6 +47,10 @@ class SpookRepair(AbstractSpookAutomationReferencesRepair): # references nested in some step types, like repeat sequences. if raw_config := getattr(entity, "raw_config", None): floor_ids.update(extract_targets_from_config(raw_config).floor_ids) + # A disabled step does nothing, so what only it names is left out. + floor_ids -= only_in_disabled_steps( + raw_config, lambda found: extract_targets_from_config(found).floor_ids + ) return async_filter_known_floor_ids( self.hass, diff --git a/custom_components/spook/ectoplasms/automation/repairs/unknown_label_references.py b/custom_components/spook/ectoplasms/automation/repairs/unknown_label_references.py index 1c72e347..990b0e6f 100644 --- a/custom_components/spook/ectoplasms/automation/repairs/unknown_label_references.py +++ b/custom_components/spook/ectoplasms/automation/repairs/unknown_label_references.py @@ -8,7 +8,10 @@ from homeassistant.components import automation from homeassistant.helpers import label_registry as lr from ....entity_filtering import async_filter_known_label_ids, async_get_all_label_ids -from ....reference_extraction import extract_targets_from_config +from ....reference_extraction import ( + extract_targets_from_config, + only_in_disabled_steps, +) from . import AbstractSpookAutomationReferencesRepair if TYPE_CHECKING: @@ -44,6 +47,10 @@ class SpookRepair(AbstractSpookAutomationReferencesRepair): # references nested in some step types, like repeat sequences. if raw_config := getattr(entity, "raw_config", None): label_ids.update(extract_targets_from_config(raw_config).label_ids) + # A disabled step does nothing, so what only it names is left out. + label_ids -= only_in_disabled_steps( + raw_config, lambda found: extract_targets_from_config(found).label_ids + ) return async_filter_known_label_ids( self.hass, diff --git a/custom_components/spook/ectoplasms/blueprint/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/blueprint/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 93f682b7c268ed41123cad7b14c2df71534945ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DPO4oI8_+rfN diff --git a/custom_components/spook/ectoplasms/blueprint/services/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/blueprint/services/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index be0e42b460a9f368c00792c4e69fec3acb979404..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DTZl eX-=wL5gX8Ckkg7efW!x8Mn=Zl43b4GKn?(fLpUh_ diff --git a/custom_components/spook/ectoplasms/blueprint/services/__pycache__/importer.cpython-314.pyc b/custom_components/spook/ectoplasms/blueprint/services/__pycache__/importer.cpython-314.pyc deleted file mode 100644 index dbc0e91689b0a2199b736551343ecf0ef1513141..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3825 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 R#0O?ZM#kF=l0_^)4ggtOGRpt} diff --git a/custom_components/spook/ectoplasms/cloud/__pycache__/entity.cpython-314.pyc b/custom_components/spook/ectoplasms/cloud/__pycache__/entity.cpython-314.pyc deleted file mode 100644 index 8bbd64a410eec128959309e43164d2ebcf08f2b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2571 zcmai0U2GIp6uz@F`@h>?+JcnsP)eJnx{HFK1;yHu?glJnkTwL$IGw%Qj?B(1ckYsO zdFg{OnE2p>4?dzX{=Dgv`s9=EZ9=3>AexxQ_*T*ec=Vh*+igL`oAl1PKj-ZE`Mz^I z+>%ZZ7+)Ov<^AJfLjJ(T)_^q*Y2#Cvmxx926HltDRElnV-1ge(`sg$R!d zSt-T}aUKU**5qOW_9H?L7n6k)PZiQUoguTzkg!ikDNRT(+KUuh3az|V*oYR}3hlf- zL%PU*qQ!cM7N1QWl>-&tp(PHGY_i^Yq3XKV)R)vtZjGr6ZpEUxtmG#R$8ovAE!Xj2 znSS@u)MA5V05rfZ+r#9Nok2(!Cf&f$kXIKAlr0Ela4piogp)A4yI3xE2tev zv8E)5i>o7{!_CwmSAz3t;$XqVxYE3t0*XzCrdqa6w!bOz3V7i(t!q&zV65IWDt^)`VU@}jWuBj^7*4Hhm;eLHe8n*;)Nc=pUSopMdX3q>q)%q!e%Py3;Z{u7 zEpQa78qr178TKxQx$skCXU|<6*8{@)?YhA^(@TQ%pj38!`5ImHTZ8?YV_mON-70}s zibw4-!(54A{kRUc4bRhc4-F3v4lc1>c$&eV8T>yu1r7=*T?BLVS7N(w zUtW&wz7uJS5E88I1>wI~o0p*2GJ`HmSQ|{fGUvKB z>i`id-3gz7eL=Wgcn#w?hzEFzv{1r{hd6z9RD8BQfpamC41xfkfba|fBp?9f^n}!W z5njcvl7TKWg}HuaOf{jseXv6plJFeeCd9XOA$qAkq>0X>T4hy0NcAYgmjSvcqpESk zuxw+_hSw#``aso>H@BFI>y+i`wmUyhO8_ge0?<0pj%XhII8O< z*VOd@{IM!?frosNiHN#@BKY{4f>hRAjZ5~u1J>~pk1$*kcXu9jYB+~LY1W4PEL3)Ie2u&zXW;@vt(8Ey5HEbc^B4*r=@Wra- z%rk_cKd=DrA5b}XKd}up9+8|VJ#0807wv=X$NLALzP~_ih}O$Bx(K85V2CY>rUTC% zD*KV(-C+pT;Au-QVDs4OeGGBbgPaV)#n$7;P6h>glbwPkrqav9 xv@T1MbdT&=BmHYcT_Y0@$jLRb_c!wTpK{v+<-nRUx*6$~61UGhBJdFg{|0jOh_wI! diff --git a/custom_components/spook/ectoplasms/cloud/__pycache__/switch.cpython-314.pyc b/custom_components/spook/ectoplasms/cloud/__pycache__/switch.cpython-314.pyc deleted file mode 100644 index d5603d50d895f9733cc60071da68f1559f808cde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9098 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 R#0O?ZM#kF=l0_^)4gg+)GU5OL diff --git a/custom_components/spook/ectoplasms/group/repairs/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/group/repairs/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 62aa3bad6fe45ab080ae96f83e32bfb722c228a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201 zcmdPq7&-6`ox#|3pfrNKvN#sFf^RVkA?xJrsEw&z zc6OOYIut@$1W7NkdvSfT3>2_)Nt{EGOAqa#=v9oJQXaI3fZB)L6v(NOTi@(*MLLnY zz|Opxc{4lj?|W}Y+ge)?JRf|z{7%A$(0}N_ew-G;?0*QvCK8Z=mXW}unG|cV{gh_Y zjudBbChbT&Q!c}0(p=h|@)%x|cBWfWKEs#t8-5sbf-BvcYBSnQ+MPa<3K)S@yU}ir zJ?UU7WQ6)r7!4r7dlU&ROQACk>mEjj;5&w5{*BP(vZh_*Px1>|1@o)gnj*(z4Dm^- zsu_}@XsQlFXAx_aG7z1~TyicezA=9(Ge1YEi}TZoWCB)>$f}_j>mrtmif-Wb7)v@5 z)7fbexYF4LF>`S`J2Nj_B5g^NF;kL^6%DV!R@cS(+1aE3JNhJjUCoOtO39G1cum!Q zt%~(?m4Z$p)^brcM5$D2T7g4C(^uiMQr<8H66CU^;JmGU+OyVx+5Zg?n`j9c3|N6( zZZ{kPV{l8(jNjsDZVk7<8lENZQcI?@dETN=v4;Nz`&tEu(FX4kfdkDukD?OTMsOMJ zf*bViA-<+-Zk)4>fXz$u23BO9H#B}lQ44%UHPyhc$!k|-%$wSv>x#;oM$zLWY!YIf zQ?xksY@UHz(w9C|daymS-wVVh%AsZU?C%gNz+5kSlUZd1MqtMoxSQh}%1c~WHY!*p z+?=M$pCD=`YA(sp>5+rv!gyX&SCry-zM>o2nwZzt%D|}_`nXPAYFy46TDc_YYx=l( zzi~@Eef$UajF;DmUli*OCyOG9H0dVZywVR})~+uwYRDYL`%>7r>^fjx$DYyc&hnH)l8 zN6wK6Hul+6CTvrU{!E=T_fEoI4!(Uz2xPh%du%GlWt!x$XYE_|qc|!%;UE{0*7s*4hvu48}l|*c3wKSc+4KtT1yx$Iln>8Kw(^ukXN z@}elE7A%T-@5>H_iM4Hnvsft>0skZbKD`Xs0+}hkanu4%g~FjVs}_ICAJ^xIuPHka zuQdSJgdEe-lu!acN--WAUc$i&3AZZgI^hbEAz>Gt za6s-Su7b8EDJrHAL0s=jrHZVRU|G|3Xq|9P zQH#V4=SzlxF~uv~LmMwWG-xT?lEFHVaPMdeq(fC&lVfhe>2hfWQ*{$BsL3p4bmFl| z;@rwNwjF zei@$JjSkhKr>fCY_WYGv`1F_I)8>4<8jW9{eLS4l>4@A+-$>sZe(GRHg5b*?!R^qo zXS6p5AW*og)-hV`7~L5dt__^74xHZYJ#mlw%=?M=+q&!TJ0Jx15gUsFd*9Gj?(UU4 zS9W@b?{(H?`Q;f$nwO|^#gq1Pr zvx@L~VJ#$Q)0eZtg7}LJ)Z57fX{%#|D8CE7rjuTKY!z%23-P*`UzOCNTp;cCD#SaM zOPW-GD!c*}9Y6peObIwkvZRWYvK0q9ra(i&qNvmVgS<%NN4Z)tWF78J;SEPozbg&zo0$>HZ$R^?I|5D?RUIGms-A3u~KZpA!>O%o4H$ig(ti4$}Wr<*_* zsIysrIRHZ@=2(E|t{2Ie7O$52o>wXHOj*u!9$y9)T5amoPxc%P!#qM=Ptf2K^wM`M k%dp?M5OelxlzW1DzeaEU8;w47on<<=gUA1ipf?rxKhgMoqW}N^ diff --git a/custom_components/spook/ectoplasms/homeassistant/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 1ca9a59453a45d09d149af062b7aff0499812906..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D&ryk0@&Ee@O9{FKt1RJ$TJ YprIgF6>|WI56p~=jJFvii&%gh08Mu`KmY&$ diff --git a/custom_components/spook/ectoplasms/homeassistant/__pycache__/button.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/__pycache__/button.cpython-314.pyc deleted file mode 100644 index 4da384d310382b68dade5a05ba177baaaf9bc382..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4139 zcmcgvO>7&-6`ox#m%pMUO0=jSi?n2^r7bVrSg~YRQA5d!NZ6s(m0}>U>BWj%S&T_8 zJ3ER^wFe<7kZX_4sYZLL(E`2ZUi5Aw2E;6E#6V#bxk;cMH@ChwyOb&0N_*-Gdi&yH86P&-#HER6p=B7E6$V?j>rsvqNl%^P${u zc7%;^KAbzA9c81S!+V0LUGNu~5glbQ0+Q|lqV^Q}3*l+Wkxsiiu|y#O^Sx?R?JERV z-gm_vuR?TXp=0Q>YCmX<&B)G>>o}$ksBv}hi1kCTez?8qcE2k zAeA0=lAksS;}eBgA=ExCn^4EbNost%Z?$fkx07d*ADde=xoK9*hLRHOkY1~qOlM`Y zX2CE}(wSbY=$4fd?XI0rWsB)G*0Bt(70BGt z%S>Od7`A^=uT+pjBo@uuM)?+KvDYuc~2^ol#vt!W!&YOzYWX4FiW_GwzlECK^F z9UcdQn5Kb^Sh=X_jL|Y2)UY&-4gy{+1kYYb7r9~5#Vw1ORjp`N>n5mWR@y?BP8&sL z*1->}R(ca|4G!s8J?>sW<2VDB>gmj1>X$1=$Z0Z$@|;nc0@XeX@El={87PMX56*o z3uIdMc+)eRH=%zQt z+20aU0@(=prLZZef-0&WRZ_jGtol@cT7o1M_zsJt?bXn$XcT!_0}Imz?ZrXV@j|^+ zLMcx=w2!Z%2HT=FTgpQa^DqZbmN!gV)foqG%<)c0C~l&F^g@3DpDw|_g>Je-UJ}0` z?;~l3;(H!=KMfD;dEvb$lfkJyA98*o2loTOafdB-2ATv~J}1Q6!eTx~5_oSBin%aA zZh*vjdPuv}PrFzppq&{f?|RBY%Cp_SYOv&1-Oaa_QL{{{9CAb4+yno)zu)=6!ux#|AHoCD^T4M(^a8x-MI=85Vj<>}lWxB0ePd_34D;Nq z?Y;KH#5=H$8$Maiw>c1AcnsL5f7}t2&Zzka6=2X|z=+<9fUEc7y=ne(V&cFH zVG)A)IU!xG3NUU={Ldu|iW_>vYfsy=mm#t~0sVJ{9nTeS5aLaRi~GnGna7g2-MfM< zMv`qBZl|K;0hS!7`;i;~1< zXUC+x>a7$f8Yng()Q1G?ggtrW`a=`18V`z<(e67m3)|EzMiEzAcEX5u;TG7K+1*96 zQh}Zh@22SMMcR#1kTr=t@`f5{*m)9zp4P29uP>p7Q=KLbPiz{MxCLAphR;2!imK z3_c@c&&Y{y$;3-Z62yH11On7Gd8&!Qy#{YW_)rte&%8P z%)Tf^gU`D{UU@&xSN1pKN;8^nb`8H}&ED|ff*|hr9t7rFWKa-a%tA4Je#|Fad@%9e MSHJn50LQ)bKTkmid;kCd diff --git a/custom_components/spook/ectoplasms/homeassistant/__pycache__/device.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/__pycache__/device.cpython-314.pyc deleted file mode 100644 index 1b3b715d96de1bb3f7cf5b581b2b3101c4774086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4566 zcmb_g&2JmW6`%d$$C45$$?}InnH$MwO3Ni3f5dL1IR}hwkqJFF^b%%NQKiLioQ3yONz4M zq(BGQ**A}GW@p~~-f!M;puJ5dP=0;n4?kRKBjoSc@QdII+~#jVSRy%MllMrD8|6lL zn@^(5j|wBAEizdcl|}+~fXU)$a3o}hK^Ah-s5}y}BO_5eI?`shjkMeCN%C$W7uZK~ z!F{A0Kk0qXj)67=+Hi|D4q6$skrr(SXrrKQYteS*+WSd5wm2|k+$t3eW!5NH4QeUH z3ROlbbB1!xvPzb%nYLmX6iuv5nsXwWX;y5_E>%nmn!-7A0bZekRxaPv3h#p))7gM3 zHD;hcT}X>gpJpwX1$D--)xvD4tgEhxrNYP5szwddcJ?=Iya7N@$Tbb&D6IEdq%(;fD(heB63m&Tn zswMKAggIpcs|P|IUyo$g+Js*TTQUk*aMtiho7X^CBGbMrVDTqC`8BkIq63T*pr62H z?g{Gm0TblTc6QAg=aIkKE@xcptvtXM6N{B7QS_6IzSRNz`nyK70 z7)U)#Rf;X3vlPulyfi>u0U0>Juau5!desW4E zaSyq1*vf9`nHC&AbBqC)9)Pxm{!A%LE{?;4Glpq=P^D*`*SA61b&#c^`yjAX@L*=J zTmcrh&Sn~225`7W^jNou5h#*K6-T57S`K0KpV4H-;0zZ;3U=n+R(ZG6m;K7yO zz4Ije-MRqHEfFU6Y0vRTZ>{uP*p#4Amq1-7LCFIt(wN7!0u_rcc(dq&H%6Dd5d`VQ=t4ij-b$OlhoxxJc4qgZrOM~I ziZm%1e@6UFN_qZUV5|R;u{=M4$kG`pirAT8}EIsUXa*Cd(7gOHWH;Lo)x=Ederrjc4%d6T;; zTp~I^HUX%~OTaM#;}{GSR<4Ua2Q@in7-oe`j>Sz3#(-xb*2K%bEas%k2VVW!qxp06 zM|5MEc}%QGW6K26!hZIo1B>T}N|tuB45y-h3?dVa=AxAFx40W`Xp=d9BGR-@D zNvAj#9L5LX<+S^p<8qKA7v?D@fN*TJP#49pcLv>K*jt}}WP_8uv{5Y;RnstZL*IqJ zkan%JPSb%`%ZDg*U}LN3;yXWL)lqy|e?|$9NcnEtOIfiy4SN3O00D#s~tyb9Y-E~ z`$@+ew*zaDjyq>RiFUHIrneU9eH=Nw9_?R?C)eUV>xqM_iQ~1z@zuoXTH^FSgJN6z zR+MxfdlY!G=k)FHT4c}TNCJBQBi8wmwMAerKJ9xO`fdr}<4pOkzkx2qy}_rDNh7-73b)mpTpCGtXTccULh@7-7lDeDkId!KZlxg&4MBys5e#gE^* z9a|42@5lb6uZAW!1(FzGC>u|AIYCv`D+N_`WK}KB!@U)6l9+Ef0ehhenVx&6?QFS% zW(pN*P!X-VTYvEW6L4L43=JY^%Wk)t-AwP;HH-P1?E&L8#yZS3EU?%VU3Z2C=PLSq z**HswU`Wi%tRF#D7dVdlguMQY^gSbcpOM%8N=`o)C63=DP=QlnPRt hGacMTJRROoQcMRKl&8b2>JrD47?4c+Ip2uQ{{n+J<_!P< diff --git a/custom_components/spook/ectoplasms/homeassistant/__pycache__/entity.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/__pycache__/entity.cpython-314.pyc deleted file mode 100644 index 3b0735fc95dac8a2621f35c0fa5136ba51b6034b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1754 zcmaJ>L2nyH6rR~#+iS;8TByn`sW?Cm+=>#lJ@RJOP7@?L($2n__vX#_zW3hD zj23bT#`mxP`q>vILVv2oaHJ9e?}i}skc$LbLoTl4njws$l8w4qvxKE(v!1G@MOw>N zJyWxV4YKK`>e*UOii1z+|Bm%K1j~`epQSP&#qx|~`qGTSx!@IMv_JE9QNMInuSStwA z#lmup3DiIrv1?AC&<>A?lxvA}1HFw!1_DaS$r?ai96~*C<<{a7Z3PkKju$!CJx-k@ zbgI!gt(ZCLsI!08VKnpvZ93W_puFN^K&3u4EcgH}-gQ9e0jM=&{wsuUX(_ z+r*FC9k7f9uX2^TD)mL&2|eEC)k9gTCV^Etn=((xAfJ>FdFKBZsvI;s0=M%bOg;1z zWmC)eA3gV9ee1Cn?ucY#7fg_^f*Xzu2tYz%_cst0a1X5xHRy`bK%YXnH-MbE0g*+t zI%oidR?|b%RYi^-m-qLEI|yAg&MR__Rozr--!Gb8u;>Pa_-=q zN3$mQ*~j3fAcg6zXk$$R#N?dh5Amz^KZ{a(6sw{)pLHP@ekaCCZmqO_My zm2$VOd-kclJUV@TU^6}U)5e_>zZ8Ei_UG&U;s>{_`}RpT4*tsqJEd?rO12dhiYutx z93kzv*$tHpHNDaG!hvRtkXFFB2%*8F7}Oa;nz0Y`>EOm8Wc8Z)nxEi$S5RO<76yd) zGz`@{#!7Ib?z?vrW>k4GHL3h^Mb@*g(L-zbsiivNiEac9SsSgC$JlFNqj0vmD$Q`e zZu`kYomqL_!#bz(*nia#7fGVe9M-JU5Q>9W28n0%2E52@Q`d-0!9xKnRVp%_G(S_l zsNWe4kf{_VcvURYUP$PChh2cB>PN2dKQ=MO57E>PD(#@zM`-2|TG&A+{y=Z;rSo_h MfBWSVr2g8|zbkslSpWb4 diff --git a/custom_components/spook/ectoplasms/homeassistant/__pycache__/sensor.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/__pycache__/sensor.cpython-314.pyc deleted file mode 100644 index 61950e9fc4202753ca0d1376a1b1af00c5bfffba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32394 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 list[str]: + """Return the integration entries an action asks for, by ID and by domain. + + A domain without any entries is refused rather than skipped: a typo in it + would otherwise do nothing, and say nothing about it either. + """ + entry_ids: list[str] = list(call.data.get(CONF_CONFIG_ENTRY_ID, [])) + domains: list[str] = call.data.get(CONF_DOMAIN, []) + + if not entry_ids and not domains: + msg = "Name the integration entries to change, or their integration" + raise ServiceValidationError(msg) + + for domain in domains: + if not (entries := hass.config_entries.async_entries(domain)): + msg = f"The {domain} integration has no entries" + raise ServiceValidationError(msg) + entry_ids.extend( + entry.entry_id for entry in entries if entry.entry_id not in entry_ids + ) + + return entry_ids + EXPOSURE_SERVICE_SCHEMA = { vol.Required(ATTR_ENTITY_ID): cv.entity_ids, diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index e82b0823142a271e828d353b7d0e311bc76bf614..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2298 zcmZ`)OKclO7@qa6-*&Qbp6(;wq@~oQwW~HF2ue_79K3bwn6>LRQqq-cZ{nr)u9+RD zbx#&TB0ZIdL_tC#JfyZKI3g~bxCgnYVJT{)szTgS1=K75?AmG4!bslv=AZxn=YRX9 zo}drG`sT>b?~J<Uq7IW!XJ*f5x`#;wEqfcgdRmoPw9tl9DJ^tER8m)oGMy8ak;!9>6Rdw+;hnp#r+-c;$VB%0SzP z-exL{#7GW_l{m>Mxg@vbiE|Jd?_V@3Y?4 zVTClXW^x%_Q|=(})U~MNT9Fvkl5z8F+;%ZuY8XVX$tAs32TqL`af2oyu9S#guNIA( z5r;%5+wQ~-+e;%Z%c`alS&r44We7zx=^}*cT!G`!OcIc@fU-1S&#Wws`ZKHG?7pU0nM{u(RA94! zpIy&!P9hi>1!n-AaMWG%!7hKn)zw&Gybw*xDrj`D>c73|=}PYO20YoHFd2>`lyOW_C;r?4&w8xED4IAw!l@OpDI+by{C78>V*~n{3TkqVIKPhe1o_ z(4m6@U3i!Zj@!n7{jwDxd7Ig8)&^Ga3&3dg$|nF?=r8oZZ*9YI=+&)A3C_2Ylot1f#~BP8WOg6_^f!>!~4Gse;)q!jeFsfYvGeC;{8Bm zmHR$?FK}UtMSCXTGbG?J*cBZ$-LhQPOR{YGWO=axvxPRgHI-bXSy#0sOpD$0E$h`r zoiy|YL>sUvSRk{=7hlo1K7&z$nG2EYDuYEHDxDx zaQAgZsn!*2V45h?QChaLi}%t~Xg6m`hs@A9YfV@zP!^A*yxUNrr12O%vePi-{fcJL zNL1l3!E}h4Ih?$Zoz0z-l5#F76~qMW@JDra+DbDXq#9{F?eg%Ps_ErMRhhzcPSUw- zT!qgj%P`DuDEtVGKSIJoH1fp5GLC;dh>8D!CLf^epDh1?dwnA`+UnhOA*bhqGw+@G zaJ0<{tK+wheRXV|b6b>1n;Tm_d290P$=fHso4PagL*l2IA7|DQx%R32+H2?92MU`` QcCTX%4L=Fck!xA@FQkkw;{X5v diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_alias_to_area.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_alias_to_area.cpython-314.pyc deleted file mode 100644 index 82dec31735374e44c9e43d01dd8a2f8bffb56c82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2471 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 diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_area_to_floor.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_area_to_floor.cpython-314.pyc deleted file mode 100644 index 76ec1930bbaa48a732d9c9ee16c878d4bfce164c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2495 zcmbVOO>7fK6rTO@PyCxW1lvgZ$k82*r$wgaoMK7L*p$Ti>jIhy_ShNAjCD@6GS` zzW3H6p`Z^z`{}jcK6=ZA(0w*&HW;(U?R*Dv8OeyC3rOZNT$(5R0Mq%5kQRyP=t9Ot z++Y(kp0t;E9h)oDk@gXvqq{TybbtiYArek^k}j}`vL_Qsca!b`G$$T(&O#{XL8zbY z?ak~-_mG|e)Pn|*+z~~xZ?1b(s81t%W#Iry_?NoRlr8IH>990wRj^dBN`{_HaMZ7w zrbSd@Sf&lb;Mv*iq%x77oH(7CJ~a&%_sRF)8_zH;lD10vxNRFYQB5+5v4s-?4XRjI z6|B#L886Z<%`)@Gyt1GcjhwSB4dsiLgn!gYwg3J+d4p5bO;+gYiBhJJ&0mulWT-`M1%kFJ+ zI)j(46I<)N+1T^z1`+Jn`@H?drzE*izCJg_laTC!Xt|@P*h#{&hjhwbh*bv-IJh)$ zSUSpZms-F}b{#5-SdyB{NvbKa5I950fjUXMz@a#SA(rCUsJ9*kBS$@s!^mk2js_%1 z1RW^D?MR?3qZxF8fBOp{8QA*K2V8-ZIhmIQSxoV8Mb{(t2r6n2Bh2!Gy0K0w*rejL zW$HH&BTnAJC6(ChgdsYUsuLrnRcvCF6wNA?VWvs!l+B2l(luh0i>h6+Q$S8#ZSpMD zc&?p#?oG+^A`K`?gTcC@(D-iPlDl1la0{D7X&F650axfT6W2f8X5Bh*Mmyj|S|d74 zM{f9ij!$G&Ig*-|Me#EB{vXv-@X$~!Ntq2 z88I7fp7yMtalK@ZHUwnXu?FS?=yU?14zwS#=Ky+BFt~(gDVA2vTv3-uL2u;|%Df2p^VgaZ~!!1K2okS>Z zDrSYB@+zi*y1!Bevz~BM(bkK3%<2$kfu%ypp2v(%&RI6ohKD6+ILh36>dOnx1C$2i z>0le1Uo^#G^;Ov65m56%yl=JtN`E!CW?VBi<40CPweY^J@ZnqG!`E{+jT^?g?{;!x zD}3_z@X6Yq-XBI+$1aam&)x1jveA3==iZIT@s)``BCk}>Y(^3rfy9GY?`q&mpn7&~ z_S)=bEVUA>Mf<*A{C2S#--?cGL`POm)dKOY!0@fWa4j}ai|<>FUyfJbxgAe#yHN1p z6C?yfI|7Ow`p0?iXA}O3AcuS4w$q6&>Qxj|EwQBaDN4!8RfJ+p0%V;HyRqO;HIfxL6^&4acy{VwQtWm@vx|N3fvTAA%JC9WlE4a@ANcc9w67X2}LJ#usZjs5FG zzb1Z3toLmWPHww|kb8ORsjpd<>L;?p5>Y$?9fK%ef%*y%VU_~aO%}`WEDYrtyzuNw znN+Nbjg!nU+*U!8&uVS5%~3}&)@DoUpJ;m7`8!Z&cTc;!pcl(Jw(DGG^uw&2;Ui2i zrsIAP)Y~lDxPuKN?R#hz)}60L9nf}=srPuD4JYskSPO+0*<&EK1&-tHp!h>H^bkq+ qQS2cayNixJ7%Q6rTO@|6l4fjU7_5NlGh|V3!aSpj9EHKVDS?vn@dpNmtwTCSI_2-Pv(O z!X<(VMO@&35bY%{9Fm^Ov79(@4@6bdQ7ctyE7V)701-Fd?Aj()TM03Tw{PBi^WMyR z-@MtOcr1)y{P5y0A6@hzbdOD%1LkaS+jXGJNI?XhLkcHzd7kinjOS$`FA~w^1=&aZ zAQR<4K1hPD%qNHPVG?$EzZ}U&Nh}{HiF^my12Rzw$jN*s>2&#^yf@!Py86(pc!-6C zQ4Ao|%i@IOefe(E-G{nRKT^WoNQumLjS8L>Nh!i0%0!ns&s1&uW9cPn+OA=#WLHch zo8f3gvn-ov#I!93rm?frg%j#T{=~#-dGgdGNc_j&eP>)|TrzK0jB&>?9imy}1jaVb z2sEZ)LsPLa4`RGX_vp4YXU?k&TG=eRZfT-uESS2{vH@4!nzLx>YOM+;hN@YMs%HbL z^!Ryjr0K-n?|89dT4xN*9Dp*?U?;S4S@#a;QOE@w+%^TejAjwxpd$Ra6cH4Th_jG= zg(rd8U?J%mwh_phFv@wN*>GWBo6?;;bDh{)7c8WnT-T4lU(b{Br$@^ngmTF z-zu?HCZqwkie{098pSe;I?G5SD@X($5XEgvz$~LFbdG=RB47|?z34rz#3`J@D}o~C zc-W=yAv*^Zbtn^#FxHO^Qo|M%CvD5PilCJKIU83r;;;w<^n;v-o19*Ah+R>2yHW*9 zi#R!l;WlUJ#IBY#r{d%QyN1?;VXkpvC->y3veiWzRRLZu!VOiW>D|f8?p6)JHXK%| zWwe80zW8HC)<1p3KG%sm+XgMt8x>+aVyOEZq1g5}aEbxc2a9K11tW#smJMk?WUe5# zOj=A6MOA)^FEp^+QUWu@6@t6x9c=tzyMy6r*CQ!bJafrF_Q_(TXbGQ>G*w0i##hJ&fkZufz$qpWr zU|`le=w8oh$=$Hp05iG zMl^H3r+0Pu@^GE3EnZpN?0I=5v6bGxn!c2-AGw*%!IBmEc64y7qi?Gty_M=+4P6S= zGdEJhI{`5k-xg5M;lJH;em3Es2y&S9*2&Bs8dOzFtFS8>R@I7Ktd$uLm-RwTD>pO= zRh=`jL&_#J0vpr;RV~`Os$zD7sK01eG}EGiW}<`zyG(a zdhGh!Kg&PK*N-Vebr zYgYI$BQCBP6lp}aVXrK%M%=4FgUv3D*$)*>wtHpf?la+~xIOJOc+|tkG5jX1WzU6k z6v!iiejDAb&;?H+lz+{4R9d5wm~zySeyA=kQ+$ IzHW;D0q}fV5&!@I diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_entity_to_area.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_entity_to_area.cpython-314.pyc deleted file mode 100644 index 3a17f8e144fb960eac46f06d063a45395b95440d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2500 zcmbVOO>7%Q6rTO@|6l4fjU58nB&EnC*rhEBrBy{pf4r&)W?Oxl+o)T4YM^viRR;agBfmYmjvtGNg+DeF(Jp1OonR)ZR z_r13siO0eSo}XU*?c*apgzmCQ>w!6Yxb5#?TtW&W=sZ$5nalHpA7C;s3we=252l7D@bag&Cln;}zEBob0K1yQwI7#HY$S%-{NzX+JT(a#|MB zRBX(F8ZXdYx^2yxbLzZSHjD1DG+|i8Bn#~|a8!Ey0<6|_;$CXJSTU_L2A(%{1B?+* zoY2Z;-3!)zLBQhq$I{CS2Il zVRR?Y-6yv91q-RC_YERAukUI7v%O@wLws{>jwf-&2kG+nqH-5WC;`%?1R+@=8g+5; za9BLRfS20HOHLCiiP(}>EJ~Wx1lXNh63a)HCc&b5z!O_#N*ZueWfp13Gnhr4A>yHe zM38|t+_nV55}HEi`8U4=qJgd-z0Z|6g;RJ%P{bS$xAZ+=aZpi*G+|a7)Q=5P!xj}M zZOgcd7#9*qrErRkmEWp|ne;TjIh)Dn7(qQ3Y;HmrTN#a`EmJKF{;(!C5Z z8TsM&1wygy%Mis78i38`Tmu^lJ6C$zK|rn`wwJVp7K^I<6kqVT+%|$R#T9}(=S^AR zMt5+lbw;ZR%nh^LDExof8-)ruAXJ5Ok(QUKc14u9bJ3Ref5jv|=LX`=+jpUpaPg{d zN-QK=-k!Xte9t+f0|CW1=3zd7PG=Abp_ib7hS3{>$z=kIsk~+t%Z5ZsM!QZJL$f#z zo)&%MPzcguSc0Y}&Du4qh}lgtoNJ!U1Szj!8q%BvOIPO%Ld6oWkjC4JW+jQx+BD4C zKIIKeyPM`(71V}Tbs0Yu9iu#pSuetjL{z9abC{*r_31F(zI_rrxR;rCH%}?K0jM73 z>0k$&pS8qg^<_BW0Z{W^s&6IwL$rQ&b^6NmMk==)Yb5$N6C*bgBWuU6$yeo{&6}fd zZzjfnPmDKqr@!C3lDU+r%QyS7>*?Iu;(GF}<%vI%d+TR5l9}~r=3Za_%IFWHb+Wo} zWnrW5_2on({o+dcQo4R*Go6Di%W@<7!e%sgBbsY;r5maKmC&V7J##ZPy5&Q${g03k zi*F04Z~s4Tyq`}*CSn}!f!j`IcF~}!T3Uq_ZCF(+cCl7wGL-YhnpXBK300jnu|vux zv= z=$%;i(#b}AXzA3QVC0+V*U@jMH-ZE80ri6EsQQ(`hal<*I=lm({o*oMG0^@P0P9 z{AQG-!B*GCAvR5Ro}t^=a6cPOKs!OE!J|z!9K*+8FEn1{9EM>_;5hCUO5aCA_mOlL qrS7A{x6#;RUgY?P2!_9-h&%YF&~;nP{^j4p#n-z>A0c?VX#NXzLsE7C diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_label_to_area.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/add_label_to_area.cpython-314.pyc deleted file mode 100644 index 2141a44e058e4668d4372d0c9a603b29108d7161..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2770 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 U{J)~3+*NKnHSr^2gpcNb0p+5QzW@LL diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/create_area.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/create_area.cpython-314.pyc deleted file mode 100644 index 9f36a7086f203de4e25d8d9f2ecbac834a896119..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2103 zcmb7F&2Jk;6rcU@dgIthoDZ7xL)kb|z%;cbR47Fik`>z8LUqlyiikwI+O8+@!g|-8 z8AnRESWqFv1#(Ktr74%ff51P$y(mJYBM_BJgnA1KYK6psH@oW)w*`qgynXXNXMXR^ z+mi#C6oU2D%iq2KSV8Ea*mPGSVx;|>z;uv_7`lc`sUnqSCQk}pt|(=dsUfda5=;Y` zT1l35riZdbrLUY~{pB>vz+N@A%0PLL4Njs(^~E?#J&Dk95i404DrZ@C5{;oLWa>wd z*|#`&8qOpZ8#a|`luxaVTxkWt2gWI5A!rk$5j0&~$V`e(Hbw6{LUlOdRq&`fTi?!UjwYDv>;bydgGd>TJmD z6LsL}5SbW>wKL_QiH#DafT`^>LqKfM2r+{Nh68(sLoqc1-(U@FEP1O1-XCc!;lNej zZepHhw4A*6i+m=2mp*W}l4B2)+2arBPVkW9&xO~q7;G8{7T zNCq>mI#4H~3sxhTwTaKwYT)B7O7~v zB6e8N@@(3qMQA;2cQ0Iw?@Eh@?o(*3^0Z~eorx`rPaFoVa9DK$@>5ZP4tj!;iPZ0c zeEs2`_7u0Da{`0ndjTo`k!)wttof&{STjmw_&Phpxk0jA~P|KUR7KvudR)j zZNKi}u(P_2CkkI6X)q-jK?Z>2m8S&NyvAjl@IITa`i`}X8CM%nSe}jqQ9-#1v%%&H zsPhU}DfX5~R>TxDl8OV(%os#GT0R>2HNx{)@mcs(PwAg$61KoRM0f;s{B`{3?ev%F zt*dtz?ksGN7eCGH=wm9sv()vM zxqi7N2DI&kl>o8Rz)hRvM4U0pg3-ZT$Fdn%UvD!^A%{3AnGi%^OT@n_Bw~QlcY%C@ z?yIE-nUT(goq=PWix2ev&(oi!znI_Fk8fVT?cQ>CV#&g`KDm{*17N`-FchM`qDMLT2g+H`mDRpmT_A!EQ G2+ZHz{`7SK diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/create_floor.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/create_floor.cpython-314.pyc deleted file mode 100644 index a3be4bd6484f94b92f6b8bdce422cea49975de8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2296 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 V`dKgh8BZ#u2eTLdLa>Lx`~$^%!fpTn diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/delete_floor.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/delete_floor.cpython-314.pyc deleted file mode 100644 index b7f9c4c4e4a123152f742cfdc1c60f1ceb2dc6ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1750 zcmZuy-ESL35a0W-eR2JfkEW?1k}nAaTw1%TT8Xp>$x1(LAzb&0B2~g!UGL8Eh4Zbu zyN;CbVnKxvFGxHgUTAq~`@}2%0PjUrAzcZ9R3g;3q97{Xn7y+_9GHjM*_oZ)ncvLZ zy^+hN5zsd$zTYfs2tAdX5hY?l*}o0K09lBmbz~`ZrKWPVC}p*-)pV{$vQ|&d zs%CH_(k1E>wKP9c%kV7hbt_rV)$%-FL@oWb;VW(mp?P^%sydwrN9-^p+~z(B8Emuf-DqCItF=q3 z@6;Pt8lXvDy!vjn-YBVJ${}If-@sdT;JcBB$h8APD5lCv(BvJt+b)YtY{HR?Fk)bQ*O7q@gF`yVkgBq8f-pd9XkC5tHbO4w z=Fv5!qgaZiTAHO-R0uBdOod}YcK|JFNHIx0-lw6^8zl4|AX%L@pOR*6eg zar%stE_O(_2PZ?$Doj?t;yIl30-JSN1vP_As)t}a1gEX zK~n(&U@rk1pchC>T=+w>`yW1+%Xd85?SmCLGN%zqJU&J!yZ;3Ui>L_B?~iP#dGO>= zy8xIqrPm=EmA|L1sZE%R!?dO}C4h&>QclW^H3%vuNicGAC!4x`7i(S%$&b**a^WaF6z?kg%~N_k;m9NJx3=QPG$;`Re6LkHaI+riM1 z$GGiN#seSvihwzVu}d6`sVs{~x})Ny^9cM9yk>kFDZ4Ty}eeN;1RE9?9B$FUf`N@r#)U(sTK0#0P)nBtSgUh?Q`N5Ul z+|=j6XTg`}ALW+stKUvMn0S~yGr0UhAGKtBnY^boLtlkO=GyMVSV68d8n`6i?m>K1 zmPe!^{(g`5NuSZzrDAz(aLWIVSb5A4!!bLiD|fu0=TS!G|AC&6Ojd!)5(;B9f>ide zm>&0Z2=$_GKn%e+EW&szuGZW19Qev_0Xqfkxuz(}6I9qki+jj?ijME0m0!@+J#^-G WZSrTm{AV()TzEKh@-GBy1k68QGsQFj diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/delete_label.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/delete_label.cpython-314.pyc deleted file mode 100644 index 2362e7f2941b5ed29890eaa9432959aeeddabc23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1744 zcmZuy-ESL35a0W-eR2JfkEW?1QZ5OEy0msxT8XxRWThXr5UzVgkt*S=E_dtr!uihK zT}MiIv7kbT7bKn#FSNWged3jWfcK&bNLNB2l?e5%D2R$TX76kf2j*dRc4lXH<~K8U zujaC81oZXE?>0_o2tARTAtho#*}nrqA6bZ_Rb(kOrK)muPReRctLj{jWUZFqNzm!F zRMp@{q)XJss%d_tn&Da4>sGRstLAxr4mI^x2Cuj&gl6SksoHpTf=|q$X*7>4V+L7c z&HOotNfMv5v_(`*Z%^IqhT;3>Y4cXtqh>qoc%)QR#1T6PLT+;}3>a*)@7`)$!OPVv z%Wv1}*Xp22UcUZLrB*MhV!{c7mbZ>KZQpYv50UfjHR5AR);-4Q791~B)>;&`r-F^09%qZ2P_w#~1TKu*&`h?E9#RxB{T5tCL)_6s|V`AWP+`rqP%@=oZV6 zw02I32qBmdIc{=naCgC>+G8 zbkJ0Q0N4w_`sg{*5|{pvZ0Cb#a`~1=yM3@CN9HsniN{9>W%oY^VFAs7^Ls-ZY8*T{ z(9Q#94e51=hK28{D{2EK;xMf!4GG{OvXoOY;sFisn1i`oMCUb6DQertt9Ib}Br5Z; z4P-q^Gy`t+VnV1s6=OEr3LLynxX{}Gs>sBO7)+tVoY+N8=#2O+ng>Uk1jb~)FD;q$ zD6HaW?9z_j!-KcP{0QLxcO(M71UEbd^nNS6_|@!}vv=M5-aYS8;mjwwUE|nK#^U$J z;@!*lYxin98$Y~s;YZ`*-%LjEVI@&XDO65Vy_gpU#(~|Dbxvd43EiGAW$1w0J=-5x z@))-~%DC@AUxi>!VeEzu##EL?B;BxMdx1y|6VNt8Ul#Pc zO*%H6laA9E!?1DB!8Ql;T8|S3m&(xSjARldDnGUnlX`Xr*vIIx{^qaQss7d7+-(2a zZf@c;|5N{qi;r?kJL)%M55^v5&-SlA*M}_`UncJh`-n6y|BmV38`2b8JyC;BUTzQ#BfZH=t^zkcL`-w{u$^=$z&C%ETJ%lBS>Zc zipfz=C!{3$2E-7I!Xk_};%dD`&x5c07O>O6o@t7rJVu2*w6KTFC+PSdy6_9SzK72K Xu8se!m;Owql}it&PW^>ojez+FZEnIm diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_config_entry.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_config_entry.cpython-314.pyc deleted file mode 100644 index af0930c8a3f546dc972429f9e0fabfa530aaeb0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2050 zcmZuyU5pb|6h8CQpKZ78Zp#X2LkBQxic4$E8byeqEDLSGrOpBgiOXbmI!gzqGv(gf z#X>?FHO3ed**tkq}55pCRah4h+tMF4d%}%;bW=a!skKObxJ7i?Arl z)LN_>XYoK5sf|<J|*=QEQzzDIGHI(eo( zYs^$)F9$Ax7VoX(`0GW6QI4EF|mOo|dEZ`KP5NX<81YWr?MA6?u(p1$T^P^e7wCW5{Zp zj|Tqr{ke3g>bGpILa9TU=`n4_C${Ez%sxi~Vl*0ZpfO)-gbZq?7V@Xrg4Yt?(pMOL zS_3i946Y2PVF^e3&Wbby47aqQA?U$rI4i&L5kLcFd3Z-^O1h-Wx}vLP86O?FFWlm) zh3u2KfKg(z4)M4;=X>@wzz9W`e9|%*6*uhSXUgI2WvfG(-@=!*+PKqWv`j@(m2Hdp zZP%nNTE@h(%}oNzeSc~BSx%MOT|R0U{RFfPgKr;Xvoxp@@u6bq!6S%9(htRO_3{HT z{mg>Z25Q0ThfH9I;_pL%)W&rLyPzPrk?M$_{B&t*AOXhU$U%U%d>ZP5*7}F)>XWAd z>R5_Tey8Pk1-Zdj4_e0NDx7us^}h9^n7HRLR_{wa<#;54?>u%0U<7ucd^g14c;Yq1 zk&5c2ZB^52xVFZc_Tx+vQ7n;Bv?&U!Pjkaq?9LUVT*Z1cx!S~l@bo|iIX2}JChdBb zL2YIXoFbwGxq51TRxc`CrMA07GAIgep!i6boYaV@Exsi*&~P!-CuXV>aG;uTrKQhDeV8`#9H>bFN4F$9V4`oFEQqdVcsAMWTq#NT>B_F+!O(G^jJXaj& zal`P;mPodQVYK{4#}ydU@IuFQ`ctMTDPAKUOSXzZFik}E1Vau1#oU08;kJ7GZYtM1vX*|acXTa1{<-_9yYj-+t@Pfj z^!tn7UHpOkbmhk@zskRl{5Ep^@~!gxO6p|q$Rl;Lc0%*Qaxq6HaT0tib+8X0A4Fs( zkFstXUra>Qc>;axv{}dRP*N0v$)QRw{ofNxLyC~H{E!Sci$4-Wv^Gq|Fy|(Qw=`|H zZ4*jF4U;KxVSKZh5K(PBJG|e*RvUD)kgZ_`dHg^a>bJ-~JS(~yeFcXHiX=(5Vf#Al nS_kbOY+HwYci_Ms*tZU^{;7=JQA>YEQ&M^*H}wzT9FX=Ogn$w^ diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_device.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_device.cpython-314.pyc deleted file mode 100644 index 7831c96ffb86d0147de62d50e714699e09802f69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1941 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 Y>Ao`hyITAwo{=)QCgvX@*aKkx1L}(ZEdT%j diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_polling.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/disable_polling.cpython-314.pyc deleted file mode 100644 index 114d1f623ebea8e9bba9227ac7869d850fde9378..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2109 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 diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_config_entry.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_config_entry.cpython-314.pyc deleted file mode 100644 index 841dcf661fa7d27b095e0c0c86f533faa35657fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1929 zcmZuyO^g&p6n_0PKRd(p?#{5lDq$MdU}lAxgseXZF^nrP6L1+DLgcVidV6}A#-8q> zs%CK@Arm#mcrl)gClmF6CPqE!Q4S`0atQ~PN=Qh^#&84INWA%~dlqMbN~Y?o_o`mK z_r3ReUNCYQ!12}eujjwh0Pf1o`bfk;S?eR{g9RMUf~C}zn#$Fp#A;ou>0FPnR!{IG z%Jh1wmgeb5mZ*=^GCYe|x03ZuwH(hCVP1cJ@Io^MuwB|o)s5OHA1%TJ>;x;l6|9l@ z+zk372OqPvDJW$w6;5`-@Evonc_!>qvlX^Iw_H+0)((P@+uREShTGh!GmXP!wsv^- zSbgs397>XJoOrWZmuTDxgGKKgS+aev8QnnPWOadYYCAkKUv0L%;G|2JJjX?4b^vE> z-*;kv;`tFdU*f(G^S~7(NIje9nx$}k9?7s&o|;cL^266+j6rHMN{sQu=BJ&Q&o;I_ zDM#ruPfE+mj2cTT3O6hr2~2DSe~gb>Nu)1@^rgk7D84~Fm#Vd}?V44_JjU&So3kNx zO)uc?IT{gTvX}#tho&3Y3%+aGW=tRMIqj82A(oQ?sgYES4)2K9bYz%=P(eqGSyk3d z1bsLSXVq6e0BE9YJG`Z|6icyGOSANfiY_Pa$)JSpAomnY1w@j%yh{V2&xL{e4anRs zhO}*SCSTYk-mb*2SDY^6VH-VbckpDuS%t~kskjagJHE}@tb+C9+Uwz02GO$0Q(`K2 zdSVkHgEDgo5tGAImWNf7=m(P->cc}wCX5Gix_bG(Tz=-!ZVk2I4Duy0B=Pq?KyK}8 z1iPRpsZknI^VqPIJww?agCqL@I_ha?3|kxAY3Pq##%)8g_}DnDO3GgzSU8GJeIC>C zuF}_zCo<^Eu^E67*n#pXNWt;Ui<+mD^h?`ncF@G4;4Sx2smMf?X%-)l&6aI?Ox8`4 zEc8mc(6Qlcp|_B8VGI?C=Q1&2vtHm3=5jK8NfSDA{Y7e^#mFo}&tUg%lNN9lo8w!p z$Pv68^{m*rAvaGu66NRwTDXTq-ziRg(fho&Tzd6p@xXHNz(*%m@>@PDeOkJipT3@- zzL`ICJ%4Crthln>yq|zVRapbAU_1mZXZ$6nH4V&z4^%||GTG_xRh8J%Z(&>@7%bc$p% zY};NSQtQ!bOQA2)mV2S=QMW0ys_zTcSrS<{U|s4GAKP0b@%!cl=WumeZri3iq^Su) zu=Ba+5SycVq03!{&q!zK7CA|d>B#ydV`H!5^da2RkKM@?`bSobss7OwWBe2UV}JSC zJvWTKSK0R$zrFYa{pre&SAJ9f82NqVm&-RQCzo@l`bQq>>+KMmmzGQ8^f_E4AB$b= zY~+K=LKI2f>!8O}=38Vi#%_mq!!D!puS4wK(7WaTyS%(X5mPp?K~`?Le#fPZ%I`Ow zme$hiy-#Hyiu}g&4t^J-k55d+MtVg0V4TO-=u3E4euUUwobGFiqTGVXRoJx(=3Usf e3j1%v{#Drbr#5z5FaMp)DaLYP&p&``M8|(Hukxq> diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_device.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_device.cpython-314.pyc deleted file mode 100644 index 2949540066ba5e870d37d2d2c239abc7485db4ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1927 zcmZuyO>7%Q6n^{TpTx19#EnviHrb}4#ci;mv{F$K$O=ttRW)XdiYhf7ZP$}{VZH0l zjw7X9EI_E@0>{For56ep-~`8VKwK+Tf_6j&Qi)J+ML|^Dc(c1{ZMAcl{pQV^H}k&t zvj=n8QNVHSz)$bo&;ahq#r8->7f(_3m)U(dJl*ETEuSL2RqaH_%Osu?Uxvm|#5Y3PH zy@u;PtN>S#WA$8tYo@~W739-Yd1@uyoE$tBVdO2bDl3SB2W#@48md2`ZRi)Y^8o68jcZhWsdZH^dkraRyRCL~)R^^5B*XLNJR{ zA0C39SXTZH=j-o12){q@XuX9RV8^kM7-Zgj0Fd3fj9?z-(7;(K4@YxQGnVZ^;+lHA z82D`GPggyyG?N1xF*KEhflL{Pr5r$4J1dzD$J=|ib7l@sj023oEcS%Ga71&Il6G-t z4eP+A25-|xsUSmBW?4MmC?$m2VqzpJpf$>c-bQ|fj+JP!G^&(zp$F7mWoR3id|-Go zkgn|8XBcb>f!GllXor`PI4)w(5bsY@3Hd*Q7VaZ~f6nZ>vhexBRsQA17aO-`Uc8jQ zUEF=8_*wDlv0saoo5jkd`fpR{-2Ur@Z>GPV{&w>F z+3#j=%>P{av2^2^TcwjXvyJ}JL;Z0MNA{)F(iD3ZKa!Bu9+n~!!ong=@{KM&nPDa4 zE;`xk@}A!d*mF{_Z>ZSHkIS+=v=MPOH6$yysoSM2V6sfuK{Ge!4%d?RzRUfJi^O0JP=aJW~biGh3^5P7v=tq)Uni+s;X)9A7t&Q=q8Q2Slz{*a7 zHMCMZjy}o3$1UwJl!q@(oa+R^d&W`Ye9&b^D`>m4QdY!>?fU_@xf}Q)viY~qH%^m< z+UbQe^`$qKP?CQA&9l||Qdt#aPT;S(>tw_B+-7t_6sgZ$-Xn~zyCG*i++V7$B4s-~ zy06-7yZ$-KHe83w+Y)lY_BRKHTA+c*DQtWD~OP#^2|!MF~0j) zOk>pAaV4g4$DU{HSQ%;Tds>dtsi&nC<+vJ4O@$XM9T81Uf;Y;GR+^7m8N@Rya#4Iq zJeN+@f;Kg(VTdr;J~tKuMh(~Jbe%;=8DWfp!2^T(_Nqq>+eb>GR}uMGL}FeHC8)Yh zM@E%I20GH1Rb|^iq7Tb(K|OI9poy|+cuQ$1mSU-vX6dGi9;WWg7=-R1)GRB>G^4!B ze4#G|KK%-0Zr1|Vws|OTm=%^8C)jklArIQb3ECap>GRMGW$l>M;X%it4u~=@i~Bw zwyZXWp4f=&vL=)8N^<)QoWZgif`i!dhv8+-Rm%FMeKp%}Vg>LPeOwnZO%;|y1K4WO zDn^=aC@ZB&R(s`?P$?5bcG&YBvQD|sTZq5Vu@P-C5sSMWn@9s4(w{(Vp# zt&S^9HuzF`Ok@f1?Y1nNVM5wLv+GG33+iIm_7Y8zkTsWu+;g!`15{@SX$B4hu9p|t6fe*yhb|9CgmjIWb&5~O;R3Kary}E>WhEmC;E$(X#}g3aL~;aKZp zCnFpz>hU!1b4Byw%3UaJ!R!_o i_h8=^y!tydw&10|w9()6%0KCXQn)oS{}6DEfcXzQmFy<~ diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_polling.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_polling.cpython-314.pyc deleted file mode 100644 index 6a70e9258d0d57e05b3b8982a5ce6322df4d35ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2106 zcma)7&2Jk;6rcU@dhPflapE=&71^dJU>e)d7L^o=6wE=yX)2#9J+^@H}AcfdG9yB z_x404okZ~c`u6YF)@6jA@I~*7g@SPKGYDHqM+{v-x=UMELNq8%;Z3psxhX3 zPOio)DpLbpteU7KSu&87>QE)c(jd!vyqc+GS#}bw%5O%u)#C^qPD0L}lM@-jgSZjFQu&sJ<4k(Yz zUG*3>Ocq=^S8rL)5~1ssNx(Q1iSveSn^7{s6oo7tT!ZBnT189%2*oR-Owt7=uL4}U z$l|MNE!V#-lp%HLv=GXx$=ad+dXxp0(q(`rb{N^iEUhanqsIXp)lUU+MsaPP<%w!9 zGS%i?N;J!1WQ_(1YF?O&#$1g!##NhWZP&IfXRYLCLIsW#el~)KIE-#!t8VfTqc9Qh z2SL)pfd;}BT1HpI_rFJ|4!Wc0bD=5df-dTkE|*0JGxk&jH(xdZH>z?`p@em)Z4DYZBJ6vh!}2*&=Ae&kC1rU?ka5D|~yyX`}YzoWhm7^c(?SDBh2d{r7%5@T`dmOzdSwcbC9IqHo9hk?O z%5-fS>8( ztVKLOYj_)uiPs3j!EwF}g>F!ewx0`aoi?aQXfDXU>=C;`v)myTZjoBvnkR!|dRo)y z7`KdtTbn^1IAWZCY=BVz=MoU>8#v)9uw!3AhbC^X+*;Ym7q|1po&34&{JGuXvD=AT ziSERS2g5T5l6WYykHmE5#=W~}s%@*gN8vI7tC>t>#m70<@oqY-EA3`JB)a6C=ZPu<^xBa z^1oLwjDq033&RiSp?v;vdUWf;Zszr^i@TYToAyt3_qCb(nc^LKZ{zMpH$A&`;kn!^ z;_x(nP+^q51rtxU(ScS3$x=>MUtt?<2$1rY?k6FEPMdYyjz>>$#q>b4DZOkQVkSw8$@0d%cXp4-*JG=x;)A51MHR%ph34`eFEn zAJJLZ%bTkA4h;K}AP5i9kv%lEhqNbXd=Je<4VK**$dfPigp(T>4uX6O!H0nP&** H0I+`n%~~1) diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_user.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/enable_user.cpython-314.pyc deleted file mode 100644 index f3f7a58fa3861413bbb8f4b39d529d660b4e2d9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2490 zcmb7GU2GFa5Z?2hf0Ed7Y$rGlG&v}cx&d2>QfS%=8X1T!Lh2sK5>mtIIB(*k=R0$^ zmyl2)tF0fw#~;*R&@Pyem{R&w+z0dmld2!GIpD!X*!XJ zqH88hL#IyXkBXV>(aiDO#MlIId`CVxF`A=PG^>~KsA(!Dku-ADFmwaB5n0!!lxcBB zQk9}(2bhV`vnDYlnK+w{7R!ou5*ss$j6v8|Co__&%5`U)VH3W)Fs-6VL>Pe3b0$Vu zfg#)^z$JKye=?AdZe8Zm;5s|ZxHM>VzDZ-UE#Lh*AGkfQb5qQ)$K|FO(k^fSt2d6+ z4iXl8q(krnr~y0Z_*eI5euM(VZz9BJbOZB>M)0)Z0K=Ou20YPutVw58%uBol%#_{c z&P6)2{j?XAqD0@CdBHr9E&o?=yOM zo09=!BS1-?fr({kHaDSb_&$Pr@J;DPSt2IgVV9jxyBC?3EtBYFQP#^9(A0>THfiLf zu}t)eDw$<74bg+8Mj)i?UYY4FKBOvhc2E@S!G%T9PHcrLwN*sHH+!M0q8F&cn`73U z{@%ss&iH`1^9|5Ia{W#yjePL`3Zc-(_u#@V)CUGqsNyLwc}Lvb3CzANx7>k_%_3A` z3n;(UVtq-TYhFudc}TZcTVXTe#vrxN!Bt#fs_eM86;-$bHx;2*Bd`TFh=x2B?`JKI z^#yMuQcFyM%R2?7A#=ygBzJ9a|6#+E4>hl+vjW%Hr4JpSL#PGyqJjrJ9|F(0|KWM~ zO|}KE;5%5y?p5z$zhQSj0fNzw#;?FX_#Ue;N&kFzR?>&(EpmoRYaP0xNZqN2fl?v91cEnY;DqsjZE0R8&GqE~l zP-JW`O;^)7z0J7^T*js;O=FWr=-$0NG=ml-yVEsQau5*fiDU0-q3I|L@!0xffsFHdyNz1ldY>H~ebh-K#q?i(`u;wVo5rD#6IL z@hjsuJAVuIu18S!zKu@Q)dOau+ioPUCvT*#r&gjvkD@~h!;8bU=+JUByAsU)8q996 zptl}FogXvnohX!J{xJu@i!Vo+OoUltnM}yH#3d=68tsJPp@+$YeGl0-N{6YEK}-Ea zGGYfrQIpEFlC+9qSua{DrJ=^0w`@&p}xf=1TR(4TC_6E5|aFAiT_wb;JD5hWbR{sqyjZW#ao diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/hide_entity.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/hide_entity.cpython-314.pyc deleted file mode 100644 index 4a826c3f69c8d8611d6d147a5f6d2b7f40cc8024..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2022 zcmZuyO>7%g5PtjPzr;U@(~{Z^%_f!Fx}@HMg7_&yveKlssv5H`kt&g{w(FO8VZH0V zeH+Q;VnKx}E?j|2i;$pj0Zwo%2gJ2i4{@JBAe9L9fK(LJiW~EG*R9=lCC{6gH}B2N zH}l5NWiw+4zAp~{{LbGJLU*_@_+lX=>|6q-hcradIiv|Sp(;{wmh)mws>)Ojc&Qen zanQ-NL^Vm1fi6}{RmbRfHBB=h%UZmat>$QM7Olw#BQMPaLNolXL~WuvNhfE~ZZwCq z>;iGMV&N&8W0?Bdkvj_baw98u1?K2ch3+rs6Nz{U;7aj8Kq*YQBioH^TA z)|aZwODAjf6LrwUkH2xMQmdClHfg%fy0xKi8n)F8POuzys71Rv!5fxGNf+!3mGi(E zCJp>mnr+KDjmf5EV(u+smkirB!vI725V0>o?x8hA1wc(am#2~@P~eE zO9wedIbD3U>b9{`@jQUSaHz8660BGb#Tz6*OYuS+6zVE1tBDoEQ9}I6Y@93fP&&g> z5v*3zgc-qd5FNKq#}*2WB44!r`~MKKL5LmXU4_AZy5xFtiN%rX#Kj4SwrmpC&8}_00rY zDI?U8R>el@F+&hnrJ)#t2{CHZNRP@~(!`gdXlK#MqX?zYKEz+`i;^Xj0l;L$9Q{TXp$t|cAli}4GY${ay9gqes z$MRbAf|nh5##N{yVe(t2m$j12WDncxBnQ3-2PbgnaTpIAP!z)7BHI<-*$M*Hqi$ex zBlUioNldm6MRb0GTT)Y=UnWOiSl*#T$sCe{0kH`9c;m zb;oG)Rvgpyw%hb=&O?)4@C`fK$?5vKMLcR-(D^QGCv?5(nz~MS$}HY=+lJ+^#30F- z!ABgEHeH(~HGILh2yQZ|V%tnKH`zFLJfC3QhM~aXFc~dp16H$z+Xk8EH}2MTm>1MC zb%Vn8d7okryz!XHG$$Nk!dodIT<@&_`4HWfkN=UG>aFy%dwVDP>51NQKRfxc{gM62 zv0K@L*QUOm{$~2S{15xS-*w9mNPTk1Vdn*s*!6OS@@Y_pMuqR<=Z+6a{K5A87JKT}llIGytqdx3wK|a)qIC}%)98>jOCc_Qy?n|h%Qj$*# zn8!*&QZvSMU+1Sy37jlq+IV0HdQM_9ZfzWGQI9+J^rFXwIW4gelkp z`hpLeI0`dUW^CbTY@Mo!*Hv~iWPr%jMHw=9c=z&{a>V^F>Os5zMQxbsq77|?!fuWM z_{|L{!{S=bf?3WA;MZtQJ-$*rbFrwL6@tC0Dyldk;b5Pv2tiT8LR!(0391GLK`aNw zv_z}`K`#a?&qGV~yh>V=@`zb=M z&94B81E@s@C$5{Si+E+Pk}1wq`;b-~ZszWvWzDqdYx$385U#Q4@3z*wXqo z48vMDdIGeED~?))9Rl2K<>*S^^EKs}D|&UqsHO=jt}|NMZqz-ndFcb>2*9{Z?O=rw@lw-muXC~f*U+dU8a+V1uUR$1Jr{E7!?+CM5`Ex7FAP$eQ)(LCe?!*FuRUE!!Uq_2xsnxNx|LLMb} zaOv<7jUkh$gm~J63c*~nolFaoa5bl#(z>dM(0FByh4Lj%4WG=&5SMx?yxl7TgQ#SN z$3S!|fx+L)%c;fLn~`5fK9irTgwEdfHBjR)zq8^^QWe+|G&0A=^am*wyf_=wXP>ig z6McBDE^I&(CadPLh%h2FikN^AX}yQx2K{{hA(Tenez0!=qegEKw8QJ_B2t-569wXW%|LV|N3CMStU#!_sVj zhAe*jf^&mKp85szKfG-V{Y7v4!a%VpwAR#FXzDE1H7<@Xg>LXGjh!o<-(O*0+ceS5 z?hQn^rb&k@N>)kw*sGzuAGdo9zF-n>*jU#;4-s3`DyJvw!7%v zznEI;xgoEFPp>xj-ShUX`x_T0Z`7`IoLu#P@AmYnf8agyy3=#*%)4ibtoMD}b=w-- zQD8gP*va+rcrzeOVe04WILDI`JD3vz?i zUMSu{_nEPESN;58(S2xssOa`w%ltgEc;c@6;8ODA^attXi@%dTk!~x0p8ezO?Mrvt zf3V^jo*#V1lygF3bTV+LK{*8#!D2cO2`*rv5V}>460HT#=3vzdVRp?9aprS4FXvU| zHKJ&*%4321${BCC%AjMWHCE|LM9G&~OeZXzhZ-dX;ctbW*c7>iHLnJwQDVQ2!%z>}wPIfC+C|x~b4g{o$twY8~wV0?`i4Y74G?wEXk5BM{;Z@PCQXqdqol}Spr#Dpm6*rZM?D>dkqFVQ)5lrnq)Mi zbhqNj6om^YYL9z@RP7!TE?F+r9)Sx#Ra`jPcqwP6*xFFwqW0#L7gEcK*F74?cBP5~ zZL8;Xzt^w(y?*cOccCMdK+t~v`9I$JP8&jh=Yz0C0z%sV9S|ExM-1IWx>S;iGLr{5 zEtiy{%2YusrART#qJmaSZN(Ui2|7}W7ZWTY=xC|Em}JRfilxN3t<+KMWSu~(daRT# zcCoGjw4@HTPGH3m>gW5!VciLKqL^VB;789PJ#keEgjiN@??HO92RUih-Hm{kf^o-E z*Hxt@aZ>L*kMimD%xvBBZflpc1+PK0s#mk|a9;A;P1p69$!yQ1Fig!Y%#0hO#qrS_ zrSc?9s0`m@@Qsd5zcErO1C?CG%&=X`Ot*sbvfnw2$z8jGbxiA^1pDdPM!oJ4M#CAd zmtsm`4}(^`I);ht$TDTbtT1t`mI~08Dta|M0MqXe@Cao1xMv+u&Omwth`OIAEmGa2Q(nL%xg}@&x$tPVOeUcq)`Z=(c<#g-{%w1%4fNXz(k5zc6$M(0Skt2(FDZbOD8_ zu?lDFM>g=zpaJoxE9cQ`${A!!dDVOqOwq-SY4|QCO)ZEMtu&^wD%J?Dc*Mex1SS;% z+8VJL!>(p|YwmC&p*LKrdF~P6A(&a?Qv{pV#gGeE?!vu7U~4whOoua@T<>sVx{h7j zUDZsD!hWs=hgh{;tW`0=+M3sJEUn&PVvn_|=V0wlgCDiEEu7nBn&Wx(i-|R{#MB&< ztYXb0U_i}dSPKJ$hrQzjrZicVZ;Bm?H9N4N>NO|^B{u8yY$tx@FYznyf9r8B|ETb=@WCq+ALJ&tJBwSn$@^b;d*o-)x1&IAbrzo~ zDDldl<5xcOTQscPj9>W=Jq-$f+B?z}`DNEgGWJ^;sNW|eyzgos$;B(cK4`66koG5F zY6C4HCc%Rs-^?*ZmzcT~VNqRXZA-D469;P!2$s+l0A*Exbh{p5Nj(ZM)8;1+W8eBT zM?UQU`m|9G0>yS2UL^vaA&vyjYRuC(Qb4#l5DfuZJP(EgwD1T12cG;t5J@_~JYTKA z8AuXHlI9(U6w(D9kzJ*FqY-HI0pv~W64PNDevC<-X8z_>IY&( zx`AD3T?LQ`Md9}&gi`z0A>(rZ1V>?P=CG_-fKUicGp+Z%H7!1iT%Tz3=!I)r`X&J& zZ$a$dlQxv8NCMU6d3lB>!LjyYEi7;pTNhs%DX;x*t!j)ei1 zPY~{}Ag@oFwCPq1(}Ls`IN%ZEmbWa@*H2jX$_l_9)C+zzc%%JvupmHYp6PyP@W=uR zz2|pHwKUIM-_#8?CQm6i<4)vgknp@FA-v1M zkG=%mPtb?z!jn{HW8!f~VPo=f@5?*Am$rH@ZK~bR)G+4*4SZcW-$T9xUvAt=0}3!+ z^6(V&qpVqnXO2J)0+-%#Os7)CwI<%pDdE%LEbS{)yvq z1P5>@{B`yyw~C!QCUp2fg?J&MdK50Egd>2cECcL>#6==ggu9OU(T{yKxY@6hF^~hG zLv$RvJw=kF4^iJP%IzX;7oFZkU;YSvDPv%j?6^ZF*r X{wvDM!%urNQg$;l^lt=T;q3ncSmINi diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_alias_from_area.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_alias_from_area.cpython-314.pyc deleted file mode 100644 index abc1a5ebd0289094a60bca7f2bd0a51df18cc1dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2491 zcma)8O>7%Q6rTODz5dIOleBh!lugp6GNd-DQk512M4_!+L@?VD6w!3GU9aN>d)J*E zrzDlC2oNe<8cskjP2iAnD<{f{6Zb?`NIIgTQmLxmf`VFcNDxn>RD> zoA15Zm)oKt1mBM@{r>SI5upcc(D;0Hf!mx1VGb#XpbJRhvRsBId@qyvtdJ3j=*mLY zNBp1@vw@66Bv_wB}A#cZg0HJ+sT`3#O zbdj!J)Pwqv65NfH&}8SZP@hJ+6=47+!?RsyE4KZKd{~~at5`1DWz$F{IU3e1%O)Bz zZOegS^xQ=5q&k{8Ir>g^?DQCD{BOVa?nstNv5Z|dMjXd)2N0GO~u9( zsPPQ#)NQL^PN~yc$;`XUf@fmnA{aHDxMvv2mrd)efu~K~0Att_N3~K(_hNH9h>v~T zCWUqmO(Mbps{Dm6A}AaYCjmi)CxJ;R7jq}JG=PLK%+*zsq1>((qdR!6pV;)1a`9*V z`Vg$Q=UM&EyQH{bzCJh2lQzW%x8vW9N*$zK36Ks&g1ZUQh>MGd!|YLpv)lw)cIr^c z#Fnv9wxQpFY($85{^2{F19 zY+Tle!*=MWAEoPbNb6OHz=3qTT!EPuancT>W7^P(T`6f!*+~N>4Xwehv=_9K-Z`pN zWrjvn)g!N=sx+}3!_;=uemIK5E^H1xMPXm)AJ*KuvcbAL#2syd73tnZF&Q!PeU4Cc z^9P8eAN7LsdDp<2-1eECwhz$DiOnfZp+Tk!KhEboN;i!ljB`0@`?xME+|V|PHP(2I zs|bg@$x{RDjuiey{3DPIM}&%aKHS)8yh-vRH!kEN4gFT1Yu}O^(y%8~d=N`#L%Y?# z$XUjHxvnjbhV&dyaVHcM-!TvWUPz_^bW||8WMDR)(X4#QkV(;KrVL|h3P)j=S>FgG zgFG9Oq1?#@yK3ce8|ZPCpN`%6uVOH?* zUT7d8Y(~fWtZD_U1`XxSLczd>1vSo3MaL)=FhdOcT@N=ig|ai{uHZ1`;Ghg2W|fL| z*Vih#5vg7-(7qOm?=;1w`Xa3G82I{ke9z6ujmW~erHRFfT0DI%x*k8c79U=T58pob z%f!zUcV2tV#VVQvZ6S<9hb1?7X=ek=MKTu5}NtbPq0& zrJ2Q<+q+i}9j|qt*zlp?z!M|{L;txUe>NH&ZRc<|9C$3*NhMXav@%QPkgAsLe6_@6 zNb^fot>jtSRkdJZhm=exBQ}@=s+zZTRmCj#s6TI)HPfPj24=#vU80g=T&kMb$WvaQ zrha&fOlyh?Bc&1z83oLrkC~KJI80}G2c<6|!s~Q1^i&bJcunjvbPOygE z6lNLaHjLEH2he-P-UpjuR_5>#wyo5t tE=oK^{ST4+0L34o+j)0%UwfH5cDo|{{t8WRxxF#!V+%a1@#gPlfHgf^;*XcAjhh`VX25{0Zb>osg@?>e(% zQiv3xwyM%zLQhS53A6{=TaM|ez4Q)36f$bmNGYmvqoPXGTi>kL1}lk_J(u1bOA#cZA0HJQSu9S(U zTS;pVYD2w93AQ68G~F^HRHl)bBJ`s~!$Rx%l5Kx156c(qGL~oUqG==(9Bt4n%O)Bz zZOegS;|CYAr__n`sfqV8lV>JDcO4qZ7i=4=*qDK-cphBi<5xh^bmAUjJXbWW^9G(XbpwnIo;aZu3c44Z+d+Wr z=u7ygK8!>a#=h}1zj0X+^CL>@A zw0}9sw*T9cG=3|2pE900XH)YXEG6@d>+%F=FD5VI%5zj&H^WCQ%&Jzl?e4t z!>sV*E@&VpY(~h+v~mfo1`XxReBQu@1*OhUMaL-QF~bZq5>Q_yJ%yq(<1XR&K|44o z!-rYLqOrdVs6Bb-HL3-hqmIQ z>+#Y1;xECUgTEfxJaKj-KDpLBx#au3d2&0}d1vJA*tcWL7dN|(t#!VBuX8PW^47#3 z(Shaj8_~pCIPoaj{>|{$!&}jT_2|GxbnsSSTROBQ^{-3)+u`QhnJ+U-S2x4G+p(^# z*x-6>aG9*mugu?T-y9m-h`qV%L&1TkNC<}hb3^`gqH&^$!!bDUWTJ&is%mLPmd_zo zE!w$qfyt2R*UDPKvoxt{-oy?mm{3Y=Fb7mMXX~npS^80b&Ms=EMFUmLggLuFCB?W_ zHnEYTygo<$@Ftno3>C%;1=?U(P8l0&0p2XI=(9#q!z|5co2tT#M@(JS2sm6R6T^XR z*kN&;HSDG^ODeZvymmf><_q*d9Dmf^O=P<#jV?I8Iv>exYJ57FqK=-duE{oZ{tF1tw*0C=w0ak2kD_&h5!Hn diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_area_from_floor.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_area_from_floor.cpython-314.pyc deleted file mode 100644 index 2cbeaad0329f1b8a047a41a48c60912677c8c95f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1922 zcmZuyO>7%Q6n^_-dz09XlQebGmS&Smu1r%~LIsM15LRevt0I_f6)A#rv|Uf)h4rpG zI~ytGVnKx}F2D(i3#A;|p12kXi4*st9-5AXs8k}~0f&oVDGjBra&=15YD25*T#sq3k>Y8T z>5WX?;6^M&N@Iv@!rkq^(S&P#@tVQ!ozGU>QZQ z29}3Up-*z~QA;}p<-sds=et4hv3b&53L<8m2N@75X%WmV*CMs33AsvB~)E=HWdUvXE-n(eu**Z}UHtX)E-?eO@v zTC3yw=P6rr9V+kYlXJG`If;A8{0y%f$oF6wxPq9e7e~2fDO_Jhd@Pk`mW}49xkgvK2Q_S>;M)SP%xj?zOfN-N4KHIY^op0{+wGgSnyz=y3gFIX8w(-7G>zJ5Gc zUaJQkYSzLKk+FSlhDpdK4@^co!5TGf-<0T?D=g@kxOhRpD&k!~(bF z$gujMfsQmLRoOO?=)odfR8L<8Xrb%~ysxwsOR-c-v-GNpE~XyKAcXE9*36J(no%Ax zU+4>gPj7=v?MlEpHV@?uGvb44lF+IXg&bXRf=(BA`aG8`rNRT*mP$xHCHL?S0?JV-}0!*+w@t<$b3~;77fVe zGvaz6ZmXOUs?EfJ9j^NhS*2X)ZA^gBvGZ(^O~g?bIa=0*9#U_G4WkhzTPrpf%6Lm9 z(`2$k#DU~UJNAI|GbpC_Bxb)Yaf0U2!ed1F&xu3dWxvf{zi@Nu`=xsm)oZzp;^A-B zzh1vSdB1q#Zt=vmvm4pT``OZO+0sU#xH0+CRtj>*pMaLjZ)-3y`)?>w{xk^O4vo-uWO z>}K&s@z&^%M{gg!GxJONr}CY{_omNmX=*;*n}2HT)O})Knl6vA5-t*ul?b~S0b#PT zM4GR6(Pf5@0BSMFsLP`u3RzhSN_+aW@_$EH_9zm}#`nl7ZR&L?3zGtpRnKIq*sQc` z>~uWCe-Mb#y(gW3(fG+pP}z%=Fy2g3`W8ElXXVoqp2TTOQxxR^Om4!=CYX<4ViVr@ c0~R*nmA|yY?|S8*bV14A9h-duxW>Tz2Woogi2wiq diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_device_from_area.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_device_from_area.cpython-314.pyc deleted file mode 100644 index 54d7897bf89de56664397d13c7ace8c8abe38196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1961 zcmZ`(O>7%Q6rTODy@_MTjvFOT1KA`}DnqbKTNJ9ING7!Psv?*zid4~bv|Uf)h4rpG zI~&R6VgW)G7kY&QE#=Vm#I>9_aj(<^>4=I-B|^Ow1yOP1&F-c+YMH~#oA=(lnfJZ# z&8zv`FoN;@D?fj9P(|pG*zAo|EJ(XoLFgkBF?0c$k|8x@CeH|2Hk5|S)JRs06ib6n zH8Ks2X^}2v3^j&XwlTtTuvg8rk#CH$(MZl1g+`GTXVAKOD7mqfL1Y@xDcN)aMtk5t zVFf+U#$gv+2)4W)j(xyXu3rQv%VyD2>aC9Boh4KR0An^0S1i}HlN6)n8L&5C+ehn& zNdT^Vp~MtZV(K~oY050Kt~JL7kHsNHT(|I>p=hrSsv4aIAxvrIwc){i|CdfL!@s| zzoT28Zc$=Y`9y3^@WaB0g@q3#s5>oNq?V+IfRPBQF74_d^wAo+Aiw!JLM_luqxYq@ zWJ;!NDyCYK;hCw&A`!0IfHxfyVww^bQje>vzDI5&QTq*_b}SZ%8|L`=T1{E zV*8ygczP_T1%g{OVl%(%T0tkM0l$c~mvSvhIjH?lM%8YQk6<86L`8xzFAqXk9W+hD zTLMv;K6;9@RQ^vf-TLH-SnjZBw+mKeCv_4sf>uGFA(Y$w7KAx81BcHGeKeYbo{4T2 z;A^VMW?0Up zEq=3jo!#tx*Sk0M`jye`@=MpsUze|+_@!LCTdrL(ejS0U7yyo^_)Zj4oI=cHHvGxN;#uMaAtl&_7UgO9qa1G&R=Z6) z7S%E1pyDap>fL4TJVP7*=|7+XoK0}Pz_&!~=P24V_ z0TrJxT@+J9p@SlB*7o5EBAmq{u5oRz(WxdJ#`eb&?$J*ZYJ0jy^y~{!j`J)aqa9qLjZ|TKpTq8lm+svYz+D diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_entity_from_area.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_entity_from_area.cpython-314.pyc deleted file mode 100644 index 396a86debea415fc778fb6fe0cadd2e48befa61f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1936 zcmZ`)O>ERg6dwQVu9NIv5@@m^Z5)t-whe1asYpc?X-NsYLQS`(iiAoS8SfT zTlri+g0cSQFQ08F2t5|e?nuRiv~wMZ9x@R_mys#erJBs-2|>$srKU19qLq4zr9r0F zGc}EAkt|j3tM#*iT9)O2SIu-iUn{V}1X@y$Cbu**2#pI{nR>A{$Ob3SK{Sa>Z3LNp zONEo*lNfBsRF0r>|Fz)@9pC?4Kc-*wL#ns@wnHjqi4Rzw=QE2rz83(S`{ZKdES{^K zojYG&IJW?j^ame(SgkLVWj<*8-mzR#S<%b@o*K_a@g?zG zo2mJ2qE~|eV6Z%<2XV+c^L0wv{wmQePmjUUm#N4{NSvlE zVoTyfD$tNyX-5a5hZfOgdFDDoO^}VFkENDmN~UZorfSIGXX=TFgsV0HPBlTMDPbY? zxVqqbz-YV}TKf3Nnbz{Elk{?ZAL4BGzuq zMiO&i{7*!cPM2pfP701-%u9PQtn5`C0$&1=njU(FGO7GuV!Hjswpi}6h}!`zvXev! z8l~a;3ZdN2Z6FS#2{?R7$fMENyEBnZ0(=cMVfHM$#nh1(rG~Kj!Z4-jJ!Mlw^RFS) zQ5NM!-wPXoT~tImP9?Wbpz~0MedrB%0*;_}6-O$o*G6lW*K~=_TI6}*h=f&W77U0c z%!rD>PP3fiGNHWB3c8++R|w;33lhLp=tqkW$KtR997zhQta3FV?lLWaDJo)#j0Pg= zsVQBjqFeZZ_+%^c11E{(ll#*1sx48P=E1@f!1|A|@te~>Os}z9-5O_&LA9@;LNeH z#Xx-}WF&xVgtK%+Oae|S{tVG1t|FtF(yQAvB=kou<=bib_dz z0oQgLpTZvr@uB@EZJ(0p<%v<*j}+J5k5f8JXW*=Ofr4W&Z7Y%_Jw&A~ba)HtkJ0EB edhd7C*g_}%REoc;mA}(PDSv->`YD1n0_I=eUGS;^ diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_label_from_area.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_label_from_area.cpython-314.pyc deleted file mode 100644 index 49bd8785954c2b9b3920781789199d5f9be5f5ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2798 zcmcImUvL}68Q=S(KbFsb6uf+laV$|taT7XF*Tj}4tq}(!#%;{a<+QRdIo*l9 zmBH>X^|YN%;HBXS+UbCShd58^qj}0x-)#ig?2wtvK!)MXj9W5&>$i83Ew|*6ec1io z_wDYt`}_XvP4xD}5VVg@{qypzFhXDPk9G?MKsfk2fOVuHie5pQP!tLx71JDxMX4ZD z*~d~bM8hDHi;+T^>@N(^0l=abEe;mqG@eF_ z^2wIs}l}w?Wip zw`Sp7R$$$RAXHuI6rftI6pTJlJIlS|9ZN}(O|)?;c3saJZd=f zJRvU0imb;V*w6`H0x?--1E%X#ttEZMu&s)}Evy`#z646cr2ZMED>ch`5t9|m#Gp(W z-l}8j<}x;~=(ceQ+n$bJhuuKe-I_LI*tXfaw?Dw$c@qu>1rBek}_A?qZ71b&J2Xr?~IbL+7bpK)*^|mEfAYf>lHHxmT;it*M5i za<}H#P@oO`qH~?j6)fsc)~yPQ`2M#lCMUE-Dv|+1015{xfOS-c5dPVp5UPM|1ic_E z3!0#bnxx5j5e^&rmea>%6TD1#wy`k5v_Tvu&$|x3fglpXRhQHZ>TwI>?6>(K%<^W# zqi`nEt<_eg+;t9f|{b!@bwHs89qm;d35uV{X;^%*%^L7j z>YVE@K&UK~i zxh$5tkM?h)@?pR~E!0C*NWu{)d}UcdTIdO>9=_OfI5XU~y)2YNrQWvuXe@5~AEqLY{v6@=Cw=W4GTga8Kqw2%$)mZ$5Xkc%%4_ z%b%T|{xtf_uTu}aul!BfcQ&TRM3T#J3u_p80sL+>uVyRdP2d*~390%2c%05Z%uLOk6$lTw`Ro9T>bhgp zc;$)ddd;mgY>uHOz1lGBmZneFs}}L7Z9&a*K^@WcifigR;e~>QD{jrO92RMlEUmaU zi)#4QhDC6NNz=B?M0168W5;U{tlRL~vM@Z+ma_ySJjFGGoZ?$0bRAxMYMHu0L4Bz~ zu?J@OVF@okzH5oX5%0L8y()BnMqkLkxYH9~pWW-ttk2zvcK=0rOS$%&Ciw5+>nA@v zxjB9_cO$p;#I3U*pWT{hj?eD3rYtn0>5cgH1rKrMd0NDE%4B&(}vsd$dg=X=@RTehylaLS!dCo2jTGynSot+G4rON+m{4E_yP^zMdNpo`V~suMQ3lLse7U% hi2sfu;mO-*@*AoDww(J{_#xrh?fB{M5sW^U{{`$FnKA$X diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_label_from_device.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/remove_label_from_device.cpython-314.pyc deleted file mode 100644 index 5ef7e2616a8c7e92a434062094584c1135a161c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2838 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 diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/restart.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/restart.cpython-314.pyc deleted file mode 100644 index 8f5da5c299b68c1547ace3996d74548700b2f9c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2778 zcmai0O-vj|7Ow7||38KqV1N+O;GYP`nX$zrj*}=7fq@wZ53_CJY_!I$rl;^UndzQX z*APQ4D2XE57m|~&QBL+=*2yK2a!V9xkFY_4Eg7vP%Ub1zm(@ml+gCl^n6YeaX}apw zd#~QB`s%$`^Kv2{LD0TA^~>_-L4+RiiPr*cLOA#ih;^hOimoC>C<#T8io={1OA?h} zluEv$pZZ;1fCho*D}{<-8g_X8QluEAQHKtcV#PR(J9My=D0a~#&^{$p>Mo{e%Av!h zbg_r_6nkm!Fk0}xKNyiX6I&R z3d)$s1|}A5N;I81dzz>=4D%8uD~66)pMslpO~-}nV9_wIxqMh2b;(JsUe{d|oB@#F zO*r^7^y_E=Q338pyxK=4MWDU~KMg1%4K9Q#DMzuR1I#4Fr})o_ZT3Q>($}HjOrEg0qa4(?@swZh&3DblUfjfz^m~(#jTx*m=DIa1f+igf>YRLoxyoAb~8revs zL}hCQ6Jk`cEz=s77h6W1zG9e%OO9C4s#RH&mn@=V*=q7D&qi3#wavn|wuIG&RmIbQ zcCd<)k^OY?V-$gt$z)3@+Huo^fW^?WE$>}YdS`AgV8conNshzWV5a+N? zEr=4kVdo%_vF3$e&ONN1a}SkmuCkb_x-pJbl?@z)JbP4h41C*z(5<82QJ;VCG4Jkt zvd@Pfs53hNDbn3Y;WYBY_ZdR*gTKPoN6|2cL#Q~S&Q1|dbK7n^%wnBGcID7w#ph`} z86iT&ybJv>y5d$MgesvYIvzCwmr-%5n8#_4m3Zfz&#ojqo@d<~pJT&QKQb!PNth|V zW881@h`=Sib6x{qLrC#A#Vct~>+<2LYQl3o_IkQc#%KF^R?c1)@-D7#3Qg$+gaLyg zbXl5#{VqeOUKiG-S$_oXjPIX+K{OkBRWgLJz*>J%Gplv%q?DIIJb<&L8}_pH(#VKB z4S56wO4iJ$((*9%Mn+hwBR*9K=Vp2BO(zCrZ0c6aq?lkxOH+m{wut<3%>cJ<_;T}4 zrTkco`Jkd|gx@|3eykA_v{+EHS54ipSX3uiqgbVy{UP(!AT?OTkvYBzk5|_1l?+pL z&qjDuu~b`;v`m#RRF|zqCP6pmV?G<#mq-uSk{UMn110X*<^g;1q%4yGF6(Jqs5#(y z#@R?m&OJ-T$-N)J!f%j3529Tg=Wm|>^8KCYi`&r`@5fRbgMmRd^?Pd@Ff;`^0O%CeIy6Vm@(WYNK?sv{AkZ3vyerGmir(o_^4k-sw64 zzs;@_yQ#SwllN1{cav#YkWTC)F`n2@q5h%Ut6#0|WX88LZ6uccO_k_x?~WW)(xn77KnqYTD5dl zB^+)Rs9FupFj>&OJtnPKbrza)O1f5OqQ1g{iV&QcBCA4Ru`vN@{C4Xo?f)YLJ)4Pex`pkn+bR%{v_T``M zoY)SXxi|X#sc%p1y1e(l#@nIOcNYJ$e0O=@FC_x&)4xZ&lG=8}eT)r|voLTVmRj&4 z01$+yEE(b409|cDgb_$pP=OP+nzUuLY?9}UlO2hbeey(PI|OaO(j7cdHsSAwlM4Nv z5`1094vKa=BYHc4HJ|xWLybM2;|KOQm=M8LQ+=)92 zz15C~x5*T& diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/set_area_aliases.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/set_area_aliases.cpython-314.pyc deleted file mode 100644 index fe429e2df9dd273466014f05823bf73928a845d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2349 zcma)7O>7fK6rTODy>{$4v6ChSN;U}sElKPOfoQA#nGmp5HFXz=6p3WDtT*wt_O6*7 zM@@Q(T2#?q;0V1CI6yejBR%!hduS99MxY|msN$9?6{xqq*Vni{=;d1#asy2#ZKX1WhBAD{=*%@O@0?i$Xypq9+T*5D9}$ zEJg|vkvv_f7%jv|%#*{#wnCgF3hg8bYf+69I|`kovky&)2Z9sJ5rp=zeWhZmkS6Iq z)Q$R)8tp-9Y^w9H;JcA7RTxA{+d}$O-EqE>N90MTf#r%*v&^i*(Kg+-9ikJZ;ljEQXAOHNbe3411f>Sf|u4}nOx5*gB4pw-Y(6Om&*qjA5o~NCL zW6xN#+MHgs%HFmRnaQ66qizuI4*7D;vQL?K&N56ewguv-UacBIZr%j>v5(uLFfO7g zL^wc|pH34&<%l>12&z1ZOi87b=h!j;62f85S53uAZ?zb`#cSil?Kr8_`*K`Ag8g>A ztbg?>S?)0J`{sDku7;pG;T}}&AW1bsI#da26QywvmjH)_BMfJG8)(_}p^}LMB9TeO zlmT4bHC@@6k#$>U70A*KFlt^g0xOMpE2|8G?{6?b0#FbT7y(JREg6JGG=Zl1cP}7R z2HhU?1y|uzPUTfW6>~gXGW3+CK}7?qgriIhW0N$nO~rA?Hg6+Fl^F-ubmFoT2I!fb zPlcS(aEVjX45wBHPn)kb z*@;|sr|E6Dgv-jbh@PWZDE2oSmcQ9#(_P}Nw!n&vpgc@QjB?*0l-Qbv90pJy1fTT` zY$)yY47B}#TS?q@*%q1r|3P#_us9{M&|A>$ za@F)0-6T2VR~EC|ErjyGYk46iLxYlMoQ7S-?VwjUZyHx5%Ila$b$8x2v{{o-u>u^T z?b{0PHBb@Skd9eCX*>_F3sw@@Pc4b#Gx#fkG)cY4lAEx##omx#PD{ymACw9J}R|88@(`GE}lBxxd*8y`x)5?yaX_$dT!)2$Y zTQ-d}VG-t>DwS08e8a+KnexUQ4MX?0>{%-0t5w=&+HM1zS`~g6U@Lw}kOsj$1H<>|f%x&`M0)YWdb_-M^0Cx*DgI;p z^5m-2x0Je-xt>|ysY&1JxzV$ny0iE8-n#?8D!(Xqcdz!3ZH9#Q@ZyQ*v1Y6APh^KF z-S{9(45FC^v^;7fK6rTODy>=Wsu@mx3vq=zWf!GuUQ6$8YpQL!*c=0u_ly6}MEWK)v|P`B-9X;C)|bXHj-2$q)nBeI8hq+a0zgjKg58Rw~&@yA1awRpc0vs zO&P$}UDK7FlB~-f+!@*qPR)lta8t>jT16TOOa_A_fCT}E5%7fDltEcQlW3a%;4(r* zu=S(QxH6}5Dz6HvnC0P^q37%(R5YMYILg#8Hc1^PF%Lb5S`5W z3jsT(|J1j}G{y7}-))=K30m<(M?E;;pT5huW*aNaPXNR%&O8r9vos-ex8goa?fh&o%hpni+qXJpOy~_*zHr_0gM0ZX8)Wx7v4Lx%d5_dY4m2uZ%xV?OHsul2VrA%2R2_ zLuvSdG`tpXyPE$#f9>LGeDHBc*LCq`^hR`1S?%7x(lN3TLeX6>kPwai>xSVV-yGX_ zY=pxdpiU_5RMNDnUSUrurfC(YShtxDkL*%iw*ya7(@GY0iEY9Ab-*3bw4!5Z8fGBT zaM7vgR+UB?un4n`O(oU5RJX8Mq`Wap!|)5Rsxwp=vu)a9R^2)_H5+~r;4#W(MaR3? zQk|LxKNw;enoc0$LYL$LNQ2^j0pdIKNc{L|YkJ}2T4HeF)KjVD zYW#=zwR0BM_sU=Wq+(DeW!%m_`xWUdC+!q7Cq3)imKNZqNsc!U{tH}$p5s}7iH za`-5AH`y}gSB6>h{>(mcJDc5Q)2^A=#jK(@%LHRC?ql0ZjRuNYi=nCJOvAyZ*TH=l zo1vxBQJ(`J;SV8~b#nIrhz)_`xJRgG9SyA``5Ee7M@OEZ!+)UD>*(NLLfaED^G~>g Oi!G=3zd$g0(ESI-(l_P+ diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/unhide_entity.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/unhide_entity.cpython-314.pyc deleted file mode 100644 index b72c48a9281bee267f61baf0c67557d5fbfa28f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1925 zcmZuyO>Epm6n_5Mzhr;%(~@r5CQd4)T1eLg6;ad*X%(98s%ltV6sZ!4<$5PsgV)}U z$C0*NR#2ge3s>OM0tX5g;8c#~fRH#9_0V)A1X77mZ$&|^xbenzO1AAto_RkrZ{GXf zd*c_1g(1N4{vD-6+;etTYWP>fz*;RtDgJu$5_y)JNIqH0*^LFtQV1 zV8;(uHZ3($xxvm{M7|)Lp`vUnstN@b`HF? zrZH;em=x2vW6!g8tPD5zJuOG+?VnW4;!8ZKp*v%hO`?s=N@3%$Vo71XZVH zi>Q*wKt&p}Ds5{>^kErR<>QwDS|~dJZ%S>+kPO*S3{{uW!_<8d16OT?nq~!=ri6vm zIfX%`09!3u zc@E2@MLhFDuae?2p*&{=JDY-mi_+ z#w04*d#N(Yv!>};9Z@txrrGgZp(|)Cr;DNGCYq9It~oSdu7g$TqdH@nE#EdxD#GOH zmfx`)k7srQQ#O2;XAN>ObSPYuO9BU!= zF~UKk4o|aQ7agYf44@W647)7!!+=(VU~ZsOtN-_ObwCj#ws$~QZ4=7)!ApNBBfqKDf6_&%cx!zAA>bMT^B)x1>y`ij diff --git a/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/update_entity_id.cpython-314.pyc b/custom_components/spook/ectoplasms/homeassistant/services/__pycache__/update_entity_id.cpython-314.pyc deleted file mode 100644 index 5a8e13a6dd8a9529287197c530533606a71624ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1938 zcmaJ?&2Jk;6rcUDy>{$4A0$nzh-_Ms$|SW#)e4XzB$KqYRgs!4RH`5yZP$}{!QORe z#z77}SWqFv1vnwNG=W3mKj=Te9aL4MBOxla5$Y`{h>9C;cGpQ9D06uGn>X*xy!U%= zX6FVoDFoxY7k_;3v4+qiIq8j92$a2B0J_LR99=_}Qc=n(SEnUbD_U9SdVsY`jK@Kz zR}y7|8-XrXNtRQ5pq%CznCn)&GFZ;?>@-@@Pe!NI5(rJny%Lq7@-QEsMq_9OS;jGB zC0DX%ASOBZh^3uC`P9bf<(BWiZ=N!*`fX~~{iaI_c|{D^p67F$yS~R@ntAJL^&DO- zpIf|GS-!9gn)uSCH%gV|yeftr-&=Lp@Veb_Yk`Nz5|6vQgDF{a8K)hvpDbMmWIH@K zuT*Qg-ep49U5CiiBDiQb8crBsDEArr7Uo^Fg17>#sn0fsDiEAcp6RFmzh6=1X)6Yd#*d-K5YJc%K4=-hXHukt{Gxm|N@c%E%o1Zj zgY9v1(Wk_8JxASx+KSjc z5{gmItavoDLaQUv7)Px~Fcy=C*(@Ahnt~u$4n!9{Me$hb4=KL?a94ir@?f?HtH_ag z^@ePxj}gl3-34$QO@s5hy){%lY#C{1fVZl?Z?X@1?a@-Ns8zUdN5>VVDoH#tEM;Cs z9F1q#sE2JV?d2${X?O@uptG8*T27<|QZp=Uja14k7V zGkxXyFO#5bufq;cfcwX}soV44%y05LovqHj+)JNkw~b@J7;`@xbKB#&uiP))&4K&l z^V_-SZ|A?xZ(h2eJF^=@$@#yKmQ4N4WKu;n94$nc+GFxt|t<`q_lOvAqZEB?Q+kbI3^$y+4z@X`?)5e&40e=p(FrLeYgGOk0wG^3wglo6^gzG(Ff-ac zzddMnFFZ5`K2Lv^{^Ff`#?0pFI}2M2+mQy}GhW!FcW!Ro+>L32@$UJjsb1HI2gtqi zqjVNNGWpdu++IkY%Ki~?-f6ks8kIE^DM+Z@;%&dpXkIF2`|fAq;3gOP7D9f;`gDal zX|xDsR9-DQCxt8vm3=OZ-c6_SiinYZPY2$@;He2|>8DB<3t{X2OZx*<|8z*gI@g&Rd>+o-?gD%^}?U=Atm+GD6|NC17iLG`+Vh? diff --git a/custom_components/spook/ectoplasms/homeassistant/services/disable_config_entry.py b/custom_components/spook/ectoplasms/homeassistant/services/disable_config_entry.py index 61165bb5..83af9c31 100644 --- a/custom_components/spook/ectoplasms/homeassistant/services/disable_config_entry.py +++ b/custom_components/spook/ectoplasms/homeassistant/services/disable_config_entry.py @@ -4,13 +4,11 @@ from __future__ import annotations from typing import TYPE_CHECKING -import voluptuous as vol - from homeassistant.components.homeassistant import DOMAIN from homeassistant.config_entries import ConfigEntryDisabler -from homeassistant.helpers import config_validation as cv from ....services import AbstractSpookAdminService +from . import CONFIG_ENTRY_SERVICE_SCHEMA, async_config_entry_ids if TYPE_CHECKING: from homeassistant.core import ServiceCall @@ -21,11 +19,11 @@ class SpookService(AbstractSpookAdminService): domain = DOMAIN service = "disable_config_entry" - schema = {vol.Required("config_entry_id"): vol.All(cv.ensure_list, [cv.string])} + schema = CONFIG_ENTRY_SERVICE_SCHEMA async def async_handle_service(self, call: ServiceCall) -> None: """Handle the service call.""" - for config_entry_id in call.data["config_entry_id"]: + for config_entry_id in async_config_entry_ids(self.hass, call): await self.hass.config_entries.async_set_disabled_by( config_entry_id, disabled_by=ConfigEntryDisabler.USER, diff --git a/custom_components/spook/ectoplasms/homeassistant/services/enable_config_entry.py b/custom_components/spook/ectoplasms/homeassistant/services/enable_config_entry.py index 849faac0..f88bd981 100644 --- a/custom_components/spook/ectoplasms/homeassistant/services/enable_config_entry.py +++ b/custom_components/spook/ectoplasms/homeassistant/services/enable_config_entry.py @@ -4,12 +4,10 @@ from __future__ import annotations from typing import TYPE_CHECKING -import voluptuous as vol - from homeassistant.components.homeassistant import DOMAIN -from homeassistant.helpers import config_validation as cv from ....services import AbstractSpookAdminService +from . import CONFIG_ENTRY_SERVICE_SCHEMA, async_config_entry_ids if TYPE_CHECKING: from homeassistant.core import ServiceCall @@ -20,11 +18,11 @@ class SpookService(AbstractSpookAdminService): domain = DOMAIN service = "enable_config_entry" - schema = {vol.Required("config_entry_id"): vol.All(cv.ensure_list, [cv.string])} + schema = CONFIG_ENTRY_SERVICE_SCHEMA async def async_handle_service(self, call: ServiceCall) -> None: """Handle the service call.""" - for config_entry_id in call.data["config_entry_id"]: + for config_entry_id in async_config_entry_ids(self.hass, call): await self.hass.config_entries.async_set_disabled_by( config_entry_id, disabled_by=None, diff --git a/custom_components/spook/ectoplasms/input_number/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/input_number/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index d18b2c36dc0e59b4ed3414d0e404dd98a07f8a5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DFHDQsJRd(dnc{eeyk2<)IIK?L*%|ghE$|6e&8jeIv@9u4-TU&8!Vhnrk|O znQy=O=9`&szTfwKJ|77O5VYT(`}0qJ7ewe8`)D?wO9(G|1%zryK{mRL6k%K#^Vpt~ zjP{I+wg{s*E{(~y+~9d_AMm7c|5%&d#(DC1U@T~dPNI4FOhbDc&bd@;4-$u4Bwnv0t^}3P zpvPS|ADT|JYH;ba$3CKjyO0v;LZzsERA~n~%IRZD4Cpwgk1L+HP@-eGW5zU$ySNX} z8dZvmMp-ARgy00VO2x1>TQ@4!Bz%W6v(qE$@Yu-k5636310z0Wl*y1~>6Wck>=8;0 z3fx%Es1)=Cbx|wndA^$I8n2jDd$L--Lzql(hR{VlM~1ah$?HT{!6}vJDa=tt%Xw{OPV`giZ4oN_Z@c9A*QA1gD+w=XOA(RK+3G^eOC@6yBQA9;b zv+K$KV5V}sq+;v#67}*AO6)4FIC7z6X!aKfcJdYsTGnifZF1U~NV}Iv=c<-%l+~P3 zHjN6Xu+kQ@Rhr~%!z^i5*-Go|W@?3BEp55?veK%mS9DudQ|6KrQq_hjiK@~L*r6N# ztz*#D&^O2{q@T0y;rTr_toQJ*7ch(Xy&1t8J38D^e(?t&YUntcYgzUhr+0?@#mzq#sQgc-44Ha&9yL$_r-%{?)h2mu_>tLDln= z*(D)Tz&3J3La8H`tpyA0jCwJqamMa!RjF#0%Fjekz#Ms)qVaHaar#`UZPyKsOhE@Nv4#5r|AL>HCjtzB*^>agU@lSCX$d_=@@DJg| zclmj~9T$qMc+(u4O`L*` zIde>iKWE)qaE}f3OMLj__c)WXd*oU@m2Jg+`ZD-3!}2sRUTvl)E48_E!d*uqbjj%lE-dv zP2yaUG5Hb|=48f^ zAnX<$pKj$!hDDsHIyx~mlu*z=fhWAFrwP>`veNn|<$ZB(Llt;c9X>d7*xBPZYufdm?HJf!6k;YaO2& z8~$!MN9~4-4?x0xu47jLeEeUSmN=%puVPx}k&Yjc0}uj_3IQ#`9})jmZ$uz!~V-Z&^>W1*KUrxu0)1miLkUx*Wx@Jh#6( zxvvwUCVp-Utz5tu^D*9T#lL;u=4#V>t2ZmRMi)3=OI&Kz4JZ|~UxHz7QOyLj@Q1+~ zh3Y5&G9IZ`Sscxcgty%>cnCinGUunU_eJP1?qVXdK8plaO0g>mSj4vxd6@d9V2vaTMxS$v8 zV#D}VGZjeqoMB@rONm>=^zf7MY%@GNJTc)_ z;#Q8nJBCpL95@{_nUQIAW<<%3509ub@Qdfh5U=)l@}TTxcu~a7)D;C3UY^zghllx!$vN^1`m<KpkI) S;HSgaaVWXIISX-`AMHQ8^o2J7 diff --git a/custom_components/spook/ectoplasms/input_number/services/__pycache__/create.cpython-314.pyc b/custom_components/spook/ectoplasms/input_number/services/__pycache__/create.cpython-314.pyc deleted file mode 100644 index 8dc1ced68740c38f0b09a33438e3fb4553d3ad8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1321 zcmb7D&2Jk;6o0c`@2>59HK|JyHDsMCY7L4VlvDwPkb)Cqw`o#2w19*&+N{UT()Es+ znN8K4B7~qvjvS+2__&Y*{{RQ>{fI=eC#tIVfCGpdGhRpOg#&YV^Jd?Bzxn;%ynTJ3 zKMis{eC?M{ZzlkrtHbHCVh43TK>)i@0|6UQLseAKgf^*st(p+J+!NJg#Slit6lTQ| zRwX4;GOpM3YPymUnMo)@J#};R6!>h`N+fX;_#+>$+ zWN=6Fy9n2Z06s*U+V%4QDR>paL>slCgzEau@iW|0FDL6IzvO~MQ%h!DjJ=nw%o?1R zH>mZWIL1cf|KJq05)wmM$U)6`1!`tJGcWn8CC=5fOH$=zkGltXYiDG=P3c{G#{QUw z%-*6cj}-DK$~Zwlg(Ex~@YTE?S*5krGQP3+(WzHmjf|zW8;f!uSpQ(Hw)p-sE?1XV zmeyspy|~E*b6n8{FEQc>QXzhuuyw+=J(tKFvq73p=nFR{7u#MhP-jY>ccBgf$=BC5 zCPbo!gkDbyqizbTrioN7aS0@`zU~!0*qw!H&<=$igsn}&Y#v``3u?Pv4z^cXD=qtYuWI3{05J*7<1K67dt{QZ!;8x%c6`d>L~5ewW3Rdrgx|4 zhFs7Vc4@0k0a{$m1I<~Bgc5J}Z?60}2PjXieatkL(-87!%79RIaJSZP#&V6%bukvVY z=5c1`BnheU2gVa?>?pVJhe|t6sPaBYT3@YvzVh|x-Wvz{^dA3q=i$!&^wZ049_nT< zFn6F|{%+*Q@gK(bm!Ia}{(Vpu?wThTbd-HGGWQqA{hv-&LUJ_`KlQ(N#roMWr2Q9c CuT5|O diff --git a/custom_components/spook/ectoplasms/input_number/services/__pycache__/decrement.cpython-314.pyc b/custom_components/spook/ectoplasms/input_number/services/__pycache__/decrement.cpython-314.pyc deleted file mode 100644 index 512e12ea7d003258315c82328bf952a5e3f64240..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2264 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 diff --git a/custom_components/spook/ectoplasms/input_number/services/__pycache__/increment.cpython-314.pyc b/custom_components/spook/ectoplasms/input_number/services/__pycache__/increment.cpython-314.pyc deleted file mode 100644 index baf6b56f8b38d581f2c07d31902a44993358c321..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2264 zcmZuy&2JM&6rcU@`r8SK69~BSl0d*D*ieN41yusq!4`>0*HI8v!fLZ#Crj;JcV-+2 zQYEXcDtamh{(!2daPOhV3aQ6RjRH-&Y9*u!A#OpTO?&H`-E{~pBl-EgnR##C`^|6O zjwIp{1moKiKi;}8A@m0`IsDKb(|9pK6U8ptRqUqSX*4e$W5>X#1`$fJxKe3f zv4{4gQ6D;tl;D1(gyzHZ!O?@>J|^cQW1?r#UL`z)GLgHzm8NZfET5Eb*extC*$vak zW;iFRS(Z&TYT6b7Gd^=;x}fHZh5Xf0`AUX&qz|r5T`HBGXvu1}Xt~u`FfdFHU0NU% zYdUqK7A$JgmAu_(+LmF_ih-9+-EjI9qgmHBqnOlZ%CHxnRgpFcdEnujo5?HYw9Gm^X{jWBJ}Y{Ut>8M9M$4gg|5HWtn7JT_uKDP~3bq4m{V0Q1)5FAd9gJ`22)Dt-Sl8L9?hqeOnza^orgIhtSg-|5% zG7Q=_>_s}jusc|#pCc6CE<+`QC=H?JIxwPHZp_!`c73k*jQOM--3@|JQ<&wa#XS?g zI_LKqg!aDi-M#>0Wx>EwlEop0D1~Hgo0>N zIwP1|hQE8D2sDS+!-(x)wai$REoVYF3L$X}gd zLASSU83JeWDTo)qmRB*^roP99c__tgif$tb9MQaj~*?u>$IEspG5t>!}O(!+&qa`!@zotqq)d96tpf1L=(e zxwQkid!?s|P)%}$&Rjrwr(7FlD--hL&s#a}XRWU=#k*an>Gc70R zV}rx)EYX*YhK7$Yqfb={`v&Q%MkzKITGSxm&XT|>CK%F~vB(wuppZcjU!s?i7zk~} zP$Jo$*h~zxuWZKU_QZ||&(wX#o;&j_XOj2?{If!8Eoekg2=@9FU16;ev)Abyg)bv? zZvtN|4f@FI9+chNa@@+i1{dmdAIx4UhL1FIkV`|iP?u+WSlaX7_}Fl}qgUOV6+5Ae z9^~)iGZ2<_5pn^~te>-|u4fm6YbrZi>fN Rqff-KpT{Tv62Nxx_!l5{Ls|d; diff --git a/custom_components/spook/ectoplasms/input_number/services/__pycache__/max.cpython-314.pyc b/custom_components/spook/ectoplasms/input_number/services/__pycache__/max.cpython-314.pyc deleted file mode 100644 index 9602f4e825c1fceb6eba183e3eab83f7122e241c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1487 zcmZuxTW=dh6h7l!@7j(p0h5+qBySQ3I7#i7f1gwI>1F~m+clh3u2)LORx;4b{0&%pYQ9N=jWjTE-MKZMKiYw_55M=<|qum zFfJLl!Z9=ULf>^7brLODK@bW{xM9F?S^D(WjW%s{+N}?}z3qA?(zZVS=xVnYEp&s? zSoFsJuEX%&>eXE?m}QIDYda9GIB13bC=8rH+;rH!Ydg|=3R{-v*(b>XpjL8_Sp(+@ z^g$5RC$m!#ib;gp&kD`V2;EeWn|c?SX*@q%yfDqmp`Kcbm92Ck=^AgU`0PM7rB$g_{w*aNt=LlAy zCeA>Bz#`7Kdy`0|M#&>WBecdG!<$AV&eoV3oI$YP@QCw2b9KPPMf`RA6 zl}kArQr<=nd-9CM4+5KFWodw&yYJAru+d_=LT!?rx)Q0}@rJB~u~``>E(xryt+6V8 zqt_BgdvPrE7q@M+{E~|QcO_PH89h8f{?j5zmp-`GLHx-qY6!dj&Uo^+J@Rn7l8mR-n`m2V+lStml_w%S%rxln!0Sh3De4 z3h`K{bP(EFn4z>9F7(;E;}P9NuA~AQw0H+ zhTp>j_5FvX>g3vA^GNl`Mz^zH!9^K;JCm3 zq*P5mE^4)?c z)L}<^E%P`q04__rT(8(&5DRr!f@QGu^I#b}g&kw_;yg6KRW-$;WaU?(Q8=pI9!1e7 z<`wf!G-l?06nJj4LE;5F3?pF+FA6y>%OBpk)uHWfr~PiPzuiz`ee3=AuJ`)!LN6SR zMSmRZxeV{EUf<(_*^WrOc0%Ea!*&#mqRN*jw`)quxxQAuEM?k=ZKeZZ?lg*dCumCp+m8IV_#yd=ci-a&rYh84n4+&@8PqG z@YtYq5IK}GO{T98qQLgT*vK+xm*nmerCdH}huT6gZ*MGIjutX5J0~YeofYO&6#Z!v!HzxK>c9#)$6^Hk#ewN$=$*gD$E4&(B#GF1s7 q58=WItek-P7&<3#?XkM}ht~W{FOd%Ux%TD{pZ`{C{|$1LQ2ZAyJcEV+ diff --git a/custom_components/spook/ectoplasms/input_select/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/input_select/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index fa343152b9a5d4b09afa8a0f0e0f7179944a170d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D2 gKczG$)vkyQXgJ7q#T-E512ZEd<821XA{HPA089=!X8-^I diff --git a/custom_components/spook/ectoplasms/input_select/services/__pycache__/random.cpython-314.pyc b/custom_components/spook/ectoplasms/input_select/services/__pycache__/random.cpython-314.pyc deleted file mode 100644 index cf2f8ea2f196b8a8f923d8e90a631aca4a1dab43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 790 zcmYjPJ&)5s5M6)7j(kV1xEvo1XFwF;z-}lg5E9~q5+s7;R1~?DaW>o@_O96-Bju_q z5dVRiiXTExi(8cbP!Khd^#GVx7*}TnEJpomTj0Bi)Xp$HKyid;8go3%S>Z?O z7jY)VoWG5;N`owDCW{hf2+*-MEr_uMvdmLm=y;oSyq2WnR$^2xn&LG-Gb_4>zfa_s z?R2`A0qONWK38U|NkElq$H1e&c?Fe1fyy=;cd^$j)tV!10nvXt(KdQ3@@iYZXhS7x zsWb@n^-0sdq-oOjbTr~yDtiX8`?xLf6V=DijEgp-a_Lx>^@H44lG{u2Xi3JuJ(^b diff --git a/custom_components/spook/ectoplasms/input_select/services/__pycache__/shuffle.cpython-314.pyc b/custom_components/spook/ectoplasms/input_select/services/__pycache__/shuffle.cpython-314.pyc deleted file mode 100644 index cfd4a0203362e3da44c421395dc0719881895b00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1674 zcmZ8h&u<$=6n^`|>-8pfLerAA1bLE>fJv|qq=-X>6opV*6(($}N|i`PoAo$eSnryd zaifHb1*iuu@IP?k#y`LXi93~mmX#`1s;a8DM5(B_@n&}|l8&@*-kX{C=DqKIvn!Rd z12}$s`?t@3H2@wfV?3-((N6B7G6EMQ?0~Daw5Bfgs?znAAq||3mf0-GLb_(QtY%Rb zo3^ykHr+z2)O4g%h3&$ddE`zJU{3j4t%>HOoUFnHm1s$0-1;VODC$ zva2sbt#mkZV-Uw*kax)Kc*sdF?gy-1(~=1AdW;E?efjrTbp#Fxw&z@)xK8K z6YIlIK3;FNlZjR|7|I(gWWL06i|e~Wa?h7(*v&`=@?azG58{YLc#7W%e5S(Z>V_AF zewI-t^=#5k>Zpuh8>EIn={wWXa5ZUe7o_FtvgjIEoH+~Ow6xuVEV&jIQ%ok)Tl3h5 zzoe-Vk>*D5e#`}}G>26pdc$rv45B?EdyEWX5v0Y|9p`3`2}b;$7ws`YJkAJ0K?aQX zJypdj@uCjd55kb_G7`o@lKmcwNT7f?b6dcKquj&T>#$BEi`ZEuG0!}jai5WOAp#-2 zNRmJh&QRr&b?Q!1%ra!YhES+a4Miesp?2~HDkHcBJNoCFm1hdm#LDXD1!b5%;?9vaxXzgz@+dLe*rJ~Rdt-HDtW*kh%&v$ITQCp_tRazQ=>m6MfN05uh2~jT~q^mIBS{gM(y}(wtX}(&WkRO*oEa|B_2(`c2vOm$bzZMM@6OC;8c!FsGZDbC?Hn!C~54W@}^YlulujSBJ)xLu4rLmUVReZ~faK#9;ntph(i5v-8O066SqA;(w zYbHXDn{l73Uqw=;o|K%%gOq*Ac~2ZfKHcYmWVGi|q=RHNBQX)xE-#}8ub>dByyfL( zKBHpfPDoh16Y)if!fl^^chpIx~(yYh#<`oON- zw-@i(i+|Zm{|W`p*Y94@_$#QiYffTQ8hL%yN{7;Z+!^BSR5kg_p%>;mQ+}A)_hK_UZ82=fuDjC><%Rp=%McAuPUL+=B2TiMn$}i z;w~KPX2Cw1gv$BRM-M8qqiZirq({o1%u_klDtrMam34QB?8mIRVg$daRxNojz~2H_ z5GPCcM`YgkiPryTfBiJs(^|5pWsiB!)l*H*z3k6f`tGvi&lZ%}S2GYl;4A2>cqbYt xjtouH{)S6WVBs;89|Cy_n@`~CQ)Bv}S$}Sw)0TgqdGF`1{+!wP2hd3C{0{`myb1sS diff --git a/custom_components/spook/ectoplasms/input_select/services/__pycache__/sort.cpython-314.pyc b/custom_components/spook/ectoplasms/input_select/services/__pycache__/sort.cpython-314.pyc deleted file mode 100644 index 2642f9635fce3adabe19bbe85c5223781cb8502f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1630 zcmZ8h&2Jk;6o32S_1cM@(6p4aL7Bu&z$LXW1#zg5q7Z5e;f8HhsfT2=Sx@4H^^Tbt z6D3F}0>nRpgv56b+c`?Y-Ojq)h@Ig<+O^bh-cfg+it6* zO7pO3zmZ4m76In8U!h%YO{j@^xCRU0IoH7}Zk9HSD>ufe0q$ZodZ|gztwFtXF!ON0 z`KRO#dBTT+^mspHjk=Lc_;JjYuR0!lT5VZ z!B9P95epRB)mFEq5`Li4sOzx`)&3gq4|vRCv=X0(0n^cQbR{ROqR8K#QiSoHnWJ`6~aa4(c*nZR;3K3@?%9pNd!YxDAsM16I<68 z7>I)Y>7{9d!!yoqp>hb1Vat5?8-Ol;n}d&yp5Ymu=~`e z4iIvIgu(BEsiBAyyMxpD0dOqN4#zf?)F~H}Rx{x7PPp3)hEnl94aRenO{qE4#F6ts zSm5Hnqd!(!G1DHX-+3bX)=3$N{#bZvzS-DiQ*7mfLmBT)#9(|o1+5m zBL}8#jEY)wp~G05ag8BI_QqS2B@5Eqp}F|8YyE#-&|T7=e_c~yZJt^eKcH` z*2{a(sKMJ-Xw=Pv*ILMkh^0izJ0}|CMyGBi73wP`C?BNs68cXex$4XPIG}qXRE+j~ zibGcnZEb{aM^w;6R8dG>;PUdam^tT!w8UHpbjspn;R2Ld+>0hDq)Qm#IU+HtLTT#Q zojY>pj@^YLci~sJ_SCH&yR{>?_Pe|EpS02Pt2<3Yyn;%nUQS#}W52J*R;IMiyF<)Q z^*}!x`cZx}Md?l`q>4g(AsnxZly-SQsnFpPr_1|(7$?QNYH?G4y++f z(L?7IuWF&&7NxP6LPfrg;#)X1?Sea+gzDA951yj9|H4Kpq+H27wNs~F5evBMvUi5a zb1YkA&q3`E@Zk#$Z?c4cc+4L^!p2LAH_kIU?IU|X?wH&{zsKatWq-la$CVX+v6;lZ zmjU>mxQ)L0k;xSlBg-(1KVbF*7EgdYgY^@*_sp99({B9ZTrrk^nR(~OFMgX@`xo$# GHuyiA!LdaE diff --git a/custom_components/spook/ectoplasms/integration/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/integration/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 23247e2ef896cb17bda50fb8bf404b8db9d450fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#De=zk?qtL#4#H;QVz~&yIwon+OuY6 z4029BRQ(?~afJ)wPf1TrSBew~MTlFfAbRD^j-xohN}l<>dCza&`@Q$(!CWeV;Cu1w z!P7Y%q2GmY^~EGmE}sJ!BO7safNZ6vR8_973ar+&s?PNYYqeO_;6{Y?TD)pBeHwNm=JR(}Ks2J;9DD4@m4 zUZvi|rJe1@PF>UDj_=?IPrn@<2aE{|@lMEXY0;hli?ACqS! z_<`&~9Ij`31IHKcwM{Z|C#gl7Dh1{8DS$C*A+7+C>Oq!kw!(E=(GRWo5S3R*^XRFb^WC{Bx0-(waJ ztd39G)=*|*u_zTQqCw~(OUozR>(YqWLYSPA!rYXACoZsb(R3AvQ~bmSZH1tW%B2Nh zjP}rh`uTH&+HkglzEuttTd`GJv-P41amW4?jD?0Q1~nsrdVEMps6jD)LBO&h3gB=i zuDBC6if%wUez)ij84vo{4f=zCfCE-!qFaid%Y#ABVSQGd3c4uCV8xrk7Y3tn7UM}n zc^HSwQ$!Y~HLGwv%fT{6Z;=+;cq5jdOBZ7MtrSU@phfPa2?UO4fz(r%@{fB;BRamh z01?X3M|PC9#SgvfI*_grVmBl1vu~bh=f&F$~4Rz-AIy4P;FcHfA`}7^y z!Uf#*_?4j~9PGoj8~Og9NtAd7T#V6$fijD)((5nN>&NCgU4la@`naB-4^51T(-#yb zFzyHKVNYNn_q(Cfn`qJ)cYMlt&xeWyppIkQ4qO06JLo$;2@P2%DxRZI^=NoojxY>w zt+|iw%A?wD)85CA8|BSrr5w)rgbkqN*!y1eK)7-x%PzriNJrQ`bV%20huJG_Kw$EF zfzzhCxX38)Y;V@;;i7D54E+<0Jp?DBqVEZ#1&o<^bzJOln5id2?lFv`{3R2DW-4CT zXvqR#WmsOI*ZTHZDmUIbN#(~|XXg7SX5y8(@zUJ5h-r()xPD=xdmG2Pb+Y`yvA%G2 z`|eMzA6v(I?yY{+XYvB!yPnU`HP{8qogqvLuuSC?@zKC1T?+k%!ew|ZJ%=%P+#JFS z*Ha=BzV&Z4foY34U%06`^m+r2GHQTRdQW(cPbP`NTaKcUl{t$(xaJ`=zmPrfCH)u_ zVs_a>SS~b0QC_2W&(WQ8WSyf2r|8pD^w~Mu`a{c{>bbv+tg>-D|JC0J_Q>5o7U$s# diff --git a/custom_components/spook/ectoplasms/integration/repairs/unknown_source.py b/custom_components/spook/ectoplasms/integration/repairs/unknown_source.py deleted file mode 100644 index fa05dfcd..00000000 --- a/custom_components/spook/ectoplasms/integration/repairs/unknown_source.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Spook - Your homie.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from homeassistant.components import sensor -from homeassistant.const import EVENT_COMPONENT_LOADED -from homeassistant.helpers import entity_registry as er - -from ....repairs import AbstractSpookEntityPlatformUnknownSourceRepair - -if TYPE_CHECKING: - from typing import Any - - -class SpookRepair(AbstractSpookEntityPlatformUnknownSourceRepair): - """Spook repair tries to find unknown source entities for integration.""" - - domain = "integration" - repair = "integration_unknown_source" - inspect_events = { - EVENT_COMPONENT_LOADED, - er.EVENT_ENTITY_REGISTRY_UPDATED, - } - inspect_config_entry_changed = True - inspect_on_reload = "integration" - - source_platform_domain = sensor.DOMAIN - - def _get_source_entity_id(self, entity: Any) -> str: - """Return the integrating sensor's source entity ID.""" - # pylint: disable-next=protected-access - return entity._sensor_source_id # noqa: SLF001 diff --git a/custom_components/spook/ectoplasms/lovelace/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/lovelace/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index e64d1a22f56d37a4f782b9ad5728b738b623509d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DUUzA#qm|0Y;A0MBYmst`YuUAlci^C>2KczG$ c)vkyQXfDWM#T-E512ZEd<821XA{HPA0Q?g-FaQ7m diff --git a/custom_components/spook/ectoplasms/lovelace/repairs/__pycache__/unknown_entity_references.cpython-314.pyc b/custom_components/spook/ectoplasms/lovelace/repairs/__pycache__/unknown_entity_references.cpython-314.pyc deleted file mode 100644 index abe351a22fdd6c4e352152730a60f618617f07be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20949 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 SfW!x8Mn=Zl43b4GKn?(y3o}0e diff --git a/custom_components/spook/ectoplasms/number/services/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/number/services/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index c9948e008c10e365a3e64f3831d7de6b60b70507..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2652 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 D(Meta diff --git a/custom_components/spook/ectoplasms/number/services/__pycache__/increment.cpython-314.pyc b/custom_components/spook/ectoplasms/number/services/__pycache__/increment.cpython-314.pyc deleted file mode 100644 index f5629d563aa4d7bb8e59bf259150e2809b590fbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2483 zcmai0U2GIp6uz@FJG1{kYzwr|>=a0~T6Zfyg3%~-E$l`pWiU%jD3j^zv>lwCS?;}C zY2l$!V=6Di7{fl9MjymC9(^=1KAIE@w%$l2L8HEvlxTeO+?m}{2#NRM&bjBFd-nW( zXP-|cV+hul&wYPkTt?_VKXewH`4TohhkXHQh@$gI6DmSEL_>K_hbkf!;VV|8a+rqO zJeexMlPZyNlt#-j8Uwzhg)8xLf+m0#G`W&2r)a92rs+JI2tVWh4nj2rq1{}sQpuFF zG@D1=s2^#O9Y~8#Bqm}*y=_7G925hZZqq`0(O_&occ$Ta@2W4T?|4nD&Ukg(DhvuN zZn&;T4QhKX8H023?RUmU^-_7X^j2l;_+W_1Z=O0iQW;~3v1WbJ!lN#==^O~|9hoE) z8z%Mj2JDhoZ+Ncd(lZvGwM`42cN=8RHFaW9oy>Uex%#Z(G%eP(<)9lxpK?5da&y`* zONQf^kGw%h3J$`?ED#H50#N}19y*_+q9#yjB1~m1M3sri__nRj0vduO9uxu^B)Xm^ zF%j=b6a|{lBnWDF2XZnrsmU~@DGSSdczpV;>L;sEW>qj(iB|2i|UU zT9^?uK?`Z321TREMG-7h{tAI8lLJd|m>(n)Vu+(YA6vAET_%k|cD_asXL-uQb%PTA zlY7|lqUpI)_H@x~66)1;vy(?sB)n>hmPx&aV~{#2x_%iH+s=~WrYnWU982hWyPhmv zXFXe^RoHsP3za+z+XDIr^@a~T;M={-?)5Ny*JYIMT~|02L9x_>C{P<9i%bZ>=7L^8 zA0U+6xD7t+L3ti1VSMxY5%G%9>7QvofV{&RVl_0*L-xcP=aGA2X+l*z*p_WbXG=rv zzUN|%@VU*?c;tzPaO_9b@Od8zH1V($+;3WQl&ACm-k&|gapWnl`}u24s!9ncRPfx# zsmCpV8@Vbb(0;igpG$V`s&c2$XM}U^k+W}{24D~Ujvv?u;u8~bC_aG5>Cu3BYtD4^Jx_Tj<=#+Q=#hB`8;$eRb9)f z5tVxCq@@~az3EW90YBwUsbhn2ChiryO>uhV24C3XA1LB;w<0#_{RNUQEb%>+pSrciFGo~!ai;g zu54gnK;;WVxDo3Lz;wos3Sg=avaJ8HHhY?g-)=vEhQFXo))OeR{o3V^FR!Na%jtZ} zSd`YX-Pe0Q>ABwbNnh*Ijjok!VNqV|>iz8GV)S7IC9~H~Ts_h1SsJ-<@tb|CiPu*W zum6(HUAuDiN~?17^taM#dUPc{3T>O~Z4I<`F6D1-Uwv`p){7&{!*8r3p(G5i4IjKQ zyDkdZ!|NhSWPlwl#_XNUp~djMcy=|u>sEZ%kMU=I>Dti> zujY%l^2L>|;lr>BL%` zg&K>TYVdh!I7}{i7B(#w_U9Lqi8%wQ#Lw}!-MUWrU^R7vQfyB)sYO69w+{F4ji(gH zI~(sLatOAM(9hDyy<~3T*jlP*;rLoAd(FA(w2rK#_AQxTUHIa{&F+45AMW#16Hp&~#vHw{%DFO~YrrJJ&~+dJ&NLge=`{&Hz!`b?TL6QL z*kHIz3y;P?AwYdUD8Y1zbOr$C0LYMEy z1gI53_!;&5j`rL~@w-U9k9Pinj{c5b{T03ZhnTr56&}j{!hz-7OE<6F$-VhEf`cD~ F{{YRaULXJf diff --git a/custom_components/spook/ectoplasms/number/services/__pycache__/max.cpython-314.pyc b/custom_components/spook/ectoplasms/number/services/__pycache__/max.cpython-314.pyc deleted file mode 100644 index 5b152e6072907ea750ffa0dc23e8c476881f5c8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1732 zcmZux-EZ4e6u&oi?0h#%x^-J4#cM}VXqpHZ9}m+YO4B7`gyEt|lV~m1aT<@<4)@+v zZN)Ks302eHIu$YT#<{jbKg5xI{ktFMo^yWZoI9UM zClJUFul;)OzJSo5d@>d(0Ql%z03)O#hE|b^Teulvks`;Dme3TL=wqQ3ZAwgP%1rj> zVk_2+vp8T;jkXfaBuf_2iuh`Hdq+m7!0(V+sb-p`i|9PMf>e15sj-#hid?=tb_1bn z;29T|QRB0yl-SNM4_x;%WkI>?4vErpZOg2bu$R;w$7MRRT!+Fm{mI?i4Xxg6)IVso zZRIiXkfhzfGMnJDz?6H9{R&E1-{Y535*>sDcVlDw4cF;f>s4b&nQLptm^50YJa1LgVD6x=Q=3*DKNHn3 zAX)|ID}yaBrDN&`T7LHt-97e3~sBM)~wmLKBw{Wgo^+ zBJ(27;SnexBP=S8IX~ztgwjVp1DHWY&J(`Pcg{$+z*$oxup8$0Fb_QpI#Bdy1y(t$XJo8(6B_Owc(;j1!mVfmJX7&3lF=>cEy2PiarJVNm-UXd*Ife$sr8&?| z8)kSe8_xdIEQy{-&3>0;K#fdrUBRa7R0K3PH>Z$EzBCzJ*Yh2PTm8=QuAEBh{~hs@ z+lCvSLplyqD3#kwPVXkCe@o8pPfmZgv{xwa7Ro!*3p)!pc5?51Q~x7(_503>EqLQ16F36Pnhw2xWZ>9s?J)Wlih2hNcl7x+is9Telof4ud5{ zzItBM;DKS5q3MhfYi-C(3L4xlnc@Ryi}3f@AF=@GGz|CAWAV+W>HKJEKa(Hbd?_9m zdZ5Qu+NBBd3jDd#-68ZRI8A~!W?KWxStq>4y&^o8rape^D#xu^2|CgDKj`?2r;nPO zhB@$`)}TmYIUS=HyB_d*gH%D6Hwb+lhC=~k{1{z2Kr;tO nd4}G9g5Ehm*PjX5Ct~FKs302eHIu$YT#<{jbKg5xI{ktFMo^yWZoI9UM zClJUFul;)WzJSo5d@>d(0Ql%z03)O#hSrdZTeulvks`;Dme3TL=wqQ3ZAwgP%1rj> zVk_2+vp8T;jkXfaBuf_2s`zSndq+m7!0(V+sb-p`i|9PMf>e15sj=1Ms$9N2b_1bn z;29T|QRB0yl-SO%3|#j!WkI>)4vErpZOg2bu$R;w$7MRRT!+Fm{mGqM4Xxg6)IVso zmrD^(TKwq4TC44)+CzKYBn^jIYzq$NnyziuD77fl9o8VkC9pbMTc?cZ2J?*thwH9A za2?ZOD<;{r43irTVO{U{jlj=fj08V?Q~@wTtB7HsFtV0qf{K~A8fB6iVX`Vf$l?_0 zXIV^*vbZWih_aXRt%i4RmjaI!unaCMG_YOPqT%?D4!lmXyWpr?86vJ zWM0HMJOTw|ghjeM@KjaW+j3B|%Jw;>r&GmokISSK_2NLa=GTlD4-&0e9m0#nyP(r8$Izg-GzYqA z(+tmL!`XkDCD9Y9+3%7JsF4Y-E7){{ih$D}b?Z^_yH$?5Nw_6p_QLV0I;VQ1mSPVT*L>VM>}e!ucCSK3LH z_A`@vnfcw!{Etg}<@#>9{xGw+7h8N3Tl|OS;L0y+v05HKNZ{Hz@j+4sO!#uUl<;C2 zL~ZlBOK6(yc7}b9p&~yY>iuwMLeshyp{#GgW8lKJtZ5zB&@{qB_oR+%>z3omVX&mg zSI=u2JTS~MG@UVGtq++=L4(^RQ+(iT5&j^OE5c)G>f@)ba@?Agpc8%ngO0y=`lxx& zFbDqA8Wc%v+_QuiwU;}6+Hi^KzZO9}r(^VD*8^T}kSgf%2BELRa42AmAEQeLXyyPZ m&(Ql%&^rg{`ZFQ>M6CQJP2dLJ$-n)}7r*Bh|3-Y~hyO2o`M;R} diff --git a/custom_components/spook/ectoplasms/person/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/person/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index c23d5bdfe91399a8f04944c6667fc69d400c7216..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 194 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DTZlX-=wL5gX7nkaLPT SfW!x8Mn=Zl43b4GKn?(!%ri{@ diff --git a/custom_components/spook/ectoplasms/person/services/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/person/services/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index aa7fc57616868137062a7efe9ce078cdb76a7cc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D7&-6`tiTmn;5>qW&nwj@FWEgNjYIvYf_oP*jm+k+sCIt3X8%%ZoL4C9bpF zWoCzNtR5`1Md9}19CI|#9_(IhpPWMvL6F|5sH&7f3@8YkAeTa|f*QT`&F+#RV;Lw= z0-Sj}?{DUN@6DT+l8GpS-ybLcdhH((guY}StzXzDxPv=DY#;>@bOk9~kt>8qXpGUJ zB3}@Q;L&_BOhi}{ijjguq(TqrDMU%s=m@hd71M=&((mc?6bA}}WN-`(qGL#j4kIPD(l^cfDr89ECr~!N-oI3LoOk5Y z@>@;=%T=dl8o4Y-W14L{L?fnSyYQKK^R4onI$M~Vy;LmCm$Ez+FTAlhQ!G)jY+%>1 z>99X95eI8)#;jvmMulmEa=PHuj2YK8U831!4r2$yYG1{%SIsr`x@MWW=Li-@X0Eyf zYZc<{YDTY__L70Gn-v3?v4EV_EDJt+=WJq<8?#QW?%0M+DghYY)Z>3}2mb2aR)LG8)z9_ z3BB@LgmhRtf_~0bIfYX~3a<$H5bQeq4LcVVDiC4JYK)55APsC&p#(9$hal|Ys)K79 zahZp4TF(1vl&>^g;?&eiD;wOr%Mv>eS?JU)&8@k4|2X;J=x+YmQ|0P6Xk1l;+&5H} zj&!1!>tv0Cf0u=C13g8<;pjivaQ9dHZ2FLRUkAWNl^|4%MltyR2BE~ktFStb#ym*M z9;&`Q(uTs_>nQP$viN8qmd zU-aW>DpVIQ2PKElve>d%<(9)`@2>3LSIdhI#Rpz^kcxLpu&;9vV%^_|@PF@vFZVv{ z;c&kS--(VAzTDT+yxiZSKy#o)e>Xp8mtv>AQ{RcB7EY}>-!ANVq(rVkWZ&a9_=Rv3 zGDV3@pa@zJ&+;ahmDV!_&DJd_*s9SkW>z*i%+9$!I_oqnU4}bcUNvo9ZddE8G7Xvf z`q4M5rW+Kv3=|VIbdzXTEdxY)yGz=|DKJ{f#;8!$T$c)PVQZMBBaN;ZMD?v>mVZ=g znV}J=Ms~&1KFz&hSJXxw)C|=mMvV#sKTjXO^NX$Y>1O=&zKBvow{PCQdF$q#<9Ao@ z+xP6hjlZ^=>9p58yVx8mb*b$oGds!2&E(`>Jbkb?i^mpAQUwAns&x zo0;5$fkz_`N1Ep^ZU1OtJF~Dguz0|Sj;Hr|A(1?YNWIagNa~4x%ZC5BBXHDTzs_BF zf%{l^Jqgn%Y3{;^@JW9JXv{iaDceUSRkgJmyAPwPT66S<#b~%G-)(4C!0A=hRTI0! zGNBnez>cV@?o?FO&l6F1YMN=&NGlfnb;qKTV!Ychv7ysY$PJSd1+Q8hxad9^_d7re1O;}_V#5&^@@$R7rQaIeAeJM@__ zx0mSOxVW1f-2kNi2Z z8&5LWcN4ukiKCw;jy7M+Z6)%}_zw@ld^Wlt7L($}#iyxOH}JQ?c90#$XJBIRtu~;c z0em>b7DRHRZrW=YN*%ZWNuy30PQ%47vxPBuj~P&K{>v%XE&w;@_c9M!%$}TBWb&DA zdE>o`QTLv3erXPNv#W;1%wUP>zrysTR&T`YGN*~5+MOSP z?x;}dH9w1fg6BbmwQ`poc%SDu?h7>X1dTsI@|P&{HCoz4OOMfyAER?m(A3v_>amb} UEL{9nJkCX%{iptoVD@1CFAOBk9smFU diff --git a/custom_components/spook/ectoplasms/person/services/__pycache__/remove_device_tracker.cpython-314.pyc b/custom_components/spook/ectoplasms/person/services/__pycache__/remove_device_tracker.cpython-314.pyc deleted file mode 100644 index 39bbd41db3badd43dbfe03e98c7f3961d29dee2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2999 zcmbtWO>7&-6`tiTmn;5>`k_QCcC?lh8&qtvkYzV+g2IYyi%cw*Sp_PBSYE7mSK>O$ zU1oNu#_GXBTNG|D&M`*=?ZNKF4p1OD^iUM(or*G3FTNxB2uZ3^p)Zy?(?GML@7y(J3)ULr2m*35a=woRRBgYsa>t($Y6XL&?($Q6uT466gW>#SO9>bhoIhVKX#kImim z2-bAsM>S{EEoaHZ>y~Z;GZB*Wnr*{p{}qQ=5IqFLpZbCi?(iQ#Y@!uJ zI7mh0MwajjM}!rCU5Sv`ic}r!Rtso=i=X8JdL>>RIZ9$B+L8D+8$y>v?noQ)>ex5e zzs;l4u~*<`BY|EqLefeUQY(%kJ45=F7|AFSB({$xeF#IyHZHLo$ekp}UVy4hTp62n zciogV*)SQe`=%Zh<{ZT=OOk>t-RKzA}VuuP9$n`@6 z=@(aBT-S)lJWSA~Vo**+z3CCRuIlZo@QNNQ@FG;B+psmS?iGU_6+;NT;*lFGG;Y(R zs)lV~swzF!4QQd8H31=bER&n)85)ho|IUU7KR#g7$Hf0S1TNCUtT7rT;Qv#EQis=I zbpnn1s8oGc?@;a53R%~Mw#=EX3i=VCi6Fd8`?kGzN*KMI)u`hFtX!~$k zgY6xMW9xs>PokMfL%be#A41Dw+hUDdj#m9+vp24`7X$hbyzn5E?3G|&7a_|=5Qp%8 z$H7H)QUSA_Z;Z{mP1}&+fR|S- z$B;WM{5_dQEMw!un>EV|8(#*B37UpQw41gGBK^I6?%@m=t>hC_sA--@1vt$$%nFpo z*G!@Y)-kI{Dz(kf7_=xm|LK6{-F9@f*#I?DwTM}#f(=&%;4bo*=ToO-_QUKPro(~Dnh%m1o&)q`Pk$dJ0tJY? zzrpY&H0yzcUMxP!Kg>TWJS_ZfWM}%)mUQ|q*BU*&KQ_HPc7AK@{A1%I>qG1JiJc4A zw#OD*nZ@_TKW7&Aax;%-Kf3tg#gE_KIahAYyw)0d{qDkNgH!i^xIK8bl{|YOqRhy> zJNNF~y>ox+!RjODq4TGSSN3w<_FCr`TO*Yowf%H%H$Az%f1`l{4l|GC~{qbi=>WhEH zhJSm)*H)?K4%Ga3%i zcbl3Wa{5(u)xsXJEx4{-V8>L|aCKD;ibXWsx@I{v)=mh&?%GsR%y*j>HVqol*J&!u zb|}iv*)~m>j@QJd>KBdZ={2*i;j<7I&8jLqIf$jJ8X?%a*(9b1-dN1|IX19r!0>ho zhAa@?Ef{`|{vy1#pBmb{x|e=ob75af+)Ms4`TpD6(#iWXyOZZWojmvW>h|QzdjX@B zePLTV^?-jA`%P>wnP%AUrTTYMCq7M`Xgy!pP8D0p?;J+?eEc9Prp3*x&ob>xA_#*; zkRQe8U}EsCHsPuR_;7?Rh~##|a@H{PJ8%J#W`i`{riU-Eg>iVXnb38?+p5s%0_We`b5xHPdEhu*CFVV)|11*2L_Lr-Qxnx{J-=zCP8< z2n`j!-mIA2f^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DPO4oI8_+@keFFitREkrnU`4-AFo$Xd5gm)H$SB` dC)KWq4QMdPWyKso;sY}yBjarb$s!gY2LMbEH`@RJ diff --git a/custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_ignored_zones.cpython-314.pyc b/custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_ignored_zones.cpython-314.pyc deleted file mode 100644 index 9006ba2fc647e8b73b3da399b82ec9a715a5860c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3210 zcmahMO>Y~=b#}SSZ&DN`T5=?tt1nA}9nyBxDzOFGf+U-S9f{q@L|s_ktjQI%3AxM8 z3>!=6K}d_B=%r0iz&*+*rj@zAZ@y>^*#(5XKLN0TR7BBTqzYLfBT_NRVKM8;NK|rQ zPgc%&sn>y}tS{rI0e~e{&IU6f8ggK7Hk^sjNG3|7&f1sl$i!$YiI%0&=Bq^?Lj9b! zKiip!(|8gk&;U{cJxC2M$0j^38tqa;Lufp_-t|t^w%=D?S8m%itgPA<(@2dAETmhO zO?7J87J+4C>2_{jo6XG6-pDRohm8~S`T2ztiX$NI@NDgi#j!CZ5tO&OQ$w2G_~SPqPr0GQ~);6GNJ<5 zM7*1z9#x={DpFbXQ17yD*`JHIUv%N~A`JpgNR?<ls(I*kM(nt2$LEHVvXsTUjx!qEfRQ4Jc;GvawNA)@=(;YXm^kQmQS%QY^3!v*bRg zHLq*U>zZ?YsUQHi{Kp5E7%VRA^#ibh^60Mk=5G-y!d^f6nXoFTf-0s3;N9~L$7Nn@ z&>FUwv;e&P1##o8*tnuo!buLYTj_#rt(c{Bp+=}((F%5@3YZopX~G>TZ4{_oE$gI0 z()*mIT@{e@u^gwWYb>m3O@}iyjrF!^G1Y!D2t1H3N0e76DaXI%pUov_+JkN62+sjN zLh$&2@1I59Pm$$i+f~1m1f?J{_WQ7s+bpYoh{shd7=r%(6!GH`T!)s6F})0 z2hSxAZndDir$s-B?)2>M9;=1ClCL|mtR?s@w+?@=Y7 z$UXPSP-WGdOCH&TrRwWJc{zq!61O-{i>iNe--7?w2LeCxYVf!>^K$EzR%%V68=oT- zK&K(S522rUOkq5;%3xb!jQt>0 zN|s(RnC!$n6M}~vpF8jyJnB;bxQ@Ssu7NvNA>ZYNFu3_e&waEZ1FMlEta{cxuPg5| z;oSwy0}c2PQ|DRWfETXEGYaOe!U9tz!!qty@iaTv9VRpyFaYs;-}PHj)8pk74>i~=^GRulnN9O4QjI>n@ym0vh% zc!aEr=algn=HbrdqH(WQ;^~ps9VV^ngfIz;jm|t!u9&xIS9H_DJP$D0q(+6Xc-1Dv zyjKRco6HmD(Thb46&kTvd)fxU0N+)c%a?lxbMI%;d$wtfdznOx@(K$V4$2$BJaCvF z%!Ajsc0z53EY`}mj`g7O5H8CZg%8(&kwcbZCx6t4gK&{_LZ0Fl``RDoy>D`7;KECvG#qUpDHh%8NWS8;vuFfSXJR`xT91wHoEq8fJ-r#) zR?gRz^E27--aJxMu4!+iq<< z6AjExxm_3ZTiK=CnmT_y`_7VjTf3E;n^~HlW6@^BX~wU>;)M0L)^5$_{Sx$?$xM4fHj^OFi-W47hzt~ zoDWY5hzjKmQ7y a{f{U1Us063FN5cV_*Q)6I|Q?%v;P4*0490> diff --git a/custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_tracked_entities.cpython-314.pyc b/custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_tracked_entities.cpython-314.pyc deleted file mode 100644 index 2d45cb0066984fb117dc6ed831a7602455c655b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3238 zcmai1-ESMm5#Ku=zduA#qNPN#c>1y=L?LZQtrA<1El9FS*pbMi=1i z?APqf?96Y5Z$!cY1nrZ*u6{V_N9f;t&}?pp5DxwX#1>KzMXN{=GD2FUVuI6R#+8<+ zWYeyUJMEz!o0c-(w2%6MmK1j;kPgzIO?xt-beM+I5gM__-b_b2N}~z1B8@d}EqD>? z=e&KH&UB2%5-5%ak>c+`N?;{A<#M*sE+sgOCPJHC?^P`8uKcEa%c^2|-6|V;azbE1 z%``2lQNuC`48zN}vWx0`dU5_*X6Y(SoLbB+E-kC`H{Q+OSmM<68*>Yb3$W6mo7A8i zD%Ohzp?CwPJlAhry}GDOi0rgRHq5-bW|XLo)w`zkk*PNJ8wJ98o#moVRjpKNTLC6r zb9aE9mZ!GBieAwS4ATQyY~3@;aN~kTwVRcKM)i5i!Ue^~0A{uPUA-XNmf#zz1V)A_SDLydnZHG| zp%$q&IH(N{j>Uk(a$W$L`HwfQH@v%W&=15G%Ar;9?cX9)fVqD33t?SQ1Vu~Z< zzL$BhPOI2t(voHBe?j~S*DPGtDB&!J*!!uxWv&^;RK7~6RaWy>xdJ;)N>YToQ%cWM zt5VWPnWS36rW`|%)T@L}RyJ5jRU4kCt19blGiS1WWeCI|UA8taQNkVjo`1HN?d|}k zk*z-`cniYsHwc9fehu0Up#<3Aw!OeVS+4Q12Js@~P*yrN4a2F%?gUsd>-p(=)_1}j zzyDZ+)1hNiFq~?<*hQftG~>Bwzmg&jquU+L6DQx@JY5lULbkKH_pqO8kWqBy3xqxb zP4EFPk@xxP1D(7nQPoUd9t=SW;kdv~#%^in*PE=gepbc-Jq5G(Y*RCUO(>`at z{hMTq&su&<}FQ( zO`vOEBIHK{fb+xXXD&mS2yVugu~97QSk@rGNd*E0AU3(#<6wnBTC-;Hf;>u=05k(_ z2pRneW=f`3)|sz~9ZU!u@qO;YZ}Heq0^#`nGP(jDSpn?J2_f+G^R9bn%MH?ok4fv= zbiFBmz=RK$Fpo75MNFP${zGxNDbL85dkgc=6m?U-SHZJvvOR1aNN?zxoGDp(twd&% zt=-dbn4E^scMz6!Bqm>fb~*d#oj>QdC${@OpZj}%^6BO5kA&;-aaUXbA$$fQRb4d- ztQT<4Bqcjdsem_mecdV*bWAo^9eb`>)i#`M+NcG%?GOvbmNnR%$2z2t0?@L3j||8s z8}y>Q$nzRtD}Ro@WtbBlCK0`~#zOhS z%ttT}CEGFZ1~*o)ZIMNrAl$SG;kulWd2`+~eB|NSYp;CMAvj24fL#0mzx74_5c+*+ zC!TyHJqbPze&w5~d(hzML;nN+!^nfku6({GpMNy<fh@-v)eaa>zm#iJomyYjYR55ibi%i5-)jg59$Hb8Q+bL)uLm2r$_gD z2eyN|^4XevcCUA6d*NZ`LFW60_Z_&v3lESu`g+~%PM)f}QRnyz& z&ObaZn5biR+NsE(7RxN(QkBK4nfI2JTk6g1!rb!W0*f>vP}P40Pfl2WbL?btUO9l9 zn+s~Uy6=1!IB1VrOVfjRoy zZiIQ3vp*ap$OZU(f}Tk?_rtwgSN6i^wyx~^2KIb`UEg@kH(qzUPIOM4*+L7e_ zk?|dA@TJsD@D5wfacQC(&qB|Qw^oH(1&oI|8Zi&us2FAu^E|`4pq|ws9H9`^Oqv8$ zp@g(87C*YOu9qqR8<{hA+{46{Q*I5+w6bmKQXfa diff --git a/custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_zone.cpython-314.pyc b/custom_components/spook/ectoplasms/proximity/repairs/__pycache__/unknown_zone.cpython-314.pyc deleted file mode 100644 index f7123cf76619e397412c5267cf05f639e8eda715..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2723 zcmai0-ESL35Z^nWopbyd=ewlIrLEgUEp{6M{Q^p<)7Z46=13z|YP!1E7w5pfGrMa_ z@)Q9=2qA%Z;kA7#@POKlz#2d4Bw&S#sIO%R@XJ=<;W@mnL zN28HW1kV>g-}vCC4upOaljh_12xl#75 zXcXzeex!HK$0mKA7)$7(gJ?Xw(*1tbaX!>u)~-1J-G7n?i$Qu)5KF}7cRQt*(98J4j z5B@=e{=}c+b8FTewtb^EuEFBQnMOM?#<}(L3BOV+_CrB&D~Xb zPRe&RWp~EO1{p^eze4CHctR%Ul)ch;PCgBmcqd;PaINjx(bJ^z{Y`3TobxrUa3eIh zv(lvIe7X-#-PfG&jJmS7HECJ*pKQhbe{U|Zx0b!nY@*o+y7VR(OjbPDps1gy(Qm?Eh1Yshca{60d!YyF>stXdn!FRd9lR609bMNZ z>e|HJ$@^#To&8liw-!J5ZTMmQ+@o;f$xE-?&D_u3%l-P&n``R8X7a>(@?<@E@?r9H zJv{nlb|bcLJ$9@fJGM5Nc^J!V#Cz7`N9yq->+z%Y_|YG-58|)3cpLkZn*+nQLhIV` zx^{fSBj2cNZ*2K}slF{g>Kc270$qt`QPkD{efYcZmMry7Z1#+-_nfHroY)Y=p?c4u zzk~j+#C9(_eCA&o0S`ZYyE~JRe%W{a6bwW(pPBI(S1}ez2VmKWam6Xt;Lfms(T{3I zxv>(%c+n!1l`X(!2bMc9E;9}NO@;7GO&7aBa&RbxTy|r zsGaNTSX~{1NcRO+XSP&yD7~g=n}^2Ml+k~bW~1=b3XL=4-Q*P*Mer7D(B~j{L_iG> zu;r>{mk6LQ;@$99V?rgt?}%Zu6u1ii8E(XScdsm&-g3Vu>=tJnf+K!x z7OgTfiO}wrVMn7G645(&YPZIncA9FHDHzcDdm%w_lpCGh5##FXUOK!<-UboTS?C0O zwtSK#Jwk&|(SfH(dx~Crj7~m9Lyytv$LQ$qzW86V><>KaJS-*F63M?2jBdF80|o=H AqyPW_ diff --git a/custom_components/spook/ectoplasms/recorder/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/recorder/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 088056b5d900721aa0ed42432986c7fe6e85cc57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DKR!M)FS8^*Uaz3?7Kcr4eoARhs$CHq&^(ZX UiaCJ92WCb_#@h^%MJzxL01t*V4FCWD diff --git a/custom_components/spook/ectoplasms/recorder/services/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/recorder/services/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index d4624cdab9c79f661b9b0ceb37f230a8dfc68069..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 205 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#Dzc{t1EHgQ^SU)~KGcU6wK3=b&@)n0pZhlH> dPO4oI8_-~o%ZfRG#0O?ZM#kF=l0_^)4gfzCH>m&s diff --git a/custom_components/spook/ectoplasms/recorder/services/__pycache__/import_statistics.cpython-314.pyc b/custom_components/spook/ectoplasms/recorder/services/__pycache__/import_statistics.cpython-314.pyc deleted file mode 100644 index 6fa96ce0341dabb04bcf686d6d75408ca4027019..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4221 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 diff --git a/custom_components/spook/ectoplasms/repairs/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/repairs/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 42766c72562ac2c55eb89c1289c23a010cda5b16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 195 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D7(25q?W9m!wEaBqfp5kFAwu%Vr`imBxQ{2&1xUg+vO4S*x;*hF+}5m9!DH z%f5YVES-d;fOG7*mpUklqPN_8>7_uA7VK8+q5=%GMv$8Xx=C~E%-f|zxz!S!oqccK zym>R<%)Gbz6MY1p-@f(dPkt96-);Xr3}k(K32|_ax4dHLX$*|!!L0o;4K~| zFw&nT>OeJB?Yk;^qgQ>+Y@|8@^GP+Orq#h}ywaWU=kW#FhHgmS0GA$CGip}NogqDP zBK>ZFjj5y6adm7O^^AiR=dz@cWaqe5=iz&znyyB>RWbC*;Bhk(VvqhS^VR0>Q~1J#fk}X6ajorn!BZwq@5_ z4dmmR_VbqB@Mi`zZ4I2o8m47fHcW>#t!~$VftjA@!oaYmfw!1h({#qD2^JWRrqLld zdj}oi77I1*CvXeX1HK=!V zXVf>Tcd%>J=qaOC8@jdGHEnUHz*^0Q@iRZ_90GTGvZx37GqR#6RX2R+r_0M9F5Rm^ zg(UdL2kIyRtO}hg!0eJ0@+ffgmxR<|Y@B=~YzV5L2GpP`sv%WU!)l};0#c*jV8wDn z+6EwzVnAF;0~gS38pDsY?rd8ChI$=GISgrnOHqThsO5@F5PgmhNHA&5rdv95(54IY z9LfO_eS`rn=^PBb3xDT2kX`Z}k%WiB5vT1~?l6d-FXP$65Hg1n8JRf@b2dVxzN09z z93(YAD?tk(BK$umB-Ft&dVx&f=>t$-g)I3P=lPQ)-6DI{_t*qTY}oE!5h0xy*g#%EggoEa^y8SG*5gyeHFu2uGoP@G_nFoKZ+<%^V@Hg>+ze^@3)Ij9;@uMho zegH!~wYB*FYAf`u5btA>DzUH{2J}WKZo6~YE1O>a$xUki!mML8_1!G%HQu}wUU}TK zpW!`DK9xx5a4<~hU^O}`kqQEAAEbE@p|eO7Bv>ZFiv@V~*qk4p!E zU~)u4{YOZiUga+j`NvoTp)mYECnVAN3osrgPY_@6?;5l;CjXrxmh5$&Dx_?5s)5On*ux8C^*R+Tp%Rk-2D9&!ZS#dG8&8&ef( zKx1SZhTj9>9>NPTcD#T|-KYn`b%VJgRxap?czJN#xHsVyYkCW|_DFjbfE_ zrMkVPo0c2#ZX9Nv=fJcq`)L;Qlr+%@_u`px>yhGNQ$aVP|6e0xzZw7a_|y9D%`eU8 zBXeK$w`FBtzV<@Cwih4&VzC_`-H%`1i(hUhGwo!qogLfHDtlRFKYML2d+jhx5?8(@ zVxqqjB$L;_cQ72E&CBx_1dgvIKs|O(ipfT^NODL92#y1M%j8ICmhHk0cU^{p#XV@r7{dbwCnQ zZE>_Mj&-1yrT|KT=|dbm8X}=9?Rc)8n`x(K+KH)lZhAjAyO*174-CFeghSF%l1q%X yb4oj{v=jLri6Jg=IG8F1cEiu2H#;OH1pa*m`||5_Lg;@s@y=JpZwYW5ME?b>+>puu diff --git a/custom_components/spook/ectoplasms/repairs/__pycache__/entity.cpython-314.pyc b/custom_components/spook/ectoplasms/repairs/__pycache__/entity.cpython-314.pyc deleted file mode 100644 index 78d23c800550a4172b2dfacaba741af115d5f8fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1722 zcmaJ>&2Jk;6o0#3+v~5SX_Y2vs(O<^!423XMJlurr6Q5+1EFjsBSe~EyB;@7*Sqe_ zxF|XKfKd4lID$Yz+{y|50WO@3RH1g1iUZ`ptte63BX4HyHhhSYJbrK9+c&@Wd+*KS zSSb&cM{Ww>H>HeLIuVTh5QRRtAYc<*qhZudVV0F_Hd1v$b2_PPyquuAUcpEoT~qdQlYXB~dEFB+P-EodVasT6`7nR!fYzxdo`?caB}@#_yxNDIJ#I`(sNLi)aXF*p*_7g10J_Kg$FQe`u4Oc-JNQJps5-HaZmTOKy z6V4iLY6?O-JR;JrB{EI8V2CWHl9or^1mvO1cFwQTb`VkSc#(6>*&#}iy#G%r_bejK%f>ovc}Mcg5N-07lcBzTRh zI@PEz;%?~i4zCSMQcH5FcDH4bkYU*pcf0gi_Gf$giw_3&^j&*tcW!a_g!A4rZ1H)64f}D)%N%>`u)eMo`>RbH+rdh#jUw^kopY9JozIna=`(|To`RnSM_Z1+@g@qRt& z)A(>wa(%cm6XT+X^yx^%KQA^En76imf+Q4xf#8V5!trx44KBa0X5YiGLuX_ zQ=_P#77dW8@+EjxF4A5}=zN1MqpHS{zlhtzlwlb6VP+5J_u!=mu=oI0_F(o`c>N$# PG}esIXCHz3*OC4LL@m3E diff --git a/custom_components/spook/ectoplasms/repairs/__pycache__/event.cpython-314.pyc b/custom_components/spook/ectoplasms/repairs/__pycache__/event.cpython-314.pyc deleted file mode 100644 index c797dae061a95f34f7fcab259d37335f08aa797b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3600 zcmb7G&2JmW6`$pPaQP)s)Td=tq9vKNESof3xpv|@h#(Om-9}YcvI_&27c{w&SW~;~ z?9c^uD1@YFol{((K-*IT^pf84C*)F~$EXQvdJv%oS|i9!0u>v%^}U&;L86`MmYuTtr^w8}cK=G<^B$xxrtf$Sn%?xb zs!n!B;jxnIdaT5%p6kOjQYo=gxmNOVl32a7oVOPW`Gp@Am)@EQ@mT($r7HEi1VBEv;JNG74LCrXFzuhO zRK($`<1Yliz6y32>(twJTpoV#3)^#R&2r#SF(`7Pv`2y2A!~#w0GQCd0j63C)2tBF zEfrj<@yQc99J{7V)9vg6Ix}4n4!U1;H_|c=!O^sX$I|duWuRpCFJx)P;HGW6r7g#{ zdDOPIyh@{neB8Ew-YC_AonG5s2m4vA>N>6m+hNrI-18w42u z#>dS1HTfz^^w%i*OYD@tf`IAY!(dZpY=WTm;Oj51c0NWwhk_ zHIel;E<}VAn|8IrW8#3#nsvutf@C(z)G0BC8`RnI9>UnDLyd4Ci-rRX%M?6@zretZ zO2@$#I)NkC1enT&LQpNyXm!Kw#wMb{t2;7f4XL7eEr|Bg?6p1j-|nP^RauQh&w zr{6e?=zXcS8cy`=tC2|bg&Nb1uYGjfJJT7Z4CB!kcv)%x2$s51`4Us7q5fksuR$_r zVAs-^u@<&;sGkPb)1w+|T47i-MU9PE5f-%&pHZ5FL))`5nSICgJ({jl?_srqIUd9y zU3De$pe|;GTX-t)v@8gsVlJJ?7c>O)R@!M`{#(A}LZF9EzXM_vEuff-^bC?oB$)3r z3Y~(X3-|)+eEJok%XVkQRXiz+;7bzLvj<2%KD&>@-xkG8LO*z{K%9_3{Y3kBFxeqF za$CV9_~y5u)I+yJ%Rzv;cQGO6f*Y?Zb#)zN#^8aTg3__9cS}nCritO)Fj&j9)^ArhEDO(x47H3K0W07Tt@ z4tAEcE|JdKw}9;~zE$FbdqhXtnai;Q)~>n^#*4Zh+CFWGKsz)IWg}<}I%vecpjSf8 za{&>e7;E@Ea`k@0=W)4L0}MMZ#b2g3fItPf4uhxwLGA6AlJIsq6`OhDiRH7e!qT@0 zzlj%K0fJRpfMIOc9Bajs?-$=IzW?@nZ$F7=cH^1PlXI=)z?0g-98&i%oQ z1q~s?*G%+T;xVJ>Rq5lCe&^;7WY_uzwLk=u%wGqxuPTc2l$?4_CZCftdnEk@S^6ir zu}g03kz3Eng*}q_LQU>z*Y>pHfk8sC$I~BZpDAY#Lz?nhOB;p%Z5>z$Rw%QNi(mCX W19o_BSc!fze(9sx7X&yFw*LU^hI^v` diff --git a/custom_components/spook/ectoplasms/repairs/__pycache__/sensor.cpython-314.pyc b/custom_components/spook/ectoplasms/repairs/__pycache__/sensor.cpython-314.pyc deleted file mode 100644 index 38bc2c4755f12e6bbd42b4d868138aa7e1cd1904..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6516 zcmc&&S!^4}8J=A(DUu>Z>ZT4$)L}-n9nx29C05+ZvTV|dX?ZC*NNjpTkt>NcugnhZ zh z=n$Npo#X#^{^R@qnY~R;JA(43J%9hj85ctTVlTDQq&I}>t0F>iw2by6f`*Zdh;SLn zLRbu$h^ZezTMSDfGckuO#KPZA;f9cvSb19t+d_6?4>gj;kb^jQpE>LdHIb%}i@5l^ zCF~A0ljcwhX$iHGR^HbTZVR=O4rrTXYq&GyAs*hgg}XxCq#N3TY={0H($kM()@O{b zPPQV{%f6{G+#A|Oc5zyca9^mO^z*he>-x5%w>o7^sU#9Y&-jG?+m%rPCRdo`M|j_EVod-n@+Cvfu|wdrG= zewW-W_sG3J5&d1;==aI}vR5AXiRcg3(?4V)K6xnij6Cdv4avJ<%-4z1ZZg6p`wYB~ z#Cl@(nq-Mz_75Tdo?>e>pUYkGKI?rYSHRxITqdOk`~q!^XR|pHC#hUk^NZA$jFWgG z9oIB!o=>UiBusJ6yfQthOoS#UroxfaeiL;>)vT7olUb4?OSH58?WC$Da4OHpQMW!2 z1?uVqBMJ0-CNE4zW|Yvmvy#7W`y3eQifc>Rgp!7tYF5c* z6RJWmkWtw#TVypKPhqWYHS}61fK_}htwOtTBA1;{EdXWCr*%A;3Kr+7%^}OBT|s?NF503-Pou;!>1(&}hYRpf9Dr_PTMw`)sST~HIE~G$y344G9uNXm%RqvoRJ2JoURnx&0 z>EOM-p-(36_Khx|y*^r&M)B_2bm}1BYiWhstVY`m%1V82(P{9}Cl&3PWa@=$Sj{FD zsYUNg7#0C!5wgJFgpg2OgvK(uv_5tLxzVgp5CfT7=qAqaRtd{18rLYbgx7sBol?2wHRob0}!KUzYv<#S? znpCc;SOZ+gKm%!Y`sG^X)fgLL!%Vf{1uG8u1ND0H=9_^1}%=<3E1O$k)ZW9Lgp&VSO-x*+qVcAtI&xqtxs~JT0UFD&VI<> zhtPATl;AfN+oCG*7V<_2Hq3VuMlddi7^aV|bD2S>+z91`I=om%<~Pu?xgSln=)Nr@ zofG$E5K-Tz0%sEDOJk*O~*FJJ%S)`==vS6Fk31qS<2b>hK5& zn7CdHr3%#$bk!wd7q=&3vII%BnJ3v6NXRVk*1+CsN`}PD3h9^)jN6VQK(4q~&#rXU zdhgm%x4XazQw3Sl9jqZxlN`tN4NR!F zo+R+pAgkooBiasvquLI>KMMe1J9vZex9vv6Q~(<^S6PG^6P8g_n65W6=w+qmh=8)p z1H__pmLE90Jhuw~#?U?kKp-FEM-AgdvKZ&*1l2F%SAh_fG&Mc1!{4%wJWj=!%4yN? z|0IukQVHOvHAD3Sz@!B^o6C-1$r1d*ae&Jq;bhXw;#P=|@}4ObxRO4?u&U#Wdxx&4K^?JvALS#h|_4sXffz0r2F>%*>_yFT1i z9ynGSICgt-W#If54tdRlI*x&GY|gT^w`A@8(y_bZZhO!6Tie~9Be$IZ^3JC3O_I(2 zNMpyKw@iR$!%v~Wap~m%7<$TybDO;K34U!%FC(&Rt>Uh9x7^u_?}lWS;9Hi zH_R#)rFB9+)?!HkuP#DEV>8}C_oU~(@*G={j(sOV99~a; z5w6w%MnGe5wHDa82MOre3uZ=#ne}28PHF4b`B}K)VNipa?a`L4!&ueB4YNKDXtaIv zltncS($jDgn~-KSEP5`4nQ>B^4ry!#M>U3+)RoAk({MurqR;~IxdevP7t!9$g!Ky^ zo~Oc%%vsyTa}VVuSE}Me43dJykO_!M;S_D(hV9K<1H(qr2h> zR9Xfq&aTSP-ipUp_8cgA4piEP%WZo~ZF?%6UFFW>rOxAbI(~MqdFnx{-E1*d_EbE< zO3O&a+5e*}*F>?!{-D(XYr8qu-il{`rN#erYgZ5X#>C}@JGRj(@(JR1vH*v)2OT|v Q{m$;Aw|l=w(BfY6Z#5|d%K!iX diff --git a/custom_components/spook/ectoplasms/repairs/services/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/repairs/services/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index bc5582f6317d6d53e83d4b6f613921eb46f43388..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D7fK6rNqL?e%X$5+@jv#{38{Keh;<0IDj5G+@&?VOE>8%jr}J5N^~=W=RaYRsvT)TC3ka3(GB zh*7CH%wU#Np|Fje87qwF!?}^+H}az=Mu8J}{nT4S`O&n@H<(Uk!n&kS8Mak&EqL6b zw1#zpFIkk4I;bl(5W?CuG!7iYWbU=6F`2SV4E#uw95!s*Y`(-@+Psal#3kX&B4QHw zQ@+^6e450RBKS^|S+J-UI$F&<8s6GJvfEigivbejfW$T)ZH2`2k(^V;sUb=MK}Ln8G+Z?-LQ~X(Oo}-vlVF44 zR9yhqxmroNpraWc@C4k?EM{XKDPd|7t17U}RS%gi7K0@`VbpBK10|=O2_mEb4JjlorGS`0XVFFZ@W%+1fY*!OmL?@l(qzr2DOnjF<$orC zz!eivO+tbU5X@?%!j(~{g1?aFPpMvjWVaKn@+h3niWQ~R3vH^ zo6M=&1})Pp@m!K^x`bxEcW0_~9@X_`0%Kk0$yU#1T16uIbQ4T7=s9ZhhyN7Q{QHYy z`JTDEC6FSs31~qhk&+)F6k9q5-@Fy|gYgAdAf`gAW|O-G{8Uh0sJtL9K;iU;CY_ZE z>hkX8s>yrB*EPpFI=)sNO<70F->MZw1M)h0fmS^&sHyAdsVnqC>u7~qwX(7n9uzQu z(T}vqHgw2mN$KeH#+*?p**L`}@e65jq0sRutJnH3u2l|%t+MqC3sO^?;{W7lejjap-orxzMy9Ajr zn)Y+qBD}+(^@^!`xYONZxiV=`N<_4|0`0(n4xvzNPY_XksX)h$9b~f*cX`&6?%VYy ziuslmU|maPyEy0rn4SUhepkZ})V^QTzDBhDTK?1gubs(8&*sLaS8k-Qr{~^W*tEZq z?7NY?o}4?pklfqo>76}uW9<6a-1I` z>w4KK)oejSE?%h_c2m-(Ls$r9wgp-3fH#SZFgUfF5O*uAK zHGHLJ5nST3ImH9eNv+ByuAXvhn_&}Z{m6Aa5q|?bmKSaK^m0)Po*U9ep?&kfu!D(- zd2rluY;08EBy$p%4YF5QC3GE1F0)MCU~s{BjbWWyJt>3RwLJNCw3u@Z^Q9 zXQgV5S$0PJKDpuLerOPsiC*RYJ5eIaChu5jV>$%84abX<0O-ruarIRXoUf5Q2t?zd z;u{xzk|aGu$){-RQK^(p Dt>%@Y diff --git a/custom_components/spook/ectoplasms/repairs/services/__pycache__/ignore_all.cpython-314.pyc b/custom_components/spook/ectoplasms/repairs/services/__pycache__/ignore_all.cpython-314.pyc deleted file mode 100644 index acbfb956f92d889fe643f6d7e7dc9c315971e721..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1600 zcmZux-ESL35TCs}pTEX z1Id#G2=#B^fe-OO`44#Hh4)qr z^`qz==XvL5G~iAr>IJM?A*$$wVI(~nM4`ZI`TEU`D|DrCWo5nDT0?<-`Sq*yW((0+ zAjE)C&UOMJ`7TPP>bDSizKmVh+r1#X#`t@I&rn%R;fmMo`f14Vfd$fwjHWT;z79MU?5JMgQ%)PEM-&&M|3ngBwQB$uySPNAQ-|8*fL)J7@&=^ z8F+(qh)Z0xrJI@g$R4nu(|#23+m$suHCsag0(mb+jneqpD}{ z!{QunLwEvKW{vzF?|0T8$LF_~llurQz)zXc7<57(0hC9dA()3*bnupz$7|!LWh$G) zVr^LI<;by=mbP!)FgENXiWH8+>KTCd(Ia0%i~N5r-dxf}zl0R%H4E$00C_kEu6Z7o z&45&_y)z9jYJM3TXedM-!U6YWMu2sn>Bj76UiQUkrJ4`C8;|YO!-Pl5v5=CN- z#+G2+ZC=K2ay`V#Zbv;Y2vsf_grF_7cwxbD9DY(8oJt&Y;#g?PoSHjE&i|RjB)y6r z9$}S#ojUvZ%%?M7w7(9%41Sn;;r7_RQ@!sj|L82=E6v3HE#9%eJ9W3T z_)}?hWWwCVzl1LChxKB;O!zp$R;8eFl!jhUw@87~Ueq3RHOA(7ci?qX%?V1kab{#U zz*dY3=YW z>?MO=JshQKIyrIlYVx>Qhjsgmib9- zcn#m`J{K2odu$Ry9>CNA%pZVr053g+iwE%HZ|3+ztNMpsAZz6A#DzaWgE;Sh0Qqg4 AbN~PV diff --git a/custom_components/spook/ectoplasms/repairs/services/__pycache__/remove.cpython-314.pyc b/custom_components/spook/ectoplasms/repairs/services/__pycache__/remove.cpython-314.pyc deleted file mode 100644 index 448231bbaacda65456a6fc5064a8323b735e9d89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1748 zcmZuyO>7%Q6rNqL?Tu?Y&3^+a-lQUxX={t95pBhfqL9`W!f`jKQl&bh?RpY#S?{=y9LtGbxJMwM9jOATM5wo-AS!OW*|qZn%whb^dvD*o_q}<~ zr}DWBg7w8q-`$u`AoN%r`YRa&ZTC|EU1TGHR*|h$wTdqEg2H+=Q89!O;Y2klQXn&` z>53@^B3ZJUsT>j6N>1eAY=CYMbY_H8)uWXmF;qYe<7Dr~W*VU>4sS!KKNOT zA>YJVEyj&Zef+-`Wldyl12{=eApfYy*(s5?)4+%+vj;=mI$xn}Vl8kEnK*&4Y|?Q& z#w{MFUrqE@rrP-RT+X>q!>kwA*i4JDcaDgXgDhS?1$O389Xh7~FRgyKz+K%Arh8ek3 zj_Xo(LoR3=yR_W_%RullS0yhKSI~~{@HQ{AIH__kDJ4*mmpYp=i*fHM5RB#IL2gP1 zRnNht`~}#$=o!+Jw)RJ~-+BLO^m|Xl=UuQsuF9pq;JKm?5X$Y|0x*LL;NV(chw29{ zd(vs(s&4F=?15{kKD4zfT3wO!uVw3}RhYe%MRR(;9SZPZPN4aOrxlH@V-+W8`ot0~ zvR7usdr2lkU7=m-az#NfnVV}l^# zDy7+3HmcO4@z<^BLfoTYP9I|V|4bsbPlJaiK-X_(=4bQx_vZ22&Ck8hyf4SUp1(Kx z?c_I;_b>c-zP5dCdHd!1Pv+&nxr*r4Yj0>wwM?xzC{2t5r>zQ`!MIJEp|3EM{ATF* zJdSx-HUnSV6HchA}jk@LcQ&P+t!P;TT6zvI#JVxMFI?M%xGgJ_p-J=%KOr zYwr2(#RvKE?xhF$p-=pe{o5CQ$j{!CpSFT#&Xd@Y1V zg2b7sj7*8m4n)Y*0FoKVAnb^chMX0ZKut&j)s3e?X}_sTy&j6n&SsVE*kRii@jHZZ zrv4GwDYdBw%Ty_45_**jY4#hCsjiU2haI?-k*N1$DpV2o>5=BEaRFavuYf|$fQX}~ x2~E=;qRAaJvxBV1=-3Wg`UNfTps7db%|HZ6 z29lEn2=#B^z=t?c{sWF&xVHk)bfij15TV|RjHtNrW_L>+!ASe&H}Ac9^FH_KsY(fO zef`uA?-&Nauj=q<4eIGuOP1$PYwfy4mT6(G|+x4+DzAat7D@UN1;}NvWQ0+PIEj1lu4qOp3lU zEeuZ+=5|h4o-Xp9fq|H(pf@cFUQQG}3&Y4uJ9#U!eejYS_o>t599{Aw;dpf5hm1Qs zb;H?>nKO*Sy(ng3wCA8JP7(#LOFQFJq8!xF7xk%S;)mJO#twoJT!kI|#ZLe_$g9E| zT37QlPxlPZZ0P7x?!F30YJ_1uKw6B7A&aEhjwAXls0^YLeP3{8eo<~Vf;id@_Zq>F zi?~mMxIaM6Nbm+%1!~Yh#DkvC`@F%@Xc}2GyfNu=eXuWWLbCEvLgd_JYU-1sC-G^1 z4#@}}f|+x*KN5O#^FgwIcPTxOQ3HakJ_>^h>0^M(_;Un{Fpmb_R{UgbO=@Pm1x(kL znH?s!l`Odf{i?oYO$ahL4eO@?K0u3X1vQHQ)p%<~rTqmgf?D&ic?O^e=fE@0!>SQ# zHS^%K>qng)bwro`+W`Sqx4y2*kaPiU*K$%1nJoHzKMKem71HeboTFT|HMVJIjgtr>W%e7`^+7C`L?}$)B1k)mc9J5 zy)iam;o{$1CHJFdxmnTJG{SbRBnyN@eqXgniI9HW8TJ&$=6P@E_cF-|LUwU-L@&f< zj8U8?q!R~(FjY;ann&idV3?y$vxMN#i7+6(5G=et6qKX7dX&v7qO!qMB@?3f{4A1B z;jX#%OJ!!XemGSfZ9Fs|H)MKGU1`@&uqE7;$GbzEDD;>mgJy~S0nRd0jUd&3jAHfD z@vlD~rg}CziT85$xLTL?29)uno($U#WZ}_>F_k`f^0 None: + schema = { + # Left out, every issue. Named, only the issues those integrations + # raised: HACS nagging for a restart after every update, for example, + # without taking anything else down with it. Named, it has to name + # something: an empty list from a template that found nothing must + # not turn into every issue there is. + vol.Optional("domain"): vol.All(cv.ensure_list, [cv.string], vol.Length(min=1)), + } + + async def async_handle_service(self, call: ServiceCall) -> None: """Handle the service call.""" + domains = set(call.data.get("domain", [])) issue_registry = ir.async_get(self.hass) for domain, issue_id in issue_registry.issues: + if domains and domain not in domains: + continue issue_registry.async_ignore(domain, issue_id, ignore=True) diff --git a/custom_components/spook/ectoplasms/repairs/services/unignore_all.py b/custom_components/spook/ectoplasms/repairs/services/unignore_all.py index 158ee6c4..a90a7ae0 100644 --- a/custom_components/spook/ectoplasms/repairs/services/unignore_all.py +++ b/custom_components/spook/ectoplasms/repairs/services/unignore_all.py @@ -4,8 +4,10 @@ from __future__ import annotations from typing import TYPE_CHECKING +import voluptuous as vol + from homeassistant.components.repairs import DOMAIN -from homeassistant.helpers import issue_registry as ir +from homeassistant.helpers import config_validation as cv, issue_registry as ir from ....services import AbstractSpookAdminService @@ -19,8 +21,20 @@ class SpookService(AbstractSpookAdminService): domain = DOMAIN service = "unignore_all" - async def async_handle_service(self, _: ServiceCall) -> None: + schema = { + # Left out, every issue. Named, only the issues those integrations + # raised: HACS nagging for a restart after every update, for example, + # without taking anything else down with it. Named, it has to name + # something: an empty list from a template that found nothing must + # not turn into every issue there is. + vol.Optional("domain"): vol.All(cv.ensure_list, [cv.string], vol.Length(min=1)), + } + + async def async_handle_service(self, call: ServiceCall) -> None: """Handle the service call.""" + domains = set(call.data.get("domain", [])) issue_registry = ir.async_get(self.hass) for domain, issue_id in issue_registry.issues: + if domains and domain not in domains: + continue issue_registry.async_ignore(domain, issue_id, ignore=False) diff --git a/custom_components/spook/ectoplasms/scene/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/scene/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 6b7197870dddfeb7ff534c3095fd4c1574cb17f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 193 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D2KczG$)vkyQXc)*f#T-E5 Q12ZEd<821XA{HPA09afy!vFvP diff --git a/custom_components/spook/ectoplasms/scene/repairs/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/scene/repairs/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index b5bd145bbb0c8135cd766c5a076092a89a39166a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201 zcmdPq)ezcluf|A77jBKXxawIvz zS+pbNOuC56qPdhi=^18Zz>RYN3n`TVE ztZ6!tiK=S`3|y@_lhy((S5`P0nJG^O6Xd2K1N1S=hiMpUvb zVWprN1aHHX>)o3xD+zI$HHTzlTgyosYLO^d`bg7%sYy+KHE)>x_HscXl3Xmdt-v8& z*Y3b^a*kL6i%LmWF-$v+oT4eYrt((D+5_f51Y#GhA;N&^+4V4Shz#LGmN-QRajm)6 zJehF&Mw`CK5}(KsKm2&n31)Nkp<;*xL^lbF9x$5M^tZ{dGiO^6TM`KbtBN5IUD!~y zyinFG9R*8Ax}a);C9o00)NjnFHIkZqEse3VpT`ZSs=T^dCi%%>FiO z#oAY*AcN6msqzv{IK$u3&)%xl9l$iQl&1u30r>q2x&vy0Q4|FO+_o0zC)0fNR}(;h z*E3m^;o9eHdZx)8g$M)=*XtY^&+Btnz-G3oMaLKz|Nq@5vggknotCli;%H2%4At#K zn`w?q%z{HTH>?b!56vOu?{qW)$7-YptGs zJM8aoaUAuTHjP`eSytpO!`spJ4pVxCSOv>{Z>%F~(;<7k{d>BEqEmEbs0W>BqPq`e zxe#jEuce9Tfpdft%rF$aZ#ds4`e9}C^&LFA#Kt+1zx>LJ{-+-X-f&0oG!3#`OXe0@ zN70RcBjiQHhxD(+1CcZq1PbU(}FIO@f{0C8dg7|SQG=o4#!or><8xqw9PU+1m7jc zr)bv+!h?HT!m~}`9XpN-!UrbvK^jv41|itkMbmpE2(|240aM2`y;lnWJfD{Ef_c6j zN$gzFR5Et8sORLOu@HO3eHk_z)SbQsq-Y>EcIMe)=HZn;=k})eBKNQTZ7}v^G4q2# z_4ur-hq3S&kI)-AC9jg5t`;`hz(?oeAgM<+jItuBd9xqVk!BRFBq;$%=9Epnm{+i| z^K<(RHuUmoIp7H0X*!rN1R1z;Sdod62U@T$5CEqImAoS)XgY<|LEmtPJugg$F~uv> znOE+V3l!mLCN#NCcv_Ro%S6UBJD9Fd6Y^909z!6Id z`%TTzv3vpQ7_>M#D?qbfIdJSb&4D+*w3Re|0+2_a^`D;L@A=>Hm7dr`?y>)o|EcF{ z!-e>f``lOF2i~uO4}wqqbI zSPymI^WOE=BO|p)tQv{cBD2-VY<=XdLpL`bY#=TataL2YBiMVkgdkCIiA=@0XD;BK7{Uy=-k_wmLCe?;oy= z&OY3z%`I2wmMgO>6(QLOxMRJbMd#EZ@^yv}dr@cKJ^l}TgJpUanf>lFweIn1_jtX# z|6b~DsxlmV(jEWa>+B34OrXA@9}Jp)KD$1iSYZBo?#ErwSz(!;?l3))MBf>*xKEO{ z^n4jyNTT`u8$O?-XWZg` zpnG?@9*FHO?|X*p9$(EfRrO3YoQ_`CZo1(?149+AXMcRE!i~J-T9u1^EIlke-HjKZ zr$*Z-Lj?v#!xYm@7uhbUS^?8Ecmk1Q5PDGIT4w4whSjl6rC0)7!8B&@BHik=nJpv; zHF49fs_E%8pzXkSH|sSF|4op2_V}?!)n4q_f(?b1X{cF_%w>A6+CH#=)38A64&x$x y8V-hGo}qykX!HdVUZA<>=<*9R_z(1p=jhxwj?i=N>Y?ir6Rw0OzDLkoC;k_}Av8Y# diff --git a/custom_components/spook/ectoplasms/script/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/script/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 8fdbe3ad6e68f0e210726543cd1145ffc888dc96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 194 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D7&-6`m!Rf8vrNDN&+gOIl0TpJkDDDn*W^x}_~jBGk(4iXjj{(q>IArA??^ zW@qR|LJw+M1aU8Jdx?Fr0~AOKw1skNuSN>wD#8vZFC0Wb>qBlTq{PUrZ+5v9l_=<; z3vlMmn>RD>y?OJ!$I)P*1;O{;-&WuJ#*5Iu=)nG57Qr0;3W#kaAc9trz@(XZ2XXXL z+L3n7vxGHiXWBLICLWV!)82U>@y)l878tXFEA5}>2xrpnbYMP6g7dAU)f{`$q4_Wg z_o65|hXijY5_~J+ai?`Qi3lzIXvDwOwp7-%xA-ysb*-ZF>)M7Q$43~$FR7|VB%)|4 zhM^nhb)`&z3P`#viMqU|V4`o1IE>&-GMg0Bi%Z$$;*Hdj(V9|;LN;$`8)Z$ERl3Ig zvzv2sDFKAfNO)7ti$$eGWLTa@V>L*e0z=ppSBA4Bnp@%#9K^fg%d|IhE&38e{DPBk z@Cyhm_|DadN+A*y+@w|Tfako1ze$;`pIC0xP1Sg!D>CMZ#upW}z*kh$P+pgdvM#H6 zxxiDKF|Vk+!AtqN@hG2zT;lMDL`i=^GNkbjv;?o5*WwQCUmmE0|~-V&1+z zoWL|b5^|ns<&uOqfMJG0!ZH;nen8=PdDGxTu^vXUC>pUQf5n?;VxR(UGc9?623?W= z(B@&*><)pAOxr0zKYEeBK`3yz4c7LdUeo@JId!thp-eq2n)w;Fd1TScZku=UC7VL% zRd9{vQ2{*p|Lz9skGF16&F7VwWdI86%b4I{kE|XZu>*EFC6LGKcJ`v>R(s9q`i|El zsO-ownUF1e+=oz#nGGUTcEb9|G2f;#QJXsMbIwf1@yMp^lR&2~o0oGgH~gESq5GZB zwqvNyx4gfwfY5TUEqc0LphIJ4TN0rRe@xp{&LKD_A#&(9#gtk5tTprPEc_bLo9?&f zo7>Z^6aa!V1I=<4Ex0>Tjt!yl`jqVr!2|mYo+9rRd@wWo{TU9u>=0T`KRv+;{>kIJ z|6kF=oxY0@IL(3_YwK*Mj9!S1g)l-s)C0+?A5AzFW+b>3&Fae9nym8@;2AChP(aRz zFK%^Oh^SDQv)Jm|Rek_N7VtJ8Klq&#;4*>HU|LQ*dHQ63MBy$Dg!SaJXx0y53Mijr zI8f(h=R0WI)r%Gabp;)D1)W>YG5!sMd1Fzh@eHRs;+G88Jcqh&kk={v8@}l^S(V=@ z>(`8n%|K=4q`C?5>5`V0O88p5A$=JZV~Sv30TAt8SbDVbkL<@wJMBB}9qF$>{&eMA zOtpWvEz0OL>I_ycC}gYM#@jlWQf9*NLAr$0DJli(Bn9J&473r0)TiQ-nT$mM;k>-A zl?t+sw`MIL7qv=LnrY;g0nAIl&qJ;uH0P+ixg`(C&nsk#PtqhruVl+{kspZ~?t-=< zDUc-AC5#QWAQ4HYrHbyMin|K(t;!nBBsBZ#KFaf&3PY|lB4rI@R#c2a$k9%-dF#!`gzav6aVy6F0!9Yeb)7;>$9FmJs{`5+-@|!!~Wg>iT{ar`rlm%pnaeC9(P@RKe*p9 zSnEi9-jR5Jc8?3yxc(~FU*m?V+|bAF+VGX?@RcXrtIyiH?-l=4tPRdo2WM(+sV8lz zXXmE(+G8K2|B&A6?tidyf2GzvRqdYI>%V;DVf!Knhz&=6A9z7KlRn^3I9dx0RYOCt zpw>NB?H=2Y^*(I>sO!V7Dh>0;7nUBMTiT294+lRQ{cv zT|T`R>wlQ54NO!ACJubAc*lVYg@=!jHyk;Npm660+#k6Emg!0|2W}KSQ)@q8Z9iYv z^4`Yd_KPn9o^a%F0Cisa)~pZSy)ia>jrnx^GPM7^aJ?Nm^HDF3gbc4J(nx?hqeT=q zv_b_!QKS{f+ZCx)pJ^4L^3*XYDNvYdFzpe=f|eIWD=6JFH-DZ?FX}WU=%X}P9FlIF zN##aG1jlrbE2XMuLDFxi9qdmt3qT9T2a8#J8459N~f9MY#>1+JPFNv z=qa1t547D~xwEo8yB8RPrXyDC7_WAW??xxK7x!XkciHIv`Qcsm+zZyOt*kxic8eoz z`cI*!PAgWRLkEuI$b|_szCgE zkJ1DfT7MZXC_$mhH^Ih!yw)EDMO`z2!2%smz4Fv)jbu6Hq@xV)v=so?^qU|;OCEdy tngb`pFi%m}bJX`7jedb9zC^-vboNV>{Q`|1xi2x1-N?W<2zt{&{{^j0Mi&48 diff --git a/custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_device_references.cpython-314.pyc b/custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_device_references.cpython-314.pyc deleted file mode 100644 index 53ff649a96ead399904a16450850dc503687962a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3607 zcmbtXO>7&-6`ox#e?%@Rk`g7#mc+I7A6ca1%9ejrC9*|HtXi2}D?|fG-mJ+LwF$M$ z>;+_u2%{R)IlBqD;YBaz84^DJQ# zRAw`-d5&<3J;ZCtT*f!wLR#ki#1CywbY}wdJmD?blL^j;NNB#5v|4R%COjV@ zkpzmN6G-%RBC%y9GVZeXCQ;GfheiS$ZP&`W{+ci*ysTFYVNG9GmE;Ix24qdsiA+>o z!_f5LyrGr}NUa6srkYnILn*457`H}PGc+^3G%aNouPse4UP)gwThkg*$*n8;dRf;L zjn4A?^y=JPT7?r4n zWwNN0W!1R!9y&fViks;iQJImW6nrq?}S>ju1$+kE5+VW|J z1Vk6%;TMEoNaO%TcPA=^Nvr4~VbKd1`piI+K^xOHpoV3hKnzvE0?~z4RVxS;%{s7P zD65L0XnCa|*k{LrstGp6IB7bPl6TMuL@j7%^j*+sDi&H;narbS+2Qd%gdfd z->G8_384!Bj*Y1ZSpL7O!Tj%+-j|y9D>KVL9CkT+w{gb<%Iva5HgxP!E)LJ1g%v{1 zVojfM2ayEIG1+jF!V$wL&CJ5QvJ2)%59_nB!+kkdw&QSb!`1{^ruS==oNIX>-z;_B z|0)gZhMayI{e=aDmJ^MkM!{VV%Jv#{0w;&fa2coq zVn}jvqtoU^l~SGEtraf{0~lh0zbOEO4}}D;Ly;!qKk)GB!~GGZy(9?s!(T$P0dQ7e z{T#!CQD2Y?V5Dp3k4G3n-WKq$S zH_FD8d8(PP>^S8dB{^Br^KuDKCHF@^50mk6sJ;e5+C}Wxu}2p(4?K7CTZ3Eik0S41 z{aflgOpowZTZ}QN1Dc#tP{~GngM1xkrJX6W1>z{gR7ov>tv(gwiUP73hj^&rk`<>V zVC1~Ark4tefj8Jw!U(hR6C3nZz0wqYnnKP17G%)m4MippjATu)mKGo!2r9WG%vix5 zTlW+LeDrl$g}AXMV{CE-naBpsWemzwhL@hfT~KaRiZsH|=xq8dvmpeU(Xx)QdZPqp zJH}(vB^L@tKb_`RF+fRV2#GcpoiAzfO<66;FyopPV(g^C@X_JX{ZLqNf+K8T--vw_ zdlQp;Rj1}^^15QSR5bN<3$+oq$zi3mYK9$9_v4#ENyGHm-Dc~-V1_Y0>!1L|c8%Zx zA27Rq&^@exwgzNPePJiidgs#ZOLq(R)qCoF?Vk3R>tFOvJ`7Ag;-fp$>Cd`8?fR_u z)85DYv2R`MWyZhb58WBPJ^HO18lBs|_){P19)B?NN#^6s=f|g@<6pke9sc+Hb}adT z`y}vj;Gu7_-Z5C~NPW?f`rT}u57+pgdk?0>P8(Vzjt7^Qy077Vi|ol7q+Ni*qO=1N);({q_?OXADYOIgT~(=%qsAq+{m2~LL1 z?nc{AxFqN_Zb|tySt}|9Gt$uN8syt0T`oZST!D-X-aF7u2}YI_S(7Sd+uyNqk=hZH zBzQ_NM3M>QpEoK*!C*BdI-`?Tl-LZKH(3=;3-FJjdIvq?7IuPdcUEq%Y|hq$DX2Q) zwT^Suj&s|wiOt1&-_SNE)Z^XTTx_R*c$+)%jBDg)_R4h8#gR7SMfg%^S1XWc0<;FD zR5K2LFBG5-_~RgJB)R|Wu#IT!pn6Rym4V3&N|MH9I)CuU0m(zJ*y1Fb!hfAQ8qLO;WB-bwwp{DG;gDc7k&<^T^GYJk5JbWbn*!r h{Sr-lg~TVQ`z!Q|FVW~z&lx7V9Ub^Lg0BV8e*hT8V;leg diff --git a/custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_entity_references.cpython-314.pyc b/custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_entity_references.cpython-314.pyc deleted file mode 100644 index f3df29ac389e01a797a4b7e201e92562ce183331..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8336 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%0AK)TkdtgRVZ+Z#0EBOA7(Kztyg5$Bt_@~uan z8JPpnfa$2(LX5L2>u7-&r~0yGLDim|vuoAam2Ym(H6L1SKD1fiwlese@F(Hzf&jpX zfyN!A+usPoHMFjD{-)=Xo_zhjY}>&7Yq{eitH(#O1EH)hoUdPvKs@n zTY6O8{Q_C4T{}lm-Qn#US$9j>G4X;}i?W&86ZQkOdB3Q2i_uO%!Sz95*e9lAI9WzAy#V zMvjZ8VsLdugu^g6b}<+p6(jf*!?hZ|&l9m5rRaE3X;$wl;g&KyahYR>M#7gS*vs5o z<8UoGG^kV+z=Pwj!C96RulBA6Ua*%h&T}z{M$GVWrCO^6VHf#CDjHXsqZzoG1NjH& zNdRSnk0!ZHS`|4-mCYEQjECdkP66mR9E}jJW+2)EOTcQI7*lFo`2>E&8F)5G3Pbiw z^q6^L)9(IY>c-UKaNgd#IJ{}vmp55*rtVc!_m-ZnH7t%oxV)n~%lPv3-YnzU#O2oK z3^jodMz3# zqJyL|-@UgQE>2YDdv@2%@`*ISqG-fDC-7LE6$t__!4SbrGj5?llzIt_=ZOt10(b;e z6=UK0Q^a1Q;@Ukx48l%oB$ZsL5a=Ah?Fr9!?}K7GtschL#h(KSwzj2F$hK&Tq8_7$ zKccp8(f&W8p0Cls*XZ~+h<(8@lx`bA_S}M~gWuB5ubIQo4RC$>{B^hqcwU2ptIv-9 RkfK~!*Z%)RxIlK~e*gu)Bw_#n diff --git a/custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_floor_references.cpython-314.pyc b/custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_floor_references.cpython-314.pyc deleted file mode 100644 index 7819bf5aca1329c2a389d7a3bd77327083daae3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3539 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 AJOBUy diff --git a/custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_service_references.cpython-314.pyc b/custom_components/spook/ectoplasms/script/repairs/__pycache__/unknown_service_references.cpython-314.pyc deleted file mode 100644 index e29ea1a98fd1c163fcbcfd83fd9a84fd25c98bf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2737 zcma)8-ER{|5Z^nWKjPSlLvRvEf{%~Vx+pPK6P1U88iK(V!AUP66)Mi=a=s*2>^rl! zrgExE7Oh%%qLunbv@aDr(Z~J?K}f}%S}9VCD&B$ujp$Qn?tBi3Ktd-MCv z%-)Hvc$7f<=I4daz7G-dCl34;^aNq+2M`)WA)G7_MaT;|k&7vm#e5(qamkef`Cu-@ zL#{04!?_5L>vXD-HLxj2vKx_Fm67s_|%5L%4Ss z_U_?5DUu|Ii4y52N=Gp<7VymYJ|%jTq&wGoXRDTVO&*c2ST!cES`|YdO$$z^YMK^T zxnY?$4C8ZGX0miLmz}(rFI<3$kgYMJ%F_X-FFT(t%+bl|OEc32l%}R9&SlRz1K#{> zR+-OFW~q|Bke{8)DxgX3)m)mM&z^&=235OmYINBsbDhy^ruDf=ZJpgPG~ISmzS1nU zC#iu3*K4||=}w<#u%dISmdkA$_}KR+mTb;cjk~^PO>Xe|q*bX}rf%}frhAa0FY65V zu@$|l8Vr^NCd|6lLe+h>uoJevgJFXd2^WA>y6Brq1!UI^UaEw>c4i`C2NYS?02_lJS;i^pe3dU(`$kSfWPh%YVU?e+qOIo$fspYXWSA z4>l1Vz+2cF0--_X$%1(5YeGseH$*-bRs}^+#Ebxb2cDv>6Jk2AG1HL>Kpg9(rJpBeTdM#@;{iiRi))!lRa@0%H{N#$2b5&KYS_TMT?G&z4~H zInt_X+iV|*0IOu{B_CKy0lFDe3Rx9NmS*>y6s5JO%&$N{!zw-8b$T z4^toAl75Q}y|g7b;pT8k*cBW&=>~@k!kdCa2`ExVga8S${b1&VAlV`D8D=O;V%LLR zKXhq<4ZzEb6xS(s5Ossly@v`MHOMvz1*6Zf`$gNt*RQMYh+&LsZNy;!H-X39EfD@6 zcUN1t1tIgo46fhPW+HngV7SBgjC%|1worr*FovMDBVa^vlcZe${Ir!dD4BXmUaHHB ziylski=%0gC1ADFwYwCjLkz!#Vew)e*dZTS1H4M?1&YX0s5%OrjnEu#Ma$t)3{G$(sQcY!EO94)JO6uf?C$*i;=SUd@rw_}FFs68H3w6V z1~U%^Gta|PBDzVWj_6Apk-j-WCPV>(03*DR9&jR*nra2pIZA28D%Hv;L!w`=spXcY zo5DrKY+g3t7O_AbrnF>flzM1{&P`vM$QPVY$*QP^iRB5e6^1M`Je0vb4bZmIh3Yo| zcH0^9$6ghsFv;pvTUE^!z2x-xTFZnQT(;Db(|_4iZ>UCDT`KFdZrRGZVeSmnb`-b` zgg(`vyxC5yHG(yI+39WrPrZo*mTX#MWm;7^T*M|6YQMta?*Moxg>}M-UMX_H*$tQz`Ou5HGi(Cvu&Uc@~ diff --git a/custom_components/spook/ectoplasms/script/repairs/unknown_area_references.py b/custom_components/spook/ectoplasms/script/repairs/unknown_area_references.py index 7a182672..32e51312 100644 --- a/custom_components/spook/ectoplasms/script/repairs/unknown_area_references.py +++ b/custom_components/spook/ectoplasms/script/repairs/unknown_area_references.py @@ -8,7 +8,10 @@ from homeassistant.components import script from homeassistant.helpers import area_registry as ar from ....entity_filtering import async_filter_known_area_ids, async_get_all_area_ids -from ....reference_extraction import extract_targets_from_config +from ....reference_extraction import ( + extract_targets_from_config, + only_in_disabled_steps, +) from ....repairs import AbstractSpookEntityComponentUnknownReferencesRepair if TYPE_CHECKING: @@ -42,6 +45,10 @@ class SpookRepair(AbstractSpookEntityComponentUnknownReferencesRepair): # references nested in some step types, like repeat sequences. if raw_config := getattr(entity, "raw_config", None): area_ids.update(extract_targets_from_config(raw_config).area_ids) + # A disabled step does nothing, so what only it names is left out. + area_ids -= only_in_disabled_steps( + raw_config, lambda found: extract_targets_from_config(found).area_ids + ) return async_filter_known_area_ids( self.hass, diff --git a/custom_components/spook/ectoplasms/script/repairs/unknown_device_references.py b/custom_components/spook/ectoplasms/script/repairs/unknown_device_references.py index 84f79931..58f5d8db 100644 --- a/custom_components/spook/ectoplasms/script/repairs/unknown_device_references.py +++ b/custom_components/spook/ectoplasms/script/repairs/unknown_device_references.py @@ -8,7 +8,10 @@ from homeassistant.components import script from homeassistant.helpers import device_registry as dr from ....entity_filtering import async_filter_known_device_ids, async_get_all_device_ids -from ....reference_extraction import extract_targets_from_config +from ....reference_extraction import ( + extract_targets_from_config, + only_in_disabled_steps, +) from ....repairs import AbstractSpookEntityComponentUnknownReferencesRepair from ....template_extraction import extract_device_ids_from_config @@ -46,6 +49,14 @@ class SpookRepair(AbstractSpookEntityComponentUnknownReferencesRepair): device_ids.update(extract_targets_from_config(raw_config).device_ids) # Devices referenced via device_entities() in templates. device_ids.update(extract_device_ids_from_config(raw_config)) + # A disabled step does nothing, so what only it names is left out. + device_ids -= only_in_disabled_steps( + raw_config, + lambda found: ( + extract_targets_from_config(found).device_ids + | extract_device_ids_from_config(found) + ), + ) return async_filter_known_device_ids( self.hass, diff --git a/custom_components/spook/ectoplasms/script/repairs/unknown_entity_references.py b/custom_components/spook/ectoplasms/script/repairs/unknown_entity_references.py index 1d879401..7e04f86f 100644 --- a/custom_components/spook/ectoplasms/script/repairs/unknown_entity_references.py +++ b/custom_components/spook/ectoplasms/script/repairs/unknown_entity_references.py @@ -8,7 +8,9 @@ from homeassistant.components import script from homeassistant.const import EVENT_COMPONENT_LOADED from homeassistant.helpers import entity_registry as er +from ....action_extraction import async_extract_entities_from_action_config from ....entity_filtering import async_get_all_entity_ids, async_get_all_services +from ....reference_extraction import without_disabled_steps from ....repairs import AbstractSpookEntityComponentUnknownReferencesRepair from ....template_extraction import ( async_extract_entities_from_config, @@ -19,37 +21,6 @@ if TYPE_CHECKING: from homeassistant.core import HomeAssistant -def extract_entities_from_trigger_config(config: dict[str, Any] | list) -> set[str]: - """Extract entity IDs from a trigger config.""" - entities = set() - - if not config: - return entities - - if isinstance(config, list): - for item in config: - entities.update(extract_entities_from_trigger_config(item)) - return entities - - if not isinstance(config, dict): - return entities - - # Extract entity_id from trigger config - if "entity_id" in config: - entity_id = config["entity_id"] - if isinstance(entity_id, str): - entities.add(entity_id) - elif isinstance(entity_id, list): - entities.update([e for e in entity_id if isinstance(e, str)]) - - # Recursively process nested configs - for value in config.values(): - if isinstance(value, (dict, list)): - entities.update(extract_entities_from_trigger_config(value)) - - return entities - - def extract_referenced_entities_from_script(entity: script.ScriptEntity) -> set[str]: """Return entity references from a script entity.""" try: @@ -74,18 +45,12 @@ async def extract_template_entities_from_script_entity( ``known_services`` is built once per inspection and handed down, because building it flattens every service Home Assistant has and every script with a template in it needs the same answer. - """ - # Get the script configuration - config = None - if hasattr(entity, "script"): - # Try to get configuration safely - if hasattr(entity.script, "config"): - config = entity.script.config - elif hasattr(entity.script, "_config"): - # Fallback to _config if needed - config = getattr(entity.script, "_config", None) - if not config: + Read from the configuration as written, like the automation repair does. + The script helper underneath keeps no configuration of its own, so this + used to find nothing at all. + """ + if not (config := getattr(entity, "raw_config", None)): return set() return await async_extract_entities_from_config(hass, config, known_services) @@ -112,34 +77,6 @@ class SpookRepair(AbstractSpookEntityComponentUnknownReferencesRepair): _known_entity_ids: set[str] _known_services: set[str] - def _get_blueprint_trigger_entities(self, entity: script.ScriptEntity) -> set[str]: - """Extract entity references from blueprint trigger inputs.""" - entities = set() - - if ( - not hasattr(entity, "referenced_blueprint") - or not entity.referenced_blueprint - ): - return entities - - config = getattr(entity, "_config", None) - if not config or not isinstance(config, dict) or "use_blueprint" not in config: - return entities - - blueprint_config = config["use_blueprint"] - if "input" not in blueprint_config: - return entities - - input_config = blueprint_config["input"] - # Look for inputs that might contain triggers (like discard_when) - for value in input_config.values(): - if isinstance(value, (dict, list)) and "trigger" in str(value): - trigger_entities = extract_entities_from_trigger_config(value) - if trigger_entities: - entities.update(trigger_entities) - - return entities - async def _async_setup_inspection(self) -> None: """Cache what every script in this cycle needs looked up. @@ -152,13 +89,42 @@ class SpookRepair(AbstractSpookEntityComponentUnknownReferencesRepair): ) self._known_services = async_get_all_services(self.hass) + async def _async_named_in(self, config: dict[str, Any]) -> set[str]: + """Return the entities a configuration names, the way this repair reads it. + + The same reading the report is built from, the steps and templates + alike, so comparing it with and without the disabled steps leaves out + exactly what only those name. + """ + steps = config.get("sequence") or [] + named = await async_extract_entities_from_action_config( + self.hass, + # A single step can be written without a list. + [steps] if isinstance(steps, dict) else steps, + known_services=self._known_services, + ) + named |= await async_extract_entities_from_config( + self.hass, config, self._known_services + ) + return named + async def _async_compute_unknown_references(self, entity: Any) -> set[str]: """Return unknown entity IDs referenced by ``entity`` (incl. templates).""" # Get all referenced entities from the script all_entities = extract_referenced_entities_from_script(entity) - # Check for blueprint trigger inputs - all_entities.update(self._get_blueprint_trigger_entities(entity)) + # Home Assistant's own list leaves out entities handed over as action + # data, like `entity: light.kitchen` in a call to another script. The + # automation repair reads those from the configuration as written, and + # so does this one. + if isinstance(raw_config := getattr(entity, "raw_config", None), dict): + all_entities.update( + await async_extract_entities_from_action_config( + self.hass, + raw_config.get("sequence") or [], + known_services=self._known_services, + ) + ) # Extract entities from Template objects within the script entity all_entities.update( @@ -167,6 +133,15 @@ class SpookRepair(AbstractSpookEntityComponentUnknownReferencesRepair): ) ) + # Home Assistant's own list includes disabled steps too. A step parked + # that way does nothing, so what only it names is left out: whatever + # this repair finds in the configuration, and no longer finds once + # those are pruned. + if isinstance(raw_config, dict): + named = await self._async_named_in(raw_config) + still_named = await self._async_named_in(without_disabled_steps(raw_config)) + all_entities -= named - still_named + return await async_filter_known_entity_ids_with_templates( self.hass, entity_ids=all_entities, diff --git a/custom_components/spook/ectoplasms/script/repairs/unknown_floor_references.py b/custom_components/spook/ectoplasms/script/repairs/unknown_floor_references.py index 230a4aa8..4f858dba 100644 --- a/custom_components/spook/ectoplasms/script/repairs/unknown_floor_references.py +++ b/custom_components/spook/ectoplasms/script/repairs/unknown_floor_references.py @@ -8,7 +8,10 @@ from homeassistant.components import script from homeassistant.helpers import floor_registry as fr from ....entity_filtering import async_filter_known_floor_ids, async_get_all_floor_ids -from ....reference_extraction import extract_targets_from_config +from ....reference_extraction import ( + extract_targets_from_config, + only_in_disabled_steps, +) from ....repairs import AbstractSpookEntityComponentUnknownReferencesRepair if TYPE_CHECKING: @@ -42,6 +45,10 @@ class SpookRepair(AbstractSpookEntityComponentUnknownReferencesRepair): # references nested in some step types, like repeat sequences. if raw_config := getattr(entity, "raw_config", None): floor_ids.update(extract_targets_from_config(raw_config).floor_ids) + # A disabled step does nothing, so what only it names is left out. + floor_ids -= only_in_disabled_steps( + raw_config, lambda found: extract_targets_from_config(found).floor_ids + ) return async_filter_known_floor_ids( self.hass, diff --git a/custom_components/spook/ectoplasms/script/repairs/unknown_label_references.py b/custom_components/spook/ectoplasms/script/repairs/unknown_label_references.py index 5ff765b9..68f182ef 100644 --- a/custom_components/spook/ectoplasms/script/repairs/unknown_label_references.py +++ b/custom_components/spook/ectoplasms/script/repairs/unknown_label_references.py @@ -8,7 +8,10 @@ from homeassistant.components import script from homeassistant.helpers import label_registry as lr from ....entity_filtering import async_filter_known_label_ids, async_get_all_label_ids -from ....reference_extraction import extract_targets_from_config +from ....reference_extraction import ( + extract_targets_from_config, + only_in_disabled_steps, +) from ....repairs import AbstractSpookEntityComponentUnknownReferencesRepair if TYPE_CHECKING: @@ -42,6 +45,10 @@ class SpookRepair(AbstractSpookEntityComponentUnknownReferencesRepair): # references nested in some step types, like repeat sequences. if raw_config := getattr(entity, "raw_config", None): label_ids.update(extract_targets_from_config(raw_config).label_ids) + # A disabled step does nothing, so what only it names is left out. + label_ids -= only_in_disabled_steps( + raw_config, lambda found: extract_targets_from_config(found).label_ids + ) return async_filter_known_label_ids( self.hass, diff --git a/custom_components/spook/ectoplasms/select/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/select/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 352780f9affe61fdbb239f87b00b05718f4b8cde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 194 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DlIYq;;_lhPbtkwwJTx+ng()CF$a+N Qz|6?Vc$-18hy}<20G1;&HUIzs diff --git a/custom_components/spook/ectoplasms/select/services/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/select/services/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index bcc21b69efb7248032b4a4c8622fe7587618cb0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DC%vZ!<_1u>d&$z7RIj diff --git a/custom_components/spook/ectoplasms/select/services/__pycache__/random.cpython-314.pyc b/custom_components/spook/ectoplasms/select/services/__pycache__/random.cpython-314.pyc deleted file mode 100644 index 806123dcd2657a78a94921a452203a634dc6d215..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2057 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 diff --git a/custom_components/spook/ectoplasms/switch_as_x/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/switch_as_x/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index d56843ea8a108674ad82e663cdb5912433f19b01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D`Kd5uaEbU!fl#pP83g5+AQuP4BO~K&2FW58AO`@;|20gJcm4kOKh3jyQw> diff --git a/custom_components/spook/ectoplasms/switch_as_x/repairs/__pycache__/unknown_source.cpython-314.pyc b/custom_components/spook/ectoplasms/switch_as_x/repairs/__pycache__/unknown_source.cpython-314.pyc deleted file mode 100644 index 1151de44f917fef56aaff801fcff8333dd2a806d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1550 zcmZux&u6rTOTvIc`mEIY01)|;d~3a; zI{3t)%CUb(d#U=TIHx2lMT%5eRc};O$+d5GaD%0ev~S*?tS{WFwA_k*(B~n#$EJp{sSRrgJ^kwR)mva3j|Bda`EnCD3&{ zQBT#^ETlGGE?8Yv?+%lh=@Kw$pbZ4>5SgUb=}2sngYiFbE!7 zx2&UJM6G@>^hmLwL}|zO1MYAy@EMqy)={&H%e89x+j`?ZEUZ@#tBn>e?>}hnH-z@x z{%)mO0nZBYxyQ$tlAgyn9m7&__Yr80%j2`EvA;QRcsHQKhyG(fIPnkQbeGs9bUX?> zv|WGf%1otH<)q9SpiEF3aRn$-k8@nJ6|UPVPuLna+R3)ryt=qi>bF&%f`8Js4m2dL zp+Sx>+Xj%31QN_B9VaGFKP^i}Xq=11sYjT_1FP%#9cv`BwOE|46+;z%Kw_~IkGp;B zF#NO_r5CJdW5K}l5LkwA6-ZEgV(9QeeUzC6$^;#vWA)3Q5bD6%Ci-6KE4E^*wr1-k z71B=pB}j=3$v1_vgFy)&Q9sgv?_UuRX$X!T&cqQnqGrhr{I1t4xg*AdA$Egd82Au? zm6&LQ5^;GD4jeXwh4*kuk_%S4oO>}GN6Q$`JB45zeXzh|anbV;9M3jDOwc=|C3gN6 z;+OJNOuv_7YX)BA&g(4nm==JZnaPcZN;6)*EPx2*blZ+$Tf9%K6Y7K^>CDTKXR=6R zt6mX(eFe{PN!GB}DX5Xo$e>HJ5SWScx3}pkOwk(d5k9XI{_p?j`X!M6_e+d62O%bC zYM|WuiWs;cqVUn?g)Lh{0HtH>_8q@RI#f^(L|`V~0v9_R?!%)ICk*2lheRakqDvP-AZK?$ zJVUSa2X8VPlf9Rj!esA_`N>N&^}^hFZthGI+PX1mOilFZoip8fr(ZOM3@Bm&Mbv^> zu+tquTLU{(wmct)p5LR;94K0aH;Xuo!DHk2ya*_u_hT~FE_?c95E9C$czNkfu{AmG zYifukae*WVQK0w0BJ(R*oUiFEaEP14J_9k;6h(Q3K0HS^&XIMF?!87|oTI%zwcKlc S;~yid?3`Ww<}HFbj`c5T(X844 diff --git a/custom_components/spook/ectoplasms/switch_as_x/repairs/unknown_source.py b/custom_components/spook/ectoplasms/switch_as_x/repairs/unknown_source.py deleted file mode 100644 index 8e5f5404..00000000 --- a/custom_components/spook/ectoplasms/switch_as_x/repairs/unknown_source.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Spook - Your homie.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from homeassistant.const import EVENT_COMPONENT_LOADED -from homeassistant.helpers import entity_registry as er - -from ....repairs import AbstractSpookEntityPlatformUnknownSourceRepair - -if TYPE_CHECKING: - from typing import Any - - -class SpookRepair(AbstractSpookEntityPlatformUnknownSourceRepair): - """Spook repair tries to find unknown source entities for switch_as_x.""" - - domain = "switch_as_x" - repair = "switch_as_x_unknown_source" - inspect_events = { - EVENT_COMPONENT_LOADED, - er.EVENT_ENTITY_REGISTRY_UPDATED, - } - inspect_config_entry_changed = "switch_as_x" - - def _get_source_entity_id(self, entity: Any) -> str: - """Return the wrapped switch's entity ID.""" - # pylint: disable-next=protected-access - return entity._switch_entity_id # noqa: SLF001 diff --git a/custom_components/spook/ectoplasms/timer/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/timer/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 35ab8a28372bb536d24207f421477a2289598369..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 193 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DPO4oI8_+P2Yl=C5 R#0O?ZM#kF=l0_^)4gg#)GS&b9 diff --git a/custom_components/spook/ectoplasms/timer/services/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/timer/services/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 77d791fb0b64d9a2318a01cd835aaf645aa1b518..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#Ds4O!%wOBtsJ~J<~BtBlRpz;=nO>TZlX-=wL a5gX7{kh6+8fW!x8Mn=Zl43b4GKn?(avo>-7 diff --git a/custom_components/spook/ectoplasms/timer/services/__pycache__/set_duration.cpython-314.pyc b/custom_components/spook/ectoplasms/timer/services/__pycache__/set_duration.cpython-314.pyc deleted file mode 100644 index 3da73d564c7700c7211708a0accc5051ed6b4a73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2940 zcma)8Uu@gP89$1YNd2R2*|IIiimdF^QgwX($S>OBQ#FWi&uurBV;6xvgins6C+4Y*0Qz4cgI4N5jxEVn2gHo z1S+J~2QNFe{l0Wkx?;Dm)Uca|UMw(PQnf6bsKl@>7pCdOE7f^rt~@{YW@X{RLLus< z=PoUrSI+%ho?Wb5TJU1$F1WTHdfd4IomXKjf(OHlvNuyRiY4J ztLr9FK{)S=s+E zEn<)xb9U3QE!`rQb$s2>bYM$%;GAljTGtK;bqvAmSwL;08X^olVPs{Durfoq8a$UA zA-v2Ip%$+W^==LD5mZZ_W&&JGReOva@|GAP${ajHbOf3Gq)(2Kewhce3tr0Cx})3r z_vy8z7x$LjKz)hW0Etv@VgJ?=7*A3qYCBx?Qm~|iON$!Sq@xX@t_k;J_KpE)qb0Nw z`Qfh-ssn8t{fucaG9yQ1R_5fW94k>r;J=Chs}}>?fgLdw;Bgw*45xiM)=3Ln9=8DI z{SyMa#)5}*>5)ghN-21vlGbvG-Bh&jA>5Kn{i_7;Z#$;yHrkMFL8QU&jIM0WZ=z z&rUIdxIw={D82VvxcpI+r>0`6{?h)=VS!E!bfCS6z(HzQ;NcE1z%=Ap8=yQgv&5#* z_aY9rOi%aQ5*NBrg8>!bCOj#RmPf-~aHE%m#>=F?0bA-59ZP=8J>7G9HFEg3fAa0m zl_R}-Wwv*=f_i7w@$gpNv3j_RL*(#FeDz2;>yGc}r#oJB0V=_J;ENwHZT78b0(?*A zCcq)ziv55!m_l@Yw5(cnQSrY{{R{o7 zYisXARH=@!o(Q7NgoL_K81Ujjd@DxX6T(eiyk!~JS~?Iht#%#{;Y;%^Bmf%%l67nc z$LT_>;Jif}*&(xeAe+fkZ&FKM3olf7QKVX-XhQF^JxT zRLIc{7$OK^nq_gGO5>Zl>%wl2513t#r|eYmL|33om6)2El4!#@x^XBtqTyQ)bn)we zcVu50c^L~0Q*oE{?Wr^HJ)AnTGkW^L zl?QJ=9RBI83;)g>yZ_E+=H$cFNk}_`xqHRC#qG@GMrLv|^U5uLCzHE(;_ivxuWe?g zU?%k45pRqC>Hp57{HX^+f6x6T_s^NF*D9O&i@R(jEk0wpc;cCeQkjpI?!9~W-R`e7Ob7L6vq0ApGqf;byI zxY2@tA3r8X_pj(5Xu9M7wFR-9-M`$>O|YbkX+6QyR3#xS#5hH#nf?5ljdlMIATZ;8 z(|W>m;0Hg(71&FwfIAD_F3T{?=P36S9es+VFVN_7F2+Q@L9`PQ^Xg;t`eXFQQ#Ac0 c+y9s=K8u}TW*!cndC=M#ocS+;iEros1F9msVE_OC diff --git a/custom_components/spook/ectoplasms/trend/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/trend/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index db889d3c48afb5b7556ef4efba9fd5727743c4c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 193 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DPO4oI8_+P2Yl=C5 R#0O?ZM#kF=l0_^)4ggzwGSUD5 diff --git a/custom_components/spook/ectoplasms/trend/repairs/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/trend/repairs/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index f9302a7d7c2e25174887f84a0308d3e1f472b44a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201 zcmdPqV1z{6!+ZcBtNF2;)S+8Lmd&iv_ zw=yRmqWmrGrRtvoCz6$rkSbL5Mn%+I`)0-n++?Ml`OUnU_wjr0&7+BI2Eq60_w65# z421p^i;FL%fOh^8zyNuOqiy7Au2$8#z96vf8dZ~<5*u!+YH>?q(@j@xZcChUuT?XA z46x~0?szrJvkRzUJ{VeQrV+X=bfrPp1fN(y)2M(vdlq@u8rjdmC-L!|m$`=)$M$cm zcL{mnJa)E7k2-s#6NSY^EgAFUm~fv*BxbP8Zfw;nc%@od`Od996&pKI?9)DGVay1H zjhV`3rM7`r)}GbZY65+~wp^~1L6Qq&9`Qb=;cmn@?ZZ}ac?XamaA~|E<@L7DTZDF= z$1h^?OS}&51fdsp{Ya=cmg9b)Tvwy6ChfchV1OEkYrvsK!-{He|}aAvGAUY~nk$|pP%_)5*9b{?3rc-~6`TXr%g9adiLR}^(r`si>Ph0Ni^ zX+?3<>8U6jCc|(f1wsxH!pRU$DCSr(Ny&9GB_}+*h}FeF1LwpqJ`;XOpLXs57@$qG zt$+0rp(gCzMn7nKnx}cXXLx2w2WL}%3yKm;u}^Kepds(kI5B|z-w@Dh3C4ZSgo(S! zx21r@t!TFt^cW`{9FR_z#Grtcn5dFc7;w^U`>exC(%X`Xos~wtE_VCLIL5;g3NcP@ zUg52HRdNRmGm(J-dWVcu;U6*lx^gC_KUU=G9Hb~1=38J%2n3xyR~I+6y4)TefC6p* zktYLo_+B_d`vW$TnZDzeMb&4pVhcyjMLjWD*ly7rsAadIC_i(Qa}IOPf$h*)*rcLLYm1YKjdARE1Zx?LJEYlb3k-bz)braz zNe<&yL>X^KkRAf!G{#L50I-^*<418~snk&M<|KMZlX=yk(3x~BZ&FjJvdU}Emfc!1qY4y5ImosJ^TICuNF18R zn2ET= z-;Ls33I&9coABU;K4b7O`7tkE7Q71f#{b1JG>&9+dQ`F(w!0x^)B>gSW1%@cYz+#} zF_PV<;uTrGiz>~M#yvk diff --git a/custom_components/spook/ectoplasms/trend/repairs/unknown_source.py b/custom_components/spook/ectoplasms/trend/repairs/unknown_source.py deleted file mode 100644 index 8c31078a..00000000 --- a/custom_components/spook/ectoplasms/trend/repairs/unknown_source.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Spook - Your homie.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from homeassistant.components import binary_sensor -from homeassistant.const import EVENT_COMPONENT_LOADED -from homeassistant.helpers import entity_registry as er - -from ....repairs import AbstractSpookEntityPlatformUnknownSourceRepair - -if TYPE_CHECKING: - from typing import Any - - -class SpookRepair(AbstractSpookEntityPlatformUnknownSourceRepair): - """Spook repair tries to find unknown source entities for trend sensors.""" - - domain = "trend" - repair = "trend_unknown_source" - inspect_events = { - EVENT_COMPONENT_LOADED, - er.EVENT_ENTITY_REGISTRY_UPDATED, - } - inspect_on_reload = "trend" - - source_platform_domain = binary_sensor.DOMAIN - - def _get_source_entity_id(self, entity: Any) -> str: - """Return the trend sensor's source entity ID.""" - # pylint: disable-next=protected-access - return entity._entity_id # noqa: SLF001 diff --git a/custom_components/spook/ectoplasms/utility_meter/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/utility_meter/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 61c9cf414d58c3115cd15d34edc66eb461d97510..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DPO4oI Z8_-aYtBN^*#0O?ZM#kF=l0_^)4ggU6HcbEk diff --git a/custom_components/spook/ectoplasms/utility_meter/repairs/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/utility_meter/repairs/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index 0b69911a5b257d05a0b7b610a801cdf2196975b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#DPO4oI8_;l&>xwym#0O?ZM#kF=l0_^)4ggUBIeGv9 diff --git a/custom_components/spook/ectoplasms/utility_meter/repairs/__pycache__/unknown_source.cpython-314.pyc b/custom_components/spook/ectoplasms/utility_meter/repairs/__pycache__/unknown_source.cpython-314.pyc deleted file mode 100644 index 6a8d41f7eaec4483adc6ceaad096df0e7c502d68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1688 zcmZux&u<$=6rTOz_11}FrzJ@n(q_}5#!(a-1Og7IB8r`Q1$E5Ejg*5k+OF5hQhV3T zj6u%HhpPVrC$4Zo{3+?F?MRU#p$KtHWkj#MnQ_z@7-?s|d2ja3d*6F+)@QOA1mpRy z2j9Li5c*wgE=NiMZS)Ml5P6891LSG0R@IrlEU@kxRg;+#8*ZvGZOPq4A zRWob~u<2RubT!Me%cyCtj&EtD5n2+q(qLfwy)Z)pu$FeZ8|;u9QKO3u6|s0Vd&LL>U=?sJQtAkRLF4Yenko9iO!m(tR91 zjgueZUGO6ay|Cv;!o0B=4+7=0nsv2kqc(scY9giqk@~?bGdzu%p3YL9!K`MwY1gk` z{ZZi$b(VoMQ_bo6;(xVl#xsGa)B@_{fg+3Ly)@8d^C{`D^6Ge7aYm$%4kJ-W9hNxl zC~i4@6@^1(7>=Ys=pjO<&!P?x;&ek65?bWhu>eZ}Uz*tBu_df51sZrOJ~2%AAd%Y0 z0Wd^+=s^Gc8A2_PEun9;BhAx1-7`G1q=WaVKLuypQt2SJ+>ns<#!d z#^a(2G3Ga~5L&#dxdRSRk(wcTi;Ps^jhKGkx)956TXHu7EeghkAh09`g3pfBk9%5O ziYF(aLOcH0lYu+zD7?(ahNRz76}?I)Qa5nivgn6dtkmOpaMW7Uxkmys!%wDP!Im)-G&xt8ik@o!!3oQ}f5>iMjaJysRQ+T3D^E z<;e=Hg28qlIsq6Ys!Q0Q7sZDJstNI%@E(RfrSN?DF)P070u}0wf2(8YCdudAq~<8> z^q}L31xCpS!g6}tCj?$=BwJEND{}mf0d9Yx^80{%1PalcbOWXfL({a^=+-&9dybrQ hw0?>{Jw>0LqwPP8{HeM4mzCEFC)dCH8^J2g{R0AB*3bX| diff --git a/custom_components/spook/ectoplasms/utility_meter/repairs/unknown_source.py b/custom_components/spook/ectoplasms/utility_meter/repairs/unknown_source.py deleted file mode 100644 index 89f7dabc..00000000 --- a/custom_components/spook/ectoplasms/utility_meter/repairs/unknown_source.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Spook - Your homie.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from homeassistant.components import sensor -from homeassistant.const import EVENT_COMPONENT_LOADED -from homeassistant.helpers import entity_registry as er - -from ....repairs import AbstractSpookEntityPlatformUnknownSourceRepair - -if TYPE_CHECKING: - from typing import Any - - -class SpookRepair(AbstractSpookEntityPlatformUnknownSourceRepair): - """Spook repair tries to find unknown source entities for utility meters.""" - - domain = "utility_meter" - repair = "utility_meter_unknown_source" - inspect_events = { - EVENT_COMPONENT_LOADED, - er.EVENT_ENTITY_REGISTRY_UPDATED, - } - inspect_on_reload = "utility_meter" - - source_platform_domain = sensor.DOMAIN - - def _get_source_entity_id(self, entity: Any) -> str: - """Return the utility-meter sensor's source entity ID.""" - # pylint: disable-next=protected-access - return entity._sensor_source_id # noqa: SLF001 diff --git a/custom_components/spook/ectoplasms/zone/__pycache__/__init__.cpython-314.pyc b/custom_components/spook/ectoplasms/zone/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index d5eb336984f4db0a875fa6fe6c4a04559b4db94e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 192 zcmdPq^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D^MASDe9K@24fL5#`_noL!~!3FvG*$TP}k@=-X z3K{vinW=hynvA#D2KczG$)vkyQ YXeh{4#T-E512ZEd<821XA{HPA0776k3;+NC diff --git a/custom_components/spook/ectoplasms/zone/services/__pycache__/create.cpython-314.pyc b/custom_components/spook/ectoplasms/zone/services/__pycache__/create.cpython-314.pyc deleted file mode 100644 index afbb41395e6beaba41cda4438738493f11b5fd78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1901 zcma)7O>7fK6n^{Twd2?hB#_i8BpV{pI)L4xv;s9yB_Uu_NZk#Jl&Z;US#RQn^{zWJ zZtL`7wN)i<<%BqK=p~UVapjEEdn1SvR;pB~iV(L{p+&v*&F-3nlmjE}%zJO<&HMY_ z?1@Y|2{?Wp`Sr$S8Q@ROv_~u?gdK)N3p8M`0Gdz|iXsySIW3l?qRixgmP)ar!jxj1 z#fu4+K$)z`rH*2fB?n<%ekZ!M5(hZKbz`N@Vv3~(;V=w=ru2appHGfT;W3uh62p+| zSniprd)`g;lse}%h+6e(ww2Ebey8EO9y6HjxfI#-wYlE}DJS{qNo{KU z+LV5=GYExYv&~QIvREH>c?+>hbHXR)x^Er z0_^3=O54KjTIMy}H|pZ7Snk{>M0QqqUEJmL|4)uaqR|Lsmq3$lAfUbxTGEwR5_7Cc z!w`on$_J7yUH18vz#*LwEMgSXeSQ=114{w<09nWvfFTh)!e+*)65 zI|k{#?F4jXH!>q@nUR&z`)60r-ao&3{?Un#9%aTh5@U}OWB<}_wEo@1sfjbf!-6pJ zq5SZ@IMU=Gil=iao;KI0@!m@6dd;ge98P1`ec3RaXs1ip7i~hBV`KArxEo4 z&;gu&geUUpKhiy|i<_C=)}_sKf9v84x!vBOD%YCMWeERY@E{f&_$XlzBzUeA)~ws^ z65-|akK*sZLdYP744371-=jRoR^WFK65;LQC)&>u!9NiA-TRe^6+DBX+pmV=C&t2D zo+t04F7E|8g44Dn2*MM{Zo#oFP@lr=8q99N>8H|x-{t&s<&bdhx1N*#0Ior7{{a!| B!+8Jz diff --git a/custom_components/spook/ectoplasms/zone/services/__pycache__/delete.cpython-314.pyc b/custom_components/spook/ectoplasms/zone/services/__pycache__/delete.cpython-314.pyc deleted file mode 100644 index 4009ac6f1d41620527b22ea29a8d60150dd0cea4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3077 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 diff --git a/custom_components/spook/ectoplasms/zone/services/update.py b/custom_components/spook/ectoplasms/zone/services/update.py index 3ca76206..ca2dfc7f 100644 --- a/custom_components/spook/ectoplasms/zone/services/update.py +++ b/custom_components/spook/ectoplasms/zone/services/update.py @@ -8,10 +8,12 @@ import voluptuous as vol from homeassistant.components.zone import ( DOMAIN, + ENTITY_ID_HOME, UPDATE_FIELDS, Zone, ZoneStorageCollection, ) +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_component import DATA_INSTANCES, EntityComponent @@ -22,8 +24,24 @@ if TYPE_CHECKING: from homeassistant.core import ServiceCall +def _is_the_house(entity: Zone) -> bool: + """Tell whether a zone is the home zone drawn from the house's location. + + Home Assistant only draws it when no zone has taken `zone.home` already: + zones from YAML and storage are set up first, and one named Home gets that + entity ID. Those are ordinary zones, and moving the house would not move + them. The drawn one is the only editable zone without a stored ID. + """ + return ( + entity.entity_id == ENTITY_ID_HOME + and entity.editable + # pylint: disable-next=protected-access + and "id" not in entity._config # noqa: SLF001 + ) + + class SpookService(AbstractSpookAdminService): - """Zone service to update a zone on the fly.""" + """Zone service to update a zone on the fly, the home zone included.""" domain = DOMAIN service = "update" @@ -50,6 +68,10 @@ class SpookService(AbstractSpookAdminService): message = f"Could not find entity_id: {call.data['entity_id']}" raise HomeAssistantError(message) + if _is_the_house(entity): + await self._async_update_home(call) + return + # pylint: disable-next=protected-access if not entity.editable or "id" not in entity._config: # noqa: SLF001 message = f"This zone is not editable: {call.data['entity_id']}" @@ -60,3 +82,37 @@ class SpookService(AbstractSpookAdminService): # pylint: disable-next=protected-access await collection.async_update_item(entity._config["id"], data) # noqa: SLF001 + + async def _async_update_home(self, call: ServiceCall) -> None: + """Move or resize the home zone. + + It is not a stored zone: Home Assistant draws it from the location + set for the whole house, and redraws it when that changes. So this + changes the location, the same way Home Assistant's own + `homeassistant.set_location` does, and the radius along with it. + + Only those. Its name is the name of the whole house, and a home zone + is never passive. + """ + data = dict(call.data) + data.pop("entity_id") + + if unsupported := set(data) - {CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS}: + message = ( + "The home zone only takes a latitude, longitude and radius, " + f"not: {', '.join(sorted(unsupported))}" + ) + raise HomeAssistantError(message) + + # Home Assistant keeps the radius of the house in whole meters, and + # never below zero. Cutting a fraction off quietly would be a + # different zone than was asked for, and the location is stored + # without going through the checks its configuration normally does. + if CONF_RADIUS in data: + radius = data[CONF_RADIUS] + if radius != int(radius) or radius < 0: + message = "The home zone takes its radius in whole meters, zero or more" + raise HomeAssistantError(message) + data[CONF_RADIUS] = int(radius) + + await self.hass.config.async_update(**data) diff --git a/custom_components/spook/entity_filtering.py b/custom_components/spook/entity_filtering.py index 757e3f1c..23af8f3f 100644 --- a/custom_components/spook/entity_filtering.py +++ b/custom_components/spook/entity_filtering.py @@ -660,17 +660,45 @@ def async_get_all_services(hass: HomeAssistant) -> set[str]: } +@callback +def async_get_disabled_integrations(hass: HomeAssistant) -> set[str]: + """Return the integrations whose config entries are all disabled. + + Turning off an integration takes its actions with it. Its config entries + are still there, which is how this tells "switched off on purpose" apart + from "gone". + """ + entries = hass.config_entries.async_entries() + with_entries = {entry.domain for entry in entries} + with_enabled_entries = { + entry.domain for entry in entries if entry.disabled_by is None + } + + return with_entries - with_enabled_entries + + @callback def async_filter_known_services( hass: HomeAssistant, *, services: set[str], known_services: set[str] | None = None ) -> set[str]: - """Filter out known services.""" + """Filter out known services. + + An action of an integration that is disabled is not unknown, it is + switched off, sometimes by Spook's own `homeassistant.disable_config_entry`. + Those are left out too. A typo in such an action only shows up once the + integration is enabled again, which is when it would break anyway. + """ if known_services is None: known_services = async_get_all_services(hass) + + disabled_integrations = async_get_disabled_integrations(hass) + return { service.lower() for service in services - known_services - if isinstance(service, str) and service + if isinstance(service, str) + and service + and service.lower().split(".", 1)[0] not in disabled_integrations } diff --git a/custom_components/spook/icons.json b/custom_components/spook/icons.json index 6eae7e09..dcea4c05 100644 --- a/custom_components/spook/icons.json +++ b/custom_components/spook/icons.json @@ -1,5 +1,8 @@ { "conditions": { + "is_at_target_temperature": { + "condition": "mdi:thermometer-check" + }, "chance": { "condition": "mdi:dice-multiple" }, @@ -23,6 +26,9 @@ } }, "triggers": { + "automation_turned_off": { + "trigger": "mdi:robot-off" + }, "all_of": { "trigger": "mdi:checkbox-multiple-marked" }, @@ -71,6 +77,18 @@ "while": { "trigger": "mdi:repeat" }, + "target_temperature_reached": { + "trigger": "mdi:thermometer-check" + }, + "update_installed": { + "trigger": "mdi:package-up" + }, + "position_reached": { + "trigger": "mdi:window-shutter-settings" + }, + "script_started": { + "trigger": "mdi:script-text-play" + }, "user_added": { "trigger": "mdi:account-plus" } diff --git a/custom_components/spook/integrations/spook_calibration/manifest.json b/custom_components/spook/integrations/spook_calibration/manifest.json index 334225f4..07bbccb0 100644 --- a/custom_components/spook/integrations/spook_calibration/manifest.json +++ b/custom_components/spook/integrations/spook_calibration/manifest.json @@ -10,5 +10,5 @@ "iot_class": "calculated", "issue_tracker": "https://github.com/frenck/spook/issues", "requirements": [], - "version": "5.8.1" + "version": "5.9.0" } diff --git a/custom_components/spook/integrations/spook_calibration/translations/hu.json b/custom_components/spook/integrations/spook_calibration/translations/hu.json index 0967ef42..b5f55bce 100644 --- a/custom_components/spook/integrations/spook_calibration/translations/hu.json +++ b/custom_components/spook/integrations/spook_calibration/translations/hu.json @@ -1 +1,28 @@ -{} +{ + "config": { + "step": { + "user": { + "data": { + "name": "Megnevezés", + "entity_id": "Forrás entitás", + "offset": "Eltolás mértéke", + "factor": "Szorzó" + }, + "title": "Kalibrálás 👻" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_id": "Forrás entitás", + "offset": "Eltolás mértéke", + "factor": "Szorzó" + }, + "title": "Kalibrálás 👻" + } + } + }, + "title": "Kalibrálás 👻" +} diff --git a/custom_components/spook/integrations/spook_inverse/manifest.json b/custom_components/spook/integrations/spook_inverse/manifest.json index 27c97122..f3e210d4 100644 --- a/custom_components/spook/integrations/spook_inverse/manifest.json +++ b/custom_components/spook/integrations/spook_inverse/manifest.json @@ -10,5 +10,5 @@ "iot_class": "calculated", "issue_tracker": "https://github.com/frenck/spook/issues", "requirements": [], - "version": "5.8.1" + "version": "5.9.0" } diff --git a/custom_components/spook/integrations/spook_inverse/translations/de.json b/custom_components/spook/integrations/spook_inverse/translations/de.json index e2e3e8a4..7bf817d1 100644 --- a/custom_components/spook/integrations/spook_inverse/translations/de.json +++ b/custom_components/spook/integrations/spook_inverse/translations/de.json @@ -11,10 +11,12 @@ "title": "Umkehren 👻" }, "user": { - "description": "Dieser Helfer ermöglicht es Ihnen, das Verhalten einer Entität umzukehren. Zum Beispiel können Sie den Zustand von binären Sensoren und Schaltern umkehren, wie z.B. Öffnen/Schließen oder Ein/Aus.", + "description": "Dieser Helfer ermöglicht es, das Verhalten einer Entität zu invertieren. So lassen sich beispielsweise die Zustände „offen/geschlossen“ oder „ein/aus“ von binären Sensoren, Abdeckungen, Schaltern und Ventilen invertieren.", "menu_options": { "switch": "Einen Schalter umkehren", - "binary_sensor": "Einen binären Sensor umkehren" + "binary_sensor": "Einen binären Sensor umkehren", + "cover": "Abdeckung invertieren", + "valve": "Ein Ventil invertieren" }, "title": "Umkehren 👻" }, @@ -25,6 +27,24 @@ "entity_id": "Quell-Entität" }, "title": "Umkehren 👻" + }, + "cover": { + "data": { + "entity_id": "Quell-Entität", + "hide_source": "Quell-Entität verbergen", + "inverse_tilt": "Neigung invertieren", + "name": "Name", + "inverse_position": "Invertiert offen, geschlossen und Position" + }, + "title": "Invertieren 👻" + }, + "valve": { + "data": { + "entity_id": "Quell-Entität", + "hide_source": "Quell-Entität verbergen", + "name": "Name" + }, + "title": "Invertieren 👻" } } }, @@ -45,6 +65,24 @@ "hide_source": "Quell-Entität verbergen", "entity_id": "Quell-Entität" } + }, + "cover": { + "data": { + "entity_id": "Quell-Entität", + "hide_source": "Quell-Entität verbergen", + "name": "Name", + "inverse_tilt": "Neigung invertieren", + "inverse_position": "Invertiert öffnen, schliessen und Position" + }, + "title": "Invertieren 👻" + }, + "valve": { + "data": { + "hide_source": "Quell-Entität verbergen", + "name": "Name", + "entity_id": "Quell-Entität" + }, + "title": "Invertieren 👻" } } } diff --git a/custom_components/spook/integrations/spook_inverse/translations/es.json b/custom_components/spook/integrations/spook_inverse/translations/es.json index 3145d947..4a5ca5fb 100644 --- a/custom_components/spook/integrations/spook_inverse/translations/es.json +++ b/custom_components/spook/integrations/spook_inverse/translations/es.json @@ -1,5 +1,5 @@ { - "title": "Inverso 👻", + "title": "Invertir 👻", "config": { "step": { "switch": { @@ -8,15 +8,17 @@ "hide_source": "Ocultar entidad origen", "entity_id": "Entidad origen" }, - "title": "Inverso 👻" + "title": "Invertir 👻" }, "user": { "description": "Este ayudante te permite invertir el comportamiento de una entidad. Por ejemplo, invertir abrir/cerrar o encender/apagar de sensores binarios, e interruptores.", "menu_options": { "switch": "Invertir un interruptor", - "binary_sensor": "Invertir un sensor binario" + "binary_sensor": "Invertir un sensor binario", + "cover": "Invertir una persiana", + "valve": "Invertir una válvula" }, - "title": "Inverso 👻" + "title": "Invertir 👻" }, "binary_sensor": { "data": { @@ -24,7 +26,24 @@ "name": "Nombre", "entity_id": "Entidad origen" }, - "title": "Inverso 👻" + "title": "Invertir 👻" + }, + "cover": { + "data": { + "entity_id": "Entidad origen", + "inverse_position": "Invertir apertura, cierre y poisición", + "inverse_tilt": "Invertir inclinación", + "name": "Nombre" + }, + "title": "Invertir 👻" + }, + "valve": { + "data": { + "entity_id": "Entidad origen", + "hide_source": "Ocultar entidad origen", + "name": "Nombre" + }, + "title": "Invertir 👻" } } }, @@ -36,15 +55,33 @@ "entity_id": "Entidad origen", "name": "Nombre" }, - "title": "Inverso 👻" + "title": "Invertir 👻" }, "binary_sensor": { - "title": "Inverso 👻", + "title": "Invertir 👻", "data": { "name": "Nombre", "hide_source": "Ocultar entidad origen", "entity_id": "Entidad origen" } + }, + "cover": { + "data": { + "entity_id": "Entidad origen", + "hide_source": "Ocultar entidad origen", + "inverse_position": "Invertir apertura, cierre y posición", + "inverse_tilt": "Invertir inclinación", + "name": "Nombre" + }, + "title": "Invertir 👻" + }, + "valve": { + "data": { + "entity_id": "Entidad origen", + "hide_source": "Ocultar entidad origen", + "name": "Nombre" + }, + "title": "Invertir 👻" } } } diff --git a/custom_components/spook/integrations/spook_inverse/translations/hu.json b/custom_components/spook/integrations/spook_inverse/translations/hu.json index b304e1c9..bd456332 100644 --- a/custom_components/spook/integrations/spook_inverse/translations/hu.json +++ b/custom_components/spook/integrations/spook_inverse/translations/hu.json @@ -25,6 +25,22 @@ "entity_id": "Forrás entitás" }, "title": "Inverz 👻" + }, + "cover": { + "data": { + "entity_id": "Forrás entitás", + "hide_source": "Forrás entitás elrejtése", + "name": "Megnevezés" + }, + "title": "Inverz 👻" + }, + "valve": { + "data": { + "entity_id": "Forrás entitás", + "hide_source": "Forrás entitás elrejtése", + "name": "Megnevezés" + }, + "title": "Inverz 👻" } } }, @@ -45,6 +61,22 @@ "hide_source": "Forrás entitás elrejtése", "entity_id": "Forrás entitás" } + }, + "cover": { + "data": { + "entity_id": "Forrás entitás", + "hide_source": "Forrás entitás elrejtése", + "name": "Megnevezés" + }, + "title": "Inverz 👻" + }, + "valve": { + "data": { + "entity_id": "Forrás entitás", + "hide_source": "Forrás entitás elrejtése", + "name": "Megnevezés" + }, + "title": "Inverz 👻" } } } diff --git a/custom_components/spook/integrations/spook_time_in_state/manifest.json b/custom_components/spook/integrations/spook_time_in_state/manifest.json index 1602f64e..92095a78 100644 --- a/custom_components/spook/integrations/spook_time_in_state/manifest.json +++ b/custom_components/spook/integrations/spook_time_in_state/manifest.json @@ -10,5 +10,5 @@ "iot_class": "calculated", "issue_tracker": "https://github.com/frenck/spook/issues", "requirements": [], - "version": "5.8.1" + "version": "5.9.0" } diff --git a/custom_components/spook/manifest.json b/custom_components/spook/manifest.json index fb804bf0..ae4483bd 100644 --- a/custom_components/spook/manifest.json +++ b/custom_components/spook/manifest.json @@ -48,5 +48,5 @@ "iot_class": "local_push", "issue_tracker": "https://github.com/frenck/spook/issues", "requirements": [], - "version": "5.8.1" + "version": "5.9.0" } diff --git a/custom_components/spook/services.yaml b/custom_components/spook/services.yaml index b8a18aec..18bb86bd 100644 --- a/custom_components/spook/services.yaml +++ b/custom_components/spook/services.yaml @@ -527,26 +527,46 @@ homeassistant_delete_area: homeassistant_disable_config_entry: name: Disable an integration 👻 description: >- - Disables an integration entry. + Disables integration entries, by entry or every entry of an integration. fields: config_entry_id: name: Integration entry description: The integration entry to disable. - required: true + required: false selector: config_entry: + domain: + name: Integration + description: >- + Disable every entry of these integrations, the ones added later + included. + required: false + example: hue + selector: + text: + multiple: true homeassistant_enable_config_entry: name: Enable an integration 👻 description: >- - Enables an integration entry. + Enables integration entries, by entry or every entry of an integration. fields: config_entry_id: name: Integration entry description: The integration entry to enable. - required: true + required: false selector: config_entry: + domain: + name: Integration + description: >- + Enable every entry of these integrations, the ones added later + included. + required: false + example: hue + selector: + text: + multiple: true homeassistant_disable_device: name: Disable a device 👻 @@ -1641,6 +1661,16 @@ input_select_random: selector: object: +input_select_reverse: + name: Reverse options 👻 + description: >- + Reverses the order of the selectable options for an `input_select` + entity, back to front. This is not persistent and will be undone once + reloaded or Home Assistant restarts. + target: + entity: + domain: input_select + input_select_shuffle: name: Shuffle options 👻 description: >- @@ -1779,6 +1809,44 @@ input_number_min: entity: domain: input_number +input_number_set_range: + name: Set range 👻 + description: >- + Changes the minimum, maximum or step of an input number made in the UI, + the same as editing it. A value outside the new range is moved inside it. + target: + entity: + domain: input_number + fields: + min: + name: Minimum + description: The new minimum value. + required: false + example: 0 + selector: + number: + mode: box + step: any + max: + name: Maximum + description: The new maximum value. + required: false + example: 3600 + selector: + number: + mode: box + step: any + step: + name: Step + description: The new step size. + required: false + example: 1 + selector: + number: + min: 0.000000001 + mode: box + step: any + counter_create: name: Create a counter 👻 description: >- @@ -2341,7 +2409,17 @@ repairs_create: repairs_ignore_all: name: Ignore all issues 👻 description: >- - Ignore all issues currently raised in Home Assistant Repairs. + Ignore all issues currently raised in Home Assistant Repairs, or only + those raised by particular integrations. + fields: + domain: + name: Domain + description: Only ignore the issues raised by these integrations. + required: false + example: hacs + selector: + text: + multiple: true repairs_list: name: List issues 👻 @@ -2394,7 +2472,17 @@ repairs_remove: repairs_unignore_all: name: Unignore all issues 👻 description: >- - Unignore all issues currently raised in Home Assistant Repairs. + Unignore all issues currently raised in Home Assistant Repairs, or only + those raised by particular integrations. + fields: + domain: + name: Domain + description: Only unignore the issues raised by these integrations. + required: false + example: hacs + selector: + text: + multiple: true sensor_set_display_precision: name: Set display precision 👻 @@ -2540,6 +2628,15 @@ zone_create: max: 999999999999 unit_of_measurement: m mode: box + passive: + name: Passive + description: >- + A passive zone is only there for automations: it is not shown on the + map, and does not change where people and device trackers are. + required: false + default: false + selector: + boolean: zone_update: name: Update a zone 👻 @@ -2591,6 +2688,14 @@ zone_update: step: any unit_of_measurement: m mode: box + passive: + name: Passive + description: >- + A passive zone is only there for automations: it is not shown on the + map, and does not change where people and device trackers are. + required: false + selector: + boolean: zone_delete: name: Delete a zone 👻 diff --git a/custom_components/spook/translations/de.json b/custom_components/spook/translations/de.json index 81a85296..008fc543 100644 --- a/custom_components/spook/translations/de.json +++ b/custom_components/spook/translations/de.json @@ -282,7 +282,8 @@ "abort": { "issue_ignored": "Spook wird ab jetzt nichts mehr zu dieser Gruppe berichten.", "not_editable": "Diese Gruppe ist in YAML definiert, daher kann Spook sie nicht bearbeiten. Passe die Gruppe stattdessen in deiner Konfiguration an.", - "manage": "Du kannst diese Gruppe selbst auf der [Seite „Helfer“](/config/helpers) verwalten." + "manage": "Du kannst diese Gruppe selbst auf der [Seite „Helfer“](/config/helpers) verwalten.", + "changed": "Die in dieser Reparaturliste aufgeführten Mitglieder sind wieder dabei oder haben sich seit Spooks letzter Überprüfung verändert, sodass niemand aus der Gruppe entfernt wurde. Spook überprüft die Liste erneut selbstständig und aktualisiert diese Reparaturliste." } } }, @@ -428,7 +429,8 @@ }, "abort": { "issue_ignored": "Spook wird zu diesem Bereich nichts mehr sagen.", - "manage": "Du kannst diesen Bereich selbst auf der [Seite „Bereiche“](/config/areas/dashboard) verwalten." + "manage": "Du kannst diesen Bereich selbst auf der [Seite „Bereiche“](/config/areas/dashboard) verwalten.", + "changed": "Dieser Bereich wird wieder genutzt, seit Spook ihn überprüft hat: Entweder befindet sich etwas darin, oder er wird von einer Automatisierung oder einem Skript verwendet. Daher wurde er nicht verändert. Spook überprüft ihn erneut selbstständig und aktualisiert diesen Reparaturvorgang." } } }, @@ -456,7 +458,8 @@ }, "abort": { "issue_ignored": "Spook wird ab jetzt zu dieser Etage nichts mehr berichten.", - "manage": "Du kannst diese Etage selbst auf der [Seite „Bereiche“](/config/areas/dashboard) verwalten." + "manage": "Du kannst diese Etage selbst auf der [Seite „Bereiche“](/config/areas/dashboard) verwalten.", + "changed": "Diese Etage wird seit Spooks letzter Überprüfung wieder genutzt: Es befindet sich ein Bereich darauf, oder eine Automatisierung bzw. ein Skript nutzt sie. Daher wurde sie nicht verändert. Spook überprüft sie erneut von selbst und aktualisiert diese Reparatur." } } }, @@ -465,7 +468,8 @@ "abort": { "too_few_members": "Ein Min/Max-Helfer benötigt mindestens zwei Elemente; würden die fehlenden entfernt, wären zu wenige übrig. Passe den Helfer selbst auf der [Seite „Helfers“](/config/helpers) an oder entferne ihn.", "issue_ignored": "Spook wird ab jetzt nichts mehr zu diesem Helfer berichten.", - "manage": "Du kannst diesen Helper selbst auf der [Seite „Helpers“](/config/helpers) verwalten." + "manage": "Du kannst diesen Helper selbst auf der [Seite „Helpers“](/config/helpers) verwalten.", + "changed": "Die in dieser Reparaturliste aufgeführten Mitglieder sind wieder da oder haben sich seit Spooks letzter Überprüfung geändert, daher wurde nichts aus dem Helfer entfernt. Spook überprüft die Liste erneut selbstständig und aktualisiert diese Reparaturliste." }, "step": { "init": { @@ -486,8 +490,17 @@ "fix_flow": { "step": { "init": { - "title": "Verwaiste Langzeitstatistiken gefunden" + "title": "Verwaiste Langzeitstatistiken gefunden", + "menu_options": { + "manage": "Lass es mich selbst beheben", + "remove": "Diese Statistiken leeren" + }, + "description": "Spook hat einen Geist in deinem Aufzeichnungsgerät gefunden 👻\n\nHome Assistant führt Langzeitstatistiken für die folgenden Elemente, aber es gibt keine entsprechenden Entitäten mehr dafür:\n\n{statistics}\n\nDas passiert normalerweise, wenn ein Sensor entfernt wurde, während seine Statistiken weiterhin gespeichert wurden. Durch das Löschen dieser Statistiken wird der Verlauf endgültig verworfen. Deshalb schaut Spook noch einmal nach, bevor es das tut, und lässt alles in Ruhe, was in der Zwischenzeit wieder aufgetaucht ist.\n\nSpook 👻 Dein Kumpel." } + }, + "abort": { + "manage": "Du kannst dies selbst auf der [Seite „Statistiken“] (/config/tools/statistics) löschen.", + "clearing_took_too_long": "Spook hat darum gebeten, die Statistiken zu löschen, und der Recorder hat noch nicht bestätigt, dass dies geschehen ist. Der Vorgang steht in der Warteschlange und wird höchstwahrscheinlich ausgeführt, daher bleibt diese Korrektur bestehen, bis Spook sich die Sache erneut angesehen hat." } } }, @@ -592,6 +605,22 @@ "homekit_unknown_entity_references_yaml": { "title": "Unbekannte Entitäten in HomeKit-Bridge verwendet: {bridge}", "description": "Spook hat einen Geist in deiner HomeKit-Bridge gefunden 👻\n\nBeim Herumschweben begegnete Spook der folgenden HomeKit-Bridge:\n\n{bridge}\n\nDiese Bridge nennt die folgenden Entitäten, die Home Assistant unbekannt sind:\n\n{entities}\n\n\n\nHomeKit folgt einer Entität nicht, wenn sie umbenannt wird. Eine eingeschlossene Entität, die umbenannt oder entfernt wurde, verschwindet aus der Home-App. Eine ausgeschlossene Entität, die umbenannt wurde, taucht dort wieder auf.\n\nDiese Bridge ist in deiner YAML-Konfiguration eingerichtet. Um diesen Fehler zu beheben, bearbeite den `filter` dieser Bridge unter `homekit:` und entferne oder korrigiere diese Entitäten.\n\nSpook 👻 Dein Kumpel." + }, + "unknown_area_sensors": { + "fix_flow": { + "step": { + "init": { + "menu_options": { + "manage": "Lass es mich selbst beheben" + }, + "description": "Spook hat in deinen Bereichen einen Geist entdeckt 👻\n\nDer Bereich {area} verwendet einen {sensors}-Sensor, der Home Assistant unbekannt ist:\n\n{entities}\n\nHome Assistant überprüft diesen Sensor nur, wenn er ausgewählt ist. Als er entfernt oder umbenannt wurde, zeigte der Bereich daher ins Leere. Assist- und Bereichskarten erhalten keine Messwerte mehr von ihm.\n\nLösche die Einstellung, damit du wieder einen Sensor auswählen kannst, oder behalte sie bei – dann wird Spook nicht mehr darauf hinweisen.\n\nSpook 👻 Dein Kumpel." + } + } + } + }, + "unknown_engines": { + "title": "Die „Assist“-Pipeline verwendet Engines, die nicht mehr verfügbar sind: {pipeline}", + "description": "Die Assist-Pipeline {pipeline} verwendet Folgendes, was Home Assistant nicht bekannt ist:\n\n{engines}\n\nDas bedeutet in der Regel, dass die dahinterstehende Integration entfernt wurde. Die Pipeline wurde nicht darüber informiert, sodass sie bei der nächsten Verwendung mit einer Fehlermeldung über einen fehlenden Anbieter oder Agenten fehlschlägt, und nichts zuvor weist darauf hin.\n\nUm dies zu beheben, öffne die Einstellungen dieses Assistenten unter [Sprachassistenten](/config/voice-assistants/assistants) und wähle für jeden dieser Einträge eine Engine aus, die noch verfügbar ist.\n\nSpook 👻 Dein Kumpel." } }, "services": { @@ -790,7 +819,7 @@ "description": "Die Maßeinheit der Eingabezahl." } }, - "description": "Legt einen neuen Eingabezahl-Helfer direkt an.", + "description": "Erstellt direkt einen neuen Eingabezahlen-Helfer. Gibt die Entitäts-ID der neuen Eingabezahl zurück.", "name": "Erstelle eine Zahleneingabe" }, "timer_set_duration": { @@ -1405,7 +1434,12 @@ }, "input_number_delete": { "name": "Löscht eine Zahleneingabe", - "description": "Einen Eingabezahl-Helfer löschen. Dies funktioniert nur mit Eingabezahlen, die über die Benutzeroberfläche erstellt und verwaltet wurden. Eingabezahlen, die in YAML erstellt und verwaltet wurden, können von Spook nicht verwaltet werden." + "description": "Einen Eingabezahl-Helfer löschen. Dies funktioniert nur mit Eingabezahlen, die über die Benutzeroberfläche erstellt und verwaltet wurden. Eingabezahlen, die in YAML erstellt und verwaltet wurden, können von Spook nicht verwaltet werden.", + "fields": { + "entity_id": { + "name": "Entitäts-ID" + } + } }, "zone_delete": { "name": "Löscht eine Zone", @@ -1672,6 +1706,411 @@ }, "description": "Ersetzt die Mitglieder einer über die Oberfläche erstellten Gruppe durch die angegebenen, während Home Assistant läuft.", "name": "Lege die Mitglieder einer Gruppe fest" + }, + "homeassistant_add_alias_to_entity": { + "fields": { + "alias": { + "name": "Alias" + }, + "entity_id": { + "name": "Entität" + } + }, + "description": "Fügt einer Entität (oder mehreren Entitäten) einen oder mehrere Aliase hinzu, unter denen Sprachassistenten sie aufrufen können." + }, + "homeassistant_remove_alias_from_entity": { + "fields": { + "alias": { + "name": "Alias" + }, + "entity_id": { + "name": "Entität" + } + } + }, + "homeassistant_set_entity_aliases": { + "fields": { + "aliases": { + "name": "Aliase" + }, + "entity_id": { + "name": "Entität" + } + } + }, + "homeassistant_update_category": { + "fields": { + "category_id": { + "name": "Kategorie" + }, + "icon": { + "name": "Symbol" + }, + "name": { + "name": "Name" + } + } + }, + "homeassistant_delete_category": { + "fields": { + "category_id": { + "name": "Kategorie" + } + }, + "name": "Löscht eine Kategorie" + }, + "homeassistant_add_category_to_entity": { + "fields": { + "category_id": { + "name": "Kategorie" + }, + "entity_id": { + "name": "Entitäts-ID" + } + }, + "name": "Eine Kategorie einer Entität zuweisen" + }, + "homeassistant_remove_category_from_entity": { + "fields": { + "category_id": { + "name": "Kategorie" + }, + "entity_id": { + "name": "Entitäts-ID" + } + } + }, + "humidifier_decrease_humidity": { + "name": "Luftfeuchtigkeit verringern", + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "cover_decrease_position": { + "name": "Position verringern", + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "valve_decrease_position": { + "name": "Position verringern", + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "climate_decrease_temperature": { + "name": "Temperatur verringern", + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "water_heater_decrease_temperature": { + "name": "Temperatur verringern", + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "media_player_decrease_volume": { + "name": "Lautstärke verringern", + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "counter_delete": { + "name": "Löscht einen Zähler", + "fields": { + "entity_id": { + "name": "Entitäts-ID" + } + } + }, + "calendar_update_event": { + "fields": { + "description": { + "name": "Beschreibung" + }, + "duration": { + "name": "Dauer" + }, + "shift": { + "name": "Verschieben" + }, + "summary": { + "name": "Zusammenfassung" + }, + "start_date_time": { + "description": "Wo soll die Suche beginnen? Standardmäßig ab dem aktuellen Zeitpunkt." + }, + "end_date_time": { + "description": "Wo soll die Suche enden? Gib dies oder eine Dauer an." + } + }, + "description": "Ändert Ereignisse in einem Kalender, die anhand ihres Titels oder ihrer UID innerhalb eines bestimmten Zeitraums gefunden wurden. Gibt die geänderten Elemente zurück, einschließlich ihrer UIDs." + }, + "timer_create": { + "fields": { + "duration": { + "name": "Dauer" + }, + "icon": { + "name": "Symbol" + }, + "name": { + "name": "Name" + } + } + }, + "calendar_delete_event": { + "fields": { + "duration": { + "name": "Dauer" + }, + "summary": { + "name": "Zusammenfassung" + }, + "start_date_time": { + "description": "Wo soll die Suche beginnen? Standardmäßig ab dem aktuellen Zeitpunkt." + }, + "end_date_time": { + "description": "Wo soll die Suche enden? Gib dies oder eine Dauer an." + } + } + }, + "homeassistant_set_entity_icon": { + "fields": { + "entity_id": { + "name": "Entität" + }, + "icon": { + "name": "Symbol" + } + } + }, + "input_select_delete": { + "fields": { + "entity_id": { + "name": "Entitäts-ID" + } + } + }, + "input_boolean_delete": { + "fields": { + "entity_id": { + "name": "Entitäts-ID" + } + } + }, + "input_text_delete": { + "fields": { + "entity_id": { + "name": "Entitäts-ID" + } + } + }, + "input_button_delete": { + "fields": { + "entity_id": { + "name": "Entitäts-ID" + } + } + }, + "input_datetime_delete": { + "fields": { + "entity_id": { + "name": "Entitäts-ID" + } + } + }, + "timer_delete": { + "fields": { + "entity_id": { + "name": "Entitäts-ID" + } + } + }, + "input_datetime_create": { + "fields": { + "has_date": { + "name": "Hat ein Datum", + "description": "Ob ein Datum angegeben ist. Entweder das Datum oder die Uhrzeit muss angegeben sein." + }, + "has_time": { + "name": "Hat eine Uhrzeit", + "description": "Ob eine Uhrzeit gespeichert ist. Entweder das Datum oder die Uhrzeit muss mindestens angegeben sein." + }, + "icon": { + "name": "Symbol" + }, + "initial": { + "name": "Startwert" + }, + "name": { + "name": "Name" + } + } + }, + "homeassistant_create_category": { + "fields": { + "icon": { + "name": "Symbol" + }, + "name": { + "name": "Name" + } + }, + "name": "Kategorie anlegen" + }, + "input_select_create": { + "fields": { + "icon": { + "name": "Symbol" + }, + "name": { + "name": "Name" + } + } + }, + "counter_create": { + "fields": { + "icon": { + "name": "Symbol" + }, + "initial": { + "name": "Startwert" + }, + "maximum": { + "name": "Maximum" + }, + "minimum": { + "name": "Minimum" + }, + "name": { + "name": "Name" + }, + "step": { + "name": "Schritt" + } + }, + "name": "Zähler anlegen" + }, + "input_boolean_create": { + "fields": { + "icon": { + "name": "Symbol" + }, + "initial": { + "name": "Startwert" + }, + "name": { + "name": "Name" + } + } + }, + "input_text_create": { + "fields": { + "icon": { + "name": "Symbol" + }, + "initial": { + "name": "Startwert" + }, + "max": { + "name": "Maximale Länge" + }, + "min": { + "name": "Minimale Länge" + }, + "mode": { + "name": "Modus" + }, + "name": { + "name": "Name" + }, + "pattern": { + "description": "Ein regulärer Ausdruck, dem der Text entsprechen muss." + } + } + }, + "input_button_create": { + "fields": { + "icon": { + "name": "Symbol" + }, + "name": { + "name": "Name" + } + } + }, + "repairs_list": { + "fields": { + "severity": { + "name": "Schweregrad" + }, + "include_ignored": { + "description": "Liste auch die Probleme auf, die bisher außer Acht gelassen wurden." + } + } + }, + "climate_increase_temperature": { + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "water_heater_increase_temperature": { + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "humidifier_increase_humidity": { + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "cover_increase_position": { + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "valve_increase_position": { + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "media_player_increase_volume": { + "fields": { + "step": { + "name": "Schritt" + } + } + }, + "todo_move_item": { + "fields": { + "after": { + "name": "Danach" + } + } } }, "conditions": { @@ -1934,6 +2373,10 @@ "device_discovered": { "name": "Gerät entdeckt", "description": "Wird ausgelöst, wenn Home Assistant etwas Neues entdeckt, das es einrichten könnte, etwa ein Gerät, das gerade in deinem Netzwerk aufgetaucht ist." + }, + "update_installed": { + "name": "Update installiert", + "description": "Wird ausgelöst, wenn eine Update-Entität eine andere installierte Version meldet – unabhängig davon, ob jemand auf „Installieren“ geklickt hat, das Gerät sich selbst aktualisiert hat oder die Aktualisierung außerhalb von Home Assistant erfolgte." } }, "selector": { @@ -1943,6 +2386,12 @@ "error": "Fehler", "warning": "Warnung" } + }, + "todo_move_position": { + "options": { + "bottom": "Ende", + "top": "Anfang" + } } } } diff --git a/custom_components/spook/translations/en.json b/custom_components/spook/translations/en.json index f56d885e..eb4592be 100644 --- a/custom_components/spook/translations/en.json +++ b/custom_components/spook/translations/en.json @@ -1,5 +1,9 @@ { "triggers": { + "automation_turned_off": { + "name": "Automation turned off", + "description": "Fires when somebody, or something, turns an automation off. Leave the target empty to watch every automation." + }, "debounce": { "name": "Once it settles", "description": "Fires once a trigger has stopped firing for a while, so a burst of them arrives as one. A motion sensor does not report motion once, it reports it twenty times.", @@ -182,6 +186,34 @@ } } }, + "target_temperature_reached": { + "name": "Target temperature reached", + "description": "Fires when the temperature of a thermostat or water heater reaches its target, or jumps past it. Changing the target does not count, and neither does a device that is off.", + "fields": { + "tolerance": { + "name": "Tolerance", + "description": "How close to the target still counts as there, in the device's unit. For a thermostat that settles just short of its setpoint. Zero means the target itself." + } + } + }, + "update_installed": { + "name": "Update installed", + "description": "Fires when an update entity reports a different installed version, whether somebody pressed install, the device updated itself, or it was updated outside of Home Assistant." + }, + "position_reached": { + "name": "Position reached", + "description": "Fires when a cover or valve gets to a position, or moves past it. One that is already there, or comes back from unavailable at it, does not count.", + "fields": { + "position": { + "name": "Position", + "description": "The position to reach, from 0 (closed) to 100 (open)." + } + } + }, + "script_started": { + "name": "Script started", + "description": "Fires when a script starts a run. A run the script refuses, because it is already running, does not count. Leave the target empty to watch every script." + }, "user_added": { "name": "User added", "description": "Fires when somebody is given a login to Home Assistant. Users Home Assistant creates for its own integrations are left out." @@ -203,6 +235,20 @@ } }, "conditions": { + "is_at_target_temperature": { + "name": "Temperature at its target", + "description": "Passes when the temperature of a thermostat or water heater is at its target. A device that is off is not at its target, and one that is unavailable is left out.", + "fields": { + "behavior": { + "name": "Behavior", + "description": "Whether any of the devices being there is enough, or all of them have to be." + }, + "tolerance": { + "name": "Tolerance", + "description": "How close to the target still counts as there, in the device's unit. For a thermostat that settles just short of its setpoint. Zero means the target itself." + } + } + }, "repair_issue_present": { "name": "Repair issue outstanding", "description": "Passes while a repair issue is outstanding. Issues somebody has ignored do not count.", @@ -1309,21 +1355,29 @@ }, "homeassistant_disable_config_entry": { "name": "Disable an integration", - "description": "Disables an integration entry.", + "description": "Disables integration entries, by entry or every entry of an integration.", "fields": { "config_entry_id": { "name": "Integration entry", "description": "The integration entry to disable." + }, + "domain": { + "name": "Integration", + "description": "Disable every entry of these integrations, the ones added later included." } } }, "homeassistant_enable_config_entry": { "name": "Enable an integration", - "description": "Enables an integration entry.", + "description": "Enables integration entries, by entry or every entry of an integration.", "fields": { "config_entry_id": { "name": "Integration entry", "description": "The integration entry to enable." + }, + "domain": { + "name": "Integration", + "description": "Enable every entry of these integrations, the ones added later included." } } }, @@ -1983,6 +2037,10 @@ } } }, + "input_select_reverse": { + "name": "Reverse options", + "description": "Reverses the order of the selectable options for an `input_select` entity, back to front. This is not persistent and will be undone once reloaded or Home Assistant restarts." + }, "input_select_shuffle": { "name": "Shuffle options", "description": "Shuffles the list of selectable options for an `input_select` entity. This is not persistent and will be undone once reloaded or Home Assistant restarts." @@ -2055,6 +2113,24 @@ "name": "Set minimum value", "description": "Set an input number entity to its minimum value." }, + "input_number_set_range": { + "name": "Set range", + "description": "Changes the minimum, maximum or step of an input number made in the UI, the same as editing it. A value outside the new range is moved inside it.", + "fields": { + "min": { + "name": "Minimum", + "description": "The new minimum value." + }, + "max": { + "name": "Maximum", + "description": "The new maximum value." + }, + "step": { + "name": "Step", + "description": "The new step size." + } + } + }, "counter_create": { "name": "Create a counter", "description": "Creates a new counter helper on the fly. Returns the entity ID of the new counter.", @@ -2367,7 +2443,13 @@ }, "repairs_ignore_all": { "name": "Ignore all issues", - "description": "Ignore all issues currently raised in Home Assistant Repairs." + "description": "Ignore all issues currently raised in Home Assistant Repairs, or only those raised by particular integrations.", + "fields": { + "domain": { + "name": "Domain", + "description": "Only ignore the issues raised by these integrations." + } + } }, "repairs_list": { "name": "List issues", @@ -2399,7 +2481,13 @@ }, "repairs_unignore_all": { "name": "Unignore all issues", - "description": "Unignore all issues currently raised in Home Assistant Repairs." + "description": "Unignore all issues currently raised in Home Assistant Repairs, or only those raised by particular integrations.", + "fields": { + "domain": { + "name": "Domain", + "description": "Only unignore the issues raised by these integrations." + } + } }, "sensor_set_display_precision": { "name": "Set display precision", @@ -2484,6 +2572,10 @@ "radius": { "name": "Radius", "description": "Radius of the zone" + }, + "passive": { + "name": "Passive", + "description": "A passive zone is only there for automations: it is not shown on the map, and does not change where people and device trackers are." } } }, @@ -2514,6 +2606,10 @@ "radius": { "name": "Radius", "description": "Radius of the zone" + }, + "passive": { + "name": "Passive", + "description": "A passive zone is only there for automations: it is not shown on the map, and does not change where people and device trackers are." } } }, diff --git a/custom_components/spook/translations/es.json b/custom_components/spook/translations/es.json index b75a58d6..d76ff84e 100644 --- a/custom_components/spook/translations/es.json +++ b/custom_components/spook/translations/es.json @@ -271,7 +271,8 @@ "abort": { "issue_ignored": "Spook no volverá a decir nada sobre este grupo.", "manage": "Puedes gestionar este grupo tú mismo en la [página de Asistentes](/config/helpers).", - "not_editable": "Este grupo está configurado en YAML, así que Spook no puede editarlo. Actualiza el grupo en tu configuración." + "not_editable": "Este grupo está configurado en YAML, así que Spook no puede editarlo. Actualiza el grupo en tu configuración.", + "changed": "Los miembros que figuraban en esta reparación han vuelto, o han cambiado, desde que Spook la revisó, así que no se ha quitado nada del grupo. Spook vuelve a comprobarlo por su cuenta y actualiza esta reparación." }, "step": { "init": { @@ -372,7 +373,8 @@ "fix_flow": { "abort": { "issue_ignored": "Spook no volverá a decir nada sobre esta área.", - "manage": "Puedes gestionar esta área tú mismo en la [página de Áreas](/config/areas/dashboard)." + "manage": "Puedes gestionar esta área tú mismo en la [página de Áreas](/config/areas/dashboard).", + "changed": "Esta área vuelve a estar en uso desde que Spook la revisó: contiene algo, o una automatización o un script la utiliza. Por eso se ha dejado en paz. Spook vuelve a comprobarlo por su cuenta y actualiza esta reparación." }, "step": { "init": { @@ -392,7 +394,8 @@ "fix_flow": { "abort": { "issue_ignored": "Spook no volverá a decir nada sobre esta planta.", - "manage": "Puedes gestionar esta planta tú mismo en la [página de Áreas](/config/areas/dashboard)." + "manage": "Puedes gestionar esta planta tú mismo en la [página de Áreas](/config/areas/dashboard).", + "changed": "Esta planta vuelve a estar en uso desde que Spook la revisó: tiene un área, o una automatización o un script la utiliza. Por eso se ha dejado en paz. Spook vuelve a comprobarlo por su cuenta y actualiza esta reparación." }, "step": { "init": { @@ -425,13 +428,14 @@ "abort": { "issue_ignored": "Spook no volverá a decir nada sobre este asistente.", "manage": "Puedes gestionar este asistente tú mismo en la [página de Asistentes](/config/helpers).", - "too_few_members": "Un asistente mín/máx necesita al menos dos miembros, así que quitar los que faltan dejaría muy pocos. Reconfigura o quita el asistente tú mismo en la [página de Asistentes](/config/helpers)." + "too_few_members": "Un asistente mín/máx necesita al menos dos miembros, así que quitar los que faltan dejaría muy pocos. Reconfigura o quita el asistente tú mismo en la [página de Asistentes](/config/helpers).", + "changed": "Los miembros que figuraban en esta reparación han vuelto, o han cambiado, desde que Spook la revisó, así que no se ha quitado nada del helper. Spook vuelve a comprobarlo por su cuenta y actualiza esta reparación." }, "step": { "init": { "description": "Spook ha encontrado un fantasma en tus asistentes 👻\n\nEl asistente mín/máx {helper} (`{entity_id}`) combina los siguientes miembros, que son desconocidos para Home Assistant:\n\n{sources}\n\nQuítalos para que el asistente siga con los miembros que quedan, o quédatelos y Spook no volverá a mencionarlos.\n\nSpook 👻 Tu colega.", "menu_options": { - "ignore": "Quedárselo y no volver a avisarme", + "ignore": "Conservarlas, no me avises más", "manage": "Lo arreglo yo mismo", "remove": "Quitar estos miembros" }, @@ -442,7 +446,20 @@ "title": "Miembros desconocidos usados en el asistente mín/máx: {helper}" }, "orphaned_statistics": { - "title": "Se han encontrado estadísticas a largo plazo huérfanas" + "title": "Se han encontrado estadísticas a largo plazo huérfanas", + "fix_flow": { + "step": { + "init": { + "title": "Se han encontrado estadísticas a largo plazo huérfanas", + "description": "Hay un fantasma en tu recorder 👻\n\nHome Assistant está guardando estadísticas a largo plazo de lo siguiente, pero ya no existe ninguna entidad que coincida:\n\n{statistics}\n\nEsto suele pasar cuando se eliminó un sensor pero se conservaron sus estadísticas. Borrarlas tira ese historial a la basura para siempre, así que Spook vuelve a comprobarlo antes de hacerlo y deja en paz todo lo que haya vuelto mientras tanto.\n\nSpook 👻 Tu colega.", + "menu_options": { + "remove": "Borrar estas estadísticas", + "manage": "Lo hago yo mismo", + "ignore": "Conservarlas, no me avises más" + } + } + } + } }, "person_unknown_device_trackers": { "fix_flow": { @@ -455,7 +472,7 @@ "init": { "description": "Spook ha encontrado un fantasma en tus personas 👻\n\n{person} sigue los siguientes rastreadores de dispositivo, que son desconocidos para Home Assistant:\n\n{device_trackers}\n\nEsto suele pasar cuando se ha renombrado un rastreador de dispositivo o se ha quitado su integración.\n\nQuítalos de {person}, o quédatelos y Spook no volverá a mencionarlos.\n\nSpook 👻 Tu colega.", "menu_options": { - "ignore": "Quedárselo y no volver a avisarme", + "ignore": "Conservarlas, no me avises más", "manage": "Lo arreglo yo mismo", "remove": "Quitar estos rastreadores de dispositivo" }, @@ -1936,6 +1953,12 @@ "error": "Error", "warning": "Advertencia" } + }, + "todo_move_position": { + "options": { + "bottom": "Abajo", + "top": "Arriba" + } } } } diff --git a/custom_components/spook/translations/nl.json b/custom_components/spook/translations/nl.json index 7b284847..df418ed8 100644 --- a/custom_components/spook/translations/nl.json +++ b/custom_components/spook/translations/nl.json @@ -2151,7 +2151,202 @@ } }, "calendar_delete_event": { - "name": "Afspraak verwijderen" + "name": "Afspraak verwijderen", + "description": "Verwijdert gebeurtenissen uit een agenda op basis van hun titel of uid binnen een bepaald tijdsbestek. Geeft de verwijderde gebeurtenissen terug, inclusief hun uid.", + "fields": { + "duration": { + "name": "Duur", + "description": "Hoe ver moet er vanaf het begin worden gekeken? Geef dit op, of een einddatum en -tijdstip." + }, + "end_date_time": { + "name": "Einddatum en -tijd" + }, + "whole_series": { + "description": "Bij een terugkerende gebeurtenis, verwijder de volledige reeks, ipv enkel de gebeurtenis die gevonden werd." + }, + "start_date_time": { + "name": "Startdatum en -tijd" + }, + "summary": { + "name": "Samenvatting", + "description": "De titel van de gebeurtenissen die moeten worden verwijderd, precies zoals die in de kalender staat." + }, + "uid": { + "description": "De identificatiecode van de gebeurtenis die je wilt verwijderen, voor het geval je die hebt." + } + } + }, + "todo_move_item": { + "fields": { + "position": { + "description": "Plaats het boven- of onderaan de lijst. Vermeld dit, of een ander item om het achter te plaatsen.", + "name": "Positie" + }, + "after": { + "name": "Na" + }, + "item": { + "name": "Item", + "description": "De titel of uid van het item dat verplaatst moet worden." + } + }, + "description": "Plaatst een item in een to-do lijst bovenaan, onderaan of achter een ander item.", + "name": "Verplaats een item" + }, + "cover_decrease_position": { + "description": "Beweegt de verduistering een stap verder gesloten, stopt wanneer volledig dicht.", + "name": "Verlaag positie", + "fields": { + "step": { + "description": "Met hoeveel procent moet het worden aangepast? Tien procent wanneer niet ingevuld.", + "name": "Stap" + } + } + }, + "cover_increase_position": { + "description": "Beweegt een verduistering een stap verder open, stoppend wanneer volledig open.", + "fields": { + "step": { + "description": "Met hoeveel procent moet het worden aangepast? Tien procent wanneer niet ingevuld.", + "name": "Stap" + } + }, + "name": "Verhoog positie" + }, + "valve_decrease_position": { + "description": "Draait een klep een stap verder dicht, stoppend wanneer volledig dicht.", + "name": "Verlaag positie", + "fields": { + "step": { + "description": "Met hoeveel procent moet het worden aangepast? Tien procent wanneer niet ingevuld.", + "name": "Stap" + } + } + }, + "valve_increase_position": { + "description": "Draait een klep verder open, stoppend wanneer volledig open.", + "fields": { + "step": { + "description": "Met hoeveel procent moet het worden aangepast? Tien procent wanneer niet ingevuld.", + "name": "Stap" + } + }, + "name": "Verhoog positie" + }, + "calendar_update_event": { + "fields": { + "new_summary": { + "name": "Nieuwe samenvatting" + }, + "description": { + "name": "Omschrijving", + "description": "De omschrijving voor de gebeurtenissen" + }, + "duration": { + "name": "Duur", + "description": "Hoe ver moet er vanaf het begin worden gekeken? Geef dit op, of een einddatum en -tijdstip." + }, + "end_date_time": { + "name": "Enddatum en -tijd" + }, + "location": { + "name": "Locatie", + "description": "De locatie voor de gebeurtenissen" + }, + "shift": { + "description": "Verschuif de gebeurtenissen met deze tijd, later of – bij een negatieve waarde – eerder. In plaats van een nieuw begin- of eindtijdstip.", + "name": "Verschuif" + }, + "new_end": { + "name": "Nieuw einde" + }, + "new_start": { + "name": "Nieuwe start" + }, + "start_date_time": { + "name": "Startdatum en -tijd" + }, + "summary": { + "name": "Samenvatting", + "description": "De titel van de gebeurtenissen die moeten worden aangepast, precies zoals die in de kalender staat." + }, + "uid": { + "description": "De identificatiecode van de gebeurtenis die je wilt wijzigen, voor het geval je die hebt." + } + }, + "description": "Wijzigt gebeurtenissen in een kalender, die op basis van hun titel of uid binnen een bepaald tijdsbestek worden gevonden. Geeft de gewijzigde gegevens terug, inclusief de uid." + }, + "humidifier_decrease_humidity": { + "name": "Verlaag vochtigheid", + "fields": { + "step": { + "description": "Met hoeveel procent moet de waarde worden aangepast? Laat dit veld leeg om de standaardinstelling van de luchtbevochtiger te gebruiken, of vul 1 procent in als er geen standaardinstelling is.", + "name": "Stap" + } + } + }, + "climate_decrease_temperature": { + "name": "Verlaag temperatuur", + "fields": { + "step": { + "description": "Met hoeveel moet je het aanpassen? Laat leegf om de eigen instelling van de thermostaat te gebruiken, of stel het in op een halve graad (één in Fahrenheit) als er geen instelling is.", + "name": "Stap" + } + } + }, + "water_heater_decrease_temperature": { + "name": "Verlaag temperatuur", + "fields": { + "step": { + "description": "Met hoeveel moet je het aanpassen? Laat leegf om de eigen instelling van de ketel te gebruiken, of stel het in op een halve graad (één in Fahrenheit) als er geen instelling is.", + "name": "Stap" + } + } + }, + "media_player_decrease_volume": { + "name": "Verlaag volume", + "fields": { + "step": { + "description": "Met hoeveel procent moet het volume worden aangepast?", + "name": "Stap" + } + } + }, + "humidifier_increase_humidity": { + "fields": { + "step": { + "description": "Met hoeveel procent moet de waarde worden aangepast? Laat dit veld leeg om de standaardinstelling van de luchtbevochtiger te gebruiken, of vul 1 procent in als er geen standaardinstelling is.", + "name": "Stap" + } + }, + "name": "Verhoog vochtigheid" + }, + "media_player_increase_volume": { + "fields": { + "step": { + "description": "Met hoeveel procent moet het volume worden aangepast?", + "name": "Stap" + } + }, + "name": "Verhoog volume" + }, + "climate_increase_temperature": { + "fields": { + "step": { + "description": "Met hoeveel moet je het aanpassen? Laat leegf om de eigen instelling van de thermostaat te gebruiken, of stel het in op een halve graad (één in Fahrenheit) als er geen instelling is.", + "name": "Stap" + } + }, + "name": "Verhoog temperatuur" + }, + "water_heater_increase_temperature": { + "fields": { + "step": { + "description": "Met hoeveel moet je het aanpassen? Laat leegf om de eigen instelling van de ketel te gebruiken, of stel het in op een halve graad (één in Fahrenheit) als er geen instelling is.", + "name": "Stap" + } + }, + "name": "Verhoog temperatuur" } }, "conditions": { diff --git a/custom_components/spook/translations/sr-Latn.json b/custom_components/spook/translations/sr-Latn.json index 1273ba4e..b4d1aba3 100644 --- a/custom_components/spook/translations/sr-Latn.json +++ b/custom_components/spook/translations/sr-Latn.json @@ -2229,6 +2229,144 @@ "name": "UID" } } + }, + "climate_increase_temperature": { + "name": "Povećaj temperaturu", + "description": "Povećava zadatu temperaturu termostata za jedan korak, odnosno obe zadate vrednosti ako termostat ima režim i za grejanje i za hlađenje. Termostat koji je isključen ostaje isključen.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko promeniti temperaturu. Izostavite ovaj parametar da biste koristili podrazumevani korak samog termostata, ili pola stepena (jedan stepen u Farenhajtiem) kada termostat nema definisan sopstveni korak." + } + } + }, + "climate_decrease_temperature": { + "name": "Smanji temperaturu", + "description": "Smanjuje zadatu temperaturu termostata za jedan korak, odnosno obe zadate vrednosti ako termostat ima režim i za grejanje i za hlađenje. Termostat koji je isključen ostaje isključen.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko promeniti temperaturu. Izostavite ovaj parametar da biste koristili podrazumevani korak samog termostata, ili pola stepena (jedan stepen u Farenhajtiem) kada termostat nema definisan sopstveni korak." + } + } + }, + "water_heater_increase_temperature": { + "name": "Povećaj temperaturu", + "description": "Povećava zadatu temperaturu bojlera za jedan korak, u okviru njegovih ograničenja.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko promeniti temperaturu. Izostavite ovaj parametar da biste koristili podrazumevani korak samog bojlera, ili pola stepena (jedan stepen u Farenhajtima) kada bojler nema definisan sopstveni korak." + } + } + }, + "water_heater_decrease_temperature": { + "name": "Smanji temperaturu", + "description": "Smanjuje zadatu temperaturu bojlera za jedan korak, u okviru njegovih ograničenja.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko promeniti temperaturu. Izostavite ovaj parametar da biste koristili podrazumevani korak samog bojlera, ili pola stepena (jedan stepen u Farenhajtima) kada bojler nema definisan sopstveni korak." + } + } + }, + "humidifier_increase_humidity": { + "name": "Povećaj vlažnost", + "description": "Povećava ciljanu vlažnost vazduha na ovlaživaču za jedan korak, u okviru njegovih ograničenja.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko procenata promeniti vlažnost. Izostavite ovaj parametar da biste koristili podrazumevani korak samog ovlaživača, ili jedan procenat kada uređaj nema definisan sopstveni korak." + } + } + }, + "humidifier_decrease_humidity": { + "name": "Smanji vlažnost", + "description": "Smanjuje ciljanu vlažnost vazduha na ovlaživaču za jedan korak, u okviru njegovih ograničenja.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko procenata promeniti vlažnost. Izostavite ovaj parametar da biste koristili podrazumevani korak samog ovlaživača, ili jedan procenat kada uređaj nema definisan sopstveni korak." + } + } + }, + "cover_increase_position": { + "name": "Povećaj otvorenost", + "description": "Otvara roletnu za jedan korak, zaustavljajući se kada se potpuno otvori.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko procenata je pomeriti. Ukoliko se izostavi, pomera se za deset procenata." + } + } + }, + "cover_decrease_position": { + "name": "Smanji otvorenost", + "description": "Zatvara roletnu za jedan korak, zaustavljajući se kada se potpuno zatvori.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko procenata je pomeriti. Ukoliko se izostavi, pomera se za deset procenata." + } + } + }, + "valve_increase_position": { + "name": "Povećaj otvorenost", + "description": "Otvara ventil za jedan korak, zaustavljajući se kada se potpuno otvori.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko procenata ga pomeriti. Ukoliko se izostavi, pomera se za deset procenata." + } + } + }, + "valve_decrease_position": { + "name": "Smanji otvorenost", + "description": "Zatvara ventil za jedan korak, zaustavljajući se kada se potpuno zatvori.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko procenata ga pomeriti. Ukoliko se izostavi, pomera se za deset procenata." + } + } + }, + "media_player_increase_volume": { + "name": "Pojačaj zvuk", + "description": "Pojačava zvuk medijskog plejera za uneti korak, zaustavljajući se na maksimalnoj jačini zvuka.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko procenata promeniti jačinu zvuka." + } + } + }, + "media_player_decrease_volume": { + "name": "Smanji zvuk", + "description": "Smanjuje zvuk medijskog plejera za uneti korak, zaustavljajući se kada se potpuno utiša.", + "fields": { + "step": { + "name": "Korak", + "description": "Za koliko procenata promeniti jačinu zvuka." + } + } + }, + "todo_move_item": { + "name": "Premesti stavku", + "description": "Premešta stavku na listi zadataka na vrh, na dno ili nakon neke druge stavke.", + "fields": { + "item": { + "name": "Stavka", + "description": "Naslov ili UID stavke koju treba premestiti." + }, + "after": { + "name": "Posle", + "description": "Naslov ili UID stavke nakon koje je treba staviti. Navedite ovo ili poziciju." + }, + "position": { + "name": "Pozicija", + "description": "Premestite je na vrh ili na dno liste. Navedite ovo ili stavku nakon koje je treba staviti." + } + } } }, "conditions": { @@ -2491,6 +2629,10 @@ "device_discovered": { "name": "Uređaj je otkriven", "description": "Aktivira se kada Home Assistant otkrije nešto novo što bi mogao da podesi, poput uređaja koji se upravo pojavio na vašoj mreži." + }, + "update_installed": { + "name": "Ažuriranje instalirano", + "description": "Aktivira se kada entitet ažuriranja prijavi drugačiju instaliranu verziju, bez obzira na to da li je neko pritisnuo dugme za instalaciju, da li se uređaj sam ažurirao ili je ažuriran van Home Assistant-a." } }, "selector": { @@ -2500,6 +2642,12 @@ "error": "Greška", "warning": "Upozorenje" } + }, + "todo_move_position": { + "options": { + "bottom": "Dole", + "top": "Gore" + } } } } diff --git a/custom_components/spook/translations/sr.json b/custom_components/spook/translations/sr.json index a7173f25..3fbb3351 100644 --- a/custom_components/spook/translations/sr.json +++ b/custom_components/spook/translations/sr.json @@ -2229,6 +2229,144 @@ "description": "Померите догађаје за оволико (касније, или раније ако је вредност негативна), уместо уноса новог почетка или завршетка." } } + }, + "todo_move_item": { + "fields": { + "position": { + "description": "Преместите је на врх или на дно листе. Наведите ово или ставку након које је треба ставати.", + "name": "Позиција" + }, + "after": { + "description": "Наслов или „UID” ставке након које је треба ставити. Наведите ово или позицију.", + "name": "После" + }, + "item": { + "description": "Наслов или „UID” ставке коју треба преместити.", + "name": "Ставка" + } + }, + "description": "Премешта ставку на листи задатака на врх, на дно или након неке друге ставке.", + "name": "Премести ставку" + }, + "media_player_decrease_volume": { + "fields": { + "step": { + "description": "За колико процената променити јачину звука.", + "name": "Корак" + } + }, + "name": "Смањи звук", + "description": "Смањује звук медијског плејера за унети корак, заустављајући се када се потпуно утиша." + }, + "climate_increase_temperature": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико променити температуру. Изоставите овај параметар да бисте користили подразумевани корак самог термостата, или пола степена (један степен у Фаренхајтима) када термостат нема дефинисан сопствени корак." + } + }, + "name": "Повећај температуру", + "description": "Повећава задату температуру термостата за један корак, односно обе задате вредности ако термостат има режим и за грејање и за хлађење. Термостат који је искључен остаје искључен." + }, + "climate_decrease_temperature": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико променити температуру. Изоставите овај параметар да бисте користили подразумевани корак самог термостата, или пола степена (један степен у Фаренхајтима) када термостат нема дефинисан сопствени корак." + } + }, + "name": "Смањи температуру", + "description": "Смањује задату температуру термостата за један корак, односно обе задате вредности ако термостат има режим и за грејање и за хлађење. Термостат који је искључен остаје искључен." + }, + "water_heater_increase_temperature": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико променити температуру. Изоставите овај параметар да бисте користили подразумевани корак самог бојлера, или пола степена (један степен у Фаренхајтима) када бојлер нема дефинисан сопствени корак." + } + }, + "name": "Повећај температуру", + "description": "Повећава задату температуру бојлера за један корак, у оквиру његових ограничења." + }, + "water_heater_decrease_temperature": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико променити температуру. Изоставите овај параметар да бисте користили подразумевани корак самог бојлера, или пола степена (један степен у Фаренхајтима) када бојлер нема дефинисан сопствени корак." + } + }, + "name": "Смањи температуру", + "description": "Смањује задату температуру бојлера за један корак, у оквиру његових ограничења." + }, + "humidifier_increase_humidity": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико процената променити влажност. Изоставите овај параметар да бисте користили подразумевани корак самог овлаживача, или један проценат када уређај нема дефинисан сопствени корак." + } + }, + "name": "Повећај влажност", + "description": "Повећава циљану влажност ваздуха на овлаживачу за један корак, у оквиру његових ограничења." + }, + "humidifier_decrease_humidity": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико процената променити влажност. Изоставите овај параметар да бисте користили подразумевани корак самог овлаживача, или један проценат када уређај нема дефинисан сопствени корак." + } + }, + "name": "Смањи влажност", + "description": "Смањује циљану влажност ваздуха на овлаживачу за један корак, у оквиру његових ограничења." + }, + "cover_increase_position": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико процената је померити. Уколико се изостави, помера се за десет процената." + } + }, + "name": "Повећај отвореност", + "description": "Отвара ролетну за један корак, заустављајући се када се потпуно отвори." + }, + "cover_decrease_position": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико процената је померити. Уколико се изостави, помера се за десет процената." + } + }, + "name": "Смањи отвореност", + "description": "Затвара ролетну за један корак, заустављајући се када се потпуно затвори." + }, + "valve_increase_position": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико процената га померити. Уколико се изостави, помера се за десет процената." + } + }, + "name": "Повећај отвореност", + "description": "Отвара вентил за један корак, заустављајући се када се потпуно отвори." + }, + "valve_decrease_position": { + "fields": { + "step": { + "name": "Корак", + "description": "За колико процената га померити. Уколико се изостави, помера се за десет процената." + } + }, + "name": "Смањи отвореност", + "description": "Затвара вентил за један корак, заустављајући се када се потпуно затвори." + }, + "media_player_increase_volume": { + "name": "Појачај звук", + "description": "Појачава звук медијског плејера за унети корак, заустављајући се на максималној јачини звука.", + "fields": { + "step": { + "name": "Корак", + "description": "За колико процената променити јачину звука." + } + } } }, "triggers": { @@ -2417,6 +2555,10 @@ "device_discovered": { "name": "Уређај је откривен", "description": "Активира се када „Home Assistant“ открије нешто ново што би могао да подеси, попут уређаја који се управо појавио на вашој мрежи." + }, + "update_installed": { + "name": "Ажурирање инсталирано", + "description": "Активира се када ентитет ажурирања пријави другачију инсталирану верзију, без обзира на то да ли је неко притиснуо дугме за инсталацију, да ли се уређај сам ажурирао или је ажуриран ван Home Assistant-а." } }, "conditions": { @@ -2500,6 +2642,12 @@ "error": "Грешка", "warning": "Упозорење" } + }, + "todo_move_position": { + "options": { + "bottom": "Доле", + "top": "Горе" + } } } } diff --git a/custom_components/spook/triggers.yaml b/custom_components/spook/triggers.yaml index 03a5177c..c4d7001c 100644 --- a/custom_components/spook/triggers.yaml +++ b/custom_components/spook/triggers.yaml @@ -17,6 +17,51 @@ stale: duration: device_added: +position_reached: + target: + entity: + - domain: cover + - domain: valve + fields: + position: + required: true + example: 30 + selector: + number: + min: 0 + max: 100 + step: 1 + unit_of_measurement: "%" + mode: slider +script_started: + target: + entity: + domain: script +target_temperature_reached: + target: + entity: + - domain: climate + - domain: water_heater + fields: + tolerance: + required: false + default: 0 + example: 0.5 + selector: + number: + min: 0 + max: 10 + step: 0.1 + mode: box +update_installed: + target: + entity: + domain: update + primary_entities_only: false +automation_turned_off: + target: + entity: + domain: automation recovered: target: fields: diff --git a/www/community/sensor-bar-card-plus/sensor-bar-card-plus.js b/www/community/sensor-bar-card-plus/sensor-bar-card-plus.js index b4340686..a8a2a723 100644 --- a/www/community/sensor-bar-card-plus/sensor-bar-card-plus.js +++ b/www/community/sensor-bar-card-plus/sensor-bar-card-plus.js @@ -16,6 +16,122 @@ } }; + // src/utils/extrema.js + function getLocalBoundaryTimestamp(date, unit) { + if (!(date instanceof Date) || Number.isNaN(date.getTime())) return null; + const year = date.getFullYear(); + const month = date.getMonth(); + const day = date.getDate(); + const hour = date.getHours(); + if (unit === "quarterly") { + return new Date(year, month, day, hour, Math.floor(date.getMinutes() / 15) * 15, 0, 0).getTime(); + } + if (unit === "hourly") { + return new Date(year, month, day, hour, 0, 0, 0).getTime(); + } + if (unit === "daily") { + return new Date(year, month, day, 0, 0, 0, 0).getTime(); + } + if (unit === "weekly") { + const daysSinceMonday = (date.getDay() + 6) % 7; + return new Date(year, month, day - daysSinceMonday, 0, 0, 0, 0).getTime(); + } + if (unit === "monthly") { + return new Date(year, month, 1, 0, 0, 0, 0).getTime(); + } + if (unit === "yearly") { + return new Date(year, 0, 1, 0, 0, 0, 0).getTime(); + } + return null; + } + function normalizeReset(value) { + if (value === void 0) value = "never"; + if (typeof value !== "string") return { kind: "never" }; + const normalized = value.trim().toLowerCase(); + if (normalized === "never") return { kind: "never" }; + if (CALENDAR_RESETS.has(normalized)) { + return { kind: "calendar", unit: normalized }; + } + const durationMatch = normalized.match(/^(\d+)(m|h)$/); + if (!durationMatch) return { kind: "never" }; + const amount = Number(durationMatch[1]); + const unit = durationMatch[2]; + if (!Number.isInteger(amount) || amount < 1 || unit === "m" && amount > 59 || unit === "h" && amount > 23) { + return { kind: "never" }; + } + return unit === "m" ? { kind: "duration", minutes: amount } : { kind: "duration", hours: amount }; + } + function isValidReset(value) { + if (typeof value !== "string") return false; + const normalized = value.trim().toLowerCase(); + if (normalized === "never" || CALENDAR_RESETS.has(normalized)) return true; + const match = normalized.match(/^(\d+)(m|h)$/); + if (!match) return false; + const amount = Number(match[1]); + return Number.isInteger(amount) && amount >= 1 && (match[2] === "m" ? amount <= 59 : amount <= 23); + } + function getResetWindowKey(reset, timestamp) { + if ((reset == null ? void 0 : reset.kind) !== "calendar") return null; + const boundary = getLocalBoundaryTimestamp(new Date(timestamp), reset.unit); + return Number.isFinite(boundary) ? String(boundary) : null; + } + function getDurationMs(reset) { + if ((reset == null ? void 0 : reset.kind) !== "duration") return null; + if (Number.isInteger(reset.minutes)) return reset.minutes * 60 * 1e3; + if (Number.isInteger(reset.hours)) return reset.hours * 60 * 60 * 1e3; + return null; + } + function initializeExtremum(sample, reset, timestamp) { + if ((reset == null ? void 0 : reset.kind) === "duration") { + return { + value: sample, + startedAtMs: Number.isFinite(timestamp) ? timestamp : null, + windowKey: null + }; + } + if ((reset == null ? void 0 : reset.kind) === "calendar") { + return { + value: sample, + startedAtMs: null, + windowKey: getResetWindowKey(reset, timestamp) + }; + } + return { + value: sample, + startedAtMs: null, + windowKey: null + }; + } + function updateExtremum(previous, sample, reset, direction, timestamp) { + if (!Number.isFinite(sample)) return previous != null ? previous : null; + const durationMs = getDurationMs(reset); + if (!previous) return initializeExtremum(sample, reset, timestamp); + if (durationMs !== null && Number.isFinite(previous.startedAtMs) && Number.isFinite(timestamp) && timestamp - previous.startedAtMs >= durationMs) { + return initializeExtremum(sample, reset, timestamp); + } + if ((reset == null ? void 0 : reset.kind) === "calendar") { + const windowKey = getResetWindowKey(reset, timestamp); + if (windowKey !== null && windowKey !== previous.windowKey) { + return initializeExtremum(sample, reset, timestamp); + } + } + const shouldReplace = direction === "min" ? sample < previous.value : sample > previous.value; + return shouldReplace ? { ...previous, value: sample } : previous; + } + var CALENDAR_RESETS; + var init_extrema = __esm({ + "src/utils/extrema.js"() { + CALENDAR_RESETS = /* @__PURE__ */ new Set([ + "quarterly", + "hourly", + "daily", + "weekly", + "monthly", + "yearly" + ]); + } + }); + // src/config/normalize.js function normalizeResolvableValue(value, entityValue, percentValue = null) { const normalized = { @@ -80,6 +196,97 @@ } return normalizeResolvableValue(input, null); } + function hasConfiguredMarkerSource(source) { + var _a; + return !!source && (getFiniteNumber((_a = source.fixed) != null ? _a : source.value) !== null || Number.isFinite(source.percent) || !!source.entity); + } + function applyGenericMarkerCapacity(markers, config) { + var _a, _b, _c, _d; + const occupied = { + above: ((_a = config == null ? void 0 : config.peak_marker) == null ? void 0 : _a.show) === true ? 1 : 0, + below: (((_b = config == null ? void 0 : config.floor_marker) == null ? void 0 : _b.show) === true ? 1 : 0) + (((_c = config == null ? void 0 : config.target_marker) == null ? void 0 : _c.enabled) !== false && hasConfiguredMarkerSource((_d = config == null ? void 0 : config.target_marker) == null ? void 0 : _d.source) ? 1 : 0) + }; + return markers.map((marker) => { + const accepted = marker.valid && occupied[marker.lane] < 4; + if (accepted) occupied[marker.lane] += 1; + return { ...marker, accepted }; + }); + } + function normalizeGenericMarkerList(input, capacityConfig = null) { + if (!Array.isArray(input)) { + return { markers: [], invalidList: input !== void 0 }; + } + const markers = input.map((rawMarker, index) => { + var _a; + if (!rawMarker || typeof rawMarker !== "object" || Array.isArray(rawMarker)) { + return { id: `generic-${index}`, index, valid: false, accepted: false, malformed: true }; + } + const rawAt = rawMarker.at; + const hasUnsupportedPercentField = rawAt && typeof rawAt === "object" && Object.prototype.hasOwnProperty.call(rawAt, "percent"); + const atInput = typeof rawAt === "string" ? rawAt.trim() : rawAt; + const source = normalizeStructuredResolvableValue(atInput, null, null, { allowPercent: true }); + const explicitEntity = rawAt && typeof rawAt === "object" && rawAt.entity !== void 0 ? rawAt.entity : typeof atInput === "string" && looksLikeEntityId(atInput) ? atInput : null; + const invalidEntity = explicitEntity !== null && explicitEntity !== void 0 && explicitEntity !== "" && !looksLikeEntityId(explicitEntity); + const invalidFixed = rawAt && typeof rawAt === "object" && rawAt.fixed !== void 0 && rawAt.fixed !== null && getFiniteNumber(rawAt.fixed) === null; + const hasSource = !!source.entity || getFiniteNumber(source.fixed) !== null || Number.isFinite(source.percent); + const invalidPercentage = Number.isFinite(source.percent) && (source.percent < 0 || source.percent > 100); + const validSource = rawAt !== void 0 && rawAt !== null && !hasUnsupportedPercentField && !invalidEntity && !invalidFixed && hasSource && !invalidPercentage; + const lane = rawMarker.lane === void 0 ? "below" : rawMarker.lane; + const validLane = lane === "above" || lane === "below"; + const supportedShapes = ["circle", "diamond", "triangle", "chevron", "arrow", "pin"]; + const validShape = rawMarker.shape === void 0 || supportedShapes.includes(rawMarker.shape); + const direction = normalizeMarkerDirection(rawMarker.direction); + const invalidDirection = rawMarker.direction !== void 0 && !["inward", "outward"].includes( + typeof rawMarker.direction === "string" ? rawMarker.direction.trim().toLowerCase() : "" + ); + const label = rawMarker.label && typeof rawMarker.label === "object" && !Array.isArray(rawMarker.label) ? rawMarker.label : {}; + const labelConfig = normalizeMarkerLabelConfig(label); + const labelEntityInput = label.entity; + const labelEntity = typeof labelEntityInput === "string" ? labelEntityInput.trim() : ""; + const invalidLabelEntity = labelEntityInput !== void 0 && labelEntityInput !== null && labelEntityInput !== "" && !looksLikeEntityId(labelEntity); + const validLabelEntity = labelEntity && looksLikeEntityId(labelEntity) ? labelEntity : null; + const showMarker = typeof rawMarker.show_marker === "boolean" ? rawMarker.show_marker : true; + return { + id: `generic-${index}`, + index, + source: { + ...source, + entity: typeof source.entity === "string" ? source.entity.trim() : source.entity + }, + lane: validLane ? lane : null, + shape: validShape ? (_a = rawMarker.shape) != null ? _a : "circle" : "circle", + direction, + invalidDirection, + showMarker, + invalidShowMarker: rawMarker.show_marker !== void 0 && typeof rawMarker.show_marker !== "boolean", + color: typeof rawMarker.color === "string" && rawMarker.color.trim() ? rawMarker.color : "#888888", + label: { + show: label.show === true, + text: labelConfig.label_text, + entity: validLabelEntity, + showValue: labelConfig.label_show_value, + showUnit: labelConfig.label_show_unit, + precision: labelConfig.label_precision, + invalidShow: labelConfig.label_invalid_show, + invalidText: labelConfig.label_invalid_text, + invalidShowValue: labelConfig.label_invalid_show_value, + invalidShowUnit: labelConfig.label_invalid_show_unit, + invalidPrecision: labelConfig.label_invalid_precision, + invalidPrecisionKey: labelConfig.label_precision_key, + unsupportedUnit: labelConfig.label_unsupported_unit, + invalidEntity: invalidLabelEntity + }, + valid: validSource && validLane, + invalidSource: !validSource, + invalidPercentage, + unsupportedPercentField: hasUnsupportedPercentField, + invalidLane: !validLane, + invalidShape: !validShape, + accepted: false + }; + }); + return { markers: applyGenericMarkerCapacity(markers, capacityConfig), invalidList: false }; + } function normalizeBaselineDirectionConfig(input, inheritedDirection = null) { var _a; const inherited = inheritedDirection != null ? inheritedDirection : { color: null }; @@ -169,37 +376,65 @@ }); return inferSegmentEndValues(segments, 100); } - function hasResolvableMagnitude(resolvable) { - return !!resolvable && (Number.isFinite(getFiniteNumber(resolvable.fixed)) || Number.isFinite(resolvable.percent)); + function normalizeSegmentBoundary(input, legacySegmentSpace = null) { + var _a; + if (input === void 0) return { value: null, issue: null }; + if (input === null) return { value: null, issue: "malformed" }; + if (input && typeof input === "object" && !Array.isArray(input)) { + if (input.entity !== void 0 && input.entity !== null && input.entity !== "") { + return { value: null, issue: "entity" }; + } + if (Object.prototype.hasOwnProperty.call(input, "percent")) { + const percent = getFiniteNumber(input.percent); + return Number.isFinite(percent) ? { value: normalizeResolvableValue(null, null, percent), issue: null } : { value: null, issue: "malformed" }; + } + const fixed = getFiniteNumber((_a = input.fixed) != null ? _a : input.value); + return Number.isFinite(fixed) ? { value: normalizeResolvableValue(fixed, null), issue: null } : { value: null, issue: "malformed" }; + } + if (typeof input === "string") { + const trimmed = input.trim(); + if (looksLikeEntityId(trimmed)) return { value: null, issue: "entity" }; + if (trimmed.includes("%")) { + const percent = parsePercentLiteral(trimmed); + return Number.isFinite(percent) ? { value: normalizeResolvableValue(null, null, percent), issue: null } : { value: null, issue: "malformed_percent" }; + } + } + const numeric = getFiniteNumber(input); + if (!Number.isFinite(numeric)) return { value: null, issue: "malformed" }; + return legacySegmentSpace === "percent" ? { value: normalizeResolvableValue(null, null, numeric), issue: null } : { value: normalizeResolvableValue(numeric, null), issue: null }; } - function normalizeGaugeSegments(input) { + function normalizeGaugeSegments(input, options = {}) { if (!Array.isArray(input)) return null; + const { legacySegmentSpace = null } = options; const segments = input.map((segment) => { - var _a; - const from = normalizeStructuredResolvableValue(segment == null ? void 0 : segment.from, null, null, { allowPercent: true }); - const to = (segment == null ? void 0 : segment.to) === void 0 ? null : normalizeStructuredResolvableValue(segment.to, null, null, { allowPercent: true }); - if (!hasResolvableMagnitude(from) || !(segment == null ? void 0 : segment.color)) { + var _a, _b; + const from = (segment == null ? void 0 : segment.from) === void 0 ? { value: null, issue: "malformed" } : normalizeSegmentBoundary(segment.from, legacySegmentSpace); + const to = (segment == null ? void 0 : segment.to) === void 0 ? { value: null, issue: null } : segment.to === null ? { value: null, issue: null } : normalizeSegmentBoundary(segment.to, legacySegmentSpace); + if (!(segment == null ? void 0 : segment.color)) { return null; } return { - from, - to, + from: from.value, + to: to.value, + invalidBoundary: (_a = from.issue) != null ? _a : to.issue, color: segment.color, - label: (_a = segment.label) != null ? _a : null + label: (_b = segment.label) != null ? _b : null }; }).filter(Boolean); return segments.map((segment, index) => { var _a; + const nextValid = segments.slice(index + 1).find((candidate) => !candidate.invalidBoundary); return { - from: { ...segment.from }, - to: segment.to ? { ...segment.to } : index < segments.length - 1 ? { ...segments[index + 1].from } : null, + from: segment.from ? { ...segment.from } : null, + to: segment.to ? { ...segment.to } : (nextValid == null ? void 0 : nextValid.from) ? { ...nextValid.from } : null, + ...segment.invalidBoundary ? { invalidBoundary: segment.invalidBoundary } : {}, color: segment.color, label: (_a = segment.label) != null ? _a : null }; }); } function normalizeScaleBound(entityConfig, cardConfig, key, defaultValue) { - var _a, _b, _c, _d, _e, _f, _g, _h; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; const cardScale = cardConfig == null ? void 0 : cardConfig.scale; const entityScale = entityConfig == null ? void 0 : entityConfig.scale; const entityKey = `${key}_entity`; @@ -211,18 +446,28 @@ (_d = cardConfig == null ? void 0 : cardConfig[key]) != null ? _d : defaultValue, (_e = cardConfig == null ? void 0 : cardConfig[entityKey]) != null ? _e : null ); + const inheritedFixedExplicit = cardBound ? cardBound.fixed_explicit !== false && cardBound.fixed !== null && cardBound.fixed !== void 0 : (cardConfig == null ? void 0 : cardConfig[key]) !== null && (cardConfig == null ? void 0 : cardConfig[key]) !== void 0; + const withFixedExplicit = (bound, explicit) => { + Object.defineProperty(bound, "fixed_explicit", { value: explicit }); + return bound; + }; if ((entityScale == null ? void 0 : entityScale[key]) !== void 0) { - return normalizeStructuredResolvableValue( + const input = entityScale[key]; + const fixedExplicit = looksLikeEntityId(input) ? inheritedFixedExplicit : typeof input === "object" && input !== null ? ((_f = input.fixed) != null ? _f : input.value) !== null && ((_g = input.fixed) != null ? _g : input.value) !== void 0 : input !== null; + return withFixedExplicit(normalizeStructuredResolvableValue( entityScale[key], inherited, defaultValue - ); + ), fixedExplicit); } const entityOverride = entityConfig[entityKey]; const hasEntityOverride = entityOverride !== void 0 && entityOverride !== null; - const value = (_g = (_f = entityConfig[key]) != null ? _f : hasEntityOverride ? null : inherited.fixed) != null ? _g : inherited.entity ? null : defaultValue; - const entity = (_h = entityOverride != null ? entityOverride : inherited.entity) != null ? _h : null; - return normalizeResolvableValue(value, entity); + const value = (_i = (_h = entityConfig[key]) != null ? _h : hasEntityOverride ? null : inherited.fixed) != null ? _i : inherited.entity ? null : defaultValue; + const entity = (_j = entityOverride != null ? entityOverride : inherited.entity) != null ? _j : null; + return withFixedExplicit( + normalizeResolvableValue(value, entity), + entityConfig[key] !== null && entityConfig[key] !== void 0 || !hasEntityOverride && inheritedFixedExplicit + ); } function normalizeScaleConfig(entityConfig, cardConfig) { return { @@ -260,6 +505,34 @@ return null; } } + function hasOwnConfigValue(config, key) { + return config !== null && config !== void 0 && Object.prototype.hasOwnProperty.call(config, key); + } + function hasExplicitColor(config) { + const values = [ + hasOwnConfigValue(config, "color") ? config.color : void 0, + hasOwnConfigValue(config == null ? void 0 : config.bar, "color") ? config.bar.color : void 0 + ]; + return values.some((value) => value !== void 0 && value !== null); + } + function getPaintExplicitness(config) { + const bar = config == null ? void 0 : config.bar; + const explicitPaintKeys = [ + [config, "color_mode"], + [bar, "color_mode"], + [bar, "fill_style"], + [config, "severity"], + [config, "segments"], + [bar, "segments"], + [config, "gradient_stops"], + [bar, "gradient_stops"], + [bar, "solid_fill"] + ]; + return { + color: hasExplicitColor(config), + paint: explicitPaintKeys.some(([source, key]) => hasOwnConfigValue(source, key)) + }; + } function normalizeBarModeConfig(barConfig = null, flatColorMode = null) { var _a, _b, _c, _d, _e, _f; const fillStyle = (_a = barConfig == null ? void 0 : barConfig.fill_style) != null ? _a : null; @@ -270,7 +543,12 @@ color_mode: normalizedColorMode }; } - function resolveNormalizedBarMode(entityBar, entityConfig, cardBar, cardConfig) { + function resolveNormalizedBarMode(entityBar, entityConfig, cardBar, cardConfig, { scopeExplicitness = null, inheritedExplicitness = null, isCardScope = false } = {}) { + const colorOnlyScope = (scopeExplicitness == null ? void 0 : scopeExplicitness.color) === true && scopeExplicitness.paint !== true; + const inheritedPaintIsExplicit = (inheritedExplicitness == null ? void 0 : inheritedExplicitness.paint) === true; + if (colorOnlyScope && (isCardScope || !inheritedPaintIsExplicit)) { + return normalizeBarModeConfig({ fill_style: "solid" }, null); + } if ((entityBar == null ? void 0 : entityBar.fill_style) !== void 0 || (entityBar == null ? void 0 : entityBar.color_mode) !== void 0 || entityConfig.color_mode !== void 0) { return normalizeBarModeConfig(entityBar, entityConfig.color_mode); } @@ -313,54 +591,60 @@ } return { ...base }; } - function normalizeBarConfig(entityConfig, cardConfig) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y; + function normalizeBarConfig(entityConfig, cardConfig, options = {}) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A; + const scopeExplicitness = (_a = options.scopeExplicitness) != null ? _a : getPaintExplicitness(entityConfig); + const inheritedExplicitness = (_c = (_b = options.inheritedExplicitness) != null ? _b : cardConfig == null ? void 0 : cardConfig[PAINT_EXPLICITNESS]) != null ? _c : getPaintExplicitness(cardConfig); + const isCardScope = (_d = options.isCardScope) != null ? _d : cardConfig == null; const cardBar = cardConfig == null ? void 0 : cardConfig.bar; const entityBar = entityConfig == null ? void 0 : entityConfig.bar; const entityStructuredSegments = entityBar == null ? void 0 : entityBar.segments; const entityTopLevelSegments = entityConfig.segments; const entityLegacySeverity = entityConfig.severity; - const cardStructuredSegments = (_a = cardBar == null ? void 0 : cardBar.segments) != null ? _a : null; - const cardTopLevelSegments = (_b = cardConfig == null ? void 0 : cardConfig.segments) != null ? _b : null; - const cardLegacySeverity = (_c = cardConfig == null ? void 0 : cardConfig.severity) != null ? _c : null; + const cardStructuredSegments = (_e = cardBar == null ? void 0 : cardBar.segments) != null ? _e : null; + const cardTopLevelSegments = (_f = cardConfig == null ? void 0 : cardConfig.segments) != null ? _f : null; + const cardLegacySeverity = (_g = cardConfig == null ? void 0 : cardConfig.severity) != null ? _g : null; let segments = null; - let segment_space = (_d = cardBar == null ? void 0 : cardBar.segment_space) != null ? _d : "percent"; + let segment_space = (cardBar == null ? void 0 : cardBar.segment_space) === "percent" || (cardBar == null ? void 0 : cardBar.segment_space) === "scale" ? cardBar.segment_space : null; if (entityStructuredSegments !== void 0 && entityStructuredSegments !== null) { - segments = normalizeGaugeSegments(entityStructuredSegments); - segment_space = "scale"; + segment_space = (entityBar == null ? void 0 : entityBar.segment_space) === "percent" || (entityBar == null ? void 0 : entityBar.segment_space) === "scale" ? entityBar.segment_space : segment_space; + segments = normalizeGaugeSegments(entityStructuredSegments, { legacySegmentSpace: segment_space }); } else if (entityTopLevelSegments !== void 0 && entityTopLevelSegments !== null) { segments = normalizeGaugeSegments(entityTopLevelSegments); - segment_space = "scale"; } else if (entityLegacySeverity !== void 0 && entityLegacySeverity !== null) { segments = normalizeSeverityToSegments(entityLegacySeverity); segment_space = "percent"; } else if (cardStructuredSegments !== null && cardStructuredSegments !== void 0) { segments = cardStructuredSegments.map((segment) => ({ ...segment })); - segment_space = (_e = cardBar == null ? void 0 : cardBar.segment_space) != null ? _e : "percent"; } else if (cardTopLevelSegments !== null && cardTopLevelSegments !== void 0) { segments = normalizeGaugeSegments(cardTopLevelSegments); - segment_space = "scale"; } else if (cardLegacySeverity !== null && cardLegacySeverity !== void 0) { segments = normalizeSeverityToSegments(cardLegacySeverity); segment_space = "percent"; } - const structuredAboveTargetColor = (entityConfig == null ? void 0 : entityConfig.target) && typeof entityConfig.target === "object" && !Array.isArray(entityConfig.target) ? (_f = entityConfig.target.when_exceeded) == null ? void 0 : _f.fill_color : void 0; - const inheritedStructuredAboveTargetColor = (cardConfig == null ? void 0 : cardConfig.target) && typeof cardConfig.target === "object" && !Array.isArray(cardConfig.target) ? (_g = cardConfig.target.when_exceeded) == null ? void 0 : _g.fill_color : void 0; - const normalizedMode = resolveNormalizedBarMode(entityBar, entityConfig, cardBar, cardConfig); + const structuredAboveTargetColor = (entityConfig == null ? void 0 : entityConfig.target) && typeof entityConfig.target === "object" && !Array.isArray(entityConfig.target) ? (_h = entityConfig.target.when_exceeded) == null ? void 0 : _h.fill_color : void 0; + const inheritedStructuredAboveTargetColor = (cardConfig == null ? void 0 : cardConfig.target) && typeof cardConfig.target === "object" && !Array.isArray(cardConfig.target) ? (_i = cardConfig.target.when_exceeded) == null ? void 0 : _i.fill_color : void 0; + const normalizedMode = resolveNormalizedBarMode( + entityBar, + entityConfig, + cardBar, + cardConfig, + { scopeExplicitness, inheritedExplicitness, isCardScope } + ); return { fill_style: normalizedMode.fill_style, color_mode: normalizedMode.color_mode, needle: normalizeNeedleConfig(entityBar == null ? void 0 : entityBar.needle, cardBar == null ? void 0 : cardBar.needle), - solid_fill: (_i = (_h = entityBar == null ? void 0 : entityBar.solid_fill) != null ? _h : cardBar == null ? void 0 : cardBar.solid_fill) != null ? _i : false, - color: (_m = (_l = (_k = (_j = entityBar == null ? void 0 : entityBar.color) != null ? _j : entityConfig.color) != null ? _k : cardBar == null ? void 0 : cardBar.color) != null ? _l : cardConfig == null ? void 0 : cardConfig.color) != null ? _m : "#4a9eff", + solid_fill: (_k = (_j = entityBar == null ? void 0 : entityBar.solid_fill) != null ? _j : cardBar == null ? void 0 : cardBar.solid_fill) != null ? _k : false, + color: (_o = (_n = (_m = (_l = entityBar == null ? void 0 : entityBar.color) != null ? _l : entityConfig.color) != null ? _m : cardBar == null ? void 0 : cardBar.color) != null ? _n : cardConfig == null ? void 0 : cardConfig.color) != null ? _o : "#4a9eff", gradient_stops: normalizeGradientStops( - (_q = (_p = (_o = (_n = entityBar == null ? void 0 : entityBar.gradient_stops) != null ? _n : entityConfig.gradient_stops) != null ? _o : cardBar == null ? void 0 : cardBar.gradient_stops) != null ? _p : cardConfig == null ? void 0 : cardConfig.gradient_stops) != null ? _q : null + (_s = (_r = (_q = (_p = entityBar == null ? void 0 : entityBar.gradient_stops) != null ? _p : entityConfig.gradient_stops) != null ? _q : cardBar == null ? void 0 : cardBar.gradient_stops) != null ? _r : cardConfig == null ? void 0 : cardConfig.gradient_stops) != null ? _s : null ), severity: segments, segments, segment_space, - animated: (_u = (_t = (_s = (_r = entityBar == null ? void 0 : entityBar.animated) != null ? _r : entityConfig.animated) != null ? _s : cardBar == null ? void 0 : cardBar.animated) != null ? _t : cardConfig == null ? void 0 : cardConfig.animated) != null ? _u : true, - above_target_color: (_y = (_x = (_w = (_v = structuredAboveTargetColor != null ? structuredAboveTargetColor : entityConfig.above_target_color) != null ? _v : cardBar == null ? void 0 : cardBar.above_target_color) != null ? _w : inheritedStructuredAboveTargetColor) != null ? _x : cardConfig == null ? void 0 : cardConfig.above_target_color) != null ? _y : null + animated: (_w = (_v = (_u = (_t = entityBar == null ? void 0 : entityBar.animated) != null ? _t : entityConfig.animated) != null ? _u : cardBar == null ? void 0 : cardBar.animated) != null ? _v : cardConfig == null ? void 0 : cardConfig.animated) != null ? _w : true, + above_target_color: (_A = (_z = (_y = (_x = structuredAboveTargetColor != null ? structuredAboveTargetColor : entityConfig.above_target_color) != null ? _x : cardBar == null ? void 0 : cardBar.above_target_color) != null ? _y : inheritedStructuredAboveTargetColor) != null ? _z : cardConfig == null ? void 0 : cardConfig.above_target_color) != null ? _A : null }; } function clampSupportedRowHeight(height) { @@ -419,46 +703,202 @@ unit: (_h = (_g = (_f = (_e = entityFormatting == null ? void 0 : entityFormatting.unit) != null ? _e : entityConfig.unit) != null ? _f : cardFormatting == null ? void 0 : cardFormatting.unit) != null ? _g : cardConfig == null ? void 0 : cardConfig.unit) != null ? _h : null }; } + function normalizeTargetMarkerShape(value) { + const normalized = String(value != null ? value : "").trim().toLowerCase(); + return normalized === "triangle" || normalized === "diamond" ? normalized : "diamond"; + } + function normalizeMarkerDirection(value) { + const normalized = typeof value === "string" ? value.trim().toLowerCase() : ""; + return normalized === "outward" ? "outward" : "inward"; + } + function normalizeMarkerLabelConfig(rawLabel, inherited = {}) { + var _a, _b, _c; + const label = rawLabel && typeof rawLabel === "object" && !Array.isArray(rawLabel) ? rawLabel : {}; + const hasText = Object.prototype.hasOwnProperty.call(label, "text"); + const textValue = hasText ? typeof label.text === "string" ? label.text.replace(/\s+/g, " ").trim() : inherited.label_text : inherited.label_text; + const hasPrecision = Object.prototype.hasOwnProperty.call(label, "precision"); + const hasDecimal = Object.prototype.hasOwnProperty.call(label, "decimal"); + const precisionValue = hasPrecision ? label.precision : label.decimal; + const precisionSet = hasPrecision || hasDecimal; + const precision = precisionSet ? getFiniteNumber(precisionValue) : (_a = inherited.label_precision) != null ? _a : null; + const precisionInvalid = precisionSet && precisionValue !== null && precisionValue !== "" && !(Number.isInteger(precision) && precision >= 0); + return { + label_text: textValue != null ? textValue : null, + label_show_value: typeof label.show_value === "boolean" ? label.show_value : (_b = inherited.label_show_value) != null ? _b : true, + label_show_unit: typeof label.show_unit === "boolean" ? label.show_unit : (_c = inherited.label_show_unit) != null ? _c : true, + label_precision: precisionInvalid ? null : precision, + label_invalid_show: label.show !== void 0 && typeof label.show !== "boolean", + label_invalid_text: hasText && typeof label.text !== "string", + label_invalid_show_value: label.show_value !== void 0 && typeof label.show_value !== "boolean", + label_invalid_show_unit: label.show_unit !== void 0 && typeof label.show_unit !== "boolean", + label_invalid_precision: precisionInvalid, + label_precision_key: precisionInvalid ? hasPrecision ? "precision" : "decimal" : void 0, + label_unsupported_unit: Object.prototype.hasOwnProperty.call(label, "unit") + }; + } + function builtinMarkerLabelFields(options) { + const fields = {}; + if (options.label_text !== null && options.label_text !== void 0) fields.label_text = options.label_text; + if (options.label_show_value === false) fields.label_show_value = false; + if (options.label_show_unit === false) fields.label_show_unit = false; + if (options.label_precision !== null && options.label_precision !== void 0) fields.label_precision = options.label_precision; + if (options.label_invalid_text) fields.label_invalid_text = true; + if (options.label_invalid_show_value) fields.label_invalid_show_value = true; + if (options.label_invalid_show_unit) fields.label_invalid_show_unit = true; + if (options.label_invalid_show) fields.label_invalid_show = true; + if (options.label_invalid_precision) fields.label_invalid_precision = true; + if (options.label_invalid_precision && options.label_precision_key) fields.label_precision_key = options.label_precision_key; + if (options.label_unsupported_unit) fields.label_unsupported_unit = true; + return fields; + } + function isInvalidMarkerDirection(value) { + return value !== void 0 && !["inward", "outward"].includes( + typeof value === "string" ? value.trim().toLowerCase() : "" + ); + } function normalizeTargetMarkerConfig(entityConfig, cardConfig) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I; const cardTarget = cardConfig == null ? void 0 : cardConfig.target_marker; const rawTarget = entityConfig == null ? void 0 : entityConfig.target; const legacyCardTarget = (cardConfig == null ? void 0 : cardConfig.target) && typeof cardConfig.target === "object" && !Array.isArray(cardConfig.target) ? null : (_a = cardConfig == null ? void 0 : cardConfig.target) != null ? _a : null; - const inheritedTarget = cardTarget != null ? cardTarget : { + const inheritedTarget = cardTarget ? { + ...cardTarget, + shape: normalizeTargetMarkerShape(cardTarget.shape), + direction: normalizeMarkerDirection(cardTarget.direction) + } : { enabled: null, source: normalizeResolvableValue(null, null), color: (_b = cardConfig == null ? void 0 : cardConfig.target_color) != null ? _b : "#888", - show_label: (_c = cardConfig == null ? void 0 : cardConfig.show_target_label) != null ? _c : false + show_label: (_c = cardConfig == null ? void 0 : cardConfig.show_target_label) != null ? _c : false, + ...builtinMarkerLabelFields(normalizeMarkerLabelConfig((_d = cardConfig == null ? void 0 : cardConfig.target) == null ? void 0 : _d.label)), + label_decimal: (_j = (_i = (_f = (_e = cardConfig == null ? void 0 : cardConfig.target) == null ? void 0 : _e.label) == null ? void 0 : _f.precision) != null ? _i : (_h = (_g = cardConfig == null ? void 0 : cardConfig.target) == null ? void 0 : _g.label) == null ? void 0 : _h.decimal) != null ? _j : null, + shape: "diamond", + direction: "inward" }; if (rawTarget && typeof rawTarget === "object" && !Array.isArray(rawTarget)) { - return { - enabled: (_e = (_d = normalizeOptionalEnabled(rawTarget.enabled)) != null ? _d : inheritedTarget.enabled) != null ? _e : null, + const labelOptions = normalizeMarkerLabelConfig(rawTarget.label, inheritedTarget); + const normalizedTarget2 = { + enabled: (_l = (_k = normalizeOptionalEnabled(rawTarget.enabled)) != null ? _k : inheritedTarget.enabled) != null ? _l : null, source: normalizeStructuredResolvableValue(rawTarget.at, inheritedTarget.source, null, { allowPercent: true }), - color: (_g = (_f = rawTarget.color) != null ? _f : entityConfig.target_color) != null ? _g : inheritedTarget.color, - show_label: (_j = (_i = (_h = rawTarget.label) == null ? void 0 : _h.show) != null ? _i : entityConfig.show_target_label) != null ? _j : inheritedTarget.show_label + color: (_n = (_m = rawTarget.color) != null ? _m : entityConfig.target_color) != null ? _n : inheritedTarget.color, + show_label: (_q = (_p = (_o = rawTarget.label) == null ? void 0 : _o.show) != null ? _p : entityConfig.show_target_label) != null ? _q : inheritedTarget.show_label, + shape: Object.prototype.hasOwnProperty.call(rawTarget, "shape") ? normalizeTargetMarkerShape(rawTarget.shape) : inheritedTarget.shape, + direction: Object.prototype.hasOwnProperty.call(rawTarget, "direction") ? normalizeMarkerDirection(rawTarget.direction) : inheritedTarget.direction, + ...isInvalidMarkerDirection(rawTarget.direction) ? { direction_invalid: true } : {}, + ...builtinMarkerLabelFields(labelOptions) }; + const labelDecimal2 = labelOptions.label_precision; + if (labelDecimal2 !== null && labelDecimal2 !== void 0) { + normalizedTarget2.label_decimal = labelDecimal2; + normalizedTarget2.label_precision = labelDecimal2; + } + return normalizedTarget2; } - const value = (_o = (_n = (_l = entityConfig.target) != null ? _l : (_k = inheritedTarget.source) == null ? void 0 : _k.fixed) != null ? _n : (_m = inheritedTarget.source) == null ? void 0 : _m.value) != null ? _o : legacyCardTarget; - const entity = (_s = (_r = (_q = entityConfig.target_entity) != null ? _q : (_p = inheritedTarget.source) == null ? void 0 : _p.entity) != null ? _r : cardConfig == null ? void 0 : cardConfig.target_entity) != null ? _s : null; - const percent = entityConfig.target === void 0 && entityConfig.target_entity === void 0 ? (_u = (_t = inheritedTarget.source) == null ? void 0 : _t.percent) != null ? _u : null : null; - return { - enabled: (_v = inheritedTarget.enabled) != null ? _v : null, + const value = (_v = (_u = (_s = entityConfig.target) != null ? _s : (_r = inheritedTarget.source) == null ? void 0 : _r.fixed) != null ? _u : (_t = inheritedTarget.source) == null ? void 0 : _t.value) != null ? _v : legacyCardTarget; + const entity = (_z = (_y = (_x = entityConfig.target_entity) != null ? _x : (_w = inheritedTarget.source) == null ? void 0 : _w.entity) != null ? _y : cardConfig == null ? void 0 : cardConfig.target_entity) != null ? _z : null; + const percent = entityConfig.target === void 0 && entityConfig.target_entity === void 0 ? (_B = (_A = inheritedTarget.source) == null ? void 0 : _A.percent) != null ? _B : null : null; + const normalizedTarget = { + enabled: (_C = inheritedTarget.enabled) != null ? _C : null, source: normalizeResolvableValue(value, entity, percent), - color: (_y = (_x = (_w = entityConfig.target_color) != null ? _w : inheritedTarget.color) != null ? _x : cardConfig == null ? void 0 : cardConfig.target_color) != null ? _y : "#888", - show_label: (_B = (_A = (_z = entityConfig.show_target_label) != null ? _z : inheritedTarget.show_label) != null ? _A : cardConfig == null ? void 0 : cardConfig.show_target_label) != null ? _B : false + color: (_F = (_E = (_D = entityConfig.target_color) != null ? _D : inheritedTarget.color) != null ? _E : cardConfig == null ? void 0 : cardConfig.target_color) != null ? _F : "#888", + show_label: (_I = (_H = (_G = entityConfig.show_target_label) != null ? _G : inheritedTarget.show_label) != null ? _H : cardConfig == null ? void 0 : cardConfig.show_target_label) != null ? _I : false, + shape: inheritedTarget.shape, + direction: inheritedTarget.direction, + ...builtinMarkerLabelFields(inheritedTarget) }; + const labelDecimal = normalizedTarget.label_precision; + if (labelDecimal !== null && labelDecimal !== void 0) { + normalizedTarget.label_decimal = labelDecimal; + } + return normalizedTarget; + } + function getRawExtremumConfig(config, key) { + const value = config == null ? void 0 : config[key]; + if (value && typeof value === "object" && !Array.isArray(value)) return value; + const markerValue = config == null ? void 0 : config[`${key}_marker`]; + return markerValue && typeof markerValue === "object" && !Array.isArray(markerValue) ? markerValue : null; + } + function normalizeLabelConfig(rawConfig, inheritedConfig) { + var _a, _b, _c; + const rawLabel = rawConfig == null ? void 0 : rawConfig.label; + const hasRawLabel = rawLabel && typeof rawLabel === "object" && !Array.isArray(rawLabel); + const options = normalizeMarkerLabelConfig(rawLabel, inheritedConfig); + return { + show: hasRawLabel && typeof rawLabel.show === "boolean" ? rawLabel.show : (_a = inheritedConfig.show_label) != null ? _a : false, + text: options.label_text, + invalidShow: options.label_invalid_show, + showValue: options.label_show_value, + showUnit: options.label_show_unit, + precision: options.label_invalid_precision ? null : (_c = (_b = options.label_precision) != null ? _b : inheritedConfig.label_decimal) != null ? _c : null, + invalidText: options.label_invalid_text, + invalidShowValue: options.label_invalid_show_value, + invalidShowUnit: options.label_invalid_show_unit, + invalidPrecision: options.label_invalid_precision, + precisionKey: options.label_precision_key, + unsupportedUnit: options.label_unsupported_unit + }; + } + function normalizeExtremumMarkerConfig(entityConfig, cardConfig, key, options = {}) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i; + const { legacy = false, defaultColor = "#888888" } = options; + const cardMarker = cardConfig == null ? void 0 : cardConfig[`${key}_marker`]; + const rawMarker = getRawExtremumConfig(entityConfig, key); + const inherited = cardMarker != null ? cardMarker : { + show: legacy ? (_a = cardConfig == null ? void 0 : cardConfig.show_peak) != null ? _a : false : false, + color: legacy ? (_b = cardConfig == null ? void 0 : cardConfig.peak_color) != null ? _b : defaultColor : defaultColor, + show_label: false, + label_decimal: null, + label_text: null, + label_show_value: true, + label_show_unit: true, + label_precision: null, + reset: { kind: "never" }, + direction: "inward" + }; + const hasReset = rawMarker && Object.prototype.hasOwnProperty.call(rawMarker, "reset"); + const rawReset = hasReset ? rawMarker.reset : void 0; + const reset = hasReset ? normalizeReset(rawReset) : (_c = inherited.reset) != null ? _c : { kind: "never" }; + const rawLabel = rawMarker == null ? void 0 : rawMarker.label; + const label = normalizeLabelConfig(rawMarker, inherited); + const normalized = { + show: (_f = (_e = (_d = rawMarker == null ? void 0 : rawMarker.enabled) != null ? _d : legacy ? entityConfig.show_peak : void 0) != null ? _e : inherited.show) != null ? _f : false, + color: (_i = (_h = (_g = rawMarker == null ? void 0 : rawMarker.color) != null ? _g : legacy ? entityConfig.peak_color : void 0) != null ? _h : inherited.color) != null ? _i : defaultColor, + direction: rawMarker && Object.prototype.hasOwnProperty.call(rawMarker, "direction") ? normalizeMarkerDirection(rawMarker.direction) : normalizeMarkerDirection(inherited.direction), + ...isInvalidMarkerDirection(rawMarker == null ? void 0 : rawMarker.direction) ? { direction_invalid: true } : {} + }; + const inheritedAdvanced = cardMarker && (Object.prototype.hasOwnProperty.call(cardMarker, "show_label") || Object.prototype.hasOwnProperty.call(cardMarker, "label_decimal") || Object.prototype.hasOwnProperty.call(cardMarker, "label_text") || Object.prototype.hasOwnProperty.call(cardMarker, "label_show_value") || Object.prototype.hasOwnProperty.call(cardMarker, "label_show_unit") || Object.prototype.hasOwnProperty.call(cardMarker, "label_precision") || Object.prototype.hasOwnProperty.call(cardMarker, "reset")); + const hasAdvancedConfig = key === "floor" || hasReset || rawLabel !== void 0 || inheritedAdvanced; + if (hasAdvancedConfig) { + normalized.show_label = label.show; + normalized.label_decimal = label.precision; + normalized.reset = reset; + } + Object.assign(normalized, builtinMarkerLabelFields({ + label_text: label.text, + label_show_value: label.showValue === true ? void 0 : label.showValue, + label_show_unit: label.showUnit === true ? void 0 : label.showUnit, + label_precision: label.precision, + label_invalid_text: label.invalidText, + label_invalid_show: label.invalidShow, + label_invalid_show_value: label.invalidShowValue, + label_invalid_show_unit: label.invalidShowUnit, + label_invalid_precision: label.invalidPrecision, + label_precision_key: label.precisionKey, + label_unsupported_unit: label.unsupportedUnit + })); + if (hasReset && !isValidReset(rawReset)) { + normalized.reset_invalid = true; + } + return normalized; } function normalizePeakMarkerConfig(entityConfig, cardConfig) { - var _a, _b, _c, _d, _e, _f, _g, _h; - const cardPeak = cardConfig == null ? void 0 : cardConfig.peak_marker; - const entityPeak = entityConfig == null ? void 0 : entityConfig.peak; - return { - show: (_d = (_c = (_b = (_a = entityPeak == null ? void 0 : entityPeak.enabled) != null ? _a : entityConfig.show_peak) != null ? _b : cardPeak == null ? void 0 : cardPeak.show) != null ? _c : cardConfig == null ? void 0 : cardConfig.show_peak) != null ? _d : false, - color: (_h = (_g = (_f = (_e = entityPeak == null ? void 0 : entityPeak.color) != null ? _e : entityConfig.peak_color) != null ? _f : cardPeak == null ? void 0 : cardPeak.color) != null ? _g : cardConfig == null ? void 0 : cardConfig.peak_color) != null ? _h : "#888" - }; + return normalizeExtremumMarkerConfig(entityConfig, cardConfig, "peak", { legacy: true, defaultColor: "#888888" }); + } + function normalizeFloorMarkerConfig(entityConfig, cardConfig) { + return normalizeExtremumMarkerConfig(entityConfig, cardConfig, "floor", { defaultColor: "#888888" }); } function normalizeEntityConfig(entityConfig, cardConfig) { - var _a; + var _a, _b, _c; const normalizedEntity = { ...entityConfig, _normalized: true, @@ -468,11 +908,22 @@ }; normalizedEntity.layout = normalizeLayoutConfig(entityConfig, cardConfig); normalizedEntity.scale = normalizeScaleConfig(entityConfig, cardConfig); - normalizedEntity.bar = normalizeBarConfig(entityConfig, cardConfig); + normalizedEntity.bar = normalizeBarConfig(entityConfig, cardConfig, { + scopeExplicitness: getPaintExplicitness(entityConfig), + inheritedExplicitness: (_b = cardConfig == null ? void 0 : cardConfig[PAINT_EXPLICITNESS]) != null ? _b : getPaintExplicitness(cardConfig), + isCardScope: false + }); normalizedEntity.baseline = normalizeBaselineConfig(entityConfig, cardConfig); normalizedEntity.formatting = normalizeFormattingConfig(entityConfig, cardConfig); normalizedEntity.target_marker = normalizeTargetMarkerConfig(entityConfig, cardConfig); normalizedEntity.peak_marker = normalizePeakMarkerConfig(entityConfig, cardConfig); + normalizedEntity.floor_marker = normalizeFloorMarkerConfig(entityConfig, cardConfig); + const normalizedMarkers = entityConfig.markers === void 0 ? { + markers: applyGenericMarkerCapacity((_c = cardConfig == null ? void 0 : cardConfig.generic_markers) != null ? _c : [], normalizedEntity), + invalidList: (cardConfig == null ? void 0 : cardConfig.generic_markers_invalid) === true + } : normalizeGenericMarkerList(entityConfig.markers, normalizedEntity); + normalizedEntity.generic_markers = normalizedMarkers.markers; + normalizedEntity.generic_markers_invalid = normalizedMarkers.invalidList; normalizedEntity.min = normalizedEntity.scale.min.fixed; normalizedEntity.min_entity = normalizedEntity.scale.min.entity; normalizedEntity.max = normalizedEntity.scale.max.fixed; @@ -500,6 +951,7 @@ } function normalizeCardConfig(rawConfig) { var _a; + const cardPaintExplicitness = getPaintExplicitness(rawConfig); const baseConfig = { title: "", label_position: "left", @@ -507,7 +959,7 @@ color: "#4a9eff", animated: true, show_peak: false, - peak_color: "#888", + peak_color: "#888888", target: null, target_entity: null, target_color: "#888", @@ -543,20 +995,35 @@ ...baseConfig, _normalized: true }; + Object.defineProperty(normalizedCard, PAINT_EXPLICITNESS, { + value: cardPaintExplicitness, + enumerable: false + }); normalizedCard.layout = normalizeLayoutConfig(baseConfig, null); - normalizedCard.scale = normalizeScaleConfig(baseConfig, null); - normalizedCard.bar = normalizeBarConfig(baseConfig, null); + normalizedCard.scale = normalizeScaleConfig(rawConfig, null); + normalizedCard.bar = normalizeBarConfig(baseConfig, null, { + scopeExplicitness: cardPaintExplicitness, + inheritedExplicitness: null, + isCardScope: true + }); normalizedCard.baseline = normalizeBaselineConfig(baseConfig, null); normalizedCard.formatting = normalizeFormattingConfig(baseConfig, null); normalizedCard.target_marker = normalizeTargetMarkerConfig(baseConfig, null); normalizedCard.peak_marker = normalizePeakMarkerConfig(baseConfig, null); + normalizedCard.floor_marker = normalizeFloorMarkerConfig(baseConfig, null); + const normalizedMarkers = normalizeGenericMarkerList(baseConfig.markers, normalizedCard); + normalizedCard.generic_markers = normalizedMarkers.markers; + normalizedCard.generic_markers_invalid = normalizedMarkers.invalidList; normalizedCard.entities = baseConfig.entities.map( (entityCfg) => normalizeEntityConfig(entityCfg, normalizedCard) ); return normalizedCard; } + var PAINT_EXPLICITNESS; var init_normalize = __esm({ "src/config/normalize.js"() { + init_extrema(); + PAINT_EXPLICITNESS = /* @__PURE__ */ Symbol("sbcp.paintExplicitness"); } }); @@ -593,6 +1060,35 @@ } return null; } + function getResolvedScale(hass, scale, previousScale = null) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l; + const isValid = (bounds) => Number.isFinite(bounds == null ? void 0 : bounds.min) && Number.isFinite(bounds == null ? void 0 : bounds.max) && bounds.min < bounds.max; + const fixed = { + min: getNumericValue(null, (_c = (_a = scale == null ? void 0 : scale.min) == null ? void 0 : _a.fixed) != null ? _c : (_b = scale == null ? void 0 : scale.min) == null ? void 0 : _b.value), + max: getNumericValue(null, (_f = (_d = scale == null ? void 0 : scale.max) == null ? void 0 : _d.fixed) != null ? _f : (_e = scale == null ? void 0 : scale.max) == null ? void 0 : _e.value) + }; + if (((_g = scale == null ? void 0 : scale.min) == null ? void 0 : _g.entity) && ((_h = scale == null ? void 0 : scale.max) == null ? void 0 : _h.entity)) { + const dynamic = { + min: getEntityNumericValue(hass, scale.min.entity), + max: getEntityNumericValue(hass, scale.max.entity) + }; + if (isValid(dynamic)) return dynamic; + if (Number.isFinite(dynamic.min) && Number.isFinite(dynamic.max) && isValid(previousScale)) { + return previousScale; + } + return scale.min.fixed_explicit !== false && scale.max.fixed_explicit !== false && isValid(fixed) ? fixed : { min: 0, max: 100 }; + } + const resolved = { + min: (_i = getNormalizedResolvableNumericValue(hass, scale == null ? void 0 : scale.min)) != null ? _i : 0, + max: (_j = getNormalizedResolvableNumericValue(hass, scale == null ? void 0 : scale.max)) != null ? _j : 100 + }; + if (isValid(resolved)) return resolved; + const fallback = { + min: (_k = fixed.min) != null ? _k : 0, + max: (_l = fixed.max) != null ? _l : 100 + }; + return isValid(fallback) ? fallback : { min: 0, max: 100 }; + } var init_resolve = __esm({ "src/config/resolve.js"() { init_normalize(); @@ -614,6 +1110,22 @@ diagnostics.warnings.push(createDiagnostic(code, message, path, entity)); } function validateScaleBounds(diagnostics, scale, path, entity = null) { + var _a, _b; + if (((_a = scale == null ? void 0 : scale.min) == null ? void 0 : _a.entity) && ((_b = scale == null ? void 0 : scale.max) == null ? void 0 : _b.entity)) { + const hasFixed = (bound) => { + var _a2, _b2; + return bound.fixed_explicit !== false && ((_a2 = bound.fixed) != null ? _a2 : bound.value) !== null && ((_b2 = bound.fixed) != null ? _b2 : bound.value) !== void 0; + }; + if (hasFixed(scale.min) !== hasFixed(scale.max)) { + addWarning( + diagnostics, + "scale.orphan_fixed_fallback", + "Both dynamic scale bounds require a complete fixed fallback pair. The single fixed fallback will not be used if the dynamic pair becomes unavailable.", + `${path}.scale`, + entity + ); + } + } const min = getStaticFixedValue(scale == null ? void 0 : scale.min); const max = getStaticFixedValue(scale == null ? void 0 : scale.max); if (Number.isFinite(min) && Number.isFinite(max) && min > max) { @@ -657,6 +1169,89 @@ ); } } + function validateExtremumReset(diagnostics, config, path, entity = null) { + var _a; + for (const marker of ["peak_marker", "floor_marker"]) { + if ((_a = config == null ? void 0 : config[marker]) == null ? void 0 : _a.reset_invalid) { + addWarning( + diagnostics, + `${marker}.invalid_reset`, + "Invalid marker reset; using never.", + `${path}.${marker}.reset`, + entity + ); + } + } + } + function validateMarkerDirections(diagnostics, config, path, entity = null) { + var _a; + for (const marker of ["target_marker", "peak_marker", "floor_marker"]) { + if ((_a = config == null ? void 0 : config[marker]) == null ? void 0 : _a.direction_invalid) { + addWarning( + diagnostics, + `${marker}.invalid_direction`, + "Invalid marker direction; using inward.", + `${path}.${marker === "target_marker" ? "target" : marker.replace("_marker", "")}.direction`, + entity + ); + } + } + } + function validateBuiltinMarkerLabels(diagnostics, config, path, entity = null) { + var _a; + for (const [key, publicKey] of [["target_marker", "target"], ["peak_marker", "peak"], ["floor_marker", "floor"]]) { + const marker = config == null ? void 0 : config[key]; + const labelPath = `${path}.${publicKey}.label`; + if (marker == null ? void 0 : marker.label_invalid_show) addWarning(diagnostics, "markers.invalid_label_show", "Marker label show must be a boolean; using the existing fallback.", `${labelPath}.show`, entity); + if (marker == null ? void 0 : marker.label_invalid_text) addWarning(diagnostics, "markers.invalid_label_text", "Marker label text must be a string; ignoring it.", `${labelPath}.text`, entity); + if (marker == null ? void 0 : marker.label_invalid_show_value) addWarning(diagnostics, "markers.invalid_label_show_value", "Marker label show_value must be a boolean; using the inherited or default value.", `${labelPath}.show_value`, entity); + if (marker == null ? void 0 : marker.label_invalid_show_unit) addWarning(diagnostics, "markers.invalid_label_show_unit", "Marker label show_unit must be a boolean; using the inherited or default value.", `${labelPath}.show_unit`, entity); + if (marker == null ? void 0 : marker.label_invalid_precision) addWarning(diagnostics, "markers.invalid_label_precision", "Marker label precision must be a non-negative integer; using the inherited or row precision.", `${labelPath}.${(_a = marker.label_precision_key) != null ? _a : "precision"}`, entity); + if (marker == null ? void 0 : marker.label_unsupported_unit) addWarning(diagnostics, "markers.unsupported_label_unit", "Marker label unit is no longer supported; use show_unit instead.", `${labelPath}.unit`, entity); + } + } + function validateGenericMarkers(diagnostics, markers, invalidList, path, entity = null) { + var _a, _b, _c, _d, _e, _f, _g, _h; + if (invalidList) { + addWarning(diagnostics, "markers.invalid_list", "Markers must be a list; ignoring the malformed value.", path, entity); + } + for (const marker of markers != null ? markers : []) { + const markerPath = `${path}[${marker.index}]`; + if (marker.malformed) { + addWarning(diagnostics, "markers.invalid_item", "Marker must be an object; ignoring this item.", markerPath, entity); + continue; + } + if (marker.invalidSource && !marker.invalidPercentage && !marker.unsupportedPercentField) { + addWarning(diagnostics, "markers.invalid_source", "Marker requires a valid fixed value, percentage, or entity source.", `${markerPath}.at`, entity); + } + if (marker.invalidPercentage) { + addWarning(diagnostics, "markers.invalid_percentage", "Marker percentage must be between 0 and 100 inclusive.", `${markerPath}.at`, entity); + } + if (marker.unsupportedPercentField) { + addWarning(diagnostics, "markers.invalid_source", 'Use a percentage string such as "35%" instead of an at.percent field.', `${markerPath}.at`, entity); + } + if (marker.invalidLane) { + addWarning(diagnostics, "markers.invalid_lane", "Marker lane must be above or below; ignoring this marker.", `${markerPath}.lane`, entity); + } + if (marker.invalidShape) { + addWarning(diagnostics, "markers.invalid_shape", "Invalid marker shape; using circle.", `${markerPath}.shape`, entity); + } + if (marker.invalidDirection) { + addWarning(diagnostics, "markers.invalid_direction", "Invalid marker direction; using inward.", `${markerPath}.direction`, entity); + } + if (marker.invalidShowMarker) addWarning(diagnostics, "markers.invalid_show_marker", "Marker show_marker must be a boolean; using true.", `${markerPath}.show_marker`, entity); + if ((_a = marker.label) == null ? void 0 : _a.invalidEntity) addWarning(diagnostics, "markers.invalid_label_entity", "Marker label entity must be a valid entity ID; ignoring it.", `${markerPath}.label.entity`, entity); + if ((_b = marker.label) == null ? void 0 : _b.invalidShow) addWarning(diagnostics, "markers.invalid_label_show", "Marker label show must be a boolean; using the existing fallback.", `${markerPath}.label.show`, entity); + if ((_c = marker.label) == null ? void 0 : _c.invalidText) addWarning(diagnostics, "markers.invalid_label_text", "Marker label text must be a string; ignoring it.", `${markerPath}.label.text`, entity); + if ((_d = marker.label) == null ? void 0 : _d.invalidShowValue) addWarning(diagnostics, "markers.invalid_label_show_value", "Marker label show_value must be a boolean; using the inherited or default value.", `${markerPath}.label.show_value`, entity); + if ((_e = marker.label) == null ? void 0 : _e.invalidShowUnit) addWarning(diagnostics, "markers.invalid_label_show_unit", "Marker label show_unit must be a boolean; using the inherited or default value.", `${markerPath}.label.show_unit`, entity); + if ((_f = marker.label) == null ? void 0 : _f.invalidPrecision) addWarning(diagnostics, "markers.invalid_label_precision", "Marker label precision must be a non-negative integer; using the inherited or row precision.", `${markerPath}.label.${(_g = marker.label.invalidPrecisionKey) != null ? _g : "precision"}`, entity); + if ((_h = marker.label) == null ? void 0 : _h.unsupportedUnit) addWarning(diagnostics, "markers.unsupported_label_unit", "Marker label unit is no longer supported; use show_unit instead.", `${markerPath}.label.unit`, entity); + if (marker.valid && !marker.accepted) { + addWarning(diagnostics, "markers.excess_capacity", "This marker is not rendered because its lane already has four markers.", markerPath, entity); + } + } + } function getStaticSegmentBound(boundary) { var _a; if (!boundary || boundary.entity || Number.isFinite(boundary.percent)) return null; @@ -671,6 +1266,18 @@ const from = getStaticSegmentBound(segment == null ? void 0 : segment.from); const to = getStaticSegmentBound(segment == null ? void 0 : segment.to); const segmentPath = `${path}.segments[${index}]`; + if ((segment == null ? void 0 : segment.invalidBoundary) === "entity") { + addWarning(diagnostics, "segments.unsupported_entity_boundary", "Entity-backed segment boundaries are not supported; ignoring this segment.", segmentPath, entity); + continue; + } + if ((segment == null ? void 0 : segment.invalidBoundary) === "malformed_percent") { + addWarning(diagnostics, "segments.invalid_percentage", "Malformed percentage segment boundary; ignoring this segment.", segmentPath, entity); + continue; + } + if (segment == null ? void 0 : segment.invalidBoundary) { + addWarning(diagnostics, "segments.invalid_boundary", 'Segment boundary must be a numeric value or a percentage such as "35%"; ignoring this segment.', segmentPath, entity); + continue; + } if (Number.isFinite(from) && Number.isFinite(to) && from > to) { addWarning(diagnostics, "segments.from_gt_to", "Segment start is greater than segment end.", segmentPath, entity); } @@ -732,11 +1339,14 @@ validateTargetRange(diagnostics, config, scaleBounds, path, entity); validateBaselineRange(diagnostics, config, scaleBounds, path, entity); validateBaselineSuppressesNeedle(diagnostics, config, path, entity); + validateExtremumReset(diagnostics, config, path, entity); + validateMarkerDirections(diagnostics, config, path, entity); + validateBuiltinMarkerLabels(diagnostics, config, path, entity); validateSegments(diagnostics, (_a = config == null ? void 0 : config.bar) == null ? void 0 : _a.segments, scaleBounds, `${path}.bar`, entity); validateGradientStops(diagnostics, (_b = config == null ? void 0 : config.bar) == null ? void 0 : _b.gradient_stops, `${path}.bar`, entity); } function validateNormalizedConfig(config) { - var _a; + var _a, _b; const diagnostics = { warnings: [], errors: [] @@ -745,6 +1355,7 @@ return diagnostics; } validateConfigScope(diagnostics, config, "card"); + validateGenericMarkers(diagnostics, config.generic_markers, config.generic_markers_invalid, "markers"); const seenEntities = /* @__PURE__ */ new Set(); const entities = Array.isArray(config.entities) ? config.entities : []; for (let index = 0; index < entities.length; index += 1) { @@ -761,6 +1372,25 @@ seenEntities.add(entityId); } validateConfigScope(diagnostics, entityConfig, path, entityId); + if (Object.prototype.hasOwnProperty.call(entityConfig, "markers")) { + validateGenericMarkers( + diagnostics, + entityConfig.generic_markers, + entityConfig.generic_markers_invalid, + `${path}.markers`, + entityId + ); + } else { + const inheritedOverflow = ((_b = entityConfig.generic_markers) != null ? _b : []).filter( + (marker) => { + var _a2, _b2; + return marker.valid && !marker.accepted && ((_b2 = (_a2 = config.generic_markers) == null ? void 0 : _a2[marker.index]) == null ? void 0 : _b2.accepted) === true; + } + ); + if (inheritedOverflow.length) { + validateGenericMarkers(diagnostics, inheritedOverflow, false, `${path}.markers`, entityId); + } + } } return diagnostics; } @@ -770,6 +1400,219 @@ } }); + // src/utils/format.js + function formatNumericDisplay(rawVal, decimal = null) { + if (!Number.isFinite(rawVal)) return String(rawVal); + if (decimal !== null) { + return rawVal.toLocaleString(void 0, { + minimumFractionDigits: decimal, + maximumFractionDigits: decimal + }); + } + return rawVal.toLocaleString(); + } + function isTightUnit(unit) { + return ["h", "m", "s"].includes(String(unit || "").trim()); + } + function formatDisplayWithUnit(display, unit) { + if (!unit) return String(display); + const cleanUnit = String(unit); + return `${display}${isTightUnit(cleanUnit) ? "" : " "}${cleanUnit}`; + } + function createNumericPresentation(value, unit, decimal = null) { + const number = formatNumericDisplay(value, decimal); + const cleanUnit = unit ? String(unit) : ""; + return { + value, + number, + unit: cleanUnit, + text: formatDisplayWithUnit(number, cleanUnit) + }; + } + function createMarkerLabelPresentation(value, unit, precision = null, options = {}) { + const semanticText = typeof options.text === "string" ? options.text : ""; + const number = options.showValue === false || value === null || value === void 0 || value === "" ? "" : Number.isFinite(value) ? formatNumericDisplay(value, precision) : String(value); + const cleanUnit = options.showUnit === false ? "" : String(unit != null ? unit : "").trim(); + const text = [semanticText, number, cleanUnit].filter(Boolean).join(" "); + return { + value: Number.isFinite(value) ? value : null, + semanticText, + number, + unit: cleanUnit, + showValue: options.showValue !== false, + showUnit: options.showUnit !== false, + text + }; + } + function createTextPresentation(text) { + const value = String(text); + return { + value: null, + number: value, + unit: "", + text: value + }; + } + var init_format = __esm({ + "src/utils/format.js"() { + } + }); + + // src/view-model/marker-view-model.js + function hasConfiguredSource(source) { + if (!source) return false; + return source.entity !== null && source.entity !== void 0 && source.entity !== "" || source.fixed !== null && source.fixed !== void 0 && source.fixed !== "" || Number.isFinite(source.percent); + } + function normalizeMarkerShape(value, fallback = "circle") { + return MARKER_SHAPES.has(value) ? value : fallback; + } + function createMarkerModel({ + id, + type, + value = null, + position = null, + lane, + visible = false, + color = null, + label = null, + labelVisible = false, + shape = "circle", + direction = "inward", + showMarker = true + }) { + return { + id, + type, + value, + position, + lane, + visible: visible === true, + color, + label, + labelVisible: labelVisible === true, + shape: normalizeMarkerShape(shape), + direction, + showMarker: showMarker !== false + }; + } + function hasMarkerLabelContent(label) { + return !!(typeof (label == null ? void 0 : label.text) === "string" && label.text.trim()) || (label == null ? void 0 : label.showValue) !== false || (label == null ? void 0 : label.showUnit) !== false; + } + function getMarkerLaneOccupancy(entityConfig) { + var _a; + const targetConfig = entityConfig == null ? void 0 : entityConfig.target_marker; + const peakConfig = entityConfig == null ? void 0 : entityConfig.peak_marker; + const floorConfig = entityConfig == null ? void 0 : entityConfig.floor_marker; + const genericMarkers = ((_a = entityConfig == null ? void 0 : entityConfig.generic_markers) != null ? _a : []).filter((marker) => marker.accepted); + return { + below: (targetConfig == null ? void 0 : targetConfig.enabled) !== false && hasConfiguredSource(targetConfig == null ? void 0 : targetConfig.source) || (floorConfig == null ? void 0 : floorConfig.show) === true || genericMarkers.some((marker) => marker.lane === "below"), + above: (peakConfig == null ? void 0 : peakConfig.show) === true || genericMarkers.some((marker) => marker.lane === "above") + }; + } + function getMarkerLabelLaneOccupancy(entityConfig) { + var _a; + const targetConfig = entityConfig == null ? void 0 : entityConfig.target_marker; + const peakConfig = entityConfig == null ? void 0 : entityConfig.peak_marker; + const floorConfig = entityConfig == null ? void 0 : entityConfig.floor_marker; + const genericMarkers = ((_a = entityConfig == null ? void 0 : entityConfig.generic_markers) != null ? _a : []).filter((marker) => marker.accepted); + return { + below: (targetConfig == null ? void 0 : targetConfig.enabled) !== false && hasConfiguredSource(targetConfig == null ? void 0 : targetConfig.source) && (targetConfig == null ? void 0 : targetConfig.show_label) === true && hasMarkerLabelContent({ text: targetConfig.label_text, showValue: targetConfig.label_show_value, showUnit: targetConfig.label_show_unit }) || (floorConfig == null ? void 0 : floorConfig.show) === true && (floorConfig == null ? void 0 : floorConfig.show_label) === true && hasMarkerLabelContent({ text: floorConfig.label_text, showValue: floorConfig.label_show_value, showUnit: floorConfig.label_show_unit }) || genericMarkers.some((marker) => { + var _a2; + return marker.lane === "below" && ((_a2 = marker.label) == null ? void 0 : _a2.show) === true && hasMarkerLabelContent(marker.label); + }), + above: (peakConfig == null ? void 0 : peakConfig.show) === true && (peakConfig == null ? void 0 : peakConfig.show_label) === true && hasMarkerLabelContent({ text: peakConfig.label_text, showValue: peakConfig.label_show_value, showUnit: peakConfig.label_show_unit }) || genericMarkers.some((marker) => { + var _a2; + return marker.lane === "above" && ((_a2 = marker.label) == null ? void 0 : _a2.show) === true && hasMarkerLabelContent(marker.label); + }) + }; + } + function buildMarkerModels({ + entityConfig, + targetValue = null, + targetPosition = null, + targetPresentation = null, + targetLabelPresentation = null, + targetVisible = Number.isFinite(targetPosition), + peakValue = null, + peakPosition = null, + peakPresentation = null, + peakLabelPresentation = null, + peakVisible = Number.isFinite(peakPosition), + floorValue = null, + floorPosition = null, + floorPresentation = null, + floorLabelPresentation = null, + floorVisible = Number.isFinite(floorPosition), + genericMarkers = [] + }) { + var _a, _b, _c, _d, _e, _f, _g; + const targetConfig = entityConfig == null ? void 0 : entityConfig.target_marker; + const peakConfig = entityConfig == null ? void 0 : entityConfig.peak_marker; + const floorConfig = entityConfig == null ? void 0 : entityConfig.floor_marker; + const targetEnabled = (targetConfig == null ? void 0 : targetConfig.enabled) !== false; + return [ + createMarkerModel({ + id: "target", + type: "target", + value: targetValue, + position: targetPosition, + lane: "below", + visible: targetEnabled && targetVisible, + color: (_a = targetConfig == null ? void 0 : targetConfig.color) != null ? _a : null, + label: targetLabelPresentation != null ? targetLabelPresentation : targetPresentation, + labelVisible: targetEnabled && (targetConfig == null ? void 0 : targetConfig.show_label) === true && hasMarkerLabelContent({ text: targetConfig.label_text, showValue: targetConfig.label_show_value, showUnit: targetConfig.label_show_unit }), + shape: (_b = targetConfig == null ? void 0 : targetConfig.shape) != null ? _b : "diamond", + direction: (_c = targetConfig == null ? void 0 : targetConfig.direction) != null ? _c : "inward" + }), + createMarkerModel({ + id: "floor", + type: "floor", + value: floorValue, + position: floorPosition, + lane: "below", + visible: (floorConfig == null ? void 0 : floorConfig.show) === true && floorVisible, + color: (_d = floorConfig == null ? void 0 : floorConfig.color) != null ? _d : null, + label: floorLabelPresentation != null ? floorLabelPresentation : floorPresentation, + labelVisible: (floorConfig == null ? void 0 : floorConfig.show) === true && (floorConfig == null ? void 0 : floorConfig.show_label) === true && hasMarkerLabelContent({ text: floorConfig.label_text, showValue: floorConfig.label_show_value, showUnit: floorConfig.label_show_unit }), + shape: "triangle", + direction: (_e = floorConfig == null ? void 0 : floorConfig.direction) != null ? _e : "inward" + }), + createMarkerModel({ + id: "peak", + type: "peak", + value: peakValue, + position: peakPosition, + lane: "above", + visible: (peakConfig == null ? void 0 : peakConfig.show) === true && peakVisible, + color: (_f = peakConfig == null ? void 0 : peakConfig.color) != null ? _f : null, + label: peakLabelPresentation != null ? peakLabelPresentation : peakPresentation, + labelVisible: (peakConfig == null ? void 0 : peakConfig.show) === true && (peakConfig == null ? void 0 : peakConfig.show_label) === true && hasMarkerLabelContent({ text: peakConfig.label_text, showValue: peakConfig.label_show_value, showUnit: peakConfig.label_show_unit }), + shape: "triangle", + direction: (_g = peakConfig == null ? void 0 : peakConfig.direction) != null ? _g : "inward" + }), + ...genericMarkers.map((marker) => createMarkerModel({ + id: marker.id, + type: "generic", + value: marker.value, + position: marker.position, + lane: marker.lane, + visible: marker.visible, + color: marker.color, + label: marker.label, + labelVisible: marker.labelVisible && hasMarkerLabelContent(marker.label), + shape: marker.shape, + direction: marker.direction, + showMarker: marker.showMarker + })) + ]; + } + var MARKER_SHAPES; + var init_marker_view_model = __esm({ + "src/view-model/marker-view-model.js"() { + MARKER_SHAPES = /* @__PURE__ */ new Set(["circle", "diamond", "triangle", "chevron", "arrow", "pin"]); + } + }); + // src/view-model/row-view-model.js function getDefaultEntityIcon(stateObj, entityId = "") { var _a, _b, _c; @@ -812,21 +1655,6 @@ const range = safeMax - safeMin || 1; return Math.min(100, Math.max(0, (value - safeMin) / range * 100)); } - function formatNumericDisplay(rawVal, decimal = null) { - if (!Number.isFinite(rawVal)) return String(rawVal); - if (decimal !== null) { - return parseFloat(rawVal.toFixed(decimal)).toLocaleString(); - } - return rawVal.toLocaleString(); - } - function isTightUnit(unit) { - return ["h", "m", "s"].includes(String(unit || "").trim()); - } - function formatDisplayWithUnit(display, unit) { - if (!unit) return String(display); - const cleanUnit = String(unit); - return `${display}${isTightUnit(cleanUnit) ? "" : " "}${cleanUnit}`; - } function parseColorToRgb(color) { const value = String(color || "").trim(); if (!value) return null; @@ -888,8 +1716,8 @@ edge: percent <= 0 ? "left" : percent >= 100 ? "right" : "middle" }; } - function getPeakState(entityId, numericValue, minValue, maxValue, peaks, peakEnabled) { - if (!peakEnabled || !Number.isFinite(numericValue)) { + function getExtremumState(numericValue, minValue, maxValue, tracker, direction, enabled) { + if (!enabled) { return { value: null, percent: null, @@ -897,92 +1725,191 @@ visible: false }; } - const existingPeak = getFiniteNumber(peaks == null ? void 0 : peaks[entityId]); - const peakValue = Number.isFinite(existingPeak) ? Math.max(existingPeak, numericValue) : numericValue; + const existingValue = getFiniteNumber(tracker == null ? void 0 : tracker.value); + if (!Number.isFinite(existingValue) && !Number.isFinite(numericValue)) { + return { + value: null, + percent: null, + visible: false + }; + } + const value = Number.isFinite(existingValue) ? Number.isFinite(numericValue) ? direction === "min" ? Math.min(existingValue, numericValue) : Math.max(existingValue, numericValue) : existingValue : numericValue; return { - value: peakValue, - percent: toScalePct(peakValue, minValue, maxValue), + value, + percent: toScalePct(value, minValue, maxValue), visible: true }; } function buildRowViewModel(options) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha; const { hass, cardConfig, entityConfig, entityState, - peaks + peaks, + extrema, + previousScale } = options; void cardConfig; const entityId = (_a = entityConfig == null ? void 0 : entityConfig.entity) != null ? _a : null; const rawState = (_b = entityState == null ? void 0 : entityState.state) != null ? _b : ""; const numericValue = getFiniteNumber(rawState); const rawUnit = (_d = (_c = entityState == null ? void 0 : entityState.attributes) == null ? void 0 : _c.unit_of_measurement) != null ? _d : ""; - const displayUnit = numericValue !== null ? (_g = (_f = (_e = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _e.unit) != null ? _f : rawUnit) != null ? _g : "" : ""; - const min = getNormalizedResolvableNumericValue(hass, (_h = entityConfig == null ? void 0 : entityConfig.scale) == null ? void 0 : _h.min); - const max = getNormalizedResolvableNumericValue(hass, (_i = entityConfig == null ? void 0 : entityConfig.scale) == null ? void 0 : _i.max); - const safeMin = Number.isFinite(min) ? min : 0; - const safeMax = Number.isFinite(max) ? max : 100; + const configuredUnit = (_e = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _e.unit; + const targetUnit = (_f = configuredUnit != null ? configuredUnit : rawUnit) != null ? _f : ""; + const displayUnit = numericValue !== null ? targetUnit : ""; + const { min: safeMin, max: safeMax } = getResolvedScale(hass, entityConfig == null ? void 0 : entityConfig.scale, previousScale); const percent = numericValue !== null ? toScalePct(numericValue, safeMin, safeMax) : 0; - const displayValue = numericValue === null ? rawState : formatNumericDisplay(numericValue, (_k = (_j = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _j.decimal) != null ? _k : null); - const targetValue = ((_l = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _l.enabled) === false ? null : getNormalizedResolvableNumericValue(hass, (_m = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _m.source, safeMin, safeMax); + const decimal = (_h = (_g = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _g.decimal) != null ? _h : null; + const primaryPresentation = numericValue === null ? createTextPresentation(rawState) : createNumericPresentation(numericValue, displayUnit, decimal); + const targetValue = ((_i = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _i.enabled) === false ? null : getNormalizedResolvableNumericValue(hass, (_j = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _j.source, safeMin, safeMax); const targetPercent = targetValue !== null ? toScalePct(targetValue, safeMin, safeMax) : null; const targetVisible = targetValue !== null; - const targetDisplay = targetValue !== null ? formatDisplayWithUnit( - formatNumericDisplay(targetValue, (_o = (_n = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _n.decimal) != null ? _o : null), - (_r = (_q = (_p = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _p.unit) != null ? _q : rawUnit) != null ? _r : "" - ) : null; - const baselineValue = ((_s = entityConfig == null ? void 0 : entityConfig.baseline) == null ? void 0 : _s.enabled) === false ? null : getNormalizedResolvableNumericValue(hass, (_t = entityConfig == null ? void 0 : entityConfig.baseline) == null ? void 0 : _t.at, safeMin, safeMax); + const targetDecimal = (_l = (_k = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _k.label_decimal) != null ? _l : decimal; + const targetPresentation = targetValue !== null ? createNumericPresentation(targetValue, targetUnit, targetDecimal) : null; + const targetLabelPresentation = targetValue !== null ? createMarkerLabelPresentation(targetValue, targetUnit, (_n = (_m = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _m.label_precision) != null ? _n : targetDecimal, { + text: (_o = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _o.label_text, + showValue: (_p = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _p.label_show_value, + showUnit: (_q = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _q.label_show_unit + }) : null; + const baselineValue = ((_r = entityConfig == null ? void 0 : entityConfig.baseline) == null ? void 0 : _r.enabled) === false ? null : getNormalizedResolvableNumericValue(hass, (_s = entityConfig == null ? void 0 : entityConfig.baseline) == null ? void 0 : _s.at, safeMin, safeMax); const baselinePercent = Number.isFinite(baselineValue) ? toScalePct(baselineValue, safeMin, safeMax) : null; const baselineVisible = Number.isFinite(baselineValue); - const peakState = getPeakState( - entityId, + const legacyPeak = Number.isFinite(getFiniteNumber(peaks == null ? void 0 : peaks[entityId])) ? { value: getFiniteNumber(peaks == null ? void 0 : peaks[entityId]) } : null; + const peakState = getExtremumState( numericValue, safeMin, safeMax, - peaks, + (_t = extrema == null ? void 0 : extrema.peak) != null ? _t : legacyPeak, + "max", ((_u = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _u.show) === true ); - const peakDisplay = peakState.visible ? formatNumericDisplay(peakState.value, (_w = (_v = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _v.decimal) != null ? _w : null) : null; + const floorState = getExtremumState( + numericValue, + safeMin, + safeMax, + extrema == null ? void 0 : extrema.floor, + "min", + ((_v = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _v.show) === true + ); + const peakDecimal = (_x = (_w = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _w.label_decimal) != null ? _x : decimal; + const floorDecimal = (_z = (_y = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _y.label_decimal) != null ? _z : decimal; + const peakLabelPrecision = (_B = (_A = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _A.label_precision) != null ? _B : peakDecimal; + const floorLabelPrecision = (_D = (_C = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _C.label_precision) != null ? _D : floorDecimal; + const peakPresentation = peakState.visible ? createNumericPresentation(peakState.value, targetUnit, peakDecimal) : null; + const floorPresentation = floorState.visible ? createNumericPresentation(floorState.value, targetUnit, floorDecimal) : null; + const peakLabelPresentation = peakState.visible ? createMarkerLabelPresentation(peakState.value, targetUnit, peakLabelPrecision, { + text: (_E = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _E.label_text, + showValue: (_F = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _F.label_show_value, + showUnit: (_G = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _G.label_show_unit + }) : null; + const floorLabelPresentation = floorState.visible ? createMarkerLabelPresentation(floorState.value, targetUnit, floorLabelPrecision, { + text: (_H = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _H.label_text, + showValue: (_I = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _I.label_show_value, + showUnit: (_J = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _J.label_show_unit + }) : null; + const genericMarkers = ((_K = entityConfig == null ? void 0 : entityConfig.generic_markers) != null ? _K : []).filter((marker) => marker.accepted).map((marker) => { + var _a2, _b2, _c2, _d2, _e2; + const value = getNormalizedResolvableNumericValue(hass, marker.source, safeMin, safeMax); + const visible = Number.isFinite(value); + const markerPrecision = (_a2 = marker.label.precision) != null ? _a2 : decimal; + const labelStateObj = marker.label.entity ? (_b2 = hass == null ? void 0 : hass.states) == null ? void 0 : _b2[marker.label.entity] : null; + const rawLabelState = labelStateObj == null ? void 0 : labelStateObj.state; + const cleanLabelState = typeof rawLabelState === "string" ? rawLabelState.trim() : ""; + const usableLabelState = labelStateObj && cleanLabelState && !["unknown", "unavailable"].includes(cleanLabelState.toLowerCase()); + const labelValue = marker.label.entity ? usableLabelState ? (_c2 = getFiniteNumber(cleanLabelState)) != null ? _c2 : cleanLabelState : null : value; + const labelUnit = marker.label.entity ? usableLabelState ? (_e2 = (_d2 = labelStateObj == null ? void 0 : labelStateObj.attributes) == null ? void 0 : _d2.unit_of_measurement) != null ? _e2 : "" : "" : targetUnit; + const label = marker.label.show ? createMarkerLabelPresentation(labelValue, labelUnit, markerPrecision, { + text: marker.label.text, + showValue: marker.label.showValue, + showUnit: marker.label.showUnit + }) : null; + return { + id: marker.id, + value, + position: visible ? toScalePct(value, safeMin, safeMax) : null, + lane: marker.lane, + visible, + color: marker.color, + shape: marker.shape, + direction: marker.direction, + showMarker: marker.showMarker, + label, + labelVisible: marker.label.show + }; + }); + const markers = buildMarkerModels({ + entityConfig, + targetValue, + targetPosition: targetPercent, + targetPresentation, + targetLabelPresentation, + targetVisible, + peakValue: peakState.value, + peakPosition: peakState.percent, + peakPresentation, + peakLabelPresentation, + peakVisible: peakState.visible, + floorValue: floorState.value, + floorPosition: floorState.percent, + floorPresentation, + floorLabelPresentation, + floorVisible: floorState.visible, + genericMarkers + }); return { entityId, - name: (_z = (_y = entityConfig == null ? void 0 : entityConfig.name) != null ? _y : (_x = entityState == null ? void 0 : entityState.attributes) == null ? void 0 : _x.friendly_name) != null ? _z : entityId, - icon: (entityConfig == null ? void 0 : entityConfig.icon) === false ? false : (_C = (_B = entityConfig == null ? void 0 : entityConfig.icon) != null ? _B : (_A = entityState == null ? void 0 : entityState.attributes) == null ? void 0 : _A.icon) != null ? _C : getDefaultEntityIcon(entityState, entityId), + name: (_N = (_M = entityConfig == null ? void 0 : entityConfig.name) != null ? _M : (_L = entityState == null ? void 0 : entityState.attributes) == null ? void 0 : _L.friendly_name) != null ? _N : entityId, + icon: (entityConfig == null ? void 0 : entityConfig.icon) === false ? false : (_Q = (_P = entityConfig == null ? void 0 : entityConfig.icon) != null ? _P : (_O = entityState == null ? void 0 : entityState.attributes) == null ? void 0 : _O.icon) != null ? _Q : getDefaultEntityIcon(entityState, entityId), state: rawState, numericValue, rawUnit, - displayUnit, min: safeMin, max: safeMax, percent, - displayValue, - unit: displayUnit, - barColor: (_E = (_D = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _D.color) != null ? _E : null, - fillStyle: (_G = (_F = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _F.fill_style) != null ? _G : null, + displayValue: primaryPresentation.number, + displayUnit: primaryPresentation.unit, + primaryPresentation, + unit: primaryPresentation.unit, + barColor: (_S = (_R = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _R.color) != null ? _S : null, + fillStyle: (_U = (_T = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _T.fill_style) != null ? _U : null, target: targetValue, targetPercent, - targetDisplay, + targetDisplay: (_V = targetPresentation == null ? void 0 : targetPresentation.text) != null ? _V : null, + targetPresentation, + targetLabelPresentation, targetVisible, baseline: baselineValue, baselinePercent, baselineVisible, peak: peakState.value, peakPercent: peakState.percent, - peakDisplay, + peakDisplay: (_W = peakPresentation == null ? void 0 : peakPresentation.number) != null ? _W : null, + peakPresentation, + peakLabelPresentation, peakVisible: peakState.visible, - segments: (_I = (_H = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _H.segments) != null ? _I : null, - gradientStops: (_K = (_J = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _J.gradient_stops) != null ? _K : null, + floor: floorState.value, + floorPercent: floorState.percent, + floorDisplay: (_X = floorPresentation == null ? void 0 : floorPresentation.number) != null ? _X : null, + floorPresentation, + floorLabelPresentation, + floorVisible: floorState.visible, + markers, + markerLaneOccupancy: getMarkerLaneOccupancy(entityConfig), + markerLabelLaneOccupancy: getMarkerLabelLaneOccupancy(entityConfig), + segments: (_Z = (_Y = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _Y.segments) != null ? _Z : null, + gradientStops: (_$ = (__ = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : __.gradient_stops) != null ? _$ : null, needle: getNeedleState(entityConfig, numericValue, safeMin, safeMax, baselinePercent), classes: { - labelPosition: (_N = (_M = (_L = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _L.label) == null ? void 0 : _M.position) != null ? _N : "left", - animated: ((_O = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _O.animated) !== false + labelPosition: (_ca = (_ba = (_aa = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _aa.label) == null ? void 0 : _ba.position) != null ? _ca : "left", + animated: ((_da = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _da.animated) !== false }, attributes: { entity: entityId, - baseHeight: (_Q = (_P = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _P.height) != null ? _Q : 38, - heightExplicit: ((_R = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _R.height_explicit) === true, - barAnimated: ((_S = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _S.animated) !== false + baseHeight: (_fa = (_ea = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _ea.height) != null ? _fa : 38, + heightExplicit: ((_ga = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _ga.height_explicit) === true, + barAnimated: ((_ha = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _ha.animated) !== false } }; } @@ -990,6 +1917,8 @@ "src/view-model/row-view-model.js"() { init_resolve(); init_normalize(); + init_format(); + init_marker_view_model(); } }); @@ -1004,6 +1933,20 @@ }); // src/card/SensorBarCard.js + function getRevealTransitionDuration(previousGeometry, nextGeometry) { + if (!previousGeometry || !nextGeometry || !Number.isFinite(previousGeometry.valuePercent) || !Number.isFinite(nextGeometry.valuePercent)) { + return 600; + } + const previousBaseline = Number.isFinite(previousGeometry.baselinePercent) ? previousGeometry.baselinePercent : 0; + const nextBaseline = Number.isFinite(nextGeometry.baselinePercent) ? nextGeometry.baselinePercent : 0; + const delta = Math.max( + Math.abs(nextGeometry.valuePercent - previousGeometry.valuePercent), + Math.abs(nextBaseline - previousBaseline) + ); + const ordinaryDuration = Math.round(Math.min(600, Math.max(150, 600 - 15 * Math.max(0, delta - 5)))); + const crossesBaseline = Number.isFinite(previousGeometry.baselinePercent) && Number.isFinite(nextGeometry.baselinePercent) && (previousGeometry.valuePercent - previousGeometry.baselinePercent) * (nextGeometry.valuePercent - nextGeometry.baselinePercent) < 0; + return crossesBaseline ? Math.min(ordinaryDuration, 300) : ordinaryDuration; + } var SensorBarCard; var init_SensorBarCard = __esm({ "src/card/SensorBarCard.js"() { @@ -1011,7 +1954,10 @@ init_resolve(); init_validate(); init_row_view_model(); + init_marker_view_model(); init_dom(); + init_format(); + init_extrema(); SensorBarCard = class extends HTMLElement { static getConfigElement() { return document.createElement("sensor-bar-card-plus-editor"); @@ -1024,7 +1970,11 @@ this._diagnostics = { warnings: [], errors: [] }; this._lastDiagnosticsSignature = null; this._hass = null; - this._peaks = {}; + this._extrema = /* @__PURE__ */ new WeakMap(); + this._rowScales = /* @__PURE__ */ new WeakMap(); + this._rowGeneration = 0; + this._rowPresence = []; + this._leftModeResponsiveHistory = /* @__PURE__ */ new WeakMap(); this._rendered = false; this._resizeObserver = null; this._densityPassScheduled = false; @@ -1032,14 +1982,20 @@ this._densityPassFrame = null; this._densityPassRetries = 0; this._boundWindowResize = () => this._schedulePostLayoutDensityPass(); + this._markerHover = null; + this._boundMarkerPointerOver = (event) => this._handleMarkerPointerOver(event); + this._boundMarkerPointerOut = (event) => this._handleMarkerPointerOut(event); this._ensureBaseDom(); } connectedCallback() { window.addEventListener("resize", this._boundWindowResize, { passive: true }); + this._setupResizeObserver(); this._schedulePostLayoutDensityPass(); } disconnectedCallback() { + this._rowGeneration += 1; window.removeEventListener("resize", this._boundWindowResize); + this._clearMarkerHover(); this._disconnectResizeObserver(); if (this._densityPassFrame) { cancelAnimationFrame(this._densityPassFrame); @@ -1049,23 +2005,90 @@ this._densityPassDirty = false; } setConfig(config) { + var _a; if (!config.entities && !config.entity) { throw new Error("You must define entities or entity"); } this._rendered = false; + this._rowGeneration += 1; + const previousConfig = this._config; this._config = this.normalizeCardConfig(config); - const activeEntityIds = new Set( - (this._config.entities || []).map((entityConfig) => entityConfig.entity) - ); - for (const entityId of Object.keys(this._peaks)) { - if (!activeEntityIds.has(entityId)) { - delete this._peaks[entityId]; - } - } + this._rowScales = /* @__PURE__ */ new WeakMap(); + this._reconcileRowHistory((_a = previousConfig == null ? void 0 : previousConfig.entities) != null ? _a : []); this._diagnostics = validateNormalizedConfig(this._config); this._logDiagnostics(); this._render(); } + _reconcileRowHistory(previousRows) { + var _a, _b, _c, _d; + const canonical = (value, key = "") => { + if (Array.isArray(value)) return value.map(canonical); + if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().filter((name) => !["severity", "segment_space", "label_precision_key"].includes(name)).map((name) => [name, canonical(value[name], name)])); + if (key === "fixed") return getNumericValue(null, value); + if (/color/i.test(key) && typeof value === "string" && /^#(?:[\da-f]{3}|[\da-f]{6})$/i.test(value.trim())) { + const hex = value.trim().slice(1).toLowerCase(); + return `#${hex.length === 3 ? hex.split("").map((char) => char + char).join("") : hex}`; + } + return value; + }; + const signature = (row) => { + var _a2; + return JSON.stringify(canonical({ + entity: row.entity, + name: row.name, + icon: (_a2 = row.icon) != null ? _a2 : null, + layout: row.layout, + scale: Object.fromEntries(["min", "max"].map((key) => [key, { + ...row.scale[key], + fixed_explicit: row.scale[key].fixed_explicit !== false + }])), + bar: row.bar, + baseline: row.baseline, + formatting: row.formatting, + target_marker: row.target_marker, + peak_marker: row.peak_marker, + floor_marker: row.floor_marker, + generic_markers: row.generic_markers + })); + }; + const unmatchedOld = new Set(previousRows); + const matches = /* @__PURE__ */ new Map(); + const oldSignatures = new Map(previousRows.map((row) => [row, signature(row)])); + for (const row of this._config.entities) { + const key = signature(row); + const previous = [...unmatchedOld].find((old) => oldSignatures.get(old) === key); + if (!previous) continue; + matches.set(row, previous); + unmatchedOld.delete(previous); + } + const unmatchedNew = this._config.entities.filter((row) => !matches.has(row)); + for (const row of unmatchedNew) { + const old = [...unmatchedOld].filter((previous) => previous.entity === row.entity); + const next = unmatchedNew.filter((current) => current.entity === row.entity); + if (old.length !== 1 || next.length !== 1) continue; + matches.set(row, old[0]); + unmatchedOld.delete(old[0]); + } + const extrema = /* @__PURE__ */ new WeakMap(); + const responsive = /* @__PURE__ */ new WeakMap(); + for (const [row, previous] of matches) { + const stored = this._extrema.get(previous); + const retained = {}; + for (const key of ["peak", "floor"]) { + const before = previous[`${key}_marker`]; + const after = row[`${key}_marker`]; + if ((stored == null ? void 0 : stored[key]) && (before == null ? void 0 : before.show) === true && (after == null ? void 0 : after.show) === true && JSON.stringify(before.reset) === JSON.stringify(after.reset)) { + retained[key] = { ...stored[key] }; + } + } + if (retained.peak || retained.floor) extrema.set(row, retained); + if (((_b = (_a = previous.layout) == null ? void 0 : _a.label) == null ? void 0 : _b.position) === "left" && ((_d = (_c = row.layout) == null ? void 0 : _c.label) == null ? void 0 : _d.position) === "left" && this._leftModeResponsiveHistory.has(previous)) { + responsive.set(row, this._leftModeResponsiveHistory.get(previous)); + } + } + this._extrema = extrema; + this._leftModeResponsiveHistory = responsive; + } _logDiagnostics() { var _a; const diagnostics = (_a = this._diagnostics) != null ? _a : { warnings: [], errors: [] }; @@ -1186,12 +2209,13 @@ set hass(hass) { const oldHass = this._hass; this._hass = hass; + if (!this._config.entities) return; if (!oldHass) { this._update(); return; } if (this._shouldUpdate(oldHass, hass)) { - this._update(); + this._update(oldHass); } } // Merge global config with per-entity overrides @@ -1205,6 +2229,38 @@ name: (_g = ecfg.name) != null ? _g : null }; } + _getStateTimestamp(stateObj) { + var _a; + const rawTimestamp = (_a = stateObj == null ? void 0 : stateObj.last_updated) != null ? _a : stateObj == null ? void 0 : stateObj.last_changed; + const timestamp = rawTimestamp instanceof Date ? rawTimestamp.getTime() : Date.parse(String(rawTimestamp != null ? rawTimestamp : "")); + return Number.isFinite(timestamp) ? timestamp : Date.now(); + } + _updateExtrema(entityCfg, normalizedEntity, stateObj) { + var _a, _b, _c; + const sample = getFiniteNumber(stateObj == null ? void 0 : stateObj.state); + if (!Number.isFinite(sample)) return; + const current = (_a = this._extrema.get(entityCfg)) != null ? _a : {}; + const timestamp = this._getStateTimestamp(stateObj); + for (const key of ["peak", "floor"]) { + const marker = normalizedEntity == null ? void 0 : normalizedEntity[`${key}_marker`]; + if ((marker == null ? void 0 : marker.show) !== true) { + delete current[key]; + continue; + } + current[key] = updateExtremum( + (_b = current[key]) != null ? _b : null, + sample, + (_c = marker.reset) != null ? _c : { kind: "never" }, + key === "floor" ? "min" : "max", + timestamp + ); + } + if (current.peak || current.floor) { + this._extrema.set(entityCfg, current); + } else { + this._extrema.delete(entityCfg); + } + } _getDefaultEntityIcon(stateObj, entityId = "") { var _a, _b, _c; const deviceClass = String((_b = (_a = stateObj == null ? void 0 : stateObj.attributes) == null ? void 0 : _a.device_class) != null ? _b : "").trim(); @@ -1240,7 +2296,7 @@ return (_c = domainIcons[domain]) != null ? _c : null; } _shouldUpdate(oldHass, newHass) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k; if (!this._config || !this._config.entities) return true; for (const entityCfg of this._config.entities) { const ecfg = this._resolve(entityCfg); @@ -1249,11 +2305,15 @@ (_b = (_a = ecfg.scale) == null ? void 0 : _a.min) == null ? void 0 : _b.entity, (_d = (_c = ecfg.scale) == null ? void 0 : _c.max) == null ? void 0 : _d.entity, (_f = (_e = ecfg.baseline) == null ? void 0 : _e.at) == null ? void 0 : _f.entity, - (_h = (_g = ecfg.target_marker) == null ? void 0 : _g.source) == null ? void 0 : _h.entity + (_h = (_g = ecfg.target_marker) == null ? void 0 : _g.source) == null ? void 0 : _h.entity, + ...((_i = ecfg.generic_markers) != null ? _i : []).filter((marker) => marker.accepted).flatMap((marker) => { + var _a2, _b2; + return [(_a2 = marker.source) == null ? void 0 : _a2.entity, (_b2 = marker.label) == null ? void 0 : _b2.entity]; + }) ].filter(Boolean); for (const ent of entitiesToWatch) { - const oldState = (_i = oldHass.states[ent]) != null ? _i : null; - const newState = (_j = newHass.states[ent]) != null ? _j : null; + const oldState = (_j = oldHass.states[ent]) != null ? _j : null; + const newState = (_k = newHass.states[ent]) != null ? _k : null; if (oldState !== newState) { return true; } @@ -1318,12 +2378,80 @@ if (!this.shadowRoot) return; this.shadowRoot.querySelectorAll(".row[data-entity]").forEach((row) => { this._positionTargetLabel(row); + this._positionMarkerValueLabel(row, ".peak-value-label", ".peak-marker"); + this._positionMarkerValueLabel(row, ".floor-value-label", ".floor-marker"); + this._positionGenericMarkerLabels(row); + }); + } + _positionGenericMarkerLabels(row) { + var _a, _b; + ((_b = (_a = row.querySelectorAll) == null ? void 0 : _a.call(row, ".generic-value-label[data-marker-id]")) != null ? _b : []).forEach((label) => { + const id = label.dataset.markerId; + this._positionMarkerValueLabel( + row, + `.generic-value-label[data-marker-id="${id}"]`, + `.generic-marker[data-marker-id="${id}"]` + ); }); } _positionTargetLabel(row) { + this._positionMarkerValueLabel(row, ".target-value-label", ".target-marker"); + } + _getMarkerLabel(markerEl) { + var _a; + const row = markerEl == null ? void 0 : markerEl.closest(".row"); + if (!row) return null; + if (markerEl.matches(".generic-marker[data-marker-id]")) { + const markerId = markerEl.dataset.markerId; + return (_a = [...row.querySelectorAll(".generic-value-label[data-marker-id]")].find((label) => label.dataset.markerId === markerId)) != null ? _a : null; + } + const labelSelector = markerEl.matches(".target-marker") ? ".target-value-label" : markerEl.matches(".peak-marker") ? ".peak-value-label" : markerEl.matches(".floor-marker") ? ".floor-value-label" : null; + return labelSelector ? row.querySelector(labelSelector) : null; + } + _getGenericMarkerForLabel(labelEl) { + var _a, _b; + const row = labelEl == null ? void 0 : labelEl.closest(".row"); + const markerId = (_a = labelEl == null ? void 0 : labelEl.dataset) == null ? void 0 : _a.markerId; + if (!row || !markerId) return null; + return (_b = [...row.querySelectorAll(".generic-marker[data-marker-id]")].find((marker) => marker.dataset.markerId === markerId)) != null ? _b : null; + } + _setMarkerHover(markerEl) { + var _a; + const label = this._getMarkerLabel(markerEl); + if (!label || markerEl.style.display === "none" || getComputedStyle(label).visibility !== "visible") { + this._clearMarkerHover(markerEl); + return; + } + if (((_a = this._markerHover) == null ? void 0 : _a.label) === label) return; + this._clearMarkerHover(); + label.dataset.markerHovered = "true"; + this._markerHover = { marker: markerEl, label }; + } + _clearMarkerHover(markerEl = null) { + if (!this._markerHover || markerEl && this._markerHover.marker !== markerEl) return; + delete this._markerHover.label.dataset.markerHovered; + this._markerHover = null; + } + _handleMarkerPointerOver(event) { + var _a, _b, _c; + if (event.pointerType === "touch") return; + const target = (_b = (_a = event.target) == null ? void 0 : _a.closest) == null ? void 0 : _b.call(_a, '.generic-value-label[data-show-marker="false"], .generic-marker, .target-marker, .peak-marker, .floor-marker'); + const markerEl = ((_c = target == null ? void 0 : target.matches) == null ? void 0 : _c.call(target, ".generic-value-label")) ? this._getGenericMarkerForLabel(target) : target; + if (markerEl) this._setMarkerHover(markerEl); + } + _handleMarkerPointerOut(event) { + var _a, _b; + if (event.pointerType === "touch") return; + const target = (_b = (_a = event.target) == null ? void 0 : _a.closest) == null ? void 0 : _b.call(_a, '.generic-value-label[data-show-marker="false"], .generic-marker, .target-marker, .peak-marker, .floor-marker'); + if (!target || target.contains(event.relatedTarget)) return; + const markerEl = target.matches(".generic-value-label") ? this._getGenericMarkerForLabel(target) : target; + if (!markerEl) return; + this._clearMarkerHover(markerEl); + } + _positionMarkerValueLabel(row, labelSelector, markerSelector) { const track = row.querySelector(".bar-track"); - const label = row.querySelector(".target-value-label"); - const marker = row.querySelector(".target-marker"); + const label = row.querySelector(labelSelector); + const marker = row.querySelector(markerSelector); if (!track || !label || !marker) return; if (marker.style.display === "none" || !label.textContent.trim()) { this._setStyleIfChanged(label, "visibility", "hidden"); @@ -1472,7 +2600,7 @@ var _a, _b; const safeMin = Number.isFinite(minValue) ? minValue : 0; const safeMax = Number.isFinite(maxValue) ? maxValue : 100; - const rawSegments = Array.isArray((_a = ecfg.bar) == null ? void 0 : _a.segments) ? ecfg.bar.segments : []; + const rawSegments = (Array.isArray((_a = ecfg.bar) == null ? void 0 : _a.segments) ? ecfg.bar.segments : []).filter((segment) => !(segment == null ? void 0 : segment.invalidBoundary)); if (((_b = ecfg.bar) == null ? void 0 : _b.segment_space) === "scale" || this._segmentsNeedBoundaryResolution(rawSegments)) { const resolvedSegments = rawSegments.map((segment) => { var _a2; @@ -1602,6 +2730,9 @@ const range = safeMax - safeMin || 1; return Math.min(100, Math.max(0, (value - safeMin) / range * 100)); } + _getRevealTransitionDuration(previousGeometry, nextGeometry) { + return getRevealTransitionDuration(previousGeometry, nextGeometry); + } _resolveBaselinePct(ecfg, safeMin, safeMax) { var _a, _b; if (((_a = ecfg.baseline) == null ? void 0 : _a.enabled) === false) return null; @@ -1610,11 +2741,7 @@ return this._toScalePct(baselineValue, safeMin, safeMax); } _formatNumericDisplay(rawVal, decimal = null) { - if (!Number.isFinite(rawVal)) return String(rawVal); - if (decimal !== null) { - return parseFloat(rawVal.toFixed(decimal)).toLocaleString(); - } - return rawVal.toLocaleString(); + return formatNumericDisplay(rawVal, decimal); } _getNormalizedPercent(valuePct, baselinePct = null) { const clampedValue = Math.min(100, Math.max(0, valuePct)); @@ -1818,6 +2945,7 @@ --sbcp-value-width: 60px; --sbcp-bar-min-width: 56px; --sbcp-target-label-font-size: 12px; + --sbcp-marker-label-lane-size: 15px; --sbcp-inline-label-padding-x: 8px; --sbcp-inline-label-padding-y: 2px; --sbcp-inline-label-font-size: 12px; @@ -1882,6 +3010,13 @@ padding: 2px 4px; } .row:last-child { margin-bottom: 0; } + .row[data-marker-label-lane-below="true"]:not(:last-child) { + margin-bottom: calc(10px + max(0px, var(--sbcp-marker-label-lane-size) - 12px)); + } + /* Facing configured lanes overlap by 9px at the approved offsets; 10px leaves a 1px gap. */ + .row[data-marker-label-lane-below="true"]:has(+ .row[data-marker-label-lane-above="true"]) { + margin-bottom: calc(10px + max(0px, var(--sbcp-marker-label-lane-size) - 12px) + 10px); + } .row-stack { --sbcp-row-height: 38px; display: flex; @@ -1909,6 +3044,12 @@ .top-right-value[data-active="true"] { display: flex; } + .above-line, + .hero-line, + .top-right-value { + position: relative; + z-index: 10; + } .row:hover .bar-track { filter: brightness(0.95); transition: filter 0.15s; } .main-line { display: flex; @@ -1916,6 +3057,9 @@ gap: var(--sbcp-main-gap); min-width: 0; } + .row[data-marker-label-lane-above="true"] .main-line:not(.hero-mode) { + margin-top: var(--sbcp-target-label-font-size); + } .main-line[data-row-density="tight"] { gap: calc(var(--sbcp-main-gap) - 1px); } @@ -1944,7 +3088,8 @@ .main-line.left-mode[data-hide-left-icon="true"] .icon-wrap, .main-line.above-mode[data-hide-above-icon="true"] .icon-wrap, .main-line.inside-mode[data-hide-inside-icon="true"] .icon-wrap, - .main-line.inside-mode[data-priority-hide-inside-icon="true"] .icon-wrap { + .main-line.inside-mode[data-priority-hide-inside-icon="true"] .icon-wrap, + .main-line.off-mode[data-hide-off-icon="true"] .icon-wrap { display: none; } .main-line.left-mode[data-left-density="normal"] { @@ -1978,14 +3123,13 @@ color: var(--primary-text-color, #333); line-height: 1; } - .main-line.left-mode[data-left-density="compressed"] .icon-wrap { - display: none; - } ha-icon { --mdc-icon-size: 20px; display: block; } .label-left { + position: relative; + z-index: 10; flex: 1 1 auto; height: var(--sbcp-row-height); min-width: 0; @@ -2023,7 +3167,7 @@ position: absolute; inset: 0; pointer-events: none; - transition: clip-path 0.6s cubic-bezier(0.4,0,0.2,1); + transition: clip-path var(--sbcp-reveal-duration, 600ms) cubic-bezier(0.4,0,0.2,1); z-index: 1; } .bar-paint-layer { @@ -2038,10 +3182,31 @@ .bar-fill-reveal.no-anim { transition: none; } + .baseline-indicator { + position: absolute; + top: 0; + bottom: 0; + width: 1px; + transform: translateX(-50%); + background-color: var(--primary-text-color, currentColor); + opacity: 0.6; + pointer-events: none; + transition: left 0.6s cubic-bezier(0.4,0,0.2,1); + z-index: 3; + } .row[data-bar-animated="false"] .bar-fill-reveal, .row[data-bar-animated="false"] .needle-marker, .row[data-bar-animated="false"] .target-marker, .row[data-bar-animated="false"] .peak-marker, + .row[data-bar-animated="false"] .floor-marker, + .row[data-bar-animated="false"] .generic-marker, + .row[data-bar-animated="false"] .baseline-indicator, + .row[data-bar-animated="false"] .target-value-label, + .row[data-bar-animated="false"] .peak-value-label, + .row[data-bar-animated="false"] .floor-value-label, + .row[data-bar-animated="false"] .generic-value-label { + transition: none; + } .row[data-bar-animated="false"] .target-value-label { transition: none; } @@ -2055,7 +3220,7 @@ gap: 6px; padding: 0 6px; pointer-events: none; - z-index: 8; + z-index: 10; } .bar-inner-label[data-inside-density="compact"] { gap: 5px; @@ -2107,15 +3272,22 @@ display: none; } .bar-inner-label .inside-value { - flex: 0 1 auto; + flex: 0 0 auto; min-width: 0; - max-width: 56%; + max-width: 100%; display: inline-flex; align-items: baseline; } .bar-inner-label .inside-value[data-hide-value="true"] { display: none; } + .bar-inner-label[data-value-fit="compact"] { + padding: 0 2px; + } + .bar-inner-label .inside-value[data-value-fit="compact"] { + padding-left: 2px; + padding-right: 2px; + } .main-line.inside-mode[data-hide-inside-icon="true"] .bar-inner-label .inside-value, .main-line.inside-mode[data-priority-hide-inside-icon="true"] .bar-inner-label .inside-value, .bar-inner-label[data-hide-name="true"] .inside-value, @@ -2171,19 +3343,77 @@ .target-value-label { position: absolute; top: 100%; - margin-top: 3px; + margin-top: 2px; font-size: var(--sbcp-target-label-font-size); line-height: 1; - color: var(--secondary-text-color, #888); + color: var(--marker-color, var(--secondary-text-color, #888)); + text-shadow: + 0 0 0.6px var(--marker-contrast-color), + 0 0 1.2px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent); + background-color: var(--card-background-color, #fff); + padding: 0 2px; + border: 0; + border-radius: 2px; + box-shadow: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; box-sizing: border-box; pointer-events: none; - z-index: 6; + z-index: 8; visibility: hidden; transition: left 0.6s cubic-bezier(0.4,0,0.2,1); } + .peak-value-label, + .floor-value-label, + .generic-value-label { + position: absolute; + font-size: var(--sbcp-target-label-font-size); + line-height: 1; + color: var(--marker-color, var(--secondary-text-color, #888)); + text-shadow: + 0 0 0.6px var(--marker-contrast-color), + 0 0 1.2px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent); + background-color: var(--card-background-color, #fff); + padding: 0 2px; + border: 0; + border-radius: 2px; + box-shadow: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + box-sizing: border-box; + pointer-events: none; + z-index: 8; + visibility: hidden; + transition: left 0.6s cubic-bezier(0.4,0,0.2,1); + } + .generic-value-label[data-show-marker="false"] { + pointer-events: auto; + cursor: pointer; + } + .peak-value-label { + bottom: 100%; + margin-bottom: 1px; + } + .floor-value-label { + top: 100%; + margin-top: 2px; + } + .generic-value-label[data-lane="above"] { + bottom: 100%; + margin-bottom: 1px; + } + .generic-value-label[data-lane="below"] { + top: 100%; + margin-top: 2px; + } + .target-value-label[data-marker-hovered="true"], + .peak-value-label[data-marker-hovered="true"], + .floor-value-label[data-marker-hovered="true"], + .generic-value-label[data-marker-hovered="true"] { + z-index: 11; + } .above-line { display: grid; grid-template-columns: var(--sbcp-icon-width) minmax(0, 1fr); @@ -2217,6 +3447,10 @@ margin-bottom: 2px; min-height: 16px; } + .above-bar-label[data-hide-name="true"], + .above-bar-label[data-priority-hide-name="true"] { + gap: 0; + } .above-bar-label-name { flex: 1 1 auto; min-width: 0; @@ -2270,6 +3504,9 @@ overflow: hidden; margin-bottom: clamp(2px, calc(var(--sbcp-row-height) * 0.08), 4px); } + .row[data-marker-label-lane-above="false"] .hero-header { + margin-bottom: 0; + } .hero-header[data-hide-name="true"] .hero-label, .hero-header[data-priority-hide-name="true"] .hero-label { display: none; @@ -2374,7 +3611,7 @@ text-overflow: clip; } /* \u2500\u2500 Shared marker base \u2500\u2500 */ - .peak-marker, .target-marker { + .peak-marker, .target-marker, .floor-marker, .generic-marker { position: absolute; top: 0; bottom: 0; @@ -2385,12 +3622,46 @@ --marker-color: #888; --marker-contrast-color: #f3f4f6; } + .peak-marker .peak-inset, + .peak-marker .peak-outset, + .target-marker .target-inset, + .target-marker .target-outset, + .floor-marker .floor-inset, + .floor-marker .floor-outset, + .generic-marker .peak-inset, + .generic-marker .peak-outset, + .generic-marker .target-inset, + .generic-marker .target-outset { + pointer-events: auto; + } + .peak-marker .peak-inset:hover, + .peak-marker .peak-outset:hover, + .target-marker .target-inset:hover, + .target-marker .target-outset:hover, + .floor-marker .floor-inset:hover, + .floor-marker .floor-outset:hover, + .generic-marker .peak-inset:hover, + .generic-marker .peak-outset:hover, + .generic-marker .target-inset:hover, + .generic-marker .target-outset:hover, + .marker-shape-svg path[data-shape]:hover { + cursor: none; + } .target-marker { z-index: 6; } + .floor-marker { + z-index: 6; + } + .generic-marker[data-lane="below"] { + z-index: 6; + } .peak-marker { z-index: 7; } + .generic-marker[data-lane="above"] { + z-index: 7; + } .needle-layer { position: absolute; inset: 0; @@ -2424,7 +3695,13 @@ .peak-marker .peak-inset, .peak-marker .peak-outset, .target-marker .target-inset, - .target-marker .target-outset { + .target-marker .target-outset, + .floor-marker .floor-inset, + .floor-marker .floor-outset, + .generic-marker[data-lane="above"] .peak-inset, + .generic-marker[data-lane="above"] .peak-outset, + .generic-marker[data-lane="below"] .target-inset, + .generic-marker[data-lane="below"] .target-outset { position: absolute; left: 50%; transform: translateX(-50%); @@ -2432,7 +3709,8 @@ height: 0; } /* Peak marker: large triangle intrudes into the bar, small one sits just above it. */ - .peak-marker .peak-inset { + .peak-marker .peak-inset, + .generic-marker[data-lane="above"] .peak-inset { top: 0; border-left: 7px solid transparent; border-right: 7px solid transparent; @@ -2442,7 +3720,14 @@ drop-shadow(0 0 1.2px var(--marker-contrast-color)) drop-shadow(0 0 3px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent)); } - .peak-marker .peak-outset { + .peak-marker[data-direction="outward"] .peak-inset, + .generic-marker[data-lane="above"][data-direction="outward"] .peak-inset { + border-top-width: 0; + border-top-color: transparent; + border-bottom: 11px solid var(--marker-color); + } + .peak-marker .peak-outset, + .generic-marker[data-lane="above"] .peak-outset { top: -4px; border-left: 5px solid transparent; border-right: 5px solid transparent; @@ -2450,7 +3735,8 @@ z-index: 3; } /* Target marker: large triangle intrudes into the bar, small one sits just below it. */ - .target-marker .target-inset { + .target-marker .target-inset, + .generic-marker[data-lane="below"] .target-inset { bottom: 0; border-left: 7px solid transparent; border-right: 7px solid transparent; @@ -2460,15 +3746,144 @@ drop-shadow(0 0 1.2px var(--marker-contrast-color)) drop-shadow(0 0 3px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent)); } - .target-marker .target-outset { + .target-marker .target-outset, + .generic-marker[data-lane="below"] .target-outset { bottom: -4px; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 4px solid var(--marker-color); z-index: 3; } + .floor-marker .floor-inset { + bottom: 0; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 11px solid var(--marker-color); + z-index: 2; + filter: + drop-shadow(0 0 1.2px var(--marker-contrast-color)) + drop-shadow(0 0 3px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent)); + } + .target-marker[data-direction="outward"] .target-inset, + .floor-marker[data-direction="outward"] .floor-inset, + .generic-marker[data-lane="below"][data-direction="outward"] .target-inset { + border-bottom-width: 0; + border-bottom-color: transparent; + border-top: 11px solid var(--marker-color); + } + .floor-marker .floor-outset { + bottom: -4px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 4px solid var(--marker-color); + z-index: 3; + } + /* Shared non-triangle marker shapes. Triangle keeps the original CSS geometry. */ + .marker-shape-svg { + display: none; + position: absolute; + left: 50%; + width: 16px; + height: 16px; + overflow: visible; + color: var(--marker-color); + pointer-events: none; + z-index: 2; + transform: translateX(-50%); + filter: + drop-shadow(0 0 1.2px var(--marker-contrast-color)) + drop-shadow(0 0 3px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent)); + } + .peak-marker .marker-shape-svg { + top: 0; + } + .target-marker .marker-shape-svg { + bottom: 0; + } + .floor-marker .marker-shape-svg { + bottom: 0; + } + .generic-marker[data-lane="above"] .marker-shape-svg { + top: 0; + } + .generic-marker[data-lane="below"] .marker-shape-svg { + bottom: 0; + } + .marker-shape-svg[data-lane="above"] { + transform-origin: 50% 0; + } + .marker-shape-svg[data-lane="below"] { + transform-origin: 50% 100%; + } + .marker-shape-svg[data-shape="diamond"], + .marker-shape-svg[data-shape="arrow"], + .marker-shape-svg[data-shape="chevron"], + .marker-shape-svg[data-shape="pin"] { + transform: translateX(-50%) scale(0.75); + } + .marker-shape-svg[data-shape="circle"] { + transform: translateX(-50%) scale(0.64); + } + .peak-marker[data-shape]:not([data-shape="triangle"]) .peak-inset, + .peak-marker[data-shape]:not([data-shape="triangle"]) .peak-outset, + .target-marker[data-shape]:not([data-shape="triangle"]) .target-inset, + .target-marker[data-shape]:not([data-shape="triangle"]) .target-outset, + .floor-marker[data-shape]:not([data-shape="triangle"]) .floor-inset, + .floor-marker[data-shape]:not([data-shape="triangle"]) .floor-outset, + .generic-marker[data-shape]:not([data-shape="triangle"]) .peak-inset, + .generic-marker[data-shape]:not([data-shape="triangle"]) .peak-outset, + .generic-marker[data-shape]:not([data-shape="triangle"]) .target-inset, + .generic-marker[data-shape]:not([data-shape="triangle"]) .target-outset { + display: none; + } + .peak-marker[data-shape]:not([data-shape="triangle"]) .marker-shape-svg, + .target-marker[data-shape]:not([data-shape="triangle"]) .marker-shape-svg { + display: block; + } + .floor-marker[data-shape]:not([data-shape="triangle"]) .marker-shape-svg { + display: block; + } + .generic-marker[data-shape]:not([data-shape="triangle"]) .marker-shape-svg { + display: block; + } + .generic-marker[data-show-marker="false"][data-shape] .peak-inset, + .generic-marker[data-show-marker="false"][data-shape] .peak-outset, + .generic-marker[data-show-marker="false"][data-shape] .target-inset, + .generic-marker[data-show-marker="false"][data-shape] .target-outset, + .generic-marker[data-show-marker="false"][data-shape] .marker-shape-svg { + display: none; + } + .marker-shape-svg path { + display: none; + fill: currentColor; + } + .marker-shape-svg path[data-shape] { + pointer-events: visiblePainted; + } + .marker-shape-svg[data-shape="circle"] path[data-shape="circle"], + .marker-shape-svg[data-shape="diamond"] path[data-shape="diamond"], + .marker-shape-svg[data-shape="chevron"] path[data-shape="chevron"], + .marker-shape-svg[data-shape="arrow"] path[data-shape="arrow"], + .marker-shape-svg[data-shape="pin"] path[data-shape="pin"] { + display: block; + } + .marker-shape-svg[data-shape="chevron"] path[data-shape="chevron"] { + fill: none; + } + .marker-shape-svg[data-shape="chevron"][data-lane="below"][data-direction="inward"] .marker-shape-paths, + .marker-shape-svg[data-shape="arrow"][data-lane="below"][data-direction="inward"] .marker-shape-paths, + .marker-shape-svg[data-shape="pin"][data-lane="below"][data-direction="inward"] .marker-shape-paths, + .marker-shape-svg[data-shape="chevron"][data-lane="above"][data-direction="outward"] .marker-shape-paths, + .marker-shape-svg[data-shape="arrow"][data-lane="above"][data-direction="outward"] .marker-shape-paths, + .marker-shape-svg[data-shape="pin"][data-lane="above"][data-direction="outward"] .marker-shape-paths { + transform-box: view-box; + transform-origin: 0 0; + transform: translateY(16px) scaleY(-1); + } .value-right { + position: relative; + z-index: 10; --sbcp-value-extra-width: 0px; flex: 0 0 calc(var(--sbcp-value-width) + var(--sbcp-value-extra-width)); width: calc(var(--sbcp-value-width) + var(--sbcp-value-extra-width)); @@ -2499,6 +3914,9 @@ overflow: hidden; white-space: nowrap; } + .main-line.off-mode .value-right { + flex-shrink: 1; + } .value-right-text.has-unit { gap: 2px; } @@ -2556,6 +3974,8 @@ `; + this.shadowRoot.addEventListener("pointerover", this._boundMarkerPointerOver); + this.shadowRoot.addEventListener("pointerout", this._boundMarkerPointerOut); this._baseDomReady = true; } _render() { @@ -2571,20 +3991,24 @@ titleEl.style.display = "none"; } } - this._disconnectResizeObserver(); - this._resizeObserver = new ResizeObserver(() => { + this._setupResizeObserver(); + this._update(); + this._schedulePostLayoutDensityPass(); + } + _setupResizeObserver() { + if (!this.isConnected || this._resizeObserver) return; + const surface = this.shadowRoot.querySelector("ha-card"); + const card = this.shadowRoot.querySelector(".card"); + if (!surface || !card) return; + const observer = new ResizeObserver(() => { + if (!this.isConnected || this._resizeObserver !== observer) return; this._applyCompactTier(); this._schedulePostLayoutDensityPass(); }); - const surface = this.shadowRoot.querySelector("ha-card"); - const card = this.shadowRoot.querySelector(".card"); - if (surface && card) { - this._applyCompactTier(); - this._resizeObserver.observe(surface); - this._resizeObserver.observe(this); - } - this._update(); - this._schedulePostLayoutDensityPass(); + this._resizeObserver = observer; + this._applyCompactTier(); + observer.observe(surface); + observer.observe(this); } _disconnectResizeObserver() { if (!this._resizeObserver) return; @@ -2612,13 +4036,17 @@ if (width < 360) return "compact"; return "normal"; } - _classifyLeftDensity(width, currentDensity = "normal") { + _classifyLeftDensity(width, currentDensity = "normal", naturalLabelWidth = Number.POSITIVE_INFINITY) { if (!this._isReliableWidth(width)) return currentDensity || "normal"; - if (width < 170) return "compressed"; - if (width < 210) return "dense"; - if (width < 255) return "tight"; - if (width < 320) return "compact"; - return "normal"; + const densities = ["normal", "compact", "tight", "dense", "compressed"]; + let density = "normal"; + if (width < 170) density = "compressed"; + else if (width < 210) density = "dense"; + else if (width < 255) density = "tight"; + else if (width < 320) density = "compact"; + const currentIndex = densities.indexOf(density); + const relaxBy = Number.isFinite(naturalLabelWidth) ? naturalLabelWidth <= 44 && width >= 205 ? 2 : naturalLabelWidth <= 72 && width >= 185 ? 1 : 0 : 0; + return densities[Math.max(0, currentIndex - relaxBy)]; } _classifyRowDensity(width, currentDensity = "normal") { if (!this._isReliableWidth(width)) return currentDensity || "normal"; @@ -2674,7 +4102,6 @@ } _applyLeftModeDensity() { if (!this.shadowRoot) return; - const densities = ["normal", "compact", "tight", "dense", "compressed"]; this.shadowRoot.querySelectorAll(".main-line.left-mode").forEach((mainLine) => { const width = mainLine.getBoundingClientRect().width; if (!this._isReliableWidth(width)) { @@ -2682,21 +4109,10 @@ if (!mainLine.dataset.leftDensity) mainLine.dataset.leftDensity = "normal"; return; } - let density = this._classifyLeftDensity(width, mainLine.dataset.leftDensity); const labelText = mainLine.querySelector(".label-left-text"); - const fullLabelWidth = labelText ? labelText.scrollWidth : Number.POSITIVE_INFINITY; - const visibleLabelWidth = labelText ? labelText.clientWidth : Number.POSITIVE_INFINITY; - const labelIsTruncated = labelText ? fullLabelWidth > visibleLabelWidth + 1 : false; - const effectiveLabelWidth = labelIsTruncated ? visibleLabelWidth : fullLabelWidth; - let relaxBy = 0; - if (Number.isFinite(effectiveLabelWidth)) { - if (effectiveLabelWidth <= 72 && width >= 185) relaxBy = 1; - if (effectiveLabelWidth <= 44 && width >= 205) relaxBy = 2; - } - const currentIndex = densities.indexOf(density); - if (currentIndex !== -1 && relaxBy > 0) { - density = densities[Math.max(0, currentIndex - relaxBy)]; - } + const text = ((labelText == null ? void 0 : labelText.textContent) || "").trim(); + const naturalLabelWidth = labelText ? this._measureTextWidthWithStyles(labelText, text) || labelText.scrollWidth : Number.POSITIVE_INFINITY; + const density = this._classifyLeftDensity(width, mainLine.dataset.leftDensity, naturalLabelWidth); mainLine.dataset.leftDensity = density; }); } @@ -2709,70 +4125,78 @@ const nameEl = innerLabel.querySelector(".inside-name"); const valueEl = innerLabel.querySelector(".inside-value"); if (!track || !nameEl || !valueEl) return; - const trackWidth = track.getBoundingClientRect().width; - const valueDisplay = this._decodeDataAttr(valueEl.dataset.display || valueEl.textContent || ""); - const valueUnit = this._decodeDataAttr(valueEl.dataset.unit || ((_a = valueEl.querySelector(".inside-unit")) == null ? void 0 : _a.textContent) || ""); - const valueWidth = this._measureInsideValueMarkupWidth(valueEl, valueDisplay, valueUnit, false); - const valueOnlyWidth = this._measureInsideValueMarkupWidth(valueEl, valueDisplay, valueUnit, true); - let density = this._classifyInsideDensity(trackWidth, valueWidth); - const rowWidth = typeof (mainLine == null ? void 0 : mainLine.getBoundingClientRect) === "function" ? mainLine.getBoundingClientRect().width : 0; - const rowDensity = this._isReliableWidth(rowWidth) ? this._classifyRowDensity(rowWidth, ((_b = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _b.rowDensity) || "normal") : ((_c = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _c.rowDensity) || "normal"; - const iconWrap = (_e = (_d = mainLine == null ? void 0 : mainLine.querySelector) == null ? void 0 : _d.call(mainLine, ".icon-wrap")) != null ? _e : null; - let hideIcon = rowDensity === "dense" || rowDensity === "compressed"; - const reclaimedWidth = iconWrap ? this._getLeftModeIconWidth(iconWrap, mainLine) + this._getLeftModeGap(mainLine) : 0; - if (!hideIcon && valueWidth > this._getInsideValueVisibleCap(trackWidth, density)) { - hideIcon = true; - } - if (iconWrap && hideIcon) { - density = this._classifyInsideDensity(trackWidth + reclaimedWidth, valueWidth); - } - const effectiveTrackWidth = trackWidth + (hideIcon ? reclaimedWidth : 0); - let hideName = false; - if (rowDensity === "compressed") { - density = "compressed"; - hideIcon = true; - } - const rawValueCap = this._getInsideValueVisibleCap(trackWidth, density); - const innerPadding = this._getNumericStyleValue(innerLabel, "padding-left", 0) + this._getNumericStyleValue(innerLabel, "padding-right", 0); - const valueCap = Math.max(0, rawValueCap - innerPadding); - const hideUnit = !!valueUnit && valueWidth > valueCap; - const hideValue = valueOnlyWidth > valueCap; - const reservedValueWidth = hideValue ? 0 : Math.ceil(hideUnit ? valueOnlyWidth : valueWidth); - const labelGap = !hideValue ? this._getNumericStyleValue(innerLabel, "gap", 0) : 0; - if (!hideValue) { - const nameText = (nameEl.textContent || "").trim(); - const nameFullWidth = nameEl.scrollWidth || ((_f = nameEl.getBoundingClientRect) == null ? void 0 : _f.call(nameEl).width) || this._measureTextWidthWithStyles(nameEl, nameText); - if (!nameText || !Number.isFinite(nameFullWidth) || nameFullWidth <= 0) { - hideName = density === "dense" || density === "compressed"; - } else { - const usefulNameWidth = this._getInsideUsefulNameWidth(nameEl, nameText, nameFullWidth); - const availableNameWidth = Math.max( - 0, - trackWidth - innerPadding - reservedValueWidth - labelGap - ); + const display = this._decodeDataAttr(valueEl.dataset.display || valueEl.textContent || ""); + const unit = this._decodeDataAttr(valueEl.dataset.unit || ""); + valueEl.dataset.valueFit = "normal"; + innerLabel.dataset.valueFit = "normal"; + const fullWidth = this._measureInsideValueMarkupWidth(valueEl, display, unit, false); + const numberWidth = this._measureInsideValueMarkupWidth(valueEl, display, unit, true); + const rowWidth = (_b = (_a = mainLine == null ? void 0 : mainLine.getBoundingClientRect) == null ? void 0 : _a.call(mainLine).width) != null ? _b : 0; + const rowDensity = this._isReliableWidth(rowWidth) ? this._classifyRowDensity(rowWidth, (_c = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _c.rowDensity) : ((_d = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _d.rowDensity) || "normal"; + const iconWrap = (_f = (_e = mainLine == null ? void 0 : mainLine.querySelector) == null ? void 0 : _e.call(mainLine, ".icon-wrap")) != null ? _f : null; + const iconReserve = iconWrap ? this._getLeftModeIconWidth(iconWrap, mainLine) + this._getLeftModeGap(mainLine) : 0; + const minimum = this._getLeftModeBarMinWidth(mainLine); + const withIconWidth = this._isReliableWidth(rowWidth) ? Math.max(minimum, rowWidth - iconReserve) : track.getBoundingClientRect().width; + const withoutIconWidth = this._isReliableWidth(rowWidth) ? Math.max(minimum, rowWidth) : withIconWidth + iconReserve; + const nameText = (nameEl.textContent || "").trim(); + const nameWidth = this._measureTextWidthWithStyles(nameEl, nameText) || nameEl.scrollWidth; + const candidate = (trackWidth) => { + const density = rowDensity === "compressed" ? "compressed" : this._classifyInsideDensity(trackWidth, fullWidth); + innerLabel.dataset.insideDensity = density; + innerLabel.dataset.valueFit = "normal"; + valueEl.dataset.valueFit = "normal"; + const padding = this._getNumericStyleValue(innerLabel, "padding-left", 0) + this._getNumericStyleValue(innerLabel, "padding-right", 0); + let cap = Math.max(0, trackWidth - padding); + const hideUnit = !!unit && fullWidth > cap; + let readingWidth = hideUnit ? numberWidth : fullWidth; + let valueFit = "normal"; + if (readingWidth > cap) { + valueFit = "compact"; + innerLabel.dataset.valueFit = valueFit; + valueEl.dataset.valueFit = valueFit; + cap = Math.max(0, trackWidth - this._getNumericStyleValue(innerLabel, "padding-left", 0) - this._getNumericStyleValue(innerLabel, "padding-right", 0)); + readingWidth = this._measureInsideValueMarkupWidth(valueEl, display, unit, true); + } + const hideValue = readingWidth > cap; + const gap = hideValue ? 0 : this._getNumericStyleValue(innerLabel, "gap", 0); + const nameShare = density === "compact" ? 0.56 : density === "tight" ? 0.48 : 0.6; + const availableNameWidth = Math.max( + 0, + Math.min(cap * nameShare, cap - (hideValue ? 0 : Math.ceil(readingWidth)) - gap) + ); + let hideName = density === "dense" || density === "compressed"; + if (nameText && nameWidth > 0) { + const usefulWidth = this._getInsideUsefulNameWidth(nameEl, nameText, nameWidth); const visibleChars = this._measureVisibleLabelCharacters(nameEl, nameText, availableNameWidth); - const minUsefulChars = Math.min(4, nameText.length); - const nameNeedsTruncation = nameFullWidth > availableNameWidth + 1; - const hasUsefulRoom = availableNameWidth >= usefulNameWidth && (!nameNeedsTruncation || visibleChars >= minUsefulChars); - hideName = !hasUsefulRoom; + hideName = availableNameWidth < usefulWidth || nameWidth > availableNameWidth + 1 && visibleChars < Math.min(4, nameText.length); + } + return { + density, + valueFit, + hideUnit, + hideValue, + hideName, + rank: hideValue ? 3 : valueFit === "compact" ? 2 : hideUnit ? 1 : 0 + }; + }; + let hideIcon = rowDensity === "dense" || rowDensity === "compressed"; + let chosen = candidate(hideIcon ? withoutIconWidth : withIconWidth); + if (iconWrap && !hideIcon) { + const withoutIcon = candidate(withoutIconWidth); + if (withoutIcon.rank < chosen.rank || withoutIcon.rank === chosen.rank && chosen.hideName && !withoutIcon.hideName) { + hideIcon = true; + chosen = withoutIcon; } } - if (hideName && density === "normal") density = "dense"; - innerLabel.dataset.insideDensity = density; - innerLabel.dataset.hideName = hideName ? "true" : "false"; - valueEl.dataset.hideUnit = hideUnit ? "true" : "false"; - valueEl.dataset.hideValue = hideValue ? "true" : "false"; - if (mainLine) { - mainLine.dataset.hideInsideIcon = hideIcon ? "true" : "false"; - } + innerLabel.dataset.insideDensity = chosen.density; + innerLabel.dataset.valueFit = chosen.valueFit; + innerLabel.dataset.hideName = chosen.hideName ? "true" : "false"; + valueEl.dataset.valueFit = chosen.valueFit; + valueEl.dataset.hideUnit = chosen.hideUnit ? "true" : "false"; + valueEl.dataset.hideValue = chosen.hideValue ? "true" : "false"; + if (mainLine) mainLine.dataset.hideInsideIcon = hideIcon ? "true" : "false"; }); } - _getInsideValueVisibleCap(trackWidth, density) { - if (density === "dense" || density === "compressed") { - return trackWidth; - } - return trackWidth * 0.56; - } _classifyInsideDensity(trackWidth, valueWidth) { if (trackWidth < Math.max(72, valueWidth + 12)) return "compressed"; if (trackWidth < valueWidth + 56) return "dense"; @@ -2795,7 +4219,7 @@ _applyAboveLabelDensity() { if (!this.shadowRoot) return; this.shadowRoot.querySelectorAll(".above-line, .hero-line").forEach((aboveLine) => { - var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o; const label = aboveLine.querySelector(".above-bar-label, .hero-header"); if (!label) return; const isHeroLine = aboveLine.classList.contains("hero-line"); @@ -2830,31 +4254,41 @@ const rowStack = (_k = aboveLine.closest) == null ? void 0 : _k.call(aboveLine, ".row-stack"); const lineWidth = (_o = (_n = (_l = rowStack == null ? void 0 : rowStack.getBoundingClientRect) == null ? void 0 : _l.call(rowStack).width) != null ? _n : (_m = aboveLine.getBoundingClientRect) == null ? void 0 : _m.call(aboveLine).width) != null ? _o : width; const spacerEl = typeof aboveLine.querySelector === "function" ? aboveLine.querySelector(".above-icon-spacer") : null; - const spacerWidth = (_q = (_p = spacerEl == null ? void 0 : spacerEl.getBoundingClientRect) == null ? void 0 : _p.call(spacerEl).width) != null ? _q : 0; + const spacerWidth = mainIconVisible ? iconRect.width : 0; const lineGap = spacerEl ? this._getNumericStyleValue(aboveLine, "gap", 0) : 0; - const nameWidth = (_t = (_s = (_r = labelText.getBoundingClientRect) == null ? void 0 : _r.call(labelText).width) != null ? _s : labelText.clientWidth) != null ? _t : 0; - const labelGap = this._getNumericStyleValue(label, "gap", this._getLeftModeGap(aboveLine)); + const labelGap = this._getNumericStyleValue(aboveLine, "--sbcp-main-gap", this._getLeftModeGap(aboveLine)); const fullValueWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, false) + 2); const valueOnlyWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, true) + 2); const text = (labelText.textContent || "").trim(); - const visibleWidth = labelText.clientWidth; - const fullWidth = labelText.scrollWidth; - const visibleChars = this._measureVisibleLabelCharacters(labelText, text, visibleWidth); - const labelIsUnhelpful = this._shouldHideLeftLabel(text, fullWidth, visibleWidth, visibleChars); const spacerReserve = mainIconVisible && spacerWidth > 0 ? spacerWidth + lineGap : 0; - const availableWithName = Math.max(0, lineWidth - spacerReserve - nameWidth - labelGap); + const nameWidth = this._measureTextWidthWithStyles(labelText, text) || labelText.scrollWidth || 0; + const withNameBudget = Math.max(0, lineWidth - spacerReserve - labelGap); + const fitsWithName = (valueWidth, budget = withNameBudget) => { + const visibleWidth = Math.max(0, budget - valueWidth); + const visibleChars = this._measureVisibleLabelCharacters(labelText, text, visibleWidth); + return !this._shouldHideLeftLabel(text, nameWidth, visibleWidth, visibleChars); + }; const availableValueOnly = Math.max(0, lineWidth); - hideName = labelIsUnhelpful; - if (!hideName && fullValueWidth <= availableWithName) { + let hideUnit = false; + if (fullValueWidth <= withNameBudget && fitsWithName(fullValueWidth)) { + hideName = false; hideSpacer = !mainIconVisible; - } else if (!hideName && valueOnlyWidth <= availableWithName) { + } else if (fullValueWidth <= availableValueOnly) { + hideName = !fitsWithName(fullValueWidth, lineWidth - labelGap); + hideSpacer = true; + } else if (valueOnlyWidth <= withNameBudget && fitsWithName(valueOnlyWidth)) { + hideName = false; + hideUnit = !!unit; hideSpacer = !mainIconVisible; + } else if (fitsWithName(valueOnlyWidth, lineWidth - labelGap)) { + hideName = false; + hideSpacer = true; + hideUnit = !!unit; } else { hideName = true; hideSpacer = true; + hideUnit = !!unit && fullValueWidth > availableValueOnly; } - const availableWidth = hideName ? availableValueOnly : availableWithName; - const hideUnit = !!unit && fullValueWidth > availableWidth; valueEl.dataset.hideUnit = hideUnit ? "true" : "false"; } else if (valueEl) { valueEl.dataset.hideUnit = "false"; @@ -2981,6 +4415,26 @@ if (valueWidth("normal", false) <= availableWithoutLabel) return; } if (hasUnit) { + for (const fit of ["tight", "minimum"]) { + const readingWidth = valueWidth(fit, false); + if (readingWidth <= availableWithoutLabel) { + heroLine.dataset.heroValueFit = fit; + if (!labelHiddenByDensity && labelEl) { + const text = (labelEl.textContent || "").trim(); + const nameWidth = this._measureTextWidthWithStyles(labelEl, text); + const availableName = Math.max(0, availableWithLabel - readingWidth); + if (!this._shouldHideLeftLabel( + text, + nameWidth, + availableName, + this._measureVisibleLabelCharacters(labelEl, text, availableName) + )) { + delete headerEl.dataset.priorityHideName; + } + } + return; + } + } heroLine.dataset.hideHeroUnit = "true"; if (valueWidth("normal", true) <= availableWithoutLabel) return; } @@ -3019,17 +4473,13 @@ clone.style.textOverflow = "clip"; clone.style.whiteSpace = "nowrap"; clone.innerHTML = this._formatInsideValueMarkup(display, unit, hideUnit); - layer.replaceChildren(clone); - let extraWidth = 0; - try { - const style = getComputedStyle(valueEl); - extraWidth += parseFloat(style.paddingLeft || "0") || 0; - extraWidth += parseFloat(style.paddingRight || "0") || 0; - extraWidth += parseFloat(style.borderLeftWidth || "0") || 0; - extraWidth += parseFloat(style.borderRightWidth || "0") || 0; - } catch (_err) { - } - return clone.getBoundingClientRect().width + extraWidth; + clone.removeAttribute("data-hide-value"); + const wrapper = document.createElement("div"); + wrapper.className = "bar-inner-label"; + wrapper.style.cssText = "position:static;display:block;padding:0"; + wrapper.appendChild(clone); + layer.replaceChildren(wrapper); + return clone.getBoundingClientRect().width; } _measureTextWidthWithStyles(sourceEl, text) { var _a; @@ -3082,7 +4532,7 @@ _applyValueWidthReservation() { if (!this.shadowRoot) return; this.shadowRoot.querySelectorAll(".value-right").forEach((valueEl) => { - var _a, _b, _c; + var _a, _b, _c, _d, _e; const display = this._decodeDataAttr(valueEl.dataset.display || ""); const unit = this._decodeDataAttr(valueEl.dataset.unit || ""); if (!display) { @@ -3093,7 +4543,29 @@ if (!getStyle) return; const style = getStyle(valueEl); const baseWidth = parseFloat(style.getPropertyValue("--sbcp-value-width")) || valueEl.clientWidth || 0; - const desiredWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, false) + 2); + const fullWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, false) + 2); + const mainLine = valueEl.closest(".main-line"); + let desiredWidth = fullWidth; + if (mainLine == null ? void 0 : mainLine.classList.contains("off-mode")) { + const barWrap = mainLine.querySelector(".bar-wrap"); + const iconWrap = mainLine.querySelector(".icon-wrap"); + mainLine.dataset.hideOffIcon = "false"; + const gap = parseFloat(getStyle(mainLine).gap) || 0; + const rowWidth = (_e = (_d = mainLine.getBoundingClientRect) == null ? void 0 : _d.call(mainLine).width) != null ? _e : 0; + const minimumRail = parseFloat(getStyle(barWrap).minWidth) || 0; + const iconVisible = iconWrap && getStyle(iconWrap).display !== "none"; + const iconWidth = iconVisible ? this._getLeftModeIconWidth(iconWrap, mainLine) : 0; + const withoutIcon = Math.max(0, rowWidth - minimumRail - gap); + const withIcon = withoutIcon - (iconVisible ? iconWidth + gap : 0); + const numberWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, true) + 2); + const hideIcon = iconVisible && (fullWidth > withIcon && fullWidth <= withoutIcon || numberWidth > withIcon && numberWidth <= withoutIcon); + mainLine.dataset.hideOffIcon = hideIcon ? "true" : "false"; + const availableWidth = hideIcon ? withoutIcon : withIcon; + if (availableWidth > 0) { + const readableWidth = fullWidth <= availableWidth ? fullWidth : numberWidth; + desiredWidth = Math.min(readableWidth, availableWidth); + } + } const extraWidth = Math.max(0, desiredWidth - baseWidth); valueEl.style.setProperty("--sbcp-value-extra-width", `${extraWidth}px`); }); @@ -3197,8 +4669,27 @@ const fullMarkupWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, false) + 2); return Math.max(reservedWidth, fullMarkupWidth); } + _getStableLeftLabelMetrics(row, rowWidth = null) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const mainLine = row == null ? void 0 : row.querySelector(".main-line"); + const labelWrap = row == null ? void 0 : row.querySelector(".label-left"); + const labelText = row == null ? void 0 : row.querySelector(".label-left-text"); + if (!mainLine || !labelWrap || !labelText) return null; + const width = (_b = rowWidth != null ? rowWidth : (_a = mainLine.getBoundingClientRect) == null ? void 0 : _a.call(mainLine).width) != null ? _b : 0; + const text = (labelText.textContent || "").trim(); + const naturalWidth = this._measureTextWidthWithStyles(labelText, text) || labelText.scrollWidth || 0; + const density = ((_c = mainLine.dataset) == null ? void 0 : _c.leftDensity) || "normal"; + const labelShares = { normal: 0.25, compact: 0.22, tight: 0.19, dense: 0.16, compressed: 0.14 }; + const entityConfig = (_e = (_d = this._config) == null ? void 0 : _d.entities) == null ? void 0 : _e.find((item) => { + var _a2; + return item.entity === ((_a2 = row.dataset) == null ? void 0 : _a2.entity); + }); + const configuredWidth = entityConfig ? (_i = (_h = (_g = (_f = this._resolve(entityConfig)) == null ? void 0 : _f.layout) == null ? void 0 : _g.label) == null ? void 0 : _h.width) != null ? _i : 100 : 100; + const maximumWidth = width > 0 ? Math.min(configuredWidth, width * ((_j = labelShares[density]) != null ? _j : labelShares.normal)) : Math.min(naturalWidth, configuredWidth); + return { text, naturalWidth, maximumWidth, labelWidth: maximumWidth, labelText }; + } _estimateLeftModeWidthBudget(row) { - var _a, _b, _c, _d, _e, _f; + var _a, _b, _c; const mainLine = row == null ? void 0 : row.querySelector(".main-line"); if (!mainLine) return null; const rowWidth = (_b = (_a = mainLine.getBoundingClientRect) == null ? void 0 : _a.call(mainLine).width) != null ? _b : 0; @@ -3206,19 +4697,38 @@ const labelWrap = row.querySelector(".label-left"); const iconWrap = row.querySelector(".icon-wrap"); const valueEl = row.querySelector(".value-right"); - const labelMetrics = this._getLabelSacrificeMetrics(row, "left", { rowWidth }); - const labelWidth = ((_c = labelWrap == null ? void 0 : labelWrap.dataset) == null ? void 0 : _c.hidden) === "true" ? 0 : (_f = (_e = (_d = labelWrap == null ? void 0 : labelWrap.getBoundingClientRect) == null ? void 0 : _d.call(labelWrap).width) != null ? _e : labelMetrics == null ? void 0 : labelMetrics.labelWidth) != null ? _f : 0; + const labelMetrics = this._getStableLeftLabelMetrics(row, rowWidth); + const labelWidth = (_c = labelMetrics == null ? void 0 : labelMetrics.labelWidth) != null ? _c : 0; const iconWidth = iconWrap ? this._getLeftModeIconWidth(iconWrap, mainLine) : 0; const valueWidth = this._getReservedInlineValueWidth(valueEl); const gap = this._getLeftModeGap(mainLine); const barMinWidth = this._getLeftModeBarMinWidth(mainLine); - const baseLabelVisible = !!labelWrap && labelWrap.dataset.hidden !== "true"; - const labelSacrificial = baseLabelVisible ? this._isLabelWorthSacrificing(row, "left", { rowWidth }) : true; + const baseLabelVisible = !!labelWrap; const hasIcon = !!iconWrap; + const labelGapCount = hasIcon ? 2 : 1; + const labelAvailableWidth = Math.max( + 0, + rowWidth - (hasIcon ? iconWidth : 0) - barMinWidth - labelGapCount * gap + ); + const candidateLabelWidth = Math.min(labelWidth, labelAvailableWidth); + const labelSacrificial = !labelMetrics || this._shouldHideLeftLabel( + labelMetrics.text, + labelMetrics.naturalWidth, + candidateLabelWidth, + this._measureVisibleLabelCharacters(labelMetrics.labelText, labelMetrics.text, candidateLabelWidth) + ); + const withoutIconLabelWidth = Math.min(labelWidth, Math.max(0, rowWidth - barMinWidth - gap)); + const labelSacrificialWithoutIcon = !labelMetrics || this._shouldHideLeftLabel( + labelMetrics.text, + labelMetrics.naturalWidth, + withoutIconLabelWidth, + this._measureVisibleLabelCharacters(labelMetrics.labelText, labelMetrics.text, withoutIconLabelWidth) + ); return { rowWidth, gap, barMinWidth, + labelSacrificialWithoutIcon, labelWidth: this._isReliableWidth(labelWidth, 0) ? labelWidth : 0, iconWidth: this._isReliableWidth(iconWidth, 0) ? iconWidth : 0, valueWidth: this._isReliableWidth(valueWidth, 0) ? valueWidth : 0, @@ -3226,7 +4736,6 @@ labelSacrificial, hasIcon, mainLine, - labelWrap, iconWrap, valueEl, rowStack: row.querySelector(".row-stack") @@ -3241,7 +4750,9 @@ const visibleItems = 1 + (showIcon ? 1 : 0) + (showLabel ? 1 : 0) + (showInlineValue ? 1 : 0); const gapCount = Math.max(0, visibleItems - 1); const reservedWidth = (showIcon ? effectiveBudget.iconWidth : 0) + (showLabel ? effectiveBudget.labelWidth : 0) + (showInlineValue ? effectiveBudget.valueWidth : 0) + gapCount * effectiveBudget.gap; - const predictedBarWidth = Math.max(0, effectiveBudget.rowWidth - reservedWidth); + const remainingWidth = Math.max(0, effectiveBudget.rowWidth - reservedWidth); + const fits = remainingWidth >= effectiveBudget.barMinWidth; + const predictedBarWidth = fits ? remainingWidth : effectiveBudget.barMinWidth; return { rowWidth: effectiveBudget.rowWidth, barWidth: predictedBarWidth, @@ -3250,31 +4761,31 @@ showIcon, showInlineValue, reservedWidth, - gapCount + gapCount, + fits }; } _getLeftModeCandidateStates(budget) { - const states = [ - { hideLabel: false, topValue: false, hideIcon: false } - ]; - if (budget == null ? void 0 : budget.labelSacrificial) { - states.push({ hideLabel: true, topValue: false, hideIcon: false }); + const states = []; + if (budget && !budget.labelSacrificial) { + states.push( + { hideLabel: false, topValue: false, hideIcon: false }, + { hideLabel: false, topValue: true, hideIcon: false } + ); + } + if (budget && !budget.labelSacrificialWithoutIcon) { + states.push( + { hideLabel: false, topValue: false, hideIcon: true }, + { hideLabel: false, topValue: true, hideIcon: true } + ); } states.push( - { hideLabel: false, topValue: true, hideIcon: false }, - { hideLabel: true, topValue: true, hideIcon: false }, + { hideLabel: true, topValue: false, hideIcon: false }, + { hideLabel: true, topValue: false, hideIcon: true }, { hideLabel: true, topValue: true, hideIcon: true } ); return states; } - _chooseFirstPredictedLeftModeState(row, states, threshold, budget) { - for (const state of states) { - const predicted = this._predictLeftModeBarShareForState(row, state, budget); - if (!predicted) continue; - if (predicted.share >= threshold) return { ...state, predicted }; - } - return null; - } _chooseFallbackPredictedLeftModeState(row, states, budget) { let fallback = null; for (const state of states) { @@ -3284,27 +4795,32 @@ } return fallback; } - _chooseLeftModeResponsiveState(row) { - var _a, _b; + _chooseLeftModeResponsiveState(row, settled = true) { + var _a, _b, _c, _d; const budget = this._estimateLeftModeWidthBudget(row); if (!budget) return null; const minimumBarShare = this._getMinimumBarShare(); const states = this._getLeftModeCandidateStates(budget); - const previousTopValue = ((_b = (_a = budget.rowStack) == null ? void 0 : _a.dataset) == null ? void 0 : _b.forceTopValue) === "true"; - const inlineStates = states.filter((state) => !state.topValue); + const entityCfg = (_b = this._config.entities) == null ? void 0 : _b[Number((_a = row == null ? void 0 : row.dataset) == null ? void 0 : _a.rowIndex)]; + const previousTopValue = entityCfg && this._leftModeResponsiveHistory.has(entityCfg) ? this._leftModeResponsiveHistory.get(entityCfg) : ((_d = (_c = budget.rowStack) == null ? void 0 : _c.dataset) == null ? void 0 : _d.forceTopValue) === "true"; const topStates = states.filter((state) => state.topValue); const enableShare = this._getTopValueEnableShare(); const disableShare = this._getTopValueDisableShare(); - const inlineChoice = previousTopValue ? this._chooseFirstPredictedLeftModeState(row, inlineStates, disableShare, budget) : this._chooseFirstPredictedLeftModeState(row, inlineStates, enableShare, budget); - if (inlineChoice) return inlineChoice; - const topChoice = this._chooseFirstPredictedLeftModeState(row, topStates, minimumBarShare, budget) || this._chooseFallbackPredictedLeftModeState(row, topStates, budget); - return topChoice; + for (const state of states) { + const threshold = state.topValue || settled ? minimumBarShare : previousTopValue ? disableShare : enableShare; + const predicted = this._predictLeftModeBarShareForState(row, state, budget); + if ((predicted == null ? void 0 : predicted.fits) && predicted.share >= threshold) return { ...state, predicted }; + } + return this._chooseFallbackPredictedLeftModeState(row, topStates, budget); } _applyLeftModeResponsiveState(row, state) { + var _a, _b; const mainLine = row == null ? void 0 : row.querySelector(".main-line"); const rowStack = row == null ? void 0 : row.querySelector(".row-stack"); const leftLabel = row == null ? void 0 : row.querySelector(".label-left"); if (!mainLine || !rowStack) return; + const entityCfg = (_b = this._config.entities) == null ? void 0 : _b[Number((_a = row == null ? void 0 : row.dataset) == null ? void 0 : _a.rowIndex)]; + if (entityCfg) this._leftModeResponsiveHistory.set(entityCfg, (state == null ? void 0 : state.topValue) === true); delete rowStack.dataset.forceTopValue; delete mainLine.dataset.hideLeftIcon; if (leftLabel) delete leftLabel.dataset.priorityHidden; @@ -3423,7 +4939,7 @@ if (!metrics || !metrics.text) return false; return this._shouldHideLeftLabel(metrics.text, metrics.fullWidth, metrics.visibleWidth, metrics.visibleChars); } - _ensureMinimumBarShare(rows = null) { + _ensureMinimumBarShare(rows = null, leftWidths = null) { if (!this.shadowRoot) return; const targetRows = rows || this.shadowRoot.querySelectorAll(".row[data-entity]"); const minimumBarShare = this._getMinimumBarShare(); @@ -3433,8 +4949,10 @@ const mode = mainLine.classList.contains("left-mode") ? "left" : mainLine.classList.contains("above-mode") ? "above" : mainLine.classList.contains("inside-mode") ? "inside" : "other"; if (mode === "other") return; if (mode === "left") { - const state = this._chooseLeftModeResponsiveState(row); + const settled = !leftWidths || leftWidths.get(mainLine) === mainLine.getBoundingClientRect().width; + const state = this._chooseLeftModeResponsiveState(row, settled); if (state) this._applyLeftModeResponsiveState(row, state); + if (!settled) this._schedulePostLayoutDensityPass(); return; } this._clearMinimumBarShareOverrides(row); @@ -3525,44 +5043,61 @@ _applyLeftLabelUsefulness() { if (!this.shadowRoot) return; this.shadowRoot.querySelectorAll(".main-line.left-mode").forEach((mainLine) => { + var _a; const labelWrap = mainLine.querySelector(".label-left"); const labelText = mainLine.querySelector(".label-left-text"); if (!labelWrap || !labelText) return; - labelWrap.dataset.hidden = "false"; - const text = (labelText.textContent || "").trim(); - const fullWidth = labelText.scrollWidth; - const visibleWidth = labelText.clientWidth; - const visibleChars = this._measureVisibleLabelCharacters(labelText, text, visibleWidth); - const shouldHide = this._shouldHideLeftLabel(text, fullWidth, visibleWidth, visibleChars); - labelWrap.dataset.hidden = shouldHide ? "true" : "false"; + const row = ((_a = mainLine.closest) == null ? void 0 : _a.call(mainLine, ".row")) || { + dataset: {}, + querySelector: (selector) => selector === ".main-line" ? mainLine : selector === ".label-left" ? labelWrap : selector === ".label-left-text" ? labelText : null + }; + const metrics = this._getStableLeftLabelMetrics(row); + if (!metrics) return; + const visibleChars = this._measureVisibleLabelCharacters(metrics.labelText, metrics.text, metrics.labelWidth); + labelWrap.dataset.hidden = this._shouldHideLeftLabel( + metrics.text, + metrics.naturalWidth, + metrics.labelWidth, + visibleChars + ) ? "true" : "false"; }); } _runPostLayoutPasses(rows = null) { + const generation = this._rowGeneration; requestAnimationFrame(() => { + var _a; + if (!this.isConnected || generation !== this._rowGeneration) return; this._applyRowDensity(); this._applyLeftModeDensity(); this._applyAboveLabelDensity(); this._applyHeroValueFit(); this._applyInsideLabelDensity(); this._applyValueWidthReservation(); + const leftWidths = new Map( + [...((_a = this.shadowRoot) == null ? void 0 : _a.querySelectorAll(".main-line.left-mode")) || []].map((mainLine) => [mainLine, mainLine.getBoundingClientRect().width]) + ); requestAnimationFrame(() => { - var _a; + var _a2; + if (!this.isConnected || generation !== this._rowGeneration) return; this._applyAdaptiveRowHeight(); this._applyValueVisibility(); this._applyLeftLabelUsefulness(); this._applyTopRightValueLayout(); - this._ensureMinimumBarShare(rows); + this._ensureMinimumBarShare(rows, leftWidths); this._applyTopRightValueLayout(); this._applyLeftLabelUsefulness(); - const targetRows = rows || ((_a = this.shadowRoot) == null ? void 0 : _a.querySelectorAll(".row[data-entity]")) || []; + const targetRows = rows || ((_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelectorAll(".row[data-entity]")) || []; targetRows.forEach((row) => { this._positionTargetLabel(row); + this._positionMarkerValueLabel(row, ".peak-value-label", ".peak-marker"); + this._positionMarkerValueLabel(row, ".floor-value-label", ".floor-marker"); + this._positionGenericMarkerLabels(row); }); }); }); } _isTightUnit(unit) { - return ["h", "m", "s"].includes(String(unit || "").trim()); + return isTightUnit(unit); } _encodeDataAttr(value) { return encodeURIComponent(String(value != null ? value : "")); @@ -3635,6 +5170,14 @@ const contrastS = Math.max(40, Math.min(100, s)); return `hsl(${Math.round(h)} ${Math.round(contrastS)}% ${Math.round(contrastL)}%)`; } + _getEffectiveMarkerColor(marker) { + var _a; + return (_a = marker == null ? void 0 : marker.color) != null ? _a : "#888888"; + } + _getMarkerLabelColorStyle(marker) { + const color = this._getEffectiveMarkerColor(marker); + return `--marker-color:${color};--marker-contrast-color:${this._getMarkerContrastColor(color)};`; + } _getNeedleBorderColor(color) { const rgb = this._parseColorToRgb(color); if (!rgb) return "#000000"; @@ -3646,9 +5189,7 @@ return luminance < 0.22 ? "#ffffff" : "#000000"; } _formatDisplayWithUnit(display, unit) { - if (!unit) return String(display); - const cleanUnit = String(unit); - return `${display}${this._isTightUnit(cleanUnit) ? "" : " "}${cleanUnit}`; + return formatDisplayWithUnit(display, unit); } _formatRightValueMarkup(display, unit, hideUnit = false) { const escapedDisplay = escapeHtml(display); @@ -3672,21 +5213,146 @@ const unitModeClass = this._isTightUnit(cleanUnit) ? "tight-unit" : "has-unit"; return `${escapedDisplay}${escapedUnit}`; } - _buildRow(entityCfg, stateDisplay, unit, pct, color, peakPct, peakDisplay, targetPct, targetDisplay, peakColor, targetColor, minValue, maxValue) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u; - const ecfg = this._resolve(entityCfg); - const stateObj = (_c = (_b = (_a = this._hass) == null ? void 0 : _a.states) == null ? void 0 : _b[entityCfg.entity]) != null ? _c : null; - const rowViewModel = stateObj ? buildRowViewModel({ + _getRowMarkerModels(rowViewModel, ecfg, peakPct, peakDisplay, targetPct, targetDisplay, peakColor, targetColor) { + if (rowViewModel == null ? void 0 : rowViewModel.markers) { + return rowViewModel.markers.map((marker) => { + var _a, _b; + if (marker.type === "target") { + return { + ...marker, + position: targetPct === void 0 ? marker.position : targetPct, + visible: targetPct !== null && targetPct !== void 0, + color: targetColor || marker.color, + label: (_a = marker.label) != null ? _a : targetDisplay === null ? null : { text: targetDisplay } + }; + } + if (marker.type === "floor") { + return marker; + } + if (marker.type !== "peak") return marker; + return { + ...marker, + position: peakPct === void 0 ? marker.position : peakPct, + visible: peakPct !== null && peakPct !== void 0 && ecfg.peak_marker.show === true, + color: peakColor || marker.color, + label: (_b = marker.label) != null ? _b : peakDisplay === null ? null : { text: peakDisplay } + }; + }); + } + const markers = buildMarkerModels({ + entityConfig: ecfg, + targetPosition: targetPct, + targetPresentation: targetDisplay === null ? null : { text: targetDisplay }, + targetVisible: Number.isFinite(targetPct), + peakPosition: peakPct, + peakPresentation: peakDisplay === null ? null : { number: peakDisplay }, + peakVisible: Number.isFinite(peakPct) + }); + const targetMarker = this._getMarkerModel(markers, "target"); + const peakMarker = this._getMarkerModel(markers, "peak"); + if (targetMarker && targetColor) targetMarker.color = targetColor; + if (peakMarker && peakColor) peakMarker.color = peakColor; + return markers; + } + _getMarkerModel(markers, type) { + var _a; + return (_a = markers.find((marker) => marker.id === type || marker.type === type)) != null ? _a : null; + } + _renderMarker(marker) { + var _a, _b, _c, _d, _e, _f, _g; + if (!marker) return ""; + const position = Number.isFinite(marker.position) ? marker.position : 0; + const color = this._getEffectiveMarkerColor(marker); + const contrastColor = this._getMarkerContrastColor(color); + const display = marker.visible ? "" : "none"; + const defaultShape = marker.type === "target" ? "diamond" : marker.type === "generic" ? "circle" : "triangle"; + const shape = normalizeMarkerShape(marker.shape, defaultShape); + const lane = (_a = marker.lane) != null ? _a : marker.type === "peak" ? "above" : "below"; + const shapePaths = ` + + + + + + `; + if (marker.type === "generic") { + const triangleClasses = lane === "above" ? ["peak-inset", "peak-outset"] : ["target-inset", "target-outset"]; + return ` +