commit 5f014117807eab0c342eb381e96c28178a813c8f Author: Florian Krebs Date: Sat May 2 14:24:53 2026 +0200 chore: initial baseline before refactoring Initial snapshot of Home Assistant config (HAOS 2026.4.4) prior to dashboard restructuring and helper YAML migration. Pre-commit cleanup applied: - Removed blueprints/switch_manager/ (9.4 MB stale, component already gone) - Removed .storage/switch_manager - Removed home-assistant.log.{1,old,fault} - Removed zigbee2mqtt/configuration_backup_v{1..4}.yaml - Removed zigbee2mqtt/database.db.tmp.2024-09-27 (1.5y old) - Created empty themes/ to satisfy configuration.yaml include .gitignore uses allowlist strategy for .storage/ to keep all tokens, keys, and PINs out of version control. diff --git a/.HA_VERSION b/.HA_VERSION new file mode 100644 index 0000000..881a774 --- /dev/null +++ b/.HA_VERSION @@ -0,0 +1 @@ +2026.4.4 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65a4238 --- /dev/null +++ b/.gitignore @@ -0,0 +1,149 @@ +# ============================================================ +# Home Assistant Config Repository – .gitignore +# ============================================================ +# Strategy: Default-deny for sensitive areas. +# .storage/* uses an explicit allowlist for files we DO want versioned. +# ============================================================ + +# === Secrets (Top-Level) === +secrets.yaml +*.key +*.pem +*.crt +known_devices.yaml +.uuid +*credentials.json +ps5-mqtt/credentials.json + +# === Datenbanken & Logs === +home-assistant_v2.db* +home-assistant.log* +*.log +*.log.* +.ha_run.lock + +# === Caches & Runtime === +tts/ +image/ +deps/ +.cloud/ +.cache/ +__pycache__/ +*.pyc +OZW_Log.txt + +# === HACS / Frontend Community Cards === +www/community/ + +# === Custom Components – haben eigene Repos === +custom_components/ + +# === Z2M-Runtime (enthält Network Keys → SECRETS!) === +zigbee2mqtt/log/ +zigbee2mqtt/database.db +zigbee2mqtt/database.db.tmp.* +zigbee2mqtt/state.json +zigbee2mqtt/coordinator_backup.json +zigbee2mqtt/device_icons/ + +# === ESPHome Build Artefakte === +esphome/.esphome/ +esphome/.pioenvs/ +esphome/.piolibdeps/ +esphome/secrets.yaml + +# === AppDaemon Runtime === +appdaemon/logs/ +appdaemon/.cache/ + +# === Backups (HAOS Backups, manuelle Snapshots) === +backup/ +backups/ +*.tar +*.tar.gz + +# === Editor / OS === +.DS_Store +Thumbs.db +*.swp +*.swo +*~ +.vscode/ +.idea/ + +# === Claude Code projekt-lokale Settings === +.claude/settings.local.json +.claude/file-history/ +.claude/sessions/ + +# ============================================================ +# .storage/ – ALLOWLIST-STRATEGIE +# Default: alles ignorieren. Dann gezielt whitelisten. +# ============================================================ +.storage/* +.storage/** + +# --- Verzeichnis selbst zulassen (sonst wirken die Whitelists nicht) --- +!.storage/ + +# --- Whitelist: Dashboards & Lovelace (für Refactoring) --- +!.storage/lovelace +!.storage/lovelace.* +!.storage/lovelace_dashboards +!.storage/lovelace_resources +!.storage/map + +# --- Whitelist: Helper (für YAML-Migration) --- +!.storage/input_boolean +!.storage/input_button +!.storage/input_datetime +!.storage/input_number +!.storage/input_select +!.storage/input_text +!.storage/timer +!.storage/counter +!.storage/schedule +!.storage/scheduler.storage + +# --- Whitelist: Registries (Areas/Floors/Labels — sicher, keine Tokens) --- +!.storage/core.area_registry +!.storage/core.floor_registry +!.storage/core.label_registry +!.storage/core.category_registry + +# --- Whitelist: Energy-Dashboard-Konfig --- +!.storage/energy + +# --- Whitelist: Frontend-Panels & exposed_entities (UI-Struktur) --- +!.storage/frontend_panels +!.storage/homeassistant.exposed_entities + +# --- Whitelist: Powercalc Group-Definitionen (Konfig, kein Secret) --- +!.storage/powercalc_group + +# ============================================================ +# WICHTIG – diese .storage-Files NIEMALS tracken (Tokens/Keys!): +# core.config_entries → 28 OAuth-Tokens und API-Keys +# core.device_registry → MACs, Seriennummern (Privacy) +# core.entity_registry → groß, ändert sich ständig (Noise) +# core.restore_state → Runtime-State, ändert sich sekündlich +# core.config → Längengrad/Breitengrad (Privacy) +# alarmo.storage → Alarm-PINs +# androidtv_adbkey* → RSA Private Keys +# esphome.* → ESPHome Verschlüsselungs-Keys +# mobile_app → Push-Tokens +# tapo_control_* → Tapo Auth +# unifi_data → Unifi Auth +# roborock/ → Roborock Auth +# home_agent.* → AI-Memories (Privacy) +# waste_collection_schedule.device_keys → API-Keys +# nibe_energy_conversion_data_* → Werte (Daten, kein Config) +# bluetooth.* → Bluetooth-Geräte-Liste +# homekit_controller* → HomeKit-Pairing-Daten +# hassio → Supervisor-Daten +# trace.saved_traces → Trace-Logs +# hacs.* → HACS-Cache +# zone → Heim-Adresse/Koordinaten +# assist_pipeline.pipelines → Cloud-API-Konfig +# Bewusst NICHT in der Whitelist oben. +# ============================================================ diff --git a/.storage/core.area_registry b/.storage/core.area_registry new file mode 100644 index 0000000..023602e --- /dev/null +++ b/.storage/core.area_registry @@ -0,0 +1,256 @@ +{ + "version": 1, + "minor_version": 9, + "key": "core.area_registry", + "data": { + "areas": [ + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": null, + "icon": "hue:room-bathroom", + "id": "badezimmer", + "labels": [], + "name": "Badezimmer", + "picture": null, + "temperature_entity_id": null, + "created_at": "2024-09-09T13:04:18.389639+00:00", + "modified_at": "2025-08-17T07:32:57.160038+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": "sensor.buro_rauchmelder_humidity", + "icon": "hue:room-office", + "id": "buro", + "labels": [], + "name": "Büro", + "picture": null, + "temperature_entity_id": "sensor.buro_rauchmelder_temperature", + "created_at": "2024-09-08T10:46:56.377433+00:00", + "modified_at": "2025-08-17T07:35:11.809131+00:00" + }, + { + "aliases": [], + "floor_id": "aussen", + "humidity_entity_id": null, + "icon": "hue:room-front-door", + "id": "eingang", + "labels": [], + "name": "Eingang", + "picture": null, + "temperature_entity_id": null, + "created_at": "2025-03-03T15:42:03.555438+00:00", + "modified_at": "2025-08-17T07:35:00.782090+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": null, + "icon": "hue:room-dining", + "id": "esszimmer", + "labels": [], + "name": "Esszimmer", + "picture": null, + "temperature_entity_id": null, + "created_at": "2024-09-11T08:18:34.627509+00:00", + "modified_at": "2025-08-17T07:32:40.006548+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": null, + "icon": "hue:room-other", + "id": "gang", + "labels": [], + "name": "Gang", + "picture": null, + "temperature_entity_id": null, + "created_at": "2024-09-09T13:31:35.850691+00:00", + "modified_at": "2025-08-17T07:32:30.684112+00:00" + }, + { + "aliases": [], + "floor_id": "aussen", + "humidity_entity_id": null, + "icon": "hue:room-carport", + "id": "garage", + "labels": [], + "name": "Garage", + "picture": null, + "temperature_entity_id": null, + "created_at": "2025-08-05T05:47:11.358780+00:00", + "modified_at": "2025-08-05T05:47:11.358784+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": null, + "icon": "hue:room-hallway", + "id": "gaderobe", + "labels": [], + "name": "Garderobe", + "picture": null, + "temperature_entity_id": null, + "created_at": "2024-09-09T13:18:10.361439+00:00", + "modified_at": "2025-08-17T07:31:31.671046+00:00" + }, + { + "aliases": [], + "floor_id": "aussen", + "humidity_entity_id": null, + "icon": "hue:room-bbq", + "id": "grillplatz", + "labels": [], + "name": "Grillplatz", + "picture": null, + "temperature_entity_id": null, + "created_at": "2025-08-20T17:18:48.577020+00:00", + "modified_at": "2025-08-20T17:18:48.577024+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": "sensor.gastezimmer_rauchmelder_humidity", + "icon": "hue:room-guestroom", + "id": "gastezimmer", + "labels": [], + "name": "Gästezimmer", + "picture": null, + "temperature_entity_id": "sensor.gastezimmer_rauchmelder_temperature", + "created_at": "2024-11-02T08:09:33.172900+00:00", + "modified_at": "2025-08-17T07:30:05.628546+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": "sensor.kinderzimmer_rauchmelder_humidity", + "icon": "hue:room-nursery", + "id": "kinderzimmer", + "labels": [], + "name": "Kinderzimmer", + "picture": null, + "temperature_entity_id": "sensor.kinderzimmer_rauchmelder_temperature", + "created_at": "2024-09-09T12:55:13.852546+00:00", + "modified_at": "2025-08-17T07:33:16.734722+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": null, + "icon": "hue:room-kitchen", + "id": "kuche", + "labels": [], + "name": "Küche", + "picture": null, + "temperature_entity_id": null, + "created_at": "2024-09-08T10:30:15.825070+00:00", + "modified_at": "2025-08-17T07:33:37.166858+00:00" + }, + { + "aliases": [], + "floor_id": "aussen", + "humidity_entity_id": null, + "icon": "mdi:spa", + "id": "ruheplatz", + "labels": [], + "name": "Ruheplatz", + "picture": null, + "temperature_entity_id": null, + "created_at": "2025-08-05T05:45:12.026858+00:00", + "modified_at": "2025-08-05T05:46:07.550591+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": "sensor.schlafzimmer_rauchmelder_humidity", + "icon": "hue:room-bedroom", + "id": "schlafzimmer", + "labels": [], + "name": "Schlafzimmer", + "picture": null, + "temperature_entity_id": "sensor.schlafzimmer_rauchmelder_temperature", + "created_at": "2024-09-08T10:30:15.825093+00:00", + "modified_at": "2025-08-17T07:33:44.481038+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": null, + "icon": "hue:room-closet", + "id": "speis", + "labels": [], + "name": "Speis", + "picture": null, + "temperature_entity_id": null, + "created_at": "2024-09-09T13:17:30.602321+00:00", + "modified_at": "2025-08-17T07:35:26.021395+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": "sensor.technikraum_rauchmelder_humidity", + "icon": "hue:room-garage", + "id": "technikraum", + "labels": [], + "name": "Technikraum", + "picture": null, + "temperature_entity_id": "sensor.technikraum_rauchmelder_temperature", + "created_at": "2024-09-08T10:48:15.405832+00:00", + "modified_at": "2025-08-17T07:34:09.996450+00:00" + }, + { + "aliases": [], + "floor_id": "aussen", + "humidity_entity_id": null, + "icon": "hue:room-porch", + "id": "terrasse", + "labels": [], + "name": "Terrasse", + "picture": null, + "temperature_entity_id": null, + "created_at": "2024-12-14T11:33:19.621992+00:00", + "modified_at": "2025-08-17T07:35:55.398434+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": null, + "icon": "hue:room-laundry", + "id": "wc", + "labels": [], + "name": "WC", + "picture": null, + "temperature_entity_id": null, + "created_at": "2024-09-09T13:06:41.472709+00:00", + "modified_at": "2025-08-17T07:34:36.025375+00:00" + }, + { + "aliases": [], + "floor_id": "erdgeschoss", + "humidity_entity_id": "sensor.wohnzimmer_rauchmelder_humidity", + "icon": "hue:room-living", + "id": "wohnzimmer", + "labels": [], + "name": "Wohnzimmer", + "picture": null, + "temperature_entity_id": "sensor.wohnzimmer_rauchmelder_temperature", + "created_at": "2024-09-08T10:30:15.824868+00:00", + "modified_at": "2025-08-17T07:34:49.227873+00:00" + }, + { + "aliases": [], + "floor_id": "aussen", + "humidity_entity_id": null, + "icon": "mdi:cannabis", + "id": "grow", + "labels": [], + "name": "Grow", + "picture": null, + "temperature_entity_id": null, + "created_at": "2026-04-13T06:45:41.915906+00:00", + "modified_at": "2026-04-13T06:45:41.915909+00:00" + } + ] + } +} \ No newline at end of file diff --git a/.storage/core.category_registry b/.storage/core.category_registry new file mode 100644 index 0000000..95473dc --- /dev/null +++ b/.storage/core.category_registry @@ -0,0 +1,46 @@ +{ + "version": 1, + "minor_version": 2, + "key": "core.category_registry", + "data": { + "categories": { + "automation": [ + { + "category_id": "01J99PMV3M2YG3FNGEH4XH7YHW", + "created_at": "2024-10-03T17:25:36.500542+00:00", + "icon": null, + "modified_at": "2024-10-03T17:25:36.500547+00:00", + "name": "Licht" + }, + { + "category_id": "01J99PNV8CSZW3EV091RMEJ8EZ", + "created_at": "2024-10-03T17:26:09.420792+00:00", + "icon": "mdi:timer", + "modified_at": "2024-10-03T17:26:09.420797+00:00", + "name": "Zeitsteuerung" + }, + { + "category_id": "01J99PPYR7R7QZJ24BS979KM8R", + "created_at": "2024-10-03T17:26:45.767138+00:00", + "icon": null, + "modified_at": "2024-10-03T17:26:45.767145+00:00", + "name": "Aqara H1 (decoupled and wireless)" + }, + { + "category_id": "01J99PQJM732WF5JC2NVVYAZDE", + "created_at": "2024-10-03T17:27:06.119785+00:00", + "icon": null, + "modified_at": "2024-10-03T17:27:06.119791+00:00", + "name": "Notifications" + }, + { + "category_id": "01K2VBACGMBJA0THY3YK7CM1GG", + "created_at": "2025-08-17T06:26:24.916078+00:00", + "icon": "mdi:hot-tub", + "modified_at": "2025-08-17T06:26:24.916081+00:00", + "name": "Whirlpool" + } + ] + } + } +} \ No newline at end of file diff --git a/.storage/core.floor_registry b/.storage/core.floor_registry new file mode 100644 index 0000000..14d154c --- /dev/null +++ b/.storage/core.floor_registry @@ -0,0 +1,27 @@ +{ + "version": 1, + "minor_version": 3, + "key": "core.floor_registry", + "data": { + "floors": [ + { + "aliases": [], + "floor_id": "erdgeschoss", + "icon": null, + "level": 0, + "name": "Erdgeschoss", + "created_at": "2024-09-14T09:36:39.934040+00:00", + "modified_at": "2024-09-14T09:36:39.934045+00:00" + }, + { + "aliases": [], + "floor_id": "aussen", + "icon": "hue:room-outdoors", + "level": null, + "name": "Außen", + "created_at": "2025-08-05T05:46:07.342525+00:00", + "modified_at": "2025-08-05T05:46:07.342526+00:00" + } + ] + } +} \ No newline at end of file diff --git a/.storage/core.label_registry b/.storage/core.label_registry new file mode 100644 index 0000000..b73b500 --- /dev/null +++ b/.storage/core.label_registry @@ -0,0 +1,63 @@ +{ + "version": 1, + "minor_version": 2, + "key": "core.label_registry", + "data": { + "labels": [ + { + "color": "deep-orange", + "description": null, + "icon": "mdi:smoke-detector", + "label_id": "rauchmelder", + "name": "Rauchmelder", + "created_at": "2024-11-26T16:26:08.211237+00:00", + "modified_at": "2024-11-26T16:26:08.211242+00:00" + }, + { + "color": "blue-grey", + "description": null, + "icon": "mdi:window-shutter", + "label_id": "rolladen", + "name": "Rolladen", + "created_at": "2024-12-07T10:49:37.543488+00:00", + "modified_at": "2024-12-07T10:49:37.543493+00:00" + }, + { + "color": "cyan", + "description": null, + "icon": "hue:friends-of-hue-senic", + "label_id": "lichtschalter", + "name": "Lichtschalter", + "created_at": "2024-12-07T10:52:02.771486+00:00", + "modified_at": "2024-12-07T10:52:02.771491+00:00" + }, + { + "color": "amber", + "description": null, + "icon": "mdi:home-lightbulb-outline", + "label_id": "leuchte", + "name": "Leuchte", + "created_at": "2024-12-07T10:54:12.326880+00:00", + "modified_at": "2024-12-07T10:54:12.326885+00:00" + }, + { + "color": "lime", + "description": null, + "icon": "mdi:motion-sensor", + "label_id": "bewegungsmelder", + "name": "Bewegungsmelder", + "created_at": "2024-12-15T16:13:00.488482+00:00", + "modified_at": "2024-12-15T16:13:10.814935+00:00" + }, + { + "color": null, + "description": null, + "icon": null, + "label_id": "seasonal", + "name": "Seasonal", + "created_at": "2025-12-21T10:06:40.104210+00:00", + "modified_at": "2025-12-21T10:06:40.104212+00:00" + } + ] + } +} \ No newline at end of file diff --git a/.storage/counter b/.storage/counter new file mode 100644 index 0000000..4d2cd7c --- /dev/null +++ b/.storage/counter @@ -0,0 +1,8 @@ +{ + "version": 1, + "minor_version": 1, + "key": "counter", + "data": { + "items": [] + } +} \ No newline at end of file diff --git a/.storage/energy b/.storage/energy new file mode 100644 index 0000000..1513f21 --- /dev/null +++ b/.storage/energy @@ -0,0 +1,98 @@ +{ + "version": 1, + "minor_version": 3, + "key": "energy", + "data": { + "energy_sources": [ + { + "type": "grid", + "cost_adjustment_day": 0.0, + "stat_energy_from": "sensor.tibber_pulse_zuhause_zahlerstand_verbrauch", + "stat_cost": null, + "entity_energy_price": "sensor.zuhause_strompreis", + "number_energy_price": null, + "stat_energy_to": "sensor.tibber_pulse_zuhause_zahlerstand_einspeisung", + "stat_compensation": null, + "entity_energy_price_export": null, + "number_energy_price_export": 0.0695, + "stat_rate": "sensor.tibber_pulse_zuhause_leistung" + }, + { + "type": "solar", + "stat_energy_from": "sensor.powerocean_hj37zdh5zg5w0109_solarertrag_heute", + "config_entry_solar_forecast": [ + "01J8CDN8M8JEDRF4BA6ENG0KQ7" + ], + "stat_rate": "sensor.powerocean_mpptpv_pwrtotal" + }, + { + "type": "battery", + "stat_energy_from": "sensor.battery_hj3azdh5zg3g0384_batterie_kumulierte_entladung", + "stat_energy_to": "sensor.battery_hj3azdh5zg3g0384_batterie_kumulierte_ladung", + "power_config": { + "stat_rate": "sensor.battery_hj3azdh5zg3g0384_batterieleistung" + }, + "stat_rate": "sensor.battery_hj3azdh5zg3g0384_batterieleistung" + }, + { + "type": "battery", + "stat_energy_from": "sensor.battery_hj3azdh5zg3g0490_batterie_kumulierte_entladung", + "stat_energy_to": "sensor.battery_hj3azdh5zg3g0490_batterie_kumulierte_ladung", + "power_config": { + "stat_rate": "sensor.battery_hj3azdh5zg3g0490_batterieleistung" + }, + "stat_rate": "sensor.battery_hj3azdh5zg3g0490_batterieleistung" + } + ], + "device_consumption": [ + { + "stat_consumption": "sensor.energy_log_used_energy_for_hot_water_over_the_past_hour_32294", + "name": "Heißwasser" + }, + { + "stat_consumption": "sensor.energy_log_used_energy_for_heating_over_the_past_hour_32292", + "name": "Heizung" + }, + { + "stat_consumption": "sensor.charger_ac31zeh4ag130052_ladeenergie_sitzung", + "stat_rate": "sensor.charger_ac31zeh4ag130052_ladebox_leistung", + "name": "Wallbox" + }, + { + "stat_consumption": "sensor.wc_trockner_energy", + "name": "Trockner" + }, + { + "stat_consumption": "sensor.wc_waschmaschine_energy", + "name": "Waschmaschine" + }, + { + "stat_consumption": "sensor.badezimmer_handtuchheizung_device_energy", + "name": "Handtuchheizung" + }, + { + "stat_consumption": "sensor.kuche_geschirrspuler_energy", + "name": "Geschirrspüler" + }, + { + "stat_consumption": "sensor.kuche_kuhlschrank_energy", + "name": "Kühlschrank" + }, + { + "stat_consumption": "sensor.technikraum_netzwerkschrank_energy", + "name": "Netzwerkschrank" + }, + { + "stat_consumption": "sensor.whirlpool_energie", + "stat_rate": "sensor.whirlpool_leistung", + "name": "Whirlpool" + }, + { + "stat_consumption": "sensor.all_standby_energy", + "stat_rate": "sensor.all_standby_power", + "name": "Standby" + } + ], + "device_consumption_water": [] + } +} \ No newline at end of file diff --git a/.storage/frontend_panels b/.storage/frontend_panels new file mode 100644 index 0000000..1482cd1 --- /dev/null +++ b/.storage/frontend_panels @@ -0,0 +1,13 @@ +{ + "version": 1, + "minor_version": 1, + "key": "frontend_panels", + "data": { + "climate": { + "show_in_sidebar": true + }, + "light": { + "show_in_sidebar": true + } + } +} \ No newline at end of file diff --git a/.storage/homeassistant.exposed_entities b/.storage/homeassistant.exposed_entities new file mode 100644 index 0000000..e64d085 --- /dev/null +++ b/.storage/homeassistant.exposed_entities @@ -0,0 +1,6937 @@ +{ + "version": 1, + "minor_version": 1, + "key": "homeassistant.exposed_entities", + "data": { + "assistants": { + "conversation": { + "expose_new": false + } + }, + "exposed_entities": { + "zone.home": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "conversation.home_assistant": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sun.sun": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_prowlarr": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_rdtclient": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_gluetun_books": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_audiobookshelf": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_shelfarr": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_flaresolverr": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_librechat": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_litellm": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_crawl4ai_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_nextcloud_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_chat_meilisearch": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_ai_saia_image_proxy_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_mongodb": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_litellm_db": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_searxng": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_papersearch_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_mermaid_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_markitdown_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_redis_searxng": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_searxng_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_homepage": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_immich_power_tools": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_immich_server": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_immich_machine_learning": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_immich_postgres": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_arcane": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_ntfy": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_siyuan": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_n8n": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_immich_redis": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_karakeep_web_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_karakeep_chrome_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_karakeep_meilisearch_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_qdrant_scientific": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_pocket_id": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_paperless_ngx_webserver_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_paperless_ngx_gotenberg_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_paperless_ngx_db_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_paperless_ngx_broker_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_paperless_ngx_tika_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_streamio": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_gluetun": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_traccar": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_vaultwarden": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_version": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_containers_running": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_containers_paused": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_containers_stopped": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_containers_total": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_images": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_prowlarr_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_rdtclient_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_books_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_audiobookshelf_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_shelfarr_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_flaresolverr_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_librechat_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_crawl4ai_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_nextcloud_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_chat_meilisearch_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ai_saia_image_proxy_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mongodb_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_litellm_db_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_papersearch_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_mermaid_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_markitdown_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_redis_searxng_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_searxng_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_homepage_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_power_tools_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_server_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_machine_learning_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_postgres_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_ntfy_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_siyuan_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_n8n_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_immich_redis_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_web_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_chrome_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_karakeep_meilisearch_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_qdrant_scientific_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_pocket_id_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_webserver_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_gotenberg_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_db_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_broker_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_paperless_ngx_tika_1_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_streamio_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_gluetun_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_traccar_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_vaultwarden_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d26a6d2d2e09_prowlarr_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_d26a6d2d2e09_prowlarr": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775530842_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_arcane_upgrader_1775530842": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_obsidian_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_obsidian_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_c8f9745e1c09_nextcloud_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_c8f9745e1c09_nextcloud_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d334573a748e_searxng_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_d334573a748e_searxng": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_8f10beb222a5_crawl4ai_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_8f10beb222a5_crawl4ai_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_859d66cb7cd0_markitdown_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_859d66cb7cd0_markitdown_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e19e30745295_markitdown_mcp_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_e19e30745295_markitdown_mcp": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775703724_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_arcane_upgrader_1775703724": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_postgres_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_lobe_postgres": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_lobe_rustfs": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_redis_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_lobe_redis": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_searxng_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_lobe_searxng": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobe_rustfs_init_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_lobe_rustfs_init": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_lobehub_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_lobehub": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_e01e4e9d4a72_searxng_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_e01e4e9d4a72_searxng": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_7c8a58e5a9df_litellm_db_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_7c8a58e5a9df_litellm_db": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_dcf293cc268d_mongodb_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_dcf293cc268d_mongodb": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_a08f11ce4179_lobehub_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_a08f11ce4179_lobehub": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_32f39ec0f44a_lobehub_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_32f39ec0f44a_lobehub": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3f58d67d91ae_lobehub_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_3f58d67d91ae_lobehub": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.mistral_ai_api": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_d333d51820a1_lobehub_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_d333d51820a1_lobehub": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_3c40ed3eb5c8_lobehub_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_3c40ed3eb5c8_lobehub": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1775876497_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_arcane_upgrader_1775876497": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_state": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_health": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_status": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_up_time": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_image": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_1cpu": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_memory": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_memory_percent": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_network_speed_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_network_speed_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_network_total_up": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "sensor.docker_arcane_upgrader_1776135686_network_total_down": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "switch.docker_arcane_upgrader_1776135686": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "plant.grow_plant_1": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "plant.grow_plant_2": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "plant.grow_plant_3": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "plant.grow_plant_4": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "plant.grow_plant_5": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "automation.log_spider_farmer_decryption": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "automation.log_spider_farmer_packets": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "automation.automation_0": { + "assistants": { + "conversation": { + "should_expose": false + } + } + }, + "device_tracker.byd_dolphin": { + "assistants": { + "conversation": { + "should_expose": false + } + } + } + } + } +} \ No newline at end of file diff --git a/.storage/input_boolean b/.storage/input_boolean new file mode 100644 index 0000000..2e963b2 --- /dev/null +++ b/.storage/input_boolean @@ -0,0 +1,22 @@ +{ + "version": 1, + "minor_version": 1, + "key": "input_boolean", + "data": { + "items": [ + { + "id": "buro_licht_helper", + "name": "Büro_Licht_Helper" + }, + { + "id": "geschirrspuler_uber_nacht", + "icon": "mdi:dishwasher-alert", + "name": "Geschirrspüler über Nacht" + }, + { + "id": "garderobe_helper", + "name": "Garderobe Helper" + } + ] + } +} \ No newline at end of file diff --git a/.storage/input_button b/.storage/input_button new file mode 100644 index 0000000..510b6be --- /dev/null +++ b/.storage/input_button @@ -0,0 +1,14 @@ +{ + "version": 1, + "minor_version": 1, + "key": "input_button", + "data": { + "items": [ + { + "id": "nachtmodus", + "name": "Nachtmodus", + "icon": "mdi:weather-night" + } + ] + } +} \ No newline at end of file diff --git a/.storage/input_datetime b/.storage/input_datetime new file mode 100644 index 0000000..1ded8c1 --- /dev/null +++ b/.storage/input_datetime @@ -0,0 +1,15 @@ +{ + "version": 1, + "minor_version": 1, + "key": "input_datetime", + "data": { + "items": [ + { + "id": "grow_startdatum", + "name": "Grow Startdatum", + "has_date": true, + "has_time": false + } + ] + } +} \ No newline at end of file diff --git a/.storage/input_select b/.storage/input_select new file mode 100644 index 0000000..90c44e0 --- /dev/null +++ b/.storage/input_select @@ -0,0 +1,8 @@ +{ + "version": 1, + "minor_version": 2, + "key": "input_select", + "data": { + "items": [] + } +} \ No newline at end of file diff --git a/.storage/input_text b/.storage/input_text new file mode 100644 index 0000000..431a509 --- /dev/null +++ b/.storage/input_text @@ -0,0 +1,8 @@ +{ + "version": 1, + "minor_version": 1, + "key": "input_text", + "data": { + "items": [] + } +} \ No newline at end of file diff --git a/.storage/lovelace.dashboard_grow b/.storage/lovelace.dashboard_grow new file mode 100644 index 0000000..7100861 --- /dev/null +++ b/.storage/lovelace.dashboard_grow @@ -0,0 +1,535 @@ +{ + "version": 1, + "minor_version": 1, + "key": "lovelace.dashboard_grow", + "data": { + "config": { + "views": [ + { + "title": "Mission Control", + "type": "sections", + "max_columns": 2, + "badges": [ + { + "type": "entity", + "entity": "sensor.spider_farmer_80f1b2b3b64a_temperature" + }, + { + "type": "entity", + "entity": "sensor.spider_farmer_80f1b2b3b64a_humidity" + }, + { + "type": "entity", + "entity": "sensor.spider_farmer_80f1b2b3b64a_vpd" + }, + { + "type": "entity", + "entity": "sensor.grow_tent_power" + } + ], + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Surveillance & Environment" + }, + { + "type": "custom:advanced-camera-card", + "cameras": [ + { + "camera_entity": "camera.tapo_c100_mainstream", + "live_provider": "ha" + } + ], + "view": { + "default": "live" + }, + "menu": { + "buttons": { + "frigate": { + "enabled": false + }, + "timeline": { + "enabled": false + } + } + } + }, + { + "type": "heading", + "heading": "Plant Health" + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:stack-in-card", + "cards": [ + { + "type": "custom:mushroom-template-card", + "primary": "P1", + "secondary": "{{ '⚠️' if is_state('plant.grow_plant_1', 'problem') else '✅' }}", + "icon": "mdi:flower", + "icon_color": "{{ 'red' if is_state('plant.grow_plant_1', 'problem') else 'green' }}", + "layout": "vertical" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.grow_plant_1_soil_moisture", + "icon": "mdi:water-percent" + }, + { + "type": "entity", + "entity": "sensor.grow_plant_1_soil_fertility", + "icon": "mdi:sprout" + } + ] + } + ] + }, + { + "type": "custom:stack-in-card", + "cards": [ + { + "type": "custom:mushroom-template-card", + "primary": "P2", + "secondary": "{{ '⚠️' if is_state('plant.grow_plant_2', 'problem') else '✅' }}", + "icon": "mdi:flower", + "icon_color": "{{ 'red' if is_state('plant.grow_plant_2', 'problem') else 'green' }}", + "layout": "vertical" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.grow_plant_2_soil_moisture", + "icon": "mdi:water-percent" + }, + { + "type": "entity", + "entity": "sensor.grow_plant_2_soil_fertility", + "icon": "mdi:sprout" + } + ] + } + ] + }, + { + "type": "custom:stack-in-card", + "cards": [ + { + "type": "custom:mushroom-template-card", + "primary": "P3", + "secondary": "{{ '⚠️' if is_state('plant.grow_plant_3', 'problem') else '✅' }}", + "icon": "mdi:flower", + "icon_color": "{{ 'red' if is_state('plant.grow_plant_3', 'problem') else 'green' }}", + "layout": "vertical" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.grow_plant_3_soil_moisture", + "icon": "mdi:water-percent" + }, + { + "type": "entity", + "entity": "sensor.grow_plant_3_soil_fertility", + "icon": "mdi:sprout" + } + ] + } + ] + }, + { + "type": "custom:stack-in-card", + "cards": [ + { + "type": "custom:mushroom-template-card", + "primary": "P4", + "secondary": "{{ '⚠️' if is_state('plant.grow_plant_4', 'problem') else '✅' }}", + "icon": "mdi:flower", + "icon_color": "{{ 'red' if is_state('plant.grow_plant_4', 'problem') else 'green' }}", + "layout": "vertical" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.grow_plant_4_soil_moisture", + "icon": "mdi:water-percent" + }, + { + "type": "entity", + "entity": "sensor.grow_plant_4_soil_fertility", + "icon": "mdi:sprout" + } + ] + } + ] + }, + { + "type": "custom:stack-in-card", + "cards": [ + { + "type": "custom:mushroom-template-card", + "primary": "P5", + "secondary": "{{ '⚠️' if is_state('plant.grow_plant_5', 'problem') else '✅' }}", + "icon": "mdi:flower", + "icon_color": "{{ 'red' if is_state('plant.grow_plant_5', 'problem') else 'green' }}", + "layout": "vertical" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.grow_plant_5_soil_moisture", + "icon": "mdi:water-percent" + }, + { + "type": "entity", + "entity": "sensor.grow_plant_5_soil_fertility", + "icon": "mdi:sprout" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Secondary Environment" + }, + { + "type": "vertical-stack", + "cards": [ + { + "type": "custom:apexcharts-card", + "graph_span": "48h", + "header": { + "show": true, + "title": "48h Klima-Vergleich (Geglättet)" + }, + "yaxis": [ + { + "id": "temp", + "min": 10, + "max": 35, + "apex_config": { + "tickAmount": 5 + } + }, + { + "id": "hum", + "opposite": true, + "min": 20, + "max": 100, + "apex_config": { + "tickAmount": 5 + } + } + ], + "series": [ + { + "entity": "sensor.spider_farmer_80f1b2b3b64a_temperature", + "name": "Zelt Temp", + "yaxis_id": "temp", + "stroke_width": 3, + "color": "#00ff00", + "group_by": { + "func": "avg", + "duration": "10m" + } + }, + { + "entity": "sensor.0x00158d0002c9fa8b_temperature", + "name": "Außen Temp (ZB)", + "yaxis_id": "temp", + "stroke_width": 2, + "color": "darkgreen", + "group_by": { + "func": "avg", + "duration": "10m" + } + }, + { + "entity": "sensor.spider_farmer_80f1b2b3b64a_humidity", + "name": "Zelt Hum", + "yaxis_id": "hum", + "stroke_width": 3, + "color": "#00bfff", + "group_by": { + "func": "avg", + "duration": "10m" + } + }, + { + "entity": "sensor.0x00158d0002c9fa8b_humidity", + "name": "Außen Hum (ZB)", + "yaxis_id": "hum", + "stroke_width": 2, + "color": "#1a4b6e", + "group_by": { + "func": "avg", + "duration": "10m" + } + } + ], + "apex_config": { + "stroke": { + "curve": "smooth", + "dashArray": [ + 0, + 5, + 0, + 5 + ] + }, + "legend": { + "show": true + }, + "grid": { + "borderColor": "#333", + "strokeDashArray": 4 + } + } + }, + { + "type": "custom:apexcharts-card", + "graph_span": "48h", + "header": { + "show": true, + "title": "48h VPD Verlauf (kPa)" + }, + "yaxis": [ + { + "id": "vpd", + "min": 0, + "max": 2.5, + "decimals": 2, + "apex_config": { + "tickAmount": 5 + } + } + ], + "series": [ + { + "entity": "sensor.spider_farmer_80f1b2b3b64a_vpd", + "name": "Zelt VPD", + "yaxis_id": "vpd", + "stroke_width": 3, + "color": "#ff9800", + "group_by": { + "func": "avg", + "duration": "10m" + } + } + ], + "apex_config": { + "stroke": { + "curve": "smooth" + }, + "annotations": { + "yaxis": [ + { + "y": 0.8, + "y2": 1.2, + "borderColor": "#00ff00", + "fillColor": "#00ff00", + "opacity": 0.1, + "label": { + "text": "Optimum Veg", + "style": { + "background": "#00ff00", + "color": "#000" + } + } + } + ] + }, + "legend": { + "show": true + }, + "grid": { + "borderColor": "#333", + "strokeDashArray": 4 + } + } + } + ] + }, + { + "type": "grid", + "columns": 2, + "square": false, + "cards": [ + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.grow_env_sensor_1_temperature", + "name": "S1 Innen", + "icon_color": "orange" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.0x00158d0002c9fa8b_temperature", + "name": "ZB Außen", + "icon_color": "blue" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.grow_env_sensor_1_humidity", + "name": "S1 Feuchte", + "icon_color": "cyan" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.0x00158d0002c9fa8b_humidity", + "name": "ZB Feuchte", + "icon_color": "light-blue" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.grow_env_sensor_1_pressure", + "name": "Druck In" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.0x00158d0002c9fa8b_pressure", + "name": "Druck Out" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Equipment & Controls" + }, + { + "type": "tile", + "entity": "binary_sensor.spider_farmer_80f1b2b3b64a_growth_plan_running", + "name": "Growth plan active", + "vertical": false, + "features_position": "bottom" + }, + { + "type": "tile", + "entity": "sensor.spider_farmer_80f1b2b3b64a_growth_progress", + "name": "Growth plan progress", + "icon": "fa6-solid:percent", + "vertical": false, + "features_position": "bottom" + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-light-card", + "entity": "light.spider_farmer_80f1b2b3b64a_primary_light", + "name": "Light", + "show_brightness_control": true, + "use_light_color": true + }, + { + "type": "custom:mushroom-fan-card", + "entity": "fan.spider_farmer_80f1b2b3b64a_blower", + "name": "Blower", + "show_percentage_control": true + } + ] + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-fan-card", + "entity": "fan.spider_farmer_80f1b2b3b64a_fan", + "name": "Fan", + "tap_action": { + "action": "more-info" + } + }, + { + "type": "custom:mushroom-entity-card", + "entity": "fan.spider_farmer_80f1b2b3b64a_heater", + "name": "Heater", + "icon": "mdi:fire" + } + ] + }, + { + "type": "glance", + "entities": [ + { + "entity": "sensor.grow_tent_power", + "name": "Watt" + }, + { + "entity": "sensor.grow_tent_current", + "name": "Ampere" + }, + { + "entity": "sensor.grow_tent_voltage", + "name": "Volt" + }, + { + "entity": "binary_sensor.grow_tent_consumer_connected", + "name": "Status" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Grow Planer" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "input_datetime.grow_startdatum", + "name": "Startdatum", + "icon": "mdi:calendar-start" + }, + { + "type": "custom:stack-in-card", + "cards": [ + { + "type": "markdown", + "content": "{% set start_state = states('input_datetime.grow_startdatum') %} {% if start_state not in ['unknown', 'unavailable'] %}\n {% set start = as_timestamp(start_state) %}\n {% set heute = as_timestamp(now().strftime('%Y-%m-%d')) %}\n {% set tage = ((heute - start) / 86400) | int %}\n {% set start_dt = start_state | as_datetime %}\n ### Aktueller Tag: {{ tage }}\n | Phase | Zeitraum | Status |\n | :--- | :--- | :--- |\n | **Keimung** | bis {{ (start_dt + timedelta(days=5)).strftime('%d.%m.') }} | {{ '🟢 Aktiv' if tage <= 5 else '✅ OK' }} |\n | **Sämling** | bis {{ (start_dt + timedelta(days=19)).strftime('%d.%m.') }} | {{ '⏳' if tage < 6 else ('🟢 Aktiv' if tage <= 19 else '✅ OK') }} |\n | **Wachstum** | {{ (start_dt + timedelta(days=20)).strftime('%d.%m.') }} bis {{ (start_dt + timedelta(days=35)).strftime('%d.%m.') }} | {{ '⏳' if tage < 20 else ('🟢 Aktiv' if tage <= 35 else '✅ OK') }} |\n | **Blüte** | {{ (start_dt + timedelta(days=36)).strftime('%d.%m.') }} bis {{ (start_dt + timedelta(days=90)).strftime('%d.%m.') }} | {{ '⏳' if tage < 36 else ('🟢 Aktiv' if tage <= 90 else '✅ OK') }} |\n \n **Voraussichtliche Ernte: {{ (start_dt + timedelta(days=80)).strftime('%d.%m.%y') }}**\n ---\n **Sorten:** 2x Amnesia XXL, 2x Sticky Orange, 1x Purple Skunk\n{% endif %}\n" + } + ] + } + ] + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/.storage/lovelace.dashboard_haustechnik b/.storage/lovelace.dashboard_haustechnik new file mode 100644 index 0000000..f69a6af --- /dev/null +++ b/.storage/lovelace.dashboard_haustechnik @@ -0,0 +1,1852 @@ +{ + "version": 1, + "minor_version": 1, + "key": "lovelace.dashboard_haustechnik", + "data": { + "config": { + "views": [ + { + "title": "PV & Speicher", + "path": "pv-speicher", + "icon": "mdi:solar-power", + "type": "sections", + "max_columns": 2, + "cards": [], + "badges": [ + { + "type": "custom:mushroom-chips-card", + "chips": [ + { + "type": "entity", + "entity": "sensor.powerocean_mpptpv_pwrtotal", + "icon": "mdi:solar-power", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.powerocean_bpsoc_2", + "icon": "mdi:battery", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.zuhause_strompreis", + "icon": "mdi:lightning-bolt", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierte_kosten", + "icon": "mdi:currency-eur", + "show_state": true, + "show_icon": true + } + ] + } + ], + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "⚡ Energiefluss" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.powerocean_hj37zdh5zg5w0109_solarertrag_mppt", + "icon": "mdi:solar-power", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.powerocean_hj37zdh5zg5w0109_netzleistung_system", + "icon": "mdi:home-lightning-bolt", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_leistung", + "icon": "mdi:transmission-tower", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_einspeiseleistung", + "icon": "mdi:transmission-tower-export", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Leistung – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.powerocean_hj37zdh5zg5w0109_solarertrag_mppt", + "name": "Solar", + "color": "#f9a825" + }, + { + "entity": "sensor.powerocean_hj37zdh5zg5w0109_netzleistung_system", + "name": "Eigenverbrauch", + "color": "#e53935" + }, + { + "entity": "sensor.tibber_pulse_zuhause_leistung", + "name": "Netzbezug", + "color": "#1e88e5" + }, + { + "entity": "sensor.tibber_pulse_zuhause_einspeiseleistung", + "name": "Einspeisung", + "color": "#43a047" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "☀️ Prognose & Kosten" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.energy_production_today_remaining", + "icon": "mdi:solar-power-variant", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.energy_production_tomorrow", + "icon": "mdi:weather-sunny", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.power_highest_peak_time_today", + "icon": "mdi:clock-fast", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierte_kosten", + "icon": "mdi:currency-eur", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierter_verbrauch", + "icon": "mdi:home-lightning-bolt", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierte_einspeisung", + "icon": "mdi:transmission-tower-export", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Strompreis", + "show_states": false, + "standard_format": true, + "disable_actions": true + }, + "apex_config": { + "legend": { + "show": false + } + }, + "yaxis": [ + { + "id": "price", + "min": "~0.1", + "max": "~0.4", + "align_to": 1, + "decimals": 2 + } + ], + "chart_type": "line", + "graph_span": "1.5d", + "span": { + "start": "day" + }, + "now": { + "show": true, + "label": "Jetzt" + }, + "experimental": { + "color_threshold": true + }, + "series": [ + { + "entity": "sensor.epex_spot_data_total_price", + "yaxis_id": "price", + "stroke_width": 2, + "show": { + "legend_value": false + }, + "type": "line", + "extend_to": false, + "data_generator": "return entity.attributes.data.map((entry) => {\n return [new Date(entry.start_time), entry.price_per_kwh];\n});\n", + "curve": "stepline", + "color_threshold": [ + { + "value": 0.1, + "color": "blue" + }, + { + "value": 0.2, + "color": "cyan" + }, + { + "value": 0.25, + "color": "green" + }, + { + "value": 0.3, + "color": "yellow" + }, + { + "value": 0.35, + "color": "orange" + }, + { + "value": 0.4, + "color": "red" + } + ] + } + ] + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.electricity_maps_co2_intensitat", + "icon": "mdi:molecule-co2", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.electricity_maps_anteil_fossiler_brennstoffe_im_netz", + "icon": "mdi:factory", + "show_state": true, + "show_icon": true + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "⚡ Tibber" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.zuhause_strompreis", + "icon": "mdi:lightning-bolt", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierte_kosten", + "icon": "mdi:currency-eur", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierter_verbrauch", + "icon": "mdi:home-lightning-bolt", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierte_einspeisung", + "icon": "mdi:transmission-tower-export", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Netzbezug & Einspeisung – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.tibber_pulse_zuhause_leistung", + "name": "Bezug (W)", + "color": "#e53935" + }, + { + "entity": "sensor.tibber_pulse_zuhause_einspeiseleistung", + "name": "Einspeisung (W)", + "color": "#43a047" + } + ] + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_spannung_l1", + "icon": "mdi:lightning-bolt-circle", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_spannung_l2", + "icon": "mdi:lightning-bolt-circle", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_spannung_l3", + "icon": "mdi:lightning-bolt-circle", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_stromstarke_l1", + "icon": "mdi:current-ac", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_stromstarke_l2", + "icon": "mdi:current-ac", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_stromstarke_l3", + "icon": "mdi:current-ac", + "show_state": true, + "show_icon": true + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🔋 Batteriespeicher" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.powerocean_hj37zdh5zg5w0109_ladezustand_der_batterie", + "icon": "mdi:battery", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.powerocean_hj37zdh5zg5w0109_emsbppower", + "icon": "mdi:flash", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Batterieladung – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "yaxis": [ + { + "id": "soc", + "min": 0, + "max": 100, + "decimals": 0 + }, + { + "id": "power", + "opposite": true, + "decimals": 0 + } + ], + "series": [ + { + "entity": "sensor.powerocean_hj37zdh5zg5w0109_ladezustand_der_batterie", + "name": "Ladestand (%)", + "yaxis_id": "soc", + "color": "#43a047" + }, + { + "entity": "sensor.powerocean_hj37zdh5zg5w0109_emsbppower", + "name": "Leistung (W)", + "yaxis_id": "power", + "color": "#1e88e5" + } + ] + } + ] + } + ] + }, + { + "title": "Wärmepumpe", + "path": "waermepumpe", + "icon": "mdi:heat-pump", + "type": "sections", + "max_columns": 2, + "cards": [], + "badges": [ + { + "type": "custom:mushroom-chips-card", + "chips": [ + { + "type": "template", + "icon": "mdi:wifi", + "icon_color": "{{ 'green' if is_state('binary_sensor.nibe_s1255_6_e_em_3x400v_konnektivitat','on') else 'red' }}\n", + "content": "{{ 'Online' if is_state('binary_sensor.nibe_s1255_6_e_em_3x400v_konnektivitat','on') else 'Offline' }}\n" + }, + { + "type": "template", + "icon": "mdi:alert-circle-outline", + "icon_color": "{{ 'red' if is_state('binary_sensor.nibe_s1255_6_e_em_3x400v_alarm','on') else 'green' }}\n", + "content": "{{ 'Alarm!' if is_state('binary_sensor.nibe_s1255_6_e_em_3x400v_alarm','on') else 'Kein Alarm' }}\n" + }, + { + "type": "entity", + "entity": "sensor.nibe_energy_cop_total_last_hour", + "icon": "mdi:gauge-full", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_current_outdoor_temperature_bt1", + "icon": "mdi:thermometer", + "show_state": true, + "show_icon": true + } + ] + } + ], + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🌡️ Heizkreis" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_supply_line_bt2", + "icon": "mdi:thermometer-chevron-up", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_return_line_bt3", + "icon": "mdi:thermometer-chevron-down", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_calculated_supply_climate_system_1", + "icon": "mdi:calculator-variant", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_temperatur_klimatisierungssyste", + "name": "Raumtemperatur Ist", + "icon": "mdi:home-thermometer", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "number.nibe_s1255_6_e_em_3x400v_raumfuhler_sollwert_klimatisierungssyste", + "name": "Raumtemperatur Soll", + "icon": "mdi:home-thermometer-outline", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info" + } + }, + { + "type": "custom:mushroom-entity-card", + "entity": "number.nibe_s1255_6_e_em_3x400v_degree_minutes", + "name": "Degree Minutes", + "icon": "mdi:timer-outline", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info" + } + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Heizkreis – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_supply_line_bt2", + "name": "Vorlauf (BT2)" + }, + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_return_line_bt3", + "name": "Rücklauf (BT3)" + }, + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_current_outdoor_temperature_bt1", + "name": "Außen (BT1)" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🚿 Warmwasser" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_hot_water_top_bt7", + "icon": "mdi:water-thermometer", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_hot_water_charging_bt6", + "icon": "mdi:water-sync", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Warmwasser – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_hot_water_top_bt7", + "name": "Oben (BT7)" + }, + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_hot_water_charging_bt6", + "name": "Ladung (BT6)" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "💨 Lüftung (AZ30)" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "select.nibe_s1255_6_e_em_3x400v_ventilation_mode", + "name": "Lüftungsmodus", + "icon": "mdi:fan", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info" + } + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_abluft_az30_bt20", + "icon": "mdi:arrow-up-circle-outline", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_fortluft_az30_bt21", + "icon": "mdi:arrow-right-circle-outline", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_supp_air_az30_bt22", + "icon": "mdi:arrow-down-circle-outline", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Lüftung – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_abluft_az30_bt20", + "name": "Abluft BT20" + }, + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_fortluft_az30_bt21", + "name": "Fortluft BT21" + }, + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_supp_air_az30_bt22", + "name": "Zuluft BT22" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🧊 Sole / Erdkollektor" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_brine_in_bt10", + "icon": "mdi:arrow-right-circle", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_brine_out_bt11", + "icon": "mdi:arrow-left-circle", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_temperatur_bt50", + "icon": "mdi:thermometer", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Sole – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_brine_in_bt10", + "name": "Sole ein (BT10)" + }, + { + "entity": "sensor.nibe_s1255_6_e_em_3x400v_brine_out_bt11", + "name": "Sole aus (BT11)" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "📊 Effizienz & Energie" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.nibe_energy_cop_total_last_hour", + "icon": "mdi:gauge-full", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_energy_cop_heating_last_hour", + "icon": "mdi:radiator", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_energy_cop_hot_water_last_hour", + "icon": "mdi:water-boiler", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.nibe_energy_produced_total_kwh", + "icon": "mdi:lightning-bolt", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_energy_used_total_kwh", + "icon": "mdi:home-lightning-bolt", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_energy_produced_heating_kwh", + "icon": "mdi:radiator", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.nibe_energy_produced_hot_water_kwh", + "icon": "mdi:water-boiler", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "COP – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.nibe_energy_cop_total_last_hour", + "name": "COP gesamt" + }, + { + "entity": "sensor.nibe_energy_cop_heating_last_hour", + "name": "COP Heizung" + }, + { + "entity": "sensor.nibe_energy_cop_hot_water_last_hour", + "name": "COP Warmwasser" + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_tarifblockierung", + "name": "Tarifblockierung", + "icon": "mdi:currency-eur-off", + "layout": "horizontal", + "fill_container": true + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🗺️ Systemdiagramm" + }, + { + "type": "picture-elements", + "image": "/api/image/serve/7ec7636ccf48b97c110d04ca4c281c6a/512x512", + "elements": [ + { + "type": "state-label", + "entity": "sensor.power_internal_additional_heat_31028", + "prefix": "Heater: ", + "style": { + "top": "50%", + "left": "35%" + } + }, + { + "type": "state-label", + "entity": "sensor.flow_sensor_bf1_30041", + "prefix": "BF1: ", + "style": { + "top": "75%", + "left": "51%" + } + }, + { + "type": "state-label", + "entity": "sensor.current_outdoor_temperature_bt1_30002", + "prefix": "BT1: ", + "style": { + "top": "88%", + "left": "10%" + } + }, + { + "type": "state-label", + "entity": "sensor.supply_line_bt2_30006", + "prefix": "BT2: ", + "style": { + "top": "6%", + "left": "40%" + } + }, + { + "type": "state-label", + "entity": "sensor.return_line_bt3_30008", + "prefix": "BT3: ", + "style": { + "top": "10%", + "left": "10%" + } + }, + { + "type": "state-label", + "entity": "sensor.hot_water_charging_bt6_30010", + "prefix": "BT6: ", + "style": { + "top": "30%", + "left": "30%" + } + }, + { + "type": "state-label", + "entity": "sensor.hot_water_top_bt7_30009", + "prefix": "BT7: ", + "style": { + "top": "23%", + "left": "30%" + } + }, + { + "type": "state-label", + "entity": "sensor.condenser_bt12_30013", + "prefix": "BT12: ", + "style": { + "top": "44%", + "left": "10%" + } + }, + { + "type": "state-label", + "entity": "sensor.discharge_bt14_30014", + "prefix": "BT14: ", + "style": { + "top": "44%", + "left": "60%" + } + }, + { + "type": "state-label", + "entity": "sensor.eb100_ep14_bt16_31627", + "prefix": "BT16: ", + "style": { + "top": "60%", + "left": "10%" + } + }, + { + "type": "state-label", + "entity": "sensor.suction_gas_bt17_30017", + "prefix": "BT17: ", + "style": { + "top": "60%", + "left": "60%" + } + }, + { + "type": "state-label", + "entity": "sensor.exh_air_az30_bt20_30020", + "prefix": "BT20: ", + "style": { + "top": "91%", + "left": "36%" + } + }, + { + "type": "state-label", + "entity": "sensor.extr_air_az30_bt21_30021", + "prefix": "BT21: ", + "style": { + "top": "68%", + "left": "12%" + } + }, + { + "type": "custom:numberbox-card", + "entity": "number.nibe_s1255_6_e_em_3x400v_raumfuhler_sollwert_klimatisierungssyste", + "name": false, + "icon": false, + "min": 16, + "max": 25, + "step": 0.5, + "card_mod": { + "style": "ha-card { background: grey; }\n" + }, + "style": { + "top": "91%", + "left": "75%" + } + } + ] + } + ] + } + ] + }, + { + "title": "IT & Server", + "path": "it-server", + "icon": "mdi:server", + "type": "sections", + "max_columns": 2, + "cards": [], + "badges": [ + { + "type": "custom:mushroom-chips-card", + "chips": [ + { + "type": "template", + "icon": "mdi:server", + "icon_color": "{{ 'green' if is_state('binary_sensor.node_nuc_status','on') else 'red' }}\n", + "content": "{{ 'NUC Online' if is_state('binary_sensor.node_nuc_status','on') else 'NUC Offline' }}\n" + }, + { + "type": "entity", + "entity": "sensor.cloud_gateway_fiber_cloudflare_wan_latency", + "icon": "mdi:web", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.adguard_home_anteil_blockierter_dns_abfragen", + "icon": "mdi:shield-check", + "show_state": true, + "show_icon": true + }, + { + "type": "conditional", + "conditions": [ + { + "condition": "state", + "entity": "binary_sensor.node_nuc_pakete_aktualisieren", + "state": "on" + } + ], + "chip": { + "type": "template", + "icon": "mdi:update", + "icon_color": "orange", + "content": "{{ states('sensor.node_nuc_gesamte_updates') }} Updates\n" + } + } + ] + }, + { + "type": "entity", + "show_name": false, + "show_state": true, + "show_icon": true, + "entity": "binary_sensor.appdaemon_aktiv" + } + ], + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🖥️ Proxmox NUC" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "template", + "icon": "mdi:server", + "icon_color": "{{ 'green' if is_state('binary_sensor.node_nuc_status','on') else 'red' }}\n", + "content": "{{ 'Online' if is_state('binary_sensor.node_nuc_status','on') else 'Offline' }}\n" + }, + { + "type": "entity", + "entity": "sensor.node_nuc_cpu_auslastung", + "icon": "mdi:cpu-64-bit", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.node_nuc_prozentsatz_genutzter_arbeitsspeicher", + "icon": "mdi:memory", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.node_nuc_prozentsatz_belegter_speicherplatz", + "icon": "mdi:harddisk", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.node_nuc_virtual_machines_running", + "icon": "mdi:monitor", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.node_nuc_container_laufen", + "icon": "mdi:docker", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.node_nuc_letzter_start", + "icon": "mdi:clock-outline", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "conditional", + "conditions": [ + { + "condition": "state", + "entity": "binary_sensor.node_nuc_pakete_aktualisieren", + "state": "on" + } + ], + "card": { + "type": "custom:mushroom-entity-card", + "entity": "sensor.node_nuc_gesamte_updates", + "name": "Updates verfügbar", + "icon": "mdi:update", + "icon_color": "orange", + "layout": "horizontal", + "fill_container": true + } + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.node_nuc_genutzter_arbeitsspeicher", + "name": "RAM genutzt", + "icon": "mdi:memory", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.node_nuc_freier_arbeitsspeicher", + "name": "RAM frei", + "icon": "mdi:memory", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "CPU & RAM – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "yaxis": [ + { + "id": "pct", + "min": 0, + "max": 100, + "decimals": 0 + } + ], + "series": [ + { + "entity": "sensor.node_nuc_cpu_auslastung", + "name": "CPU %", + "yaxis_id": "pct", + "color": "#1e88e5" + }, + { + "entity": "sensor.node_nuc_prozentsatz_genutzter_arbeitsspeicher", + "name": "RAM %", + "yaxis_id": "pct", + "color": "#e53935" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🌐 UniFi Gateway" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.cloud_gateway_fiber_cloudflare_wan_latency", + "icon": "mdi:web", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.cloud_gateway_fiber_cpu_utilization", + "icon": "mdi:cpu-64-bit", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.cloud_gateway_fiber_memory_utilization", + "icon": "mdi:memory", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.cloud_gateway_fiber_cloud_gateway_fiber_cpu_temperature", + "icon": "mdi:thermometer", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.cloud_gateway_fiber_state", + "name": "Status", + "icon": "mdi:router-network", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.cloud_gateway_fiber_uptime", + "name": "Uptime", + "icon": "mdi:clock-outline", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "WAN Latenz – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.cloud_gateway_fiber_cloudflare_wan_latency", + "name": "WAN Latenz (ms)", + "color": "#1e88e5" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🛡️ AdGuard Home" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.adguard_home_anteil_blockierter_dns_abfragen", + "icon": "mdi:shield-check", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.adguard_home_dns_abfragen", + "icon": "mdi:dns", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.adguard_home_blockierte_dns_abfragen", + "icon": "mdi:shield-off", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.adguard_home_durchschnittliche_verarbeitungsgeschwindigkeit", + "name": "Ø Antwortzeit", + "icon": "mdi:timer-outline", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.adguard_home_durch_safe_browsing_blockiert", + "name": "Safe Browsing blockiert", + "icon": "mdi:web-off", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.adguard_home_durch_jugendschutz_blockiert", + "name": "Jugendschutz blockiert", + "icon": "mdi:account-child", + "layout": "horizontal", + "fill_container": true + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "📷 Immich Fotoverwaltung" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.immich_anzahl_fotos", + "icon": "mdi:image-multiple", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.immich_anzahl_videos", + "icon": "mdi:video-box", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.immich_gesamter_speicherplatz", + "name": "Gesamt Speicher", + "icon": "mdi:harddisk", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.immich_verfugbarer_speicherplatz", + "name": "Verfügbar", + "icon": "mdi:harddisk-plus", + "layout": "horizontal", + "fill_container": true + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🎵 Music Assistant" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "template", + "icon": "mdi:music-circle", + "icon_color": "{{ 'green' if is_state('binary_sensor.music_assistant_server_aktiv','on') else 'red' }}\n", + "content": "{{ 'Aktiv' if is_state('binary_sensor.music_assistant_server_aktiv','on') else 'Inaktiv' }}\n" + }, + { + "type": "entity", + "entity": "sensor.music_assistant_server_cpu_prozent", + "icon": "mdi:cpu-64-bit", + "show_state": true, + "show_icon": true + }, + { + "type": "entity", + "entity": "sensor.music_assistant_server_arbeitsspeicher_in_prozent", + "icon": "mdi:memory", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.music_assistant_server_version", + "name": "Version", + "icon": "mdi:tag-outline", + "layout": "horizontal", + "fill_container": true + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "💾 Backup" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.backup_backup_manager_zustand", + "name": "Backup-Status", + "icon": "mdi:backup-restore", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.backup_letztes_erfolgreiches_automatisches_backup", + "name": "Letztes erfolgreiches Backup", + "icon": "mdi:check-circle-outline", + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.backup_nachstes_geplantes_automatisches_backup", + "name": "Nächstes Backup", + "icon": "mdi:clock-outline", + "layout": "horizontal", + "fill_container": true + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "📡 Zigbee2MQTT" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "template", + "icon": "mdi:zigbee", + "icon_color": "{{ 'green' if is_state('binary_sensor.zigbee2mqtt_bridge_connection_state','on') else 'red' }}\n", + "content": "{{ 'Verbunden' if is_state('binary_sensor.zigbee2mqtt_bridge_connection_state','on') else 'Getrennt' }}\n" + }, + { + "type": "entity", + "entity": "sensor.zigbee2mqtt_bridge_version", + "icon": "mdi:tag-outline", + "show_state": true, + "show_icon": true + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "switch.zigbee2mqtt_bridge_permit_join", + "name": "Pairing erlauben", + "icon": "mdi:link-plus", + "tap_action": { + "action": "toggle" + }, + "layout": "horizontal", + "fill_container": true + }, + { + "type": "custom:mushroom-entity-card", + "entity": "button.zigbee2mqtt_bridge_restart", + "name": "Bridge neu starten", + "icon": "mdi:restart", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "call-service", + "service": "button.press", + "target": { + "entity_id": "button.zigbee2mqtt_bridge_restart" + } + } + } + ] + } + ] + }, + { + "title": "Sicherheit", + "path": "sicherheit", + "icon": "mdi:shield-home", + "type": "sections", + "max_columns": 2, + "cards": [], + "badges": [ + { + "type": "custom:mushroom-chips-card", + "chips": [ + { + "type": "template", + "icon": "mdi:smoke-detector-variant", + "icon_color": "{{ 'red' if is_state('binary_sensor.buro_rauchmelder_smoke','on')\n or is_state('binary_sensor.kinderzimmer_rauchmelder_smoke','on')\n or is_state('binary_sensor.schlafzimmer_rauchmelder_smoke','on')\n or is_state('binary_sensor.gastezimmer_rauchmelder_smoke','on')\n or is_state('binary_sensor.technikraum_rauchmelder_smoke','on')\n or is_state('binary_sensor.wohnzimmer_rauchmelder_smoke','on')\n else 'green' }}\n", + "content": "{% set rooms = {\n 'Büro': 'binary_sensor.buro_rauchmelder_smoke',\n 'Kinderzimmer': 'binary_sensor.kinderzimmer_rauchmelder_smoke',\n 'Schlafzimmer': 'binary_sensor.schlafzimmer_rauchmelder_smoke',\n 'Gästezimmer': 'binary_sensor.gastezimmer_rauchmelder_smoke',\n 'Technikraum': 'binary_sensor.technikraum_rauchmelder_smoke',\n 'Wohnzimmer': 'binary_sensor.wohnzimmer_rauchmelder_smoke'\n} %} {% set alarm = namespace(room='') %} {% for name, entity in rooms.items() %}\n {% if is_state(entity,'on') and alarm.room == '' %}\n {% set alarm.room = name %}\n {% endif %}\n{% endfor %} {{ 'Rauch – ' ~ alarm.room ~ '!' if alarm.room != '' else 'Kein Rauch' }}\n" + }, + { + "type": "template", + "icon": "mdi:water-alert", + "icon_color": "{{ 'red' if is_state('binary_sensor.technikraum_wassermelder_water_leak','on')\n or is_state('binary_sensor.wc_wassermelder_water_leak','on')\n or is_state('binary_sensor.kuche_wassermelder_water_leak','on')\n else 'green' }}\n", + "content": "{% if is_state('binary_sensor.technikraum_wassermelder_water_leak','on') %}\n Wasser – Technikraum!\n{% elif is_state('binary_sensor.wc_wassermelder_water_leak','on') %}\n Wasser – WC!\n{% elif is_state('binary_sensor.kuche_wassermelder_water_leak','on') %}\n Wasser – Küche!\n{% else %}\n Trocken\n{% endif %}\n" + } + ] + } + ], + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🔥 Rauchmelder" + }, + { + "type": "custom:mushroom-template-card", + "primary": "Büro", + "secondary": "{{ states('sensor.buro_rauchmelder_temperature') }}°C · {{ states('sensor.buro_rauchmelder_humidity') }}% · AQI {{ states('sensor.buro_rauchmelder_aqi') }}\n", + "icon": "mdi:smoke-detector-variant", + "icon_color": "{{ 'red' if is_state('binary_sensor.buro_rauchmelder_smoke','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.buro_rauchmelder_smoke" + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "Kinderzimmer", + "secondary": "{{ states('sensor.kinderzimmer_rauchmelder_temperature') }}°C · {{ states('sensor.kinderzimmer_rauchmelder_humidity') }}% · AQI {{ states('sensor.kinderzimmer_rauchmelder_aqi') }}\n", + "icon": "mdi:smoke-detector-variant", + "icon_color": "{{ 'red' if is_state('binary_sensor.kinderzimmer_rauchmelder_smoke','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.kinderzimmer_rauchmelder_smoke" + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "Schlafzimmer", + "secondary": "{{ states('sensor.schlafzimmer_rauchmelder_temperature') }}°C · {{ states('sensor.schlafzimmer_rauchmelder_humidity') }}% · AQI {{ states('sensor.schlafzimmer_rauchmelder_aqi') }}\n", + "icon": "mdi:smoke-detector-variant", + "icon_color": "{{ 'red' if is_state('binary_sensor.schlafzimmer_rauchmelder_smoke','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.schlafzimmer_rauchmelder_smoke" + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "Gästezimmer", + "secondary": "{{ states('sensor.gastezimmer_rauchmelder_temperature') }}°C · {{ states('sensor.gastezimmer_rauchmelder_humidity') }}% · AQI {{ states('sensor.gastezimmer_rauchmelder_aqi') }}\n", + "icon": "mdi:smoke-detector-variant", + "icon_color": "{{ 'red' if is_state('binary_sensor.gastezimmer_rauchmelder_smoke','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.gastezimmer_rauchmelder_smoke" + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "Wohnzimmer", + "secondary": "{{ states('sensor.wohnzimmer_rauchmelder_temperature') | default('–') }}°C · {{ states('sensor.wohnzimmer_rauchmelder_humidity') | default('–') }}% · AQI {{ states('sensor.wohnzimmer_rauchmelder_aqi') }}\n", + "icon": "mdi:smoke-detector-variant", + "icon_color": "{{ 'red' if is_state('binary_sensor.wohnzimmer_rauchmelder_smoke','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.wohnzimmer_rauchmelder_smoke" + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "Technikraum", + "secondary": "{{ states('sensor.technikraum_rauchmelder_temperature') }}°C · {{ states('sensor.technikraum_rauchmelder_humidity') }}% · AQI {{ states('sensor.technikraum_rauchmelder_aqi') }}\n", + "icon": "mdi:smoke-detector-variant", + "icon_color": "{{ 'red' if is_state('binary_sensor.technikraum_rauchmelder_smoke','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.technikraum_rauchmelder_smoke" + } + }, + { + "type": "custom:mushroom-title-card", + "title": "💧 Wassermelder" + }, + { + "type": "custom:mushroom-template-card", + "primary": "Küche", + "secondary": "{{ states('sensor.kuche_wassermelder_device_temperature') }}°C · Batterie {{ states('sensor.kuche_wassermelder_battery') }}%\n", + "icon": "mdi:water-alert", + "icon_color": "{{ 'red' if is_state('binary_sensor.kuche_wassermelder_water_leak','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.kuche_wassermelder_water_leak" + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "WC", + "secondary": "{{ states('sensor.wc_wassermelder_device_temperature') }}°C · Batterie {{ states('sensor.wc_wassermelder_battery') }}%\n", + "icon": "mdi:water-alert", + "icon_color": "{{ 'red' if is_state('binary_sensor.wc_wassermelder_water_leak','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.wc_wassermelder_water_leak" + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "Technikraum", + "secondary": "{{ states('sensor.technikraum_wassermelder_device_temperature') }}°C · Batterie {{ states('sensor.technikraum_wassermelder_battery') }}%\n", + "icon": "mdi:water-alert", + "icon_color": "{{ 'red' if is_state('binary_sensor.technikraum_wassermelder_water_leak','on') else 'green' }}\n", + "layout": "horizontal", + "fill_container": true, + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.technikraum_wassermelder_water_leak" + } + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🌡️ Raumklima (via Rauchmelder)" + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Luftqualität (AQI) – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.wohnzimmer_rauchmelder_aqi", + "name": "Wohnzimmer" + }, + { + "entity": "sensor.buro_rauchmelder_aqi", + "name": "Büro" + }, + { + "entity": "sensor.schlafzimmer_rauchmelder_aqi", + "name": "Schlafzimmer" + }, + { + "entity": "sensor.kinderzimmer_rauchmelder_aqi", + "name": "Kinderzimmer" + }, + { + "entity": "sensor.gastezimmer_rauchmelder_aqi", + "name": "Gästezimmer" + }, + { + "entity": "sensor.technikraum_rauchmelder_aqi", + "name": "Technikraum" + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Temperaturen – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.wohnzimmer_rauchmelder_temperature", + "name": "Wohnzimmer" + }, + { + "entity": "sensor.buro_rauchmelder_temperature", + "name": "Büro" + }, + { + "entity": "sensor.kinderzimmer_rauchmelder_temperature", + "name": "Kinderzimmer" + }, + { + "entity": "sensor.schlafzimmer_rauchmelder_temperature", + "name": "Schlafzimmer" + }, + { + "entity": "sensor.gastezimmer_rauchmelder_temperature", + "name": "Gästezimmer" + }, + { + "entity": "sensor.technikraum_rauchmelder_temperature", + "name": "Technikraum" + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Luftfeuchtigkeit – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "yaxis": [ + { + "min": 0, + "max": 100, + "decimals": 0 + } + ], + "series": [ + { + "entity": "sensor.wohnzimmer_rauchmelder_humidity", + "name": "Wohnzimmer" + }, + { + "entity": "sensor.buro_rauchmelder_humidity", + "name": "Büro" + }, + { + "entity": "sensor.kinderzimmer_rauchmelder_humidity", + "name": "Kinderzimmer" + }, + { + "entity": "sensor.schlafzimmer_rauchmelder_humidity", + "name": "Schlafzimmer" + }, + { + "entity": "sensor.gastezimmer_rauchmelder_humidity", + "name": "Gästezimmer" + }, + { + "entity": "sensor.technikraum_rauchmelder_humidity", + "name": "Technikraum" + } + ] + } + ] + } + ] + }, + { + "type": "sections", + "max_columns": 1, + "title": "experiments", + "path": "experiments", + "icon": "fa6-solid:flask", + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Neuer Abschnitt" + }, + { + "type": "history-graph", + "entities": [ + { + "entity": "binary_sensor.technikraum_wasserticker_contact" + } + ], + "hours_to_show": 0.02 + } + ] + } + ], + "cards": [] + } + ] + } + } +} \ No newline at end of file diff --git a/.storage/lovelace.dashboard_spa b/.storage/lovelace.dashboard_spa new file mode 100644 index 0000000..4da92a3 --- /dev/null +++ b/.storage/lovelace.dashboard_spa @@ -0,0 +1,498 @@ +{ + "version": 1, + "minor_version": 1, + "key": "lovelace.dashboard_spa", + "data": { + "config": { + "views": [ + { + "title": "Whirlpool", + "type": "sections", + "max_columns": 2, + "path": "whirlpool", + "icon": "mdi:hot-tub", + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Status & Steuerung" + }, + { + "type": "custom:mushroom-template-card", + "primary": "{% set ph = states('sensor.whirlpool_ph') | float(0) %} {% set orp = states('sensor.whirlpool_oxydo_reduktionspotenzial') | float(0) %} {% set online = is_state('binary_sensor.isonline_2', 'on') %} {% if not online %} Offline ⚠️ {% elif ph >= 7.0 and ph <= 7.6 and orp >= 650 %} Badespaß bereit! 🛁 {% else %} Wasser prüfen! ⚠️ {% endif %}", + "secondary": "{% set temp = states('sensor.spa_current_temperature') | float | round(1) %} {% set ziel = states('sensor.spa_desired_temperature') | float | round(1) %} {% set rate = states('sensor.whirlpool_heizrate') | float(0) | round(2) %} {{ temp }}°C → Ziel {{ ziel }}°C {%- if rate > 0 %} ↑ {{ rate }} °C/min {%- elif rate < 0 %} ↓ {{ rate | abs }} °C/min {%- endif %}", + "icon": "mdi:hot-tub", + "icon_color": "{% set ph = states('sensor.whirlpool_ph') | float(0) %} {% set orp = states('sensor.whirlpool_oxydo_reduktionspotenzial') | float(0) %} {% if not is_state('binary_sensor.isonline_2', 'on') %} grey {% elif ph >= 7.0 and ph <= 7.6 and orp >= 650 %} green {% elif (ph < 6.8 or ph > 7.9) or orp < 550 %} red {% else %} orange {% endif %}", + "vertical": true, + "grid_options": { + "columns": 12, + "rows": 2 + } + }, + { + "type": "custom:mushroom-chips-card", + "chips": [ + { + "type": "template", + "icon": "mdi:ph", + "content": "{{ states(\"sensor.whirlpool_ph\") | float | round(1) }}", + "icon_color": "blue" + }, + { + "type": "template", + "icon": "mdi:water-percent", + "content": "{{ states(\"sensor.whirlpool_oxydo_reduktionspotenzial\") | int }} mV", + "icon_color": "purple" + }, + { + "type": "template", + "icon": "mdi:flash", + "content": "{{ states(\"sensor.whirlpool_leistung\") | int(0) }} W", + "icon_color": "amber" + }, + { + "type": "entity", + "entity": "binary_sensor.isonline_2", + "icon": "mdi:wifi" + } + ], + "alignment": "center" + }, + { + "type": "custom:mushroom-climate-card", + "entity": "climate.spa_thermostat", + "name": "Temperatur einstellen", + "show_temperature_control": true, + "layout": "horizontal" + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-entity-card", + "entity": "switch.whirlpool", + "name": "Strom", + "icon": "mdi:power" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "switch.spa_pump", + "name": "Düsen", + "icon": "mdi:pump" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "switch.spa_light", + "name": "Licht", + "icon": "mdi:lightbulb-on" + } + ] + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.spa_filter_1", + "name": "Filter 1" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.spa_ozone", + "name": "UVC/Ozon" + }, + { + "type": "custom:mushroom-entity-card", + "entity": "sensor.spa_circulation_pump", + "name": "Zirkulation" + } + ] + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-select-card", + "entity": "select.spa_heater_mode", + "name": "Heizmodus", + "icon": "mdi:fire" + }, + { + "type": "custom:mushroom-select-card", + "entity": "select.spa_temperature_range", + "name": "Temp.-Bereich", + "icon": "mdi:thermometer-lines" + } + ] + }, + { + "type": "custom:mushroom-template-card", + "primary": "{% set alerts = states('sensor.spa_total_alerts') | int(0) %} {% if alerts == 0 %} Keine Störungen {% else %} {{ alerts }} Warnung(en) aktiv {% endif %}", + "secondary": "{{ states(\"sensor.spa_fault_message\") }}", + "icon": "{% if states('sensor.spa_total_alerts') | int(0) > 0 %} mdi:alert-circle {% else %} mdi:check-circle {% endif %}", + "icon_color": "{% if states('sensor.spa_total_alerts') | int(0) > 0 %} red {% else %} green {% endif %}", + "fill_container": true, + "grid_options": { + "columns": 12, + "rows": 1 + } + }, + { + "type": "heading", + "heading": "Ambiente" + }, + { + "type": "custom:mushroom-light-card", + "entity": "light.ruheplatz_lichterkette", + "name": "Lichterkette", + "show_brightness_control": true, + "use_light_color": true + }, + { + "type": "custom:mushroom-light-card", + "entity": "light.ruheplatz_wegbeleuchtung", + "name": "Wegbeleuchtung", + "show_brightness_control": true, + "use_light_color": true + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Wasseranalyse (24h)" + }, + { + "type": "custom:apexcharts-card", + "graph_span": "24h", + "header": { + "show": true, + "show_states": true, + "colorize_states": true + }, + "yaxis": [ + { + "id": "ph", + "min": 6.5, + "max": 8, + "decimals": 1, + "apex_config": { + "tickAmount": 3 + } + }, + { + "id": "orp", + "min": 400, + "max": 900, + "opposite": true, + "decimals": 0 + } + ], + "series": [ + { + "entity": "sensor.whirlpool_ph", + "name": "pH", + "yaxis_id": "ph", + "color": "#2196f3", + "stroke_width": 3 + }, + { + "entity": "sensor.whirlpool_oxydo_reduktionspotenzial", + "name": "ORP", + "yaxis_id": "orp", + "color": "#9c27b0", + "stroke_width": 3 + } + ], + "apex_config": { + "annotations": { + "position": "back", + "yaxis": [ + { + "y": 7, + "y2": 7.6, + "y_axis_id": "ph", + "fillColor": "#4CAF50", + "opacity": 0.15, + "label": { + "text": "IDEAL" + } + } + ] + } + } + }, + { + "type": "custom:apexcharts-card", + "graph_span": "24h", + "header": { + "show": true, + "title": "Temperaturverlauf", + "show_states": true, + "colorize_states": true + }, + "series": [ + { + "entity": "sensor.spa_current_temperature", + "name": "Ist", + "color": "#f44336", + "stroke_width": 3 + }, + { + "entity": "sensor.spa_desired_temperature", + "name": "Soll", + "color": "#ff9800", + "stroke_width": 2, + "curve": "stepline" + } + ] + }, + { + "type": "custom:apexcharts-card", + "graph_span": "24h", + "header": { + "show": true, + "title": "Verbrauch (W)", + "show_states": true, + "colorize_states": true + }, + "series": [ + { + "entity": "sensor.whirlpool_leistung", + "name": "Gesamtleistung", + "color": "#FF5722", + "stroke_width": 2, + "type": "area" + } + ] + }, + { + "type": "heading", + "heading": "Energieverbrauch" + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-template-card", + "primary": "{{ states('sensor.spa_heater_energy') | float | round(2) }} kWh", + "secondary": "Heizung", + "icon": "mdi:fire", + "icon_color": "red" + }, + { + "type": "custom:mushroom-template-card", + "primary": "{{ states('sensor.spa_pump_energy') | float | round(2) }} kWh", + "secondary": "Düsen", + "icon": "mdi:pump", + "icon_color": "blue" + }, + { + "type": "custom:mushroom-template-card", + "primary": "{{ states('sensor.spa_circulation_pump_energy') | float | round(2) }} kWh", + "secondary": "Zirkulation", + "icon": "mdi:water-sync", + "icon_color": "cyan" + } + ] + }, + { + "type": "heading", + "heading": "Ondilo ICO Sensor" + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-template-card", + "primary": "{{ states(\"sensor.whirlpool_batterie\") }} %", + "secondary": "Sensor Akku", + "icon": "mdi:battery-bluetooth", + "icon_color": "{% set b = states('sensor.whirlpool_batterie') | int(100) %} {% if b < 20 %} red {% elif b < 50 %} orange {% else %} green {% endif %}" + }, + { + "type": "custom:mushroom-template-card", + "primary": "{{ states(\"sensor.whirlpool_rssi\") }} dBm", + "secondary": "Signal", + "icon": "mdi:wifi", + "icon_color": "{% set r = states('sensor.whirlpool_rssi') | int(-100) %} {% if r < -80 %} red {% elif r < -60 %} orange {% else %} green {% endif %}" + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Pflege-Protokoll" + }, + { + "type": "custom:mushroom-template-card", + "primary": "{% set last = states('input_datetime.whirlpool_letzte_wasserpflege') %} {% if last == 'unknown' or last == '' %}\n Noch kein Datum gesetzt\n{% else %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n Letzte Pflege: vor {{ days }} Tag{{ 'en' if days != 1 else '' }}\n{% endif %}", + "secondary": "{% set last = states('input_datetime.whirlpool_letzte_wasserpflege') %} {% set ph = states('sensor.whirlpool_ph') | float(7.2) %} {% set orp = states('sensor.whirlpool_oxydo_reduktionspotenzial') | float(700) %} {% if last != 'unknown' and last != '' %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n {% if days >= 10 %} ⚠️ Pflege überfällig!\n {% elif days >= 7 %} Pflegerunde empfohlen\n {% else %} Nächste Pflege in {{ 7 - days }} Tagen\n {% endif %}\n{% else %}\n pH + Brom + Wasserrein einmal wöchentlich\n{% endif %} {%- set ph_ok = ph >= 7.0 and ph <= 7.6 %} {%- set orp_ok = orp >= 650 and orp <= 750 %} {{ ' | pH: ✓' if ph_ok else ' | pH: ⚠️ ' ~ ph | round(1) }} {{ ' | ORP: ✓' if orp_ok else ' | ORP: ⚠️ ' ~ orp | round(0) ~ ' mV' }}", + "icon": "mdi:water-check", + "icon_color": "{% set last = states('input_datetime.whirlpool_letzte_wasserpflege') %} {% set ph = states('sensor.whirlpool_ph') | float(7.2) %} {% set orp = states('sensor.whirlpool_oxydo_reduktionspotenzial') | float(700) %} {% if last == 'unknown' or last == '' %} grey {% elif ph < 6.8 or ph > 7.9 or orp < 550 %} red {% else %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n {% if days >= 10 %} red\n {% elif days >= 7 %} orange\n {% else %} green {% endif %}\n{% endif %}", + "fill_container": true, + "grid_options": { + "columns": 12, + "rows": 2 + } + }, + { + "type": "custom:mushroom-entity-card", + "entity": "input_datetime.whirlpool_letzte_wasserpflege", + "name": "Wasserpflege – Datum setzen", + "icon": "mdi:calendar-edit" + }, + { + "type": "custom:mushroom-template-card", + "primary": "{% set last = states('input_datetime.whirlpool_letzter_kartuschenwechsel') %} {% if last == 'unknown' or last == '' %}\n Noch kein Datum gesetzt\n{% else %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n Letzter Kartuschenwechsel: vor {{ days }} Tag{{ 'en' if days != 1 else '' }}\n{% endif %}", + "secondary": "{% set last = states('input_datetime.whirlpool_letzter_kartuschenwechsel') %} {% set intervall = 60 %} {% if last != 'unknown' and last != '' %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n {% set rest = intervall - days %}\n {% if rest <= 0 %} ⚠️ Kartusche wechseln! ({{ days }} Tage alt)\n {% elif rest <= 14 %} Wechsel in {{ rest }} Tagen empfohlen\n {% else %} Nächster Wechsel in {{ rest }} Tagen (alle 2 Monate)\n {% endif %}\n{% else %}\n Empfehlung: alle 60 Tage bei 1–2× Nutzung/Woche\n{% endif %}", + "icon": "mdi:air-filter", + "icon_color": "{% set last = states('input_datetime.whirlpool_letzter_kartuschenwechsel') %} {% if last == 'unknown' or last == '' %} grey {% else %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n {% if days >= 60 %} red\n {% elif days >= 46 %} orange\n {% else %} green {% endif %}\n{% endif %}", + "fill_container": true, + "grid_options": { + "columns": 12, + "rows": 1 + } + }, + { + "type": "custom:mushroom-entity-card", + "entity": "input_datetime.whirlpool_letzter_kartuschenwechsel", + "name": "Kartuschenwechsel – Datum setzen", + "icon": "mdi:calendar-edit" + }, + { + "type": "custom:mushroom-template-card", + "primary": "{% set last = states('input_datetime.whirlpool_letzter_wasserwechsel') %} {% if last == 'unknown' or last == '' %}\n Noch kein Datum gesetzt\n{% else %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n Letzter Wasserwechsel: vor {{ days }} Tag{{ 'en' if days != 1 else '' }}\n{% endif %}", + "secondary": "{% set last = states('input_datetime.whirlpool_letzter_wasserwechsel') %} {% set intervall = 120 %} {% if last != 'unknown' and last != '' %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n {% set rest = intervall - days %}\n {% if rest <= 0 %} ⚠️ Wasserwechsel überfällig! ({{ days }} Tage alt)\n {% elif rest <= 21 %} Wasserwechsel in {{ rest }} Tagen planen\n {% else %} Nächster Wechsel in {{ rest }} Tagen (alle 4 Monate)\n {% endif %}\n{% else %}\n Empfehlung: alle 4 Monate bei 1–2× Nutzung/Woche\n{% endif %}", + "icon": "mdi:water-sync", + "icon_color": "{% set last = states('input_datetime.whirlpool_letzter_wasserwechsel') %} {% if last == 'unknown' or last == '' %} grey {% else %}\n {% set days = ((now().date() - strptime(last[:10], '%Y-%m-%d').date()).days) %}\n {% if days >= 120 %} red\n {% elif days >= 99 %} orange\n {% else %} green {% endif %}\n{% endif %}", + "fill_container": true, + "grid_options": { + "columns": 12, + "rows": 1 + } + }, + { + "type": "custom:mushroom-entity-card", + "entity": "input_datetime.whirlpool_letzter_wasserwechsel", + "name": "Wasserwechsel – Datum setzen", + "icon": "mdi:calendar-edit" + }, + { + "type": "heading", + "heading": "Dosierungsempfehlung" + }, + { + "type": "custom:mushroom-template-card", + "primary": "Dosierung diese Woche", + "secondary": "{% set ph = states('sensor.whirlpool_ph') | float(7.2) %} {% set orp = states('sensor.whirlpool_oxydo_reduktionspotenzial') | float(700) %}\n🧪 pH ({{ ph | round(1) }}): {% if ph >= 7.0 and ph <= 7.6 %} Optimal – nichts nötig. {% elif ph > 7.6 %}\n pH-Minus: ca. {{ (((ph - 7.6) / 0.1) * 10 / 1.1) | round(0) | int }} g zugeben.\n{% else %}\n pH-Plus: ca. {{ (((7.0 - ph) / 0.1) * 10) | round(0) | int }} g zugeben.\n{% endif %} Warte 4h, dann nachmessen. |\n💊 Brom ({{ orp | round(0) }} mV): {% if orp >= 650 and orp <= 750 %} Optimal – nichts nötig. {% elif orp > 750 %} Zu hoch – keine Tabletten, Deckel öffnen. {% elif orp < 600 %} 3–4 Tabletten in Dosierschwimmer. {% else %} 1–2 Tabletten nachlegen. {% endif %} |\n🫧 Wasserrein (Bayrol): 10 ml auf 1.500 l wöchentlich zugeben, Pumpe dabei 30 min laufen lassen.", + "icon": "mdi:flask-outline", + "icon_color": "{% set ph = states('sensor.whirlpool_ph') | float(7.2) %} {% set orp = states('sensor.whirlpool_oxydo_reduktionspotenzial') | float(700) %} {% if ph < 6.8 or ph > 7.9 or orp < 550 %} red {% elif (ph < 7.0 or ph > 7.6) or (orp < 650 or orp > 750) %} orange {% else %} green {% endif %}", + "fill_container": true, + "grid_options": { + "columns": 12, + "rows": 4 + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "Nach Wasserwechsel – Erstbefüllung", + "secondary": "① 30 g Aktivsauerstoff (Bayrol Soft & Easy) direkt zugeben | ② Pumpe 1h laufen lassen | ③ pH messen & korrigieren (Ziel 7,0–7,6) | ④ Brom-Tabletten in Dosierschwimmer legen | ⑤ Nach 24h nochmals alle Werte prüfen", + "icon": "mdi:flask", + "icon_color": "blue", + "fill_container": true, + "grid_options": { + "columns": 12, + "rows": 3 + } + }, + { + "type": "custom:mushroom-template-card", + "primary": "Wöchentliche Pflege-Checkliste", + "secondary": "① pH messen & ggf. korrigieren (Ziel 7,0–7,6) | ② Brom-Tabletten im Dosierschwimmer prüfen & ggf. nachlegen | ③ 10 ml Wasserrein (Bayrol) zugeben | ④ Pumpe mind. 30 min laufen lassen | ⑤ Filtration läuft? (mind. 4–6h/Tag) | ⑥ Abdeckung nach Nutzung schließen", + "icon": "mdi:clipboard-check-outline", + "icon_color": "teal", + "fill_container": true, + "grid_options": { + "columns": 12, + "rows": 3 + } + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "heading", + "heading": "Filterprogramme" + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-select-card", + "entity": "select.spa_filter_time_1", + "name": "Filterzeit 1", + "icon": "mdi:clock-start" + }, + { + "type": "custom:mushroom-select-card", + "entity": "select.spa_filter_duration_1", + "name": "Dauer 1", + "icon": "mdi:timer" + } + ] + }, + { + "type": "horizontal-stack", + "cards": [ + { + "type": "custom:mushroom-select-card", + "entity": "select.spa_filter_time_2", + "name": "Filterzeit 2", + "icon": "mdi:clock-start" + }, + { + "type": "custom:mushroom-select-card", + "entity": "select.spa_filter_duration_2", + "name": "Dauer 2", + "icon": "mdi:timer" + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "switch.whirlpool_filterzyklus_2_aktivieren", + "name": "Filterzyklus 2 aktiv", + "icon": "mdi:filter-check" + }, + { + "type": "heading", + "heading": "Zeitpläne & Automatik" + }, + { + "type": "custom:scheduler-card", + "include": [ + "climate.spa_thermostat", + "switch.spa_pump", + "switch.whirlpool" + ], + "title": "Whirlpool Automatik" + } + ] + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/.storage/lovelace.lovelace b/.storage/lovelace.lovelace new file mode 100644 index 0000000..0546dfe --- /dev/null +++ b/.storage/lovelace.lovelace @@ -0,0 +1,1192 @@ +{ + "version": 1, + "minor_version": 1, + "key": "lovelace.lovelace", + "data": { + "config": { + "views": [ + { + "title": "Home", + "type": "sections", + "icon": "mdi:home", + "max_columns": 2, + "header": { + "layout": "center", + "badges_position": "bottom", + "badges_wrap": "scroll" + }, + "cards": [], + "badges": [ + { + "type": "custom:mushroom-chips-card", + "chips": null + } + ], + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "⚡ Energie" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.powerocean_hj37zdh5zg5w0109_solarertrag_mppt", + "icon": "mdi:solar-power" + }, + { + "type": "entity", + "entity": "sensor.powerocean_hj37zdh5zg5w0109_ladezustand_der_batterie", + "icon": "mdi:battery" + }, + { + "type": "entity", + "entity": "sensor.zuhause_strompreis", + "icon": "mdi:lightning-bolt" + }, + { + "type": "entity", + "entity": "sensor.byd_dolphin_60_5_kwh_abrp_state_of_charge", + "icon": "mdi:car-electric" + } + ] + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "show_states": false, + "standard_format": true, + "disable_actions": true + }, + "apex_config": { + "legend": { + "show": false + } + }, + "yaxis": [ + { + "id": "price", + "min": "~0.1", + "max": "~0.4", + "align_to": 1, + "decimals": 2 + }, + { + "id": "energy", + "opposite": true, + "align_to": 100, + "decimals": 0 + } + ], + "chart_type": "line", + "show": { + "last_updated": true + }, + "graph_span": "1.5d", + "span": { + "start": "day" + }, + "now": { + "show": true, + "label": "Jetzt" + }, + "experimental": { + "color_threshold": true + }, + "series": [ + { + "entity": "sensor.epex_spot_data_total_price", + "yaxis_id": "price", + "stroke_width": 2, + "show": { + "legend_value": false + }, + "type": "line", + "extend_to": false, + "data_generator": "return entity.attributes.data.map((entry) => {\n return [new Date(entry.start_time), entry.price_per_kwh];\n});\n", + "curve": "stepline", + "color_threshold": [ + { + "value": 0.1, + "color": "blue" + }, + { + "value": 0.2, + "color": "cyan" + }, + { + "value": 0.25, + "color": "green" + }, + { + "value": 0.3, + "color": "yellow" + }, + { + "value": 0.35, + "color": "orange" + }, + { + "value": 0.4, + "color": "red" + } + ] + } + ] + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierte_kosten", + "icon": "mdi:currency-eur" + }, + { + "type": "entity", + "entity": "sensor.tibber_pulse_zuhause_kumulierter_verbrauch", + "icon": "mdi:home-lightning-bolt" + }, + { + "type": "entity", + "entity": "sensor.karolina_tracker_batterie_history", + "name": "Lini", + "icon": "fa6-solid:child-reaching" + } + ] + }, + { + "type": "custom:mushroom-entity-card", + "entity": "input_boolean.geschirrspuler_uber_nacht", + "name": "Geschirrspüler über Nacht", + "icon": "mdi:dishwasher", + "tap_action": { + "action": "toggle" + }, + "layout": "horizontal", + "fill_container": true, + "grid_options": { + "columns": 6, + "rows": 1 + } + }, + { + "type": "custom:mushroom-entity-card", + "entity": "binary_sensor.014040394857010622_bsh_common_status_doorstate", + "name": "Geschirrspüler Tür", + "icon": "mdi:door", + "layout": "horizontal", + "fill_container": true, + "grid_options": { + "columns": 6, + "rows": 1 + } + }, + { + "type": "custom:power-flow-card-plus", + "entities": { + "battery": { + "entity": "sensor.powerocean_hj37zdh5zg5w0109_batterieleistung", + "state_of_charge": "sensor.powerocean_hj37zdh5zg5w0109_ladezustand_der_batterie", + "use_metadata": false, + "state_of_charge_unit_white_space": false, + "invert_state": true, + "state_of_charge_unit": "%", + "color_value": true + }, + "grid": { + "entity": { + "consumption": "sensor.powerocean_hj37zdh5zg5w0109_netz_zu_haus", + "production": "sensor.powerocean_hj37zdh5zg5w0109_solar_zu_netz" + }, + "secondary_info": {}, + "use_metadata": false, + "color_value": true, + "invert_state": false, + "display_state": "two_way" + }, + "solar": { + "entity": "sensor.powerocean_hj37zdh5zg5w0109_solarertrag_mppt", + "display_zero_state": true, + "secondary_info": {}, + "color_value": false, + "color_icon": false, + "use_metadata": false, + "invert_state": false + }, + "home": { + "secondary_info": {}, + "circle_animation": false, + "entity": "sensor.powerocean_hj37zdh5zg5w0109_hausverbrauch", + "subtract_individual": false + }, + "fossil_fuel_percentage": { + "secondary_info": {}, + "entity": "sensor.electricity_maps_anteil_fossiler_brennstoffe_im_netz", + "state_type": "percentage", + "display_zero": true, + "display_zero_state": true, + "use_metadata": false, + "unit_white_space": true, + "icon": "mdi:molecule-co2" + }, + "individual": [ + { + "entity": "sensor.energy_log_current_power_consumption_32306", + "name": "Wärmepumpe", + "color_icon": false, + "icon": "mdi:heat-pump", + "secondary_info": {} + }, + { + "entity": "sensor.charger_ac31zeh4ag130052_evpwr", + "secondary_info": {}, + "name": "Wallbox", + "icon": "mdi:car", + "display_zero_state": false, + "color_value": false + }, + { + "entity": "sensor.whirlpool_leistung", + "name": "Whirlpool" + } + ] + }, + "clickable_entities": false, + "display_zero_lines": { + "mode": "transparency", + "transparency": 85, + "grey_color": [ + 189, + 189, + 189 + ] + }, + "use_new_flow_rate_model": true, + "min_flow_rate": 0.75, + "max_flow_rate": 6, + "max_expected_power": 4000, + "min_expected_power": 0.01, + "transparency_zero_lines": 0, + "sort_individual_devices": true, + "disable_dots": false, + "kilo_threshold": 900, + "base_decimals": 0, + "kilo_decimals": 1, + "no_labels": false, + "allow_layout_break": false + }, + { + "type": "custom:mushroom-title-card", + "title": "🔒 Sicherheit" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "start", + "chips": [ + { + "type": "template", + "icon": "mdi:smoke-detector-variant", + "icon_color": "{{ 'red' if is_state('binary_sensor.buro_rauchmelder_smoke','on')\n or is_state('binary_sensor.kinderzimmer_rauchmelder_smoke','on')\n or is_state('binary_sensor.schlafzimmer_rauchmelder_smoke','on')\n or is_state('binary_sensor.gastezimmer_rauchmelder_smoke','on')\n or is_state('binary_sensor.technikraum_rauchmelder_smoke','on')\n or is_state('binary_sensor.wohnzimmer_rauchmelder_smoke','on')\n else 'green' }}\n", + "content": "{% set rooms = {\n 'Büro': 'binary_sensor.buro_rauchmelder_smoke',\n 'Kinderzimmer': 'binary_sensor.kinderzimmer_rauchmelder_smoke',\n 'Schlafzimmer': 'binary_sensor.schlafzimmer_rauchmelder_smoke',\n 'Gästezimmer': 'binary_sensor.gastezimmer_rauchmelder_smoke',\n 'Technikraum': 'binary_sensor.technikraum_rauchmelder_smoke',\n 'Wohnzimmer': 'binary_sensor.wohnzimmer_rauchmelder_smoke'\n} %} {% set alarm = namespace(room='') %} {% for name, entity in rooms.items() %}\n {% if is_state(entity,'on') and alarm.room == '' %}\n {% set alarm.room = name %}\n {% endif %}\n{% endfor %} {{ 'Rauch – ' ~ alarm.room ~ '!' if alarm.room != '' else 'Kein Rauch' }}\n" + }, + { + "type": "template", + "icon": "mdi:water-alert", + "icon_color": "{{ 'red' if is_state('binary_sensor.technikraum_wassermelder_water_leak','on')\n or is_state('binary_sensor.wc_wassermelder_water_leak','on')\n or is_state('binary_sensor.kuche_wassermelder_water_leak','on')\n else 'green' }}\n", + "content": "{% if is_state('binary_sensor.technikraum_wassermelder_water_leak','on') %}\n Wasser – Technikraum!\n{% elif is_state('binary_sensor.wc_wassermelder_water_leak','on') %}\n Wasser – WC!\n{% elif is_state('binary_sensor.kuche_wassermelder_water_leak','on') %}\n Wasser – Küche!\n{% else %}\n Trocken\n{% endif %}\n" + }, + { + "type": "template", + "icon": "mdi:heat-pump-outline", + "icon_color": "{{ 'red' if is_state('binary_sensor.nibe_s1255_6_e_em_3x400v_alarm','on')\n else 'green' }}\n", + "content": "{{ 'WP – Alarm!' if is_state('binary_sensor.nibe_s1255_6_e_em_3x400v_alarm','on')\n else 'WP ok' }}\n", + "tap_action": { + "action": "more-info", + "entity": "binary_sensor.nibe_s1255_6_e_em_3x400v_alarm" + } + } + ] + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🌤️ Wetter & Termine" + }, + { + "type": "custom:mushroom-chips-card", + "chips": [ + { + "type": "entity", + "entity": "calendar.abfallkalender", + "content_info": "state" + }, + { + "type": "entity" + } + ], + "alignment": "center" + }, + { + "type": "custom:clock-weather-card", + "entity": "weather.wetter", + "show_humidity": true, + "hide_clock": true + }, + { + "type": "conditional", + "conditions": [ + { + "condition": "state", + "entity": "sensor.mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_vorwarnstufe", + "state_not": "0" + } + ], + "card": { + "type": "markdown", + "content": "{% set dwd_sensor = \"sensor.mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_vorwarnstufe\" %} {% set type_to_img = {\n 22: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Frost_gelb.png?__blob=normal&v=6\",\n 24: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Glaette_gelb.png?__blob=normal&v=4\",\n 31: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_gelb.png?__blob=normal&v=4\",\n 33: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 34: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 36: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 38: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 40: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 41: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 42: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 44: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 45: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 46: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 48: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 49: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 51: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_gelb.png?__blob=normal&v=4\",\n 52: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_ocker.png?__blob=normal&v=4\",\n 53: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_ocker.png?__blob=normal&v=4\",\n 54: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_rot.png?__blob=normal&v=4\",\n 55: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_rot.png?__blob=normal&v=4\",\n 56: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_lila.png?__blob=normal&v=4\",\n 57: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_gelb.png?__blob=normal&v=4\",\n 58: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_ocker.png?__blob=normal&v=4\",\n 59: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Nebel_gelb.png?__blob=normal&v=4\",\n 61: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_ocker.png?__blob=normal&v=4\",\n 62: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_rot.png?__blob=normal&v=4\",\n 63: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_ocker.png?__blob=normal&v=4\",\n 64: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_rot.png?__blob=normal&v=4\",\n 65: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_lila.png?__blob=normal&v=4\",\n 66: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_lila.png?__blob=normal&v=4\",\n 70: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_gelb.png?__blob=normal&v=4\",\n 71: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_ocker.png?__blob=normal&v=4\",\n 72: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_rot.png?__blob=normal&v=4\",\n 73: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_lila.png?__blob=normal&v=4\",\n 74: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_ocker.png?__blob=normal&v=4\",\n 75: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_rot.png?__blob=normal&v=4\",\n 76: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_lila.png?__blob=normal&v=4\",\n 82: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Frost_ocker.png?__blob=normal&v=4\",\n 84: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Glaette_gelb.png?__blob=normal&v=4\",\n 85: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Glaette_ocker.png?__blob=normal&v=4\",\n 87: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Glaette_rot.png?__blob=normal&v=4\",\n 88: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Tauwetter_ocker.png?__blob=normal&v=5\",\n 89: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Tauwetter_rot.png?__blob=normal&v=5\",\n 90: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_gelb.png?__blob=normal&v=4\",\n 91: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 92: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 93: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 95: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 96: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 246: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/UV_lila.png?__blob=normal&v=4\",\n 247: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Hitze_lila.png?__blob=normal&v=5\",\n 248: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Hitze_dunkellila.png?__blob=normal&v=1\"}\n%} {% for idx in range(1, state_attr(dwd_sensor, \"warning_count\")+1) %} \n \n \n

{{ state_attr(dwd_sensor, (\"warning_\" ~ idx)).headline }}

*Gültig bis {{ as_timestamp(state_attr(dwd_sensor, (\"warning_\" ~ idx)).end_time) | timestamp_local }} Uhr:* {{ state_attr(dwd_sensor, (\"warning_\" ~ idx)).description }} {% endfor %}" + } + }, + { + "type": "conditional", + "conditions": [ + { + "entity": "sensor.mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_aktuelle_warnstufe", + "state_not": "0" + } + ], + "card": { + "type": "markdown", + "content": "{% set dwd_sensor = \"sensor.mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_aktuelle_warnstufe\" %} {% set type_to_img = {\n 22: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Frost_gelb.png?__blob=normal&v=6\",\n 24: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Glaette_gelb.png?__blob=normal&v=4\",\n 31: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_gelb.png?__blob=normal&v=4\",\n 33: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 34: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 36: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 38: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 40: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 41: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 42: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 44: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 45: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 46: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 48: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 49: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 51: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_gelb.png?__blob=normal&v=4\",\n 52: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_ocker.png?__blob=normal&v=4\",\n 53: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_ocker.png?__blob=normal&v=4\",\n 54: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_rot.png?__blob=normal&v=4\",\n 55: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_rot.png?__blob=normal&v=4\",\n 56: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_lila.png?__blob=normal&v=4\",\n 57: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_gelb.png?__blob=normal&v=4\",\n 58: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Wind_ocker.png?__blob=normal&v=4\",\n 59: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Nebel_gelb.png?__blob=normal&v=4\",\n 61: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_ocker.png?__blob=normal&v=4\",\n 62: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_rot.png?__blob=normal&v=4\",\n 63: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_ocker.png?__blob=normal&v=4\",\n 64: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_rot.png?__blob=normal&v=4\",\n 65: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_lila.png?__blob=normal&v=4\",\n 66: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Regen_lila.png?__blob=normal&v=4\",\n 70: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_gelb.png?__blob=normal&v=4\",\n 71: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_ocker.png?__blob=normal&v=4\",\n 72: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_rot.png?__blob=normal&v=4\",\n 73: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_lila.png?__blob=normal&v=4\",\n 74: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_ocker.png?__blob=normal&v=4\",\n 75: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_rot.png?__blob=normal&v=4\",\n 76: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Schnee_lila.png?__blob=normal&v=4\",\n 82: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Frost_ocker.png?__blob=normal&v=4\",\n 84: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Glaette_gelb.png?__blob=normal&v=4\",\n 85: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Glaette_ocker.png?__blob=normal&v=4\",\n 87: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Glaette_rot.png?__blob=normal&v=4\",\n 88: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Tauwetter_ocker.png?__blob=normal&v=5\",\n 89: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Tauwetter_rot.png?__blob=normal&v=5\",\n 90: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_gelb.png?__blob=normal&v=4\",\n 91: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_ocker.png?__blob=normal&v=4\",\n 92: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_rot.png?__blob=normal&v=4\",\n 93: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 95: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 96: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Gewitter_lila.png?__blob=normal&v=4\",\n 246: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/UV_lila.png?__blob=normal&v=4\",\n 247: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Hitze_lila.png?__blob=normal&v=5\",\n 248: \"https://www.dwd.de/DE/wetter/warnungen_aktuell/kriterien/pictogramme/Hitze_dunkellila.png?__blob=normal&v=1\"}\n%} {% for idx in range(1, state_attr(dwd_sensor, \"warning_count\")+1) %} \n \n \n

{{ state_attr(dwd_sensor, (\"warning_\" ~ idx)).headline }}

*Gültig bis {{ as_timestamp(state_attr(dwd_sensor, (\"warning_\" ~ idx)).end_time) | timestamp_local }} Uhr:* {{ state_attr(dwd_sensor, (\"warning_\" ~ idx)).description }} {% endfor %}" + } + }, + { + "event_grouping": true, + "drop_todayevents_from": "10:00:00", + "next_days": 14, + "pattern": [ + { + "icon": "mdi:flower", + "color": "lime", + "type": "organic", + "pattern": "Bio" + }, + { + "icon": "mdi:newspaper", + "color": "blue", + "type": "paper", + "pattern": "Altpapier" + }, + { + "icon": "mdi:recycle-variant", + "color": "amber", + "type": "recycle", + "pattern": "Gelber" + }, + { + "icon": "mdi:trash-can-outline", + "color": "grey", + "type": "waste", + "pattern": "Restmüll", + "label": "Restmüll Tonne" + }, + { + "icon": "mdi:dump-truck", + "color": "purple", + "type": "others" + } + ], + "day_style": "default", + "day_style_format": "yyyy.MM.dd", + "card_style": "card", + "alignment_style": "left", + "color_mode": "icon", + "items_per_row": 1, + "refresh_rate": 60, + "with_label": true, + "layout": "default", + "type": "custom:trash-card", + "entities": [ + "calendar.abfallkalender" + ], + "filter_events": false, + "only_all_day_events": false, + "hide_time_range": true, + "use_summary": false + }, + { + "type": "custom:mushroom-title-card", + "title": "♨️ Wärme & Wasser" + }, + { + "type": "custom:mushroom-chips-card", + "alignment": "center", + "chips": [ + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_hot_water_top_bt7", + "icon": "mdi:water-thermometer" + }, + { + "type": "entity", + "entity": "sensor.nibe_s1255_6_e_em_3x400v_supply_line_bt2", + "icon": "mdi:thermometer-chevron-up" + }, + { + "type": "entity", + "entity": "sensor.nibe_energy_cop_total_last_hour", + "icon": "mdi:gauge-full" + }, + { + "type": "entity", + "entity": "climate.climate_system_s1", + "icon": "mdi:heat-pump-outline" + }, + { + "type": "entity", + "icon": "mdi:heat-pump-outline" + } + ] + }, + { + "type": "custom:mushroom-title-card", + "title": "🏊 Whirlpool" + }, + { + "type": "custom:mushroom-climate-card", + "entity": "climate.spa_thermostat", + "name": "Whirlpool", + "show_temperature_control": true, + "hvac_modes": [ + "heat", + "off" + ], + "fill_container": true, + "grid_options": { + "columns": 12, + "rows": 2 + } + }, + { + "type": "custom:mushroom-chips-card", + "chips": [ + { + "type": "template", + "icon": "mdi:ph", + "content": "{{ states('sensor.whirlpool_ph') | float | round(1) }}", + "icon_color": "blue" + }, + { + "type": "template", + "icon": "mdi:water-percent", + "content": "{{ states('sensor.whirlpool_oxydo_reduktionspotenzial') | int }} mV", + "icon_color": "purple" + } + ], + "alignment": "center" + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🪟 Jalousien & Rollläden" + }, + { + "type": "custom:auto-entities", + "card": { + "type": "grid", + "columns": 2, + "square": false + }, + "card_param": "cards", + "filter": { + "include": [ + { + "domain": "scene", + "options": { + "type": "custom:mushroom-entity-card", + "tap_action": { + "action": "toggle" + } + } + } + ], + "exclude": [] + } + }, + { + "type": "custom:auto-entities", + "card": { + "type": "grid", + "columns": 2, + "square": false + }, + "card_param": "cards", + "filter": { + "include": [ + { + "domain": "cover", + "options": { + "type": "custom:mushroom-cover-card", + "show_buttons_control": true, + "show_position_control": true + } + } + ], + "exclude": [] + } + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "💡 Licht" + }, + { + "type": "custom:auto-entities", + "card": { + "type": "grid", + "columns": 2, + "square": false + }, + "card_param": "cards", + "filter": { + "include": [ + { + "domain": "light", + "options": { + "type": "custom:mushroom-light-card", + "show_brightness_control": true, + "show_color_temp_control": true, + "collapsible_controls": true + } + } + ], + "exclude": [ + { + "hidden_by": "integration" + }, + { + "hidden_by": "user" + }, + { + "area": "grow" + } + ] + }, + "sort": { + "method": "friendly_name" + } + } + ] + }, + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🤖 Saugroboter" + }, + { + "type": "custom:xiaomi-vacuum-map-card", + "vacuum_platform": "Roborock", + "title": "Roborock Qrevo Curv", + "entity": "vacuum.roborock_qrevo_curv", + "map_source": { + "camera": "image.roborock_qrevo_curv_zuhause_custom" + }, + "calibration_source": { + "camera": true + }, + "map_locked": true, + "map_modes": [ + { + "template": "vacuum_clean_segment", + "predefined_selections": [ + { + "id": "1", + "icon": { + "name": "mdi:broom", + "x": 25525, + "y": 30900 + }, + "label": { + "text": "Gang", + "x": 25525, + "y": 30900, + "offset_y": 35 + }, + "outline": [ + [ + 24400, + 28400 + ], + [ + 26650, + 28400 + ], + [ + 26650, + 33400 + ], + [ + 24400, + 33400 + ] + ] + }, + { + "id": "2", + "icon": { + "name": "mdi:broom", + "x": 23700, + "y": 22450 + }, + "label": { + "text": "WC", + "x": 23700, + "y": 22450, + "offset_y": 35 + }, + "outline": [ + [ + 22050, + 21700 + ], + [ + 25350, + 21700 + ], + [ + 25350, + 23200 + ], + [ + 22050, + 23200 + ] + ] + }, + { + "id": "3", + "icon": { + "name": "mdi:broom", + "x": 24300, + "y": 25700 + }, + "label": { + "text": "Speis", + "x": 24300, + "y": 25700, + "offset_y": 35 + }, + "outline": [ + [ + 23350, + 24800 + ], + [ + 25250, + 24800 + ], + [ + 25250, + 26600 + ], + [ + 23350, + 26600 + ] + ] + }, + { + "id": "4", + "icon": { + "name": "mdi:broom", + "x": 23300, + "y": 28675 + }, + "label": { + "text": "Büro", + "x": 23300, + "y": 28675, + "offset_y": 35 + }, + "outline": [ + [ + 21650, + 26950 + ], + [ + 24950, + 26950 + ], + [ + 24950, + 30400 + ], + [ + 21650, + 30400 + ] + ] + }, + { + "id": "5", + "icon": { + "name": "mdi:broom", + "x": 28525, + "y": 30125 + }, + "label": { + "text": "Gästezimmer", + "x": 28525, + "y": 30125, + "offset_y": 35 + }, + "outline": [ + [ + 26150, + 28450 + ], + [ + 30900, + 28450 + ], + [ + 30900, + 31800 + ], + [ + 26150, + 31800 + ] + ] + }, + { + "id": "6", + "icon": { + "name": "mdi:broom", + "x": 23425, + "y": 31200 + }, + "label": { + "text": "Badezimmer", + "x": 23425, + "y": 31200, + "offset_y": 35 + }, + "outline": [ + [ + 21650, + 29600 + ], + [ + 25200, + 29600 + ], + [ + 25200, + 32800 + ], + [ + 21650, + 32800 + ] + ] + }, + { + "id": "7", + "icon": { + "name": "mdi:broom", + "x": 28250, + "y": 33675 + }, + "label": { + "text": "Schlafzimmer", + "x": 28250, + "y": 33675, + "offset_y": 35 + }, + "outline": [ + [ + 26200, + 31600 + ], + [ + 30300, + 31600 + ], + [ + 30300, + 35750 + ], + [ + 26200, + 35750 + ] + ] + }, + { + "id": "8", + "icon": { + "name": "mdi:broom", + "x": 23950, + "y": 34400 + }, + "label": { + "text": "Kinderzimmer", + "x": 23950, + "y": 34400, + "offset_y": 35 + }, + "outline": [ + [ + 21700, + 33050 + ], + [ + 26200, + 33050 + ], + [ + 26200, + 35750 + ], + [ + 21700, + 35750 + ] + ] + }, + { + "id": "9", + "icon": { + "name": "mdi:broom", + "x": 23425, + "y": 20500 + }, + "label": { + "text": "Technikraum", + "x": 23425, + "y": 20500, + "offset_y": 35 + }, + "outline": [ + [ + 21950, + 19450 + ], + [ + 24900, + 19450 + ], + [ + 24900, + 21550 + ], + [ + 21950, + 21550 + ] + ] + }, + { + "id": "10", + "icon": { + "name": "mdi:broom", + "x": 22325, + "y": 23975 + }, + "label": { + "text": "Garderobe", + "x": 22325, + "y": 23975, + "offset_y": 35 + }, + "outline": [ + [ + 19850, + 21400 + ], + [ + 24800, + 21400 + ], + [ + 24800, + 26550 + ], + [ + 19850, + 26550 + ] + ] + }, + { + "id": "11", + "icon": { + "name": "mdi:broom", + "x": 28475, + "y": 26100 + }, + "label": { + "text": "Wohnzimmer", + "x": 28475, + "y": 26100, + "offset_y": 35 + }, + "outline": [ + [ + 24800, + 23400 + ], + [ + 32150, + 23400 + ], + [ + 32150, + 28800 + ], + [ + 24800, + 28800 + ] + ] + }, + { + "id": "12", + "icon": { + "name": "mdi:broom", + "x": 29700, + "y": 21400 + }, + "label": { + "text": "Esszimmer", + "x": 29700, + "y": 21400, + "offset_y": 35 + }, + "outline": [ + [ + 27950, + 19250 + ], + [ + 31450, + 19250 + ], + [ + 31450, + 23550 + ], + [ + 27950, + 23550 + ] + ] + }, + { + "id": "13", + "icon": { + "name": "mdi:broom", + "x": 31575, + "y": 33750 + }, + "label": { + "text": "Room 13", + "x": 31575, + "y": 33750, + "offset_y": 35 + }, + "outline": [ + [ + 30250, + 31750 + ], + [ + 32900, + 31750 + ], + [ + 32900, + 35750 + ], + [ + 30250, + 35750 + ] + ] + }, + { + "id": "14", + "icon": { + "name": "mdi:broom", + "x": 26750, + "y": 21275 + }, + "label": { + "text": "Küche", + "x": 26750, + "y": 21275, + "offset_y": 35 + }, + "outline": [ + [ + 24950, + 19100 + ], + [ + 28550, + 19100 + ], + [ + 28550, + 23450 + ], + [ + 24950, + 23450 + ] + ] + } + ] + }, + { + "template": "vacuum_goto" + } + ] + } + ] + }, + { + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🌬️ Luftqualität (AQI)" + }, + { + "type": "custom:apexcharts-card", + "header": { + "show": true, + "title": "Luftqualität (AQI) – 24 h", + "show_states": true, + "standard_format": true + }, + "graph_span": "24h", + "series": [ + { + "entity": "sensor.wohnzimmer_rauchmelder_aqi", + "name": "Wohnzimmer" + }, + { + "entity": "sensor.buro_rauchmelder_aqi", + "name": "Büro" + }, + { + "entity": "sensor.schlafzimmer_rauchmelder_aqi", + "name": "Schlafzimmer" + }, + { + "entity": "sensor.kinderzimmer_rauchmelder_aqi", + "name": "Kinderzimmer" + }, + { + "entity": "sensor.gastezimmer_rauchmelder_aqi", + "name": "Gästezimmer" + }, + { + "entity": "sensor.technikraum_rauchmelder_aqi", + "name": "Technikraum" + } + ] + } + ] + } + ] + }, + { + "type": "sections", + "max_columns": 1, + "title": "Einkaufsliste", + "path": "einkaufsliste", + "icon": "mdi:cart", + "dense_section_placement": true, + "header": { + "layout": "responsive", + "badges_position": "bottom", + "badges_wrap": "wrap" + }, + "cards": [], + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "custom:mushroom-title-card", + "title": "🛒 Einkaufsliste" + }, + { + "type": "todo-list", + "entity": "todo.einkaufsliste_2", + "title": "Einkaufsliste", + "hide_completed": true, + "hide_section_headers": true + } + ] + } + ] + }, + { + "type": "sections", + "max_columns": 1, + "title": "music", + "path": "music", + "icon": "fa6-brands:itunes-note", + "sections": [ + { + "type": "grid", + "cards": [ + { + "type": "custom:mass-player-card", + "entities": [ + "media_player.unnamed_room_2" + ], + "expressive_scheme": "vibrant", + "default_section": "music_player", + "queue": { + "show_album_covers": true, + "show_artist_names": true + }, + "media_browser": { + "default_section": "recommendations", + "recommendations": {} + } + } + ] + } + ], + "cards": [] + } + ] + } + } +} \ No newline at end of file diff --git a/.storage/lovelace.map b/.storage/lovelace.map new file mode 100644 index 0000000..693208b --- /dev/null +++ b/.storage/lovelace.map @@ -0,0 +1,12 @@ +{ + "version": 1, + "minor_version": 1, + "key": "lovelace.map", + "data": { + "config": { + "strategy": { + "type": "map" + } + } + } +} \ No newline at end of file diff --git a/.storage/lovelace_dashboards b/.storage/lovelace_dashboards new file mode 100644 index 0000000..f380122 --- /dev/null +++ b/.storage/lovelace_dashboards @@ -0,0 +1,54 @@ +{ + "version": 1, + "minor_version": 1, + "key": "lovelace_dashboards", + "data": { + "items": [ + { + "id": "map", + "icon": "mdi:map", + "title": "Karte", + "url_path": "map", + "require_admin": false, + "mode": "storage", + "show_in_sidebar": true + }, + { + "id": "dashboard_haustechnik", + "show_in_sidebar": true, + "title": "Technik", + "require_admin": false, + "mode": "storage", + "url_path": "dashboard-haustechnik", + "icon": "mdi:wrench" + }, + { + "id": "lovelace", + "icon": "mdi:view-dashboard", + "title": "Übersicht", + "url_path": "lovelace", + "mode": "storage", + "require_admin": false, + "show_in_sidebar": true + }, + { + "id": "dashboard_grow", + "show_in_sidebar": true, + "icon": "mdi:cannabis", + "title": "Grow", + "require_admin": false, + "mode": "storage", + "url_path": "dashboard-grow" + }, + { + "id": "dashboard_spa", + "show_in_sidebar": true, + "icon": "mdi:spa", + "title": "Spa", + "require_admin": false, + "mode": "storage", + "url_path": "dashboard-spa" + } + ] + } +} \ No newline at end of file diff --git a/.storage/lovelace_resources b/.storage/lovelace_resources new file mode 100644 index 0000000..8600a36 --- /dev/null +++ b/.storage/lovelace_resources @@ -0,0 +1,104 @@ +{ + "version": 1, + "minor_version": 1, + "key": "lovelace_resources", + "data": { + "items": [ + { + "id": "ea21d63a2ca2461d919284d86bea62c4", + "url": "/hacsfiles/hass-hue-icons/hass-hue-icons.js?hacstag=3844345221253", + "type": "module" + }, + { + "id": "860cd39e0b9b4a14a7a2287a38495c11", + "url": "/hacsfiles/apexcharts-card/apexcharts-card.js?hacstag=331701152223", + "type": "module" + }, + { + "id": "a55e416394c2425a9c813642a219c087", + "url": "/hacsfiles/lovelace-auto-entities/auto-entities.js?hacstag=1677445841161", + "type": "module" + }, + { + "id": "86772e61c8584e0081b0deefdf8bb03d", + "url": "/hacsfiles/numberbox-card/numberbox-card.js?hacstag=304967918417", + "type": "module" + }, + { + "id": "06ff0cbb5d0c4affbdcf4ad1d64aacaa", + "url": "/hacsfiles/hassio-trash-card/trashcard.js?hacstag=678764124247", + "type": "module" + }, + { + "id": "c0876f3fda434950bec3967dd6b0f4af", + "url": "/hacsfiles/vacuum-card/vacuum-card.js?hacstag=2612912952120", + "type": "module" + }, + { + "id": "a9711994054740c08f780a15e066a954", + "url": "/webrtc/webrtc-camera.js?v=v3.6.0", + "type": "module" + }, + { + "id": "2bc385f6fa2f496c9537c0507ac8ccc2", + "url": "/hacsfiles/lovelace-xiaomi-vacuum-map-card/xiaomi-vacuum-map-card.js?hacstag=193372044232", + "type": "module" + }, + { + "id": "f51ee9a679e74256a344b5b924862570", + "url": "/hacsfiles/lovelace-mushroom/mushroom.js?hacstag=444350375511", + "type": "module" + }, + { + "id": "c2d677a4e95a42189bb1af34bd44cc38", + "url": "/hacsfiles/spotify-card-v2/spotify-card-v2.js?hacstag=88521021411", + "type": "module" + }, + { + "id": "508c6e86a63e4d16acbf8e4e7aaaa10a", + "url": "/hacsfiles/mini-media-player/mini-media-player-bundle.js?hacstag=14852083811611", + "type": "module" + }, + { + "id": "11a657e713774a3191dd802c102c9fed", + "url": "/hacsfiles/clock-weather-card/clock-weather-card.js?hacstag=522634019293", + "type": "module" + }, + { + "id": "84f219107ed14b9d8618e9ce083c3e91", + "url": "/hacsfiles/power-flow-card-plus/power-flow-card-plus.js?hacstag=618081815037", + "type": "module" + }, + { + "id": "6621533b33ce4101a1367464c999de79", + "url": "/hacsfiles/scheduler-card/scheduler-card.js?hacstag=2862701574017", + "type": "module" + }, + { + "id": "993a69f87387426cb5712bbe8382555e", + "url": "/hacsfiles/Bubble-Card/bubble-card.js?hacstag=680112919316", + "type": "module" + }, + { + "id": "385152627dee4e749c262ce7af48e242", + "url": "/hacsfiles/lovelace-fold-entity-row/fold-entity-row.js?hacstag=150781994232", + "type": "module" + }, + { + "id": "78d6832cd0df4534ac0476ddeb07ede2", + "url": "/hacsfiles/stack-in-card/stack-in-card.js?hacstag=248954055020", + "type": "module" + }, + { + "id": "1a3f9968eb924fff990c9401c686ca83", + "url": "/hacsfiles/advanced-camera-card/advanced-camera-card.js?hacstag=3940825527274", + "type": "module" + }, + { + "id": "c5057a3229b04e639c8a6bafcd8b9d7e", + "url": "/hacsfiles/mass-player-card/mass-player-card.js?hacstag=1043277514260", + "type": "module" + } + ] + } +} \ No newline at end of file diff --git a/.storage/map b/.storage/map new file mode 100644 index 0000000..cccd9fe --- /dev/null +++ b/.storage/map @@ -0,0 +1,8 @@ +{ + "version": 1, + "minor_version": 1, + "key": "map", + "data": { + "migrated": true + } +} \ No newline at end of file diff --git a/.storage/powercalc_group b/.storage/powercalc_group new file mode 100644 index 0000000..ff8c341 --- /dev/null +++ b/.storage/powercalc_group @@ -0,0 +1,21 @@ +{ + "version": 2, + "minor_version": 1, + "key": "powercalc_group", + "data": { + "sensor.whirlpool_gesamtenergie_energy": { + "sensor.spa_circulation_pump_energy": {"entity_id":"sensor.spa_circulation_pump_energy","state":"483.754","attributes":{"state_class":"total_increasing","last_update_time":"2026-04-27T13:09:03.212088","last_pump_state":"HIGH","unit_of_measurement":"kWh","device_class":"energy","icon":"mdi:lightning-bolt-circle","friendly_name":"Whirlpool Umwälzpumpe Energieverbrauch"},"last_changed":"2026-04-27T11:09:03.212113+00:00","last_reported":"2026-04-27T11:09:03.212113+00:00","last_updated":"2026-04-27T11:09:03.212113+00:00","context":{"id":"01KQ7A1RBCVAABXZ6N81GAJ2KH","parent_id":null,"user_id":null}}, + "sensor.spa_heater_energy": {"entity_id":"sensor.spa_heater_energy","state":"1584.904","attributes":{"state_class":"total_increasing","last_update_time":"2026-04-27T13:09:03.211779","last_heater_state":"OFF","unit_of_measurement":"kWh","device_class":"energy","icon":"mdi:lightning-bolt-circle","friendly_name":"Whirlpool Heater energy consumption"},"last_changed":"2026-04-27T11:00:02.830914+00:00","last_reported":"2026-04-27T11:09:03.211827+00:00","last_updated":"2026-04-27T11:09:03.211827+00:00","context":{"id":"01KQ7A1RBB3TWY00Z55TM1Y9N7","parent_id":null,"user_id":null}}, + "sensor.spa_pump_energy": {"entity_id":"sensor.spa_pump_energy","state":"14.837","attributes":{"state_class":"total_increasing","last_update_time":"2026-04-27T13:09:03.211995","last_pump_state":"OFF","unit_of_measurement":"kWh","device_class":"energy","icon":"mdi:lightning-bolt-circle","friendly_name":"Whirlpool Pumpenenergieverbrauch"},"last_changed":"2026-04-27T09:23:03.104558+00:00","last_reported":"2026-04-27T11:09:03.212024+00:00","last_updated":"2026-04-27T11:09:03.212024+00:00","context":{"id":"01KQ7A1RBCEJ4CSGN95A1STRSW","parent_id":null,"user_id":null}} + }, + "sensor.whirlpool_gesamtenergie": { + "sensor.spa_circulation_pump_energy": {"entity_id":"sensor.spa_circulation_pump_energy","state":"492.776","attributes":{"state_class":"total_increasing","last_update_time":"2026-04-30T13:03:25.823471","last_pump_state":"HIGH","unit_of_measurement":"kWh","device_class":"energy","icon":"mdi:lightning-bolt-circle","friendly_name":"Whirlpool Umwälzpumpe Energieverbrauch"},"last_changed":"2026-04-30T11:03:25.823496+00:00","last_reported":"2026-04-30T11:03:25.823496+00:00","last_updated":"2026-04-30T11:03:25.823496+00:00","context":{"id":"01KQF0XKVZ9GX58KQ04DFAS0BX","parent_id":null,"user_id":null}}, + "sensor.spa_heater_energy": {"entity_id":"sensor.spa_heater_energy","state":"1608.081","attributes":{"state_class":"total_increasing","last_update_time":"2026-04-30T13:03:25.823225","last_heater_state":"OFF","unit_of_measurement":"kWh","device_class":"energy","icon":"mdi:lightning-bolt-circle","friendly_name":"Whirlpool Heater energy consumption"},"last_changed":"2026-04-30T11:00:06.409674+00:00","last_reported":"2026-04-30T11:03:25.823259+00:00","last_updated":"2026-04-30T11:03:25.823259+00:00","context":{"id":"01KQF0XKVZ8B1XC1VXH3TP8AB1","parent_id":null,"user_id":null}}, + "sensor.spa_pump_energy": {"entity_id":"sensor.spa_pump_energy","state":"14.91","attributes":{"state_class":"total_increasing","last_update_time":"2026-04-30T13:03:25.823397","last_pump_state":"OFF","unit_of_measurement":"kWh","device_class":"energy","icon":"mdi:lightning-bolt-circle","friendly_name":"Whirlpool Pumpenenergieverbrauch"},"last_changed":"2026-04-30T10:19:25.584716+00:00","last_reported":"2026-04-30T11:03:25.823422+00:00","last_updated":"2026-04-30T11:03:25.823422+00:00","context":{"id":"01KQF0XKVZCFK29KV975QRCS1X","parent_id":null,"user_id":null}}, + "sensor.charger_ac31zeh4ag130052_evchargingenergy": {"entity_id":"sensor.charger_ac31zeh4ag130052_evchargingenergy","state":"0.0","attributes":{"state_class":"total_increasing","product_description":"evChargingEnergy","product_serial":"AC31ZEH4AG130052","unit_of_measurement":"Wh","device_class":"energy","friendly_name":"Powerpulse evChargingEnergy"},"last_changed":"2026-04-30T10:19:24.150827+00:00","last_reported":"2026-04-30T10:20:24.141960+00:00","last_updated":"2026-04-30T10:19:24.150827+00:00","context":{"id":"01KQEYD03PMX73BPJ96F8B7EM4","parent_id":null,"user_id":null}}, + "sensor.grow_tent_energy": {"entity_id":"sensor.grow_tent_energy","state":"59.38","attributes":{"state_class":"total_increasing","unit_of_measurement":"kWh","device_class":"energy","friendly_name":"Grow Tent Energie"},"last_changed":"2026-04-30T11:01:47.091434+00:00","last_reported":"2026-04-30T11:01:47.091434+00:00","last_updated":"2026-04-30T11:01:47.091434+00:00","context":{"id":"01KQF0TKEKET31BTMNA72TTVTX","parent_id":null,"user_id":null}}, + "sensor.whirlpool_energie": {"entity_id":"sensor.whirlpool_energie","state":"2096.010077","attributes":{"state_class":"total_increasing","unit_of_measurement":"kWh","device_class":"energy","friendly_name":"Whirlpool Energie"},"last_changed":"2026-04-30T11:02:59.995617+00:00","last_reported":"2026-04-30T11:02:59.995617+00:00","last_updated":"2026-04-30T11:02:59.995617+00:00","context":{"id":"01KQF0WTMVJRRZ7R8RRKM3AVSC","parent_id":null,"user_id":null}}, + "sensor.whirlpool_eingespeiste_energie": {"entity_id":"sensor.whirlpool_eingespeiste_energie","state":"0.0","attributes":{"state_class":"total_increasing","unit_of_measurement":"kWh","device_class":"energy","friendly_name":"Whirlpool Eingespeiste Energie"},"last_changed":"2026-04-30T10:58:42.003752+00:00","last_reported":"2026-04-30T10:58:42.003752+00:00","last_updated":"2026-04-30T10:58:42.003752+00:00","context":{"id":"01KQF0MYPK2K678MA5MYAENSW9","parent_id":null,"user_id":null}} + } + } +} \ No newline at end of file diff --git a/.storage/schedule b/.storage/schedule new file mode 100644 index 0000000..efde2a4 --- /dev/null +++ b/.storage/schedule @@ -0,0 +1,56 @@ +{ + "version": 1, + "minor_version": 1, + "key": "schedule", + "data": { + "items": [ + { + "id": "whirlpool_heizung", + "name": "Whirlpool Heizung", + "icon": "mdi:hot-tub", + "monday": [ + { + "from": "09:00:00", + "to": "16:00:00" + } + ], + "tuesday": [ + { + "from": "09:00:00", + "to": "16:00:00" + } + ], + "wednesday": [ + { + "from": "09:00:00", + "to": "16:00:00" + } + ], + "thursday": [ + { + "from": "09:00:00", + "to": "16:00:00" + } + ], + "friday": [ + { + "from": "09:00:00", + "to": "22:00:00" + } + ], + "saturday": [ + { + "from": "09:00:00", + "to": "22:00:00" + } + ], + "sunday": [ + { + "from": "09:00:00", + "to": "16:00:00" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/.storage/scheduler.storage b/.storage/scheduler.storage new file mode 100644 index 0000000..9e18afa --- /dev/null +++ b/.storage/scheduler.storage @@ -0,0 +1,91 @@ +{ + "version": 3, + "minor_version": 1, + "key": "scheduler.storage", + "data": { + "schedules": [ + { + "schedule_id": "a54f12", + "timeslots": [ + { + "start": "00:00:00", + "stop": "10:00:00", + "conditions": [], + "condition_type": null, + "track_conditions": false, + "actions": [ + { + "service": "climate.set_temperature", + "entity_id": "climate.spa_thermostat", + "service_data": { + "hvac_mode": "heat", + "temperature": 20 + } + } + ] + }, + { + "start": "10:00:00", + "stop": "13:00:00", + "conditions": [], + "condition_type": null, + "track_conditions": false, + "actions": [ + { + "service": "climate.set_temperature", + "entity_id": "climate.spa_thermostat", + "service_data": { + "hvac_mode": "heat", + "temperature": 40 + } + } + ] + }, + { + "start": "13:00:00", + "stop": "16:00:00", + "conditions": [], + "condition_type": null, + "track_conditions": false, + "actions": [ + { + "service": "climate.set_temperature", + "entity_id": "climate.spa_thermostat", + "service_data": { + "temperature": 20, + "hvac_mode": "heat" + } + } + ] + }, + { + "start": "16:00:00", + "stop": "00:00:00", + "conditions": [], + "condition_type": null, + "track_conditions": false, + "actions": [ + { + "service": "climate.set_temperature", + "entity_id": "climate.spa_thermostat", + "service_data": { + "hvac_mode": "heat", + "temperature": 20 + } + } + ] + } + ], + "weekdays": [ + "daily" + ], + "start_date": null, + "end_date": null, + "repeat_type": "repeat", + "name": null, + "enabled": false + } + ], + "tags": [] + } +} \ No newline at end of file diff --git a/.storage/timer b/.storage/timer new file mode 100644 index 0000000..7d759dc --- /dev/null +++ b/.storage/timer @@ -0,0 +1,15 @@ +{ + "version": 1, + "minor_version": 1, + "key": "timer", + "data": { + "items": [ + { + "id": "timer_badezimmer_heizung", + "name": "timer_badezimmer_heizung", + "duration": "0:00:00", + "restore": false + } + ] + } +} \ No newline at end of file diff --git a/appdaemon/apps/apps.yaml b/appdaemon/apps/apps.yaml new file mode 100644 index 0000000..1869e04 --- /dev/null +++ b/appdaemon/apps/apps.yaml @@ -0,0 +1,275 @@ +# ========================================================================= +# BÜRO +# WS-EUK03 – operation_mode: decoupled ✓ +# ========================================================================= +buro_licht: + module: controllerx + class: LightController + controller: Büro Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.buro_decke + mapping: + single: toggle + +buro_schreibtisch_switch: + module: controllerx + class: WXKG01LMLightController + controller: Büro Schreibtischschalter + integration: + name: z2m + listen_to: mqtt + light: light.buro_schreibtisch + +# ========================================================================= +# WC +# WS-EUK03 – operation_mode: decoupled ✓ +# WXKG15LM – operation_mode: event ✓ +# ========================================================================= +wc_licht: + module: controllerx + class: LightController + controller: WC Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.wc_decke + mapping: + single: toggle + double: + - service: light.turn_off + entity_id: light.wc_decke + - service: light.turn_off + entity_id: light.technikraum_decke + +wc_wireless: + module: controllerx + class: WXKG15LMLightController + controller: WC Wireless Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.wc_decke + mapping: + single_left: + service: light.toggle + entity_id: light.wc_decke + single_right: + service: light.toggle + entity_id: light.wc_spiegel + +# ========================================================================= +# BADEZIMMER +# WS-EUK03 – operation_mode: decoupled ✓ +# WXKG15LM – operation_mode: event ✓ +# ========================================================================= +badezimmer_lichtschalter: + module: controllerx + class: LightController + controller: Badezimmer Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.badezimmer_decke + mapping: + single: toggle + +badezimmer_h1w: + module: controllerx + class: WXKG15LMLightController + controller: Badezimmer Wireless Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.badezimmer_decke + mapping: + single_left: + service: light.toggle + entity_id: light.badezimmer_decke + single_right: + service: light.toggle + entity_id: light.badezimmer_spiegel + +# ========================================================================= +# WOHNBEREICH & GANG +# WS-EUK04 – operation_mode_left/right: decoupled ✓ +# ========================================================================= +wohnzimmer_garderobe_h1: + module: controllerx + class: LightController + controller: Gang Wohnzimmer Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.garderobe_spots + mapping: + single_left: + service: light.toggle + entity_id: light.garderobe_spots + single_right: + service: light.toggle + entity_id: light.gang_spots + +esszimmer_kuche_h1: + module: controllerx + class: LightController + controller: Wohnzimmer Küche Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.esszimmer_decke + mapping: + single_left: + service: light.toggle + entity_id: light.esszimmer_decke + single_right: + service: light.toggle + entity_id: light.kuche_spots + +wohnzimmer_gang_h1: + module: controllerx + class: LightController + controller: Esszimmer Gang Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.wohnzimmer_decke + mapping: + single_left: + service: light.toggle + entity_id: light.wohnzimmer_decke + single_right: + - service: light.toggle + entity_id: light.gang_spots + - service: light.toggle + entity_id: light.wohnzimmer_spots + +# WS-EUK03 – operation_mode: decoupled ✓ +gang_h1: + module: controllerx + class: LightController + controller: Gang Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.gang_spots + mapping: + single: + - service: light.toggle + entity_id: light.gang_spots + - service: light.toggle + entity_id: light.wohnzimmer_spots + double: + service: input_button.press + entity_id: input_button.nachtmodus + +wohnzimmer_button: + module: controllerx + class: WXKG01LMLightController + controller: Wohnzimmer Button + integration: + name: z2m + listen_to: mqtt + light: light.wohnzimmer_decke + mapping: + single: + service: switch.toggle + entity_id: switch.wohnzimmer_steckdose + double: + service: input_button.press + entity_id: input_button.nachtmodus + +# ========================================================================= +# SCHLAFRÄUME, KINDERZIMMER & SPEIS +# WS-EUK03 – decoupled ✓ (Gästezimmer & Speis noch setzen!) +# ========================================================================= +kinderzimmer_bett: + module: controllerx + class: WXKG01LMLightController + controller: Kinderzimmer Bettschalter + integration: + name: z2m + listen_to: mqtt + light: light.kinderzimmer_spots + +kinderzimmer_lichtschalter: + module: controllerx + class: LightController + controller: Kinderzimmer Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.kinderzimmer_spots + mapping: + single: toggle + +schlafzimmer_h1: + module: controllerx + class: LightController + controller: Schlafzimmer Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.schlafzimmer_decke + mapping: + single: toggle + +gastezimmer_h1: + module: controllerx + class: LightController + controller: Gästezimmer Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.gastezimmer_decke + mapping: + single: toggle + +speis_h1: + module: controllerx + class: LightController + controller: Speis Lichtschalter + integration: + name: z2m + listen_to: mqtt + light: light.speis_decke + mapping: + single: toggle + +# ========================================================================= +# TERRASSE & AUSSENBEREICH +# ========================================================================= + +# WS-EUK03 – operation_mode: decoupled ✓ +esszimmer_terrasse: + module: controllerx + class: LightController + controller: Esszimmer Lichtschalter Terrasse + integration: + name: z2m + listen_to: mqtt + light: light.grillplatz_wandleuchte + mapping: + single: toggle + +# WS-EUK03 – decoupled noch setzen! +wohnzimmer_terrasse: + module: controllerx + class: LightController + controller: Wohnzimmer Lichtschalter Terrasse + integration: + name: z2m + listen_to: mqtt + light: light.terrasse_wandleuchte + mapping: + single: toggle + +terrasse_remote: + module: controllerx + class: TS0044LightController + controller: Terrasse Fernbedienung + integration: + name: z2m + listen_to: mqtt + light: light.technikraum_decke diff --git a/appdaemon/apps/controllerx/controllerx.py b/appdaemon/apps/controllerx/controllerx.py new file mode 100644 index 0000000..0f93898 --- /dev/null +++ b/appdaemon/apps/controllerx/controllerx.py @@ -0,0 +1,39 @@ +""" +Create controller-based automations with ease to control your home devices and scenes. + +https://github.com/xaviml/controllerx +""" + +from cx_core import ( + Controller, + CoverController, + LightController, + MediaPlayerController, + SwitchController, + Z2MLightController, +) +from cx_devices.adeo import * +from cx_devices.aqara import * +from cx_devices.aurora import * +from cx_devices.homematic import * +from cx_devices.ikea import * +from cx_devices.legrand import * +from cx_devices.linkind import * +from cx_devices.livarno import * +from cx_devices.lutron import * +from cx_devices.muller_licht import * +from cx_devices.namron import * +from cx_devices.osram import * +from cx_devices.philips import * +from cx_devices.prolight import * +from cx_devices.rgb_genie import * +from cx_devices.robb import * +from cx_devices.sengled import * +from cx_devices.shelly import * +from cx_devices.smartkontakten import * +from cx_devices.smartthings import * +from cx_devices.sonoff import * +from cx_devices.tasmota import * +from cx_devices.terncy import * +from cx_devices.trust import * +from cx_devices.tuya import * diff --git a/appdaemon/apps/controllerx/cx_const.py b/appdaemon/apps/controllerx/cx_const.py new file mode 100644 index 0000000..44265c8 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_const.py @@ -0,0 +1,150 @@ +from collections.abc import Awaitable, Callable +from typing import Any, Optional, Union + +ActionFunction = Callable[..., Awaitable[Any]] +ActionParams = tuple[Any, ...] +ActionFunctionWithParams = tuple[ActionFunction, ActionParams] +TypeAction = Union[ActionFunction, ActionFunctionWithParams] +ActionEvent = Union[str, int] +PredefinedActionsMapping = dict[str, TypeAction] +DefaultActionsMapping = dict[ActionEvent, Optional[str]] + +CustomAction = Union[str, dict[str, Any]] +CustomActions = Union[list[CustomAction], CustomAction] +CustomActionsMapping = dict[ActionEvent, Optional[CustomActions]] + +Number = Union[int, float] + + +class Light: + ON = "on" + OFF = "off" + TOGGLE = "toggle" + TOGGLE_FULL_BRIGHTNESS = "toggle_full_brightness" + TOGGLE_FULL_WHITE_VALUE = "toggle_full_white_value" + TOGGLE_FULL_COLOR_TEMP = "toggle_full_color_temp" + TOGGLE_MIN_BRIGHTNESS = "toggle_min_brightness" + TOGGLE_MIN_WHITE_VALUE = "toggle_min_white_value" + TOGGLE_MIN_COLOR_TEMP = "toggle_min_color_temp" + RELEASE = "release" + ON_FULL_BRIGHTNESS = "on_full_brightness" + ON_FULL_WHITE_VALUE = "on_full_white_value" + ON_FULL_COLOR_TEMP = "on_full_color_temp" + ON_MIN_BRIGHTNESS = "on_min_brightness" + ON_MIN_WHITE_VALUE = "on_min_white_value" + ON_MIN_COLOR_TEMP = "on_min_color_temp" + ON_MIN_MAX_BRIGHTNESS = "on_min_max_brightness" + ON_MAX_MIN_BRIGHTNESS = "on_max_min_brightness" + ON_MIN_MAX_COLOR_TEMP = "on_min_max_color_temp" + ON_MAX_MIN_COLOR_TEMP = "on_max_min_color_temp" + SET_HALF_BRIGHTNESS = "set_half_brightness" + SET_HALF_WHITE_VALUE = "set_half_white_value" + SET_HALF_COLOR_TEMP = "set_half_color_temp" + SYNC = "sync" + CLICK = "click" + CLICK_BRIGHTNESS_UP = "click_brightness_up" + CLICK_BRIGHTNESS_DOWN = "click_brightness_down" + CLICK_WHITE_VALUE_UP = "click_white_value_up" + CLICK_WHITE_VALUE_DOWN = "click_white_value_down" + CLICK_COLOR_UP = "click_color_up" + CLICK_COLOR_DOWN = "click_color_down" + CLICK_COLOR_TEMP_UP = "click_colortemp_up" + CLICK_COLOR_TEMP_DOWN = "click_colortemp_down" + CLICK_XY_COLOR_UP = "click_xycolor_up" + CLICK_XY_COLOR_DOWN = "click_xycolor_down" + HOLD = "hold" + HOLD_BRIGHTNESS_UP = "hold_brightness_up" + HOLD_BRIGHTNESS_DOWN = "hold_brightness_down" + HOLD_BRIGHTNESS_TOGGLE = "hold_brightness_toggle" + HOLD_WHITE_VALUE_UP = "hold_white_value_up" + HOLD_WHITE_VALUE_DOWN = "hold_white_value_down" + HOLD_WHITE_VALUE_TOGGLE = "hold_white_value_toggle" + HOLD_COLOR_UP = "hold_color_up" + HOLD_COLOR_DOWN = "hold_color_down" + HOLD_COLOR_TOGGLE = "hold_color_toggle" + HOLD_COLOR_TEMP_UP = "hold_colortemp_up" + HOLD_COLOR_TEMP_DOWN = "hold_colortemp_down" + HOLD_COLOR_TEMP_TOGGLE = "hold_colortemp_toggle" + HOLD_XY_COLOR_UP = "hold_xycolor_up" + HOLD_XY_COLOR_DOWN = "hold_xycolor_down" + HOLD_XY_COLOR_TOGGLE = "hold_xycolor_toggle" + XYCOLOR_FROM_CONTROLLER = "xycolor_from_controller" + COLORTEMP_FROM_CONTROLLER = "colortemp_from_controller" + COLORTEMP_FROM_CONTROLLER_STEP = "colortemp_from_controller_step" + BRIGHTNESS_FROM_CONTROLLER_LEVEL = "brightness_from_controller_level" + BRIGHTNESS_FROM_CONTROLLER_ANGLE = "brightness_from_controller_angle" + BRIGHTNESS_FROM_CONTROLLER_STEP = "brightness_from_controller_step" + + +class Z2MLight: + ON = "on" + OFF = "off" + TOGGLE = "toggle" + RELEASE = "release" + ON_FULL_BRIGHTNESS = "on_full_brightness" + ON_FULL_COLOR_TEMP = "on_full_color_temp" + ON_MIN_BRIGHTNESS = "on_min_brightness" + ON_MIN_COLOR_TEMP = "on_min_color_temp" + SET_HALF_BRIGHTNESS = "set_half_brightness" + SET_HALF_COLOR_TEMP = "set_half_color_temp" + CLICK = "click" + CLICK_BRIGHTNESS_UP = "click_brightness_up" + CLICK_BRIGHTNESS_DOWN = "click_brightness_down" + CLICK_COLOR_TEMP_UP = "click_colortemp_up" + CLICK_COLOR_TEMP_DOWN = "click_colortemp_down" + HOLD = "hold" + HOLD_BRIGHTNESS_UP = "hold_brightness_up" + HOLD_BRIGHTNESS_DOWN = "hold_brightness_down" + HOLD_BRIGHTNESS_TOGGLE = "hold_brightness_toggle" + HOLD_COLOR_TEMP_UP = "hold_colortemp_up" + HOLD_COLOR_TEMP_DOWN = "hold_colortemp_down" + HOLD_COLOR_TEMP_TOGGLE = "hold_colortemp_toggle" + XYCOLOR_FROM_CONTROLLER = "xycolor_from_controller" + COLORTEMP_FROM_CONTROLLER = "colortemp_from_controller" + BRIGHTNESS_FROM_CONTROLLER_LEVEL = "brightness_from_controller_level" + BRIGHTNESS_FROM_CONTROLLER_ANGLE = "brightness_from_controller_angle" + + +class MediaPlayer: + HOLD_VOLUME_DOWN = "hold_volume_down" + HOLD_VOLUME_UP = "hold_volume_up" + CLICK_VOLUME_DOWN = "click_volume_down" + CLICK_VOLUME_UP = "click_volume_up" + VOLUME_SET = "volume_set" + RELEASE = "release" + PLAY = "play" + PAUSE = "pause" + PLAY_PAUSE = "play_pause" + NEXT_TRACK = "next_track" + PREVIOUS_TRACK = "previous_track" + NEXT_SOURCE = "next_source" + PREVIOUS_SOURCE = "previous_source" + MUTE = "mute" + TTS = "tts" + VOLUME_FROM_CONTROLLER_ANGLE = "volume_from_controller_angle" + + +class Switch: + ON = "on" + OFF = "off" + TOGGLE = "toggle" + + +class Cover: + OPEN = "open" + CLOSE = "close" + STOP = "stop" + TOGGLE_OPEN = "toggle_open" + TOGGLE_CLOSE = "toggle_close" + + +class StepperDir: + UP = "up" + DOWN = "down" + TOGGLE = "toggle" + + +class StepperMode: + STOP = "stop" + LOOP = "loop" + BOUNCE = "bounce" diff --git a/appdaemon/apps/controllerx/cx_core/__init__.py b/appdaemon/apps/controllerx/cx_core/__init__.py new file mode 100644 index 0000000..539ec28 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/__init__.py @@ -0,0 +1,18 @@ +from cx_core.controller import Controller, action +from cx_core.release_hold_controller import ReleaseHoldController +from cx_core.type.cover_controller import CoverController +from cx_core.type.light_controller import LightController +from cx_core.type.media_player_controller import MediaPlayerController +from cx_core.type.switch_controller import SwitchController +from cx_core.type.z2m_light_controller import Z2MLightController + +__all__ = [ + "Controller", + "ReleaseHoldController", + "LightController", + "Z2MLightController", + "MediaPlayerController", + "SwitchController", + "CoverController", + "action", +] diff --git a/appdaemon/apps/controllerx/cx_core/action_type/__init__.py b/appdaemon/apps/controllerx/cx_core/action_type/__init__.py new file mode 100644 index 0000000..37f8e37 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/action_type/__init__.py @@ -0,0 +1,46 @@ +from typing import TYPE_CHECKING + +from cx_const import ActionEvent, CustomAction, CustomActions +from cx_core.action_type.base import ActionType +from cx_core.action_type.call_service_action_type import CallServiceActionType +from cx_core.action_type.delay_action_type import DelayActionType +from cx_core.action_type.predefined_action_type import PredefinedActionType +from cx_core.action_type.scene_action_type import SceneActionType + +if TYPE_CHECKING: + from cx_core import Controller + +ActionsMapping = dict[ActionEvent, list[ActionType]] + +action_type_mapping: dict[str, type[ActionType]] = { + "action": PredefinedActionType, + "service": CallServiceActionType, + "scene": SceneActionType, + "delay": DelayActionType, +} + + +def parse_actions(controller: "Controller", data: CustomActions) -> list[ActionType]: + actions: CustomActions + if isinstance(data, (list, tuple)): + actions = list(data) + else: + actions = [data] + + return [_parse_action(controller, action) for action in actions] + + +def _parse_action(controller: "Controller", action: CustomAction) -> ActionType: + if isinstance(action, str): + return PredefinedActionType(controller, {"action": action}) + try: + return next( + action_type(controller, action) + for key in action + for action_type_key, action_type in action_type_mapping.items() + if key == action_type_key + ) + except StopIteration: + raise ValueError( + f"Not able to parse `{action}`. Available keys are: {list(action_type_mapping.keys())}" + ) diff --git a/appdaemon/apps/controllerx/cx_core/action_type/base.py b/appdaemon/apps/controllerx/cx_core/action_type/base.py new file mode 100644 index 0000000..9aa361e --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/action_type/base.py @@ -0,0 +1,25 @@ +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any + +from cx_core.integration import EventData + +if TYPE_CHECKING: + from cx_core import Controller + + +class ActionType(ABC): + controller: "Controller" + + def __init__(self, controller: "Controller", action: dict[str, Any]) -> None: + self.controller = controller + self.initialize(**action) + + def initialize(self, **kwargs: Any) -> None: + pass + + @abstractmethod + async def run(self, extra: EventData | None = None) -> None: + raise NotImplementedError + + def __str__(self) -> str: + return f"{self.__class__.__name__}" diff --git a/appdaemon/apps/controllerx/cx_core/action_type/call_service_action_type.py b/appdaemon/apps/controllerx/cx_core/action_type/call_service_action_type.py new file mode 100644 index 0000000..3f6fae0 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/action_type/call_service_action_type.py @@ -0,0 +1,52 @@ +from typing import TYPE_CHECKING, Any, cast + +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData + +if TYPE_CHECKING: + from cx_core.type_controller import Entity, TypeController + + +class CallServiceActionType(ActionType): + service: str + # Priority order for entity_id: + # - Inside data + # - In the same level as "service" + # - From the main config if the domain matches + entity_id: str | None + data: dict[str, Any] + + def initialize(self, **kwargs: Any) -> None: + self.service = kwargs["service"] + self.data = kwargs.get("data", {}) + + self.entity_id = self.data.get("entity_id") or kwargs.get("entity_id") + if ( + self.entity_id is None + and self._check_controller_isinstance_type_controller() + ): + type_controller = cast("TypeController[Entity]", self.controller) + if self._get_service_domain(self.service) in type_controller.domains: + self.entity_id = type_controller.entity.name + if "entity_id" in self.data: + del self.data["entity_id"] + + def _check_controller_isinstance_type_controller(self) -> bool: + # This is checked dynamically without the isinstance to avoid + # circular dependency + class_names = [c.__name__ for c in type(self.controller).mro()] + return "TypeController" in class_names + + def _get_service_domain(self, service: str) -> str: + return service.replace(".", "/").split("/")[0] + + async def run(self, extra: EventData | None = None) -> None: + if self.entity_id: + await self.controller.call_service( + self.service, entity_id=self.entity_id, **self.data + ) + else: + await self.controller.call_service(self.service, **self.data) + + def __str__(self) -> str: + return f"Service ({self.service})" diff --git a/appdaemon/apps/controllerx/cx_core/action_type/delay_action_type.py b/appdaemon/apps/controllerx/cx_core/action_type/delay_action_type.py new file mode 100644 index 0000000..6978dca --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/action_type/delay_action_type.py @@ -0,0 +1,17 @@ +from typing import Any + +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData + + +class DelayActionType(ActionType): + delay: int + + def initialize(self, **kwargs: Any) -> None: + self.delay = kwargs["delay"] + + async def run(self, extra: EventData | None = None) -> None: + await self.controller.sleep(self.delay) + + def __str__(self) -> str: + return f"Delay ({self.delay} seconds)" diff --git a/appdaemon/apps/controllerx/cx_core/action_type/predefined_action_type.py b/appdaemon/apps/controllerx/cx_core/action_type/predefined_action_type.py new file mode 100644 index 0000000..c162409 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/action_type/predefined_action_type.py @@ -0,0 +1,121 @@ +import inspect +from typing import Any + +from cx_const import ( + ActionFunction, + ActionFunctionWithParams, + ActionParams, + PredefinedActionsMapping, + TypeAction, +) +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData + + +def _get_action(action_value: TypeAction) -> ActionFunctionWithParams: + if isinstance(action_value, tuple): + return action_value + else: + return (action_value, tuple()) + + +def _get_arguments( + action: ActionFunction, + args: ActionParams, + predefined_action_kwargs: dict[str, Any], + extra: EventData | None, +) -> tuple[ActionParams, dict[str, Any]]: + action_parameters = inspect.signature(action).parameters + action_parameters_without_extra = { + key: param for key, param in action_parameters.items() if key != "extra" + } + action_parameters_without_default = { + key: param + for key, param in action_parameters.items() + if param.default is inspect.Signature.empty + } + action_args: dict[str, Any] = dict( + zip(action_parameters_without_extra.keys(), args) + ) # ControllerX args + action_positional_args = set(action_args.keys()) + action_args.update(predefined_action_kwargs) # User args + action_args.update({"extra": extra} if "extra" in action_parameters else {}) + action_args = { + key: value for key, value in action_args.items() if key in action_parameters + } + + if len(set(action_parameters_without_default).difference(action_args)) != 0: + error_msg = [ + f"`{action.__name__}` action is missing some parameters. Parameters available:" + ] + for key, param in action_parameters_without_extra.items(): + if hasattr(param.annotation, "__name__"): + attr_msg = f" {key}: {param.annotation.__name__}" + else: + attr_msg = f" {key}:" + if param.default is not inspect.Signature.empty: + attr_msg += f" [default: {param.default}]" + if key in action_args: + attr_msg += f" (value given: {action_args[key]})" + elif param.default is inspect.Signature.empty: + attr_msg += " (missing)" + error_msg.append(attr_msg) + raise ValueError("\n".join(error_msg)) + + positional = tuple( + value for key, value in action_args.items() if key in action_positional_args + ) + action_args = { + key: value + for key, value in action_args.items() + if key not in action_positional_args + } + return positional, action_args + + +class PredefinedActionType(ActionType): + predefined_action_key: str + predefined_action_kwargs: dict[str, Any] + predefined_actions_mapping: PredefinedActionsMapping + + def _raise_action_key_not_found( + self, predefined_action_key: str, predefined_actions: PredefinedActionsMapping + ) -> None: + raise ValueError( + f"`{predefined_action_key}` is not one of the predefined actions. " + f"Available actions are: {list(predefined_actions.keys())}." + "See more in: https://xaviml.github.io/controllerx/advanced/custom-controllers" + ) + + def initialize(self, **kwargs: Any) -> None: + self.predefined_action_key = kwargs.pop("action") + self.predefined_action_kwargs = kwargs + self.predefined_actions_mapping = ( + self.controller.get_predefined_actions_mapping() + ) + if not self.predefined_actions_mapping: + raise ValueError( + f"Cannot use predefined actions for `{self.controller.__class__.__name__}` class." + ) + if ( + not self.controller.contains_templating(self.predefined_action_key) + and self.predefined_action_key not in self.predefined_actions_mapping + ): + self._raise_action_key_not_found( + self.predefined_action_key, self.predefined_actions_mapping + ) + + async def run(self, extra: EventData | None = None) -> None: + action_key = await self.controller.render_value(self.predefined_action_key) + if action_key not in self.predefined_actions_mapping: + self._raise_action_key_not_found( + action_key, self.predefined_actions_mapping + ) + action, args = _get_action(self.predefined_actions_mapping[action_key]) + positional, action_args = _get_arguments( + action, args, self.predefined_action_kwargs, extra + ) + await action(*positional, **action_args) + + def __str__(self) -> str: + return f"Predefined ({self.predefined_action_key})" diff --git a/appdaemon/apps/controllerx/cx_core/action_type/scene_action_type.py b/appdaemon/apps/controllerx/cx_core/action_type/scene_action_type.py new file mode 100644 index 0000000..76af527 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/action_type/scene_action_type.py @@ -0,0 +1,17 @@ +from typing import Any + +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData + + +class SceneActionType(ActionType): + scene: str + + def initialize(self, **kwargs: Any) -> None: + self.scene = kwargs["scene"] + + async def run(self, extra: EventData | None = None) -> None: + await self.controller.call_service("scene/turn_on", entity_id=self.scene) + + def __str__(self) -> str: + return f"Scene ({self.scene})" diff --git a/appdaemon/apps/controllerx/cx_core/color_helper.py b/appdaemon/apps/controllerx/cx_core/color_helper.py new file mode 100644 index 0000000..ab4f646 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/color_helper.py @@ -0,0 +1,81 @@ +Color = tuple[float, float] +Colors = list[Color] + +# These are the 24 colors that appear in the circle color of home assistant +default_color_wheel = [ + (0.701, 0.299), + (0.667, 0.284), + (0.581, 0.245), + (0.477, 0.196), + (0.385, 0.155), + (0.301, 0.116), + (0.217, 0.077), + (0.157, 0.05), + (0.136, 0.04), + (0.137, 0.065), + (0.141, 0.137), + (0.146, 0.238), + (0.323, 0.329), # 12; white color middle + (0.151, 0.343), + (0.157, 0.457), + (0.164, 0.591), + (0.17, 0.703), + (0.172, 0.747), + (0.199, 0.724), + (0.269, 0.665), + (0.36, 0.588), + (0.444, 0.517), + (0.527, 0.447), + (0.612, 0.374), + (0.677, 0.319), +] + +# These are the xy colors translated from color temperature (2000K to 6488K) +# They were extracted from here https://www.waveformlighting.com/files/blackBodyLocus_1.txt +color_temp_wheel = [ + (0.527, 0.413), + (0.507, 0.415), + (0.489, 0.415), + (0.472, 0.413), + (0.456, 0.41), + (0.442, 0.406), + (0.428, 0.401), + (0.416, 0.396), + (0.406, 0.391), + (0.396, 0.386), + (0.386, 0.38), + (0.378, 0.375), + (0.37, 0.37), + (0.363, 0.365), + (0.357, 0.361), + (0.351, 0.358), + (0.345, 0.355), + (0.34, 0.352), + (0.336, 0.349), + (0.331, 0.346), + (0.327, 0.343), + (0.323, 0.339), + (0.32, 0.336), + (0.317, 0.333), + (0.314, 0.33), +] + +COLOR_WHEELS = { + "default_color_wheel": default_color_wheel, + "color_temp_wheel": color_temp_wheel, +} + + +def get_color_wheel(colors: str | Colors) -> Colors: + if isinstance(colors, str): + if colors not in COLOR_WHEELS: + raise ValueError( + f"`{colors}` is not an option for `color_wheel`. Options are: {list(COLOR_WHEELS.keys())}" + ) + return COLOR_WHEELS[colors] + elif isinstance(colors, (list, tuple)): + return colors + else: + raise ValueError( + f"Type {type(colors)} is not supported for `color_wheel` attribute" + ) diff --git a/appdaemon/apps/controllerx/cx_core/controller.py b/appdaemon/apps/controllerx/cx_core/controller.py new file mode 100644 index 0000000..5659c16 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/controller.py @@ -0,0 +1,611 @@ +import asyncio +import re +import time +from ast import literal_eval +from asyncio import CancelledError, Task +from collections import Counter, defaultdict +from collections.abc import Awaitable, Callable +from functools import wraps +from typing import ( + Any, + DefaultDict, + Literal, + Optional, + TypeVar, + overload, +) + +import appdaemon.utils as utils +import cx_core.integration as integration_module +import cx_version +from appdaemon.adapi import ADAPI +from appdaemon.plugins.hass.hassapi import Hass +from appdaemon.plugins.mqtt.mqttapi import Mqtt +from cx_const import ( + ActionEvent, + ActionFunction, + CustomActionsMapping, + DefaultActionsMapping, + PredefinedActionsMapping, +) +from cx_core.action_type import ActionsMapping, parse_actions +from cx_core.action_type.base import ActionType +from cx_core.integration import EventData, Integration + +DEFAULT_ACTION_DELTA = 300 # In milliseconds +DEFAULT_MULTIPLE_CLICK_DELAY = 500 # In milliseconds +MULTIPLE_CLICK_TOKEN = "$" + +MODE_SINGLE = "single" +MODE_RESTART = "restart" +MODE_QUEUED = "queued" +MODE_PARALLEL = "parallel" + +T = TypeVar("T") + + +def action(method: Callable[..., Awaitable[Any]]) -> ActionFunction: + @wraps(method) + async def _action_impl(controller: "Controller", *args: Any, **kwargs: Any) -> None: + continue_call = await controller.before_action(method.__name__, *args, **kwargs) + if continue_call: + await method(controller, *args, **kwargs) + + return _action_impl + + +def run_in(fn: Callable[..., Any], delay: float, **kwargs: Any) -> "Task[None]": + """ + It runs the function (fn) in running event loop in `delay` seconds. + This function has been created because the default run_in function + from AppDaemon does not accept microseconds. + """ + + async def inner() -> None: + await asyncio.sleep(delay) + await fn(kwargs) + + task = asyncio.create_task(inner()) + return task + + +class Controller(Hass, Mqtt): # type: ignore[misc] + """ + This is the parent Controller, all controllers must extend from this class. + """ + + args: dict[str, Any] + integration: Integration + actions_mapping: ActionsMapping + action_handles: DefaultDict[ActionEvent, Optional["Task[None]"]] + action_delay_handles: dict[ActionEvent, str | None] + multiple_click_actions: set[ActionEvent] + action_delay: dict[ActionEvent, int] + action_delta: dict[ActionEvent, int] + action_times: dict[str, float] + previous_states: dict[ActionEvent, str | None] + multiple_click_action_times: dict[str, float] + click_counter: Counter[ActionEvent] + multiple_click_action_delay_tasks: DefaultDict[ActionEvent, Optional["Task[None]"]] + multiple_click_delay: int + + async def initialize(self) -> None: + self.log(f"🎮 ControllerX {cx_version.__version__}", ascii_encode=False) + await self.init() + + async def init(self) -> None: + controllers_ids: list[str] = self.get_list(self.args["controller"]) + self.integration = self.get_integration(self.args["integration"]) + + if "mapping" in self.args and "merge_mapping" in self.args: + raise ValueError("`mapping` and `merge_mapping` cannot be used together") + + custom_mapping: CustomActionsMapping | None = self.args.get("mapping", None) + merge_mapping: CustomActionsMapping | None = self.args.get( + "merge_mapping", None + ) + + if custom_mapping is None: + default_actions_mapping = self.get_default_actions_mapping(self.integration) + self.actions_mapping = self.parse_action_mapping(default_actions_mapping) # type: ignore[arg-type] + else: + self.actions_mapping = self.parse_action_mapping(custom_mapping) + + if merge_mapping is not None: + self.actions_mapping.update(self.parse_action_mapping(merge_mapping)) + + # Filter actions with include and exclude + if "actions" in self.args and "excluded_actions" in self.args: + raise ValueError("`actions` and `excluded_actions` cannot be used together") + include: list[ActionEvent] = self.get_list( + self.args.get("actions", list(self.actions_mapping.keys())) + ) + exclude: list[ActionEvent] = self.get_list( + self.args.get("excluded_actions", []) + ) + self.actions_mapping = self.filter_actions( + self.actions_mapping, set(include), set(exclude) + ) + + # Action delay + self.action_delay = self.get_mapping_per_action( + self.actions_mapping, + custom=self.args.get("action_delay"), + default=0, + ) + self.action_delay_handles = defaultdict(lambda: None) + self.action_handles = defaultdict(lambda: None) + + # Action delta + self.action_delta = self.get_mapping_per_action( + self.actions_mapping, + custom=self.args.get("action_delta"), + default=DEFAULT_ACTION_DELTA, + ) + self.action_times = defaultdict(float) + + # Previous state + self.previous_states = self.get_mapping_per_action( + self.actions_mapping, + custom=self.args.get("previous_state"), + default=None, + ) + + # Multiple click + self.multiple_click_actions = self.get_multiple_click_actions( + self.actions_mapping + ) + self.multiple_click_delay = self.args.get( + "multiple_click_delay", DEFAULT_MULTIPLE_CLICK_DELAY + ) + self.multiple_click_action_times = defaultdict(float) + self.click_counter = Counter() + self.multiple_click_action_delay_tasks = defaultdict(lambda: None) + + # Mode + self.mode = self.get_mapping_per_action( + self.actions_mapping, custom=self.args.get("mode"), default=MODE_SINGLE + ) + + # Listen for device changes + for controller_id in controllers_ids: + await self.integration.listen_changes(controller_id) + + def filter_actions( + self, + actions_mapping: ActionsMapping, + include: set[ActionEvent], + exclude: set[ActionEvent], + ) -> ActionsMapping: + allowed_actions = include - exclude + return { + key: value + for key, value in actions_mapping.items() + if key in allowed_actions + } + + @staticmethod + def get_option(value: str, options: list[str], ctx: str | None = None) -> str: + if value in options: + return value + else: + raise ValueError( + f"{f'{ctx} - ' if ctx is not None else ''}`{value}` is not an option. " + f"The options are {options}" + ) + + def parse_integration( + self, integration: str | dict[str, Any] | Any + ) -> dict[str, str]: + if isinstance(integration, str): + return {"name": integration} + elif isinstance(integration, dict): + if "name" in integration: + return integration + else: + raise ValueError("'name' attribute is mandatory") + else: + raise ValueError( + f"Type {type(integration)} is not supported for `integration` attribute" + ) + + def get_integration(self, integration: str | dict[str, Any]) -> Integration: + parsed_integration = self.parse_integration(integration) + kwargs = {k: v for k, v in parsed_integration.items() if k != "name"} + integrations = integration_module.get_integrations(self, kwargs) + integration_argument = self.get_option( + parsed_integration["name"], [i.name for i in integrations] + ) + return next(i for i in integrations if i.name == integration_argument) + + def get_default_actions_mapping( + self, integration: Integration + ) -> DefaultActionsMapping: + actions_mapping = integration.get_default_actions_mapping() + if actions_mapping is None: + raise ValueError( + f"This controller does not support {integration.name}. Use `mapping` to define the actions." + ) + return actions_mapping + + @overload + def get_list(self, entities: list[T]) -> list[T]: + pass + + @overload + def get_list(self, entities: T) -> list[T]: + pass + + def get_list(self, entities: list[T] | T) -> list[T]: + if isinstance(entities, (list, tuple)): + return list(entities) + return [entities] + + @overload + def get_mapping_per_action( + self, + actions_mapping: ActionsMapping, + *, + custom: T | dict[ActionEvent, T] | None, + default: None, + ) -> dict[ActionEvent, T | None]: + pass + + @overload + def get_mapping_per_action( + self, + actions_mapping: ActionsMapping, + *, + custom: T | dict[ActionEvent, T] | None, + default: T, + ) -> dict[ActionEvent, T]: + pass + + def get_mapping_per_action( + self, + actions_mapping: ActionsMapping, + *, + custom: T | dict[ActionEvent, T] | None, + default: None | T, + ) -> dict[ActionEvent, T | None] | dict[ActionEvent, T]: + if custom is not None and not isinstance(custom, dict): + default = custom + mapping = {action: default for action in actions_mapping} + if custom is not None and isinstance(custom, dict): + mapping.update(custom) + return mapping + + def parse_action_mapping(self, mapping: CustomActionsMapping) -> ActionsMapping: + return { + event: parse_actions(self, action) + for event, action in mapping.items() + if action is not None + } + + def get_multiple_click_actions(self, mapping: ActionsMapping) -> set[ActionEvent]: + to_return: set[ActionEvent] = set() + for key in mapping.keys(): + if not isinstance(key, str) or MULTIPLE_CLICK_TOKEN not in key: + continue + splitted = key.split(MULTIPLE_CLICK_TOKEN) + assert 1 <= len(splitted) <= 2 + action_key, _ = splitted + try: + to_return.add(int(action_key)) + except ValueError: + to_return.add(action_key) + return to_return + + def format_multiple_click_action( + self, action_key: ActionEvent, click_count: int + ) -> str: + return ( + str(action_key) + MULTIPLE_CLICK_TOKEN + str(click_count) + ) # e.g. toggle$2 + + async def _render_template(self, template: str) -> Any: + result = await self.render_template(template) + if result is None: + raise ValueError(f"Template {template} returned None") + try: + return literal_eval(result) + except (SyntaxError, ValueError): + return result + + _TEMPLATE_RE = re.compile(r"\s*\{\{.*\}\}") + + def contains_templating(self, template: str) -> bool: + is_template = self._TEMPLATE_RE.search(template) is not None + if not is_template: + self.log(f"`{template}` is not recognized as a template", level="DEBUG") + return is_template + + async def render_value(self, value: Any) -> Any: + if isinstance(value, str) and self.contains_templating(value): + return await self._render_template(value) + else: + return value + + async def render_attributes(self, attributes: dict[str, Any]) -> dict[str, Any]: + new_attributes: dict[str, Any] = {} + for key, value in attributes.items(): + new_value = await self.render_value(value) + if isinstance(value, dict): + new_value = await self.render_attributes(value) + new_attributes[key] = new_value + return new_attributes + + async def call_service( + self, service: str, render_template: bool = True, **attributes: Any + ) -> Any | None: + service = service.replace(".", "/") + to_log = ["\n", f"🤖 Service: \033[1m{service.replace('/', '.')}\033[0m"] + if service != "template/render" and render_template: + attributes = await self.render_attributes(attributes) + for attribute, value in attributes.items(): + if isinstance(value, float): + value = f"{value:.2f}" + to_log.append(f" - {attribute}: {value}") + self.log("\n".join(to_log), level="INFO", ascii_encode=False) + return await ADAPI.call_service(self, service, **attributes) + + @utils.sync_decorator # type: ignore[untyped-decorator] + async def get_state( + self, + entity_id: str | None = None, + attribute: str | Literal["all"] | None = None, + default: Any | None = None, + namespace: str | None = None, + copy: bool = True, + **kwargs: dict[str, Any], # left in intentionally for compatibility + ) -> Any | dict[str, Any] | None: + rendered_entity_id = await self.render_value(entity_id) + return await super().get_state( + rendered_entity_id, attribute, default=default, copy=copy + ) + + async def handle_action( + self, + action_key: str, + previous_state: str | None = None, + extra: EventData | None = None, + ) -> None: + if ( + action_key in self.actions_mapping + and self.previous_states[action_key] is not None + and previous_state != self.previous_states[action_key] + ): + self.log( + f"🎮 `{action_key}` not triggered because previous action was `{previous_state}`", + level="DEBUG", + ascii_encode=False, + ) + return + if ( + action_key in self.actions_mapping + and action_key not in self.multiple_click_actions + ): + previous_call_time = self.action_times[action_key] + now = time.time() * 1000 + self.action_times[action_key] = now + if now - previous_call_time > self.action_delta[action_key]: + await self.call_action(action_key, extra=extra) + elif action_key in self.multiple_click_actions: + now = time.time() * 1000 + previous_call_time = self.multiple_click_action_times.get(action_key, now) + self.multiple_click_action_times[action_key] = now + if now - previous_call_time > self.multiple_click_delay: + pass + + previous_task = self.multiple_click_action_delay_tasks[action_key] + if previous_task is not None: + previous_task.cancel() + + self.click_counter[action_key] += 1 + click_count = self.click_counter[action_key] + + new_task = run_in( + self.multiple_click_call_action, + self.multiple_click_delay / 1000, + action_key=action_key, + extra=extra, + click_count=click_count, + ) + self.multiple_click_action_delay_tasks[action_key] = new_task + else: + self.log( + f"🎮 Button event triggered, but not registered: `{action_key}`", + level="DEBUG", + ascii_encode=False, + ) + + async def multiple_click_call_action(self, kwargs: dict[str, Any]) -> None: + action_key: ActionEvent = kwargs["action_key"] + extra: EventData = kwargs["extra"] + click_count: int = kwargs["click_count"] + self.multiple_click_action_delay_tasks[action_key] = None + self.log( + f"🎮 {action_key} clicked `{click_count}` time(s)", + level="DEBUG", + ascii_encode=False, + ) + self.click_counter[action_key] = 0 + click_action_key = self.format_multiple_click_action(action_key, click_count) + if click_action_key in self.actions_mapping: + await self.call_action(click_action_key, extra=extra) + elif action_key in self.actions_mapping and click_count == 1: + await self.call_action(action_key, extra=extra) + + async def call_action( + self, action_key: ActionEvent, extra: EventData | None = None + ) -> None: + self.log( + f"🎮 Button event triggered: `{action_key}`", + level="INFO", + ascii_encode=False, + ) + self.log( + f"Extra:\n{extra}", + level="DEBUG", + ) + delay = self.action_delay[action_key] + if delay > 0: + handle = self.action_delay_handles[action_key] + if handle is not None: + await self.cancel_timer(handle) + self.log( + f"🕒 Running action(s) from `{action_key}` in {delay} seconds", + level="INFO", + ascii_encode=False, + ) + new_handle = await self.run_in( + self.action_timer_callback, delay, action_key=action_key, extra=extra + ) + self.action_delay_handles[action_key] = new_handle + else: + await self.action_timer_callback({"action_key": action_key, "extra": extra}) + + async def _apply_mode_strategy(self, action_key: ActionEvent) -> bool: + previous_task = self.action_handles[action_key] + if previous_task is None or previous_task.done(): + return False + if self.mode[action_key] == MODE_SINGLE: + self.log( + f"There is already an action executing for `{action_key}`. " + "If you want a different behaviour change `mode` parameter, " + "the default value is `single`.", + level="WARNING", + ) + return True + elif self.mode[action_key] == MODE_RESTART: + previous_task.cancel() + elif self.mode[action_key] == MODE_QUEUED: + await previous_task + elif self.mode[action_key] == MODE_PARALLEL: + pass + else: + raise ValueError( + f"`{self.mode[action_key]}` is not a possible value for `mode` parameter." + "Possible values: `single`, `restart`, `queued` and `parallel`." + ) + return False + + async def action_timer_callback(self, kwargs: dict[str, Any]) -> None: + action_key: ActionEvent = kwargs["action_key"] + extra: EventData = kwargs["extra"] + self.action_delay_handles[action_key] = None + skip = await self._apply_mode_strategy(action_key) + if skip: + return + action_types = self.actions_mapping[action_key] + task = asyncio.create_task(self.call_action_types(action_types, extra)) + self.action_handles[action_key] = task + try: + await task + except CancelledError: + self.log( + f"Task(s) from `{action_key}` was/were canceled and executed again", + level="DEBUG", + ) + + async def call_action_types( + self, action_types: list[ActionType], extra: EventData | None = None + ) -> None: + for action_type in action_types: + self.log( + f"🏃 Running `{action_type}` now", + level="INFO", + ascii_encode=False, + ) + await action_type.run(extra=extra) + + async def before_action(self, action: str, *args: str, **kwargs: Any) -> bool: + """ + Controllers have the option to implement this function, which is called + everytime before an action is called and it has the check_before_action decorator. + It should return True if the action shoul be called. + Otherwise it should return False. + """ + return True + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the states that a controller can take and the functions as values. + This is used for zigbee2mqtt support. + """ + return None + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the event id that a controller can take and the functions as values. + This is used for deCONZ support. + """ + return None + + def get_zha_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the command that a controller can take and the functions as values. + This is used for ZHA support. + """ + return None + + def get_zha_action(self, data: EventData) -> str | None: + """ + This method can be override for controllers that do not support + the standard extraction of the actions on cx_core/integration/zha.py + """ + return None + + def get_lutron_caseta_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the command that a controller can take and the functions as values. + This is used for Lutron support. + """ + return None + + def get_state_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the command that a controller can take and the functions as values. + This is used for State integration support. + """ + return None + + def get_homematic_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the command that a controller can take and the functions as values. + This is used for Homematic support. + """ + return None + + def get_shelly_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the command that a controller can take and the functions as values. + This is used for Shelly support. + """ + return None + + def get_shellyforhass_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the command that a controller can take and the functions as values. + This is used for Shelly for HASS support. + """ + return None + + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping | None: + """ + Controllers can implement this function. It should return a dict + with the command that a controller can take and the functions as values. + This is used for Tasmota support. + """ + return None + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + return {} diff --git a/appdaemon/apps/controllerx/cx_core/feature_support/__init__.py b/appdaemon/apps/controllerx/cx_core/feature_support/__init__.py new file mode 100644 index 0000000..5e22c61 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/feature_support/__init__.py @@ -0,0 +1,40 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from cx_core.type_controller import Entity, TypeController + + +class FeatureSupport: + controller: "TypeController[Entity]" + update_supported_features: bool + _supported_features: int | None + + def __init__( + self, + controller: "TypeController[Entity]", + supported_features: int | None = None, + update_supported_features: bool = False, + ) -> None: + self.controller = controller + self._supported_features = supported_features + self.update_supported_features = update_supported_features + + @property + async def supported_features(self) -> int: + if self._supported_features is None or self.update_supported_features: + bitfield: str = await self.controller.get_entity_state( + attribute="supported_features" + ) + if bitfield is not None: + self._supported_features = int(bitfield) + else: + raise ValueError( + f"`supported_features` could not be read from `{self.controller.entity}`. Entity might not be available." + ) + return self._supported_features + + async def is_supported(self, feature: int) -> bool: + return feature & await self.supported_features != 0 + + async def not_supported(self, feature: int) -> bool: + return not await self.is_supported(feature) diff --git a/appdaemon/apps/controllerx/cx_core/feature_support/cover.py b/appdaemon/apps/controllerx/cx_core/feature_support/cover.py new file mode 100644 index 0000000..5fd8ee2 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/feature_support/cover.py @@ -0,0 +1,9 @@ +class CoverSupport: + OPEN = 1 + CLOSE = 2 + SET_COVER_POSITION = 4 + STOP = 8 + OPEN_TILT = 16 + CLOSE_TILT = 32 + STOP_TILT = 64 + SET_TILT_POSITION = 128 diff --git a/appdaemon/apps/controllerx/cx_core/feature_support/light.py b/appdaemon/apps/controllerx/cx_core/feature_support/light.py new file mode 100644 index 0000000..d7b501b --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/feature_support/light.py @@ -0,0 +1,4 @@ +class LightSupport: + EFFECT = 4 + FLASH = 8 + TRANSITION = 32 diff --git a/appdaemon/apps/controllerx/cx_core/feature_support/media_player.py b/appdaemon/apps/controllerx/cx_core/feature_support/media_player.py new file mode 100644 index 0000000..319aaab --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/feature_support/media_player.py @@ -0,0 +1,20 @@ +class MediaPlayerSupport: + PAUSE = 1 + SEEK = 2 + VOLUME_SET = 4 + VOLUME_MUTE = 8 + PREVIOUS_TRACK = 16 + NEXT_TRACK = 32 + TURN_ON = 128 + TURN_OFF = 256 + PLAY_MEDIA = 512 + VOLUME_STEP = 1024 + SELECT_SOURCE = 2048 + STOP = 4096 + CLEAR_PLAYLIST = 8192 + PLAY = 16384 + SHUFFLE_SET = 32768 + SELECT_SOUND_MODE = 65536 + SUPPORT_BROWSE_MEDIA = 131072 + SUPPORT_REPEAT_SET = 262144 + SUPPORT_GROUPING = 524288 diff --git a/appdaemon/apps/controllerx/cx_core/integration/__init__.py b/appdaemon/apps/controllerx/cx_core/integration/__init__.py new file mode 100644 index 0000000..4da0b05 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/__init__.py @@ -0,0 +1,35 @@ +import abc +from typing import TYPE_CHECKING, Any + +from cx_const import DefaultActionsMapping +from cx_helper import get_classes + +if TYPE_CHECKING: + from cx_core.controller import Controller + +EventData = dict[str, Any] + + +class Integration(abc.ABC): + name: str + controller: "Controller" + kwargs: dict[str, Any] + + def __init__(self, controller: "Controller", kwargs: dict[str, Any]): + self.controller = controller + self.kwargs = kwargs + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return None + + @abc.abstractmethod + async def listen_changes(self, controller_id: str) -> None: + raise NotImplementedError + + +def get_integrations( + controller: "Controller", kwargs: dict[str, Any] +) -> list[Integration]: + integration_classes = get_classes(__file__, __package__, Integration) + integrations = [cls_(controller, kwargs) for cls_ in integration_classes] + return integrations diff --git a/appdaemon/apps/controllerx/cx_core/integration/deconz.py b/appdaemon/apps/controllerx/cx_core/integration/deconz.py new file mode 100644 index 0000000..4db7ab8 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/deconz.py @@ -0,0 +1,34 @@ +from typing import Any + +from appdaemon.plugins.hass.hassapi import Hass +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + +LISTENS_TO_ID = "id" +LISTENS_TO_UNIQUE_ID = "unique_id" + + +class DeCONZIntegration(Integration): + name = "deconz" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_deconz_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + listens_to = self.kwargs.get("listen_to", LISTENS_TO_ID) + if listens_to not in (LISTENS_TO_ID, LISTENS_TO_UNIQUE_ID): + raise ValueError( + "`listens_to` for deCONZ integration should either be `id` or `unique_id`" + ) + await Hass.listen_event( + self.controller, + self.event_callback, + "deconz_event", + **{listens_to: controller_id} + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + type_ = self.kwargs.get("type", "event") + await self.controller.handle_action(data[type_], extra=data) diff --git a/appdaemon/apps/controllerx/cx_core/integration/event.py b/appdaemon/apps/controllerx/cx_core/integration/event.py new file mode 100644 index 0000000..f5adb77 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/event.py @@ -0,0 +1,41 @@ +from typing import Any + +from appdaemon.plugins.hass.hassapi import Hass +from cx_core.integration import EventData, Integration + + +class EventIntegration(Integration): + name = "event" + + def get_arg(self, arg: str) -> Any: + try: + return self.kwargs[arg] + except KeyError: + raise ValueError(f"{arg} is a mandatory field for event integration.") + + async def listen_changes(self, controller_id: str) -> None: + event_type: str = self.get_arg("event_type") + controller_key: str = self.get_arg("controller_key") + self.controller.log( + f"Listening to `{event_type}` events for controller `{controller_key}={controller_id}`" + ) + await Hass.listen_event( + self.controller, + self.event_callback, + event_type, + **{controller_key: controller_id}, + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + action_template: str = self.get_arg("action_template") + try: + action = action_template.format(**data) + except Exception: + self.controller.log( + f"Template `{action_template}` could not be rendered with data={data}", + level="WARNING", + ) + return + await self.controller.handle_action(action) diff --git a/appdaemon/apps/controllerx/cx_core/integration/homematic.py b/appdaemon/apps/controllerx/cx_core/integration/homematic.py new file mode 100644 index 0000000..df1b388 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/homematic.py @@ -0,0 +1,36 @@ +from typing import TYPE_CHECKING, Any + +from appdaemon.plugins.hass.hassapi import Hass +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + +if TYPE_CHECKING: + from cx_core.controller import Controller + + +class HomematicIntegration(Integration): + name = "homematic" + _registered_controller_ids: set[str] + + def __init__(self, controller: "Controller", kwargs: dict[str, Any]): + self._registered_controller_ids = set() + super().__init__(controller, kwargs) + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_homematic_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + self._registered_controller_ids.add(controller_id) + await Hass.listen_event( + self.controller, self.event_callback, "homematic.keypress" + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + if data["name"] not in self._registered_controller_ids: + return + param = data["param"] + channel = data["channel"] + action = f"{param}_{channel}" + await self.controller.handle_action(action, extra=data) diff --git a/appdaemon/apps/controllerx/cx_core/integration/lutron_caseta.py b/appdaemon/apps/controllerx/cx_core/integration/lutron_caseta.py new file mode 100644 index 0000000..1620289 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/lutron_caseta.py @@ -0,0 +1,28 @@ +from typing import Any + +from appdaemon.plugins.hass.hassapi import Hass +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + + +class LutronIntegration(Integration): + name = "lutron_caseta" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_lutron_caseta_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + await Hass.listen_event( + self.controller, + self.event_callback, + "lutron_caseta_button_event", + serial=controller_id, + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + button = data["button_number"] + action_type = data["action"] + action = f"button_{button}_{action_type}" + await self.controller.handle_action(action, extra=data) diff --git a/appdaemon/apps/controllerx/cx_core/integration/mqtt.py b/appdaemon/apps/controllerx/cx_core/integration/mqtt.py new file mode 100644 index 0000000..91864bb --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/mqtt.py @@ -0,0 +1,43 @@ +import json +from typing import Any + +from appdaemon.plugins.mqtt.mqttapi import Mqtt +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + + +class MQTTIntegration(Integration): + name = "mqtt" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_z2m_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + await Mqtt.listen_event( + self.controller, self.event_callback, topic=controller_id, namespace="mqtt" + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + self.controller.log(f"MQTT data event: {data}", level="DEBUG") + payload_key = self.kwargs.get("key") + if "payload" not in data: + return + payload = data["payload"] + action_key: str + if payload_key is None: + action_key = payload + else: + try: + action_key = str(json.loads(payload)[payload_key]).lower() + except json.decoder.JSONDecodeError: + raise ValueError( + f"`key` is being used ({payload_key}). " + f"Following payload is not a valid JSON: {payload}" + ) + except KeyError: + raise ValueError( + f"Following payload does not contain `{payload_key}`: {payload}" + ) + await self.controller.handle_action(action_key) diff --git a/appdaemon/apps/controllerx/cx_core/integration/shelly.py b/appdaemon/apps/controllerx/cx_core/integration/shelly.py new file mode 100644 index 0000000..7fb1bea --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/shelly.py @@ -0,0 +1,25 @@ +from typing import Any + +from appdaemon.plugins.hass.hassapi import Hass +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + + +class ShellyIntegration(Integration): + name = "shelly" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_shelly_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + await Hass.listen_event( + self.controller, self.event_callback, "shelly.click", device=controller_id + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + click_type = data["click_type"] + channel = data["channel"] + action = f"{click_type}_{channel}" + await self.controller.handle_action(action, extra=data) diff --git a/appdaemon/apps/controllerx/cx_core/integration/shellyforhass.py b/appdaemon/apps/controllerx/cx_core/integration/shellyforhass.py new file mode 100644 index 0000000..35d57c2 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/shellyforhass.py @@ -0,0 +1,27 @@ +from typing import Any + +from appdaemon.plugins.hass.hassapi import Hass +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + + +class ShellyForHASSIntegration(Integration): + name = "shellyforhass" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_shellyforhass_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + await Hass.listen_event( + self.controller, + self.event_callback, + "shellyforhass.click", + entity_id=controller_id, + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + click_type = data["click_type"] + action = f"{click_type}" + await self.controller.handle_action(action, extra=data) diff --git a/appdaemon/apps/controllerx/cx_core/integration/state.py b/appdaemon/apps/controllerx/cx_core/integration/state.py new file mode 100644 index 0000000..3d3276b --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/state.py @@ -0,0 +1,28 @@ +from typing import Any + +from appdaemon.plugins.hass.hassapi import Hass +from cx_const import DefaultActionsMapping +from cx_core.integration import Integration + + +class StateIntegration(Integration): + name = "state" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_state_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + attribute = self.kwargs.get("attribute", None) + await Hass.listen_state( + self.controller, self.state_callback, controller_id, attribute=attribute + ) + + async def state_callback( + self, + entity: str | None, + attribute: str | None, + old: str | None, + new: str, + kwargs: dict[str, Any], + ) -> None: + await self.controller.handle_action(new, previous_state=old) diff --git a/appdaemon/apps/controllerx/cx_core/integration/tasmota.py b/appdaemon/apps/controllerx/cx_core/integration/tasmota.py new file mode 100644 index 0000000..02d0d94 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/tasmota.py @@ -0,0 +1,51 @@ +import json +from typing import Any + +from appdaemon.plugins.mqtt.mqttapi import Mqtt +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + + +class TasmotaIntegration(Integration): + name = "tasmota" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_tasmota_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + component_key = self.kwargs.get("component") + if component_key is None: + raise ValueError( + "`component` attribute is mandatory. " + "Check example from https://xaviml.github.io/controllerx/start/integrations/tasmota" + ) + await Mqtt.listen_event( + self.controller, self.event_callback, topic=controller_id, namespace="mqtt" + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + self.controller.log(f"MQTT data event: {data}", level="DEBUG") + component_key: str = self.kwargs["component"] + payload_key: str = self.kwargs.get("key", "Action") + if "payload" not in data: + return + payload: str = data["payload"] + # Even though this checks if "compoenent_key" is in the payload + # (as string, not dictionary), it is preferred for its performance + if component_key not in payload: + return + try: + action_key = str(json.loads(payload)[component_key][payload_key]) + except json.decoder.JSONDecodeError: + raise ValueError( + f"`key` is being used ({payload_key}). " + f"Following payload is not a valid JSON: {payload}" + ) + except KeyError: + raise ValueError( + "Following payload does not contain " + f"payload_key=`{payload_key}`: {payload}" + ) + await self.controller.handle_action(action_key) diff --git a/appdaemon/apps/controllerx/cx_core/integration/z2m.py b/appdaemon/apps/controllerx/cx_core/integration/z2m.py new file mode 100644 index 0000000..ad744fb --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/z2m.py @@ -0,0 +1,85 @@ +import json +from typing import Any + +from appdaemon.plugins.hass.hassapi import Hass +from appdaemon.plugins.mqtt.mqttapi import Mqtt +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + +LISTENS_TO_HA = "ha" +LISTENS_TO_MQTT = "mqtt" +LISTENS_TO_EVENT = "event" + + +class Z2MIntegration(Integration): + name = "z2m" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_z2m_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + listens_to = self.kwargs.get("listen_to", LISTENS_TO_HA) + if listens_to == LISTENS_TO_HA: + self.controller.log( + "⚠️ Listening to HA sensor actions is now deprecated and will be removed in the future. Use `listen_to: mqtt` or `listen_to: event` instead." + " Read more about it here: https://xaviml.github.io/controllerx/others/z2m-ha-sensor-deprecated", + level="WARNING", + ascii_encode=False, + ) + await Hass.listen_state(self.controller, self.state_callback, controller_id) + elif listens_to == LISTENS_TO_MQTT: + topic_prefix = self.kwargs.get("topic_prefix", "zigbee2mqtt") + await Mqtt.listen_event( + self.controller, + self.event_callback, + topic=f"{topic_prefix}/{controller_id}", + namespace="mqtt", + ) + elif listens_to == LISTENS_TO_EVENT: + await Hass.listen_state( + self.controller, + self.state_callback, + f"event.{controller_id}", + attribute="event_type", + ) + else: + raise ValueError( + "`listen_to` has to be either `ha`, `mqtt` or `event`. Default is `ha`." + ) + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + self.controller.log(f"MQTT data event: {data}", level="DEBUG") + action_key = self.kwargs.get("action_key", "action") + action_group_key = self.kwargs.get("action_group_key", "action_group") + if "payload" not in data: + return + payload = json.loads(data["payload"]) + if action_key not in payload: + self.controller.log( + f"⚠️ There is no `{action_key}` in the MQTT topic payload", + level="WARNING", + ascii_encode=False, + ) + return + if action_group_key in payload and "action_group" in self.kwargs: + action_group = self.controller.get_list(self.kwargs["action_group"]) + if payload["action_group"] not in action_group: + self.controller.log( + f"Action group {payload['action_group']} not found in " + f"action groups: {action_group}", + level="DEBUG", + ) + return + await self.controller.handle_action(payload[action_key], extra=payload) + + async def state_callback( + self, + entity: str | None, + attribute: str | None, + old: str | None, + new: str, + kwargs: dict[str, Any], + ) -> None: + await self.controller.handle_action(new, previous_state=old) diff --git a/appdaemon/apps/controllerx/cx_core/integration/zha.py b/appdaemon/apps/controllerx/cx_core/integration/zha.py new file mode 100644 index 0000000..a02111e --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/integration/zha.py @@ -0,0 +1,46 @@ +from typing import Any + +from appdaemon.plugins.hass.hassapi import Hass +from cx_const import DefaultActionsMapping +from cx_core.integration import EventData, Integration + + +class ZHAIntegration(Integration): + name = "zha" + + def get_default_actions_mapping(self) -> DefaultActionsMapping | None: + return self.controller.get_zha_actions_mapping() + + async def listen_changes(self, controller_id: str) -> None: + await Hass.listen_event( + self.controller, self.event_callback, "zha_event", device_ieee=controller_id + ) + + def get_action(self, data: EventData) -> str: + command = data["command"] + args = data["args"] + if isinstance(args, dict): + args = args["args"] + args = list(map(str, args)) + action = command + if not (command == "stop" or command == "release"): + if len(args) > 0: + action += "_" + "_".join(args) + return action + + async def event_callback( + self, event_name: str, data: EventData, kwargs: dict[str, Any] + ) -> None: + action = self.controller.get_zha_action(data) + if action is None: + # If there is no action extracted from the controller then + # we extract with the standard function + try: + action = self.get_action(data) + except Exception: + self.controller.log( + f"The following event could not be parsed: {data}", level="WARNING" + ) + return + + await self.controller.handle_action(action, extra=data) diff --git a/appdaemon/apps/controllerx/cx_core/release_hold_controller.py b/appdaemon/apps/controllerx/cx_core/release_hold_controller.py new file mode 100644 index 0000000..e016617 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/release_hold_controller.py @@ -0,0 +1,69 @@ +import abc +from typing import Any + +from cx_core.controller import Controller, action + +DEFAULT_DELAY = 350 # In milliseconds +DEFAULT_RELEASE_DELAY = 0 # In seconds + + +class ReleaseHoldController(Controller, abc.ABC): + DEFAULT_MAX_LOOPS = 50 + + on_hold: bool + delay: float + max_loops: int + hold_release_toggle: bool + release_delay: int + + async def init(self) -> None: + self.on_hold = False + self.delay = self.args.get("delay", self.default_delay()) + self.max_loops = self.args.get( + "max_loops", ReleaseHoldController.DEFAULT_MAX_LOOPS + ) + self.hold_release_toggle = self.args.get("hold_release_toggle", False) + self.release_delay = self.args.get("release_delay", DEFAULT_RELEASE_DELAY) + await super().init() + + @action + async def release(self) -> None: + if self.release_delay > 0: + await self.sleep(self.release_delay) + self.on_hold = False + + async def hold(self, *args: Any) -> None: + loops = 0 + self.on_hold = True + stop = False + while self.on_hold and not stop: + stop = await self.hold_loop(*args) + # Stop the iteration if we either stop from the hold_loop + # or we reached the max loop number + stop = stop or loops >= self.max_loops + await self.sleep(self.delay / 1000) + loops += 1 + self.on_hold = False + + async def before_action(self, action: str, *args: Any, **kwargs: Any) -> bool: + super_before_action = await super().before_action(action, *args, **kwargs) + to_return = not (action == "hold" and self.on_hold) + if action == "hold" and self.on_hold and self.hold_release_toggle: + self.on_hold = False + return super_before_action and to_return + + @abc.abstractmethod + async def hold_loop(self, *args: Any) -> bool: + """ + This function is called by the ReleaseHoldController depending on the settings. + It stops calling the function once release action is called or when this function + returns True. + """ + raise NotImplementedError + + def default_delay(self) -> int: + """ + This function can be overwritten for each device to indeicate the delay + for the specific device, by default it returns the default delay from the app + """ + return DEFAULT_DELAY diff --git a/appdaemon/apps/controllerx/cx_core/stepper/__init__.py b/appdaemon/apps/controllerx/cx_core/stepper/__init__.py new file mode 100644 index 0000000..1d34e59 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/stepper/__init__.py @@ -0,0 +1,112 @@ +import abc +from dataclasses import dataclass + +from cx_const import Number, StepperDir + + +class MinMax: + def __init__(self, min: Number, max: Number, margin: float = 0.05) -> None: + self._min = min + self._max = max + self.margin_dist = (max - min) * margin + + @property + def min(self) -> Number: + return self._min + + @property + def max(self) -> Number: + return self._max + + def is_min(self, value: Number) -> bool: + return self._min == value + + def is_max(self, value: Number) -> bool: + return self._max == value + + def is_between(self, value: Number) -> bool: + return self._min < value < self._max + + def in_min_boundaries(self, value: Number) -> bool: + return self._min <= value <= (self._min + self.margin_dist) + + def in_max_boundaries(self, value: Number) -> bool: + return (self._max - self.margin_dist) <= value <= self._max + + def clip(self, value: Number) -> Number: + return max(self._min, min(value, self._max)) + + def __repr__(self) -> str: + return f"MinMax({self.min}, {self.max})" + + +@dataclass +class StepperOutput: + next_value: Number + next_direction: str | None + + @property + def exceeded(self) -> bool: + return self.next_direction is None + + +class Stepper(abc.ABC): + sign_mapping = {StepperDir.UP: 1, StepperDir.DOWN: -1} + + min_max: MinMax + steps: Number + previous_direction: str + relative_steps: bool + + @staticmethod + def invert_direction(direction: str) -> str: + return StepperDir.UP if direction == StepperDir.DOWN else StepperDir.DOWN + + @staticmethod + def sign(direction: str) -> int: + return Stepper.sign_mapping[direction] + + @staticmethod + def apply_sign(value: Number, direction: str) -> Number: + return Stepper.sign(direction) * value + + def __init__( + self, + min_max: MinMax, + steps: Number, + previous_direction: str = StepperDir.DOWN, + relative_steps: bool = True, + ) -> None: + self.min_max = min_max + self.steps = steps + self.previous_direction = previous_direction + self.relative_steps = relative_steps + + def _compute_step(self) -> float: + if self.relative_steps: + max_ = self.min_max.max + min_ = self.min_max.min + return (max_ - min_) / self.steps + else: + return self.steps + + def get_direction(self, value: Number, direction: str) -> str: + if direction == StepperDir.TOGGLE: + direction = Stepper.invert_direction(self.previous_direction) + self.previous_direction = direction + return direction + + @abc.abstractmethod + def step(self, value: Number, direction: str) -> StepperOutput: + """ + This function updates the value according to the steps + that needs to take and returns the new value together with + the new direction it will need to go. If next_direction is + None, the loop will stop executing. + """ + raise NotImplementedError + + +class InvertStepper(Stepper): + def step(self, value: Number, direction: str) -> StepperOutput: + return StepperOutput(self.apply_sign(value, direction), next_direction=None) diff --git a/appdaemon/apps/controllerx/cx_core/stepper/bounce_stepper.py b/appdaemon/apps/controllerx/cx_core/stepper/bounce_stepper.py new file mode 100644 index 0000000..ba87310 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/stepper/bounce_stepper.py @@ -0,0 +1,17 @@ +from cx_const import Number +from cx_core.stepper import Stepper, StepperOutput + + +class BounceStepper(Stepper): + def step(self, value: Number, direction: str) -> StepperOutput: + value = self.min_max.clip(value) + step = self._compute_step() + + new_value = value + Stepper.apply_sign(step, direction) + if self.min_max.is_between(new_value): + return StepperOutput(round(new_value, 3), next_direction=direction) + else: + new_value = 2 * self.min_max.clip(new_value) - new_value + return StepperOutput( + round(new_value, 3), next_direction=Stepper.invert_direction(direction) + ) diff --git a/appdaemon/apps/controllerx/cx_core/stepper/index_loop_stepper.py b/appdaemon/apps/controllerx/cx_core/stepper/index_loop_stepper.py new file mode 100644 index 0000000..78aa6c6 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/stepper/index_loop_stepper.py @@ -0,0 +1,31 @@ +from cx_const import Number, StepperDir +from cx_core.stepper import MinMax, Stepper, StepperOutput + + +class IndexLoopStepper(Stepper): + def __init__( + self, + size: int, + previous_direction: str = StepperDir.DOWN, + relative_steps: bool = True, + ) -> None: + super().__init__( + MinMax(0, size - 1), + size, + previous_direction, + relative_steps, + ) + + def step(self, value: Number, direction: str) -> StepperOutput: + value = self.min_max.clip(value) + sign = self.sign(direction) + # We add +1 to make the max be included + max_ = int(self.min_max.max) + 1 + min_ = int(self.min_max.min) + if self.relative_steps: + step = (max_ - min_) // self.steps + else: + step = self.steps + + new_value = (int(value) + step * sign) % (max_ - min_) + min_ + return StepperOutput(new_value, next_direction=direction) diff --git a/appdaemon/apps/controllerx/cx_core/stepper/loop_stepper.py b/appdaemon/apps/controllerx/cx_core/stepper/loop_stepper.py new file mode 100644 index 0000000..1a66cb0 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/stepper/loop_stepper.py @@ -0,0 +1,16 @@ +from cx_const import Number +from cx_core.stepper import Stepper, StepperOutput + + +class LoopStepper(Stepper): + def step(self, value: Number, direction: str) -> StepperOutput: + value = self.min_max.clip(value) + max_ = self.min_max.max + min_ = self.min_max.min + step = self._compute_step() + + new_value = ( + ((value + Stepper.apply_sign(step, direction)) - min_) % (max_ - min_) + ) + min_ + new_value = round(new_value, 3) + return StepperOutput(new_value, next_direction=direction) diff --git a/appdaemon/apps/controllerx/cx_core/stepper/stop_stepper.py b/appdaemon/apps/controllerx/cx_core/stepper/stop_stepper.py new file mode 100644 index 0000000..b8047f8 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/stepper/stop_stepper.py @@ -0,0 +1,26 @@ +from cx_const import Number, StepperDir +from cx_core.stepper import Stepper, StepperOutput + + +class StopStepper(Stepper): + def get_direction(self, value: Number, direction: str) -> str: + value = self.min_max.clip(value) + if direction == StepperDir.TOGGLE and self.min_max.in_min_boundaries(value): + self.previous_direction = StepperDir.UP + return self.previous_direction + if direction == StepperDir.TOGGLE and self.min_max.in_max_boundaries(value): + self.previous_direction = StepperDir.DOWN + return self.previous_direction + return super().get_direction(value, direction) + + def step(self, value: Number, direction: str) -> StepperOutput: + value = self.min_max.clip(value) + step = self._compute_step() + + new_value = value + Stepper.apply_sign(step, direction) + new_value = round(new_value, 3) + if self.min_max.is_between(new_value): + return StepperOutput(new_value, next_direction=direction) + else: + new_value = self.min_max.clip(new_value) + return StepperOutput(new_value, next_direction=None) diff --git a/appdaemon/apps/controllerx/cx_core/type/__init__.py b/appdaemon/apps/controllerx/cx_core/type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/appdaemon/apps/controllerx/cx_core/type/cover_controller.py b/appdaemon/apps/controllerx/cx_core/type/cover_controller.py new file mode 100644 index 0000000..677c4fa --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/type/cover_controller.py @@ -0,0 +1,125 @@ +from collections.abc import Awaitable, Callable +from typing import Any + +from cx_const import Cover, PredefinedActionsMapping +from cx_core.controller import action +from cx_core.feature_support.cover import CoverSupport +from cx_core.type_controller import Entity, TypeController + + +class CoverController(TypeController[Entity]): + """ + This is the main class that controls the coveres for different devices. + Type of actions: + - Open + - Close + Parameters taken: + - controller (required): Inherited from Controller + - cover (required): cover entity name + - open_position (optional): The open position. Default is 100 + - close_position (optional): The close position. Default is 0 + """ + + domains = ["cover"] + entity_arg = "cover" + + open_position: int + close_position: int + + cover_duration: int | None + + is_supposedly_moving: bool = False + stop_timer_handle: str | None = None + + async def init(self) -> None: + self.open_position = self.args.get("open_position", 100) + self.close_position = self.args.get("close_position", 0) + self.cover_duration = self.args.get("cover_duration") + if self.open_position < self.close_position: + raise ValueError("`open_position` must be higher than `close_position`") + await super().init() + + def _get_entity_type(self) -> type[Entity]: + return Entity + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + return { + Cover.OPEN: self.open, + Cover.CLOSE: self.close, + Cover.STOP: self.stop, + Cover.TOGGLE_OPEN: (self.toggle, (self.open,)), + Cover.TOGGLE_CLOSE: (self.toggle, (self.close,)), + } + + async def cover_stopped_cb(self, kwargs: dict[str, Any]) -> None: + self.is_supposedly_moving = False + self.stop_timer_handle = None + + async def start_timer(self) -> None: + if self.cover_duration is None: + return + await self.stop_timer() + self.is_supposedly_moving = True + self.stop_timer_handle = await self.run_in( + self.cover_stopped_cb, self.cover_duration + ) + + async def stop_timer(self) -> None: + if self.stop_timer_handle is not None: + self.is_supposedly_moving = False + await self.cancel_timer(self.stop_timer_handle) + + @action + async def open(self) -> None: + if await self.feature_support.is_supported(CoverSupport.SET_COVER_POSITION): + await self.call_service( + "cover/set_cover_position", + entity_id=self.entity.name, + position=self.open_position, + ) + elif await self.feature_support.is_supported(CoverSupport.OPEN): + await self.call_service("cover/open_cover", entity_id=self.entity.name) + else: + self.log( + f"⚠️ `{self.entity}` does not support SET_COVER_POSITION or OPEN", + level="WARNING", + ascii_encode=False, + ) + return + await self.start_timer() + + @action + async def close(self) -> None: + if await self.feature_support.is_supported(CoverSupport.SET_COVER_POSITION): + await self.call_service( + "cover/set_cover_position", + entity_id=self.entity.name, + position=self.close_position, + ) + elif await self.feature_support.is_supported(CoverSupport.CLOSE): + await self.call_service("cover/close_cover", entity_id=self.entity.name) + else: + self.log( + f"⚠️ `{self.entity}` does not support SET_COVER_POSITION or CLOSE", + level="WARNING", + ascii_encode=False, + ) + return + await self.start_timer() + + @action + async def stop(self) -> None: + await self.stop_timer() + await self.call_service("cover/stop_cover", entity_id=self.entity.name) + + @action + async def toggle(self, action: Callable[[], Awaitable[None]]) -> None: + cover_state = await self.get_entity_state() + if ( + cover_state == "opening" + or cover_state == "closing" + or self.is_supposedly_moving + ): + await self.stop() + else: + await action() diff --git a/appdaemon/apps/controllerx/cx_core/type/light_controller.py b/appdaemon/apps/controllerx/cx_core/type/light_controller.py new file mode 100644 index 0000000..d6b5a37 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/type/light_controller.py @@ -0,0 +1,958 @@ +import asyncio +from functools import lru_cache +from typing import Any, Literal + +from cx_const import Light, Number, PredefinedActionsMapping, StepperDir, StepperMode +from cx_core.color_helper import Color, get_color_wheel +from cx_core.controller import action +from cx_core.feature_support.light import LightSupport +from cx_core.integration import EventData +from cx_core.integration.deconz import DeCONZIntegration +from cx_core.integration.z2m import Z2MIntegration +from cx_core.integration.zha import ZHAIntegration +from cx_core.release_hold_controller import ReleaseHoldController +from cx_core.stepper import MinMax, Stepper +from cx_core.stepper.bounce_stepper import BounceStepper +from cx_core.stepper.index_loop_stepper import IndexLoopStepper +from cx_core.stepper.loop_stepper import LoopStepper +from cx_core.stepper.stop_stepper import StopStepper +from cx_core.type_controller import Entity, TypeController + +DEFAULT_MANUAL_STEPS = 10 +DEFAULT_AUTOMATIC_STEPS = 10 +DEFAULT_MIN_BRIGHTNESS = 1 +DEFAULT_MAX_BRIGHTNESS = 255 +DEFAULT_MIN_WHITE_VALUE = 1 +DEFAULT_MAX_WHITE_VALUE = 255 +DEFAULT_MIN_COLOR_TEMP = 153 +DEFAULT_MAX_COLOR_TEMP = 500 +DEFAULT_COLOR_TEMP = ( + DEFAULT_MAX_COLOR_TEMP - DEFAULT_MIN_COLOR_TEMP +) // 2 + DEFAULT_MIN_COLOR_TEMP +DEFAULT_TRANSITION = 300 +DEFAULT_ADD_TRANSITION = True +DEFAULT_TRANSITION_TURN_TOGGLE = False +DEFAULT_HOLD_TOGGLE_DIRECTION_INIT = "up" + +ColorMode = Literal["auto", "xy_color", "color_temp"] + +COLOR_MODES = {"hs", "xy", "rgb", "rgbw", "rgbww"} +STEPPER_MODES: dict[str, type[Stepper]] = { + StepperMode.STOP: StopStepper, + StepperMode.LOOP: LoopStepper, + StepperMode.BOUNCE: BounceStepper, +} + + +class LightEntity(Entity): + color_mode: ColorMode + + def __init__( + self, + name: str, + entities: list[str] | None = None, + color_mode: ColorMode = "auto", + ) -> None: + super().__init__(name, entities) + self.color_mode = color_mode + + +class LightController(TypeController[LightEntity], ReleaseHoldController): + """ + This is the main class that controls the lights for different devices. + Type of actions: + - On/Off/Toggle + - Brightness click and hold + - Color temperature click and hold + - xy color click and hold + If a light supports xy_color and color_temperature, then xy_color will be the + default functionality. Parameters taken: + - controller (required): Inherited from Controller + - light (required): This is either the light entity name or a dictionary as + {name: string, color_mode: auto | xy_color | color_temp} + - delay (optional): Inherited from ReleaseHoldController + - manual_steps (optional): Number of steps to go from min to max when clicking. + - automatic_steps (optional): Number of steps to go from min to max when smoothing. + """ + + ATTRIBUTE_BRIGHTNESS = "brightness" + ATTRIBUTE_WHITE_VALUE = "white_value" + # With the following attribute, it will select color_temp or xy_color, depending on the light. + ATTRIBUTE_COLOR = "color" + ATTRIBUTE_COLOR_TEMP = "color_temp" + ATTRIBUTE_XY_COLOR = "xy_color" + + ATTRIBUTES_LIST = [ + ATTRIBUTE_BRIGHTNESS, + ATTRIBUTE_WHITE_VALUE, + ATTRIBUTE_COLOR, + ATTRIBUTE_COLOR_TEMP, + ATTRIBUTE_XY_COLOR, + ] + + index_color = 0 + value_attribute = None + + # These are intermediate variables to store the checked value + smooth_power_on_check: bool + remove_transition_check: bool + next_direction: str | None = None + + manual_steps: Number + automatic_steps: Number + min_max_attributes: dict[str, MinMax] + + domains = ["light"] + entity_arg = "light" + + _supported_color_modes: set[str] | None + + async def init(self) -> None: + self.manual_steps = self.args.get("manual_steps", DEFAULT_MANUAL_STEPS) + self.automatic_steps = self.args.get("automatic_steps", DEFAULT_AUTOMATIC_STEPS) + + self.min_max_attributes = { + self.ATTRIBUTE_BRIGHTNESS: MinMax( + self.args.get("min_brightness", DEFAULT_MIN_BRIGHTNESS), + self.args.get("max_brightness", DEFAULT_MAX_BRIGHTNESS), + ), + self.ATTRIBUTE_WHITE_VALUE: MinMax( + self.args.get("min_white_value", DEFAULT_MIN_WHITE_VALUE), + self.args.get("max_white_value", DEFAULT_MAX_WHITE_VALUE), + ), + self.ATTRIBUTE_COLOR_TEMP: MinMax( + self.args.get("min_color_temp", DEFAULT_MIN_COLOR_TEMP), + self.args.get("max_color_temp", DEFAULT_MAX_COLOR_TEMP), + ), + } + + self.transition = self.args.get("transition", DEFAULT_TRANSITION) + self.color_wheel = get_color_wheel( + self.args.get("color_wheel", "default_color_wheel") + ) + self._supported_color_modes = self.args.get("supported_color_modes") + + self.smooth_power_on = self.args.get( + "smooth_power_on", self.supports_smooth_power_on() + ) + self.add_transition = self.args.get("add_transition", DEFAULT_ADD_TRANSITION) + self.add_transition_turn_toggle = self.args.get( + "add_transition_turn_toggle", DEFAULT_TRANSITION_TURN_TOGGLE + ) + self.hold_toggle_direction_init = self.get_option( + self.args.get( + "hold_toggle_direction_init", DEFAULT_HOLD_TOGGLE_DIRECTION_INIT + ), + [StepperDir.UP, StepperDir.DOWN], + "`hold_toggle_direction_init`", + ) + await super().init() + + def _get_entity_type(self) -> type[LightEntity]: + return LightEntity + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + return { + Light.ON: self.on, + Light.OFF: self.off, + Light.TOGGLE: self.toggle, + Light.TOGGLE_FULL_BRIGHTNESS: ( + self.toggle_full, + (LightController.ATTRIBUTE_BRIGHTNESS,), + ), + Light.TOGGLE_FULL_WHITE_VALUE: ( + self.toggle_full, + (LightController.ATTRIBUTE_WHITE_VALUE,), + ), + Light.TOGGLE_FULL_COLOR_TEMP: ( + self.toggle_full, + (LightController.ATTRIBUTE_COLOR_TEMP,), + ), + Light.TOGGLE_MIN_BRIGHTNESS: ( + self.toggle_min, + (LightController.ATTRIBUTE_BRIGHTNESS,), + ), + Light.TOGGLE_MIN_WHITE_VALUE: ( + self.toggle_min, + (LightController.ATTRIBUTE_WHITE_VALUE,), + ), + Light.TOGGLE_MIN_COLOR_TEMP: ( + self.toggle_min, + (LightController.ATTRIBUTE_COLOR_TEMP,), + ), + Light.RELEASE: self.release, + Light.ON_FULL_BRIGHTNESS: ( + self.on_full, + (LightController.ATTRIBUTE_BRIGHTNESS,), + ), + Light.ON_FULL_WHITE_VALUE: ( + self.on_full, + (LightController.ATTRIBUTE_WHITE_VALUE,), + ), + Light.ON_FULL_COLOR_TEMP: ( + self.on_full, + (LightController.ATTRIBUTE_COLOR_TEMP,), + ), + Light.ON_MIN_BRIGHTNESS: ( + self.on_min, + (LightController.ATTRIBUTE_BRIGHTNESS,), + ), + Light.ON_MIN_WHITE_VALUE: ( + self.on_min, + (LightController.ATTRIBUTE_WHITE_VALUE,), + ), + Light.ON_MIN_COLOR_TEMP: ( + self.on_min, + (LightController.ATTRIBUTE_COLOR_TEMP,), + ), + Light.ON_MIN_MAX_BRIGHTNESS: ( + self.on_min_max, + (LightController.ATTRIBUTE_BRIGHTNESS,), + ), + Light.ON_MAX_MIN_BRIGHTNESS: ( + self.on_max_min, + (LightController.ATTRIBUTE_BRIGHTNESS,), + ), + Light.ON_MIN_MAX_COLOR_TEMP: ( + self.on_min_max, + (LightController.ATTRIBUTE_COLOR_TEMP,), + ), + Light.ON_MAX_MIN_COLOR_TEMP: ( + self.on_max_min, + (LightController.ATTRIBUTE_COLOR_TEMP,), + ), + Light.SET_HALF_BRIGHTNESS: ( + self.set_value, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + 0.5, + ), + ), + Light.SET_HALF_WHITE_VALUE: ( + self.set_value, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + 0.5, + ), + ), + Light.SET_HALF_COLOR_TEMP: ( + self.set_value, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + 0.5, + ), + ), + Light.SYNC: self.sync, + Light.CLICK: self.click, + Light.CLICK_BRIGHTNESS_UP: ( + self.click, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.UP, + ), + ), + Light.CLICK_BRIGHTNESS_DOWN: ( + self.click, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.DOWN, + ), + ), + Light.CLICK_WHITE_VALUE_UP: ( + self.click, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + StepperDir.UP, + ), + ), + Light.CLICK_WHITE_VALUE_DOWN: ( + self.click, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + StepperDir.DOWN, + ), + ), + Light.CLICK_COLOR_UP: ( + self.click, + ( + LightController.ATTRIBUTE_COLOR, + StepperDir.UP, + ), + ), + Light.CLICK_COLOR_DOWN: ( + self.click, + ( + LightController.ATTRIBUTE_COLOR, + StepperDir.DOWN, + ), + ), + Light.CLICK_COLOR_TEMP_UP: ( + self.click, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.UP, + ), + ), + Light.CLICK_COLOR_TEMP_DOWN: ( + self.click, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.DOWN, + ), + ), + Light.CLICK_XY_COLOR_UP: ( + self.click, + ( + LightController.ATTRIBUTE_XY_COLOR, + StepperDir.UP, + ), + ), + Light.CLICK_XY_COLOR_DOWN: ( + self.click, + ( + LightController.ATTRIBUTE_XY_COLOR, + StepperDir.DOWN, + ), + ), + Light.HOLD: self.hold, + Light.HOLD_BRIGHTNESS_UP: ( + self.hold, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.UP, + ), + ), + Light.HOLD_BRIGHTNESS_DOWN: ( + self.hold, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.DOWN, + ), + ), + Light.HOLD_BRIGHTNESS_TOGGLE: ( + self.hold, + ( + LightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.TOGGLE, + ), + ), + Light.HOLD_WHITE_VALUE_UP: ( + self.hold, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + StepperDir.UP, + ), + ), + Light.HOLD_WHITE_VALUE_DOWN: ( + self.hold, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + StepperDir.DOWN, + ), + ), + Light.HOLD_WHITE_VALUE_TOGGLE: ( + self.hold, + ( + LightController.ATTRIBUTE_WHITE_VALUE, + StepperDir.TOGGLE, + ), + ), + Light.HOLD_COLOR_UP: ( + self.hold, + ( + LightController.ATTRIBUTE_COLOR, + StepperDir.UP, + ), + ), + Light.HOLD_COLOR_DOWN: ( + self.hold, + ( + LightController.ATTRIBUTE_COLOR, + StepperDir.DOWN, + ), + ), + Light.HOLD_COLOR_TOGGLE: ( + self.hold, + ( + LightController.ATTRIBUTE_COLOR, + StepperDir.TOGGLE, + ), + ), + Light.HOLD_COLOR_TEMP_UP: ( + self.hold, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.UP, + ), + ), + Light.HOLD_COLOR_TEMP_DOWN: ( + self.hold, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.DOWN, + ), + ), + Light.HOLD_COLOR_TEMP_TOGGLE: ( + self.hold, + ( + LightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.TOGGLE, + ), + ), + Light.HOLD_XY_COLOR_UP: ( + self.hold, + ( + LightController.ATTRIBUTE_XY_COLOR, + StepperDir.UP, + ), + ), + Light.HOLD_XY_COLOR_DOWN: ( + self.hold, + ( + LightController.ATTRIBUTE_XY_COLOR, + StepperDir.DOWN, + ), + ), + Light.HOLD_XY_COLOR_TOGGLE: ( + self.hold, + ( + LightController.ATTRIBUTE_XY_COLOR, + StepperDir.TOGGLE, + ), + ), + Light.XYCOLOR_FROM_CONTROLLER: self.xycolor_from_controller, + Light.COLORTEMP_FROM_CONTROLLER: self.colortemp_from_controller, + Light.COLORTEMP_FROM_CONTROLLER_STEP: ( + self.attribute_from_controller_step, + (LightController.ATTRIBUTE_COLOR_TEMP,), + ), + Light.BRIGHTNESS_FROM_CONTROLLER_LEVEL: self.brightness_from_controller_level, + Light.BRIGHTNESS_FROM_CONTROLLER_ANGLE: self.brightness_from_controller_angle, + Light.BRIGHTNESS_FROM_CONTROLLER_STEP: ( + self.attribute_from_controller_step, + (LightController.ATTRIBUTE_BRIGHTNESS,), + ), + } + + async def check_remove_transition(self, on_from_user: bool) -> bool: + return ( + not self.add_transition + or (on_from_user and not self.add_transition_turn_toggle) + or await self.feature_support.not_supported(LightSupport.TRANSITION) + ) + + async def call_light_service( + self, service: str, force_transition: bool = False, **attributes: Any + ) -> None: + if "transition" not in attributes: + attributes["transition"] = self.transition / 1000 + if self.remove_transition_check and not force_transition: + del attributes["transition"] + await self.call_service(service, entity_id=self.entity.name, **attributes) + + async def _on(self, **attributes: Any) -> None: + await self.call_light_service("light/turn_on", **attributes) + + @action + async def on(self, attributes: dict[str, float] | None = None) -> None: + attributes = {} if attributes is None else attributes + await self._on(**attributes) + + async def _off(self, **attributes: Any) -> None: + await self.call_light_service("light/turn_off", **attributes) + + @action + async def off(self) -> None: + await self._off() + + async def _toggle(self, **attributes: Any) -> None: + await self.call_light_service("light/toggle", **attributes) + + @action + async def toggle(self, attributes: dict[str, float] | None = None) -> None: + attributes = {} if attributes is None else attributes + await self._toggle(**attributes) + + async def _set_value(self, attribute: str, fraction: float) -> None: + fraction = max(0, min(fraction, 1)) + min_ = self.min_max_attributes[attribute].min + max_ = self.min_max_attributes[attribute].max + value = (max_ - min_) * fraction + min_ + await self._on(**{attribute: value}) + + @action + async def set_value(self, attribute: str, fraction: float) -> None: + await self._set_value(attribute, fraction) + + @action + async def toggle_full(self, attribute: str) -> None: + await self._toggle(**{attribute: self.min_max_attributes[attribute].max}) + + @action + async def toggle_min(self, attribute: str) -> None: + await self._toggle(**{attribute: self.min_max_attributes[attribute].min}) + + async def _on_full(self, attribute: str) -> None: + await self._set_value(attribute, 1) + + @action + async def on_full(self, attribute: str) -> None: + await self._on_full(attribute) + + async def _on_min(self, attribute: str) -> None: + await self._set_value(attribute, 0) + + @action + async def on_min(self, attribute: str) -> None: + await self._on_min(attribute) + + @action + async def on_min_max(self, attribute: str) -> None: + min_ = self.min_max_attributes[attribute].min + max_ = self.min_max_attributes[attribute].max + await self._on_min_max(attribute, default=min_, other=max_) + + @action + async def on_max_min(self, attribute: str) -> None: + min_ = self.min_max_attributes[attribute].min + max_ = self.min_max_attributes[attribute].max + await self._on_min_max(attribute, default=max_, other=min_) + + async def _on_min_max( + self, attribute: str, *, default: Number, other: Number + ) -> None: + light_state: str + attribute_value: Number + light_state, attribute_value = await asyncio.gather( + self.get_entity_state(), self.get_entity_state(attribute=attribute) + ) + + if light_state == "off" or attribute_value != default: + await self._on(**{attribute: default}) + else: + await self._on(**{attribute: other}) + + @action + async def sync( + self, + brightness: int | None = None, + color_temp: int = 370, # 2700K light + xy_color: Color = (0.323, 0.329), # white colour + ) -> None: + attributes: dict[Any, Any] = {} + try: + color_attribute = await self.get_attribute(LightController.ATTRIBUTE_COLOR) + if color_attribute == LightController.ATTRIBUTE_COLOR_TEMP: + attributes[color_attribute] = color_temp + else: + attributes[color_attribute] = list(xy_color) + except ValueError: + self.log( + "⚠️ `sync` action will only change brightness", + level="WARNING", + ascii_encode=False, + ) + await self._on( + **attributes, + brightness=( + brightness + or self.min_max_attributes[LightController.ATTRIBUTE_BRIGHTNESS].max + ), + ) + + @action + async def xycolor_from_controller(self, extra: EventData | None = None) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_color" not in extra: + self.log( + "`action_color` is not present in the MQTT payload", level="WARNING" + ) + return + xy_color = extra["action_color"] + await self._on(xy_color=[xy_color["x"], xy_color["y"]]) + elif isinstance(self.integration, DeCONZIntegration): + if "xy" not in extra: + self.log("`xy` is not present in the deCONZ event", level="WARNING") + return + await self._on(xy_color=list(extra["xy"])) + + @action + async def colortemp_from_controller(self, extra: EventData | None = None) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_color_temperature" not in extra: + self.log( + "`action_color_temperature` is not present in the MQTT payload", + level="WARNING", + ) + return + await self._on(color_temp=extra["action_color_temperature"]) + + @action + async def brightness_from_controller_level( + self, extra: EventData | None = None + ) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_level" not in extra: + self.log( + "`action_level` is not present in the MQTT payload", + level="WARNING", + ) + return + await self._on(brightness=extra["action_level"]) + + @action + async def attribute_from_controller_step( + self, attribute: str, extra: EventData | None = None + ) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, ZHAIntegration): + try: + step_mode: int = extra["params"]["step_mode"] + step_size: int = extra["params"]["step_size"] + stepper = self.generate_stepper( + attribute, step_size, StepperMode.STOP, relative_steps=False + ) + direction = StepperDir.UP + if ( + attribute == LightController.ATTRIBUTE_BRIGHTNESS and step_mode == 1 + ) or ( + attribute == LightController.ATTRIBUTE_COLOR_TEMP and step_mode == 3 + ): + direction = StepperDir.DOWN + + # Transition time in seconds + transition_time: int = extra["params"]["transition_time"] + self.value_attribute = await self.get_value_attribute(attribute) + extra_attributes = { + "transition": transition_time, + "force_transition": True, + } + await self.change_light_state( + self.value_attribute, + attribute, + direction, + stepper, + extra_attributes=extra_attributes, + ) + except (KeyError, TypeError): + self.log( + "`params` should be present in ZHA event with " + "`step_mode`, `step_size`, and `transition_time`", + level="WARNING", + ) + + @action + async def brightness_from_controller_angle( + self, + mode: str = StepperMode.STOP, + steps: Number | None = None, + extra: EventData | None = None, + ) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_rotation_angle" not in extra: + self.log( + "`action_rotation_angle` is not present in the MQTT payload", + level="WARNING", + ) + return + angle = extra["action_rotation_angle"] + direction = StepperDir.UP if angle > 0 else StepperDir.DOWN + await self._hold( + LightController.ATTRIBUTE_BRIGHTNESS, direction, mode=mode, steps=steps + ) + + @property + async def supported_color_modes(self) -> set[str]: + if self._supported_color_modes is None or self.update_supported_features: + supported_color_modes: list[str] = await self.get_entity_state( + attribute="supported_color_modes" + ) + if supported_color_modes is not None: + self._supported_color_modes = set(supported_color_modes) + else: + raise ValueError( + f"`supported_color_modes` could not be read from `{self.entity}`. " + "Entity might not be available." + ) + + return self._supported_color_modes + + async def is_color_supported(self) -> bool: + return len(COLOR_MODES.intersection(await self.supported_color_modes)) > 0 + + async def is_colortemp_supported(self) -> bool: + return "color_temp" in await self.supported_color_modes + + def generate_stepper( + self, attribute: str, steps: Number, mode: str, *, relative_steps: bool = True + ) -> Stepper: + previous_direction = Stepper.invert_direction(self.hold_toggle_direction_init) + if attribute == LightController.ATTRIBUTE_XY_COLOR: + return IndexLoopStepper( + len(self.color_wheel), + previous_direction, + relative_steps, + ) + if mode not in STEPPER_MODES: + raise ValueError( + f"`{mode}` mode is not available. Options are: {list(STEPPER_MODES.keys())}" + ) + stepper_cls = STEPPER_MODES[mode] + return stepper_cls( + self.min_max_attributes[attribute], + steps, + previous_direction, + relative_steps, + ) + + @lru_cache(maxsize=None) + def get_stepper( + self, attribute: str, steps: Number, mode: str, *, tag: str + ) -> Stepper: + return self.generate_stepper(attribute, steps, mode) + + async def get_attribute(self, attribute: str) -> str: + if attribute == LightController.ATTRIBUTE_COLOR: + if self.entity.color_mode == "auto": + if await self.is_color_supported(): + return LightController.ATTRIBUTE_XY_COLOR + elif await self.is_colortemp_supported(): + return LightController.ATTRIBUTE_COLOR_TEMP + else: + raise ValueError( + "This light does not support xy_color or color_temp" + ) + else: + return self.entity.color_mode + else: + return attribute + + async def get_value_attribute(self, attribute: str) -> Number: + if self.smooth_power_on_check: + return 0 + if attribute == LightController.ATTRIBUTE_XY_COLOR: + return 0 + elif ( + attribute == LightController.ATTRIBUTE_BRIGHTNESS + or attribute == LightController.ATTRIBUTE_WHITE_VALUE + or attribute == LightController.ATTRIBUTE_COLOR_TEMP + ): + value = await self.get_entity_state(attribute=attribute) + if value is None and attribute == LightController.ATTRIBUTE_COLOR_TEMP: + return float(DEFAULT_COLOR_TEMP) + elif value is None and attribute != LightController.ATTRIBUTE_COLOR_TEMP: + raise ValueError( + f"Value for `{attribute}` attribute could not be retrieved " + f"from `{self.entity.main}`. " + "Check the FAQ to know more about this error: " + "https://xaviml.github.io/controllerx/faq" + ) + else: + try: + return float(value) + except ValueError: + raise ValueError( + f"Attribute `{attribute}` with `{value}` as a value " + "could not be converted to float" + ) + else: + raise ValueError(f"Attribute `{attribute}` not expected") + + def check_smooth_power_on( + self, attribute: str, direction: str, light_state: str + ) -> bool: + return ( + direction != StepperDir.DOWN + and attribute == self.ATTRIBUTE_BRIGHTNESS + and self.smooth_power_on + and light_state == "off" + ) + + async def before_action(self, action: str, *args: Any, **kwargs: Any) -> bool: + to_return = True + self.next_direction = None + light_state: str + if action in ("click", "hold"): + if len(args) == 2: + attribute, direction = args + elif "attribute" in kwargs and "direction" in kwargs: + attribute, direction = kwargs["attribute"], kwargs["direction"] + else: + raise ValueError( + f"`attribute` and `direction` are mandatory fields for `{action}` action" + ) + light_state = await self.get_entity_state() + self.smooth_power_on_check = self.check_smooth_power_on( + attribute, direction, light_state + ) + self.remove_transition_check = await self.check_remove_transition( + on_from_user=False + ) + to_return = (light_state == "on") or self.smooth_power_on_check + elif action == "attribute_from_controller_step": + light_state = await self.get_entity_state() + to_return = light_state == "on" + self.smooth_power_on_check = False + self.remove_transition_check = False + else: + self.remove_transition_check = await self.check_remove_transition( + on_from_user=True + ) + self.smooth_power_on_check = False + return await super().before_action(action, *args, **kwargs) and to_return + + @action + async def click( + self, + attribute: str, + direction: str, + mode: str = StepperMode.STOP, + steps: Number | None = None, + ) -> None: + attribute = self.get_option( + attribute, LightController.ATTRIBUTES_LIST, "`click` action" + ) + direction = self.get_option( + direction, [StepperDir.UP, StepperDir.DOWN], "`click` action" + ) + mode = self.get_option( + mode, [StepperMode.STOP, StepperMode.LOOP], "`click` action" + ) + attribute = await self.get_attribute(attribute) + self.value_attribute = await self.get_value_attribute(attribute) + await self.change_light_state( + self.value_attribute, + attribute, + direction, + self.get_stepper(attribute, steps or self.manual_steps, mode, tag="click"), + ) + + @action + async def hold( # type: ignore[override] + self, + attribute: str, + direction: str, + mode: str = StepperMode.STOP, + steps: Number | None = None, + ) -> None: + await self._hold(attribute, direction, mode, steps) + + async def _hold( + self, + attribute: str, + direction: str, + mode: str = StepperMode.STOP, + steps: Number | None = None, + ) -> None: + attribute = self.get_option( + attribute, LightController.ATTRIBUTES_LIST, "`hold` action" + ) + direction = self.get_option( + direction, + [StepperDir.UP, StepperDir.DOWN, StepperDir.TOGGLE], + "`hold` action", + ) + mode = self.get_option( + mode, + [StepperMode.STOP, StepperMode.LOOP, StepperMode.BOUNCE], + "`hold` action", + ) + attribute = await self.get_attribute(attribute) + self.value_attribute = await self.get_value_attribute(attribute) + self.log( + f"Attribute value before running the hold action: {self.value_attribute}", + level="DEBUG", + ) + stepper = self.get_stepper( + attribute, steps or self.automatic_steps, mode, tag="hold" + ) + if direction == StepperDir.TOGGLE: + self.log( + f"Previous direction: {stepper.previous_direction}", + level="DEBUG", + ) + direction = stepper.get_direction(self.value_attribute, direction) + self.log(f"Going direction: {direction}", level="DEBUG") + await super().hold(attribute, direction, stepper) + + async def hold_loop( + self, + attribute: str, + direction: str, + stepper: Stepper, + ) -> bool: + if self.value_attribute is None: + return True + extra_attributes = {"transition": self.delay / 1000} + return await self.change_light_state( + self.value_attribute, + attribute, + direction, + stepper, + extra_attributes=extra_attributes, + ) + + async def change_light_state( + self, + old: float, + attribute: str, + direction: str, + stepper: Stepper, + *, + extra_attributes: dict[str, Any] | None = None, + ) -> bool: + """ + This functions changes the state of the light depending on the previous + value and attribute. It returns True when no more changes will need to be done. + Otherwise, it returns False. + """ + attributes: dict[str, Any] = ( + {} if extra_attributes is None else extra_attributes + ) + direction = self.next_direction or direction + if attribute == LightController.ATTRIBUTE_XY_COLOR: + stepper_output = stepper.step(self.index_color, direction) + self.index_color = int(stepper_output.next_value) + xy_color = self.color_wheel[self.index_color] + attributes[attribute] = list(xy_color) + await self._on(**attributes) + # In case of xy_color mode it never finishes the loop, the hold loop + # will only stop if the hold action is called when releasing the button. + # I haven't experimented any problems with it, but a future implementation + # would be to force the loop to stop after 4 or 5 loops as a safety measure. + return False + if self.smooth_power_on_check: + await self._on_min(attribute) + # # After smooth power on, the light should not brighten up. + return True + stepper_output = stepper.step(old, direction) + self.next_direction = stepper_output.next_direction + next_value = int(stepper_output.next_value) + attributes[attribute] = next_value + await self._on(**attributes) + self.value_attribute = next_value + return stepper_output.exceeded + + def supports_smooth_power_on(self) -> bool: + """ + This function can be overrided for each device to indicate the default behaviour of the controller + when the associated light is off and an event for incrementing brightness is received. + Returns True if the associated light should be turned on with minimum brightness if an event for incrementing + brightness is received, while the lamp is off. + The behaviour can be overridden by the user with the 'smooth_power_on' option in app configuration. + """ + return False diff --git a/appdaemon/apps/controllerx/cx_core/type/media_player_controller.py b/appdaemon/apps/controllerx/cx_core/type/media_player_controller.py new file mode 100644 index 0000000..b03dad2 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/type/media_player_controller.py @@ -0,0 +1,196 @@ +from typing import Any + +from cx_const import MediaPlayer, Number, PredefinedActionsMapping, StepperDir +from cx_core.controller import action +from cx_core.feature_support.media_player import MediaPlayerSupport +from cx_core.integration import EventData +from cx_core.integration.z2m import Z2MIntegration +from cx_core.release_hold_controller import ReleaseHoldController +from cx_core.stepper import MinMax +from cx_core.stepper.index_loop_stepper import IndexLoopStepper +from cx_core.stepper.stop_stepper import StopStepper +from cx_core.type_controller import Entity, TypeController + +DEFAULT_VOLUME_STEPS = 10 + + +class MediaPlayerController(TypeController[Entity], ReleaseHoldController): + domains = ["media_player"] + entity_arg = "media_player" + volume_level: float + + async def init(self) -> None: + volume_steps = self.args.get("volume_steps", DEFAULT_VOLUME_STEPS) + self.volume_stepper = StopStepper(MinMax(0, 1), volume_steps) + self.volume_level = 0.0 + await super().init() + + def _get_entity_type(self) -> type[Entity]: + return Entity + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + return { + MediaPlayer.HOLD_VOLUME_DOWN: (self.hold, (StepperDir.DOWN,)), + MediaPlayer.HOLD_VOLUME_UP: (self.hold, (StepperDir.UP,)), + MediaPlayer.CLICK_VOLUME_DOWN: self.volume_down, + MediaPlayer.CLICK_VOLUME_UP: self.volume_up, + MediaPlayer.VOLUME_SET: self.volume_set, + MediaPlayer.RELEASE: self.release, + MediaPlayer.PLAY: self.play, + MediaPlayer.PAUSE: self.pause, + MediaPlayer.PLAY_PAUSE: self.play_pause, + MediaPlayer.NEXT_TRACK: self.next_track, + MediaPlayer.PREVIOUS_TRACK: self.previous_track, + MediaPlayer.NEXT_SOURCE: (self.change_source_list, (StepperDir.UP,)), + MediaPlayer.PREVIOUS_SOURCE: (self.change_source_list, (StepperDir.DOWN,)), + MediaPlayer.MUTE: self.volume_mute, + MediaPlayer.TTS: self.tts, + MediaPlayer.VOLUME_FROM_CONTROLLER_ANGLE: self.volume_from_controller_angle, + } + + @action + async def change_source_list(self, direction: str) -> None: + entity_states = await self.get_entity_state(attribute="all") + entity_attributes = entity_states["attributes"] + source_list: list[str] | None = entity_attributes.get("source_list") + if source_list is None or len(source_list) == 0: + self.log( + f"⚠️ There is no `source_list` parameter in `{self.entity}`", + level="WARNING", + ascii_encode=False, + ) + return + source = entity_attributes.get("source") + new_index_source: Number + if source is None: + new_index_source = 0 + else: + index_source = source_list.index(source) + source_stepper = IndexLoopStepper(len(source_list)) + stepper_output = source_stepper.step(index_source, direction) + new_index_source = stepper_output.next_value + await self.call_service( + "media_player/select_source", + entity_id=self.entity.name, + source=source_list[int(new_index_source)], + ) + + @action + async def play(self) -> None: + await self.call_service("media_player/media_play", entity_id=self.entity.name) + + @action + async def pause(self) -> None: + await self.call_service("media_player/media_pause", entity_id=self.entity.name) + + @action + async def play_pause(self) -> None: + await self.call_service( + "media_player/media_play_pause", entity_id=self.entity.name + ) + + @action + async def previous_track(self) -> None: + await self.call_service( + "media_player/media_previous_track", entity_id=self.entity.name + ) + + @action + async def next_track(self) -> None: + await self.call_service( + "media_player/media_next_track", entity_id=self.entity.name + ) + + @action + async def volume_set(self, volume_level: float) -> None: + await self.call_service( + "media_player/volume_set", + entity_id=self.entity.name, + volume_level=volume_level, + ) + + @action + async def volume_up(self) -> None: + await self.prepare_volume_change() + await self.volume_change(StepperDir.UP) + + @action + async def volume_down(self) -> None: + await self.prepare_volume_change() + await self.volume_change(StepperDir.DOWN) + + @action + async def volume_mute(self) -> None: + await self.call_service("media_player/volume_mute", entity_id=self.entity.name) + + @action + async def tts( + self, + message: str, + service: str = "google_translate_say", + cache: bool | None = None, + language: str | None = None, + options: dict[str, Any] | None = None, + ) -> None: + args: dict[str, Any] = {"entity_id": self.entity.name, "message": message} + if cache is not None: + args["cache"] = cache + if language is not None: + args["language"] = language + if options is not None: + args["options"] = options + await self.call_service(f"tts.{service}", **args) + + @action + async def volume_from_controller_angle( + self, extra: EventData | None = None + ) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_rotation_angle" not in extra: + self.log( + "`action_rotation_angle` is not present in the MQTT payload", + level="WARNING", + ) + return + angle = extra["action_rotation_angle"] + direction = StepperDir.UP if angle > 0 else StepperDir.DOWN + await self._hold(direction) + + @action + async def hold(self, direction: str) -> None: # type: ignore[override] + await self._hold(direction) + + async def _hold(self, direction: str) -> None: + await self.prepare_volume_change() + await super().hold(direction) + + async def prepare_volume_change(self) -> None: + volume_level = await self.get_entity_state(attribute="volume_level") + if volume_level is not None: + self.volume_level = volume_level + + async def volume_change(self, direction: str) -> bool: + if await self.feature_support.is_supported(MediaPlayerSupport.VOLUME_SET): + stepper_output = self.volume_stepper.step(self.volume_level, direction) + self.volume_level = stepper_output.next_value + await self.volume_set(self.volume_level) + return stepper_output.exceeded + else: + if direction == StepperDir.UP: + await self.call_service( + "media_player/volume_up", entity_id=self.entity.name + ) + else: + await self.call_service( + "media_player/volume_down", entity_id=self.entity.name + ) + return False + + async def hold_loop(self, direction: str) -> bool: + return await self.volume_change(direction) + + def default_delay(self) -> int: + return 500 diff --git a/appdaemon/apps/controllerx/cx_core/type/switch_controller.py b/appdaemon/apps/controllerx/cx_core/type/switch_controller.py new file mode 100644 index 0000000..d4cab4a --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/type/switch_controller.py @@ -0,0 +1,48 @@ +from cx_const import PredefinedActionsMapping, Switch +from cx_core.controller import action +from cx_core.type_controller import Entity, TypeController + + +class SwitchController(TypeController[Entity]): + """ + This is the main class that controls the switches for different devices. + Type of actions: + - On/Off/Toggle + Parameters taken: + - controller (required): Inherited from Controller + - switch (required): Switch entity name + """ + + domains = [ + "switch", + "alert", + "automation", + "cover", + "input_boolean", + "light", + "media_player", + "script", + ] + entity_arg = "switch" + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + return { + Switch.ON: self.on, + Switch.OFF: self.off, + Switch.TOGGLE: self.toggle, + } + + def _get_entity_type(self) -> type[Entity]: + return Entity + + @action + async def on(self) -> None: + await self.call_service("homeassistant/turn_on", entity_id=self.entity.name) + + @action + async def off(self) -> None: + await self.call_service("homeassistant/turn_off", entity_id=self.entity.name) + + @action + async def toggle(self) -> None: + await self.call_service("homeassistant/toggle", entity_id=self.entity.name) diff --git a/appdaemon/apps/controllerx/cx_core/type/z2m_light_controller.py b/appdaemon/apps/controllerx/cx_core/type/z2m_light_controller.py new file mode 100644 index 0000000..f6576d3 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/type/z2m_light_controller.py @@ -0,0 +1,429 @@ +import json +from collections.abc import Awaitable, Callable +from functools import lru_cache +from typing import Annotated, Any, Literal + +from cx_const import PredefinedActionsMapping, StepperDir, Z2MLight +from cx_core.controller import Controller, action +from cx_core.integration import EventData +from cx_core.integration.z2m import Z2MIntegration +from cx_core.stepper import InvertStepper, MinMax +from cx_core.type_controller import Entity, TypeController + +DEFAULT_CLICK_STEPS = 70 +DEFAULT_HOLD_STEPS = 70 +DEFAULT_TRANSITION = 0.5 +DEFAULT_MODE = "ha" +DEFAULT_TOPIC_PREFIX = "zigbee2mqtt" + +Mode = Annotated[str, Literal["ha", "mqtt"]] + + +class Z2MLightEntity(Entity): + mode: Mode + topic_prefix: str + + def __init__( + self, + name: str, + entities: list[str] | None = None, + mode: Mode = DEFAULT_MODE, + topic_prefix: str = DEFAULT_TOPIC_PREFIX, + ) -> None: + super().__init__(name, entities) + mode = Controller.get_option(mode, ["ha", "mqtt"]) + self.mode = mode + self.topic_prefix = topic_prefix + + +class Z2MLightController(TypeController[Z2MLightEntity]): + """ + This is the main class that controls the Zigbee2MQTT lights for different devices. + Type of actions: + - On/Off/Toggle + - Brightness click and hold + - Color temp click and hold + """ + + ATTRIBUTE_BRIGHTNESS = "brightness" + ATTRIBUTE_COLOR_TEMP = "color_temp" + + ATTRIBUTES_LIST = [ + ATTRIBUTE_BRIGHTNESS, + ATTRIBUTE_COLOR_TEMP, + ] + + MIN_MAX_ATTR = { + ATTRIBUTE_BRIGHTNESS: MinMax(min=1, max=254), + ATTRIBUTE_COLOR_TEMP: MinMax(min=250, max=454), + } + + entity_arg = "light" + + click_steps: float + hold_steps: float + transition: float + use_onoff: bool + + hold_attribute: str | None + + _mqtt_fn: dict[Mode, Callable[[str, str], Awaitable[None]]] + + async def init(self) -> None: + self.click_steps = self.args.get("click_steps", DEFAULT_CLICK_STEPS) + self.hold_steps = self.args.get("hold_steps", DEFAULT_HOLD_STEPS) + self.transition = self.args.get("transition", DEFAULT_TRANSITION) + self.use_onoff = self.args.get("use_onoff", False) + + self._mqtt_fn = { + "ha": self._ha_mqtt_call, + "mqtt": self._mqtt_plugin_call, + } + self.hold_attribute = None + + await super().init() + + def _get_entity_type(self) -> type[Z2MLightEntity]: + return Z2MLightEntity + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + return { + Z2MLight.ON: self.on, + Z2MLight.OFF: self.off, + Z2MLight.TOGGLE: self.toggle, + Z2MLight.RELEASE: self.release, + Z2MLight.ON_FULL_BRIGHTNESS: ( + self.on_full, + (Z2MLightController.ATTRIBUTE_BRIGHTNESS,), + ), + Z2MLight.ON_FULL_COLOR_TEMP: ( + self.on_full, + (Z2MLightController.ATTRIBUTE_COLOR_TEMP,), + ), + Z2MLight.ON_MIN_BRIGHTNESS: ( + self.on_min, + (Z2MLightController.ATTRIBUTE_BRIGHTNESS,), + ), + Z2MLight.ON_MIN_COLOR_TEMP: ( + self.on_min, + (Z2MLightController.ATTRIBUTE_COLOR_TEMP,), + ), + Z2MLight.SET_HALF_BRIGHTNESS: ( + self.set_value, + ( + Z2MLightController.ATTRIBUTE_BRIGHTNESS, + 0.5, + ), + ), + Z2MLight.SET_HALF_COLOR_TEMP: ( + self.set_value, + ( + Z2MLightController.ATTRIBUTE_COLOR_TEMP, + 0.5, + ), + ), + Z2MLight.CLICK: self.click, + Z2MLight.CLICK_BRIGHTNESS_UP: ( + self.click, + ( + Z2MLightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.UP, + ), + ), + Z2MLight.CLICK_COLOR_TEMP_UP: ( + self.click, + ( + Z2MLightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.UP, + ), + ), + Z2MLight.CLICK_BRIGHTNESS_DOWN: ( + self.click, + ( + Z2MLightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.DOWN, + ), + ), + Z2MLight.CLICK_COLOR_TEMP_DOWN: ( + self.click, + ( + Z2MLightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.DOWN, + ), + ), + Z2MLight.HOLD: self.hold, + Z2MLight.HOLD_BRIGHTNESS_UP: ( + self.hold, + ( + Z2MLightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.UP, + ), + ), + Z2MLight.HOLD_COLOR_TEMP_UP: ( + self.hold, + ( + Z2MLightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.UP, + ), + ), + Z2MLight.HOLD_BRIGHTNESS_DOWN: ( + self.hold, + ( + Z2MLightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.DOWN, + ), + ), + Z2MLight.HOLD_BRIGHTNESS_TOGGLE: ( + self.hold, + ( + Z2MLightController.ATTRIBUTE_BRIGHTNESS, + StepperDir.TOGGLE, + ), + ), + Z2MLight.HOLD_COLOR_TEMP_DOWN: ( + self.hold, + ( + Z2MLightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.DOWN, + ), + ), + Z2MLight.HOLD_COLOR_TEMP_TOGGLE: ( + self.hold, + ( + Z2MLightController.ATTRIBUTE_COLOR_TEMP, + StepperDir.TOGGLE, + ), + ), + Z2MLight.XYCOLOR_FROM_CONTROLLER: self.xycolor_from_controller, + Z2MLight.COLORTEMP_FROM_CONTROLLER: self.colortemp_from_controller, + Z2MLight.BRIGHTNESS_FROM_CONTROLLER_LEVEL: self.brightness_from_controller_level, + Z2MLight.BRIGHTNESS_FROM_CONTROLLER_ANGLE: self.brightness_from_controller_angle, + } + + async def before_action(self, action: str, *args: Any, **kwargs: Any) -> bool: + to_return = not (action == "hold" and self.hold_attribute is not None) + return await super().before_action(action, *args, **kwargs) and to_return + + async def _ha_mqtt_call(self, topic: str, payload: str) -> None: + await self.call_service("mqtt.publish", topic=topic, payload=payload) + + async def _mqtt_plugin_call(self, topic: str, payload: str) -> None: + await self.call_service( + "mqtt.publish", topic=topic, payload=payload, namespace="mqtt" + ) + + async def _mqtt_call(self, payload: dict[str, Any]) -> None: + await self._mqtt_fn[self.entity.mode]( + f"{self.entity.topic_prefix}/{self.entity.name}/set", json.dumps(payload) + ) + + async def _on(self, **attributes: Any) -> None: + await self._mqtt_call({"state": "ON", **attributes}) + + @action + async def on(self, attributes: dict[str, float] | None = None) -> None: + attributes = attributes or {} + await self._on(**attributes) + + async def _off(self) -> None: + await self._mqtt_call({"state": "OFF"}) + + @action + async def off(self) -> None: + await self._off() + + async def _toggle(self, **attributes: Any) -> None: + await self._mqtt_call({"state": "TOGGLE", **attributes}) + + @action + async def toggle(self, attributes: dict[str, float] | None = None) -> None: + attributes = attributes or {} + await self._toggle(**attributes) + + async def _set_value(self, attribute: str, fraction: float) -> None: + fraction = max(0, min(fraction, 1)) + min_ = self.MIN_MAX_ATTR[attribute].min + max_ = self.MIN_MAX_ATTR[attribute].max + value = (max_ - min_) * fraction + min_ + await self._on(**{attribute: value}) + + @action + async def set_value(self, attribute: str, fraction: float) -> None: + await self._set_value(attribute, fraction) + + async def _on_full(self, attribute: str) -> None: + await self._set_value(attribute, 1) + + @action + async def on_full(self, attribute: str) -> None: + await self._on_full(attribute) + + async def _on_min(self, attribute: str) -> None: + await self._set_value(attribute, 0) + + @action + async def on_min(self, attribute: str) -> None: + await self._on_min(attribute) + + @lru_cache(maxsize=None) + def get_stepper(self, attribute: str, steps: float, *, tag: str) -> InvertStepper: + previous_direction = StepperDir.DOWN + return InvertStepper(self.MIN_MAX_ATTR[attribute], steps, previous_direction) + + async def _change_light_state( + self, + *, + attribute: str, + direction: str, + stepper: InvertStepper, + transition: float | None, + use_onoff: bool, + mode: str, + ) -> None: + onoff_cmd = ( + "_onoff" if use_onoff and attribute == self.ATTRIBUTE_BRIGHTNESS else "" + ) + stepper_output = stepper.step(stepper.steps, direction) + kwargs = {} + if transition is not None: + kwargs["transition"] = transition + await self._mqtt_call( + { + f"{attribute}_{mode}{onoff_cmd}": stepper_output.next_value, + **kwargs, + } + ) + + @action + async def click( + self, + attribute: str, + direction: str, + steps: float | None = None, + transition: float | None = None, + use_onoff: bool | None = None, + ) -> None: + attribute = self.get_option(attribute, self.ATTRIBUTES_LIST, "`click` action") + direction = self.get_option( + direction, [StepperDir.UP, StepperDir.DOWN], "`click` action" + ) + steps = steps if steps is not None else self.click_steps + stepper = self.get_stepper(attribute, steps, tag="click") + await self._change_light_state( + attribute=attribute, + direction=direction, + stepper=stepper, + transition=transition if transition is not None else self.transition, + use_onoff=use_onoff if use_onoff is not None else self.use_onoff, + mode="step", + ) + + async def _hold( + self, + attribute: str, + direction: str, + steps: float | None = None, + use_onoff: bool | None = None, + ) -> None: + attribute = self.get_option(attribute, self.ATTRIBUTES_LIST, "`hold` action") + direction = self.get_option( + direction, + [StepperDir.UP, StepperDir.DOWN, StepperDir.TOGGLE], + "`hold` action", + ) + steps = steps if steps is not None else self.hold_steps + stepper = self.get_stepper(attribute, steps, tag="hold") + direction = stepper.get_direction(steps, direction) + self.hold_attribute = attribute + await self._change_light_state( + attribute=attribute, + direction=direction, + stepper=stepper, + transition=None, + use_onoff=use_onoff if use_onoff is not None else self.use_onoff, + mode="move", + ) + + @action + async def hold( + self, + attribute: str, + direction: str, + steps: float | None = None, + use_onoff: bool | None = None, + ) -> None: + await self._hold(attribute, direction, steps, use_onoff) + + @action + async def release(self) -> None: + if self.hold_attribute is None: + return + await self._mqtt_call({f"{self.hold_attribute}_move": "stop"}) + self.hold_attribute = None + + @action + async def xycolor_from_controller(self, extra: EventData | None = None) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_color" not in extra: + self.log( + "`action_color` is not present in the MQTT payload", level="WARNING" + ) + return + xy_color = extra["action_color"] + await self._on(color={"x": xy_color["x"], "y": xy_color["y"]}) + + @action + async def colortemp_from_controller(self, extra: EventData | None = None) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_color_temperature" not in extra: + self.log( + "`action_color_temperature` is not present in the MQTT payload", + level="WARNING", + ) + return + await self._on(color_temp=extra["action_color_temperature"]) + + @action + async def brightness_from_controller_level( + self, extra: EventData | None = None + ) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_level" not in extra: + self.log( + "`action_level` is not present in the MQTT payload", + level="WARNING", + ) + return + await self._on(brightness=extra["action_level"]) + + @action + async def brightness_from_controller_angle( + self, + steps: float | None = None, + use_onoff: bool | None = None, + extra: EventData | None = None, + ) -> None: + if extra is None: + self.log("No event data present", level="WARNING") + return + if isinstance(self.integration, Z2MIntegration): + if "action_rotation_angle" not in extra: + self.log( + "`action_rotation_angle` is not present in the MQTT payload", + level="WARNING", + ) + return + angle = extra["action_rotation_angle"] + direction = StepperDir.UP if angle > 0 else StepperDir.DOWN + await self._hold( + self.ATTRIBUTE_BRIGHTNESS, direction, steps=steps, use_onoff=use_onoff + ) diff --git a/appdaemon/apps/controllerx/cx_core/type_controller.py b/appdaemon/apps/controllerx/cx_core/type_controller.py new file mode 100644 index 0000000..1065686 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_core/type_controller.py @@ -0,0 +1,133 @@ +import abc +from typing import Any, Generic, TypeVar + +from cx_core.controller import Controller +from cx_core.feature_support import FeatureSupport + +EntityVar = TypeVar("EntityVar", bound="Entity") + + +class Entity: + name: str + entities: list[str] + + def __init__( + self, name: str, entities: list[str] | None = None, **kwargs: Any + ) -> None: + self.name = name + self.set_entities(entities) + + @property + def main(self) -> str: + return self.entities[0] + + @property + def is_group(self) -> bool: + return self.entities[0] != self.name + + def set_entities(self, value: list[str] | None = None) -> None: + self.entities = value if value is not None else [self.name] + + @classmethod + def instantiate( + cls: type[EntityVar], + name: str, + entities: list[str] | None = None, + **params: Any, + ) -> EntityVar: + return cls(name=name, entities=entities, **params) + + def __str__(self) -> str: + return self.name if not self.is_group else f"{self.name}({self.entities})" + + +class TypeController(Controller, abc.ABC, Generic[EntityVar]): + domains: list[str] = [] + entity_arg: str + entity: EntityVar + update_supported_features: bool + feature_support: FeatureSupport + + async def init(self) -> None: + if self.entity_arg not in self.args: + raise ValueError( + f"{self.__class__.__name__} class needs the `{self.entity_arg}` attribute" + ) + self.entity = await self._get_entity(self.args[self.entity_arg]) + self._check_domain(self.entity) + self.update_supported_features = self.args.get( + "update_supported_features", False + ) + supported_features: int | None = self.args.get("supported_features") + self.feature_support = FeatureSupport( + self, supported_features, self.update_supported_features + ) + await super().init() + + @abc.abstractmethod + def _get_entity_type(self) -> type[EntityVar]: + raise NotImplementedError + + async def _get_entities(self, entity_name: str) -> list[str] | None: + entities: str | list[str] | None = await self.get_state( + entity_name, attribute="entity_id" + ) + self.log( + f"Entities from `{entity_name}` (entity_id attribute): `{entities}`", + level="DEBUG", + ) + # If the entity groups other entities, this attribute will be a list + if not isinstance(entities, (list, tuple)): + return None + if entities is not None and len(entities) == 0: + raise ValueError(f"`{entity_name}` does not have any entities registered.") + return entities + + async def _get_entity(self, entity: str | dict[str, Any]) -> EntityVar: + entity_args: dict[str, Any] + entity_name: str + if isinstance(entity, str): + entity_name = entity + entity_args = {} + elif isinstance(entity, dict): + entity_name = entity["name"] + entity_args = {key: value for key, value in entity.items() if key != "name"} + else: + raise ValueError( + f"Type {type(entity)} is not supported for `{self.entity_arg}` attribute" + ) + entities = await self._get_entities(entity_name) if self.domains else None + return self._get_entity_type().instantiate( + name=entity_name, entities=entities, **entity_args + ) + + def _check_domain(self, entity: Entity) -> None: + if not self.domains: + return + if self.contains_templating(entity.name): + return + same_domain = all( + any(elem.startswith(domain + ".") for domain in self.domains) + for elem in entity.entities + ) + if not same_domain: + if entity.is_group: + error_msg = ( + f"All the subentities from {entity} must be from one " + f"of the following domains {self.domains} (e.g. {self.domains[0]}.bedroom)" + ) + else: + error_msg = ( + f"'{entity}' must be from one " + f"of the following domains {self.domains} (e.g. {self.domains[0]}.bedroom)" + ) + raise ValueError(error_msg) + + async def get_entity_state(self, attribute: str | None = None) -> Any: + entity = self.entity.main + if self.update_supported_features: + entities = await self._get_entities(self.entity.name) + self.entity.set_entities(entities) + entity = self.entity.entities[0] + out = await self.get_state(entity, attribute=attribute) + return out diff --git a/appdaemon/apps/controllerx/cx_devices/__init__.py b/appdaemon/apps/controllerx/cx_devices/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/appdaemon/apps/controllerx/cx_devices/adeo.py b/appdaemon/apps/controllerx/cx_devices/adeo.py new file mode 100644 index 0000000..427a71f --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/adeo.py @@ -0,0 +1,46 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController + + +class AdeoHRC99CZC045LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "brightness_step_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_step_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Light.RELEASE, + "color_hue_step_up": Light.HOLD_COLOR_UP, + "color_hue_step_down": Light.HOLD_COLOR_DOWN, + "color_saturation_step_up": Light.HOLD_COLOR_UP, + "color_saturation_step_down": Light.HOLD_COLOR_DOWN, + "color_temperature_step_up": Light.HOLD_COLOR_TEMP_UP, + "color_temperature_step_down": Light.HOLD_COLOR_TEMP_DOWN, + "color_stop": Light.RELEASE, + "scene_1": None, + "scene_2": None, + "scene_3": None, + "scene_4": None, + } + + +class AdeoHRC99CZC045Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "off": Z2MLight.OFF, + "brightness_step_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_step_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Z2MLight.RELEASE, + "color_hue_step_up": Z2MLight.HOLD_COLOR_TEMP_UP, + "color_hue_step_down": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "color_saturation_step_up": Z2MLight.HOLD_COLOR_TEMP_UP, + "color_saturation_step_down": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "color_temperature_step_up": Z2MLight.HOLD_COLOR_TEMP_UP, + "color_temperature_step_down": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "color_stop": Z2MLight.RELEASE, + "scene_1": None, + "scene_2": None, + "scene_3": None, + "scene_4": None, + } diff --git a/appdaemon/apps/controllerx/cx_devices/aqara.py b/appdaemon/apps/controllerx/cx_devices/aqara.py new file mode 100644 index 0000000..b7a91ff --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/aqara.py @@ -0,0 +1,878 @@ +from cx_const import DefaultActionsMapping, Light, MediaPlayer, Switch, Z2MLight +from cx_core import ( + LightController, + MediaPlayerController, + SwitchController, + Z2MLightController, +) +from cx_core.integration import EventData + + +class WXKG02LMLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Light.TOGGLE, + "double_both": Light.CLICK_BRIGHTNESS_UP, + "hold_both": Light.CLICK_BRIGHTNESS_DOWN, + "single_left": Light.TOGGLE, + "double_left": Light.CLICK_BRIGHTNESS_UP, + "hold_left": Light.CLICK_BRIGHTNESS_DOWN, + "single_right": Light.TOGGLE, + "double_right": Light.CLICK_BRIGHTNESS_UP, + "hold_right": Light.CLICK_BRIGHTNESS_DOWN, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, # single left + 1001: Light.CLICK_BRIGHTNESS_DOWN, # long left + 1004: Light.CLICK_BRIGHTNESS_UP, # double left + 2002: Light.TOGGLE, # single right + 2001: Light.CLICK_BRIGHTNESS_DOWN, # long right + 2004: Light.CLICK_BRIGHTNESS_UP, # double right + 3002: Light.TOGGLE, # single both + 3001: Light.CLICK_BRIGHTNESS_DOWN, # long both + 3004: Light.CLICK_BRIGHTNESS_UP, # double both + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "both_single": Light.TOGGLE, + "both_double": Light.CLICK_BRIGHTNESS_UP, + "both_long press": Light.CLICK_BRIGHTNESS_DOWN, + "left_single": Light.TOGGLE, + "left_double": Light.CLICK_BRIGHTNESS_UP, + "left_long press": Light.CLICK_BRIGHTNESS_DOWN, + "right_single": Light.TOGGLE, + "right_double": Light.CLICK_BRIGHTNESS_UP, + "right_long press": Light.CLICK_BRIGHTNESS_DOWN, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class WXKG02LMZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Z2MLight.TOGGLE, + "double_both": Z2MLight.CLICK_BRIGHTNESS_UP, + "hold_both": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "single_left": Z2MLight.TOGGLE, + "double_left": Z2MLight.CLICK_BRIGHTNESS_UP, + "hold_left": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "single_right": Z2MLight.TOGGLE, + "double_right": Z2MLight.CLICK_BRIGHTNESS_UP, + "hold_right": Z2MLight.CLICK_BRIGHTNESS_DOWN, + } + + +class WXKG02LMSwitchController(SwitchController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Switch.TOGGLE, + "single_left": Switch.TOGGLE, + "single_right": Switch.TOGGLE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Switch.TOGGLE, + 2002: Switch.TOGGLE, + 3002: Switch.TOGGLE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "both_single": Switch.TOGGLE, + "left_single": Switch.TOGGLE, + "right_single": Switch.TOGGLE, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class WXKG01LMLightController(LightController): + """ + Different states reported from the controller: + single, double, triple, quadruple, + many, hold, release + """ + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "triple": Light.ON_MIN_BRIGHTNESS, + "quadruple": Light.SET_HALF_BRIGHTNESS, + "many": None, + "hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, # single + 1004: Light.ON_FULL_BRIGHTNESS, # double + 1005: Light.ON_MIN_BRIGHTNESS, # triple + 1006: Light.SET_HALF_BRIGHTNESS, # quadruple + 1010: None, # many + 1001: Light.HOLD_BRIGHTNESS_TOGGLE, # hold the button + 1003: Light.RELEASE, # release the button + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "triple": Light.ON_MIN_BRIGHTNESS, + "quadruple": Light.SET_HALF_BRIGHTNESS, + } + + def get_zha_action(self, data: EventData) -> str: + args = data["args"] + if "click_type" in args: + return args["click_type"] + return data["command"] + + +class WXKG01LMZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Z2MLight.TOGGLE, + "double": Z2MLight.ON_FULL_BRIGHTNESS, + "triple": Z2MLight.ON_MIN_BRIGHTNESS, + "quadruple": Z2MLight.SET_HALF_BRIGHTNESS, + "many": None, + "hold": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "release": Z2MLight.RELEASE, + } + + +class WXKG11LMRemoteLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 1004: Light.ON_FULL_BRIGHTNESS, + 1001: Light.HOLD_BRIGHTNESS_TOGGLE, + 1003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "release": Light.RELEASE, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class WXKG11LMRemoteZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Z2MLight.TOGGLE, + "double": Z2MLight.ON_FULL_BRIGHTNESS, + "hold": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "release": Z2MLight.RELEASE, + } + + +class WXKG11LMSensorSwitchLightController(LightController): + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 1004: Light.ON_FULL_BRIGHTNESS, + 1005: Light.ON_MIN_BRIGHTNESS, + 1006: Light.SET_HALF_BRIGHTNESS, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "triple": Light.ON_MIN_BRIGHTNESS, + "quadruple": Light.SET_HALF_BRIGHTNESS, + } + + def get_zha_action(self, data: EventData) -> str: + mapping = { + 1: "single", + 2: "double", + 3: "triple", + 4: "quadruple", + } + clicks = data["args"]["value"] + return mapping.get(clicks, "") + + +class WXKG12LMLightController(LightController): + """ + Different states reported from the controller: + single, double, shake, hold, release + """ + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "shake": Light.ON_MIN_BRIGHTNESS, + "hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, # button_1_press + 1004: Light.ON_FULL_BRIGHTNESS, # button_1_double_press + 1007: Light.ON_MIN_BRIGHTNESS, # button_1_shake + 1001: Light.HOLD_BRIGHTNESS_TOGGLE, # button_1_hold + 1003: Light.RELEASE, # button_1_release_after_hold + } + + +class WXKG12LMZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Z2MLight.TOGGLE, + "double": Z2MLight.ON_FULL_BRIGHTNESS, + "shake": Z2MLight.ON_MIN_BRIGHTNESS, + "hold": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "release": Z2MLight.RELEASE, + } + + +class MFKZQ01LMLightController(LightController): + """ + This controller allows movement actions for Xiaomi Aqara Smart Cube as + shake, wakeup, fall, slide, flip180 or 90 and rotate_left or right. + Then from the apps.yaml the needed actions can be included and create + different instances for different lights. + """ + + # Different states reported from the controller: + # shake, wakeup, fall, tap, slide, flip180 + # flip90, rotate_left and rotate_right + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "shake": Light.ON_MIN_BRIGHTNESS, + "tap": Light.TOGGLE, + "slide": Light.ON_FULL_BRIGHTNESS, + "flip180": Light.CLICK_COLOR_UP, + "flip90": Light.CLICK_COLOR_DOWN, + "rotate_left": Light.CLICK_BRIGHTNESS_DOWN, + "rotate_right": Light.CLICK_BRIGHTNESS_UP, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1: Light.ON_MIN_BRIGHTNESS, + 6: Light.TOGGLE, + 5: Light.ON_FULL_BRIGHTNESS, + 4: Light.CLICK_COLOR_UP, + 3: Light.CLICK_COLOR_DOWN, + 8: Light.CLICK_BRIGHTNESS_DOWN, + 7: Light.CLICK_BRIGHTNESS_UP, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "shake": Light.ON_MIN_BRIGHTNESS, + "knock": Light.TOGGLE, + "slide": Light.ON_FULL_BRIGHTNESS, + "flip180": Light.CLICK_COLOR_UP, + "flip90": Light.CLICK_COLOR_DOWN, + "rotate_left": Light.CLICK_BRIGHTNESS_DOWN, + "rotate_right": Light.CLICK_BRIGHTNESS_UP, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + action = command + args = data.get("args", {}) + if command == "flip": + action = command + str(args["flip_degrees"]) + return action + + +class WXCJKG11LMLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_single": Light.OFF, + "button_1_double": Light.ON_MIN_BRIGHTNESS, + "button_1_hold": Light.HOLD_BRIGHTNESS_DOWN, + "button_1_release": Light.RELEASE, + "button_2_single": Light.ON, + "button_2_double": Light.ON_FULL_BRIGHTNESS, + "button_2_hold": Light.HOLD_BRIGHTNESS_UP, + "button_2_release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.OFF, + 1001: Light.HOLD_BRIGHTNESS_DOWN, + 1003: Light.RELEASE, + 2002: Light.ON, + 2001: Light.HOLD_BRIGHTNESS_UP, + 2003: Light.RELEASE, + } + + +class WXCJKG11LMZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_single": Z2MLight.OFF, + "button_1_double": Z2MLight.ON_MIN_BRIGHTNESS, + "button_1_hold": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "button_1_release": Z2MLight.RELEASE, + "button_2_single": Z2MLight.ON, + "button_2_double": Z2MLight.ON_FULL_BRIGHTNESS, + "button_2_hold": Z2MLight.HOLD_BRIGHTNESS_UP, + "button_2_release": Z2MLight.RELEASE, + } + + +class WXCJKG12LMLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_single": Light.OFF, + "button_1_double": Light.ON_MIN_COLOR_TEMP, + "button_1_triple": None, + "button_1_hold": Light.HOLD_COLOR_DOWN, + "button_1_release": Light.RELEASE, + "button_2_single": Light.ON, + "button_2_double": Light.ON_FULL_COLOR_TEMP, + "button_2_triple": None, + "button_2_hold": Light.HOLD_COLOR_UP, + "button_2_release": Light.RELEASE, + "button_3_single": Light.CLICK_BRIGHTNESS_DOWN, + "button_3_double": Light.ON_MIN_BRIGHTNESS, + "button_3_triple": None, + "button_3_hold": Light.HOLD_BRIGHTNESS_DOWN, + "button_3_release": Light.RELEASE, + "button_4_single": Light.CLICK_BRIGHTNESS_UP, + "button_4_double": Light.ON_FULL_BRIGHTNESS, + "button_4_triple": None, + "button_4_hold": Light.HOLD_BRIGHTNESS_UP, + "button_4_release": Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_single": Light.OFF, + "1_double": Light.ON_MIN_COLOR_TEMP, + "1_triple": None, + "1_long press": Light.HOLD_COLOR_DOWN, + "1_release": Light.RELEASE, + "2_single": Light.ON, + "2_double": Light.ON_FULL_COLOR_TEMP, + "2_triple": None, + "2_long press": Light.HOLD_COLOR_UP, + "2_release": Light.RELEASE, + "3_single": Light.CLICK_BRIGHTNESS_DOWN, + "3_double": Light.ON_MIN_BRIGHTNESS, + "3_triple": None, + "3_long press": Light.HOLD_BRIGHTNESS_DOWN, + "3_release": Light.RELEASE, + "4_single": Light.CLICK_BRIGHTNESS_UP, + "4_double": Light.ON_FULL_BRIGHTNESS, + "4_triple": None, + "4_long press": Light.HOLD_BRIGHTNESS_UP, + "4_release": Light.RELEASE, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class WXCJKG12LMZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_single": Z2MLight.OFF, + "button_1_double": Z2MLight.ON_MIN_COLOR_TEMP, + "button_1_triple": None, + "button_1_hold": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "button_1_release": Z2MLight.RELEASE, + "button_2_single": Z2MLight.ON, + "button_2_double": Z2MLight.ON_FULL_COLOR_TEMP, + "button_2_triple": None, + "button_2_hold": Z2MLight.HOLD_COLOR_TEMP_UP, + "button_2_release": Z2MLight.RELEASE, + "button_3_single": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "button_3_double": Z2MLight.ON_MIN_BRIGHTNESS, + "button_3_triple": None, + "button_3_hold": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "button_3_release": Z2MLight.RELEASE, + "button_4_single": Z2MLight.CLICK_BRIGHTNESS_UP, + "button_4_double": Z2MLight.ON_FULL_BRIGHTNESS, + "button_4_triple": None, + "button_4_hold": Z2MLight.HOLD_BRIGHTNESS_UP, + "button_4_release": Z2MLight.RELEASE, + } + + +class WXCJKG13LMLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_single": Light.OFF, + "button_1_double": Light.SYNC, + "button_1_triple": None, + "button_1_hold": None, + "button_1_release": None, + "button_2_single": Light.ON, + "button_2_double": Light.SYNC, + "button_2_triple": None, + "button_2_hold": None, + "button_2_release": None, + "button_3_single": Light.CLICK_BRIGHTNESS_DOWN, + "button_3_double": Light.ON_MIN_BRIGHTNESS, + "button_3_triple": None, + "button_3_hold": Light.HOLD_BRIGHTNESS_DOWN, + "button_3_release": Light.RELEASE, + "button_4_single": Light.CLICK_BRIGHTNESS_UP, + "button_4_double": Light.ON_FULL_BRIGHTNESS, + "button_4_triple": None, + "button_4_hold": Light.HOLD_BRIGHTNESS_UP, + "button_4_release": Light.RELEASE, + "button_5_single": Light.CLICK_COLOR_DOWN, + "button_5_double": Light.ON_MIN_COLOR_TEMP, + "button_5_triple": None, + "button_5_hold": Light.HOLD_COLOR_DOWN, + "button_5_release": Light.RELEASE, + "button_6_single": Light.CLICK_COLOR_UP, + "button_6_double": Light.ON_FULL_COLOR_TEMP, + "button_6_triple": None, + "button_6_hold": Light.HOLD_COLOR_UP, + "button_6_release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.OFF, + 1004: Light.SYNC, + 1005: None, + 1001: None, + 1003: None, + 2002: Light.ON, + 2004: Light.SYNC, + 2005: None, + 2001: None, + 2003: None, + 3002: Light.CLICK_BRIGHTNESS_DOWN, + 3004: Light.ON_MIN_BRIGHTNESS, + 3005: None, + 3001: Light.HOLD_BRIGHTNESS_DOWN, + 3003: Light.RELEASE, + 4002: Light.CLICK_BRIGHTNESS_UP, + 4004: Light.ON_FULL_BRIGHTNESS, + 4005: None, + 4001: Light.HOLD_BRIGHTNESS_UP, + 4003: Light.RELEASE, + 5002: Light.CLICK_COLOR_DOWN, + 5004: Light.ON_MIN_COLOR_TEMP, + 5005: None, + 5001: Light.HOLD_COLOR_DOWN, + 5003: Light.RELEASE, + 6002: Light.CLICK_COLOR_UP, + 6004: Light.ON_FULL_COLOR_TEMP, + 6005: None, + 6001: Light.HOLD_COLOR_UP, + 6003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_single": Light.OFF, + "1_double": Light.SYNC, + "1_triple": None, + "1_long": None, + "1_release": None, + "2_single": Light.ON, + "2_double": Light.SYNC, + "2_triple": None, + "2_long": None, + "2_release": None, + "3_single": Light.CLICK_BRIGHTNESS_DOWN, + "3_double": Light.ON_MIN_BRIGHTNESS, + "3_triple": None, + "3_long": Light.HOLD_BRIGHTNESS_DOWN, + "3_release": Light.RELEASE, + "4_single": Light.CLICK_BRIGHTNESS_UP, + "4_double": Light.ON_FULL_BRIGHTNESS, + "4_triple": None, + "4_long": Light.HOLD_BRIGHTNESS_UP, + "4_release": Light.RELEASE, + "5_single": Light.CLICK_COLOR_DOWN, + "5_double": Light.ON_MIN_COLOR_TEMP, + "5_triple": None, + "5_long": Light.HOLD_COLOR_DOWN, + "5_release": Light.RELEASE, + "6_single": Light.CLICK_COLOR_UP, + "6_double": Light.ON_FULL_COLOR_TEMP, + "6_triple": None, + "6_long": Light.HOLD_COLOR_UP, + "6_release": Light.RELEASE, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class WXCJKG13LMZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_single": Z2MLight.OFF, + "button_1_double": Z2MLight.ON_FULL_BRIGHTNESS, + "button_1_triple": None, + "button_1_hold": None, + "button_1_release": None, + "button_2_single": Z2MLight.ON, + "button_2_double": Z2MLight.ON_FULL_BRIGHTNESS, + "button_2_triple": None, + "button_2_hold": None, + "button_2_release": None, + "button_3_single": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "button_3_double": Z2MLight.ON_MIN_BRIGHTNESS, + "button_3_triple": None, + "button_3_hold": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "button_3_release": Z2MLight.RELEASE, + "button_4_single": Z2MLight.CLICK_BRIGHTNESS_UP, + "button_4_double": Z2MLight.ON_FULL_BRIGHTNESS, + "button_4_triple": None, + "button_4_hold": Z2MLight.HOLD_BRIGHTNESS_UP, + "button_4_release": Z2MLight.RELEASE, + "button_5_single": Z2MLight.CLICK_COLOR_TEMP_DOWN, + "button_5_double": Z2MLight.ON_MIN_COLOR_TEMP, + "button_5_triple": None, + "button_5_hold": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "button_5_release": Z2MLight.RELEASE, + "button_6_single": Z2MLight.CLICK_COLOR_TEMP_UP, + "button_6_double": Z2MLight.ON_FULL_COLOR_TEMP, + "button_6_triple": None, + "button_6_hold": Z2MLight.HOLD_COLOR_TEMP_UP, + "button_6_release": Z2MLight.RELEASE, + } + + +class WXCJKG13LMMediaPlayerController(MediaPlayerController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_single": MediaPlayer.CLICK_VOLUME_DOWN, + "button_1_double": MediaPlayer.MUTE, + "button_1_triple": None, + "button_1_hold": MediaPlayer.HOLD_VOLUME_DOWN, + "button_1_release": MediaPlayer.RELEASE, + "button_2_single": MediaPlayer.CLICK_VOLUME_UP, + "button_2_double": MediaPlayer.PLAY_PAUSE, + "button_2_triple": None, + "button_2_hold": MediaPlayer.HOLD_VOLUME_UP, + "button_2_release": MediaPlayer.RELEASE, + "button_3_single": MediaPlayer.PREVIOUS_TRACK, + "button_3_double": None, + "button_3_triple": None, + "button_3_hold": None, + "button_3_release": None, + "button_4_single": MediaPlayer.NEXT_TRACK, + "button_4_double": None, + "button_4_triple": None, + "button_4_hold": None, + "button_4_release": None, + "button_5_single": MediaPlayer.PREVIOUS_SOURCE, + "button_5_double": None, + "button_5_triple": None, + "button_5_hold": None, + "button_5_release": None, + "button_6_single": MediaPlayer.NEXT_SOURCE, + "button_6_double": None, + "button_6_triple": None, + "button_6_hold": None, + "button_6_release": None, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + ####################### + # Button layout + ####################### + # 1000: top-left + # 2000: top-right + # 3000: middle-left + # 4000: middle-right + # 5000: bottom-left + # 6000: bottom-right + ####################### + # Actions + ####################### + # 0001: hold + # 0002: click + # 0003: release + # 0004: double-click + # 0005: triple-click + ####################### + return { + 1002: MediaPlayer.CLICK_VOLUME_DOWN, + 1001: MediaPlayer.HOLD_VOLUME_DOWN, + 1003: MediaPlayer.RELEASE, + 1004: MediaPlayer.MUTE, + 1005: None, + 2002: MediaPlayer.CLICK_VOLUME_UP, + 2001: MediaPlayer.HOLD_VOLUME_UP, + 2003: MediaPlayer.RELEASE, + 2004: MediaPlayer.PLAY_PAUSE, + 2005: None, + 3002: MediaPlayer.PREVIOUS_TRACK, + 3001: None, + 3003: None, + 3004: None, + 3005: None, + 4002: MediaPlayer.NEXT_TRACK, + 4001: None, + 4003: None, + 4004: None, + 4005: None, + 5002: MediaPlayer.PREVIOUS_SOURCE, + 5001: None, + 5003: None, + 5004: None, + 5005: None, + 6002: MediaPlayer.NEXT_SOURCE, + 6001: None, + 6003: None, + 6004: None, + 6005: None, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_single": MediaPlayer.CLICK_VOLUME_DOWN, + "1_double": MediaPlayer.MUTE, + "1_triple": None, + "1_long": MediaPlayer.HOLD_VOLUME_DOWN, + "1_release": MediaPlayer.RELEASE, + "2_single": MediaPlayer.CLICK_VOLUME_UP, + "2_double": MediaPlayer.PLAY_PAUSE, + "2_triple": None, + "2_long": MediaPlayer.HOLD_VOLUME_UP, + "2_release": MediaPlayer.RELEASE, + "3_single": MediaPlayer.PREVIOUS_TRACK, + "3_double": None, + "3_triple": None, + "3_long": None, + "3_release": None, + "4_single": MediaPlayer.NEXT_TRACK, + "4_double": None, + "4_triple": None, + "4_long": None, + "4_release": None, + "5_single": MediaPlayer.PREVIOUS_SOURCE, + "5_double": None, + "5_triple": None, + "5_long": None, + "5_release": Light.RELEASE, + "6_single": MediaPlayer.NEXT_SOURCE, + "6_double": None, + "6_triple": None, + "6_long": None, + "6_release": None, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class WXKG06LMLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.CLICK_BRIGHTNESS_UP, + "hold": Light.CLICK_BRIGHTNESS_UP, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 1001: Light.CLICK_BRIGHTNESS_DOWN, + 1004: Light.CLICK_BRIGHTNESS_UP, + } + + +class WXKG06LMSwitchController(SwitchController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return {"single": Light.TOGGLE} + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return {1002: Light.TOGGLE} + + +class WXKG07LMLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Light.TOGGLE, + "double_both": Light.CLICK_BRIGHTNESS_UP, + "hold_both": Light.CLICK_BRIGHTNESS_DOWN, + "single_left": Light.TOGGLE, + "double_left": Light.CLICK_BRIGHTNESS_UP, + "hold_left": Light.CLICK_BRIGHTNESS_DOWN, + "single_right": Light.TOGGLE, + "double_right": Light.CLICK_BRIGHTNESS_UP, + "hold_right": Light.CLICK_BRIGHTNESS_DOWN, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1001: Light.CLICK_BRIGHTNESS_DOWN, # long left + 1002: Light.TOGGLE, # single left + 1004: Light.CLICK_BRIGHTNESS_UP, # double left + 2001: Light.CLICK_BRIGHTNESS_DOWN, # long right + 2002: Light.TOGGLE, # single right + 2004: Light.CLICK_BRIGHTNESS_UP, # double right + 3001: Light.CLICK_BRIGHTNESS_DOWN, # long both + 3002: Light.TOGGLE, # single both + 3004: Light.CLICK_BRIGHTNESS_UP, # double both + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "both_single": Light.TOGGLE, + "both_double": Light.CLICK_BRIGHTNESS_UP, + "both_long press": Light.CLICK_BRIGHTNESS_DOWN, + "left_single": Light.TOGGLE, + "left_double": Light.CLICK_BRIGHTNESS_UP, + "left_long press": Light.CLICK_BRIGHTNESS_DOWN, + "right_single": Light.TOGGLE, + "right_double": Light.CLICK_BRIGHTNESS_UP, + "right_long press": Light.CLICK_BRIGHTNESS_DOWN, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class WXKG07LMZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Z2MLight.TOGGLE, + "double_both": Z2MLight.CLICK_BRIGHTNESS_UP, + "hold_both": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "single_left": Z2MLight.TOGGLE, + "double_left": Z2MLight.CLICK_BRIGHTNESS_UP, + "hold_left": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "single_right": Z2MLight.TOGGLE, + "double_right": Z2MLight.CLICK_BRIGHTNESS_UP, + "hold_right": Z2MLight.CLICK_BRIGHTNESS_DOWN, + } + + +class WXKG07LMSwitchController(SwitchController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Switch.TOGGLE, + "single_left": Switch.TOGGLE, + "single_right": Switch.TOGGLE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Switch.TOGGLE, + 2002: Switch.TOGGLE, + 3002: Switch.TOGGLE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "both_single": Switch.TOGGLE, + "left_single": Switch.TOGGLE, + "right_single": Switch.TOGGLE, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class ZNXNKG02LMLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "hold": Light.HOLD_COLOR_TOGGLE, + "release": Light.RELEASE, + "start_rotating": Light.BRIGHTNESS_FROM_CONTROLLER_ANGLE, + "stop_rotating": Light.RELEASE, + } + + +class ZNXNKG02LMMediaPlayerController(MediaPlayerController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": MediaPlayer.PLAY_PAUSE, + "double": MediaPlayer.NEXT_TRACK, + "hold": MediaPlayer.PREVIOUS_TRACK, + "release": MediaPlayer.RELEASE, + "start_rotating": MediaPlayer.VOLUME_FROM_CONTROLLER_ANGLE, + "stop_rotating": MediaPlayer.RELEASE, + } + + +class WXKG15LMLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Light.TOGGLE, + "double_both": Light.CLICK_BRIGHTNESS_UP, + "triple_both": Light.CLICK_BRIGHTNESS_DOWN, + "hold_both": Light.ON_FULL_BRIGHTNESS, + "single_left": Light.TOGGLE, + "double_left": Light.CLICK_BRIGHTNESS_UP, + "triple_left": Light.CLICK_BRIGHTNESS_DOWN, + "hold_left": Light.ON_FULL_BRIGHTNESS, + "single_right": Light.TOGGLE, + "double_right": Light.CLICK_BRIGHTNESS_UP, + "triple_right": Light.CLICK_BRIGHTNESS_DOWN, + "hold_right": Light.ON_FULL_BRIGHTNESS, + } + + +class WXKG15LMZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Z2MLight.TOGGLE, + "double_both": Z2MLight.CLICK_BRIGHTNESS_UP, + "triple_both": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "hold_both": Z2MLight.ON_FULL_BRIGHTNESS, + "single_left": Z2MLight.TOGGLE, + "double_left": Z2MLight.CLICK_BRIGHTNESS_UP, + "triple_left": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "hold_left": Z2MLight.ON_FULL_BRIGHTNESS, + "single_right": Z2MLight.TOGGLE, + "double_right": Z2MLight.CLICK_BRIGHTNESS_UP, + "triple_right": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "hold_right": Z2MLight.ON_FULL_BRIGHTNESS, + } + + +class WXKG15LMSwitchController(SwitchController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_both": Switch.TOGGLE, + "single_left": Switch.TOGGLE, + "single_right": Switch.TOGGLE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/aurora.py b/appdaemon/apps/controllerx/cx_devices/aurora.py new file mode 100644 index 0000000..d85523c --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/aurora.py @@ -0,0 +1,37 @@ +from cx_const import DefaultActionsMapping, Light +from cx_core import LightController +from cx_core.integration import EventData + + +class AUA1ZBR2GWLightController(LightController): + # Different states reported from the controller: + # on, off, brightness_step_up, brightness_step_down, + # color_temperature_step_up, color_temperature_step_down + # There is one copy of these actions per endpoint + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_toggle": Light.TOGGLE, + "1_step_up": Light.CLICK_BRIGHTNESS_UP, + "1_step_down": Light.CLICK_BRIGHTNESS_DOWN, + "1_step_color_temp_up": Light.CLICK_COLOR_TEMP_UP, + "1_step_color_temp_down": Light.CLICK_COLOR_TEMP_DOWN, + "2_toggle": Light.TOGGLE, + "2_step_up": Light.CLICK_BRIGHTNESS_UP, + "2_step_down": Light.CLICK_BRIGHTNESS_DOWN, + "2_step_color_temp_up": Light.CLICK_COLOR_TEMP_UP, + "2_step_color_temp_down": Light.CLICK_COLOR_TEMP_DOWN, + } + + def get_zha_action(self, data: EventData) -> str: + endpoint_id = data.get("endpoint_id", 1) + command: str = data["command"] + action = command + args = data.get("args", []) + if command == "step" or command == "step_color_temp": + args_mapping = {0: "up", 1: "down", 3: "up"} + action = "_".join((action, args_mapping[args[0]])) + if command == "on" or command == "off": + action = "toggle" + action = f"{endpoint_id}_{action}" + return action diff --git a/appdaemon/apps/controllerx/cx_devices/homematic.py b/appdaemon/apps/controllerx/cx_devices/homematic.py new file mode 100644 index 0000000..61344d3 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/homematic.py @@ -0,0 +1,78 @@ +from cx_const import DefaultActionsMapping, Light +from cx_core import LightController + + +class HMPB2WM552LightController(LightController): + def get_homematic_actions_mapping(self) -> DefaultActionsMapping: + return { + "PRESS_SHORT_1": Light.OFF, + "PRESS_LONG_1": Light.HOLD_BRIGHTNESS_DOWN, + "PRESS_CONT_1": None, + "PRESS_LONG_RELEASE_1": Light.RELEASE, + "PRESS_SHORT_2": Light.ON, + "PRESS_LONG_2": Light.HOLD_BRIGHTNESS_UP, + "PRESS_CONT_2": None, + "PRESS_LONG_RELEASE_2": Light.RELEASE, + } + + +class HMPBI4FMLightController(LightController): + def get_homematic_actions_mapping(self) -> DefaultActionsMapping: + return { + "PRESS_SHORT_1": Light.OFF, + "PRESS_CONT_1": None, + "PRESS_LONG_1": Light.CLICK_COLOR_DOWN, + "PRESS_SHORT_2": Light.ON, + "PRESS_CONT_2": None, + "PRESS_LONG_2": Light.CLICK_COLOR_UP, + "PRESS_SHORT_3": Light.CLICK_BRIGHTNESS_DOWN, + "PRESS_CONT_3": None, + "PRESS_LONG_3": Light.ON_MIN_BRIGHTNESS, + "PRESS_SHORT_4": Light.CLICK_BRIGHTNESS_UP, + "PRESS_CONT_4": None, + "PRESS_LONG_4": Light.ON_FULL_BRIGHTNESS, + } + + +class HMPB6WM55LightController(LightController): + def get_homematic_actions_mapping(self) -> DefaultActionsMapping: + return { + "PRESS_SHORT_1": Light.OFF, + "PRESS_CONT_1": None, + "PRESS_LONG_1": Light.SYNC, + "PRESS_LONG_RELEASE_1": Light.RELEASE, + "PRESS_SHORT_2": Light.ON, + "PRESS_CONT_2": None, + "PRESS_LONG_2": Light.SYNC, + "PRESS_LONG_RELEASE_2": Light.RELEASE, + "PRESS_SHORT_3": Light.CLICK_BRIGHTNESS_DOWN, + "PRESS_CONT_3": None, + "PRESS_LONG_3": Light.HOLD_BRIGHTNESS_DOWN, + "PRESS_LONG_RELEASE_3": Light.RELEASE, + "PRESS_SHORT_4": Light.CLICK_BRIGHTNESS_UP, + "PRESS_CONT_4": None, + "PRESS_LONG_4": Light.HOLD_BRIGHTNESS_UP, + "PRESS_LONG_RELEASE_4": Light.RELEASE, + "PRESS_SHORT_5": Light.CLICK_COLOR_DOWN, + "PRESS_CONT_5": None, + "PRESS_LONG_5": Light.HOLD_COLOR_DOWN, + "PRESS_LONG_RELEASE_5": Light.RELEASE, + "PRESS_SHORT_6": Light.CLICK_COLOR_UP, + "PRESS_CONT_6": None, + "PRESS_LONG_6": Light.HOLD_COLOR_UP, + "PRESS_LONG_RELEASE_6": Light.RELEASE, + } + + +class HMSenMDIRWM55LightController(LightController): + def get_homematic_actions_mapping(self) -> DefaultActionsMapping: + return { + "PRESS_SHORT_1": Light.OFF, + "PRESS_LONG_1": Light.HOLD_BRIGHTNESS_DOWN, + "PRESS_CONT_1": None, + "PRESS_LONG_RELEASE_1": Light.RELEASE, + "PRESS_SHORT_2": Light.ON, + "PRESS_LONG_2": Light.HOLD_BRIGHTNESS_UP, + "PRESS_CONT_2": None, + "PRESS_LONG_RELEASE_2": Light.RELEASE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/ikea.py b/appdaemon/apps/controllerx/cx_devices/ikea.py new file mode 100644 index 0000000..efe22e4 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/ikea.py @@ -0,0 +1,844 @@ +from cx_const import ( + Cover, + DefaultActionsMapping, + Light, + MediaPlayer, + PredefinedActionsMapping, + Switch, + Z2MLight, +) +from cx_core import ( + CoverController, + LightController, + MediaPlayerController, + SwitchController, + Z2MLightController, + action, +) +from cx_core.integration import EventData + + +class E1810Controller(LightController): + # Different states reported from the controller: + # toggle, brightness_up_click, brightness_down_click + # arrow_left_click, arrow_right_click, brightness_up_hold + # brightness_up_release, brightness_down_hold, brightness_down_release, + # arrow_left_hold, arrow_left_release, arrow_right_hold + # arrow_right_release + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": Light.TOGGLE, + "toggle_hold": Light.SYNC, + "brightness_up_click": Light.CLICK_BRIGHTNESS_UP, + "brightness_down_click": Light.CLICK_BRIGHTNESS_DOWN, + "arrow_left_click": Light.CLICK_COLOR_DOWN, + "arrow_right_click": Light.CLICK_COLOR_UP, + "brightness_up_hold": Light.HOLD_BRIGHTNESS_UP, + "brightness_up_release": Light.RELEASE, + "brightness_down_hold": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_down_release": Light.RELEASE, + "arrow_left_hold": Light.HOLD_COLOR_DOWN, + "arrow_left_release": Light.RELEASE, + "arrow_right_hold": Light.HOLD_COLOR_UP, + "arrow_right_release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 1001: Light.SYNC, + 2002: Light.CLICK_BRIGHTNESS_UP, + 3002: Light.CLICK_BRIGHTNESS_DOWN, + 4002: Light.CLICK_COLOR_DOWN, + 5002: Light.CLICK_COLOR_UP, + 2001: Light.HOLD_BRIGHTNESS_UP, + 2003: Light.RELEASE, + 3001: Light.HOLD_BRIGHTNESS_DOWN, + 3003: Light.RELEASE, + 4001: Light.HOLD_COLOR_DOWN, + 4003: Light.RELEASE, + 5001: Light.HOLD_COLOR_UP, + 5003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": Light.TOGGLE, + "press_2_0_0": Light.SYNC, + "step_with_on_off_0_43_5": Light.CLICK_BRIGHTNESS_UP, + "step_1_43_5_0_0": Light.CLICK_BRIGHTNESS_DOWN, + "press_257_13_0": Light.CLICK_COLOR_DOWN, + "press_256_13_0": Light.CLICK_COLOR_UP, + "move_with_on_off_0_83": Light.HOLD_BRIGHTNESS_UP, + "move_with_on_off_0_84": Light.HOLD_BRIGHTNESS_UP, # ZigBee 3.0 firmware + "move_1_83_0_0": Light.HOLD_BRIGHTNESS_DOWN, + "move_1_84_0_0": Light.HOLD_BRIGHTNESS_DOWN, # ZigBee 3.0 firmware + "hold_3329_0": Light.HOLD_COLOR_DOWN, + "hold_3328_0": Light.HOLD_COLOR_UP, + "stop": Light.RELEASE, + "stop_with_on_off": Light.RELEASE, + "release": Light.RELEASE, + } + + +class E1810Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": Z2MLight.TOGGLE, + "toggle_hold": Z2MLight.ON_FULL_BRIGHTNESS, + "brightness_up_click": Z2MLight.CLICK_BRIGHTNESS_UP, + "brightness_down_click": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "arrow_left_click": Z2MLight.CLICK_COLOR_TEMP_DOWN, + "arrow_right_click": Z2MLight.CLICK_COLOR_TEMP_UP, + "brightness_up_hold": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_up_release": Z2MLight.RELEASE, + "brightness_down_hold": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_down_release": Z2MLight.RELEASE, + "arrow_left_hold": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "arrow_left_release": Z2MLight.RELEASE, + "arrow_right_hold": Z2MLight.HOLD_COLOR_TEMP_UP, + "arrow_right_release": Z2MLight.RELEASE, + } + + +class E1810MediaPlayerController(MediaPlayerController): + # Different states reported from the controller: + # toggle, brightness_up_click, brightness_down_click + # arrow_left_click, arrow_right_click, brightness_up_hold + # brightness_up_release, brightness_down_hold, brightness_down_release, + # arrow_left_hold, arrow_left_release, arrow_right_hold + # arrow_right_release + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": MediaPlayer.PLAY_PAUSE, + "brightness_up_click": MediaPlayer.CLICK_VOLUME_UP, + "brightness_down_click": MediaPlayer.CLICK_VOLUME_DOWN, + "arrow_left_click": MediaPlayer.PREVIOUS_TRACK, + "arrow_right_click": MediaPlayer.NEXT_TRACK, + "arrow_left_hold": MediaPlayer.PREVIOUS_SOURCE, + "arrow_right_hold": MediaPlayer.NEXT_SOURCE, + "brightness_up_hold": MediaPlayer.HOLD_VOLUME_UP, + "brightness_up_release": MediaPlayer.RELEASE, + "brightness_down_hold": MediaPlayer.HOLD_VOLUME_DOWN, + "brightness_down_release": MediaPlayer.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: MediaPlayer.PLAY_PAUSE, + 2002: MediaPlayer.CLICK_VOLUME_UP, + 3002: MediaPlayer.CLICK_VOLUME_DOWN, + 4002: MediaPlayer.PREVIOUS_TRACK, + 5002: MediaPlayer.NEXT_TRACK, + 2001: MediaPlayer.HOLD_VOLUME_UP, + 2003: MediaPlayer.RELEASE, + 3001: MediaPlayer.HOLD_VOLUME_DOWN, + 3003: MediaPlayer.RELEASE, + 4001: MediaPlayer.PREVIOUS_SOURCE, + 5001: MediaPlayer.NEXT_SOURCE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": MediaPlayer.PLAY_PAUSE, + "step_with_on_off_0_43_5": MediaPlayer.CLICK_VOLUME_UP, + "step_1_43_5_0_0": MediaPlayer.CLICK_VOLUME_DOWN, + "press_257_13_0": MediaPlayer.PREVIOUS_TRACK, + "press_256_13_0": MediaPlayer.NEXT_TRACK, + "move_with_on_off_0_83": MediaPlayer.HOLD_VOLUME_UP, + "move_with_on_off_0_84": MediaPlayer.HOLD_VOLUME_UP, # ZigBee 3.0 firmware + "stop": MediaPlayer.RELEASE, + "stop_with_on_off": MediaPlayer.RELEASE, + "move_1_83_0_0": MediaPlayer.HOLD_VOLUME_DOWN, + "move_1_84_0_0": MediaPlayer.HOLD_VOLUME_DOWN, # ZigBee 3.0 firmware + "hold_3329_0": MediaPlayer.PREVIOUS_SOURCE, + "hold_3328_0": MediaPlayer.NEXT_SOURCE, + "release": MediaPlayer.RELEASE, + } + + +class E1743Controller(LightController): + # Different states reported from the controller: + # on, off, brightness_up, brightness_down, brightness_stop + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.ON, + 2002: Light.OFF, + 1001: Light.HOLD_BRIGHTNESS_UP, + 2001: Light.HOLD_BRIGHTNESS_DOWN, + 1003: Light.RELEASE, + 2003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "move_with_on_off_0_83": Light.HOLD_BRIGHTNESS_UP, + "move_1_83_0_0": Light.HOLD_BRIGHTNESS_DOWN, + "stop": Light.RELEASE, + "stop_with_on_off": Light.RELEASE, + } + + +class E1743Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "off": Z2MLight.OFF, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Z2MLight.RELEASE, + } + + +class E1743MediaPlayerController(MediaPlayerController): + # Different states reported from the controller: + # on, off, brightness_up, brightness_down, brightness_stop + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": MediaPlayer.PLAY_PAUSE, + "off": MediaPlayer.NEXT_TRACK, + "brightness_move_up": MediaPlayer.HOLD_VOLUME_UP, + "brightness_move_down": MediaPlayer.HOLD_VOLUME_DOWN, + "brightness_stop": MediaPlayer.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: MediaPlayer.PLAY_PAUSE, + 2002: MediaPlayer.NEXT_TRACK, + 1001: MediaPlayer.HOLD_VOLUME_UP, + 2001: MediaPlayer.HOLD_VOLUME_DOWN, + 1003: MediaPlayer.RELEASE, + 2003: MediaPlayer.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": MediaPlayer.PLAY_PAUSE, + "off": MediaPlayer.NEXT_TRACK, + "move_with_on_off_0_83": MediaPlayer.HOLD_VOLUME_UP, + "move_1_83_0_0": MediaPlayer.HOLD_VOLUME_DOWN, + "stop": MediaPlayer.RELEASE, + "stop_with_on_off": MediaPlayer.RELEASE, + } + + +class E1743SwitchController(SwitchController): + # Different states reported from the controller: + # on, off + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return {"on": Switch.ON, "off": Switch.OFF} + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return {1002: Switch.ON, 2002: Switch.OFF} + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return {"on": Switch.ON, "off": Switch.OFF} + + +class E1743CoverController(CoverController): + # Different states reported from the controller: + # on, off + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Cover.TOGGLE_OPEN, + "off": Cover.TOGGLE_CLOSE, + "brightness_move_up": Cover.OPEN, + "brightness_move_down": Cover.CLOSE, + "brightness_stop": Cover.STOP, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Cover.TOGGLE_OPEN, + 2002: Cover.TOGGLE_CLOSE, + 1001: Cover.OPEN, + 2001: Cover.CLOSE, + 1003: Cover.STOP, + 2003: Cover.STOP, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Cover.TOGGLE_OPEN, + "off": Cover.TOGGLE_CLOSE, + "move_with_on_off_0_83": Cover.OPEN, + "move_1_83_0_0": Cover.CLOSE, + "stop": Cover.STOP, + "stop_with_on_off": Cover.STOP, + } + + +class E2201LightController(E1743Controller): + pass + + +class E2201Z2MLightController(E1743Z2MLightController): + pass + + +class E2201MediaPlayerController(E1743MediaPlayerController): + pass + + +class E2201SwitchController(E1743SwitchController): + pass + + +class E2201CoverController(E1743CoverController): + pass + + +class ICTCG1Controller(LightController): + # Different states reported from the controller: + # rotate_left, rotate_left_quick + # rotate_right, rotate_right_quick + # rotate_stop + + @action + async def rotate_left_quick(self) -> None: + await self.release() + await self.off() + + @action + async def rotate_right_quick(self) -> None: + await self.release() + await self.on_full(LightController.ATTRIBUTE_BRIGHTNESS) + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + parent_mapping = super().get_predefined_actions_mapping() + mapping: PredefinedActionsMapping = { + "rotate_left_quick": self.rotate_left_quick, + "rotate_right_quick": self.rotate_right_quick, + } + mapping.update(parent_mapping) + return mapping + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Light.RELEASE, + "brightness_move_to_level": Light.BRIGHTNESS_FROM_CONTROLLER_LEVEL, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: "rotate_right_quick", + 2002: Light.CLICK_BRIGHTNESS_UP, + 3002: Light.CLICK_BRIGHTNESS_DOWN, + 4002: "rotate_left_quick", + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "move_1_70": Light.HOLD_BRIGHTNESS_DOWN, + "move_1_195": Light.HOLD_BRIGHTNESS_DOWN, + "move_to_level_with_on_off_0_1": "rotate_left_quick", + "move_with_on_off_0_70": Light.HOLD_BRIGHTNESS_UP, + "move_with_on_off_0_195": Light.ON, + "move_to_level_with_on_off_255_1": "rotate_right_quick", + "stop": Light.RELEASE, + } + + +class ICTCG1Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Z2MLight.RELEASE, + "brightness_move_to_level": Z2MLight.BRIGHTNESS_FROM_CONTROLLER_LEVEL, + } + + +class ICTCG1MediaPlayerController(MediaPlayerController): + # Different states reported from the controller: + # rotate_left, rotate_left_quick + # rotate_right, rotate_right_quick + # rotate_stop + + @action + async def rotate_left_quick(self) -> None: + await self.release() + await self.pause() + + @action + async def rotate_right_quick(self) -> None: + await self.release() + await self.play() + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + parent_mapping = super().get_predefined_actions_mapping() + mapping: PredefinedActionsMapping = { + "rotate_left_quick": self.rotate_left_quick, + "rotate_right_quick": self.rotate_right_quick, + } + mapping.update(parent_mapping) + return mapping + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "brightness_move_up": MediaPlayer.HOLD_VOLUME_UP, + "brightness_move_down": MediaPlayer.HOLD_VOLUME_DOWN, + "brightness_stop": MediaPlayer.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: "rotate_right_quick", + 2002: MediaPlayer.CLICK_VOLUME_UP, + 3002: MediaPlayer.CLICK_VOLUME_DOWN, + 4002: "rotate_left_quick", + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "move_1_70": MediaPlayer.HOLD_VOLUME_DOWN, + "move_1_195": MediaPlayer.HOLD_VOLUME_DOWN, + "move_to_level_with_on_off_0_1": "rotate_left_quick", + "move_with_on_off_0_70": MediaPlayer.HOLD_VOLUME_UP, + "move_with_on_off_0_195": MediaPlayer.HOLD_VOLUME_UP, + "move_to_level_with_on_off_255_1": "rotate_right_quick", + "stop": MediaPlayer.RELEASE, + } + + +class E1744LightController(LightController): + # Different states reported from the controller: + # brightness_move_down, brightness_move_up, brightness_stop, + # toggle, brightness_step_up, brightness_step_down + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_stop": Light.RELEASE, + "toggle": Light.TOGGLE, + "brightness_step_up": Light.ON_FULL_BRIGHTNESS, + "brightness_step_down": Light.ON_MIN_BRIGHTNESS, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 3001: Light.HOLD_BRIGHTNESS_DOWN, + 2001: Light.HOLD_BRIGHTNESS_UP, + 2003: Light.RELEASE, + 3003: Light.RELEASE, + 1002: Light.TOGGLE, + 1004: Light.ON_FULL_BRIGHTNESS, + 1005: Light.ON_MIN_BRIGHTNESS, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "move_1_195_0_0": Light.HOLD_BRIGHTNESS_DOWN, + "move_0_195_0_0": Light.HOLD_BRIGHTNESS_UP, + "stop": Light.RELEASE, + "toggle": Light.TOGGLE, + "step_0_1_0_0_0": Light.ON_FULL_BRIGHTNESS, + "step_1_1_0_0_0": Light.ON_MIN_BRIGHTNESS, + } + + def default_delay(self) -> int: + return 500 + + +class E1744Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_stop": Z2MLight.RELEASE, + "toggle": Z2MLight.TOGGLE, + "brightness_step_up": Z2MLight.ON_FULL_BRIGHTNESS, + "brightness_step_down": Z2MLight.ON_MIN_BRIGHTNESS, + } + + +class E1744MediaPlayerController(MediaPlayerController): + # Different states reported from the controller: + # brightness_move_down, brightness_move_up, brightness_stop, + # toggle, brightness_step_up, brightness_step_down + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "brightness_move_down": MediaPlayer.HOLD_VOLUME_DOWN, + "brightness_move_up": MediaPlayer.HOLD_VOLUME_UP, + "brightness_stop": MediaPlayer.RELEASE, + "toggle": MediaPlayer.PLAY_PAUSE, + "brightness_step_up": MediaPlayer.NEXT_TRACK, + "brightness_step_down": MediaPlayer.PREVIOUS_TRACK, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 2001: MediaPlayer.HOLD_VOLUME_UP, + 3001: MediaPlayer.HOLD_VOLUME_DOWN, + 2003: MediaPlayer.RELEASE, + 3003: MediaPlayer.RELEASE, + 1002: MediaPlayer.PLAY_PAUSE, + 1004: MediaPlayer.NEXT_TRACK, + 1005: MediaPlayer.PREVIOUS_TRACK, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "move_1_195_0_0": MediaPlayer.HOLD_VOLUME_DOWN, + "move_0_195_0_0": MediaPlayer.HOLD_VOLUME_UP, + "stop": MediaPlayer.RELEASE, + "toggle": MediaPlayer.PLAY_PAUSE, + "step_0_1_0_0_0": MediaPlayer.NEXT_TRACK, + "step_1_1_0_0_0": MediaPlayer.PREVIOUS_TRACK, + } + + def default_delay(self) -> int: + return 500 + + +class E1766LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return {"open": Light.ON, "close": Light.OFF} + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.ON, + 1003: Light.ON_FULL_BRIGHTNESS, + 2002: Light.OFF, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "up_open": Light.ON, + "down_close": Light.OFF, + } + + +class E1766Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return {"open": Z2MLight.ON, "close": Z2MLight.OFF} + + +class E1766SwitchController(SwitchController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return {"open": Switch.ON, "close": Switch.OFF} + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return {1002: Switch.ON, 2002: Switch.OFF} + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return {"up_open": Switch.ON, "down_close": Switch.OFF} + + +class E1766CoverController(CoverController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "open": Cover.TOGGLE_OPEN, + "close": Cover.TOGGLE_CLOSE, + "stop": Cover.STOP, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Cover.TOGGLE_OPEN, + 1003: Cover.STOP, + 2002: Cover.TOGGLE_CLOSE, + 2003: Cover.STOP, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "up_open": Cover.TOGGLE_OPEN, + "down_close": Cover.TOGGLE_CLOSE, + "stop": Cover.STOP, + } + + +class E1812LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.TOGGLE, + "brightness_move_up": Light.HOLD_BRIGHTNESS_TOGGLE, + "brightness_stop": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 1001: Light.HOLD_BRIGHTNESS_TOGGLE, + 1003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.TOGGLE, + "move_with_on_off": Light.HOLD_BRIGHTNESS_TOGGLE, + "stop": Light.RELEASE, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class E1812Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.TOGGLE, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "brightness_stop": Z2MLight.RELEASE, + } + + +class E1812SwitchController(SwitchController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return {"on": Switch.TOGGLE} + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return {1002: Switch.TOGGLE} + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return {"on": Light.TOGGLE} + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class E2002LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "arrow_left_click": Light.CLICK_COLOR_DOWN, + "arrow_right_click": Light.CLICK_COLOR_UP, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_stop": Light.RELEASE, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "arrow_left_hold": Light.HOLD_COLOR_DOWN, + "arrow_left_release": Light.RELEASE, + "arrow_right_hold": Light.HOLD_COLOR_UP, + "arrow_right_release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.ON, + 2002: Light.OFF, + 3002: Light.CLICK_COLOR_DOWN, + 4002: Light.CLICK_COLOR_UP, + 1001: Light.HOLD_BRIGHTNESS_UP, + 1003: Light.RELEASE, + 2001: Light.HOLD_BRIGHTNESS_DOWN, + 2003: Light.RELEASE, + 3001: Light.HOLD_COLOR_DOWN, + 3003: Light.RELEASE, + 4001: Light.HOLD_COLOR_UP, + 4003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "press_257_13_0": Light.CLICK_COLOR_DOWN, + "press_256_13_0": Light.CLICK_COLOR_UP, + "move_with_on_off_0_83": Light.HOLD_BRIGHTNESS_UP, + "move_1_83_0_0": Light.HOLD_BRIGHTNESS_DOWN, + "hold_3329_0": Light.HOLD_COLOR_DOWN, + "hold_3328_0": Light.HOLD_COLOR_UP, + "stop": Light.RELEASE, + "stop_with_on_off": Light.RELEASE, + "release": Light.RELEASE, + } + + +class W2049LightController(E2002LightController): + async def initialize(self) -> None: + await super().initialize() + self.log( + "⚠️ `W2049LightController` is deprecated and will be removed. Use `E2002LightController` instead.", + level="WARNING", + ascii_encode=False, + ) + + +class E2002Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "off": Z2MLight.OFF, + "arrow_left_click": Z2MLight.CLICK_COLOR_TEMP_DOWN, + "arrow_right_click": Z2MLight.CLICK_COLOR_TEMP_UP, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_stop": Z2MLight.RELEASE, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "arrow_left_hold": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "arrow_left_release": Z2MLight.RELEASE, + "arrow_right_hold": Z2MLight.HOLD_COLOR_TEMP_UP, + "arrow_right_release": Z2MLight.RELEASE, + } + + +class E2002MediaPlayerController(MediaPlayerController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": MediaPlayer.PLAY_PAUSE, + "off": MediaPlayer.CLICK_VOLUME_DOWN, + "arrow_left_click": MediaPlayer.PREVIOUS_TRACK, + "arrow_right_click": MediaPlayer.NEXT_TRACK, + "arrow_left_hold": MediaPlayer.PREVIOUS_SOURCE, + "arrow_left_release": MediaPlayer.RELEASE, + "arrow_right_hold": MediaPlayer.NEXT_SOURCE, + "arrow_right_release": MediaPlayer.RELEASE, + "brightness_move_up": MediaPlayer.HOLD_VOLUME_UP, + "brightness_move_down": MediaPlayer.HOLD_VOLUME_DOWN, + "brightness_stop": MediaPlayer.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: MediaPlayer.PLAY_PAUSE, + 2002: MediaPlayer.CLICK_VOLUME_DOWN, + 3002: MediaPlayer.PREVIOUS_TRACK, + 4002: MediaPlayer.NEXT_TRACK, + 1001: MediaPlayer.HOLD_VOLUME_UP, + 1003: MediaPlayer.RELEASE, + 2001: MediaPlayer.HOLD_VOLUME_DOWN, + 2003: MediaPlayer.RELEASE, + 3001: MediaPlayer.PREVIOUS_SOURCE, + 3003: MediaPlayer.RELEASE, + 4001: MediaPlayer.NEXT_SOURCE, + 4003: MediaPlayer.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": MediaPlayer.PLAY_PAUSE, + "off": MediaPlayer.CLICK_VOLUME_DOWN, + "press_257_13_0": MediaPlayer.PREVIOUS_TRACK, + "press_256_13_0": MediaPlayer.NEXT_TRACK, + "move_with_on_off_0_83": MediaPlayer.HOLD_VOLUME_UP, + "move_1_83_0_0": MediaPlayer.HOLD_VOLUME_DOWN, + "hold_3329_0": MediaPlayer.PREVIOUS_SOURCE, + "hold_3328_0": MediaPlayer.NEXT_SOURCE, + "stop": MediaPlayer.RELEASE, + "stop_with_on_off": MediaPlayer.RELEASE, + "release": MediaPlayer.RELEASE, + } + + +class W2049MediaPlayerController(E2002MediaPlayerController): + async def initialize(self) -> None: + await super().initialize() + self.log( + "⚠️ `W2049MediaPlayerController` is deprecated and will be removed. " + "Use `E2002MediaPlayerController` instead.", + level="WARNING", + ascii_encode=False, + ) + + +class E2213LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_long_press": Light.HOLD_BRIGHTNESS_UP, + "2_long_press": Light.HOLD_BRIGHTNESS_DOWN, + "1_short_release": Light.ON, + "2_short_release": Light.OFF, + "1_long_release": Light.RELEASE, + "2_long_release": Light.RELEASE, + "1_double_press": Light.HOLD_COLOR_UP, + "2_double_press": Light.HOLD_COLOR_DOWN, + } + + +class E2213Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_long_press": Z2MLight.HOLD_BRIGHTNESS_UP, + "2_long_press": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "1_short_release": Z2MLight.ON, + "2_short_release": Z2MLight.OFF, + "1_long_release": Z2MLight.RELEASE, + "2_long_release": Z2MLight.RELEASE, + "1_double_press": Z2MLight.HOLD_COLOR_TEMP_UP, + "2_double_press": Z2MLight.HOLD_COLOR_TEMP_DOWN, + } + + +class E2123MediaPlayerController(MediaPlayerController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": MediaPlayer.PLAY_PAUSE, # click Play button + "play_pause": MediaPlayer.PLAY_PAUSE, # click Play button + "track_next": MediaPlayer.NEXT_TRACK, # click Next Track + "track_previous": MediaPlayer.PREVIOUS_TRACK, # click Previous Track + "volume_up": MediaPlayer.CLICK_VOLUME_UP, # click + (Volume up) + "volume_up_hold": MediaPlayer.CLICK_VOLUME_UP, # hold + (Volume up) + "volume_down": MediaPlayer.CLICK_VOLUME_DOWN, # click - (Volume down) + "volume_down_hold": MediaPlayer.CLICK_VOLUME_DOWN, # hold - (Volume down) + "dots_1_initial_press": MediaPlayer.PREVIOUS_SOURCE, # click . (Previous Source) + "dots_2_initial_press": MediaPlayer.NEXT_SOURCE, # click .. (Next Source) + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: MediaPlayer.PLAY, # Play; Press/Release or Hold + 2001: MediaPlayer.HOLD_VOLUME_UP, # +; Hold (every 0.2s) + 2002: MediaPlayer.RELEASE, # +; Press/Release + 3001: MediaPlayer.HOLD_VOLUME_DOWN, # +; Hold (every 0.2s) + 3002: MediaPlayer.RELEASE, # -; Press/Release + 4002: MediaPlayer.PREVIOUS_TRACK, # Previous; Press/Release or Hold + 5002: MediaPlayer.NEXT_TRACK, # Next; Press/Release or Hold + 6000: MediaPlayer.PREVIOUS_SOURCE, # .; Initial Press + 7000: MediaPlayer.NEXT_SOURCE, # ..; Initial Press + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": MediaPlayer.PLAY_PAUSE, # click Play button + "play_pause": MediaPlayer.PLAY_PAUSE, # click Play button + "step_0_1_0_0_0": MediaPlayer.NEXT_TRACK, # click Next Track + "step_1_1_0_0_0": MediaPlayer.PREVIOUS_TRACK, # click Previous Track + "move_with_on_off_0_255": MediaPlayer.CLICK_VOLUME_UP, # click + (Volume up) + "move_0_255_0_0": MediaPlayer.HOLD_VOLUME_UP, # hold + (Volume up) + "move_with_on_off_1_255": MediaPlayer.CLICK_VOLUME_DOWN, # click - (Volume down) + "move_1_255_0_0": MediaPlayer.HOLD_VOLUME_DOWN, # hold - (Volume down) + "2_initial_press_0": MediaPlayer.PREVIOUS_SOURCE, # click . (Previous Source) + "3_initial_press_0": MediaPlayer.NEXT_SOURCE, # click .. (Next Source) + } + + def get_zha_action(self, data: EventData) -> str: + command = data["command"] + args = data["args"] + if isinstance(args, dict): + args = args["args"] + args = list(map(str, args)) + action = command + if not (command == "stop" or command == "release"): + if len(args) > 0: + action += "_" + "_".join(args) + endpoint = data["endpoint_id"] + # We only need to add the endpoint ID for the dot buttons, which use 2 and 3 + if endpoint != 1: + return f"{data['endpoint_id']}_{action}" + else: + return action diff --git a/appdaemon/apps/controllerx/cx_devices/legrand.py b/appdaemon/apps/controllerx/cx_devices/legrand.py new file mode 100644 index 0000000..53ea06f --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/legrand.py @@ -0,0 +1,100 @@ +from typing import Any + +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController +from cx_core.integration import EventData + + +def get_zha_action_LegrandWallController(data: dict[str, Any]) -> str: + endpoint_id = data.get("endpoint_id", 1) + command: str = data["command"] + action = command + args = data.get("args", {}) + args_mapping = {0: "up", 1: "down"} + if command == "move": + action = "_".join((action, args_mapping[args[0]])) + action = "_".join((str(endpoint_id), action)) + return action + + +class Legrand600083LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_on": Light.ON, + "1_off": Light.OFF, + "1_move_up": Light.HOLD_BRIGHTNESS_UP, + "1_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "1_stop": Light.RELEASE, + } + + def get_zha_action(self, data: EventData) -> str: + return get_zha_action_LegrandWallController(data) + + +class Legrand600083Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "off": Z2MLight.OFF, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Z2MLight.RELEASE, + } + + +class Legrand600088LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_left": Light.ON, + "off_left": Light.OFF, + "brightness_move_up_left": Light.HOLD_COLOR_UP, + "brightness_move_down_left": Light.HOLD_COLOR_DOWN, + "brightness_stop_left": Light.RELEASE, + "on_right": Light.ON_FULL_BRIGHTNESS, + "off_right": Light.ON_MIN_BRIGHTNESS, + "brightness_move_up_right": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_right": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_right": Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_on": Light.ON, + "1_off": Light.OFF, + "1_move_up": Light.HOLD_COLOR_UP, + "1_move_down": Light.HOLD_COLOR_DOWN, + "1_stop": Light.RELEASE, + "2_on": Light.ON_FULL_BRIGHTNESS, + "2_off": Light.ON_MIN_BRIGHTNESS, + "2_move_up": Light.HOLD_BRIGHTNESS_UP, + "2_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "2_stop": Light.RELEASE, + } + + def get_zha_action(self, data: EventData) -> str: + return get_zha_action_LegrandWallController(data) + + +class Legrand600088Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_left": Z2MLight.ON, + "off_left": Z2MLight.OFF, + "brightness_move_up_left": Z2MLight.HOLD_COLOR_TEMP_UP, + "brightness_move_down_left": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "brightness_stop_left": Z2MLight.RELEASE, + "on_right": Z2MLight.ON_FULL_BRIGHTNESS, + "off_right": Z2MLight.ON_MIN_BRIGHTNESS, + "brightness_move_up_right": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_right": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_right": Z2MLight.RELEASE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/linkind.py b/appdaemon/apps/controllerx/cx_devices/linkind.py new file mode 100644 index 0000000..4759ecc --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/linkind.py @@ -0,0 +1,38 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController + + +class ZS23000278LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "brightness_step_up": Light.CLICK_BRIGHTNESS_UP, + "brightness_step_down": Light.CLICK_BRIGHTNESS_DOWN, + "brightness_move_to_level": Light.BRIGHTNESS_FROM_CONTROLLER_LEVEL, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Light.RELEASE, + "color_temperature_move": Light.COLORTEMP_FROM_CONTROLLER, + "color_temperature_move_up": Light.CLICK_COLOR_TEMP_UP, + "color_temperature_move_down": Light.CLICK_COLOR_TEMP_DOWN, + "color_move": Light.XYCOLOR_FROM_CONTROLLER, + } + + +class ZS23000278Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "off": Z2MLight.OFF, + "brightness_step_up": Z2MLight.CLICK_BRIGHTNESS_UP, + "brightness_step_down": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "brightness_move_to_level": Z2MLight.BRIGHTNESS_FROM_CONTROLLER_LEVEL, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Z2MLight.RELEASE, + "color_temperature_move": Z2MLight.COLORTEMP_FROM_CONTROLLER, + "color_temperature_move_up": Z2MLight.CLICK_COLOR_TEMP_UP, + "color_temperature_move_down": Z2MLight.CLICK_COLOR_TEMP_DOWN, + "color_move": Z2MLight.XYCOLOR_FROM_CONTROLLER, + } diff --git a/appdaemon/apps/controllerx/cx_devices/livarno.py b/appdaemon/apps/controllerx/cx_devices/livarno.py new file mode 100644 index 0000000..055dd71 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/livarno.py @@ -0,0 +1,47 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController + + +class HG06323LightController(LightController): + # Different states reported from the controller: + # on, off, brightness_step_up, brightness_move_up, + # brightness_step_down, brightness_move_down, brightness_stop + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "brightness_step_up": Light.CLICK_BRIGHTNESS_UP, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_stop": Light.RELEASE, + "brightness_step_down": Light.CLICK_BRIGHTNESS_DOWN, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "off": Light.OFF, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "step_0_51_10": Light.CLICK_BRIGHTNESS_UP, + "move_0_51": Light.HOLD_BRIGHTNESS_UP, + "stop": Light.RELEASE, + "step_1_51_10": Light.CLICK_BRIGHTNESS_DOWN, + "move_1_51": Light.HOLD_BRIGHTNESS_DOWN, + "off": Light.OFF, + } + + +class HG06323Z2MLightController(Z2MLightController): + # Different states reported from the controller: + # on, off, brightness_step_up, brightness_move_up, + # brightness_step_down, brightness_move_down, brightness_stop + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "brightness_step_up": Z2MLight.CLICK_BRIGHTNESS_UP, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_stop": Z2MLight.RELEASE, + "brightness_step_down": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "off": Z2MLight.OFF, + } diff --git a/appdaemon/apps/controllerx/cx_devices/lutron.py b/appdaemon/apps/controllerx/cx_devices/lutron.py new file mode 100644 index 0000000..732b21e --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/lutron.py @@ -0,0 +1,167 @@ +from cx_const import DefaultActionsMapping, Light, MediaPlayer, Z2MLight +from cx_core import LightController, MediaPlayerController +from cx_core.type.z2m_light_controller import Z2MLightController + + +class LZL4BWHL01LightController(LightController): + # Each button press fires an event but no separate + # hold event. Press of up or down generates a stop event + # when released. + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.ON_FULL_BRIGHTNESS, + 2001: Light.HOLD_BRIGHTNESS_UP, + 2003: Light.RELEASE, + 3001: Light.HOLD_BRIGHTNESS_DOWN, + 3003: Light.RELEASE, + 4002: Light.OFF, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "move_to_level_with_on_off_254_4": Light.ON_FULL_BRIGHTNESS, + "step_with_on_off_0_30_6": Light.HOLD_BRIGHTNESS_UP, + "step_1_30_6": Light.HOLD_BRIGHTNESS_DOWN, + "move_to_level_with_on_off_0_4": Light.OFF, + "stop": Light.RELEASE, + } + + +class Z31BRLLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "brightness_move_to_level": Light.BRIGHTNESS_FROM_CONTROLLER_LEVEL, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 2002: Light.CLICK_BRIGHTNESS_UP, + 3002: Light.CLICK_BRIGHTNESS_DOWN, + } + + +class Z31BRLZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "brightness_move_to_level": Z2MLight.BRIGHTNESS_FROM_CONTROLLER_LEVEL, + } + + +class LutronPJ22BLightController(LightController): + def get_state_actions_mapping(self) -> DefaultActionsMapping: + return { + "1": Light.ON_FULL_BRIGHTNESS, + "4": Light.OFF, + } + + def get_lutron_caseta_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_2_press": Light.ON, + "button_4_press": Light.OFF, + } + + +class LutronPJ22BMediaPlayerController(MediaPlayerController): + def get_state_actions_mapping(self) -> DefaultActionsMapping: + return { + "1": MediaPlayer.PLAY_PAUSE, + "4": MediaPlayer.NEXT_TRACK, + } + + def get_lutron_caseta_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_2_press": MediaPlayer.PLAY_PAUSE, + "button_4_press": MediaPlayer.NEXT_TRACK, + } + + +class LutronPJ22BRLLightController(LightController): + def get_state_actions_mapping(self) -> DefaultActionsMapping: + return { + "1": Light.ON_FULL_BRIGHTNESS, + "8": Light.HOLD_BRIGHTNESS_UP, + "16": Light.HOLD_BRIGHTNESS_DOWN, + "4": Light.OFF, + "0": Light.RELEASE, + } + + +class LutronPJ22BRLMediaPlayerController(MediaPlayerController): + def get_state_actions_mapping(self) -> DefaultActionsMapping: + return { + "1": MediaPlayer.PLAY_PAUSE, + "8": MediaPlayer.HOLD_VOLUME_UP, + "16": MediaPlayer.HOLD_VOLUME_DOWN, + "4": MediaPlayer.NEXT_TRACK, + "0": MediaPlayer.RELEASE, + } + + +class LutronPJ23BRLLightController(LightController): + def get_state_actions_mapping(self) -> DefaultActionsMapping: + return { + "1": Light.ON_FULL_BRIGHTNESS, + "8": Light.HOLD_BRIGHTNESS_UP, + "2": Light.SET_HALF_BRIGHTNESS, + "16": Light.HOLD_BRIGHTNESS_DOWN, + "4": Light.OFF, + "0": Light.RELEASE, + } + + def get_lutron_caseta_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_2_press": Light.ON_FULL_BRIGHTNESS, + "button_4_press": Light.OFF, + "button_3_press": Light.SET_HALF_BRIGHTNESS, + "button_5_press": Light.HOLD_BRIGHTNESS_UP, + "button_5_release": Light.RELEASE, + "button_6_press": Light.HOLD_BRIGHTNESS_DOWN, + "button_6_release": Light.RELEASE, + } + + +class LutronPJ23BRLMediaPlayerController(MediaPlayerController): + def get_state_actions_mapping(self) -> DefaultActionsMapping: + return { + "1": MediaPlayer.PLAY_PAUSE, + "8": MediaPlayer.HOLD_VOLUME_UP, + "2": MediaPlayer.NEXT_SOURCE, + "16": MediaPlayer.HOLD_VOLUME_DOWN, + "4": MediaPlayer.NEXT_TRACK, + "0": MediaPlayer.RELEASE, + } + + def get_lutron_caseta_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_2_press": MediaPlayer.PLAY_PAUSE, + "button_4_press": MediaPlayer.NEXT_TRACK, + "button_3_press": MediaPlayer.NEXT_SOURCE, + "button_5_press": MediaPlayer.HOLD_VOLUME_UP, + "button_5_release": MediaPlayer.RELEASE, + "button_6_press": MediaPlayer.HOLD_VOLUME_DOWN, + "button_6_release": MediaPlayer.RELEASE, + } + + +class LutronPJ24BLightController(LightController): + def get_state_actions_mapping(self) -> DefaultActionsMapping: + return { + "1": Light.ON_FULL_BRIGHTNESS, + "2": Light.HOLD_BRIGHTNESS_UP, + "4": Light.HOLD_BRIGHTNESS_DOWN, + "8": Light.OFF, + "0": Light.RELEASE, + } + + +class LutronPJ24BMediaPlayerController(MediaPlayerController): + def get_state_actions_mapping(self) -> DefaultActionsMapping: + return { + "1": MediaPlayer.PLAY_PAUSE, + "2": MediaPlayer.HOLD_VOLUME_UP, + "4": MediaPlayer.HOLD_VOLUME_DOWN, + "8": MediaPlayer.NEXT_TRACK, + "0": MediaPlayer.RELEASE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/muller_licht.py b/appdaemon/apps/controllerx/cx_devices/muller_licht.py new file mode 100644 index 0000000..dd97e68 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/muller_licht.py @@ -0,0 +1,118 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController +from cx_core.controller import Controller +from cx_core.integration import EventData + + +class MLI404011LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.TOGGLE, + "off": Light.TOGGLE, + "brightness_down_click": Light.CLICK_BRIGHTNESS_DOWN, + "brightness_down_hold": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_down_release": Light.RELEASE, + "brightness_up_click": Light.CLICK_BRIGHTNESS_UP, + "brightness_up_hold": Light.HOLD_BRIGHTNESS_UP, + "brightness_up_release": Light.RELEASE, + "color_wheel": Light.XYCOLOR_FROM_CONTROLLER, # Color ring press + "color_temp": Light.COLORTEMP_FROM_CONTROLLER, # warm or cold + "scene_1": None, # sunset button + "scene_2": None, # party button + "scene_3": None, # reading button + "scene_4": None, # fire button + "scene_5": None, # heart button + "scene_6": None, # night button + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 2001: Light.HOLD_BRIGHTNESS_UP, + 2002: Light.CLICK_BRIGHTNESS_UP, + 2003: Light.RELEASE, + 3001: Light.HOLD_BRIGHTNESS_DOWN, + 3002: Light.CLICK_BRIGHTNESS_DOWN, + 3003: Light.RELEASE, + 4002: Light.CLICK_COLOR_UP, + 5002: Light.CLICK_COLOR_DOWN, + 6002: Light.XYCOLOR_FROM_CONTROLLER, # Color ring press + 7002: None, # reading button + 8002: None, # sunset button + 9002: None, # party button + 10002: None, # night button + 11002: None, # fire button + 12002: None, # heart button + } + + +class MLI404011Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.TOGGLE, + "off": Z2MLight.TOGGLE, + "brightness_down_click": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "brightness_down_hold": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_down_release": Z2MLight.RELEASE, + "brightness_up_click": Z2MLight.CLICK_BRIGHTNESS_UP, + "brightness_up_hold": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_up_release": Z2MLight.RELEASE, + "color_wheel": Z2MLight.XYCOLOR_FROM_CONTROLLER, # Color ring press + "color_temp": Z2MLight.COLORTEMP_FROM_CONTROLLER, # warm or cold + "scene_1": None, # sunset button + "scene_2": None, # party button + "scene_3": None, # reading button + "scene_4": None, # fire button + "scene_5": None, # heart button + "scene_6": None, # night button + } + + +class MLI404002Controller(Controller): + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + if command not in ("move", "step"): + return command + args = data["args"] + direction_mapping = {0: "up", 1: "down"} + return f"{command}_{direction_mapping[args[0]]}" + + +class MLI404002LightController(MLI404002Controller, LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.TOGGLE, + "off": Light.TOGGLE, + "brightness_step_up": Light.CLICK_BRIGHTNESS_UP, + "brightness_step_down": Light.CLICK_BRIGHTNESS_DOWN, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Light.RELEASE, + "recall_1": Light.ON_FULL_BRIGHTNESS, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.TOGGLE, + "off": Light.TOGGLE, + "move_up": Light.HOLD_BRIGHTNESS_UP, + "move_down": Light.HOLD_BRIGHTNESS_DOWN, + "stop": Light.RELEASE, + "step_up": Light.CLICK_BRIGHTNESS_UP, + "step_down": Light.CLICK_BRIGHTNESS_DOWN, + "recall": Light.ON_FULL_BRIGHTNESS, + } + + +class MLI404002Z2MLightController(MLI404002Controller, Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.TOGGLE, + "off": Z2MLight.TOGGLE, + "brightness_step_up": Z2MLight.CLICK_BRIGHTNESS_UP, + "brightness_step_down": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Z2MLight.RELEASE, + "recall_1": Z2MLight.ON_FULL_BRIGHTNESS, + } diff --git a/appdaemon/apps/controllerx/cx_devices/namron.py b/appdaemon/apps/controllerx/cx_devices/namron.py new file mode 100644 index 0000000..a8a08bd --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/namron.py @@ -0,0 +1,54 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController + + +class Namron4512773LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_l1": Light.ON, + "off_l1": Light.OFF, + "brightness_move_up_l1": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_l1": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_l1": Light.RELEASE, + "on_l2": Light.ON, + "off_l2": Light.OFF, + "brightness_move_up_l2": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_l2": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_l2": Light.RELEASE, + "on_l3": Light.ON, + "off_l3": Light.OFF, + "brightness_move_up_l3": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_l3": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_l3": Light.RELEASE, + "on_l4": Light.ON, + "off_l4": Light.OFF, + "brightness_move_up_l4": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_l4": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_l4": Light.RELEASE, + } + + +class Namron4512773Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_l1": Z2MLight.ON, + "off_l1": Z2MLight.OFF, + "brightness_move_up_l1": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_l1": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_l1": Z2MLight.RELEASE, + "on_l2": Z2MLight.ON, + "off_l2": Z2MLight.OFF, + "brightness_move_up_l2": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_l2": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_l2": Z2MLight.RELEASE, + "on_l3": Z2MLight.ON, + "off_l3": Z2MLight.OFF, + "brightness_move_up_l3": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_l3": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_l3": Z2MLight.RELEASE, + "on_l4": Z2MLight.ON, + "off_l4": Z2MLight.OFF, + "brightness_move_up_l4": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_l4": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_l4": Z2MLight.RELEASE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/osram.py b/appdaemon/apps/controllerx/cx_devices/osram.py new file mode 100644 index 0000000..62f7965 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/osram.py @@ -0,0 +1,38 @@ +from cx_const import DefaultActionsMapping, Light +from cx_core import LightController +from cx_core.integration import EventData + + +class OsramAC025XX00NJLightController(LightController): + # This mapping works for: AC0251100NJ / AC0251400NJ / AC0251600NJ / AC0251700NJ + # (different models are just different colours) + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.ON, # Click Arrow up + 1001: Light.HOLD_BRIGHTNESS_UP, # Hold Arrow Up + 1003: Light.RELEASE, # Release Arrow Up + 2002: Light.OFF, # Click Arrow down + 2001: Light.HOLD_BRIGHTNESS_DOWN, # Hold Arrow down + 2003: Light.RELEASE, # Release Arrow down + 3002: Light.SYNC, # Click Circle button + 3001: Light.HOLD_COLOR_UP, # Hold Circle button + 3003: Light.RELEASE, # Release Circle button + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_on": Light.ON, + "1_move_with_on_off": Light.HOLD_BRIGHTNESS_UP, + "1_stop": Light.RELEASE, + "3_move_to_level_with_on_off": None, + "3_move_to_color_temp": Light.SYNC, + "3_move_to_saturation": Light.HOLD_COLOR_UP, + "3_move_hue": Light.RELEASE, + "2_off": Light.OFF, + "2_move": Light.HOLD_BRIGHTNESS_DOWN, + "2_stop": Light.RELEASE, + } + + def get_zha_action(self, data: EventData) -> str: + return f"{data['endpoint_id']}_{data['command']}" diff --git a/appdaemon/apps/controllerx/cx_devices/philips.py b/appdaemon/apps/controllerx/cx_devices/philips.py new file mode 100644 index 0000000..0c52602 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/philips.py @@ -0,0 +1,326 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController +from cx_core.integration import EventData + + +class HueDimmerController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_press_release": Light.ON, + "on_hold": Light.HOLD_COLOR_UP, + "on_hold_release": Light.RELEASE, + "up_press_release": Light.CLICK_BRIGHTNESS_UP, + "up_hold": Light.HOLD_BRIGHTNESS_UP, + "up_hold_release": Light.RELEASE, + "down_press_release": Light.CLICK_BRIGHTNESS_DOWN, + "down_hold": Light.HOLD_BRIGHTNESS_DOWN, + "down_hold_release": Light.RELEASE, + "off_press_release": Light.OFF, + "off_hold": Light.HOLD_COLOR_DOWN, + "off_hold_release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.ON, + 1001: Light.HOLD_COLOR_UP, + 1003: Light.RELEASE, + 2002: Light.CLICK_BRIGHTNESS_UP, + 2001: Light.HOLD_BRIGHTNESS_UP, + 2003: Light.RELEASE, + 3002: Light.CLICK_BRIGHTNESS_DOWN, + 3001: Light.HOLD_BRIGHTNESS_DOWN, + 3003: Light.RELEASE, + 4002: Light.OFF, + 4001: Light.HOLD_COLOR_DOWN, + 4003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "off_long_release": Light.RELEASE, + "off_hold": Light.HOLD_COLOR_DOWN, + "off_short_release": Light.OFF, + "down_long_release": Light.RELEASE, + "down_hold": Light.HOLD_BRIGHTNESS_DOWN, + "down_short_release": Light.CLICK_BRIGHTNESS_DOWN, + "up_long_release": Light.RELEASE, + "up_hold": Light.HOLD_BRIGHTNESS_UP, + "up_short_release": Light.CLICK_BRIGHTNESS_UP, + "on_long_release": Light.RELEASE, + "on_hold": Light.HOLD_COLOR_UP, + "on_short_release": Light.ON, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class HueDimmerZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_press_release": Z2MLight.ON, + "on_hold": Z2MLight.HOLD_COLOR_TEMP_UP, + "on_hold_release": Z2MLight.RELEASE, + "up_press_release": Z2MLight.CLICK_BRIGHTNESS_UP, + "up_hold": Z2MLight.HOLD_BRIGHTNESS_UP, + "up_hold_release": Z2MLight.RELEASE, + "down_press_release": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "down_hold": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "down_hold_release": Z2MLight.RELEASE, + "off_press_release": Z2MLight.OFF, + "off_hold": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "off_hold_release": Z2MLight.RELEASE, + } + + +class Philips929002398602LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_press_release": Light.TOGGLE, + "on_hold": Light.TOGGLE, + "on_hold_release": Light.RELEASE, + "up_press_release": Light.CLICK_BRIGHTNESS_UP, + "up_hold": Light.HOLD_BRIGHTNESS_UP, + "up_hold_release": Light.RELEASE, + "down_press_release": Light.CLICK_BRIGHTNESS_DOWN, + "down_hold": Light.HOLD_BRIGHTNESS_DOWN, + "down_hold_release": Light.RELEASE, + "off_press_release": Light.CLICK_COLOR_UP, + "off_hold": Light.HOLD_COLOR_DOWN, + "off_hold_release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 1001: Light.TOGGLE, + 1003: Light.RELEASE, + 2002: Light.CLICK_BRIGHTNESS_UP, + 2001: Light.HOLD_BRIGHTNESS_UP, + 2003: Light.RELEASE, + 3002: Light.CLICK_BRIGHTNESS_DOWN, + 3001: Light.HOLD_BRIGHTNESS_DOWN, + 3003: Light.RELEASE, + 4002: Light.CLICK_COLOR_UP, + 4001: Light.HOLD_COLOR_DOWN, + 4003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "off_long_release": Light.RELEASE, + "off_hold": Light.HOLD_COLOR_DOWN, + "off_short_release": Light.CLICK_COLOR_UP, + "down_long_release": Light.RELEASE, + "down_hold": Light.HOLD_BRIGHTNESS_DOWN, + "down_short_release": Light.CLICK_BRIGHTNESS_DOWN, + "up_long_release": Light.RELEASE, + "up_hold": Light.HOLD_BRIGHTNESS_UP, + "up_short_release": Light.CLICK_BRIGHTNESS_UP, + "on_long_release": Light.RELEASE, + "on_hold": Light.TOGGLE, + "on_short_release": Light.TOGGLE, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class Philips929002398602Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_press_release": Z2MLight.ON, + "on_hold": Z2MLight.HOLD_COLOR_TEMP_UP, + "on_hold_release": Z2MLight.RELEASE, + "up_press_release": Z2MLight.CLICK_BRIGHTNESS_UP, + "up_hold": Z2MLight.HOLD_BRIGHTNESS_UP, + "up_hold_release": Z2MLight.RELEASE, + "down_press_release": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "down_hold": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "down_hold_release": Z2MLight.RELEASE, + "off_press_release": Z2MLight.OFF, + "off_hold": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "off_hold_release": Z2MLight.RELEASE, + } + + +class PTM215XLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "press_1": Light.ON, + "release_1": Light.RELEASE, + "press_2": Light.OFF, + "release_2": Light.RELEASE, + "press_3": Light.ON_FULL_BRIGHTNESS, + "release_3": Light.RELEASE, + "press_4": Light.ON_MIN_BRIGHTNESS, + "release_4": Light.RELEASE, + "press_1_and_3": Light.ON_FULL_COLOR_TEMP, + "release_1_and_3": Light.RELEASE, + "press_2_and_4": Light.ON_MIN_COLOR_TEMP, + "release_2_and_4": Light.RELEASE, + "press_energy_bar": Light.SYNC, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.ON, # button_1_release + 1001: Light.SYNC, # button_1_hold + 1000: None, # button_1_press + 1003: None, # button_1_release_after_hold + 2002: Light.OFF, # button_2_release + 2001: Light.SYNC, # button_2_hold + 2000: None, # button_2_press + 2003: None, # button_2_release_after_hold + 3002: Light.ON_FULL_BRIGHTNESS, # button_3_release + 3001: Light.HOLD_BRIGHTNESS_UP, # button_3_hold + 3000: None, # button_3_press + 3003: Light.RELEASE, # button_3_release_after_hold + 4002: Light.ON_MIN_BRIGHTNESS, # button_4_release + 4001: Light.HOLD_BRIGHTNESS_DOWN, # button_4_hold + 4000: None, # button_4_press + 4003: Light.RELEASE, # button_4_release_after_hold + 5002: Light.ON_FULL_COLOR_TEMP, # button_1_3_release + 5001: Light.HOLD_COLOR_UP, # button_1_3_hold + 5000: None, # button_1_3_press + 5003: Light.RELEASE, # button_1_3_release_after_hold + 6002: Light.ON_MIN_COLOR_TEMP, # button_2_4_release + 6001: Light.HOLD_COLOR_DOWN, # button_2_4_hold + 6000: None, # button_2_4_press + 6003: Light.RELEASE, # button_2_4_release_after_hold + } + + +class HueSmartButtonLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.TOGGLE, + "off": Light.TOGGLE, + "hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1000: None, # Initial press + 1001: Light.HOLD_BRIGHTNESS_TOGGLE, + 1002: Light.TOGGLE, + 1003: Light.RELEASE, + } + + +class HueSmartButtonZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.TOGGLE, + "off": Z2MLight.TOGGLE, + "hold": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "release": Z2MLight.RELEASE, + } + + +class Philips929003017102LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + # "left_press": Light.TOGGLE, + "left_press_release": Light.TOGGLE, + "left_hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "left_hold_release": Light.RELEASE, + # "right_press": Light.TOGGLE, + "right_press_release": Light.TOGGLE, + "right_hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "right_hold_release": Light.RELEASE, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1000: None, # Initial press + 1001: Light.HOLD_BRIGHTNESS_TOGGLE, + 1002: Light.TOGGLE, + 1003: Light.RELEASE, + 2000: None, # Initial press + 2001: Light.HOLD_BRIGHTNESS_TOGGLE, + 2002: Light.TOGGLE, + 2003: Light.RELEASE, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + # "left_press": Light.TOGGLE, + "left_press_release": Light.TOGGLE, + "left_hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "left_hold_release": Light.RELEASE, + # "right_press": Light.TOGGLE, + "right_short_release": Light.TOGGLE, + "right_hold": Light.HOLD_BRIGHTNESS_TOGGLE, + "right_long_release": Light.RELEASE, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class Philips929003017102Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + # "left_press": Z2MLight.TOGGLE, + "left_press_release": Z2MLight.TOGGLE, + "left_hold": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "left_hold_release": Z2MLight.RELEASE, + # "right_press": Z2MLight.TOGGLE, + "right_press_release": Z2MLight.TOGGLE, + "right_hold": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "right_hold_release": Z2MLight.RELEASE, + } + + +class PhilipsRDM002LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_press_release": Light.OFF, + "button_1_hold": Light.HOLD_BRIGHTNESS_DOWN, + "button_1_hold_release": Light.RELEASE, + "button_2_press_release": Light.ON, + "button_2_hold": Light.HOLD_BRIGHTNESS_UP, + "button_2_hold_release": Light.RELEASE, + "button_3_press_release": Light.CLICK_COLOR_DOWN, + "button_3_hold": Light.HOLD_COLOR_DOWN, + "button_3_hold_release": Light.RELEASE, + "button_4_press_release": Light.CLICK_COLOR_UP, + "button_4_hold": Light.HOLD_COLOR_UP, + "button_4_hold_release": Light.RELEASE, + "dial_rotate_left_step": Light.CLICK_BRIGHTNESS_DOWN, + "dial_rotate_left_slow": Light.ON_MIN_COLOR_TEMP, + "dial_rotate_left_fast": Light.ON_MIN_BRIGHTNESS, + "dial_rotate_right_step": Light.CLICK_BRIGHTNESS_UP, + "dial_rotate_right_slow": Light.ON_FULL_COLOR_TEMP, + "dial_rotate_right_fast": Light.ON_FULL_BRIGHTNESS, + } + + +class PhilipsRDM002Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_1_press_release": Z2MLight.OFF, + "button_1_hold": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "button_1_hold_release": Z2MLight.RELEASE, + "button_2_press_release": Z2MLight.ON, + "button_2_hold": Z2MLight.HOLD_BRIGHTNESS_UP, + "button_2_hold_release": Z2MLight.RELEASE, + "button_3_press_release": Z2MLight.CLICK_COLOR_TEMP_DOWN, + "button_3_hold": Z2MLight.HOLD_COLOR_TEMP_DOWN, + "button_3_hold_release": Z2MLight.RELEASE, + "button_4_press_release": Z2MLight.CLICK_COLOR_TEMP_UP, + "button_4_hold": Z2MLight.HOLD_COLOR_TEMP_UP, + "button_4_hold_release": Z2MLight.RELEASE, + "dial_rotate_left_step": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "dial_rotate_left_slow": Z2MLight.ON_MIN_COLOR_TEMP, + "dial_rotate_left_fast": Z2MLight.ON_MIN_BRIGHTNESS, + "dial_rotate_right_step": Light.CLICK_BRIGHTNESS_UP, + "dial_rotate_right_slow": Z2MLight.ON_FULL_COLOR_TEMP, + "dial_rotate_right_fast": Z2MLight.ON_FULL_BRIGHTNESS, + } diff --git a/appdaemon/apps/controllerx/cx_devices/prolight.py b/appdaemon/apps/controllerx/cx_devices/prolight.py new file mode 100644 index 0000000..299a0a2 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/prolight.py @@ -0,0 +1,34 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController + + +class Prolight5412748727388LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Light.RELEASE, + "brightness_move_to_level": Light.BRIGHTNESS_FROM_CONTROLLER_LEVEL, + "color_temperature_move": Light.COLORTEMP_FROM_CONTROLLER, + "color_temperature_move_up": Light.CLICK_COLOR_TEMP_UP, + "color_temperature_move_down": Light.CLICK_COLOR_TEMP_DOWN, + "color_move": Light.XYCOLOR_FROM_CONTROLLER, + } + + +class Prolight5412748727388Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "off": Z2MLight.OFF, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Z2MLight.RELEASE, + "brightness_move_to_level": Z2MLight.BRIGHTNESS_FROM_CONTROLLER_LEVEL, + "color_temperature_move": Z2MLight.COLORTEMP_FROM_CONTROLLER, + "color_temperature_move_up": Z2MLight.CLICK_COLOR_TEMP_UP, + "color_temperature_move_down": Z2MLight.CLICK_COLOR_TEMP_DOWN, + "color_move": Z2MLight.XYCOLOR_FROM_CONTROLLER, + } diff --git a/appdaemon/apps/controllerx/cx_devices/rgb_genie.py b/appdaemon/apps/controllerx/cx_devices/rgb_genie.py new file mode 100644 index 0000000..d861cb7 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/rgb_genie.py @@ -0,0 +1,90 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController +from cx_core.controller import action +from cx_core.integration import EventData +from cx_core.integration.zha import ZHAIntegration + + +class ZB5121LightController(LightController): + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "off": Light.OFF, + "step_with_on_off_0_32_0": Light.CLICK_BRIGHTNESS_UP, # Click brightness up + "move_with_on_off_0_50": Light.HOLD_BRIGHTNESS_UP, # Hold brightness up + "step_with_on_off_1_32_0": Light.CLICK_BRIGHTNESS_DOWN, # Click brightness down + "move_with_on_off_1_50": Light.HOLD_BRIGHTNESS_DOWN, # Hold brightness down + "recall_0_1": None, # Click clapperboard + "stop": Light.RELEASE, # long release + } + + +class ZB5122LightController(LightController): + @action + async def colortemp_from_controller(self, extra: EventData) -> None: + if isinstance(self.integration, ZHAIntegration): + await self._on(color_temp=extra["args"][0]) + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, # Click light on + "off": Light.OFF, # Click light off + "hold_brightness_up": Light.HOLD_BRIGHTNESS_UP, # Hold light on + "hold_brightness_down": Light.HOLD_BRIGHTNESS_DOWN, # Hold light off + "stop": Light.RELEASE, # long release + "move_to_color": Light.CLICK_XY_COLOR_UP, # click RGB + "move_hue": Light.HOLD_XY_COLOR_UP, # hold RGB + "stop_move_hue": Light.RELEASE, # release RGB + "move_to_color_temp": Light.COLORTEMP_FROM_CONTROLLER, # click CW + "move_color_temp": Light.HOLD_COLOR_TEMP_TOGGLE, # hold CW + "stop_move_step": Light.RELEASE, # release CW + "recall_0_1": None, # Click clapperboard + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + if command == "move_with_on_off": + return ( + "hold_brightness_up" if data["args"][0] == 0 else "hold_brightness_down" + ) + elif command == "move_hue": + return "stop_move_hue" if tuple(data["args"]) == (0, 0) else "move_hue" + return command + + +class ZB3009LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.TOGGLE, + "off": Light.TOGGLE, + "brightness_move_up": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Light.RELEASE, + "color_temperature_move_down": Light.CLICK_COLOR_TEMP_DOWN, + "color_temperature_move_up": Light.CLICK_COLOR_TEMP_UP, + "color_temperature_move": Light.COLORTEMP_FROM_CONTROLLER, + "color_move": Light.XYCOLOR_FROM_CONTROLLER, + "hue_move": None, # Play/pause button + "recall_1": None, # Scene 1 + "recall_3": None, # Scene 2 + "recall_2": None, # Scene 3 + } + + +class ZB3009Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.TOGGLE, + "off": Z2MLight.TOGGLE, + "brightness_move_up": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop": Z2MLight.RELEASE, + "color_temperature_move_down": Z2MLight.CLICK_COLOR_TEMP_DOWN, + "color_temperature_move_up": Z2MLight.CLICK_COLOR_TEMP_UP, + "color_temperature_move": Z2MLight.COLORTEMP_FROM_CONTROLLER, + "color_move": Z2MLight.XYCOLOR_FROM_CONTROLLER, + "hue_move": None, # Play/pause button + "recall_1": None, # Scene 1 + "recall_3": None, # Scene 2 + "recall_2": None, # Scene 3 + } diff --git a/appdaemon/apps/controllerx/cx_devices/robb.py b/appdaemon/apps/controllerx/cx_devices/robb.py new file mode 100644 index 0000000..6bf4e73 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/robb.py @@ -0,0 +1,54 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController + + +class ROB2000070LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_1": Light.ON, + "off_1": Light.OFF, + "brightness_move_up_1": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_1": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_1": Light.RELEASE, + "on_2": Light.ON, + "off_2": Light.OFF, + "brightness_move_up_2": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_2": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_2": Light.RELEASE, + "on_3": Light.ON, + "off_3": Light.OFF, + "brightness_move_up_3": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_3": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_3": Light.RELEASE, + "on_4": Light.ON, + "off_4": Light.OFF, + "brightness_move_up_4": Light.HOLD_BRIGHTNESS_UP, + "brightness_move_down_4": Light.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_4": Light.RELEASE, + } + + +class ROB2000070Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on_1": Z2MLight.ON, + "off_1": Z2MLight.OFF, + "brightness_move_up_1": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_1": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_1": Z2MLight.RELEASE, + "on_2": Z2MLight.ON, + "off_2": Z2MLight.OFF, + "brightness_move_up_2": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_2": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_2": Z2MLight.RELEASE, + "on_3": Z2MLight.ON, + "off_3": Z2MLight.OFF, + "brightness_move_up_3": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_3": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_3": Z2MLight.RELEASE, + "on_4": Z2MLight.ON, + "off_4": Z2MLight.OFF, + "brightness_move_up_4": Z2MLight.HOLD_BRIGHTNESS_UP, + "brightness_move_down_4": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "brightness_stop_4": Z2MLight.RELEASE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/sengled.py b/appdaemon/apps/controllerx/cx_devices/sengled.py new file mode 100644 index 0000000..c36fa98 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/sengled.py @@ -0,0 +1,48 @@ +from cx_const import DefaultActionsMapping, Light, Z2MLight +from cx_core import LightController, Z2MLightController + + +class E1EG7FLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "on_long": Light.CLICK_COLOR_UP, + "on_double": Light.ON_FULL_COLOR_TEMP, + "up": Light.CLICK_BRIGHTNESS_UP, + "up_long": Light.ON_FULL_BRIGHTNESS, + "down": Light.CLICK_BRIGHTNESS_DOWN, + "down_long": Light.ON_MIN_BRIGHTNESS, + "off": Light.OFF, + "off_long": Light.CLICK_COLOR_DOWN, + "off_double": Light.ON_MIN_COLOR_TEMP, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "on_long": Light.CLICK_COLOR_UP, + "on_double": Light.ON_FULL_COLOR_TEMP, + "step_0_1_0": Light.CLICK_BRIGHTNESS_UP, + "step_0_2_0": Light.ON_FULL_BRIGHTNESS, + "step_1_1_0": Light.CLICK_BRIGHTNESS_DOWN, + "step_1_2_0": Light.ON_MIN_BRIGHTNESS, + "off": Light.OFF, + "off_long": Light.CLICK_COLOR_DOWN, + "off_double": Light.ON_MIN_COLOR_TEMP, + } + + +class E1EG7FZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "on_long": Z2MLight.CLICK_COLOR_TEMP_UP, + "on_double": Z2MLight.ON_FULL_COLOR_TEMP, + "up": Z2MLight.CLICK_BRIGHTNESS_UP, + "up_long": Z2MLight.ON_FULL_BRIGHTNESS, + "down": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "down_long": Z2MLight.ON_MIN_BRIGHTNESS, + "off": Z2MLight.OFF, + "off_long": Z2MLight.CLICK_COLOR_TEMP_DOWN, + "off_double": Z2MLight.ON_MIN_COLOR_TEMP, + } diff --git a/appdaemon/apps/controllerx/cx_devices/shelly.py b/appdaemon/apps/controllerx/cx_devices/shelly.py new file mode 100644 index 0000000..fca81a0 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/shelly.py @@ -0,0 +1,57 @@ +from cx_const import DefaultActionsMapping, Light +from cx_core import LightController + + +class ShellyI3LightController(LightController): + def get_shellyforhass_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.CLICK_BRIGHTNESS_UP, + "long": Light.CLICK_BRIGHTNESS_DOWN, + "double": Light.ON_FULL_BRIGHTNESS, + } + + +class ShellyPlusI4LightController(LightController): + def get_shelly_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_push_1": Light.ON, + "long_push_1": Light.HOLD_COLOR_UP, + "btn_up_1": Light.RELEASE, + "double_push_1": Light.ON_FULL_COLOR_TEMP, + "single_push_2": Light.OFF, + "long_push_2": Light.HOLD_COLOR_DOWN, + "btn_up_2": Light.RELEASE, + "double_push_2": Light.ON_MIN_COLOR_TEMP, + "single_push_3": Light.CLICK_BRIGHTNESS_UP, + "long_push_3": Light.HOLD_BRIGHTNESS_UP, + "btn_up_3": Light.RELEASE, + "double_push_3": Light.ON_FULL_BRIGHTNESS, + "single_push_4": Light.CLICK_BRIGHTNESS_DOWN, + "long_push_4": Light.HOLD_BRIGHTNESS_DOWN, + "btn_up_4": Light.RELEASE, + "double_push_4": Light.ON_MIN_BRIGHTNESS, + } + + +class Shelly25LightController(LightController): + def get_shelly_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_push_1": Light.ON, + "long_push_1": Light.HOLD_BRIGHTNESS_UP, + "btn_up_1": Light.RELEASE, + "double_push_1": Light.ON_FULL_BRIGHTNESS, + "single_push_2": Light.OFF, + "long_push_2": Light.HOLD_BRIGHTNESS_DOWN, + "btn_up_2": Light.RELEASE, + "double_push_2": Light.ON_MIN_BRIGHTNESS, + } + + +class ShellyDimmer2LightController(LightController): + def get_shelly_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_1": Light.TOGGLE, + "single_2": None, + "long_1": Light.TOGGLE, + "long_2": None, + } diff --git a/appdaemon/apps/controllerx/cx_devices/smartkontakten.py b/appdaemon/apps/controllerx/cx_devices/smartkontakten.py new file mode 100644 index 0000000..255f0f2 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/smartkontakten.py @@ -0,0 +1,25 @@ +from cx_const import DefaultActionsMapping, Light +from cx_core import LightController + + +class SK5700002228949LightController(LightController): + # This mapping works for: 5700002228949, 5700002228963, 5745000433087 + # The buttons are distributted like: + # 1 3 + # 2 4 + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.ON, + 1001: Light.HOLD_BRIGHTNESS_UP, + 1003: Light.RELEASE, + 3002: Light.OFF, + 3001: Light.HOLD_BRIGHTNESS_DOWN, + 3003: Light.RELEASE, + 2002: Light.CLICK_COLOR_UP, + 2001: Light.HOLD_COLOR_UP, + 2003: Light.RELEASE, + 4002: Light.CLICK_COLOR_DOWN, + 4001: Light.HOLD_COLOR_DOWN, + 4003: Light.RELEASE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/smartthings.py b/appdaemon/apps/controllerx/cx_devices/smartthings.py new file mode 100644 index 0000000..eaffa85 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/smartthings.py @@ -0,0 +1,58 @@ +from cx_const import DefaultActionsMapping, Light, MediaPlayer +from cx_core import LightController, MediaPlayerController + + +class SmartThingsButtonLightController(LightController): + """ + This controller sends click, double click, and hold commands. + No release command is sent. + """ + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "hold": Light.SET_HALF_BRIGHTNESS, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: Light.TOGGLE, + 1004: Light.ON_FULL_BRIGHTNESS, + 1001: Light.SET_HALF_BRIGHTNESS, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_single_1_0_0_0": Light.TOGGLE, + "button_double_2_0_0_0": Light.ON_FULL_BRIGHTNESS, + "button_hold_3_0_0_0": Light.SET_HALF_BRIGHTNESS, + } + + +class SmartThingsButtonMediaPlayerController(MediaPlayerController): + """ + This controller sends click, double click, and hold commands. + No release command is sent. + """ + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single_click": MediaPlayer.PLAY_PAUSE, + "double_click": MediaPlayer.NEXT_TRACK, + "hold": MediaPlayer.PREVIOUS_TRACK, + } + + def get_deconz_actions_mapping(self) -> DefaultActionsMapping: + return { + 1002: MediaPlayer.PLAY_PAUSE, + 1004: MediaPlayer.NEXT_TRACK, + 1001: MediaPlayer.PREVIOUS_TRACK, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_single_1_0_0_0": MediaPlayer.PLAY_PAUSE, + "button_double_2_0_0_0": MediaPlayer.NEXT_TRACK, + "button_hold_3_0_0_0": MediaPlayer.PREVIOUS_TRACK, + } diff --git a/appdaemon/apps/controllerx/cx_devices/sonoff.py b/appdaemon/apps/controllerx/cx_devices/sonoff.py new file mode 100644 index 0000000..c7e8166 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/sonoff.py @@ -0,0 +1,23 @@ +from cx_const import DefaultActionsMapping, Light +from cx_core import LightController +from cx_core.integration import EventData + + +class SNZB01LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "single": Light.TOGGLE, # single click + "double": Light.ON_FULL_BRIGHTNESS, # double click + "long": Light.ON_MIN_BRIGHTNESS, # hold + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": Light.TOGGLE, # single click + "on": Light.ON_FULL_BRIGHTNESS, # double click + "off": Light.ON_MIN_BRIGHTNESS, # hold + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command diff --git a/appdaemon/apps/controllerx/cx_devices/tasmota.py b/appdaemon/apps/controllerx/cx_devices/tasmota.py new file mode 100644 index 0000000..322d285 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/tasmota.py @@ -0,0 +1,103 @@ +from cx_const import Cover, DefaultActionsMapping, Light, Switch, Z2MLight +from cx_core import ( + CoverController, + LightController, + SwitchController, + Z2MLightController, +) + + +class TasmotaButtonZ2MLightController(Z2MLightController): + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping: + return { + "TOGGLE": Z2MLight.TOGGLE, + "SINGLE": Z2MLight.TOGGLE, + "DOUBLE": Z2MLight.ON_MIN_BRIGHTNESS, + "TRIPLE": Z2MLight.SET_HALF_BRIGHTNESS, + "QUAD": Z2MLight.ON_FULL_BRIGHTNESS, + "PENTA": None, + "HOLD": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "CLEAR": Z2MLight.RELEASE, + } + + +class TasmotaSwitchZ2MLightController(Z2MLightController): + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping: + return { + "TOGGLE": Z2MLight.TOGGLE, + "ON": Z2MLight.ON, + "OFF": Z2MLight.OFF, + "HOLD": Z2MLight.SET_HALF_BRIGHTNESS, + } + + +class TasmotaButtonLightController(LightController): + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping: + return { + "TOGGLE": Light.TOGGLE, + "SINGLE": Light.TOGGLE, + "DOUBLE": Light.ON_MIN_BRIGHTNESS, + "TRIPLE": Light.SET_HALF_BRIGHTNESS, + "QUAD": Light.ON_FULL_BRIGHTNESS, + "PENTA": None, + "HOLD": Light.HOLD_BRIGHTNESS_TOGGLE, + "CLEAR": Light.RELEASE, + } + + +class TasmotaSwitchLightController(LightController): + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping: + return { + "TOGGLE": Light.TOGGLE, + "ON": Light.ON, + "OFF": Light.OFF, + "HOLD": Light.ON_MIN_MAX_BRIGHTNESS, + } + + +class TasmotaButtonSwitchController(SwitchController): + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping: + return { + "TOGGLE": Light.TOGGLE, + "SINGLE": Switch.TOGGLE, + "DOUBLE": Switch.ON, + "TRIPLE": Switch.OFF, + "QUAD": None, + "PENTA": None, + "HOLD": None, + "CLEAR": None, + } + + +class TasmotaSwitchSwitchController(SwitchController): + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping: + return { + "TOGGLE": Switch.TOGGLE, + "ON": Switch.ON, + "OFF": Switch.OFF, + "HOLD": None, + } + + +class TasmotaButtonCoverController(CoverController): + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping: + return { + "TOGGLE": Cover.TOGGLE_OPEN, + "SINGLE": Cover.TOGGLE_OPEN, + "DOUBLE": Cover.CLOSE, + "TRIPLE": None, + "QUAD": None, + "PENTA": None, + "HOLD": Cover.CLOSE, + "CLEAR": None, + } + + +class TasmotaSwitchCoverController(CoverController): + def get_tasmota_actions_mapping(self) -> DefaultActionsMapping: + return { + "TOGGLE": Cover.TOGGLE_OPEN, + "ON": Cover.OPEN, + "OFF": Cover.CLOSE, + "HOLD": Cover.TOGGLE_CLOSE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/terncy.py b/appdaemon/apps/controllerx/cx_devices/terncy.py new file mode 100644 index 0000000..dee3f6e --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/terncy.py @@ -0,0 +1,52 @@ +from cx_const import DefaultActionsMapping, Light, MediaPlayer +from cx_core import LightController, MediaPlayerController +from cx_core.integration import EventData + + +class TerncyPP01LightController(LightController): + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_single": Light.TOGGLE, + "button_double": Light.ON_FULL_BRIGHTNESS, + "button_triple": Light.ON_MIN_BRIGHTNESS, + "button_quadruple": Light.SET_HALF_BRIGHTNESS, + "button_quintuple": Light.SET_HALF_COLOR_TEMP, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class TerncySD01LightController(LightController): + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_single": Light.TOGGLE, + "button_double": Light.ON_FULL_BRIGHTNESS, + "button_triple": Light.ON_MIN_BRIGHTNESS, + "button_quadruple": Light.SET_HALF_BRIGHTNESS, + "button_quintuple": Light.SET_HALF_COLOR_TEMP, + "rotate_left": Light.CLICK_BRIGHTNESS_DOWN, + "rotate_right": Light.CLICK_BRIGHTNESS_UP, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command + + +class TerncySD01MediaPlayerController(MediaPlayerController): + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "button_single": MediaPlayer.PLAY_PAUSE, + "button_double": MediaPlayer.MUTE, + "button_triple": MediaPlayer.NEXT_TRACK, + "button_quadruple": MediaPlayer.PREVIOUS_TRACK, + "button_quintuple": MediaPlayer.NEXT_SOURCE, + "rotate_left": MediaPlayer.CLICK_VOLUME_DOWN, + "rotate_right": MediaPlayer.CLICK_VOLUME_UP, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + return command diff --git a/appdaemon/apps/controllerx/cx_devices/trust.py b/appdaemon/apps/controllerx/cx_devices/trust.py new file mode 100644 index 0000000..2caf213 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/trust.py @@ -0,0 +1,39 @@ +from cx_const import DefaultActionsMapping, Light, MediaPlayer, Z2MLight +from cx_core import LightController, MediaPlayerController, Z2MLightController + + +class ZYCT202LightController(LightController): + """ + This controller does not send hold action for on/off + """ + + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, + "up-press": Light.HOLD_BRIGHTNESS_UP, + "down-press": Light.HOLD_BRIGHTNESS_DOWN, + "off": Light.OFF, + "stop": Light.RELEASE, + } + + +class ZYCT202Z2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Z2MLight.ON, + "up-press": Z2MLight.HOLD_BRIGHTNESS_UP, + "down-press": Z2MLight.HOLD_BRIGHTNESS_DOWN, + "off": Z2MLight.OFF, + "stop": Z2MLight.RELEASE, + } + + +class ZYCT202MediaPlayerController(MediaPlayerController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": MediaPlayer.PLAY_PAUSE, + "up-press": MediaPlayer.HOLD_VOLUME_UP, + "down-press": MediaPlayer.HOLD_VOLUME_DOWN, + "off": MediaPlayer.PLAY_PAUSE, + "stop": MediaPlayer.RELEASE, + } diff --git a/appdaemon/apps/controllerx/cx_devices/tuya.py b/appdaemon/apps/controllerx/cx_devices/tuya.py new file mode 100644 index 0000000..6133c2c --- /dev/null +++ b/appdaemon/apps/controllerx/cx_devices/tuya.py @@ -0,0 +1,179 @@ +from cx_const import Cover, DefaultActionsMapping, Light, MediaPlayer, Z2MLight +from cx_core import ( + CoverController, + LightController, + MediaPlayerController, + Z2MLightController, +) +from cx_core.integration import EventData + + +class TS0044LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_single": Light.TOGGLE, + "1_double": Light.CLICK_BRIGHTNESS_UP, + "1_hold": Light.CLICK_BRIGHTNESS_DOWN, + "2_single": Light.TOGGLE, + "2_double": Light.CLICK_BRIGHTNESS_UP, + "2_hold": Light.CLICK_BRIGHTNESS_DOWN, + "3_single": Light.TOGGLE, + "3_double": Light.CLICK_BRIGHTNESS_UP, + "3_hold": Light.CLICK_BRIGHTNESS_DOWN, + "4_single": Light.TOGGLE, + "4_double": Light.CLICK_BRIGHTNESS_UP, + "4_hold": Light.CLICK_BRIGHTNESS_DOWN, + } + + +class TS0044FLightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.TOGGLE, + "off": Light.TOGGLE, + "brightness_step_up": Light.CLICK_BRIGHTNESS_UP, + "brightness_step_down": Light.CLICK_BRIGHTNESS_DOWN, + } + + +class TS0043LightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_single": Light.TOGGLE, + "1_double": Light.CLICK_BRIGHTNESS_UP, + "1_hold": Light.CLICK_BRIGHTNESS_DOWN, + "2_single": Light.TOGGLE, + "2_double": Light.CLICK_BRIGHTNESS_UP, + "2_hold": Light.CLICK_BRIGHTNESS_DOWN, + "3_single": Light.TOGGLE, + "3_double": Light.CLICK_BRIGHTNESS_UP, + "3_hold": Light.CLICK_BRIGHTNESS_DOWN, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_remote_button_short_press": Light.TOGGLE, + "1_remote_button_double_press": Light.CLICK_BRIGHTNESS_UP, + "1_remote_button_long_press": Light.CLICK_BRIGHTNESS_DOWN, + "2_remote_button_short_press": Light.TOGGLE, + "2_remote_button_double_press": Light.CLICK_BRIGHTNESS_UP, + "2_remote_button_long_press": Light.CLICK_BRIGHTNESS_DOWN, + "3_remote_button_short_press": Light.TOGGLE, + "3_remote_button_double_press": Light.CLICK_BRIGHTNESS_UP, + "3_remote_button_long_press": Light.CLICK_BRIGHTNESS_DOWN, + } + + def get_zha_action(self, data: EventData) -> str: + args: str = "" + # Command is {endpoint_id}_{command} + if len(data["args"]) > 0: + if isinstance(data["args"], list): + args = "_" + "_".join([str(d) for d in data["args"]]) + else: + args = args + f"{data['args']}" + command: str = f"{data['endpoint_id']}_{data['command']}{args}" + return command + + +class TS0043CoverController(CoverController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_single": Cover.OPEN, + "2_single": Cover.STOP, + "3_single": Cover.CLOSE, + } + + +class TuYaERS10TZBVKAALightController(LightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + # Command mode + "brightness_step_up": Light.CLICK_BRIGHTNESS_UP, + "brightness_step_down": Light.CLICK_BRIGHTNESS_DOWN, + "toggle": Light.TOGGLE, + "hue_move": Light.HOLD_BRIGHTNESS_TOGGLE, + "hue_stop": Light.RELEASE, + "color_temperature_step_up": Light.CLICK_COLOR_UP, + "color_temperature_step_down": Light.CLICK_COLOR_DOWN, + # Event mode + "rotate_left": Light.CLICK_BRIGHTNESS_DOWN, + "rotate_right": Light.CLICK_BRIGHTNESS_UP, + "single": Light.TOGGLE, + "double": Light.ON_FULL_BRIGHTNESS, + "hold": Light.ON_MIN_BRIGHTNESS, + } + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "toggle": Light.TOGGLE, + "step_brightness": Light.BRIGHTNESS_FROM_CONTROLLER_STEP, + "step_color_temp": Light.COLORTEMP_FROM_CONTROLLER_STEP, + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + if command == "step": + return "step_brightness" + return command + + +class TuYaERS10TZBVKAAZ2MLightController(Z2MLightController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + # Command mode + "brightness_step_up": Z2MLight.CLICK_BRIGHTNESS_UP, + "brightness_step_down": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "toggle": Z2MLight.TOGGLE, + "hue_move": Z2MLight.HOLD_BRIGHTNESS_TOGGLE, + "hue_stop": Z2MLight.RELEASE, + "color_temperature_step_up": Z2MLight.CLICK_COLOR_TEMP_UP, + "color_temperature_step_down": Z2MLight.CLICK_COLOR_TEMP_DOWN, + # Event mode + "rotate_left": Z2MLight.CLICK_BRIGHTNESS_DOWN, + "rotate_right": Z2MLight.CLICK_BRIGHTNESS_UP, + "single": Z2MLight.TOGGLE, + "double": Z2MLight.ON_FULL_BRIGHTNESS, + "hold": Z2MLight.ON_MIN_BRIGHTNESS, + } + + +class TuYaERS10TZBVKAAMediaPlayerController(MediaPlayerController): + def get_z2m_actions_mapping(self) -> DefaultActionsMapping: + return { + # Command mode + "brightness_step_up": MediaPlayer.CLICK_VOLUME_UP, + "brightness_step_down": MediaPlayer.CLICK_VOLUME_DOWN, + "toggle": MediaPlayer.PLAY_PAUSE, + "hue_move": MediaPlayer.HOLD_VOLUME_UP, + "hue_stop": Z2MLight.RELEASE, + "color_temperature_step_up": MediaPlayer.NEXT_TRACK, + "color_temperature_step_down": MediaPlayer.PREVIOUS_TRACK, + # Event mode + "rotate_left": MediaPlayer.CLICK_VOLUME_DOWN, + "rotate_right": MediaPlayer.CLICK_VOLUME_UP, + "single": MediaPlayer.PLAY_PAUSE, + "double": MediaPlayer.NEXT_TRACK, + "hold": MediaPlayer.PREVIOUS_TRACK, + } + + +class TS0042LightController(LightController): + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "1_remote_button_short_press": Light.ON, + "1_remote_button_double_press": Light.CLICK_BRIGHTNESS_UP, + "1_remote_button_long_press": Light.ON_FULL_BRIGHTNESS, + "2_remote_button_short_press": Light.OFF, + "2_remote_button_double_press": Light.ON_MIN_BRIGHTNESS, + } + + def get_zha_action(self, data: EventData) -> str: + args: str = "" + # Command is {endpoint_id}_{command} + if len(data["args"]) > 0: + if isinstance(data["args"], list): + args = "_" + "_".join([str(d) for d in data["args"]]) + else: + args = args + f"{data['args']}" + command: str = f"{data['endpoint_id']}_{data['command']}{args}" + return command diff --git a/appdaemon/apps/controllerx/cx_helper.py b/appdaemon/apps/controllerx/cx_helper.py new file mode 100644 index 0000000..15c4862 --- /dev/null +++ b/appdaemon/apps/controllerx/cx_helper.py @@ -0,0 +1,33 @@ +import importlib +import os +import pkgutil +from typing import Any + + +def _import_modules(file_dir: str, package: str) -> None: + pkg_dir = os.path.dirname(file_dir) + for _, name, ispkg in pkgutil.iter_modules([pkg_dir]): + if ispkg: + _import_modules(pkg_dir + "/" + name + "/__init__.py", package + "." + name) + else: + importlib.import_module("." + name, package) + + +def _all_subclasses(cls: type[Any]) -> list[type[Any]]: + return list( + set(type.__subclasses__(cls)).union( + [s for c in type.__subclasses__(cls) for s in _all_subclasses(c)] + ) + ) + + +def get_classes(file_: str, package_: str, class_: type[Any]) -> list[type[Any]]: + _import_modules(file_, package_) + subclasses = _all_subclasses(class_) + subclasses = [cls_ for cls_ in subclasses if f"{package_}." in cls_.__module__] + return subclasses + + +def get_instances(file_: str, package_: str, class_: type[Any]) -> list[Any]: + classes = get_classes(file_, package_, class_) + return [cls_() for cls_ in classes] diff --git a/appdaemon/apps/controllerx/cx_version.py b/appdaemon/apps/controllerx/cx_version.py new file mode 100644 index 0000000..47290bf --- /dev/null +++ b/appdaemon/apps/controllerx/cx_version.py @@ -0,0 +1 @@ +__version__ = "v5.2.3" diff --git a/automations.yaml b/automations.yaml new file mode 100644 index 0000000..34445f0 --- /dev/null +++ b/automations.yaml @@ -0,0 +1,386 @@ +- id: '1726037322181' + alias: WC Sensor Light + description: '' + use_blueprint: + path: Blackshome/sensor-light.yaml + input: + motion_trigger: + - binary_sensor.wc_bewegungsmelder_occupancy + light_switch: + entity_id: light.wc_decke + include_sun: sun_disabled + night_lights: + entity_id: light.wc_decke + night_light_brightness: 30 +- id: '1726044464634' + alias: Badezimmer Sensor Light + description: '' + use_blueprint: + path: Blackshome/sensor-light.yaml + input: + motion_trigger: + - binary_sensor.badezimmer_bewegungsmelder_occupancy + light_switch: + entity_id: light.badezimmer_decke + include_sun: sun_disabled + night_lights: + entity_id: light.badezimmer_decke + night_light_brightness: 30 + time_delay: 15 +- id: '1726159872511' + alias: Wassermeldung + description: '' + use_blueprint: + path: Blackshome/state-notifications-and-actions.yaml + input: + start_trigger_state_entity: + - binary_sensor.wc_wassermelder_water_leak + - binary_sensor.technikraum_wassermelder_water_leak + - binary_sensor.kuche_wassermelder_water_leak + start_notify_device: + - 5fb7463ffcf5af63b5b871d88de9384d + - b66802e4b09680de467594c871f22d67 + start_title: WASSERALARM + start_message: '{{ states[trigger.entity_id].attributes.friendly_name }} hat + Feuchtigkeit festgestellt.' + start_notify_data: + - high_priority + - sticky + start_trigger_state: 'on' + include_start_notify: enable_start_notify_options +- id: '1726164370652' + alias: Büro Anwesenheit + description: '' + use_blueprint: + path: Blackshome/sensor-light.yaml + input: + motion_trigger: + - binary_sensor.buro_anwesenheitssensor_raum + light_switch: + entity_id: + - light.buro_decke + time_delay: 0.5 + include_sun: sun_enabled + include_light_colour_control: disable_colour_control + ambient_light_sensor: sensor.buro_anwesenheitssensor_light_sensor_light_level + ambient_light_options: ambient_light_option_enabled + include_ambient: ambient_enabled + ambient_light_high_value: 60 +- id: '1726175048536' + alias: Low Battery + description: '' + use_blueprint: + path: Blackshome/low-battery-notifications-and-actions.yaml + input: + include_time: time_enabled + include_easy_notify: disabled_easy_notify + include_persistent_notification: disabled_persistent_notification + notify_device: [] + time: '16:00:00' + exclude_sensors: + entity_id: + - sensor.powerocean_bpack1_bpsoc + - sensor.powerocean_bpack2_bpsoc + - sensor.powerocean_bpsoc_2 + - sensor.pixel_8_pro_car_battery + - sensor.karolina_tracker_batterie + include_custom_actions: enable_custom_actions + custom_actions: + - action: notify.send_message + data: + title: Low battery warning + message: '{{sensors}}' + entity_id: notify.home_assistant +- id: '1726304621477' + alias: Speis Sensor Light + description: '' + use_blueprint: + path: Blackshome/sensor-light.yaml + input: + motion_trigger: + - binary_sensor.speis_bewegungsmelder_occupancy + light_switch: + entity_id: light.speis_decke + time_delay: 0.5 +- id: '1727623123034' + alias: Badheizung + description: '' + use_blueprint: + path: Blackshome/timer.yaml + input: + entity_input: + entity_id: switch.badezimmer_handtuchheizung + trigger_one_time: 05:15:00 + trigger_one_duration: + hours: 0 + minutes: 15 + seconds: 0 + include_weekdays: weekday_enabled + weekday_options: + - mon + - tue + - wed + - thu + - fri + timer_helper: timer.timer_badezimmer_heizung +- id: '1727976866531' + alias: Reminder Abfallkalender + description: '' + use_blueprint: + path: Blackshome/calendar-notifications-and-actions.yaml + input: + calendar: calendar.abfallkalender + include_calendar_trigger: disable_key_word + start_calendar_offset: + hours: -12 + minutes: 0 + seconds: 0 + days: 0 + start_notify_device: + - 5fb7463ffcf5af63b5b871d88de9384d + - b66802e4b09680de467594c871f22d67 + include_start_notify: enable_start_notify_options +- id: '1729356542767' + alias: Nachtmodus Button + description: '' + triggers: + - trigger: state + entity_id: + - input_button.nachtmodus + conditions: [] + actions: + - action: light.turn_off + metadata: {} + data: {} + target: + area_id: + - badezimmer + - buro + - esszimmer + - gang + - gaderobe + - speis + - technikraum + - wc + - wohnzimmer + - kuche + - terrasse + - eingang + - gastezimmer + - grillplatz + - ruheplatz + mode: single +- id: '1729414838563' + alias: Geschirrspüler Nachtbetrieb + description: '' + triggers: + - trigger: state + entity_id: + - binary_sensor.cheapest_window_dishwasher + to: 'on' + conditions: + - condition: state + entity_id: input_boolean.geschirrspuler_uber_nacht + state: 'on' + actions: + - type: turn_on + device_id: 04ff0a4c8bbdb791bc2f414ef87557e0 + entity_id: 37e7a92fe454c1a3a9eb60ff311e6c85 + domain: switch + - device_id: 04ff0a4c8bbdb791bc2f414ef87557e0 + domain: select + entity_id: f15c038737129ff56b3fd0dbdf3f23db + type: select_option + option: Dishcare.Dishwasher.Program.Eco50 + - device_id: 04ff0a4c8bbdb791bc2f414ef87557e0 + domain: button + entity_id: f925a6bf1e774246be1c06f6d9fd7fe6 + type: press + enabled: true + - action: input_boolean.turn_off + metadata: {} + data: {} + target: + entity_id: input_boolean.geschirrspuler_uber_nacht + mode: single +- id: '1731252691843' + alias: Test sugbna + description: '' + triggers: [] + conditions: [] + actions: + - action: notify.send_message + data: + message: 'Reminder: Have you considered frogs?' + entity_id: notify.home_assistant + mode: single +- id: '1732639576586' + alias: Rauchmeldeung + description: '' + use_blueprint: + path: Blackshome/state-notifications-and-actions.yaml + input: + start_trigger_state_entity: + - binary_sensor.buro_rauchmelder_smoke + - binary_sensor.wohnzimmer_rauchmelder_smoke + - binary_sensor.gastezimmer_rauchmelder_smoke + - binary_sensor.technikraum_rauchmelder_smoke + - binary_sensor.kinderzimmer_rauchmelder_smoke + - binary_sensor.schlafzimmer_rauchmelder_smoke + start_notify_device: + - 5fb7463ffcf5af63b5b871d88de9384d + - b66802e4b09680de467594c871f22d67 + start_title: RAUCHALARM + start_message: '{{ states[trigger.entity_id].attributes.friendly_name }} hat + Rauch festgestellt.' + start_notify_data: + - high_priority + - sticky + start_trigger_state: 'on' + include_start_notify: enable_start_notify_options +- id: '1734279266836' + alias: Technikraum Bewegungsmelder + description: '' + use_blueprint: + path: Blackshome/sensor-light.yaml + input: + motion_trigger: + - binary_sensor.technikraum_bewegungsmelder_occupancy + light_switch: + entity_id: light.technikraum_decke + time_delay: 0.5 +- id: '1746210944619' + alias: Wetterwarnung DWD + description: '' + triggers: + - entity_id: sensor.mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_aktuelle_warnstufe + above: 0 + trigger: numeric_state + conditions: [] + actions: + - data: + message: '{{ state_attr(''mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_aktuelle_warnstufe'', + ''warning_1_description'') }} Von: {{ as_timestamp(state_attr(''mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_aktuelle_warnstufe'', + ''warning_1_start'')) | timestamp_custom(''%d.%m.%Y, %H:%M'')}} Uhr Bis: {{ + as_timestamp(state_attr(''mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_aktuelle_warnstufe'', + ''warning_1_end'')) | timestamp_custom(''%d.%m.%Y, %H:%M'')}} Uhr' + title: '{{ state_attr(''sensor.mitgliedsgemeinde_in_verwaltungsgemeinschaft_welden_aktuelle_warnstufe'', + ''warning_1_name'') }} ⛈️' + action: notify.send_message + entity_id: notify.home_assistant + mode: single +- id: '1746266479215' + alias: Kinderzimmer Sensor Light + description: '' + use_blueprint: + path: Blackshome/sensor-light.yaml + input: + motion_trigger: + - binary_sensor.kinderzimmer_bewegungsmelder_occupancy + light_switch: + entity_id: light.garderobe_decke + include_light_control: + - use_brightness + light_brightness: 100 + include_ambient: ambient_disabled + ambient_light_sensor: sensor.kinderzimmer_rauchmelder_illuminance + ambient_light_options: ambient_light_option_enabled + time_delay: 0.5 + ambient_light_value: 20 + include_sun: sun_disabled + after_time: '18:00:00' + before_time: 08:00:00 + include_time: time_disabled + include_light_colour_control: disable_colour_control + light_rgb_colour: + - 117 + - 53 + - 21 + light_transition_on: 0 + light_transition_off: 0 + boolean_scenes_scripts: input_boolean.garderobe_helper + light_colour_temperature: 2000 +- id: '1776921027487' + alias: Tracker Reminder + description: '' + use_blueprint: + path: Blackshome/low-battery-notifications-and-actions.yaml + input: + battery_level: 29 + custom_group: + entity_id: + - sensor.karolina_tracker_batterie_history + include_easy_notify: enable_easy_notify + notify_device: + - b66802e4b09680de467594c871f22d67 + - 5fb7463ffcf5af63b5b871d88de9384d + notify_title: Tracker laden! + sensor_selection: enable_battery_sensors_custom_group + notify_data: [] + time: '17:00:00' + custom_group_battery_level: 29 + include_persistent_notification: enable_persistent_notification +- id: '1777224364774' + alias: Befeuchter ein – Luftfeuchtigkeit unter 50% + description: '' + triggers: + - trigger: numeric_state + entity_id: sensor.wohnzimmer_rauchmelder_humidity + below: 50 + conditions: [] + actions: + - action: humidifier.set_mode + target: + entity_id: + - humidifier.evaporative_humidifier_05de + data: + mode: low + mode: single +- id: '1777224379805' + alias: Befeuchter aus – Luftfeuchtigkeit über 60% + description: '' + triggers: + - trigger: numeric_state + entity_id: sensor.wohnzimmer_rauchmelder_humidity + above: 60 + conditions: + - condition: state + entity_id: humidifier.evaporative_humidifier_05de + state: 'on' + actions: + - action: humidifier.turn_off + target: + entity_id: humidifier.evaporative_humidifier_05de + mode: single +- id: '1777224493981' + alias: Befeuchter – Wasserstand niedrig + description: '' + triggers: + - trigger: state + entity_id: sensor.evaporative_humidifier_05de_wasserstand + to: niedrig + conditions: [] + actions: + - action: notify.notify + data: + title: "\U0001F4A7 Befeuchter Wasser leer" + message: Der Wasserstand des Befeuchters ist niedrig – bitte nachfüllen. + mode: single +- id: '1777224986079' + alias: BYD Dolphin Position aktualisieren + description: '' + triggers: + - trigger: state + entity_id: + - sensor.byd_dolphin_60_5_kwh_abrp_latitude + - sensor.byd_dolphin_60_5_kwh_abrp_longitude + conditions: [] + actions: + - action: device_tracker.see + data: + dev_id: byd_dolphin + gps: + - '{{ states(''sensor.byd_dolphin_60_5_kwh_abrp_latitude'') | float }}' + - '{{ states(''sensor.byd_dolphin_60_5_kwh_abrp_longitude'') | float }}' + gps_accuracy: 10 + mode: single diff --git a/blueprints/automation/Blackshome/calendar-notifications-and-actions.yaml b/blueprints/automation/Blackshome/calendar-notifications-and-actions.yaml new file mode 100644 index 0000000..0926a79 --- /dev/null +++ b/blueprints/automation/Blackshome/calendar-notifications-and-actions.yaml @@ -0,0 +1,1324 @@ +blueprint: + name: Calendar Notifications & Actions + description: "# \U0001F4C5 Calendar Notifications & Actions\n**Version: 1.7**\n\nTransform + Your Calendar: Turn Events Into Notifications and Actions! \U0001F4C5\U0001F514⚙️\n\n**If + you like my blueprints, and would like to show your support or just say thank + you?** [Click Here](https://www.paypal.com/donate/?hosted_button_id=WAZS3QSDTPGA8) + \U0001F642\n
The Automation Process: \U0001F448\n\n + \ - **Calendar Event Triggering:**\n - Trigger based on a keyword in a calendar + event or on all events in a selected calendar.\n - Choose to trigger at the + start or end of a calendar event with time offsets.\n\n - **Notification Options:**\n + \ - Option to send notifications to one or multiple devices when triggered.\n + \ - Action Buttons (Optional):\n - Notifications can include up to 3 + action buttons.\n - Each action button is linked to a designated custom + action group.\n - Option to send a confirmation message when an action + button is pressed.\n - The automation will continue after a specified time + delay if an action button is not pressed.\n - Compatible with Apple iOS and + Android devices.\n\n - **Custom Actions:**\n - Option to define up to 3 different + action groups when triggered.\n - Each action group can contain as many actions + as desired.\n - Each action group can be linked to a notification action button + or executed automatically when the automation is triggered.\n
\n\nNeed + help? \n- FAQ: [Click Here](https://community.home-assistant.io/t/612326/2)\n- + The Settings & Best Practice Guidelines: [Click Here](https://community.home-assistant.io/t/612326/3)\n- + Community Support Including Updates: [Click Here](https://community.home-assistant.io/t/612326)\n\nRequired + = *\n" + domain: automation + input: + trigger_settings: + name: Trigger * + icon: mdi:cog-outline + description: ⚠️ Calendar events must be added at least 15 minutes before the + trigger time, otherwise the automation will not execute. + collapsed: true + input: + calendar: + name: Calendar * + description: Select the calendar to be used for the automation. + default: {} + selector: + entity: + filter: + - domain: + - calendar + multiple: false + include_calendar_trigger: + name: Use The Calendar Trigger - Key Word (Optional) + description: 'Choose whether to use a "Keyword" to trigger the automation. + + + **Note** - If you choose not to use a "Keyword", all calendar events will + trigger the automation.' + default: enable_key_word + selector: + select: + options: + - label: Don't use "Key Word" in calendar + value: disable_key_word + - label: Use "Key Word" in calendar + value: enable_key_word + multiple: false + custom_value: false + sort: false + calendar_trigger: + name: Calendar Trigger - Key Word In Calendar Summary & / or Description + description: 'Input a single keyword or, for multiple keywords, use a comma-separated + list. Enter these keywords into the calendar event summary or description + to trigger this automation. Keywords can include letters, numbers, and + symbols. Be mindful of your choices, as any event containing these keywords + will trigger the automation. + + + To prevent false triggers, it''s recommended to use **@** or **#** followed + by the keyword in the calendar event (e.g., **@bins** or **#bins**) or + for multiple keywords (e.g., **@bins, @rubbish**). + + + For more information on how to use Trigger Keywords, [Click Here](https://community.home-assistant.io/t/612326/4)' + default: [] + selector: + text: {} + start_calendar_offset: + name: Calendar Start - Offset + description: You can specify an offset from the start of the calendar event + for when the trigger should be activated. To trigger the automation before + the start of the event, use a negative sign "-" followed by the time value. + default: + days: 0 + hours: 0 + minutes: 0 + seconds: 0 + selector: + duration: + enable_day: true + end_calendar_offset: + name: Calendar End - Offset + description: You can specify an offset from the end of the calendar event + for when the trigger should be activated. To trigger the automation before + the end of the event, use a negative sign "-" followed by the time value. + default: + days: 0 + hours: 0 + minutes: 0 + seconds: 0 + selector: + duration: + enable_day: true + start_notify_settings: + name: Start Notify + icon: mdi:calendar-start-outline + collapsed: true + input: + include_start_notify: + name: Use The Start Notify Options (Optional) + description: Select "Enable" if you would like to receive notifications + at the start of the calendar event. + default: disable_start_notify_options + selector: + select: + options: + - label: Enable Start Notify Options + value: enable_start_notify_options + - label: Disable Start Notify Options + value: disable_start_notify_options + multiple: false + custom_value: false + sort: false + start_notify_device: + name: Start Notify - Devices To Notify + description: Select the devices to receive notifications at the start of + a calendar event. + default: [] + selector: + device: + filter: + - integration: mobile_app + multiple: true + start_title: + name: Start Notify - Title + description: Specify the notification title for the start of the calendar + event. + default: '{{ trigger.calendar_event.summary }}' + selector: + text: {} + start_message: + name: Start Notify - Message + description: Specify the notification message for the start of the calendar + event. + default: '{{ trigger.calendar_event.description }}' + selector: + text: {} + start_notify_interruption_level: + name: Interruption Level - iOS Only + description: 'On devices running iOS 15 and later, you can configure the + interruption level for your notifications to ensure they are delivered + according to your preferences. Choose the desired interruption level from + the dropdown list. Critical and time-sensitive notifications must be turned + ON within the Home Assistant App, and time-sensitive notifications must + be allowed in your Focus settings. + + + For more information on interruption levels [Click Here](https://community.home-assistant.io/t/612326/155)' + default: active + selector: + select: + mode: dropdown + options: + - label: Default + value: active + - label: Critical Notifications + value: critical + - label: Time Sensitive Notifications + value: time-sensitive + - label: Quiet Notifications Without Waking Screen + value: passive + multiple: false + custom_value: false + sort: false + start_notify_data: + name: Android Only Options (Optional) + description: '**High Priority** notifications ensure that you receive important + alerts immediately. These notifications bypass normal delivery delays + and are delivered instantly to your device. + + + **Sticky Notification** ensure that important alerts are not dismissed + when selected. They remain on your notification screen until you actively + clear or swipe them away, ensuring that critical information stays visible + until addressed. + + + **Notification Channel** allow you to easily organize different notification + settings, including notification sounds, vibrations, and other device-specific + features. If you choose this option, please enter your desired channel + name below.' + default: [] + selector: + select: + multiple: true + options: + - label: High Priority + value: high_priority + - label: Sticky Notification + value: sticky + - label: Notification Channel + value: channel + custom_value: false + sort: false + start_notify_channel: + name: Notification Channel - Android Only + description: 'If you have chosen to use a notification channel, simply enter + the name of an existing channel to apply its pre-configured settings. + If you don''t have a channel yet, you can create a new one by entering + a name here. When your automation sends the first notification, it will + create the channel on your device. After the channel is created, you can + customize its notification settings to your preference directly on your + device. + + + For more information on notification channels [Click Here](https://community.home-assistant.io/t/612326/155)' + default: [] + selector: + text: {} + start_action_settings: + name: Start Actions Buttons + icon: mdi:gesture-tap + collapsed: true + input: + include_start_action_buttons: + name: Start Notify - Action Buttons Options (Optional) + description: 'Enable the action buttons you would like to use. These buttons + will be shown in the notification, allowing you to perform actions when + clicked. + + + **NOTE** - You do not need to select any notification action buttons to + execute a start action. If you prefer to run a start action without using + any action buttons, simply enable any ''Start Auto Action'' located in + the ''Start Actions'' section below. + + + ⚠️ Enter the names for all enabled **Action Buttons** below, including + the **CANCEL Action Button**. Missing any button names may prevent the + automation from functioning correctly and could result in not receiving + the notification.' + default: [] + selector: + select: + options: + - label: Enable action button 1 + value: enable_start_action_button_1 + - label: Enable action button 2 + value: enable_start_action_button_2 + - label: Enable action button 3 + value: enable_start_action_button_3 + multiple: true + custom_value: false + sort: false + start_action_button_1: + name: Start Notify - Action Button 1 + description: Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "Start - Action + 1" input located in the 'Start Actions' section below. + default: [] + selector: + text: {} + start_action_button_2: + name: Start Notify - Action Button 2 + description: Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "Start - Action + 2" input located in the 'Start Actions' section below. + default: [] + selector: + text: {} + start_action_button_3: + name: Start Notify - Action Button 3 + description: Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "Start - Action + 3" input located in the 'Start Actions' section below. + default: [] + selector: + text: {} + start_action_button_stop: + name: Start Notify - CANCEL Action Button + description: Enter the CANCEL button name to be displayed in the notification. + This button will cancel all actions and stop the automation. It will be + automatically included in the notification if you have enabled an action + button above. + default: [] + selector: + text: {} + start_time_delay: + name: Start Notify - Time Delay + description: Specify the time delay after which the automation will resume + its execution if an action button has not been pressed. + default: + hours: 0 + minutes: 30 + seconds: 0 + selector: + duration: {} + include_start_action_button_confirmation: + name: Start Notify - Action Button Confirmation Option (Optional) + description: 'Select "Enable" if you would like to notify all devices when + an action button has been pressed. This is useful when notifying multiple + devices, informing others that an action has been selected. + + + ⚠️ If Action Button Confirmation is enabled, you must include the **Confirmation + Title**, **Confirmation Message** for each enabled action button, **Cancel + Confirmation Message**, and **Time Delay Confirmation Message**. Missing + any of these may prevent the automation from functioning correctly and + could result in not receiving the notification.' + default: disable_start_action_button_confirmation + selector: + select: + options: + - label: Enable Action Button Confirmation + value: enable_start_action_button_confirmation + - label: Disable Action Button Confirmation + value: disable_start_action_button_confirmation + multiple: false + custom_value: false + sort: false + start_action_button_confirmation_title: + name: Start Notify - Action Button Confirmation Title + description: Specify the notification title when an action button is pressed + or when the time delay has passed. + default: [] + selector: + text: {} + start_action_button_confirmation_message: + name: Start Notify - Action Button 1 Confirmation Message + description: Specify the notification message when action button 1 is pressed. + default: [] + selector: + text: {} + start_action_button_2_confirmation_message: + name: Start Notify - Action Button 2 Confirmation Message + description: Specify the notification message when action button 2 is pressed. + default: [] + selector: + text: {} + start_action_button_3_confirmation_message: + name: Start Notify - Action Button 3 Confirmation Message + description: Specify the notification message when action button 3 is pressed. + default: [] + selector: + text: {} + start_action_button_stop_confirmation_message: + name: Start Notify - Action Button Cancel Confirmation Message + description: Specify the notification message when the cancel button is + pressed. + default: [] + selector: + text: {} + start_time_delay_confirmation_message: + name: Start Notify - Time Delay Confirmation Message + description: Specify the notification message when the time delay has passed. + default: [] + selector: + text: {} + start_auto_action_settings: + name: Start Actions + icon: mdi:code-tags + collapsed: true + input: + include_start_auto_actions: + name: Use The Start Auto Action Options (Optional) + description: 'Enabling the auto option will allow the "Start Actions" below + to run when the calendar event is triggered. + + + When using action buttons in a notification, it is not necessary to enable + an auto action for the start actions to work, as the action button pressed + in a notification will run the selected action. However, if you miss the + notification and don''t press an action button, and after the time delay + has passed, enabling a start auto action option ensures the action still + runs.' + default: [] + selector: + select: + options: + - label: Enable start auto action 1 + value: enable_start_action_1 + - label: Enable start auto action 2 + value: enable_start_action_2 + - label: Enable start auto action 3 + value: enable_start_action_3 + multiple: true + custom_value: false + sort: false + start_action_1: + name: Start Action - Action 1 + description: Enter the actions you would like to run at the start of a calendar + event. These actions are also linked to the "Start Notify - Action Button + 1" input located in the 'Start Actions Buttons' section above. + default: [] + selector: + action: {} + start_action_2: + name: Start Action - Action 2 + description: Enter the actions you would like to run at the start of a calendar + event. These actions are also linked to the "Start Notify - Action Button + 2" input located in the 'Start Actions Buttons' section above. + default: [] + selector: + action: {} + start_action_3: + name: Start Action - Action 3 + description: Enter the actions you would like to run at the start of a calendar + event. These actions are also linked to the "Start Notify - Action Button + 3" input located in the 'Start Actions Buttons' section above. + default: [] + selector: + action: {} + end_notify_settings: + name: End Notify + icon: mdi:calendar-end-outline + collapsed: true + input: + include_end_notify: + name: Use The End Notify Options (Optional) + description: Select "Enable" if you would like to receive notifications + at the end of the calendar event. + default: disable_end_notify_options + selector: + select: + options: + - label: Enable End Notify Options + value: enable_end_notify_options + - label: Disable End Notify Options + value: disable_end_notify_options + multiple: false + custom_value: false + sort: false + end_notify_device: + name: End Notify - Devices To Notify + description: Select the devices to receive notifications at the end of a + calendar event. + default: [] + selector: + device: + filter: + - integration: mobile_app + multiple: true + end_title: + name: End Notify - Title + description: Specify the notification title for the end of the calendar + event. + default: '{{ trigger.calendar_event.summary }}' + selector: + text: {} + end_message: + name: End Notify - Message + description: Specify the notification message for the end of the calendar + event. + default: '{{ trigger.calendar_event.description }}' + selector: + text: {} + end_notify_interruption_level: + name: Interruption Level - iOS Only + description: 'On devices running iOS 15 and later, you can configure the + interruption level for your notifications to ensure they are delivered + according to your preferences. Choose the desired interruption level from + the dropdown list. Critical and time-sensitive notifications must be turned + ON within the Home Assistant App, and time-sensitive notifications must + be allowed in your Focus settings. + + + For more information on interruption levels [Click Here](https://community.home-assistant.io/t/612326/155)' + default: active + selector: + select: + mode: dropdown + options: + - label: Default + value: active + - label: Critical Notifications + value: critical + - label: Time Sensitive Notifications + value: time-sensitive + - label: Quiet Notifications Without Waking Screen + value: passive + multiple: false + custom_value: false + sort: false + end_notify_data: + name: Android Only Options (Optional) + description: '**High Priority** notifications ensure that you receive important + alerts immediately. These notifications bypass normal delivery delays + and are delivered instantly to your device. + + + **Sticky Notification** ensure that important alerts are not dismissed + when selected. They remain on your notification screen until you actively + clear or swipe them away, ensuring that critical information stays visible + until addressed. + + + **Notification Channel** allow you to easily organize different notification + settings, including notification sounds, vibrations, and other device-specific + features. If you choose this option, please enter your desired channel + name below.' + default: [] + selector: + select: + multiple: true + options: + - label: High Priority + value: high_priority + - label: Sticky Notification + value: sticky + - label: Notification Channel + value: channel + custom_value: false + sort: false + end_notify_channel: + name: Notification Channel - Android Only + description: 'If you have chosen to use a notification channel, simply enter + the name of an existing channel to apply its pre-configured settings. + If you don''t have a channel yet, you can create a new one by entering + a name here. When your automation sends the first notification, it will + create the channel on your device. After the channel is created, you can + customize its notification settings to your preference directly on your + device. + + + For more information on notification channels [Click Here](https://community.home-assistant.io/t/612326/155)' + default: [] + selector: + text: {} + end_action_settings: + name: End Actions Buttons + icon: mdi:gesture-double-tap + collapsed: true + input: + include_end_action_buttons: + name: End Notify - Action Buttons Options (Optional) + description: 'Enable the action buttons you would like to use. These buttons + will be shown in the notification, allowing you to perform actions when + clicked. + + + **NOTE** - You do not need to select any notification action buttons to + execute an end action. If you prefer to run an end action without using + any action buttons, simply enable any ''End Auto Action'' located in the + ''End Actions'' section below. + + + ⚠️ Enter the names for all enabled **Action Buttons** below, including + the **CANCEL Action Button**. Missing any button names may prevent the + automation from functioning correctly and could result in not receiving + the notification.' + default: [] + selector: + select: + options: + - label: Enable action button 1 + value: enable_end_action_button_1 + - label: Enable action button 2 + value: enable_end_action_button_2 + - label: Enable action button 3 + value: enable_end_action_button_3 + multiple: true + custom_value: false + sort: false + end_action_button_1: + name: End Notify - Action Button 1 + description: Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "End - Action 1" + input located in the 'End Actions' section below. + default: [] + selector: + text: {} + end_action_button_2: + name: End Notify - Action Button 2 + description: Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "End - Action 2" + input located in the 'End Actions' section below. + default: [] + selector: + text: {} + end_action_button_3: + name: End Notify - Action Button 3 + description: Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "End - Action 3" + input located in the 'End Actions' section below. + default: [] + selector: + text: {} + end_action_button_stop: + name: End Notify - CANCEL Action Button + description: Enter the CANCEL button name to be displayed in the notification. + This button will cancel all actions and stop the automation. It will be + automatically included in the notification if you have enabled an action + button above. + default: [] + selector: + text: {} + end_time_delay: + name: End Notify - Time Delay + description: Specify the time delay after which the automation will resume + its execution if an action button has not been pressed. + default: + hours: 0 + minutes: 30 + seconds: 0 + selector: + duration: {} + include_end_action_button_confirmation: + name: End Notify - Action Button Confirmation Option (Optional) + description: 'Select "Enable" if you would like to notify all devices when + an action button has been pressed. This is useful when notifying multiple + devices, informing others that an action has been selected. + + + ⚠️ If Action Button Confirmation is enabled, you must include the **Confirmation + Title**, **Confirmation Message** for each enabled action button, **Cancel + Confirmation Message**, and **Time Delay Confirmation Message**. Missing + any of these may prevent the automation from functioning correctly and + could result in not receiving the notification.' + default: disable_end_action_button_confirmation + selector: + select: + options: + - label: Enable Action Button Confirmation + value: enable_end_action_button_confirmation + - label: Disable Action Button Confirmation + value: disable_end_action_button_confirmation + multiple: false + custom_value: false + sort: false + end_action_button_confirmation_title: + name: End Notify - Action Button Confirmation Title + description: Specify the notification title when an action button is pressed + or when the time delay has passed. + default: [] + selector: + text: {} + end_action_button_confirmation_message: + name: End Notify - Action Button 1 Confirmation Message + description: Specify the notification message when action button 1 is pressed. + default: [] + selector: + text: {} + end_action_button_2_confirmation_message: + name: End Notify - Action Button 2 Confirmation Message + description: Specify the notification message when action button 2 is pressed. + default: [] + selector: + text: {} + end_action_button_3_confirmation_message: + name: End Notify - Action Button 3 Confirmation Message + description: Specify the notification message when action button 3 is pressed. + default: [] + selector: + text: {} + end_action_button_stop_confirmation_message: + name: End Notify - Action Button Cancel Confirmation Message + description: Specify the notification message when the cancel button is + pressed. + default: [] + selector: + text: {} + end_time_delay_confirmation_message: + name: End Notify - Time Delay Confirmation Message + description: Specify the notification message when the time delay has passed. + default: [] + selector: + text: {} + end_auto_action_settings: + name: End Actions + icon: mdi:code-tags + collapsed: true + input: + include_end_auto_actions: + name: Use The End Auto Action Options (Optional) + description: 'Enabling the auto option will allow the "End Actions" below + to run when the calendar event is triggered. + + + When using action buttons in a notification, it is not necessary to enable + an auto action for the end actions to work, as the action button pressed + in a notification will run the selected action. However, if you miss the + notification and don''t press an action button, and after the time delay + has passed, enabling an end auto action option ensures the action still + runs.' + default: [] + selector: + select: + options: + - label: Enable auto end action 1 option + value: enable_end_action_1 + - label: Enable auto end action 2 option + value: enable_end_action_2 + - label: Enable auto end action 3 option + value: enable_end_action_3 + multiple: true + custom_value: false + sort: false + end_action_1: + name: End Action - Action 1 + description: Enter the actions you would like to run at the end of a calendar + event. These actions are also linked to the "End Notify - Action Button + 1" input located in the 'End Actions Buttons' section above. + default: [] + selector: + action: {} + end_action_2: + name: End Action - Action 2 + description: Enter the actions you would like to run at the end of a calendar + event. These actions are also linked to the "End Notify - Action Button + 2" input located in the 'End Actions Buttons' section above. + default: [] + selector: + action: {} + end_action_3: + name: End Action - Action 3 + description: Enter the actions you would like to run at the end of a calendar + event. These actions are also linked to the "End Notify - Action Button + 3" input located in the 'End Actions Buttons' section above. + default: [] + selector: + action: {} + global_conditions_settings: + name: Global Conditions + icon: mdi:earth + collapsed: true + input: + global_conditions: + name: Global Conditions + description: Enter any global conditions you would like to apply to the + automation. + default: [] + selector: + condition: {} + source_url: https://gist.github.com/Blackshome/40bfd92fb1fe6a9a9189325704a3c81b +mode: parallel +max_exceeded: silent +variables: + calendar: !input calendar + include_calendar_trigger: !input include_calendar_trigger + calendar_trigger: !input calendar_trigger + start_calendar_offset: !input start_calendar_offset + end_calendar_offset: !input end_calendar_offset + include_start_notify: !input include_start_notify + start_notify_device: !input start_notify_device + start_title: !input start_title + start_message: !input start_message + start_notify_interruption_level: !input start_notify_interruption_level + start_notify_data: !input start_notify_data + start_notify_channel: !input start_notify_channel + include_start_action_buttons: !input include_start_action_buttons + start_action_button_1: !input start_action_button_1 + start_action_button_2: !input start_action_button_2 + start_action_button_3: !input start_action_button_3 + start_action_button_stop: !input start_action_button_stop + start_time_delay: !input start_time_delay + include_start_action_button_confirmation: !input include_start_action_button_confirmation + start_action_button_confirmation_title: !input start_action_button_confirmation_title + start_action_button_confirmation_message: !input start_action_button_confirmation_message + start_action_button_2_confirmation_message: !input start_action_button_2_confirmation_message + start_action_button_3_confirmation_message: !input start_action_button_3_confirmation_message + start_action_button_stop_confirmation_message: !input start_action_button_stop_confirmation_message + start_time_delay_confirmation_message: !input start_time_delay_confirmation_message + include_start_auto_actions: !input include_start_auto_actions + start_action_1: !input start_action_1 + start_action_2: !input start_action_2 + start_action_3: !input start_action_3 + include_end_notify: !input include_end_notify + end_notify_device: !input end_notify_device + end_title: !input end_title + end_message: !input end_message + end_notify_interruption_level: !input end_notify_interruption_level + end_notify_data: !input end_notify_data + end_notify_channel: !input end_notify_channel + include_end_action_buttons: !input include_end_action_buttons + end_action_button_1: !input end_action_button_1 + end_action_button_2: !input end_action_button_2 + end_action_button_3: !input end_action_button_3 + end_action_button_stop: !input end_action_button_stop + end_time_delay: !input end_time_delay + include_end_action_button_confirmation: !input include_end_action_button_confirmation + end_action_button_confirmation_title: !input end_action_button_confirmation_title + end_action_button_confirmation_message: !input end_action_button_confirmation_message + end_action_button_2_confirmation_message: !input end_action_button_2_confirmation_message + end_action_button_3_confirmation_message: !input end_action_button_3_confirmation_message + end_action_button_stop_confirmation_message: !input end_action_button_stop_confirmation_message + end_time_delay_confirmation_message: !input end_time_delay_confirmation_message + include_end_auto_actions: !input include_end_auto_actions + end_action_1: !input end_action_1 + end_action_2: !input end_action_2 + end_action_3: !input end_action_3 + global_conditions: !input global_conditions +trigger: +- platform: calendar + id: t0 + event: start + entity_id: !input calendar + offset: !input start_calendar_offset +- platform: calendar + id: t1 + event: end + entity_id: !input calendar + offset: !input end_calendar_offset +condition: +- condition: or + conditions: + - condition: and + conditions: + - condition: trigger + id: t0 + - condition: template + value_template: '{{ (''enable_start_notify_options'' in include_start_notify) + or (''enable_start_action_1'' in include_start_auto_actions) or (''enable_start_action_2'' + in include_start_auto_actions) or (''enable_start_action_3'' in include_start_auto_actions) + }}' + - condition: and + conditions: + - condition: trigger + id: t1 + - condition: template + value_template: '{{ (''enable_end_notify_options'' in include_end_notify) or + (''enable_end_action_1'' in include_end_auto_actions) or (''enable_end_action_2'' + in include_end_auto_actions) or (''enable_end_action_3'' in include_end_auto_actions) + }}' +- condition: or + conditions: + - condition: or + conditions: + - condition: template + value_template: '{% set keywords = calendar_trigger.split('','') %} {% set keywords + = keywords | map(''trim'') | list %} {{ keywords | select(''in'', trigger.calendar_event.summary) + | list | count > 0 }} + + ' + - condition: template + value_template: '{% set keywords = calendar_trigger.split('','') %} {% set keywords + = keywords | map(''trim'') | list %} {{ keywords | select(''in'', trigger.calendar_event.description) + | list | count > 0 }} + + ' + - condition: template + value_template: '{{ include_calendar_trigger == ''disable_key_word'' }}' +- condition: and + conditions: !input global_conditions +action: +- choose: + - alias: Start trigger + conditions: + - condition: trigger + id: t0 + sequence: + - choose: + - alias: Check if the start of notification is enabled + conditions: + - condition: template + value_template: '{{ include_start_notify == ''enable_start_notify_options''}}' + sequence: + - variables: + action_button_1: '{{ ''BUTTON_1_'' ~ context.id }}' + action_button_2: '{{ ''BUTTON_2_'' ~ context.id }}' + action_button_3: '{{ ''BUTTON_3_'' ~ context.id }}' + action_button_stop: '{{ ''BUTTON_STOP_'' ~ context.id }}' + start_message_data: "{% set message = namespace(data={}) %} {% if start_notify_interruption_level + in ['active', 'critical', 'time-sensitive', 'passive'] %}\n {% set + message.data = dict(message.data, **{ 'push': {'interruption-level': + start_notify_interruption_level }}) %}\n{% endif %} {% if 'high_priority' + in start_notify_data %}\n {% set message.data = dict(message.data, + **{ 'ttl': 0, 'priority': 'high' }) %}\n{% endif %} {% if 'channel' + in start_notify_data %}\n {% set message.data = dict(message.data, + **{ 'channel': start_notify_channel }) %}\n{% endif %} {% if 'sticky' + in start_notify_data %}\n {% set message.data = dict(message.data, + **{ 'sticky': \"true\" }) %}\n{% endif %} {{ message.data }}" + start_message_data_action_button: "{% set message = namespace(data={}) + %} {% set actions = [] %} {% if start_notify_interruption_level in ['active', + 'critical', 'time-sensitive', 'passive'] %}\n {% set message.data = + dict(message.data, **{ 'push': {'interruption-level': start_notify_interruption_level + }}) %}\n{% endif %} {% if 'high_priority' in start_notify_data %}\n + \ {% set message.data = dict(message.data, **{ 'ttl': 0, 'priority': + 'high' }) %}\n{% endif %} {% if 'channel' in start_notify_data %}\n + \ {% set message.data = dict(message.data, **{ 'channel': start_notify_channel + }) %}\n{% endif %} {% if 'sticky' in start_notify_data %}\n {% set + message.data = dict(message.data, **{ 'sticky': \"true\" }) %}\n{% endif + %} {% if 'enable_start_action_button_1' in include_start_action_buttons + %}\n {% set actions = actions + [{ 'action': action_button_1, 'title': + start_action_button_1 }] %}\n{% endif %} {% if 'enable_start_action_button_2' + in include_start_action_buttons %}\n {% set actions = actions + [{ + 'action': action_button_2, 'title': start_action_button_2 }] %}\n{% + endif %} {% if 'enable_start_action_button_3' in include_start_action_buttons + %}\n {% set actions = actions + [{ 'action': action_button_3, 'title': + start_action_button_3 }] %}\n{% endif %} {% if ('enable_start_action_button_1' + in include_start_action_buttons) or \n ('enable_start_action_button_2' + in include_start_action_buttons) or \n ('enable_start_action_button_3' + in include_start_action_buttons) %}\n {% set actions = actions + [{ + 'action': action_button_stop, 'title': start_action_button_stop }] %}\n{% + endif %} {% if actions | length > 0 %}\n {% set message.data = dict(message.data, + **{ 'actions': actions }) %}\n{% endif %} {{ message.data }}" + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify + }} + data: + title: !input start_title + message: !input start_message + data: '{{ start_message_data_action_button }}' + - choose: + - alias: Check if any start action buttons are enabled + conditions: + - condition: template + value_template: '{{ (''enable_start_action_button_1'' in include_start_action_buttons) + or (''enable_start_action_button_2'' in include_start_action_buttons) + or (''enable_start_action_button_3'' in include_start_action_buttons) + }}' + sequence: + - alias: Wait for a response from the action button + wait_for_trigger: + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_1 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_2 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_3 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_stop }}' + timeout: !input start_time_delay + - choose: + - alias: Check if confirmation message is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_action_button_confirmation'' + in include_start_action_button_confirmation }}' + sequence: + - if: + - alias: Confirmation from the actions buttons + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - choose: + - alias: Check if action button 1 is pressed + conditions: + - condition: template + value_template: '{{ action_button_1 in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_action_button_confirmation_message + data: '{{ start_message_data }}' + - alias: Check if action button 2 is pressed + conditions: + - condition: template + value_template: '{{ action_button_2 in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_action_button_2_confirmation_message + data: '{{ start_message_data }}' + - alias: Check if action button 3 is pressed + conditions: + - condition: template + value_template: '{{ action_button_3 in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_action_button_3_confirmation_message + data: '{{ start_message_data }}' + - alias: Check if action stop button is pressed + conditions: + - condition: template + value_template: '{{ action_button_stop in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_action_button_stop_confirmation_message + data: '{{ start_message_data }}' + - alias: Perform the action button selected action + choose: + - conditions: '{{ wait.trigger.event.data.action == action_button_1 + }}' + sequence: !input start_action_1 + - conditions: '{{ wait.trigger.event.data.action == action_button_2 + }}' + sequence: !input start_action_2 + - conditions: '{{ wait.trigger.event.data.action == action_button_3 + }}' + sequence: !input start_action_3 + - conditions: '{{ wait.trigger.event.data.action == action_button_stop + }}' + sequence: + - stop: Stop the automation + else: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') + | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_time_delay_confirmation_message + data: '{{ start_message_data }}' + - alias: Check if confirmation message is disabled + conditions: + - condition: template + value_template: '{{ ''disable_start_action_button_confirmation'' + in include_start_action_button_confirmation }}' + sequence: + - if: + - alias: Confirmation from the actions buttons + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - alias: Perform the action button selected action + choose: + - conditions: '{{ wait.trigger.event.data.action == action_button_1 + }}' + sequence: !input start_action_1 + - conditions: '{{ wait.trigger.event.data.action == action_button_2 + }}' + sequence: !input start_action_2 + - conditions: '{{ wait.trigger.event.data.action == action_button_3 + }}' + sequence: !input start_action_3 + - conditions: '{{ wait.trigger.event.data.action == action_button_stop + }}' + sequence: + - stop: Stop the automation + - choose: + - alias: Check if auto action 1 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_action_1'' in include_start_auto_actions + }}' + sequence: !input start_action_1 + - choose: + - alias: Check if auto action 2 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_action_2'' in include_start_auto_actions + }}' + sequence: !input start_action_2 + - choose: + - alias: Check if auto action 3 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_action_3'' in include_start_auto_actions + }}' + sequence: !input start_action_3 + default: + - choose: + - alias: End Trigger + conditions: + - condition: trigger + id: t1 + sequence: + - choose: + - alias: Check if end of notification is enabled + conditions: + - condition: template + value_template: '{{ include_end_notify == ''enable_end_notify_options'' + }}' + sequence: + - variables: + action_button_1: '{{ ''BUTTON_1_'' ~ context.id }}' + action_button_2: '{{ ''BUTTON_2_'' ~ context.id }}' + action_button_3: '{{ ''BUTTON_3_'' ~ context.id }}' + action_button_stop: '{{ ''BUTTON_STOP_'' ~ context.id }}' + end_message_data: "{% set message = namespace(data={}) %} {% if end_notify_interruption_level + in ['active', 'critical', 'time-sensitive', 'passive'] %}\n {% set + message.data = dict(message.data, **{ 'push': {'interruption-level': + end_notify_interruption_level }}) %}\n{% endif %} {% if 'high_priority' + in end_notify_data %}\n {% set message.data = dict(message.data, + **{ 'ttl': 0, 'priority': 'high' }) %}\n{% endif %} {% if 'channel' + in end_notify_data %}\n {% set message.data = dict(message.data, + **{ 'channel': end_notify_channel }) %}\n{% endif %} {% if 'sticky' + in end_notify_data %}\n {% set message.data = dict(message.data, + **{ 'sticky': \"true\" }) %}\n{% endif %} {{ message.data }}" + end_message_data_action_button: "{% set message = namespace(data={}) + %} {% set actions = [] %} {% if end_notify_interruption_level in ['active', + 'critical', 'time-sensitive', 'passive'] %}\n {% set message.data + = dict(message.data, **{ 'push': {'interruption-level': end_notify_interruption_level + }}) %}\n{% endif %} {% if 'high_priority' in end_notify_data %}\n + \ {% set message.data = dict(message.data, **{ 'ttl': 0, 'priority': + 'high' }) %}\n{% endif %} {% if 'channel' in end_notify_data %}\n + \ {% set message.data = dict(message.data, **{ 'channel': end_notify_channel + }) %}\n{% endif %} {% if 'sticky' in end_notify_data %}\n {% set + message.data = dict(message.data, **{ 'sticky': \"true\" }) %}\n{% + endif %} {% if 'enable_end_action_button_1' in include_end_action_buttons + %}\n {% set actions = actions + [{ 'action': action_button_1, 'title': + end_action_button_1 }] %}\n{% endif %} {% if 'enable_end_action_button_2' + in include_end_action_buttons %}\n {% set actions = actions + [{ + 'action': action_button_2, 'title': end_action_button_2 }] %}\n{% + endif %} {% if 'enable_end_action_button_3' in include_end_action_buttons + %}\n {% set actions = actions + [{ 'action': action_button_3, 'title': + end_action_button_3 }] %}\n{% endif %} {% if ('enable_end_action_button_1' + in include_end_action_buttons) or \n ('enable_end_action_button_2' + in include_end_action_buttons) or \n ('enable_end_action_button_3' + in include_end_action_buttons) %}\n {% set actions = actions + [{ + 'action': action_button_stop, 'title': end_action_button_stop }] %}\n{% + endif %} {% if actions | length > 0 %}\n {% set message.data = dict(message.data, + **{ 'actions': actions }) %}\n{% endif %} {{ message.data }}" + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify + }} + data: + title: !input end_title + message: !input end_message + data: '{{ end_message_data_action_button }}' + - choose: + - alias: Check if any start action buttons are enabled + conditions: + - condition: template + value_template: '{{ (''enable_end_action_button_1'' in include_end_action_buttons) + or (''enable_end_action_button_2'' in include_end_action_buttons) + or (''enable_end_action_button_3'' in include_end_action_buttons) + }}' + sequence: + - alias: Wait for a response from the action button + wait_for_trigger: + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_1 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_2 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_3 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_stop }}' + timeout: !input end_time_delay + - choose: + - alias: Check if confirmation message is enabled + conditions: + - condition: template + value_template: '{{ ''enable_end_action_button_confirmation'' + in include_end_action_button_confirmation }}' + sequence: + - if: + - alias: Confirmation from the actions buttons + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - choose: + - alias: Check if action button 1 is pressed + conditions: + - condition: template + value_template: '{{ action_button_1 in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_action_button_confirmation_message + data: '{{ end_message_data }}' + - alias: Check if action button 2 is pressed + conditions: + - condition: template + value_template: '{{ action_button_2 in wait.trigger.event.data.action }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_action_button_2_confirmation_message + data: '{{ end_message_data }}' + - alias: Check if action button 3 is pressed + conditions: + - condition: template + value_template: '{{ action_button_3 in wait.trigger.event.data.action }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_action_button_3_confirmation_message + data: '{{ end_message_data }}' + - alias: Check if action stop button is pressed + conditions: + - condition: template + value_template: '{{ action_button_stop in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_action_button_stop_confirmation_message + data: '{{ end_message_data }}' + - alias: Perform the action button selected action + choose: + - conditions: '{{ action_button_1 in wait.trigger.event.data.action + }}' + sequence: !input end_action_1 + - conditions: '{{ action_button_2 in wait.trigger.event.data.action + }}' + sequence: !input end_action_2 + - conditions: '{{ action_button_3 in wait.trigger.event.data.action + }}' + sequence: !input end_action_3 + - conditions: '{{ action_button_stop in wait.trigger.event.data.action + }}' + sequence: + - stop: Stop the automation + else: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') + | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_time_delay_confirmation_message + data: '{{ end_message_data }}' + - alias: Check if confirmation message is disabled + conditions: + - condition: template + value_template: '{{ ''disable_end_action_button_confirmation'' + in include_end_action_button_confirmation }}' + sequence: + - if: + - alias: Perform the action button selected action + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - alias: Perform the action + choose: + - conditions: '{{ action_button_1 in wait.trigger.event.data.action + }}' + sequence: !input end_action_1 + - conditions: '{{ action_button_2 in wait.trigger.event.data.action + }}' + sequence: !input end_action_2 + - conditions: '{{ action_button_3 in wait.trigger.event.data.action + }}' + sequence: !input end_action_3 + - conditions: '{{ action_button_stop in wait.trigger.event.data.action + }}' + sequence: + - stop: Stop the automation + - choose: + - alias: Check if auto action 1 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_end_action_1'' in include_end_auto_actions + }}' + sequence: !input end_action_1 + - choose: + - alias: Check if auto action 2 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_end_action_2'' in include_end_auto_actions + }}' + sequence: !input end_action_2 + - choose: + - alias: Check if auto action 3 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_end_action_3'' in include_end_auto_actions + }}' + sequence: !input end_action_3 diff --git a/blueprints/automation/Blackshome/low-battery-notifications-and-actions.yaml b/blueprints/automation/Blackshome/low-battery-notifications-and-actions.yaml new file mode 100644 index 0000000..6d56d11 --- /dev/null +++ b/blueprints/automation/Blackshome/low-battery-notifications-and-actions.yaml @@ -0,0 +1,1074 @@ +blueprint: + name: Low Battery Notifications & Actions + description: "# \U0001FAAB Low Battery Notifications & Actions\n**Version: 2.9**\n\n\U0001F680 + Stay Charged, Stay Smart! Let's automate and take charge of your battery maintenance!\U0001F50B⚡\n\n**If + you like my blueprints, and would like to show your support or just say thank + you?** [Click Here](https://www.paypal.com/donate/?hosted_button_id=WAZS3QSDTPGA8) + \U0001F642\n\n
The Automation Process: \U0001F448\n\n + \ - **Trigger Options:**\n - **Button Helper:** Press a button in the user + interface (UI) to check battery levels.\n - **Time & Day:** Automatically periodically + checks battery levels on any given time and day.\n\n - **Battery Sensors:**\n + \ - Automatically detects all battery sensors used in Home Assistant.\n - + Set a battery low warning percentage to identify all batteries below this level.\n + \ - Detects Low, unavailable & unknown batteries.\n - Excludes specific battery + sensors from notifications.\n - Create a custom group of battery sensors with + its own independent low battery warning percentage.\n\n - **Notification Options:**\n + \ - Opt to send messages to one or multiple devices.\n - Opt to display your + notifications directly within the Home Assistant user interface (UI).\n - Choose + which battery sensors to use, and then select their states from one of three preset + messages:\n - Battery Low % Level + Low Battery Level + Unavailable + Unknown.\n + \ - Battery Low % Level & Low Battery Level.\n - Unavailable or Unknown + Sensors.\n - Opt to receive a confirmation message if all batteries are okay.\n + \ - Opt to use action buttons in your device notification that allows you to + add your low batteries to a To-Do list.\n - Compatible with Apple iOS and Android + devices.\n\n - **Custom Actions:**\n - Customize actions to suit your preferences + and needs, enhancing the automations functionality.\n - Utilize 10 custom predetermined + battery sensors within the blueprint for more tailored and precise actions.\n + \ - Configure custom actions to include features like customizing notifications + or playing announcements on platforms like The Voice (HA), Google, Alexa, TTS, + etc.\n\n - **Custom Conditions:**\n - Enter any custom conditions to further + customize the automation process.\n
\n\nNeed help? \n- FAQ: [Click Here](https://community.home-assistant.io/t/653754/2?u=blacky)\n- + Community Support Including Updates: [Click Here](https://community.home-assistant.io/t/653754)\n\nRequired + = *\n" + domain: automation + input: + trigger_settings: + name: Triggers * + icon: mdi:cog-outline + description: 'You must select at least one trigger * + + ' + collapsed: true + input: + include_button: + name: Use The Button Helper Trigger (Optional) + description: 'Select if you would like a button helper to trigger the automation. + This is useful if you would like to check the battery levels by pressing + a button in the UI. + + + For more information on how to create a button helper [Click Here](https://community.home-assistant.io/t/653754/4) + + ' + default: disable_button_trigger + selector: + select: + options: + - label: Use a button trigger + value: enable_button_trigger + - label: Dont use a button trigger + value: disable_button_trigger + multiple: false + sort: false + custom_value: false + button_entity: + name: Button Helper + description: 'Input your button helper. + + ' + default: [] + selector: + entity: + filter: + - domain: + - input_button + reorder: false + multiple: false + include_time: + name: Use The Time Trigger (Optional) + description: 'Select if you would like to use the time trigger. This is + useful if you would like to periodically check the battery levels on any + given time and day. + + ' + default: time_disabled + selector: + select: + options: + - label: Enable the time options + value: time_enabled + - label: Disable the time options + value: time_disabled + multiple: false + sort: false + custom_value: false + time: + name: Time + description: 'Set the time you would like to run the automation. + + ' + default: 00:00:00 + selector: + time: {} + weekday_options: + name: Weekdays + description: 'Select the days of the week you would like the automation + to run. + + ' + default: + - mon + - tue + - wed + - thu + - fri + - sat + - sun + selector: + select: + multiple: true + mode: list + options: + - label: Monday + value: mon + - label: Tuesday + value: tue + - label: Wednesday + value: wed + - label: Thursday + value: thu + - label: Friday + value: fri + - label: Saturday + value: sat + - label: Sunday + value: sun + sort: false + custom_value: false + battery_settings: + name: Battery Settings + icon: mdi:battery-alert-variant-outline + collapsed: true + input: + battery_level: + name: Battery Low Warning Level + description: 'Set the battery low warning percentage to identify all batteries + below this level. + + ' + default: 20 + selector: + number: + min: 1.0 + max: 100.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + exclude_sensors: + name: Excluded Battery Sensors (Optional) + description: 'Choose the battery sensors to be excluded when the automation + runs. The automation will attempt to detect all battery sensors used in + Home Assistant, including those of your phones, tablets, etc. However, + it may not always be necessary or desirable to monitor these batteries. + Selecting these batteries here will exclude them from your notifications. + + + **NOTE** - You can only use entities and labels. Areas and devices are + not supported. + + ' + default: + entity_id: [] + selector: + target: + entity: + - device_class: + - battery + custom_group: + name: Battery Sensors - Custom Group (Optional) + description: 'You can select a custom group of battery sensors to be included + in your notifications. This custom group has its own independent ''Battery + Low Warning Level'' setting below. When using all batteries along with + the custom group in a single automation, it is recommended to exclude + the custom group of batteries specified above in the ''Excluded Battery + Sensors'' section. + + + Excluded battery sensors are not linked to this custom group. + + + **NOTE** - You can only use entities and labels. Areas and devices are + not supported. + + ' + default: + entity_id: [] + selector: + target: + entity: + - device_class: + - battery + custom_group_battery_level: + name: Custom Group - Battery Low Warning Level + description: 'Set the battery low warning percentage to identify all batteries + below this level. + + ' + default: 20 + selector: + number: + min: 1.0 + max: 100.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + easy_notify_settings: + name: Easy Notify + icon: mdi:bell-check-outline + collapsed: true + input: + include_easy_notify: + name: Use The Easy Notify - Device Notification Options (Optional) + description: 'Enabling this option will send notifications to the selected + devices below. It allows you to choose which battery sensors to use, and + then select their states from one of three preset messages. You can also + opt to receive a confirmation message if all batteries are okay. For further + customization or to integrate with voice assistants like HA, Google, Alexa, + TTS, etc., please explore the custom action options below. + + ' + default: disabled_easy_notify + selector: + select: + options: + - label: Enable Device Notification + value: enable_easy_notify + - label: Enable Device Notification + Okay Confirmation Message + value: enable_easy_okay_notify + - label: Disable Device Notification + value: disabled_easy_notify + multiple: false + sort: false + custom_value: false + include_persistent_notification: + name: Use The Easy Notify - UI Notification Options (Optional) + description: 'Enabling this option will display notifications directly within + the Home Assistant user interface (UI). It allows you to choose which + battery sensors to use, and then select their states from one of three + preset messages. You can also opt to receive a confirmation message in + the UI if all batteries are okay. For further customization or to integrate + with voice assistants like HA, Google, Alexa, TTS, etc., please explore + the custom action options below. + + ' + default: disabled_persistent_notification + selector: + select: + options: + - label: Enable UI Notification + value: enable_persistent_notification + - label: Enable UI Notification + Okay Confirmation Message + value: enable_persistent_okay_notification + - label: Disable UI Notification + value: disabled_persistent_notification + multiple: false + sort: false + custom_value: false + sensor_selection: + name: Sensor Selection + description: 'Please select which sensors you would like to include in your + notifications. + + ' + default: enable_all_sensors + selector: + select: + options: + - label: Use Battery Sensors - Custom Group + value: enable_battery_sensors_custom_group + - label: Use All Battery Sensors Minus Excluded Battery Sensors + value: enable_all_sensors + - label: Use All Battery Sensors Minus Excluded Battery Sensors + Custom + Group + value: enable_all_and_custom_group_sensors + multiple: false + sort: false + custom_value: false + notify_device: + name: Devices Notified (Optional) + description: 'If you''ve enabled device notifications above, please select + the devices to receive the notifications. + + ' + default: [] + selector: + device: + filter: + - integration: mobile_app + multiple: true + notify_title: + name: Title + description: 'Enter in the notification title of your message. + + ' + default: Low Battery Notification + selector: + text: {} + notify_message: + name: Message + description: 'Choose one of the three preset messages from the dropdown + menu. + + + **1 - Battery Low % Level + Low Battery Level + Unavailable + Unknown** + Includes sensors with battery percentages below the low warning level, + in a low battery state, or marked as unavailable or unknown. + + + **2 - Battery Low % Level & Low Battery Level** Includes sensors with + battery percentages below the low warning level or in a low battery state. + + + **3 - Unavailable or Unknown Sensors** Includes sensors that are unavailable + or have an unknown state. + + ' + default: all_sensors + selector: + select: + mode: dropdown + options: + - label: 1 - Battery Low % Level + Low Battery Level + Unavailable + + Unknown + value: all_sensors + - label: 2 - Battery Low % Level & Low Battery Level + value: sensors + - label: 3 - Unavailable or Unknown Sensors + value: unavailable_sensors + multiple: false + sort: false + custom_value: false + notify_okay_message: + name: Okay Confirmation Message + description: 'Enter in the notification message you would like to receive + when all your batteries are okay. + + ' + default: YES! All batteries are okay :) + selector: + text: {} + notify_interruption_level: + name: Interruption Level - iOS Only + description: 'On devices running iOS 15 and later, you can configure the + interruption level for your notifications to ensure they are delivered + according to your preferences. Choose the desired interruption level from + the dropdown list. Critical and time-sensitive notifications must be turned + ON within the Home Assistant App, and time-sensitive notifications must + be allowed in your Focus settings. + + + For more information on interruption levels [Click Here](https://community.home-assistant.io/t/653754/192?u=blacky) + + ' + default: active + selector: + select: + mode: dropdown + options: + - label: Default + value: active + - label: Critical Notifications + value: critical + - label: Time Sensitive Notifications + value: time-sensitive + - label: Quiet Notifications Without Waking Screen + value: passive + multiple: false + sort: false + custom_value: false + notify_sound: + name: Notification Sound - iOS Only + description: 'The Home Assistant app for iOS includes built-in notification + sounds, and you can import sounds from your iOS device into the app. Custom + sounds can also be added via iTunes or from your cloud storage. When entering + a sound, ensure you use the full filename, including the extension. + + + For more information on using sounds in the Home Assistant app, [Click + Here](https://community.home-assistant.io/t/653754/192?u=blacky) + + ' + default: [] + selector: + text: {} + notify_data: + name: Android Only Options (Optional) + description: '**High Priority** notifications ensure that you receive important + alerts immediately. These notifications bypass normal delivery delays + and are delivered instantly to your device. + + + **Sticky Notification** ensure that important alerts are not dismissed + when selected. They remain on your notification screen until you actively + clear or swipe them away, ensuring that critical information stays visible + until addressed. + + + **Notification Channel** allow you to easily organize different notification + settings, including notification sounds, vibrations, and other device-specific + features. If you choose this option, please enter your desired channel + name below. + + ' + default: [] + selector: + select: + multiple: true + options: + - label: High Priority + value: high_priority + - label: Sticky Notification + value: sticky + - label: Notification Channel + value: channel + sort: false + custom_value: false + notify_channel: + name: Notification Channel - Android Only + description: 'If you have chosen to use a notification channel, simply enter + the name of an existing channel to apply its pre-configured settings. + If you don''t have a channel yet, you can create a new one by entering + a name here. When your automation sends the first notification, it will + create the channel on your device. After the channel is created, you can + customize its notification settings to your preference directly on your + device. + + + For more information on notification channels [Click Here](https://community.home-assistant.io/t/653754/192?u=blacky) + + ' + default: [] + selector: + text: {} + action_buttons_settings: + name: Action Buttons + icon: mdi:gesture-tap-button + collapsed: true + input: + notify_action_buttons: + name: Easy Notify - Action Buttons (Optional) + description: 'Enhance your device notifications with an actionable To-Do + list button feature. With just a hold, release and tap, effortlessly add + selected items from your low battery notification to any of your chosen + To-Do lists. Plus, for added assurance, you can opt to receive a confirmation + message confirming the action. + + ' + default: disabled_notify_action_buttons + selector: + select: + options: + - label: Add To-Do List Action Buttons + value: enable_to_do_list_button + - label: Add To-Do List Action Buttons + Confirmation Message + value: enable_to_do_list_button_and_confirmation + - label: Disable Action Buttons + value: disabled_notify_action_buttons + multiple: false + sort: false + custom_value: false + to_do_list: + name: Easy Notify Action Buttons - To-Do List Selection + description: 'Choose the To-Do list where you''d like to add your task. + + + **Note** - The default shopping list isn''t compatible due to its lack + of description capability. However, all other To-Do lists are fully supported. + + ' + default: [] + selector: + entity: + filter: + - domain: + - todo + reorder: false + multiple: false + to_do_task_title: + name: Easy Notify Action Buttons - Task Title + description: 'When the action button is pressed, it will add a task to your + chosen To-Do list. Please enter the title you would like for your To-Do + task + + ' + default: Batteries To Be Replaced + selector: + text: {} + action_button_to_do: + name: Easy Notify Action Buttons - Action Button + description: 'Please specify the name for the action button to be displayed + in the notification. When pressed this button effortlessly adds a task + to your chosen To-Do list. + + ' + default: Add To To-Do List! + selector: + text: {} + action_button_cancel: + name: Easy Notify Action Buttons - Cancel Button + description: 'Please specify the name for the cancel button to be displayed + in the notification. This button allows you to cancel the task addition + process. + + ' + default: Cancel + selector: + text: {} + action_button_confirmation_title: + name: Easy Notify Action Buttons - Action & Cancel Button Confirmation Title + description: 'Please enter in a title for the confirmation notification + message when an action button or a cancel button is pressed. + + ' + default: Low Battery Confirmation + selector: + text: {} + action_button_confirmation_message: + name: Easy Notify Action Buttons - Action Button Confirmation Message + description: 'Please enter in the message for the confirmation notification + message when the action button is pressed. + + ' + default: Your low batteries have been added to your To-Do list + selector: + text: {} + cancel_action_button_confirmation_message: + name: Easy Notify Action Buttons - Cancel Button Confirmation Message + description: 'Please enter in the message for the confirmation notification + message when the cancel button is pressed. + + ' + default: Cancelled - Nothing was added to your To-Do list + selector: + text: {} + custom_actions_settings: + name: Custom Actions + icon: mdi:code-tags + collapsed: true + input: + include_custom_actions: + name: Use The Custom Action Options (Optional) + description: 'You can add any custom actions you''d like to run when the + automation is triggered. This can include customizing notifications or + playing announcements on platforms like The Voice (HA), Google, Alexa, + TTS, etc. + + ' + default: disabled_custom_actions + selector: + select: + options: + - label: Enable Custom Actions + value: enable_custom_actions + - label: Disable Custom Actions + value: disabled_custom_actions + multiple: false + sort: false + custom_value: false + custom_actions: + name: Custom Actions + description: 'Add your custom actions that you''d like to run when the automation + is triggered. Below are 10 sensors you can include in your actions, depending + on your preferences. + + + **All Battery Sensors Minus Excluded** + + 1 - Use "{{all_sensors}}" for - Battery Low % Level + Low Battery Level + + Unavailable + Unknown. + + 2 - Use "{{sensors}}" for - Battery Low % Level & Low Battery Level. + + 3 - Use "{{unavailable_sensors}}" for - Unavailable or Unknown Sensors. + + 4 - Use "{{sensors_names}}" for - Battery Low % Level & Low Battery Level + - Sensor name only. + + 5 - Use "{{unavailable_sensors_names}}" for - Unavailable or Unknown Sensors + - Sensor name only. + + + **Battery Sensors - Custom Group** + + 1 - Use "{{all_sensors_custom_group}}" for - Battery Low % Level + Low + Battery Level + Unavailable + Unknown. + + 2 - Use "{{sensors_custom_group}}" for - Battery Low % Level & Low Battery + Level. + + 3 - Use "{{unavailable_sensors_custom_group}}" for - Unavailable or Unknown + Sensors. + + 4 - Use "{{sensors_names_custom_group}}" for - Battery Low % Level & Low + Battery Level - Sensor name only. + + 5 - Use "{{unavailable_sensors_names_custom_group}}" for - Unavailable + or Unknown Sensors - Sensor name only. + + + For more information on how to use these sensors in your actions [Click + Here](https://community.home-assistant.io/t/653754/21) + + ' + default: [] + selector: + action: {} + global_conditions_settings: + name: Global Conditions + icon: mdi:earth + collapsed: true + input: + global_conditions: + name: Global Conditions + description: 'Enter any global conditions you would like to apply to the + automation. + + ' + default: [] + selector: + condition: {} + source_url: https://gist.github.com/Blackshome/4010fb83bb8c19b5fa1425526c6ff0e2 +mode: restart +max_exceeded: silent +variables: + include_button: !input include_button + button_entity: !input button_entity + include_time: !input include_time + time: !input time + weekday_options: !input weekday_options + battery_level: !input battery_level + exclude_sensors: !input exclude_sensors + custom_group: !input custom_group + custom_group_battery_level: !input custom_group_battery_level + include_easy_notify: !input include_easy_notify + include_persistent_notification: !input include_persistent_notification + sensor_selection: !input sensor_selection + notify_device: !input notify_device + notify_title: !input notify_title + notify_message: !input notify_message + notify_okay_message: !input notify_okay_message + notify_interruption_level: !input notify_interruption_level + notify_sound: !input notify_sound + notify_data: !input notify_data + notify_channel: !input notify_channel + notify_action_buttons: !input notify_action_buttons + to_do_list: !input to_do_list + to_do_task_title: !input to_do_task_title + action_button_to_do: !input action_button_to_do + action_button_cancel: !input action_button_cancel + action_button_confirmation_title: !input action_button_confirmation_title + action_button_confirmation_message: !input action_button_confirmation_message + cancel_action_button_confirmation_message: !input cancel_action_button_confirmation_message + include_custom_actions: !input include_custom_actions + global_conditions: !input global_conditions + all_exclude_sensors: "{% set label_entities_list = label_entities(exclude_sensors.label_id) + if exclude_sensors.label_id is defined and label_entities(exclude_sensors.label_id) + is not none else [] %} {% set entity_list = exclude_sensors.entity_id if exclude_sensors.entity_id + is defined else [] %} {% if entity_list is string %}\n {% set entity_list = [entity_list] + %}\n{% endif %} {% set all_exclude_sensors = label_entities_list + entity_list + %} {{ all_exclude_sensors }}" + custom_group_sensors: "{% set label_entities_list = label_entities(custom_group.label_id) + if custom_group.label_id is defined and label_entities(custom_group.label_id) + is not none else [] %} {% set entity_list = custom_group.entity_id if custom_group.entity_id + is defined else [] %} {% if entity_list is string %}\n {% set entity_list = [entity_list] + %}\n{% endif %} {% set custom_group_sensors = label_entities_list + entity_list + %} {{ custom_group_sensors }}" + device_message_data: "{% set message = namespace(data={}) %} {% set push = namespace(data={}) + %} {% if notify_interruption_level in ['active', 'critical', 'time-sensitive', + 'passive'] %}\n {% set push.data = dict(push.data, **{ 'interruption-level': + notify_interruption_level }) %}\n{% endif %} {% if notify_sound != [] %}\n {% + set push.data = dict(push.data, **{ 'sound': notify_sound }) %}\n{% endif %} {% + if push.data %}\n {% set message.data = dict(message.data, **{ 'push': push.data + }) %}\n{% endif %} {% if 'high_priority' in notify_data %}\n {% set message.data + = dict(message.data, **{ 'ttl': 0, 'priority': 'high' }) %}\n{% endif %} {% if + 'channel' in notify_data %}\n {% set message.data = dict(message.data, **{ 'channel': + notify_channel }) %}\n{% endif %} {% if 'sticky' in notify_data %}\n {% set message.data + = dict(message.data, **{ 'sticky': \"true\" }) %}\n{% endif %} {{ message.data + }}" + device_message_data_action_button: "{% set message = namespace(data={}) %} {% set + push = namespace(data={}) %} {% if notify_interruption_level in ['active', 'critical', + 'time-sensitive', 'passive'] %}\n {% set push.data = dict(push.data, **{ 'interruption-level': + notify_interruption_level }) %}\n{% endif %} {% if notify_sound != [] %}\n {% + set push.data = dict(push.data, **{ 'sound': notify_sound }) %}\n{% endif %} {% + if push.data %}\n {% set message.data = dict(message.data, **{ 'push': push.data + }) %}\n{% endif %} {% if 'high_priority' in notify_data %}\n {% set message.data + = dict(message.data, **{ 'ttl': 0, 'priority': 'high' }) %}\n{% endif %} {% if + 'channel' in notify_data %}\n {% set message.data = dict(message.data, **{ 'channel': + notify_channel }) %}\n{% endif %} {% if 'sticky' in notify_data %}\n {% set message.data + = dict(message.data, **{ 'sticky': \"true\" }) %}\n{% endif %} {% set actions + = [\n {'action': action_button_to_do, 'title': action_button_to_do},\n {'action': + action_button_cancel, 'title': action_button_cancel}\n] %}\n {% set message.data + = dict(message.data, **{ 'actions': actions }) %}\n{{ message.data }}" + device_confirmation_message_data: "{% set message = namespace(data={}) %} {% set + push = namespace(data={}) %} {% if notify_interruption_level in ['active', 'critical', + 'time-sensitive', 'passive'] %}\n {% set push.data = dict(push.data, **{ 'interruption-level': + notify_interruption_level }) %}\n{% endif %} {% if notify_sound != [] %}\n {% + set push.data = dict(push.data, **{ 'sound': notify_sound }) %}\n{% endif %} {% + if push.data %}\n {% set message.data = dict(message.data, **{ 'push': push.data + }) %}\n{% endif %} {% if 'high_priority' in notify_data %}\n {% set message.data + = dict(message.data, **{ 'ttl': 0, 'priority': 'high' }) %}\n{% endif %} {% if + 'channel' in notify_data %}\n {% set message.data = dict(message.data, **{ 'channel': + notify_channel }) %}\n{% endif %} {% if 'sticky' in notify_data %}\n {% set message.data + = dict(message.data, **{ 'sticky': \"true\" }) %}\n{% endif %}\n {% set message.data + = dict(message.data, **{ 'url': \"/todo?entity_id=\" ~ to_do_list, 'clickAction': + \"/todo?entity_id=\" ~ to_do_list }) %}\n{{ message.data }}" + all_sensors: "{% set low_battery = namespace(sensors=[]) %} {% for state in states.sensor\n + \ | rejectattr('attributes.device_class', 'undefined')\n | selectattr('attributes.device_class', + '==', 'battery') %}\n {% if 0 <= state.state | int(-1) <= battery_level | int + and state.entity_id not in all_exclude_sensors %}\n {% set low_battery.sensors + = low_battery.sensors + [state.name ~ ' @ ' ~ state.state ~ '%'] %}\n {% endif + %}\n{% endfor %} {% for state in states.binary_sensor\n | rejectattr('attributes.device_class', + 'undefined')\n | selectattr('attributes.device_class', '==', 'battery')\n | + selectattr('state', 'in', ['on', 'low']) %}\n {% if state.entity_id not in all_exclude_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name ~ ' is + low!'] %}\n {% endif %}\n{% endfor %} {% for state in states.sensor\n | rejectattr('attributes.device_class', + 'undefined')\n | selectattr('attributes.device_class', '==', 'battery')\n | + selectattr('state', 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id + not in all_exclude_sensors %}\n {% set low_battery.sensors = low_battery.sensors + + [state.name ~ ' is ' ~ state.state ~ '!'] %}\n {% endif %}\n{% endfor %} {% + for state in states.binary_sensor\n | rejectattr('attributes.device_class', 'undefined')\n + \ | selectattr('attributes.device_class', '==', 'battery')\n | selectattr('state', + 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id not in all_exclude_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name ~ ' is + ' ~ state.state ~ '!'] %}\n {% endif %}\n{% endfor %} {{ low_battery.sensors + | join(', ') }}" + sensors: "{% set low_battery = namespace(sensors=[]) %} {% for state in states.sensor\n + \ | rejectattr('attributes.device_class', 'undefined')\n | selectattr('attributes.device_class', + '==', 'battery') %}\n {% if 0 <= state.state | int(-1) <= battery_level | int + and state.entity_id not in all_exclude_sensors %}\n {% set low_battery.sensors + = low_battery.sensors + [state.name ~ ' @ ' ~ state.state ~ '%'] %}\n {% endif + %}\n{% endfor %} {% for state in states.binary_sensor\n | rejectattr('attributes.device_class', + 'undefined')\n | selectattr('attributes.device_class', '==', 'battery')\n | + selectattr('state', 'in', ['on', 'low']) %}\n {% if state.entity_id not in all_exclude_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name ~ ' is + low!'] %}\n {% endif %}\n{% endfor %} {{ low_battery.sensors | join(', ') }}" + unavailable_sensors: "{% set low_battery = namespace(sensors=[]) %} {% for state + in states.sensor\n | rejectattr('attributes.device_class', 'undefined')\n | + selectattr('attributes.device_class', '==', 'battery')\n | selectattr('state', + 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id not in all_exclude_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name ~ ' is + ' ~ state.state ~ '!'] %}\n {% endif %}\n{% endfor %} {% for state in states.binary_sensor\n + \ | rejectattr('attributes.device_class', 'undefined')\n | selectattr('attributes.device_class', + '==', 'battery')\n | selectattr('state', 'in', ['unavailable', 'unknown']) %}\n + \ {% if state.entity_id not in all_exclude_sensors %}\n {% set low_battery.sensors + = low_battery.sensors + [state.name ~ ' is ' ~ state.state ~ '!'] %}\n {% endif + %}\n{% endfor %} {{ low_battery.sensors | join(', ') }}" + sensors_names: "{% set low_battery = namespace(sensors=[]) %} {% for state in states.sensor\n + \ | rejectattr('attributes.device_class', 'undefined')\n | selectattr('attributes.device_class', + '==', 'battery') %}\n {% if 0 <= state.state | int(-1) <= battery_level | int + and state.entity_id not in all_exclude_sensors %}\n {% set low_battery.sensors + = low_battery.sensors + [state.name] %}\n {% endif %}\n{% endfor %} {% for state + in states.binary_sensor\n | rejectattr('attributes.device_class', 'undefined')\n + \ | selectattr('attributes.device_class', '==', 'battery')\n | selectattr('state', + 'in', ['on', 'low']) %}\n {% if state.entity_id not in all_exclude_sensors %}\n + \ {% set low_battery.sensors = low_battery.sensors + [state.name] %}\n {% endif + %}\n{% endfor %} {{low_battery.sensors | join(', ')}}" + unavailable_sensors_names: "{% set low_battery = namespace(sensors=[]) %} {% for + state in states.sensor\n | rejectattr('attributes.device_class', 'undefined')\n + \ | selectattr('attributes.device_class', '==', 'battery')\n | selectattr('state', + 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id not in all_exclude_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name] %}\n {% + endif %}\n{% endfor %} {% for state in states.binary_sensor\n | rejectattr('attributes.device_class', + 'undefined')\n | selectattr('attributes.device_class', '==', 'battery')\n | + selectattr('state', 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id + not in all_exclude_sensors %}\n {% set low_battery.sensors = low_battery.sensors + + [state.name] %}\n {% endif %}\n{% endfor %} {{low_battery.sensors | join(', + ')}}" + all_sensors_custom_group: "{% set low_battery = namespace(sensors=[]) %} {% for + state in states.sensor\n | rejectattr('attributes.device_class', 'undefined')\n + \ | selectattr('attributes.device_class', '==', 'battery') %}\n {% if 0 <= state.state + | int(-1) <= custom_group_battery_level | int and state.entity_id in custom_group_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name ~ ' @ ' + ~ state.state ~ '%'] %}\n {% endif %}\n{% endfor %} {% for state in states.binary_sensor\n + \ | rejectattr('attributes.device_class', 'undefined')\n | selectattr('attributes.device_class', + '==', 'battery')\n | selectattr('state', 'in', ['on', 'low']) %}\n {% if state.entity_id + in custom_group_sensors %}\n {% set low_battery.sensors = low_battery.sensors + + [state.name ~ ' is low!'] %}\n {% endif %}\n{% endfor %} {% for state in states.sensor\n + \ | rejectattr('attributes.device_class', 'undefined')\n | selectattr('attributes.device_class', + '==', 'battery')\n | selectattr('state', 'in', ['unavailable', 'unknown']) %}\n + \ {% if state.entity_id in custom_group_sensors %}\n {% set low_battery.sensors + = low_battery.sensors + [state.name ~ ' is ' ~ state.state ~ '!'] %}\n {% endif + %}\n{% endfor %} {% for state in states.binary_sensor\n | rejectattr('attributes.device_class', + 'undefined')\n | selectattr('attributes.device_class', '==', 'battery')\n | + selectattr('state', 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id + in custom_group_sensors %}\n {% set low_battery.sensors = low_battery.sensors + + [state.name ~ ' is ' ~ state.state ~ '!'] %}\n {% endif %}\n{% endfor %} {{ + low_battery.sensors | join(', ') }}" + sensors_custom_group: "{% set low_battery = namespace(sensors=[]) %} {% for state + in states.sensor\n | rejectattr('attributes.device_class', 'undefined')\n | + selectattr('attributes.device_class', '==', 'battery') %}\n {% if 0 <= state.state + | int(-1) <= custom_group_battery_level | int and state.entity_id in custom_group_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name ~ ' @ ' + ~ state.state ~ '%'] %}\n {% endif %}\n{% endfor %} {% for state in states.binary_sensor\n + \ | rejectattr('attributes.device_class', 'undefined')\n | selectattr('attributes.device_class', + '==', 'battery')\n | selectattr('state', 'in', ['on', 'low']) %}\n {% if state.entity_id + in custom_group_sensors %}\n {% set low_battery.sensors = low_battery.sensors + + [state.name ~ ' is low!'] %}\n {% endif %}\n{% endfor %} {{ low_battery.sensors + | join(', ') }}" + unavailable_sensors_custom_group: "{% set low_battery = namespace(sensors=[]) %} + {% for state in states.sensor\n | rejectattr('attributes.device_class', 'undefined')\n + \ | selectattr('attributes.device_class', '==', 'battery')\n | selectattr('state', + 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id in custom_group_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name ~ ' is + ' ~ state.state ~ '!'] %}\n {% endif %}\n{% endfor %} {% for state in states.binary_sensor\n + \ | rejectattr('attributes.device_class', 'undefined')\n | selectattr('attributes.device_class', + '==', 'battery')\n | selectattr('state', 'in', ['unavailable', 'unknown']) %}\n + \ {% if state.entity_id in custom_group_sensors %}\n {% set low_battery.sensors + = low_battery.sensors + [state.name ~ ' is ' ~ state.state ~ '!'] %}\n {% endif + %}\n{% endfor %} {{ low_battery.sensors | join(', ') }}" + sensors_names_custom_group: "{% set low_battery = namespace(sensors=[]) %} {% for + state in states.sensor\n | rejectattr('attributes.device_class', 'undefined')\n + \ | selectattr('attributes.device_class', '==', 'battery') %}\n {% if 0 <= state.state + | int(-1) <= custom_group_battery_level | int and state.entity_id in custom_group_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name] %}\n {% + endif %}\n{% endfor %} {% for state in states.binary_sensor\n | rejectattr('attributes.device_class', + 'undefined')\n | selectattr('attributes.device_class', '==', 'battery')\n | + selectattr('state', 'in', ['on', 'low']) %}\n {% if state.entity_id in custom_group_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name] %}\n {% + endif %}\n{% endfor %} {{ low_battery.sensors | join(', ') }}" + unavailable_sensors_names_custom_group: "{% set low_battery = namespace(sensors=[]) + %} {% for state in states.sensor\n | rejectattr('attributes.device_class', 'undefined')\n + \ | selectattr('attributes.device_class', '==', 'battery')\n | selectattr('state', + 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id in custom_group_sensors + %}\n {% set low_battery.sensors = low_battery.sensors + [state.name] %}\n {% + endif %}\n{% endfor %} {% for state in states.binary_sensor\n | rejectattr('attributes.device_class', + 'undefined')\n | selectattr('attributes.device_class', '==', 'battery')\n | + selectattr('state', 'in', ['unavailable', 'unknown']) %}\n {% if state.entity_id + in custom_group_sensors %}\n {% set low_battery.sensors = low_battery.sensors + + [state.name] %}\n {% endif %}\n{% endfor %} {{ low_battery.sensors | join(', + ') }}" + easy_notify_message: "{% set low_battery = namespace(sensors=[]) %} {% if sensor_selection + == 'enable_battery_sensors_custom_group' and notify_message == 'all_sensors' %}\n + \ {{all_sensors_custom_group|replace(', ', '\\n')}}\n{% elif sensor_selection + == 'enable_battery_sensors_custom_group' and notify_message == 'sensors'%}\n {{sensors_custom_group|replace(', + ', '\\n')}}\n{% elif sensor_selection == 'enable_battery_sensors_custom_group' + and notify_message == 'unavailable_sensors'%}\n {{unavailable_sensors_custom_group|replace(', + ', '\\n')}}\n{% elif sensor_selection == 'enable_all_sensors' and notify_message + == 'all_sensors' %}\n {{all_sensors|replace(', ', '\\n')}}\n{% elif sensor_selection + == 'enable_all_sensors' and notify_message == 'sensors'%}\n {{sensors|replace(', + ', '\\n')}}\n{% elif sensor_selection == 'enable_all_sensors' and notify_message + == 'unavailable_sensors'%}\n {{unavailable_sensors|replace(', ', '\\n')}}\n{% + elif sensor_selection == 'enable_all_and_custom_group_sensors' and notify_message + == 'all_sensors' %}\n {{all_sensors|replace(', ', '\\n')}}{{'\\n'}}{{all_sensors_custom_group|replace(', + ', '\\n')}}\n{% elif sensor_selection == 'enable_all_and_custom_group_sensors' + and notify_message == 'sensors'%}\n {{sensors|replace(', ', '\\n')}}{{'\\n'}}{{sensors_custom_group|replace(', + ', '\\n')}}\n{% elif sensor_selection == 'enable_all_and_custom_group_sensors' + and notify_message == 'unavailable_sensors'%}\n {{unavailable_sensors|replace(', + ', '\\n')}}{{'\\n'}}{{unavailable_sensors_custom_group|replace(', ', '\\n')}}\n{% + else %}\n []\n{% endif %}" +triggers: +- trigger: state + entity_id: !input button_entity + id: t0 +- trigger: time + at: !input time + id: t1 +condition: +- condition: or + conditions: + - condition: and + conditions: + - condition: trigger + id: t0 + - '{{ include_button == ''enable_button_trigger'' }}' + - condition: and + conditions: + - condition: trigger + id: t1 + - '{{ include_time == ''time_enabled'' }}' +- condition: or + conditions: + - '{{ include_time == ''time_disabled'' }}' + - condition: and + conditions: + - condition: time + weekday: !input weekday_options + - '{{ include_time == ''time_enabled'' }}' + - condition: trigger + id: t0 +- condition: and + conditions: !input global_conditions +action: +- choose: + - alias: Perform the custom actions + conditions: + - condition: template + value_template: '{{ include_custom_actions == ''enable_custom_actions'' }}' + sequence: !input custom_actions +- choose: + - alias: Use the easy notify options + conditions: + - condition: template + value_template: '{{ (include_easy_notify == ''enable_easy_notify'') or (include_easy_notify + == ''enable_easy_okay_notify'') }}' + - condition: template + value_template: '{{ notify_device | length > 0 }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input notify_device + sequence: + - choose: + - alias: Sensors have been found + conditions: + - '{{ easy_notify_message != '''' }}' + sequence: + - choose: + - alias: Easy notify with no action button + conditions: + - '{{ notify_action_buttons == ''disabled_notify_action_buttons'' + }}' + sequence: + - action: notify.mobile_app_{{ device_attr(repeat.item, 'name') | + slugify }} + data: + title: !input notify_title + message: '{{easy_notify_message}}' + data: '{{ device_message_data }}' + - alias: Easy notify with action button + conditions: + - '{{ notify_action_buttons != ''disabled_notify_action_buttons'' + }}' + sequence: + - action: notify.mobile_app_{{ device_attr(repeat.item, 'name') | + slugify }} + data: + title: !input notify_title + message: '{{easy_notify_message}}' + data: '{{ device_message_data_action_button }}' + - alias: No sensors have been found + conditions: + - '{{ easy_notify_message == '''' }}' + - '{{ include_easy_notify == ''enable_easy_okay_notify'' }}' + sequence: + - action: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify + }} + data: + title: !input notify_title + message: !input notify_okay_message + data: '{{ device_message_data }}' +- choose: + - alias: Use the easy notify persistent notification options + conditions: + - condition: template + value_template: '{{ (include_persistent_notification == ''enable_persistent_notification'') + or (include_persistent_notification == ''enable_persistent_okay_notification'') + }}' + sequence: + - choose: + - alias: Sensors have been found + conditions: + - '{{ easy_notify_message != '''' }}' + sequence: + - action: persistent_notification.create + data: + title: "\U0001FAAB {{notify_title}}" + message: '{{easy_notify_message}}' + - alias: No sensors have been found + conditions: + - '{{ easy_notify_message == '''' }}' + - '{{ include_persistent_notification == ''enable_persistent_okay_notification'' + }}' + sequence: + - action: persistent_notification.create + data: + title: "\U0001FAAB {{notify_title}}" + message: !input notify_okay_message +- choose: + - alias: Check if the To-Do action button is enabled + conditions: + - condition: template + value_template: '{{ (include_easy_notify == ''enable_easy_notify'') or (include_easy_notify + == ''enable_easy_okay_notify'') }}' + - condition: template + value_template: '{{(notify_action_buttons == ''enable_to_do_list_button'') or + (notify_action_buttons == ''enable_to_do_list_button_and_confirmation'')}}' + - condition: template + value_template: '{{ notify_device | length > 0 }}' + sequence: + - alias: Wait for a response from the action buttons + wait_for_trigger: + - trigger: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_to_do }}' + - trigger: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_cancel }}' + - choose: + - alias: Check if confirmation message is disabled + conditions: + - condition: template + value_template: '{{ notify_action_buttons == ''enable_to_do_list_button'' + }}' + sequence: + - if: + - alias: Confirmation from the action buttons + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - alias: Perform the chosen actions + choose: + - conditions: '{{ wait.trigger.event.data.action == action_button_to_do + }}' + sequence: + - alias: Add task to To-Do list + action: todo.add_item + metadata: {} + data: + item: !input to_do_task_title + description: '{{easy_notify_message}}' + target: + entity_id: !input to_do_list + - conditions: '{{ wait.trigger.event.data.action == action_button_cancel + }}' + sequence: + - stop: Stop the automation + - alias: Check if confirmation message is enabled + conditions: + - condition: template + value_template: '{{ notify_action_buttons == ''enable_to_do_list_button_and_confirmation'' + }}' + sequence: + - if: + - alias: Confirmation from the action buttons + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - alias: Perform the chosen actions + choose: + - conditions: '{{ wait.trigger.event.data.action == action_button_to_do + }}' + sequence: + - alias: Add task to To-Do list + action: todo.add_item + metadata: {} + data: + item: !input to_do_task_title + description: '{{easy_notify_message}}' + target: + entity_id: !input to_do_list + - alias: Send a notification to each device + repeat: + for_each: !input notify_device + sequence: + - action: notify.mobile_app_{{ device_attr(repeat.item, 'name') + | slugify }} + data: + title: !input action_button_confirmation_title + message: !input action_button_confirmation_message + data: '{{ device_confirmation_message_data }}' + - conditions: '{{ wait.trigger.event.data.action == action_button_cancel + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input notify_device + sequence: + - action: notify.mobile_app_{{ device_attr(repeat.item, 'name') + | slugify }} + data: + title: !input action_button_confirmation_title + message: !input cancel_action_button_confirmation_message + data: '{{ device_message_data }}' diff --git a/blueprints/automation/Blackshome/notifications-and-announcements.yaml b/blueprints/automation/Blackshome/notifications-and-announcements.yaml new file mode 100644 index 0000000..dcce3aa --- /dev/null +++ b/blueprints/automation/Blackshome/notifications-and-announcements.yaml @@ -0,0 +1,601 @@ +blueprint: + name: Notifications & Announcements + description: "# \U0001F4E2 Notifications & Announcements\n**Version: 1.2**\n\nState + your trigger and spread the word \U0001F4AC\U0001F509\n\n**If you like my blueprints, + and would like to show your support or just say thank you?** [Click Here](https://www.paypal.com/donate/?hosted_button_id=WAZS3QSDTPGA8) + \U0001F642\n
The Automation Process: - Click here to + expand\n\n\n - **Trigger Options:**\n\n - Specify the state change + of one or more entities that will initiate the automation from a dropdown list. + You can also specify your own custom state if it is not available from the dropdown + list.\n - State Change Options: The available state changes include:\n - + **General States:** Button or any state, ON, OFF, Unavailable or Unknown\n - + **Location-Based States:** Home or Not Home\n - **Numeric States:** Numeric + state above, Numeric state below or Numeric state above & below\n\n - **Notification + Options:**\n\n - Opt to send messages to one or multiple devices. Compatible + with Apple iOS and Android devices.\n - Opt to display your notifications directly + within the Home Assistant user interface (UI).\n - Opt to send Text-to-Speech + Announcement to your selected media players.\n\n - **Time-Based Automation:**\n\n + \ - Specify precise start and end times along with weekday selections to define + when the automation can run.\n\n - **Custom Conditions:**\n\n - Enter any + custom conditions to further customize the automation process.\n\n
\n\nNeed + help? See our FAQ: [Click Here](https://community.home-assistant.io/t/notifications-announcements/728100/2?u=blacky)\nLet + us know what you think of this blueprint and for community support including updates: + [Click Here](https://community.home-assistant.io/t/notifications-announcements/728100?u=blacky)\n\nRequired + = *\n" + domain: automation + input: + trigger_settings: + name: Triggers * + icon: mdi:cog-outline + collapsed: true + input: + trigger_state: + name: Trigger - State * + description: 'Please select what state you would like for your trigger. + You can choose from ten options available in the dropdown menu. If your + option is not in the dropdown menu then just type the state you would + like to use in and click the save button. + + + 1 - Button Or Any State + + 2 - ON + + 3 - OFF + + 4 - Unavailable + + 5 - Unknown + + 6 - Home + + 7 - Not Home + + 8 - Numeric State - Above + + 9 - Numeric State - Below + + 10 - Numeric State - Above & Below + + + A numbering system has been implemented to facilitate navigation within + the dropdown selections. Each number corresponds to a specific configuration, + aiding users in identifying and adjusting the settings used within each + selection. For instance, when selecting "8 - Numeric State - Above" as + a dropdown option, settings marked "Used in options 8, 9 or 10" are required + for that respective selection because number 8 is included in those options. + + ' + default: button_any_state + selector: + select: + custom_value: true + mode: dropdown + options: + - label: 1 - Button Or Any State + value: button_any_state + - label: 2 - ON + value: 'on' + - label: 3 - OFF + value: 'off' + - label: 4 - Unavailable + value: unavailable + - label: 5 - Unknown + value: unknown + - label: 6 - Home + value: home + - label: 7 - Not Home + value: not_home + - label: 8 - Numeric State - Above + value: numeric_state_above + - label: 9 - Numeric State - Below + value: numeric_state_below + - label: 10 - Numeric State - Above & Below + value: numeric_state_above_below + sort: false + multiple: false + trigger_state_entity: + name: State Entities + description: '**Used in options 1, 2, 3, 4, 5, 6 or 7**. Enter the entities + that will trigger the automation on state change. + + ' + default: [] + selector: + entity: + multiple: true + trigger_numeric_entity: + name: Numeric State Entities + description: '**Used in options 8, 9 or 10**. Enter the entities that will + trigger the automation numeric state change. + + ' + default: [] + selector: + entity: + multiple: true + above_state: + name: Numeric State Above Option + description: '**Used in options 8, or 10**. Set the above value. The numeric + state trigger will be when the value crosses over to above the set value. + + ' + default: 0 + selector: + number: + min: -20.0 + max: 100.0 + step: 1.0 + unit_of_measurement: value + mode: slider + below_state: + name: Numeric State Below Option + description: '**Used in options 9 or 10**. Set the below value. The numeric + state trigger will be when the value crosses over to below the set value. + + ' + default: 0 + selector: + number: + min: -20.0 + max: 100.0 + step: 1.0 + unit_of_measurement: value + mode: slider + time_delay_state: + name: Time Delay + description: '**Used in options 1, 2, 3, 4, 5, 6, 7, 8, 9 or 10** Specifies + the duration that the trigger must maintain its state before the automation + is executed. The automation will only proceed if the trigger condition + is met continuously for this entire time delay. + + ' + default: + hours: 0 + minutes: 0 + seconds: 0 + selector: + duration: {} + device_notify_settings: + name: Mobile App Notify + icon: mdi:devices + collapsed: true + input: + include_notify: + name: Use The Mobile App Notify Option (Optional) + description: "Enabling this option will send notifications to the selected + devices below. \n" + default: disable_mobile_app_notify + selector: + select: + options: + - label: Enable Mobile App Notify Options + value: enable_mobile_app_notify + - label: Disable Mobile App Notify Options + value: disable_mobile_app_notify + custom_value: false + sort: false + multiple: false + notify_device: + name: Devices Notified + description: 'Select the devices to be notified when the automation is triggered. + + ' + default: [] + selector: + device: + filter: + - integration: mobile_app + multiple: true + notify_title: + name: Title + description: 'Enter in the title of your notification. + + ' + default: "\U0001F4E2 Enter Notify Title Here" + selector: + text: {} + notify_message: + name: Message + description: 'Enter in the message of your notification. + + ' + default: "Enter Notify Message Here \U0001F642" + selector: + text: {} + notify_interruption_level: + name: Interruption Level - iOS Only + description: 'On devices running iOS 15 and later, you can configure the + interruption level for your notifications to ensure they are delivered + according to your preferences. Choose the desired interruption level from + the dropdown list. Critical and time-sensitive notifications must be turned + ON within the Home Assistant App, and time-sensitive notifications must + be allowed in your Focus settings. + + + For more information on interruption levels [Click Here](https://community.home-assistant.io/t/728100/13) + + ' + default: active + selector: + select: + mode: dropdown + options: + - label: Default + value: active + - label: Critical Notifications + value: critical + - label: Time Sensitive Notifications + value: time-sensitive + - label: Quiet Notifications Without Waking Screen + value: passive + custom_value: false + sort: false + multiple: false + notify_data: + name: Android Only Options (Optional) + description: '**High Priority** notifications ensure that you receive important + alerts immediately. These notifications bypass normal delivery delays + and are delivered instantly to your device. + + + **Sticky Notification** ensure that important alerts are not dismissed + when selected. They remain on your notification screen until you actively + clear or swipe them away, ensuring that critical information stays visible + until addressed. + + + **Notification Channel** allow you to easily organize different notification + settings, including notification sounds, vibrations, and other device-specific + features. If you choose this option, please enter your desired channel + name below. + + ' + default: [] + selector: + select: + multiple: true + options: + - label: High Priority + value: high_priority + - label: Sticky Notification + value: sticky + - label: Notification Channel + value: channel + custom_value: false + sort: false + notify_channel: + name: Notification Channel - Android Only + description: 'If you have chosen to use a notification channel, simply enter + the name of an existing channel to apply its pre-configured settings. + If you don''t have a channel yet, you can create a new one by entering + a name here. When your automation sends the first notification, it will + create the channel on your device. After the channel is created, you can + customize its notification settings to your preference directly on your + device. + + + For more information on notification channels [Click Here](https://community.home-assistant.io/t/728100/13) + + ' + default: [] + selector: + text: {} + persistent_notify_settings: + name: Notifications + icon: mdi:bell-outline + collapsed: true + input: + include_persistent_notification: + name: Use The UI Notification Option (Optional) + description: "Enabling this option will display notifications directly within + the Home Assistant user interface (UI). \n" + default: disabled_persistent_notification + selector: + select: + options: + - label: Enable UI Notifications + value: enable_persistent_notification + - label: Disable UI Notifications + value: disabled_persistent_notification + custom_value: false + sort: false + multiple: false + persistent_title: + name: Title + description: 'Enter in the title of your UI notification. + + ' + default: "\U0001F4E2 Enter UI Title Here" + selector: + text: {} + persistent_message: + name: Message + description: 'Enter in the message of your UI notification. + + ' + default: "Enter UI Message Here \U0001F642" + selector: + text: {} + tts_notify_settings: + name: Text-to-Speech Announcement + icon: mdi:bullhorn-outline + collapsed: true + input: + include_tts_announcement: + name: Use The Text-to-Speech Announcement Option (Optional) + description: 'Enabling this option will send a Text-to-Speech notification + to the selected Media Players below. + + ' + default: disable_tts_announcement + selector: + select: + options: + - label: Enable Text-to-Speech Announcements + value: enable_tts_announcement + - label: Disable Text-to-Speech Announcements + value: disable_tts_announcement + custom_value: false + sort: false + multiple: false + text_to_speech_engine: + name: Text-to-Speech Service Provider + description: 'Select the Text-to-Speech service provider. + + ' + default: [] + selector: + entity: + domain: + - tts + multiple: false + media_player: + name: Media Player + description: 'Select the Media Player/s that will play your Text-to-Speech + announcement message. + + ' + default: [] + selector: + entity: + domain: + - media_player + multiple: true + notify_tts_message: + name: Announcement Message + description: 'Enter in the message of your Text-to-Speech announcement. + + ' + default: Enter TTS Message Here + selector: + text: + multiline: true + multiple: false + time_settings: + name: Time + icon: mdi:clock-outline + collapsed: true + input: + include_time: + name: Use The Time Options (Optional) + description: 'Use the "Start Time", **End Time** and the **Weekdays** values + to only run the automation between the time periods. This option allows + you to disable the automation during sleeping hours to avoid disturbing + anyone. Another example is setting the time between 5pm and 9pm for the + ''Dinner is Ready'' call. This way, if you accidentally activate the trigger + outside these hours, the automation will not run. + + ' + default: time_disabled + selector: + select: + options: + - label: Enable the time options + value: time_enabled + - label: Disable the time options + value: time_disabled + custom_value: false + sort: false + multiple: false + after_time: + name: Start Time + description: 'Set the start time. + + ' + default: 00:00:00 + selector: + time: {} + before_time: + name: End Time + description: 'Set the end time. + + ' + default: 00:00:00 + selector: + time: {} + weekday_options: + name: Weekdays + description: 'Select the days of the week you would like the automation + to run. You must select "Enable the time options" above for the weekdays + selections to work. + + ' + default: + - mon + - tue + - wed + - thu + - fri + - sat + - sun + selector: + select: + multiple: true + mode: list + options: + - label: Monday + value: mon + - label: Tuesday + value: tue + - label: Wednesday + value: wed + - label: Thursday + value: thu + - label: Friday + value: fri + - label: Saturday + value: sat + - label: Sunday + value: sun + custom_value: false + sort: false + global_conditions_settings: + name: Global Conditions + icon: mdi:earth + collapsed: true + input: + global_conditions: + name: Global Conditions + description: 'Enter any global conditions you would like to apply to the + automation. + + ' + default: [] + selector: + condition: {} + source_url: https://gist.github.com/Blackshome/180ca4a24af81cd5d843acfc039039bc +mode: single +max_exceeded: silent +variables: + trigger_state: !input trigger_state + trigger_state_entity: !input trigger_state_entity + trigger_numeric_entity: !input trigger_numeric_entity + above_state: !input above_state + below_state: !input below_state + time_delay_state: !input time_delay_state + include_notify: !input include_notify + notify_device: !input notify_device + notify_title: !input notify_title + notify_message: !input notify_message + notify_interruption_level: !input notify_interruption_level + notify_data: !input notify_data + notify_channel: !input notify_channel + include_persistent_notification: !input include_persistent_notification + persistent_title: !input persistent_title + persistent_message: !input persistent_message + include_tts_announcement: !input include_tts_announcement + text_to_speech_engine: !input text_to_speech_engine + media_player: !input media_player + notify_tts_message: !input notify_tts_message + include_time: !input include_time + after_time: !input after_time + before_time: !input before_time + weekday_options: !input weekday_options + global_conditions: !input global_conditions + device_message_data: "{% set message = namespace(data={}) %} {% if notify_interruption_level + in ['active', 'critical', 'time-sensitive', 'passive'] %}\n {% set message.data + = dict(message.data, **{ 'push': {'interruption-level': notify_interruption_level + }}) %}\n{% endif %} {% if 'high_priority' in notify_data %}\n {% set message.data + = dict(message.data, **{ 'ttl': 0, 'priority': 'high' }) %}\n{% endif %} {% if + 'channel' in notify_data %}\n {% set message.data = dict(message.data, **{ 'channel': + notify_channel }) %}\n{% endif %} {% if 'sticky' in notify_data %}\n {% set message.data + = dict(message.data, **{ 'sticky': \"true\" }) %}\n{% endif %} {{ message.data + }}" +trigger: +- platform: state + id: t0 + entity_id: !input trigger_state_entity + for: !input time_delay_state +- platform: state + id: t1 + entity_id: !input trigger_state_entity + to: !input trigger_state + for: !input time_delay_state +- platform: numeric_state + id: t2 + entity_id: !input trigger_numeric_entity + above: !input above_state + for: !input time_delay_state +- platform: numeric_state + id: t3 + entity_id: !input trigger_numeric_entity + below: !input below_state + for: !input time_delay_state +condition: +- condition: or + conditions: + - condition: and + conditions: + - condition: trigger + id: t0 + - '{{ trigger_state == ''button_any_state'' }}' + - condition: and + conditions: + - condition: trigger + id: t1 + - '{{ (trigger_state != ''button_any_state'') or (trigger_state != ''numeric_state_above'') + or (trigger_state != ''numeric_state_below'') or (trigger_state != ''numeric_state_above_below'') + }}' + - condition: and + conditions: + - condition: trigger + id: t2 + - '{{ (trigger_state == ''numeric_state_above'') or (trigger_state == ''numeric_state_above_below'') + }}' + - condition: and + conditions: + - condition: trigger + id: t3 + - '{{ (trigger_state == ''numeric_state_below'') or (trigger_state == ''numeric_state_above_below'') + }}' +- condition: or + conditions: + - '{{ include_time == ''time_disabled'' }}' + - condition: and + conditions: + - condition: time + after: !input after_time + before: !input before_time + weekday: !input weekday_options + - '{{ include_time == ''time_enabled'' }}' +- condition: and + conditions: !input global_conditions +action: +- choose: + - alias: Check if notification is enabled + conditions: + - '{{ include_notify == ''enable_mobile_app_notify'' }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify + }} + data: + title: !input notify_title + message: !input notify_message + data: '{{ device_message_data }}' +- choose: + - alias: Use the persistent notification is enabled + conditions: + - condition: template + value_template: '{{ include_persistent_notification == ''enable_persistent_notification'' + }}' + sequence: + - service: persistent_notification.create + data: + title: !input persistent_title + message: !input persistent_message +- choose: + - alias: Check if tts announcement is enabled + conditions: + - '{{ include_tts_announcement == ''enable_tts_announcement'' }}' + sequence: + - service: tts.speak + target: + entity_id: !input text_to_speech_engine + data: + media_player_entity_id: !input media_player + message: !input notify_tts_message diff --git a/blueprints/automation/Blackshome/press-button-turn-on-off-entities.yaml b/blueprints/automation/Blackshome/press-button-turn-on-off-entities.yaml new file mode 100644 index 0000000..0a374bf --- /dev/null +++ b/blueprints/automation/Blackshome/press-button-turn-on-off-entities.yaml @@ -0,0 +1,103 @@ +blueprint: + name: Press Button - Turn ON & OFF Entities + description: "# \U0001F9FF Press Button - Turn ON & OFF Entities\n**Version: 1.0**\n\nLet + us know what you think of this blueprint and for community support including updates: + [Click Here](https://community.home-assistant.io/t/press-button-turn-on-off-entities/634488)\n\n**The + Automation Process:**\n - When you press the button it will turn ON the main + entity input.\n - When you press the button again it will turn OFF the main entity + input.\n - You have the option to input an alternating entity that will be the + opposite state to the main entity.\n\n**NOTE** - The button must have no state, + like a button helper.\n\nRequired = *\n\nNeed help? See our FAQ: [Click Here](https://community.home-assistant.io/t/press-button-turn-on-off-entities/634488/2?u=blacky)\n\n**If + you like my blueprints, and would like to show your support or just say thank + you?** [Click Here](https://www.paypal.com/donate/?hosted_button_id=WAZS3QSDTPGA8) + \U0001F642\n" + domain: automation + input: + press_button: + name: Press Button * + description: The press button that controls the entities. + selector: + entity: {} + main_entity: + name: Main Entity * + description: The main entity that will be turned ON when the button is pressed. + selector: + entity: + multiple: true + alternating_entity: + name: Alternating Entity (optional) + description: The alternating entity that will be the opposite state to the main + entity when the button is pressed. + default: [] + selector: + entity: + multiple: true + source_url: https://gist.github.com/Blackshome/ca6be121c8d3e4404e33114fc88b599a +variables: + press_button: !input press_button + main_entity: !input main_entity + alternating_entity: !input alternating_entity +trigger: +- platform: state + entity_id: !input press_button +action: +- choose: + - alias: Check if any alternating entity are enter in + conditions: + - '{{ alternating_entity == [] }}' + sequence: + - choose: + - alias: Check any main entities are on + conditions: + - condition: state + entity_id: !input main_entity + match: any + state: 'on' + sequence: + - alias: Turn OFF the main entities + service: homeassistant.turn_off + target: + entity_id: !input main_entity + - alias: Check all main entities are off + conditions: + - condition: state + entity_id: !input main_entity + match: all + state: 'off' + sequence: + - alias: Turn ON the main entities + service: homeassistant.turn_on + target: + entity_id: !input main_entity + default: + - choose: + - alias: Check any main entities are on + conditions: + - condition: state + entity_id: !input main_entity + match: any + state: 'on' + sequence: + - alias: Turn ON the main entities + service: homeassistant.turn_off + target: + entity_id: !input main_entity + - alias: Turn ON the alternating entity entities + service: homeassistant.turn_on + target: + entity_id: !input alternating_entity + - alias: Check all main entities are off + conditions: + - condition: state + entity_id: !input main_entity + match: all + state: 'off' + sequence: + - alias: Turn OFF the main entities + service: homeassistant.turn_on + target: + entity_id: !input main_entity + - alias: Turn OFF the alternating entity entities + service: homeassistant.turn_off + target: + entity_id: !input alternating_entity diff --git a/blueprints/automation/Blackshome/sensor-light.yaml b/blueprints/automation/Blackshome/sensor-light.yaml new file mode 100644 index 0000000..27adb5d --- /dev/null +++ b/blueprints/automation/Blackshome/sensor-light.yaml @@ -0,0 +1,8994 @@ +blueprint: + name: Sensor Light + description: "# \U0001F4A1 Sensor Light\n**Version: 8.1**\n\nYour lighting experience, + your way - take control and customize it to perfection! \U0001F4A1✨\n\n**If you + like my blueprints, and would like to show your support or just say thank you?** + [Click Here](https://www.paypal.com/donate/?hosted_button_id=WAZS3QSDTPGA8) \U0001F642\n\n
+ The Automation Process: \U0001F448\n\n\n - **Trigger:**\n + \ - You can choose any [binary sensors](https://www.home-assistant.io/integrations/binary_sensor/) + or [schedules](https://www.home-assistant.io/integrations/schedule) to trigger + the automation.\n - A trigger sensor, such as a Motion Sensor, will activate + lights, switches, scenes, and scripts when the automation is triggered.\n - + A time delay begins the turning OFF process after all triggers are clear. See + [grouping](https://community.home-assistant.io/t/481048/34) sensors for more information.\n + \ - If triggers are activated again before the time delay ends, the automation + resets for continuous lighting.\n\n - **Scene and Script Integration:**\n - + Scenes or scripts offer additional customization for your spaces.\n\n - **Light + Control Options:**\n - Utilize \"Light Control\" to adjust brightness, colour + temperature, colour, and transition times.\n\n - **Dynamic Lighting Options:**\n + \ - Opt for 'Dynamic Lighting' to automatically adjust brightness based on lux + values, and/or adjust brightness and colour temperature according to the sun's + position or the time of day.\n - Opt to use the 'State Control Option,' allowing + you to toggle between dynamic lighting and normal lighting based on the entity's + activation state.\n\n - **Manual Override:**\n - Use \"Bypass Options\" for + manual bypassing of trigger sensors, providing manual control and additional customization.\n\n + \ - **Sun Elevation Options:**\n - Implement \"Sun Elevation\" to restrict + automation to darker conditions aligned with the sun's position.\n\n - **Ambient + Light Sensing Options:**\n - Set up an Ambient Light Sensor for activation + in low-light conditions, customizable with specified LUX values.\n\n - **Time-Based + Options:**\n - Specify precise start and end times along with weekday selections + to define when the automation should run.\n\n - **Device Tracker Options:**\n + \ - Utilize \"Device Tracker\" to activate automation when home or within specified + zones, preventing unnecessary activations. This feature is particularly useful + to prevent unnecessary lighting activations when pets trigger sensors while no + one is home.\n\n - **Night Lights Mode:**\n - Enable \"Night Lights\" for + softer illumination during night time activities, automatically enabled based + on conditions.\n - Opt to use \"Night Glow\". This feature activates a low-level + light when no trigger is detected, creating a subtle ambient glow perfect for + nighttime use.\n\n - **HA Restart Safeguards:**\n - Benefit from built-in + safeguards for Home Assistant restarts.\n\n - **Blueprint Add-On Integration:**\n + \ - Sensor Light Add-On - Enhances media control automation. [Click Here](https://community.home-assistant.io/t/591824) + to learn more.\n - Bathroom Humidity Exhaust Fan - Keeps the lights ON when + showering. [Click Here](https://community.home-assistant.io/t/509992) to learn + more.\n
\n\nNeed help?\n- The Settings & Best Practice Guidelines: [Click + Here](https://community.home-assistant.io/t/481048/345)\n- FAQ: [Click Here](https://community.home-assistant.io/t/481048/6)\n- + Community Support Including Updates: [Click Here](https://community.home-assistant.io/t/481048)\n\nRequired + = *\n" + domain: automation + input: + trigger: + name: Trigger * + icon: mdi:cog-outline + collapsed: true + input: + motion_trigger: + name: Trigger Sensor - Binary Sensors - Schedule * + description: 'The trigger sensors are responsible for turning the lights, + switches, scenes, and scripts ON and OFF. You can choose any [binary sensors](https://www.home-assistant.io/integrations/binary_sensor/) + you prefer as triggers. Alternatively, if you prefer a time-based trigger, + you can also add a [schedule helper](https://community.home-assistant.io/t/481048/1616). + + + When using multiple trigger sensors, it''s advisable to group them together + using a group helper. This ensures smoother automation execution and prevents + conflicts. + + + For more information on grouping your trigger sensors [Click Here](https://community.home-assistant.io/t/481048/34) + + ' + default: [] + selector: + entity: + filter: + - domain: + - binary_sensor + - schedule + multiple: true + lights_settings: + name: Lights * + icon: mdi:lightbulb-outline + collapsed: true + input: + light_switch: + name: Lights - Switches - Scenes - Scripts * + description: 'The lights, switches, scenes, and scripts that will be activated + by the trigger sensor/s. If adding a scene or script, please refer to + the ''Scenes & Scripts - Toggle Helper'' section and the ''Scenes - Scripts + To Turn OFF'' section below. + + + For more information on how to use scenes & scripts [Click Here](https://community.home-assistant.io/t/481048/1595) + + + **NOTE** - You can only use entities. Areas, devices and labels are not + supported. + + ' + default: [] + selector: + target: + entity: + - domain: + - light + - switch + - scene + - script + boolean_scenes_scripts: + name: Scenes & Scripts - Toggle Helper + description: 'To ensure the smooth operation of the automation, it''s recommended + to create an independent toggle helper when selecting a scene or script + in "Lights - Switches - Scenes - Scripts" above and then enter it here. + + ' + default: [] + selector: + entity: + filter: + - domain: + - input_boolean + multiple: false + end_scenes: + name: Scenes - Scripts To Turn OFF + description: 'If you have selected a scene or a script to be turned ON above + in "Lights - Switches - Scenes - Scripts" or below in "Night Lights," + and you would like it to be turned OFF after the time delay, then you + must create a scene or a script with everything OFF and enter it here. + + ' + default: [] + selector: + entity: + multiple: true + filter: + - domain: + - scene + - script + time_delay: + name: Time Delay + description: 'The time delay determines how long the lights, switches, scenes, + and scripts will remain active after all triggers are clear, initiating + the time delay to turn them OFF. + + ' + default: 5 + selector: + number: + min: 0.0 + max: 30.0 + step: 0.5 + unit_of_measurement: minutes + mode: slider + normal_lights_settings: + name: Light Control + icon: mdi:lightbulb-on-outline + collapsed: true + input: + include_light_control: + name: Light Control Options (Optional) + description: 'Select if you would like to use brightness or transition. + These settings will only affect a ''light'' entity that supports each + selected option. The settings for brightness and transition are provided + below. + + ' + default: [] + selector: + select: + multiple: true + options: + - label: Use Brightness + value: use_brightness + - label: Use Transition + value: use_transition + custom_value: false + sort: false + light_brightness: + name: Brightness + description: 'The brightness setting for the lights when they are turned + ON. + + ' + default: 100 + selector: + number: + min: 1.0 + max: 100.0 + mode: slider + step: 1.0 + unit_of_measurement: '%' + light_transition_on: + name: Transition - ON + description: 'The transition setting for the lights when they are turned + ON. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 5.0 + mode: slider + step: 0.5 + unit_of_measurement: seconds + light_transition_off: + name: Transition - OFF + description: 'The transition setting for the lights when they are turned + OFF. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 30.0 + mode: slider + step: 1.0 + unit_of_measurement: seconds + include_light_colour_control: + name: Light Colour Control Options (Optional) + description: 'Select if you would like to use colour temperature, RGB, RGBW + or RGBWW colour. These settings will only affect a ''light'' entity that + supports each selected option. The settings for colour temperature, RGB, + RGBW and RGBWW colour are provided below. + + ' + default: disable_colour_control + selector: + select: + options: + - label: Use Colour Temperature + value: use_colour_temperature + - label: Use RGB Colour + value: use_rgb_colour + - label: Use RGBW Colour + value: use_rgbw_colour + - label: Use RGBWW Colour + value: use_rgbww_colour + - label: Disable Colour Control + value: disable_colour_control + custom_value: false + multiple: false + sort: false + light_colour_temperature: + name: Colour Temperature + description: 'The colour temperature setting for the lights when they are + turned ON. + + ' + default: 5000 + selector: + number: + min: 2000.0 + max: 8000.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + light_rgb_colour: + name: RGB Colour + description: 'The RGB colour setting for the lights when they are turned + ON. + + ' + default: + - 255 + - 255 + - 255 + selector: + color_rgb: {} + light_rgbw_colour: + name: RGBW Colour + description: 'The RGBW colour setting for the lights when they are turned + ON. Enter four integers between 0 and 255 to define the values for Red, + Green, Blue, and White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + light_rgbww_colour: + name: RGBWW Colour + description: 'The RGBWW colour setting for the lights when they are turned + ON. Enter five integers between 0 and 255 to define the values for Red, + Green, Blue, Cold White and Warm White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + dynamic_lighting_settings: + name: Dynamic Lighting + icon: mdi:creation-outline + collapsed: true + input: + include_dynamic_lighting: + name: Use The Dynamic Lighting Options (Optional) + description: "This option makes continual adjustments to your normal lighting + setup by modulating brightness based on floating lux values, or adapting + both colour temperature and brightness according to the sun's changing + elevation or the time of day. Choose from thirteen preset options available + in the dropdown menu.\n\n 1 - Lux Controlled Brightness\n 2 - Lux Controlled + Brightness Inverted\n 3 - Sun Elevation Lighting - Colour Temp\n 4 - + Sun Elevation Lighting - Brightness\n 5 - Sun Elevation Lighting - Brightness + Inverted\n 6 - Sun Elevation Lighting - Colour Temp + Brightness\n 7 + - Sun Elevation Lighting - Colour Temp + Brightness Inverted\n 8 - Sun + Elevation Lighting - Colour Temp + Lux Controlled Brightness\n 9 - Sun + Elevation Lighting - Colour Temp + Lux Controlled Brightness Inverted\n + \ 10 - Time Controlled - Colour Temp\n 11 - Time Controlled - Brightness\n + \ 12 - Time Controlled - Brightness Inverted\n 13 - Time Controlled - + Colour Temp + Brightness\n\nA numbering system has been implemented to + facilitate navigation within the dropdown selections. Each number corresponds + to a specific configuration, aiding users in identifying and adjusting + the settings used within each selection. For instance, when selecting + \"3 - Sun Elevation Lighting - Colour Temp\" as a dropdown option, settings + marked \"Used in options 3, 6, 7, 8, 9, 10 or 13\" are required for that + respective selection because number 3 is included in those options.\n\nFor + more information on dynamic lighting settings [Click Here](https://community.home-assistant.io/t/481048/837)\n" + default: disable_dynamic_lighting + selector: + select: + mode: dropdown + options: + - label: Disable Dynamic Lighting + value: disable_dynamic_lighting + - label: 1 - Lux Controlled Brightness + value: enable_lux_controled_brightness + - label: 2 - Lux Controlled Brightness Inverted + value: enable_lux_controled_brightness_inv + - label: 3 - Sun Elevation Lighting - Colour Temp + value: enable_sun_elevation_colour + - label: 4 - Sun Elevation Lighting - Brightness + value: enable_sun_elevation_brightness + - label: 5 - Sun Elevation Lighting - Brightness Inverted + value: enable_sun_elevation_brightness_inv + - label: 6 - Sun Elevation Lighting - Colour Temp + Brightness + value: enable_sun_elevation_colour_brightness + - label: 7 - Sun Elevation Lighting - Colour Temp + Brightness Inverted + value: enable_sun_elevation_colour_brightness_inv + - label: 8 - Sun Elevation Lighting - Colour Temp + Lux Controlled Brightness + value: enable_sun_elevation_colour_lux_brightness + - label: 9 - Sun Elevation Lighting - Colour Temp + Lux Controlled Brightness + Inverted + value: enable_sun_elevation_colour_lux_brightness_inv + - label: 10 - Time Controlled - Colour Temp + value: enable_time_controled_colour + - label: 11 - Time Controlled - Brightness + value: enable_time_controled_brightness + - label: 12 - Time Controlled - Brightness Inverted + value: enable_time_controled_brightness_inv + - label: 13 - Time Controlled - Colour Temp + Brightness + value: enable_time_controled_colour_brightness + custom_value: false + multiple: false + sort: false + dynamic_lighting_lux_sensor: + name: Dynamic Lighting - Ambient Light Sensor + description: '**Used in options 1, 2, 8 or 9** - Enter the specific ambient + light sensor to be used, based on your selected Dynamic Lighting option. + + ' + default: [] + selector: + entity: + filter: + - domain: + - sensor + device_class: + - illuminance + multiple: false + dynamic_lighting_max_lux: + name: Dynamic Lighting - Max Lux Value + description: '**Used in options 1, 2, 8 or 9** - Specify the maximum lux + value. Once the lux level meets or surpasses this set point, your lights + will adjust to either their maximum or minimum brightness, depending on + your chosen option. This value indicates when you prefer your lights + to start turning on or off. + + ' + default: 400 + selector: + number: + min: 10.0 + max: 900.0 + step: 10.0 + unit_of_measurement: lux + mode: slider + dynamic_lighting_min_lux: + name: Dynamic Lighting - Min Lux Value + description: '**Used in options 1, 2, 8 or 9** - Specify the minimum lux + value. Once the lux level meets or surpasses this set point, your lights + will adjust to either their maximum or minimum brightness, depending on + your chosen option. This value indicates when you prefer your lights + to start turning on or off. + + ' + default: 40 + selector: + number: + min: 0.0 + max: 600.0 + step: 10.0 + unit_of_measurement: lux + mode: slider + dynamic_lighting_max_brightness: + name: Dynamic Lighting - Max Brightness Value + description: '**Used in options 1, 2, 4, 5, 6, 7, 8, 9, 11, 12 or 13** - + Define the maximum brightness value, determines the peak brightness level + for your lights. + + ' + default: 100 + selector: + number: + min: 10.0 + max: 100.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + dynamic_lighting_min_brightness: + name: Dynamic Lighting - Min Brightness Value + description: '**Used in options 1, 2, 4, 5, 6, 7, 8, 9, 11, 12 or 13** - + Specify the minimum brightness value, establishing the lowest brightness + level for your lights. When setting the brightness to 0%, the light will + be turned off. + + ' + default: 0 + selector: + number: + min: 0.0 + max: 100.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + dynamic_lighting_boolean: + name: Dynamic Lighting - Toggle Helper (Optional) + description: '**Used in options 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12 or 13** + - If you have chosen a brightness level of 0%, then it''s essential to + create an independent toggle helper and enter it here. This toggle helper + can also address issues if the automation doesn''t behave as expected + due to the occasional unavailability of light entities. While it may not + always be necessary, if you''re open to creating toggle helpers, it''s + highly recommended to utilize this option to ensure proper functionality + of the automation. + + ' + default: [] + selector: + entity: + filter: + - domain: + - input_boolean + multiple: false + dynamic_lighting_max_colour_temp: + name: Dynamic Lighting - Max Colour Temperature + description: '**Used in options 3, 6, 7, 8, 9, 10 or 13** - Specify the + highest colour temperature value, determining the coolest colour setting + for your lights. + + ' + default: 5000 + selector: + number: + min: 2500.0 + max: 8000.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + dynamic_lighting_min_colour_temp: + name: Dynamic Lighting - Min Colour Temperature + description: '**Used in options 3, 6, 7, 8, 9, 10 or 13** - Set the lowest + colour temperature value, defining the warmest colour setting for your + lights. + + ' + default: 3000 + selector: + number: + min: 2000.0 + max: 7500.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + dynamic_lighting_sun_elevation_start_rising: + name: Dynamic Lighting - Sun Elevation Rising - Start Point + description: '**Used in options 3, 4, 5, 6, 7, 8 or 9** - When the sun rises + above the sensor''s starting point, both the colour temperature and brightness + percentage will transition linearly from their minimum to maximum values + until they reach the rising endpoint, based on your selected Dynamic Lighting + option. + + ' + default: -1.5 + selector: + number: + min: -10.0 + max: 30.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + dynamic_lighting_sun_elevation_end_rising: + name: Dynamic Lighting - Sun Elevation Rising - End Point + description: '**Used in options 3, 4, 5, 6, 7, 8 or 9** - When the sun rises + above the sensor''s endpoint, the maximum colour temperature and maximum + brightness value will be established, based on your selected Dynamic Lighting + option. + + ' + default: 15 + selector: + number: + min: 0.0 + max: 90.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + dynamic_lighting_sun_elevation_start_falling: + name: Dynamic Lighting - Sun Elevation Descending - Start Point + description: '**Used in options 3, 4, 5, 6, 7, 8 or 9** - When the sun descends + below the sensor''s starting point, both the colour temperature and brightness + percentage will transition linearly from their maximum to minimum values + until they reach the descending endpoint, based on your selected Dynamic + Lighting option. + + ' + default: 15 + selector: + number: + min: 0.0 + max: 90.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + dynamic_lighting_sun_elevation_end_falling: + name: Dynamic Lighting - Sun Elevation Descending - End Point + description: '**Used in options 3, 4, 5, 6, 7, 8 or 9** - When the sun descends + below the sensor''s endpoint, the minimum colour temperature and minimum + brightness value will be established, based on your selected Dynamic + Lighting option. + + ' + default: -4.0 + selector: + number: + min: -10.0 + max: 30.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + dynamic_lighting_time_start_morning: + name: Dynamic Lighting - Time Morning - Start Point + description: '**Used in options 10, 11, 12 or 13** - When the time passes + over the start point, both the colour temperature and brightness percentage + will transition linearly from their minimum to maximum values until they + reach the rising endpoint, based on your selected Dynamic Lighting option. + + ' + default: 06:00:00 + selector: + time: {} + dynamic_lighting_time_end_morning: + name: Dynamic Lighting - Time Morning - End Point + description: '**Used in options 10, 11, 12 or 13** - When the time passes + over the end point, the maximum colour temperature and maximum brightness + value will be established, based on your selected Dynamic Lighting option. + + ' + default: 09:00:00 + selector: + time: {} + dynamic_lighting_time_start_evening: + name: Dynamic Lighting - Time Evening - Start Point + description: '**Used in options 10, 11, 12 or 13** - When the time passes + over the start point, both the colour temperature and brightness percentage + will transition linearly from their maximum to minimum values until they + reach the descending endpoint, based on your selected Dynamic Lighting + option. + + ' + default: '17:00:00' + selector: + time: {} + dynamic_lighting_time_end_evening: + name: Dynamic Lighting - Time Evening - End Point + description: '**Used in options 10, 11, 12 or 13** - When the time passes + over the end point, the minimum colour temperature and minimum brightness + value will be established, based on your selected Dynamic Lighting option. + + ' + default: '19:00:00' + selector: + time: {} + dynamic_lighting_heartbeat: + name: Dynamic Lighting - Heartbeat + description: '**Used in options 1, 2, 8, 9, 10, 11, 12 or 13** - Define + the heartbeat rate to set the operational speed. During each heartbeat, + it may evaluate the lux value, light brightness, colour temperature, target + colour temperature, and target brightness, adjusting the colour temperature + or brightness based on your selected Dynamic Lighting option. + + + **Note** - When using a **Time Controlled** option (10, 11, 12, or 13), + the minimum heartbeat should be set to no lower than 1 minute, with adjustments + made in 1-minute increments (e.g., 1, 2, 3, 4, 5, and so on), as setting + it lower will not provide any benefit and may slow down your system''s + performance. Depending on the duration between the start and end points + of the light transition, adjusting this value will determine the size + of the step value. A longer heartbeat generally improves performance, + but it may also result in larger step values that could be bothersome + to the eye. Take the time to determine the correct heartbeat value for + your needs. The heartbeat is only active between the start and end points. + + ' + default: 1 + selector: + number: + min: 0.25 + max: 10.0 + step: 0.25 + unit_of_measurement: minutes + mode: slider + dynamic_lighting_step_value: + name: Dynamic Lighting - Step Value + description: '**Used in options 1, 2, 8, or 9** - With every heartbeat, + the system evaluates the lux value, light brightness, and targeted brightness. + If the targeted brightness exceeds the predefined step value, the system + applies the set step value to gradually moderate the rate of change, ensuring + a smoother transition in light brightness. + + ' + default: 4 + selector: + number: + min: 1.0 + max: 100.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + dynamic_lighting_dead_zone: + name: Dynamic Lighting - Dead Zone (±) + description: '**Used in options 1, 2, 8, or 9** - With every heartbeat, + the system evaluates the current light brightness and the targeted brightness. + If the targeted brightness falls within the defined dead zone, the system + maintains the current light brightness. This prevents minor fluctuations + in light intensity that could be bothersome to the eye. + + ' + default: 7 + selector: + number: + min: 0.0 + max: 15.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + dynamic_lighting_state_control: + name: Dynamic Lighting - State Control Option (Optional) + description: 'Enable or disable dynamic lighting based on the state of a + specified entity. When the entity is in the selected activation state + below, dynamic lighting will function as configured. When the entity is + not in the activation state below, dynamic lighting will be disabled. + This allows you to toggle between normal lighting and dynamic lighting + based on the entity''s state. + + ' + default: disable_state_control + selector: + select: + options: + - label: Enable State Control Option + value: enable_state_control + - label: Disable State Control Option + value: disable_state_control + custom_value: false + multiple: false + sort: false + dynamic_lighting_state_control_activation_state: + name: Dynamic Lighting - State Control Activation State + description: 'Select the state the entity must be in to activate dynamic + lighting. When the entity is in the selected state (ON or OFF), dynamic + lighting will be enabled. This option defines the activation condition + for dynamic lighting. + + ' + default: 'on' + selector: + select: + options: + - label: ON State + value: 'on' + - label: OFF State + value: 'off' + custom_value: false + multiple: false + sort: false + dynamic_lighting_state_control_entity: + name: Dynamic Lighting - State Control Entity + description: 'If the state control option above is enabled, select the entity + that will toggle dynamic lighting based on its activation state above. + + ' + default: [] + selector: + entity: {} + bypass_settings: + name: Bypass + icon: mdi:cog-pause-outline + collapsed: true + input: + include_bypass: + name: Use The Bypass Options (Optional) + description: "Select if you would like to enable an option. Each option + determines how your lights will behave when the bypass switch is turned + ON:\n\n 1 - Turns the lights ON\n 2 - Turns the lights OFF\n 3 - Keeps + the lights current state\n\nIf you enable an option, make sure to enter + the corresponding bypass switch in the input below.\n\nFor more information + on how to use the bypass [Click Here](https://community.home-assistant.io/t/481048/895)\n" + default: [] + selector: + select: + options: + - label: 1 - Enable the Bypass - Turn the Lights ON + value: bypass_enabled_turn_on + - label: 2 - Enable the Bypass - Turn the Lights OFF + value: bypass_enabled_turn_off + - label: 3 - Enable the Bypass - Keep the Lights Current State + value: bypass_enabled_stop + multiple: true + custom_value: false + sort: false + motion_bypass_lights_on: + name: Bypass Switch - Turn the Lights ON + description: 'Select the switches that will turn your lights ON, bypass + the trigger sensor, and allow your lights to function as normal. Please + note that the entity cannot be included in the ''Lights - Switches - Scenes + - Scripts'' or ''Night Lights'' selections. + + ' + default: [] + selector: + entity: + multiple: true + motion_bypass_lights_off: + name: Bypass Switch - Turn the Lights OFF + description: 'Select the switches that will turn your lights OFF, bypass + the trigger sensor, and allow your lights to function as normal. Please + note that the entity cannot be included in the ''Lights - Switches - Scenes + - Scripts'' or ''Night Lights'' selections. + + ' + default: [] + selector: + entity: + multiple: true + motion_bypass_lights_stop: + name: Bypass Switch - Keep the Lights Current State + description: 'Select the switches that will keep your lights current state, + bypass the trigger sensor, and allow your lights to function as normal. + Please note that the entity cannot be included in the ''Lights - Switches + - Scenes - Scripts'' or ''Night Lights'' selections. + + ' + default: [] + selector: + entity: + multiple: true + bypass_time_delay: + name: Bypass - Time Delay + description: "This is only used in two specific bypass scenarios when your + lights are ON:\n\n 1. When you have selected option 2 above to \"Enable + the Bypass - Turn the Lights OFF\" and you turn the bypass ON.\n 2. When + the bypass is turned OFF, the trigger sensor is OFF, and your lights are + still ON.\n\nIn both cases, the automation will turn your lights OFF after + the set time delay.\n" + default: 0 + selector: + number: + min: 0.0 + max: 10.0 + step: 0.25 + unit_of_measurement: minutes + mode: slider + include_bypass_auto_off: + name: Use The Bypass Auto OFF Option (Optional) + description: "Enable this option if you want the bypass to automatically + turn OFF after a set time delay. You can choose which bypass option the + auto OFF will apply to:\n\n 1 - Turns the lights ON\n 2 - Turns the + lights OFF\n 3 - Keeps the lights current state\n\nEach option corresponds + to one of the bypass options above.\n" + default: [] + selector: + select: + options: + - label: A - Enable Auto OFF for Bypass Option 1 - Turn the Lights ON + value: bypass_auto_off_enabled_on + - label: B - Enable Auto OFF for Bypass Option 2 - Turn the Lights OFF + value: bypass_auto_off_enabled_off + - label: C - Enable Auto OFF for Bypass Option 3 - Keep the Lights Current + State + value: bypass_auto_off_enabled_stop + multiple: true + custom_value: false + sort: false + bypass_auto_off_delay: + name: Bypass Auto OFF - Time Delay + description: 'Set the bypass auto OFF time delay. The time delay starts + from the last bypass that was turned ON. + + ' + default: 60 + selector: + number: + min: 1.0 + max: 240.0 + step: 1.0 + unit_of_measurement: minutes + mode: slider + sun_settings: + name: Sun Elevation + icon: mdi:weather-sunny + collapsed: true + input: + include_sun: + name: Use The Sun Option (Optional) + description: 'This option is used to add a condition that only allows the + automation to run when it is dark or below the ''Sun Elevation'' settings. + It''s a global condition that can work alongside other options. Enabling + this option is not necessary for the night lights sun elevation condition + to work. + + + For more information on sun settings [Click Here](https://community.home-assistant.io/t/481048/37) + + ' + default: sun_disabled + selector: + select: + options: + - label: Enable the sun option + value: sun_enabled + - label: Disable the sun option + value: sun_disabled + custom_value: false + multiple: false + sort: false + sun_elevation: + name: Sun Elevation Falling + description: 'The sun elevation falling refers to the angle between the + sun and the horizon when the sun is setting. A negative value indicates + that the sun is BELOW the horizon. For example, a setting guide of -1.5 + corresponds to dusk + + ' + default: -1.5 + selector: + number: + min: -10.0 + max: 5.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + sun_elevation_rising: + name: Sun Elevation Rising + description: 'The sun elevation rising refers to the angle between the sun + and the horizon during sunrise. A negative value indicates that the sun + is BELOW the horizon. For example, a setting guide of -4.0 corresponds + to dawn. + + ' + default: -4.0 + selector: + number: + min: -10.0 + max: 5.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + ambient_settings: + name: Ambient + icon: mdi:theme-light-dark + collapsed: true + input: + include_ambient: + name: Use The Ambient Options (Optional) + description: 'This option is used to add a condition that only allows the + automation to run when it is dark or below the ''Ambient'' settings. It''s + a global condition that can work alongside other options. + + + For more information on ambient settings [Click Here](https://community.home-assistant.io/t/481048/1608) + + ' + default: ambient_disabled + selector: + select: + options: + - label: Enable the ambient options + value: ambient_enabled + - label: Disable the ambient options + value: ambient_disabled + custom_value: false + multiple: false + sort: false + ambient_light_sensor: + name: Ambient Light Sensor + description: 'Select the ambient sensor to be used. + + ' + default: [] + selector: + entity: + filter: + - domain: + - sensor + device_class: + - illuminance + multiple: false + ambient_light_options: + name: Ambient Light Sensor - Site Conditions + description: 'In some cases when your lights turn ON, your ambient light + sensor is affected, increasing its LUX value. This can cause the lights + to go OFF prematurely. Please select an option that best suits your installation. + + + **NOTE** - If using an offset between the high and low lux value, then + please select NO. + + ' + default: ambient_light_option_disabled + selector: + select: + options: + - label: YES - My Ambient Light Sensor is affected by the Lights + value: ambient_light_option_enabled + - label: NO - My Ambient Light Sensor is not affected by the Lights + value: ambient_light_option_disabled + custom_value: false + multiple: false + sort: false + ambient_light_value: + name: Ambient Light - Low Lux Value + description: 'Set the Ambient Light Low Lux Value. The light will turn ON + when the lux level is below the set value. This value must be equal or + lower than the "High Lux Value" below. Guide is 20 lux (dusk). + + ' + default: 20 + selector: + number: + min: 0.0 + max: 500.0 + step: 10.0 + unit_of_measurement: lux + mode: slider + ambient_light_high_value: + name: Ambient Light - High Lux Value + description: 'Set the Ambient Light High Lux Value. The light will turn + OFF when the lux level is above the set value. This value must be equal + or higher than the "Low Lux Value" above. Setting a value higher than + the low value allows for an offset. Guide is 80 lux (dawn). + + ' + default: 80 + selector: + number: + min: 0.0 + max: 1000.0 + step: 10.0 + unit_of_measurement: lux + mode: slider + time_settings: + name: Time + icon: mdi:clock-outline + collapsed: true + input: + include_time: + name: Use The Time Options (Optional) + description: 'This option is used to add a condition that only allows the + automation to run between the start time and end time settings on the + selected weekdays. These time settings are global conditions that can + work alongside other options. Enabling this option is not necessary for + the night lights time condition to work. + + + For more information on time settings [Click Here](https://community.home-assistant.io/t/481048/310) + + ' + default: time_disabled + selector: + select: + options: + - label: Enable the time options + value: time_enabled + - label: Disable the time options + value: time_disabled + custom_value: false + multiple: false + sort: false + after_time: + name: Start Time + description: 'Set the start time. + + ' + default: 00:00:00 + selector: + time: {} + before_time: + name: End Time + description: 'Set the end time. + + ' + default: 00:00:00 + selector: + time: {} + weekday_options: + name: Weekdays + description: 'Select the days of the week you would like the automation + to run. You must select "Enable the time options" above for the weekday + selections to work. + + ' + default: + - mon + - tue + - wed + - thu + - fri + - sat + - sun + selector: + select: + multiple: true + mode: list + options: + - label: Monday + value: mon + - label: Tuesday + value: tue + - label: Wednesday + value: wed + - label: Thursday + value: thu + - label: Friday + value: fri + - label: Saturday + value: sat + - label: Sunday + value: sun + custom_value: false + sort: false + device_tracker_settings: + name: Device Tracker + icon: mdi:account-multiple-check-outline + collapsed: true + input: + include_device_tracker: + name: Use The Device Tracker Options (Optional) + description: 'Home Assistant can track the location of your devices (such + as mobile phones) within a designated zone via the mobile app device tracker. + To utilize this option, you need to set up a zone and configure your devices + for tracking in Home Assistant. There are two enable options available; + "Zone" tracks all devices within the zone, while "Zone + People" allows + you to track individuals within a zone. This feature can be particularly + useful if you have pets triggering the sensor, turning your lights ON + and OFF when no one is home. + + ' + default: device_tracker_disabled + selector: + select: + options: + - label: Enable the zone option + value: zone_enabled + - label: Enable the zone + people options + value: zone_people_enabled + - label: Disable the device tracker options + value: device_tracker_disabled + custom_value: false + multiple: false + sort: false + zone: + name: Device Tracker - Zone + description: 'Choose the zone that will track your devices and/or people + in it. + + ' + default: [] + selector: + entity: + filter: + - domain: + - zone + multiple: false + people: + name: Device Tracker - People + description: 'Select the people you would like to track in the zone selected + above. You must enter a zone above for this option to work. + + ' + default: [] + selector: + entity: + multiple: true + filter: + - domain: + - person + night_lights_trigger_settings: + name: Night Lights Settings + icon: mdi:weather-night + collapsed: true + input: + include_night_lights: + name: Use The Night Lights Options (Optional) + description: 'Enabling the night lights options allows for separate and + customizable lighting control when the night lights conditions are met. + Night light condition options include entity state, time, or sun elevation. + Please choose the condition option you would like to use below. This feature + is particularly beneficial for creating a soothing ambiance with softer + lighting at night. The flexibility of these options allows for personalized + adjustments, enhancing your night time ambiance and comfort, especially + when moving around during the night. + + ' + default: night_lights_disabled + selector: + select: + options: + - label: Enable the night lights options + value: night_lights_enabled + - label: Disable the night lights options + value: night_lights_disabled + custom_value: false + multiple: false + sort: false + night_lights_conditions: + name: Night Lights Conditions (Required For Night Lights) + description: 'Select any night light condition from the options provided + and ensure that **Use Night Lights Options** is enabled in the section + above. You must choose at least one ''Night Light Condition'' for the + night lights to operate, and the settings for each condition are provided + below. If you select two or more conditions, all selected conditions must + be met for the night lights to activate. + + ' + default: [] + selector: + select: + multiple: true + options: + - label: Enable entity state option + value: entity_state_enabled + - label: Enable time option + value: time_enabled + - label: Enable sun elevation option + value: sun_enabled + custom_value: false + sort: false + night_lights_entity_state: + name: Night Lights - Entity State + description: 'Select an entity that will trigger the activation of night + lights when turned ON. This could be your phone on ''Do Not Disturb'', + a helper, a bed sensor, etc. Please note that the selected entity cannot + be included in the ''Lights - Switches - Scenes - Scripts'' or ''Night + Lights'' selections. + + + For more information on entity state [Click Here](https://community.home-assistant.io/t/481048/663) + + ' + default: [] + selector: + entity: + multiple: true + night_lights_after_time: + name: Night Lights - Start Time + description: 'Set the start time. + + ' + default: 00:00:00 + selector: + time: {} + night_lights_before_time: + name: Night Lights - End Time + description: 'Set the end time. + + ' + default: 00:00:00 + selector: + time: {} + night_lights_sun_elevation: + name: Night Lights - Sun Elevation Falling + description: 'The sun elevation falling refers to the angle between the + sun and the horizon when the sun is setting. A negative value indicates + that the sun is BELOW the horizon. For example, a setting guide of -1.5 + corresponds to dusk. + + ' + default: -1.5 + selector: + number: + min: -10.0 + max: 5.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + night_lights_sun_elevation_rising: + name: Night Lights - Sun Elevation Rising + description: 'The sun elevation rising refers to the angle between the sun + and the horizon during sunrise. A negative value indicates that the sun + is BELOW the horizon. For example, a setting guide of -4.0 corresponds + to dawn. + + ' + default: -4.0 + selector: + number: + min: -10.0 + max: 5.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + night_lights_settings: + name: Night Lights + icon: mdi:lightbulb-night-outline + collapsed: true + input: + night_lights: + name: Night Lights + description: 'The lights, switches, scenes, and scripts that will be activated + by the trigger sensor/s. If adding a scene or script, please refer to + the ''Night Lights - Scenes & Scripts - Toggle Helper'' section below + and the ''Scenes - Scripts To Turn OFF'' input in the lights section above. + + + For more information on how to use scenes & scripts [Click Here](https://community.home-assistant.io/t/481048/1595) + + + **NOTE** - You can only use entities. Areas, devices and labels are not + supported. + + ' + default: {} + selector: + target: + entity: + - domain: + - light + - switch + - scene + - script + night_boolean_scenes_scripts: + name: Night Lights - Scenes & Scripts - Toggle Helper + description: 'To ensure the smooth operation of the automation, it''s recommended + to create an independent toggle helper when selecting a scene or script + in "Night Lights" above and then enter it here. + + ' + default: [] + selector: + entity: + filter: + - domain: + - input_boolean + multiple: false + night_time_delay: + name: Night Lights - Time Delay + description: 'The time delay determines how long the night lights will remain + active after all triggers are clear, initiating the time delay to turn + them OFF. + + ' + default: 5 + selector: + number: + min: 0.0 + max: 30.0 + step: 0.5 + unit_of_measurement: minutes + mode: slider + night_lights_light_control_settings: + name: Night Light Control + icon: mdi:lightbulb-on-80 + collapsed: true + input: + include_night_light_control: + name: Night Lights - Light Control Options (Optional) + description: "Select if you would like to use brightness or transition. + These settings will only affect a 'light' entity that supports each selected + option. The settings for brightness and transition are provided below. + \n\nSelecting 'If lights are ON, adjust the lights when crossing over' + option ensures that the light control settings will be applied to any + lights that are already ON during the transition from normal lights to + night lights and vice versa.\n\nSelecting 'Yes - Manage OFF script when + crossing over' is beneficial for scripts containing if-then-else actions + that toggle various functions on and off during the transition from normal + lights to night lights and vice versa.\n" + default: [] + selector: + select: + multiple: true + options: + - label: Use brightness + value: use_brightness + - label: Use transition + value: use_transition + - label: If lights are ON, adjust the lights when crossing over + value: if_lights_are_on_adjust_when_crossing_over + - label: Yes - Manage OFF script when crossing over + value: manage_scripts_crossing_over + custom_value: false + sort: false + night_light_brightness: + name: Night Lights - Brightness + description: 'The brightness setting of the lights when they are turned + ON. + + ' + default: 20 + selector: + number: + min: 1.0 + max: 100.0 + mode: slider + step: 1.0 + unit_of_measurement: '%' + night_light_transition_on: + name: Night Lights - Transition - ON + description: 'The transition of the lights when they are turned ON. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 5.0 + mode: slider + step: 0.5 + unit_of_measurement: seconds + night_light_transition_off: + name: Night Lights - Transition - OFF + description: 'The transition of the lights when they are turned OFF. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 30.0 + mode: slider + step: 1.0 + unit_of_measurement: seconds + include_night_light_colour_control: + name: Night Lights - Light Colour Control Options (Optional) + description: 'Select if you would like to use colour temperature, RGB, RGBW + or RGBWW colour. These settings will only affect a ''light'' entity that + supports each selected option. The settings for colour temperature, RGB, + RGBW and RGBWW colour are provided below + + ' + default: disable_colour_control + selector: + select: + options: + - label: Use Colour Temperature + value: use_colour_temperature + - label: Use RGB Colour + value: use_rgb_colour + - label: Use RGBW Colour + value: use_rgbw_colour + - label: Use RGBWW Colour + value: use_rgbww_colour + - label: Disable Colour Control + value: disable_colour_control + custom_value: false + multiple: false + sort: false + night_light_colour_temperature: + name: Night Lights - Colour Temperature + description: 'The colour temperature setting for the lights when they are + turned ON. + + ' + default: 5000 + selector: + number: + min: 2000.0 + max: 8000.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + night_light_rgb_colour: + name: Night Lights - RGB Colour + description: 'The RGB colour setting for the lights when they are turned + ON. + + ' + default: + - 255 + - 255 + - 255 + selector: + color_rgb: {} + night_light_rgbw_colour: + name: Night Lights - RGBW Colour + description: 'The RGBW colour setting for the lights when they are turned + ON. Enter four integers between 0 and 255 to define the values for Red, + Green, Blue, and White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + night_light_rgbww_colour: + name: Night Lights - RGBWW Colour + description: 'The RGBWW colour setting for the lights when they are turned + ON. Enter five integers between 0 and 255 to define the values for Red, + Green, Blue, Cold White and Warm White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + night_lights_night_glow: + name: Night Glow + icon: mdi:candle + collapsed: true + input: + include_night_glow: + name: Night Glow (Optional) + description: 'This feature activates a low-level light when no trigger is + detected, creating a subtle ambient glow perfect for nighttime use. When + a trigger, such as a motion sensor, is detected, the light gradually increases + to the night light''s brightness level, providing enhanced visibility + and functionality without abrupt changes in lighting. You can also customize + the light''s colour temperature or RGB colour for added personalization. + + ' + default: night_glow_disabled + selector: + select: + options: + - label: Enable night glow + value: night_glow_enabled + - label: Disable night glow + value: night_glow_disabled + custom_value: false + multiple: false + sort: false + night_glow_lights: + name: Night Glow - Lights + description: 'The lights, switches, scenes, and scripts to be used for a + night glow. If adding a scene or script, please refer to the ''Scenes + - Scripts To Turn OFF'' input in the lights section above. + + + For more information on how to use scenes & scripts [Click Here](https://community.home-assistant.io/t/481048/1595) + + + **NOTE** - You can only use entities. Areas, devices and labels are not + supported. + + ' + default: {} + selector: + target: + entity: + - domain: + - light + - switch + - scene + - script + include_night_glow_light_control: + name: Night Glow - Light Control Options (Optional) + description: "Select if you would like to use brightness or transition. + These settings will only affect a 'light' entity that supports each selected + option. The settings for brightness and transition are provided below. + \n" + default: [] + selector: + select: + multiple: true + options: + - label: Use brightness + value: use_brightness + - label: Use transition + value: use_transition + custom_value: false + sort: false + night_glow_light_brightness: + name: Night Glow - Brightness + description: 'The brightness setting of the lights when they are turned + ON. + + ' + default: 20 + selector: + number: + min: 1.0 + max: 100.0 + mode: slider + step: 1.0 + unit_of_measurement: '%' + night_glow_light_transition_on: + name: Night Glow - Transition - ON + description: 'The transition of the lights when they are turned ON. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 5.0 + mode: slider + step: 0.5 + unit_of_measurement: seconds + night_glow_light_transition_off: + name: Night Glow - Transition - OFF + description: 'The transition of the lights when they are turned OFF. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 30.0 + mode: slider + step: 1.0 + unit_of_measurement: seconds + include_night_glow_light_colour_control: + name: Night Glow - Light Colour Control Options (Optional) + description: 'Select if you would like to use colour temperature, RGB, RGBW + or RGBWW colour. These settings will only affect a ''light'' entity that + supports each selected option. The settings for colour temperature, RGB, + RGBW and RGBWW colour are provided below + + ' + default: disable_colour_control + selector: + select: + options: + - label: Use Colour Temperature + value: use_colour_temperature + - label: Use RGB Colour + value: use_rgb_colour + - label: Use RGBW Colour + value: use_rgbw_colour + - label: Use RGBWW Colour + value: use_rgbww_colour + - label: Disable Colour Control + value: disable_colour_control + custom_value: false + multiple: false + sort: false + night_glow_light_colour_temperature: + name: Night Glow - Colour Temperature + description: 'The colour temperature setting for the lights when they are + turned ON. + + ' + default: 5000 + selector: + number: + min: 2000.0 + max: 8000.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + night_glow_light_rgb_colour: + name: Night Glow - RGB Colour + description: 'The RGB colour setting for the lights when they are turned + ON. + + ' + default: + - 255 + - 255 + - 255 + selector: + color_rgb: {} + night_glow_light_rgbw_colour: + name: Night Glow - RGBW Colour + description: 'The RGBW colour setting for the lights when they are turned + ON. Enter four integers between 0 and 255 to define the values for Red, + Green, Blue, and White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + night_glow_light_rgbww_colour: + name: Night Glow - RGBWW Colour + description: 'The RGBWW colour setting for the lights when they are turned + ON. Enter five integers between 0 and 255 to define the values for Red, + Green, Blue, Cold White and Warm White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + source_url: https://gist.github.com/Blackshome/6edfec0ff6a25c5da0d07b88dc908238 +mode: restart +max_exceeded: silent +variables: + motion_trigger: !input motion_trigger + light_switch: !input light_switch + boolean_scenes_scripts: !input boolean_scenes_scripts + end_scenes: !input end_scenes + time_delay: !input time_delay + include_light_control: !input include_light_control + light_brightness: !input light_brightness + light_transition_on: !input light_transition_on + light_transition_off: !input light_transition_off + include_light_colour_control: !input include_light_colour_control + light_colour_temperature: !input light_colour_temperature + light_rgb_colour: !input light_rgb_colour + light_rgbw_colour: !input light_rgbw_colour + light_rgbww_colour: !input light_rgbww_colour + include_dynamic_lighting: !input include_dynamic_lighting + dynamic_lighting_lux_sensor: !input dynamic_lighting_lux_sensor + dynamic_lighting_max_lux: !input dynamic_lighting_max_lux + dynamic_lighting_min_lux: !input dynamic_lighting_min_lux + dynamic_lighting_max_brightness: !input dynamic_lighting_max_brightness + dynamic_lighting_min_brightness: !input dynamic_lighting_min_brightness + dynamic_lighting_boolean: !input dynamic_lighting_boolean + dynamic_lighting_max_colour_temp: !input dynamic_lighting_max_colour_temp + dynamic_lighting_min_colour_temp: !input dynamic_lighting_min_colour_temp + dynamic_lighting_sun_elevation_start_rising: !input dynamic_lighting_sun_elevation_start_rising + dynamic_lighting_sun_elevation_end_rising: !input dynamic_lighting_sun_elevation_end_rising + dynamic_lighting_sun_elevation_start_falling: !input dynamic_lighting_sun_elevation_start_falling + dynamic_lighting_sun_elevation_end_falling: !input dynamic_lighting_sun_elevation_end_falling + dynamic_lighting_time_start_morning: !input dynamic_lighting_time_start_morning + dynamic_lighting_time_end_morning: !input dynamic_lighting_time_end_morning + dynamic_lighting_time_start_evening: !input dynamic_lighting_time_start_evening + dynamic_lighting_time_end_evening: !input dynamic_lighting_time_end_evening + dynamic_lighting_heartbeat: !input dynamic_lighting_heartbeat + dynamic_lighting_step_value: !input dynamic_lighting_step_value + dynamic_lighting_dead_zone: !input dynamic_lighting_dead_zone + dynamic_lighting_state_control: !input dynamic_lighting_state_control + dynamic_lighting_state_control_activation_state: !input dynamic_lighting_state_control_activation_state + dynamic_lighting_state_control_entity: !input dynamic_lighting_state_control_entity + include_bypass: !input include_bypass + motion_bypass_lights_on: !input motion_bypass_lights_on + motion_bypass_lights_off: !input motion_bypass_lights_off + motion_bypass_lights_stop: !input motion_bypass_lights_stop + bypass_time_delay: !input bypass_time_delay + include_bypass_auto_off: !input include_bypass_auto_off + bypass_auto_off_delay: !input bypass_auto_off_delay + include_sun: !input include_sun + sun_elevation: !input sun_elevation + sun_elevation_rising: !input sun_elevation_rising + include_ambient: !input include_ambient + ambient_light_sensor: !input ambient_light_sensor + ambient_light_options: !input ambient_light_options + ambient_light_value: !input ambient_light_value + ambient_light_high_value: !input ambient_light_high_value + include_time: !input include_time + after_time: !input after_time + before_time: !input before_time + weekday_options: !input weekday_options + include_device_tracker: !input include_device_tracker + zone: !input zone + people: !input people + include_night_lights: !input include_night_lights + night_lights_conditions: !input night_lights_conditions + night_lights_entity_state: !input night_lights_entity_state + night_lights_after_time: !input night_lights_after_time + night_lights_before_time: !input night_lights_before_time + night_lights_sun_elevation: !input night_lights_sun_elevation + night_lights_sun_elevation_rising: !input night_lights_sun_elevation_rising + night_lights: !input night_lights + night_boolean_scenes_scripts: !input night_boolean_scenes_scripts + night_time_delay: !input night_time_delay + include_night_light_control: !input include_night_light_control + night_light_brightness: !input night_light_brightness + night_light_transition_on: !input night_light_transition_on + night_light_transition_off: !input night_light_transition_off + include_night_light_colour_control: !input include_night_light_colour_control + night_light_colour_temperature: !input night_light_colour_temperature + night_light_rgb_colour: !input night_light_rgb_colour + night_light_rgbw_colour: !input night_light_rgbw_colour + night_light_rgbww_colour: !input night_light_rgbww_colour + include_night_glow: !input include_night_glow + night_glow_lights: !input night_glow_lights + include_night_glow_light_control: !input include_night_glow_light_control + night_glow_light_brightness: !input night_glow_light_brightness + night_glow_light_transition_on: !input night_glow_light_transition_on + night_glow_light_transition_off: !input night_glow_light_transition_off + include_night_glow_light_colour_control: !input include_night_glow_light_colour_control + night_glow_light_colour_temperature: !input night_glow_light_colour_temperature + night_glow_light_rgb_colour: !input night_glow_light_rgb_colour + night_glow_light_rgbw_colour: !input night_glow_light_rgbw_colour + night_glow_light_rgbww_colour: !input night_glow_light_rgbww_colour + light_entities_off: "{{ expand(light_switch.entity_id)\n | selectattr('domain', + 'eq', 'light')\n | selectattr('state', 'eq', 'off')\n | map(attribute='entity_id')\n + \ | list }}\n" + switch_entities_off: "{{ expand(light_switch.entity_id)\n | selectattr('domain', + 'eq', 'switch')\n | selectattr('state', 'eq', 'off')\n | map(attribute='entity_id')| + list }}\n" + light_data: "{% set light = namespace(data={}) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if 'use_brightness' in include_light_control %}\n {% set + light.data = dict(light.data, **{ 'brightness_pct': light_brightness }) %}\n{% + endif %} {% if include_light_colour_control == 'use_colour_temperature' %}\n {% + set light.data = dict(light.data, **{ 'color_temp_kelvin': light_colour_temperature + }) %}\n{% endif %} {% if include_light_colour_control == 'use_rgb_colour' %}\n + \ {% set light.data = dict(light.data, **{ 'rgb_color': light_rgb_colour }) %}\n{% + endif %} {% if include_light_colour_control == 'use_rgbw_colour' %}\n {% set + light.data = dict(light.data, **{ 'rgbw_color': light_rgbw_colour }) %}\n{% endif + %} {% if include_light_colour_control == 'use_rgbww_colour' %}\n {% set light.data + = dict(light.data, **{ 'rgbww_color': light_rgbww_colour }) %}\n{% endif %} {{ + light.data }}" + light_entities: "{{ expand(light_switch.entity_id)\n | selectattr('domain', 'eq', + 'light')\n | map(attribute='entity_id')\n | list }}\n" + switch_entities: "{{ expand(light_switch.entity_id)\n | selectattr('domain', 'eq', + 'switch')\n | map(attribute='entity_id')\n | list }}\n" + scene_entities: "{% set a = light_switch.entity_id %} {% if boolean_scenes_scripts + == [] %}\n {{ expand(a) | selectattr('domain', 'eq', 'scene') | map(attribute='entity_id') + | list }}\n{% elif is_state(boolean_scenes_scripts, 'off') %}\n {{ expand(a) + | selectattr('domain', 'eq', 'scene') | map(attribute='entity_id') | list }}\n{% + else %}\n []\n{% endif %}" + script_entities: "{% set a = light_switch.entity_id %} {% if boolean_scenes_scripts + == [] %}\n {{ expand(a) | selectattr('domain', 'eq', 'script') | map(attribute='entity_id') + | list }}\n{% elif is_state(boolean_scenes_scripts, 'off') %}\n {{ expand(a) + | selectattr('domain', 'eq', 'script') | map(attribute='entity_id') | list }}\n{% + else %}\n []\n{% endif %}" + boolean_scenes_scripts_helper: "{% if boolean_scenes_scripts | length > 0 and states(boolean_scenes_scripts) + == 'off' %}\n {{ boolean_scenes_scripts }}\n{% else %}\n []\n{% endif %}" + end_scene_entities: '{{ end_scenes | select(''match'', ''^scene\..*'') | list }}' + end_script_entities: '{{ end_scenes | select(''match'', ''^script\..*'') | list + }}' + night_light_entities_off: "{% set b = night_lights and night_lights.entity_id %} + {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'light') | selectattr('state', + 'eq', 'off') | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif + %}" + night_switch_entities_off: "{% set b = night_lights and night_lights.entity_id %} + {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'switch') | selectattr('state', + 'eq', 'off') | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif + %}" + night_light_data: "{% set light = namespace(data={}) %} {% if 'use_transition' in + include_night_light_control %}\n {% set light.data = dict(light.data, **{ 'transition': + night_light_transition_on }) %}\n{% endif %} {% if 'use_brightness' in include_night_light_control + %}\n {% set light.data = dict(light.data, **{ 'brightness_pct': night_light_brightness + }) %}\n{% endif %} {% if include_night_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': night_light_colour_temperature + }) %}\n{% endif %} {% if include_night_light_colour_control == 'use_rgb_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgb_color': night_light_rgb_colour + }) %}\n{% endif %} {% if include_night_light_colour_control == 'use_rgbw_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgbw_color': night_light_rgbw_colour + }) %}\n{% endif %} {% if include_night_light_colour_control == 'use_rgbww_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgbww_color': night_light_rgbww_colour + }) %}\n{% endif %} {{ light.data }}" + night_light_entities: "{% set b = night_lights and night_lights.entity_id %} {% + if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'light') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_switch_entities: "{% set b = night_lights and night_lights.entity_id %} {% + if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'switch') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_scene_entities: "{% set b = night_lights and night_lights.entity_id %} {% + if night_boolean_scenes_scripts == [] %}\n {{ expand(b) | selectattr('domain', + 'eq', 'scene') | map(attribute='entity_id') | list }}\n{% elif is_state(night_boolean_scenes_scripts, + 'off') %}\n {{ expand(b) | selectattr('domain', 'eq', 'scene') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_script_entities: "{% set b = night_lights and night_lights.entity_id %} {% + if night_boolean_scenes_scripts == [] %}\n {{ expand(b) | selectattr('domain', + 'eq', 'script') | map(attribute='entity_id') | list }}\n{% elif is_state(night_boolean_scenes_scripts, + 'off') %}\n {{ expand(b) | selectattr('domain', 'eq', 'script') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_boolean_scenes_scripts_helper: "{% if night_boolean_scenes_scripts | length + > 0 and states(night_boolean_scenes_scripts) == 'off' %}\n {{ night_boolean_scenes_scripts + }}\n{% else %}\n []\n{% endif %}" + night_glow_light_data: "{% set light = namespace(data={}) %} {% if 'use_transition' + in include_night_glow_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': night_glow_light_transition_on }) %}\n{% endif %} {% if 'use_brightness' + in include_night_glow_light_control %}\n {% set light.data = dict(light.data, + **{ 'brightness_pct': night_glow_light_brightness }) %}\n{% endif %} {% if include_night_glow_light_colour_control + == 'use_colour_temperature' %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + night_glow_light_colour_temperature }) %}\n{% endif %} {% if include_night_glow_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, **{ 'rgb_color': + night_glow_light_rgb_colour }) %}\n{% endif %} {% if include_night_glow_light_colour_control + == 'use_rgbw_colour' %}\n {% set light.data = dict(light.data, **{ 'rgbw_color': + night_glow_light_rgbw_colour }) %}\n{% endif %} {% if include_night_glow_light_colour_control + == 'use_rgbww_colour' %}\n {% set light.data = dict(light.data, **{ 'rgbww_color': + night_glow_light_rgbww_colour }) %}\n{% endif %} {{ light.data }}" + night_glow_light_entities: "{% set b = night_glow_lights and night_glow_lights.entity_id + %} {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'light') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_glow_switch_entities: "{% set b = night_glow_lights and night_glow_lights.entity_id + %} {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'switch') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_glow_scene_entities: "{% set b = night_glow_lights and night_glow_lights.entity_id + %} {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'scene') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_glow_script_entities: "{% set b = night_glow_lights and night_glow_lights.entity_id + %} {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'script') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_night_glow_to_night_lights_light: "{% set a = night_lights and night_lights.entity_id + %} {% set b = night_glow_lights and night_glow_lights.entity_id %} {% if a and + b %}\n {{ expand(b) | reject('in', expand(a)) | selectattr('domain', 'eq', 'light') + | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif %}" + crossover_night_glow_to_night_lights_switch: "{% set a = night_lights and night_lights.entity_id + %} {% set b = night_glow_lights and night_glow_lights.entity_id %} {% if a and + b %}\n {{ expand(b) | reject('in', expand(a)) | selectattr('domain', 'eq', 'switch') + | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif %}" + crossover_night_lights_to_night_glow_light: "{% set a = night_glow_lights and night_glow_lights.entity_id + %} {% set b = night_lights and night_lights.entity_id %} {% if a and b %}\n {{ + expand(b) | reject('in', expand(a)) | selectattr('domain', 'eq', 'light') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_night_lights_to_night_glow_switch: "{% set a = night_glow_lights and night_glow_lights.entity_id + %} {% set b = night_lights and night_lights.entity_id %} {% if a and b %}\n {{ + expand(b) | reject('in', expand(a)) | selectattr('domain', 'eq', 'switch') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_lights_to_night_glow_light: "{% set a = night_glow_lights and night_glow_lights.entity_id + %} {% set b = light_switch.entity_id %} {% if a and b %}\n {{ expand(b) | reject('in', + expand(a)) | selectattr('domain', 'eq', 'light') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_lights_to_night_glow_switch: "{% set a = night_glow_lights and night_glow_lights.entity_id + %} {% set b = light_switch.entity_id %} {% if a and b %}\n {{ expand(b) | reject('in', + expand(a)) | selectattr('domain', 'eq', 'switch') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_lights_light: "{% set a = light_switch.entity_id %} {% set b = night_lights + and night_lights.entity_id %} {% if a and b %}\n {{ expand(a) | reject('in', + expand(b)) | selectattr('domain', 'eq', 'light') | selectattr('state', 'eq', 'on') + | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif %}" + crossover_lights_switch: "{% set a = light_switch.entity_id %} {% set b = night_lights + and night_lights.entity_id %} {% if a and b %}\n {{ expand(a) | reject('in', + expand(b)) | selectattr('domain', 'eq', 'switch') | selectattr('state', 'eq', + 'on') | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif %}" + crossover_night_lights_light_on: "{% set b = night_lights and night_lights.entity_id + %} {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'light') | selectattr('state', + 'eq', 'on') | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif + %}" + crossover_night_lights_light: "{% set a = light_switch.entity_id %} {% set b = night_lights + and night_lights.entity_id %} {% if a and b %}\n {{ expand(b) | reject('in', + expand(a)) | selectattr('domain', 'eq', 'light') | selectattr('state', 'eq', 'on') + | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif %}" + crossover_night_lights_switch: "{% set a = light_switch.entity_id %} {% set b = + night_lights and night_lights.entity_id %} {% if a and b %}\n {{ expand(b) | + reject('in', expand(a)) | selectattr('domain', 'eq', 'switch') | selectattr('state', + 'eq', 'on') | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif + %}" + crossover_lights_light_on: "{% set a = light_switch.entity_id %} {% if a %}\n {{ + expand(a) | selectattr('domain', 'eq', 'light') | selectattr('state', 'eq', 'on') + | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif %}" + dynamic_lighting_state_control_activation_state_inv: "{% if dynamic_lighting_state_control_activation_state + == 'on' %}\n off\n{% else %}\n on\n{% endif %}\n" +triggers: +- trigger: state + id: t0 + entity_id: !input motion_trigger + from: 'off' + to: 'on' +- trigger: numeric_state + id: t1 + entity_id: sun.sun + attribute: elevation + below: !input sun_elevation +- trigger: numeric_state + id: t2 + entity_id: !input ambient_light_sensor + below: !input ambient_light_value +- trigger: time + id: t3 + at: !input after_time +- trigger: state + id: t4 + entity_id: !input night_lights_entity_state + from: 'off' + to: 'on' +- trigger: time + id: t5 + at: !input night_lights_after_time +- trigger: numeric_state + id: t6 + entity_id: sun.sun + attribute: elevation + below: !input night_lights_sun_elevation +- trigger: state + id: t7_on + entity_id: !input motion_bypass_lights_on + from: 'off' + to: 'on' +- trigger: state + id: t7_off + entity_id: !input motion_bypass_lights_off + from: 'off' + to: 'on' +- trigger: state + id: t7_stop + entity_id: !input motion_bypass_lights_stop + from: 'off' + to: 'on' +- trigger: state + id: t8_on + entity_id: !input motion_bypass_lights_on + from: 'on' + to: 'off' +- trigger: state + id: t8_off + entity_id: !input motion_bypass_lights_off + from: 'on' + to: 'off' +- trigger: state + id: t8_stop + entity_id: !input motion_bypass_lights_stop + from: 'on' + to: 'off' +- trigger: numeric_state + id: t9 + entity_id: sun.sun + attribute: elevation + above: !input sun_elevation_rising +- trigger: numeric_state + id: t10 + entity_id: !input ambient_light_sensor + above: !input ambient_light_high_value +- trigger: time + id: t11 + at: !input before_time +- trigger: state + id: t12 + entity_id: !input night_lights_entity_state + from: 'on' + to: 'off' +- trigger: time + id: t13 + at: !input night_lights_before_time +- trigger: numeric_state + id: t14 + entity_id: sun.sun + attribute: elevation + above: !input night_lights_sun_elevation_rising +- trigger: state + id: t15 + entity_id: !input dynamic_lighting_state_control_entity + to: !input dynamic_lighting_state_control_activation_state +- trigger: state + id: t16 + entity_id: !input dynamic_lighting_state_control_entity + from: !input dynamic_lighting_state_control_activation_state +- trigger: homeassistant + id: t17 + event: start +condition: +- condition: or + conditions: + - condition: and + conditions: + - condition: trigger + id: t0 + - condition: state + entity_id: !input motion_trigger + match: any + state: 'on' + - condition: and + conditions: + - condition: trigger + id: t1 + - condition: state + entity_id: !input motion_trigger + state: 'on' + match: any + - '{{ include_sun == ''sun_enabled'' }}' + - condition: and + conditions: + - condition: trigger + id: t2 + - condition: state + entity_id: !input motion_trigger + state: 'on' + match: any + - '{{ include_ambient == ''ambient_enabled'' }}' + - condition: and + conditions: + - condition: trigger + id: t3 + - condition: state + entity_id: !input motion_trigger + state: 'on' + match: any + - '{{ include_time == ''time_enabled'' }}' + - condition: and + conditions: + - condition: trigger + id: t4 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''entity_state_enabled'' in night_lights_conditions }}' + - condition: state + entity_id: !input night_lights_entity_state + match: any + state: 'on' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ include_night_glow == ''night_glow_enabled'' }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t5 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''time_enabled'' in night_lights_conditions }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ include_night_glow == ''night_glow_enabled'' }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t6 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''sun_enabled'' in night_lights_conditions }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ include_night_glow == ''night_glow_enabled'' }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t7_on + - '{{ ''bypass_enabled_turn_on'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t7_off + - '{{ ''bypass_enabled_turn_off'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t7_stop + - '{{ ''bypass_enabled_stop'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t8_on + - '{{ ''bypass_enabled_turn_on'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t8_off + - '{{ ''bypass_enabled_turn_off'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t8_stop + - '{{ ''bypass_enabled_stop'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t9 + - '{{ include_sun == ''sun_enabled'' }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (include_night_glow + == ''night_glow_enabled'') and (expand(night_glow_lights.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t10 + - '{{ include_ambient == ''ambient_enabled'' }}' + - '{{ ambient_light_options == ''ambient_light_option_disabled'' }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (include_night_glow + == ''night_glow_enabled'') and (expand(night_glow_lights.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t11 + - '{{ include_time == ''time_enabled'' }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (include_night_glow + == ''night_glow_enabled'') and (expand(night_glow_lights.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t12 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''entity_state_enabled'' in night_lights_conditions }}' + - condition: state + entity_id: !input night_lights_entity_state + state: 'off' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (include_night_glow + == ''night_glow_enabled'') and (expand(night_glow_lights.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t13 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''time_enabled'' in night_lights_conditions }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (include_night_glow + == ''night_glow_enabled'') and (expand(night_glow_lights.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t14 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''sun_enabled'' in night_lights_conditions }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (include_night_glow + == ''night_glow_enabled'') and (expand(night_glow_lights.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: + - t15 + - t16 + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - '{{ dynamic_lighting_state_control == ''enable_state_control'' }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t17 + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t17 + - condition: state + entity_id: !input motion_trigger + match: any + state: 'on' + - condition: and + conditions: + - condition: trigger + id: t17 + - '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_off'' + in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' +- condition: or + conditions: + - '{{ include_bypass == [] }}' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: trigger + id: + - t7_on + - t7_off + - t7_stop + - t8_on + - t8_off + - t8_stop + - t17 +- condition: or + conditions: + - '{{ include_sun == ''sun_disabled'' }}' + - '{{ include_sun == ''sun_enabled_night_lights'' }}' + - '{{ (include_sun == ''sun_enabled'') and (is_state_attr(''sun.sun'', ''rising'', + false)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation | float(90)) + }}' + - '{{ (include_sun == ''sun_enabled'') and (is_state_attr(''sun.sun'', ''rising'', + true)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation_rising | float(90)) + }}' + - condition: trigger + id: + - t7_on + - t7_off + - t7_stop + - t8_on + - t8_off + - t8_stop + - t9 +- condition: or + conditions: + - '{{ include_ambient == ''ambient_disabled'' }}' + - '{{ ambient_light_sensor == [] }}' + - '{{ (include_ambient == ''ambient_enabled'') and ((states(ambient_light_sensor) + | int) < (ambient_light_value | int)) }}' + - '{{ (include_ambient == ''ambient_enabled'') and ((states(ambient_light_sensor) + | int) < (ambient_light_high_value | int)) and (expand(light_switch.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - '{{ (include_ambient == ''ambient_enabled'') and ((states(ambient_light_sensor) + | int) < (ambient_light_high_value | int)) and ((include_night_lights == ''night_lights_enabled'') + and (expand(night_lights.entity_id) | selectattr(''state'', ''=='', ''on'') | + list | count > 0)) }}' + - '{{ (include_ambient == ''ambient_enabled'') and (ambient_light_options == ''ambient_light_option_enabled'') + and (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') | + list | count > 0) }}' + - '{{ (include_ambient == ''ambient_enabled'') and (ambient_light_options == ''ambient_light_option_enabled'') + and ((include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0)) }}' + - condition: and + conditions: + - '{{ (include_ambient == ''ambient_enabled'') and ((states(ambient_light_sensor) + | int) < (ambient_light_high_value | int)) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - '{{ (include_ambient == ''ambient_enabled'') and ((states(ambient_light_sensor) + | int) < (ambient_light_high_value | int)) }}' + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - '{{ (include_ambient == ''ambient_enabled'') and ( ambient_light_options == + ''ambient_light_option_enabled'' ) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - '{{ (include_ambient == ''ambient_enabled'') and ( ambient_light_options == + ''ambient_light_option_enabled'' ) }}' + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: trigger + id: + - t7_on + - t7_off + - t7_stop + - t8_on + - t8_off + - t8_stop + - t10 +- condition: or + conditions: + - '{{ include_time == ''time_disabled'' }}' + - condition: and + conditions: + - condition: time + after: !input after_time + before: !input before_time + weekday: !input weekday_options + - '{{ include_time == ''time_enabled'' }}' + - condition: trigger + id: + - t7_on + - t7_off + - t7_stop + - t8_on + - t8_off + - t8_stop + - t11 +- condition: or + conditions: + - '{{ include_device_tracker == ''device_tracker_disabled'' }}' + - condition: and + conditions: + - '{{ include_device_tracker == ''zone_enabled'' }}' + - condition: numeric_state + entity_id: !input zone + above: 0 + - condition: and + conditions: + - '{{ include_device_tracker == ''zone_people_enabled'' }}' + - '{{ (state_attr(zone, ''persons'') | list | select(''in'', people) | list | + length) > 0 }}' + - condition: trigger + id: + - t7_on + - t7_off + - t7_stop + - t8_on + - t8_off + - t8_stop +action: +- choose: + - alias: Check if night lights are enabled and within conditions + conditions: + - condition: and + conditions: + - alias: Check if night lights is enabled + condition: template + value_template: '{{ include_night_lights == ''night_lights_enabled'' }}' + - alias: Check if night lights entity state is enabled + condition: or + conditions: + - '{{ (''time_enabled'' in night_lights_conditions) and not (''entity_state_enabled'' + in night_lights_conditions) }}' + - '{{ (''sun_enabled'' in night_lights_conditions) and not (''entity_state_enabled'' + in night_lights_conditions) }}' + - condition: and + conditions: + - '{{ ''entity_state_enabled'' in night_lights_conditions }}' + - condition: state + entity_id: !input night_lights_entity_state + state: 'on' + match: any + - alias: Check if night lights time is enabled + condition: or + conditions: + - '{{ (''entity_state_enabled'' in night_lights_conditions) and not (''time_enabled'' + in night_lights_conditions) }}' + - '{{ (''sun_enabled'' in night_lights_conditions) and not (''time_enabled'' + in night_lights_conditions) }}' + - condition: and + conditions: + - '{{ ''time_enabled'' in night_lights_conditions }}' + - condition: time + after: !input night_lights_after_time + before: !input night_lights_before_time + - alias: Check if night lights sun is enabled + condition: or + conditions: + - '{{ (''entity_state_enabled'' in night_lights_conditions) and not (''sun_enabled'' + in night_lights_conditions) }}' + - '{{ (''time_enabled'' in night_lights_conditions) and not (''sun_enabled'' + in night_lights_conditions) }}' + - '{{ (''sun_enabled'' in night_lights_conditions) and (((is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') <= night_lights_sun_elevation + | float(90))) or ((is_state_attr(''sun.sun'', ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') + <= night_lights_sun_elevation_rising | float(90)))) }}' + sequence: + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - choose: + - alias: By-pass is turned on & check by-pass option - Turn lights off + conditions: + - condition: trigger + id: t7_off + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + data: + transition: '{{ night_light_transition_off }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + data: + transition: '{{ night_glow_light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_lights_switch }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' }}' + sequence: + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_switch }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' + in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - alias: By-pass is turned on & check by-pass option - Keep the current lights + state + conditions: + - condition: trigger + id: t7_stop + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' + in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - choose: + - alias: By-pass is turned off & check if the motion trigger is off + conditions: + - condition: trigger + id: + - t8_on + - t8_off + - t8_stop + - condition: state + entity_id: !input motion_trigger + match: all + state: 'off' + sequence: + - choose: + - alias: Check all by-pass are off + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If night glow is disabled + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_disabled'' + }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_lights_switch }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: If night glow is enabled + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn on the lights + action: light.turn_on + target: + entity_id: '{{ night_glow_light_entities }}' + data: '{{ night_glow_light_data }}' + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn OFF crossover night lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_to_night_glow_light + }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn OFF crossover lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_to_night_glow_light }}' + data: + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn OFF crossover night lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_to_night_glow_light + }}' + - alias: Turn OFF crossover lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_to_night_glow_light }}' + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_glow_light_control + }}' + sequence: + - alias: Turn on the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_on }}' + - alias: Turn on the scenes + action: scene.turn_on + data: + entity_id: '{{ night_glow_scene_entities }}' + transition: '{{ night_glow_light_transition_on }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn on the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn on the scenes + action: scene.turn_on + data: + entity_id: '{{ night_glow_scene_entities }}' + - alias: Turn on the switches + action: switch.turn_on + target: + entity_id: '{{ night_glow_switch_entities }}' + - alias: Turn OFF crossover night switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_lights_to_night_glow_switch }}' + - alias: Turn OFF crossover light switches + action: switch.turn_off + target: + entity_id: '{{ crossover_lights_to_night_glow_switch }}' + - alias: Turn on the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn on the script + action: script.turn_on + data: + entity_id: '{{ night_glow_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Motion trigger is off and check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time + delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + - alias: By-pass is turned off & check if the motion trigger is on + conditions: + - condition: trigger + id: + - t8_on + - t8_off + - t8_stop + - condition: state + entity_id: !input motion_trigger + match: any + state: 'on' + sequence: + - choose: + - alias: Check all by-pass are off and check conditions if enabled + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: or + conditions: + - '{{ (include_sun == ''sun_enabled'') and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation | float(90)) }}' + - '{{ (include_ambient == ''ambient_enabled'') and (ambient_light_options + == ''ambient_light_option_disabled'') and ((states(ambient_light_sensor) + | int) > (ambient_light_value | int)) }}' + - '{{ (include_ambient == ''ambient_enabled'') and (ambient_light_options + == ''ambient_light_option_enabled'') and ((states(ambient_light_sensor) + | int) > (ambient_light_value | int)) and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''off'') | list | count > 0) }}' + - '{{ (include_ambient == ''ambient_enabled'') and (ambient_light_options + == ''ambient_light_option_enabled'') and ((states(ambient_light_sensor) + | int) > (ambient_light_value | int)) and (is_state(night_boolean_scenes_scripts, + ''off'')) }}' + - condition: and + conditions: + - '{{ include_time == ''time_enabled'' }}' + - condition: time + after: !input before_time + before: !input after_time + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Motion trigger is on and check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time + delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + - alias: By-pass is turned off & reset lights to the site conditions + conditions: + - condition: trigger + id: + - t8_on + - t8_off + - t8_stop + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + data: + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_lights_switch }}' + - choose: + - alias: Check if normal lights Scenes & Scripts - Toggle Helper is + on + conditions: + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the input boolean normal lights + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn ON lights + action: light.turn_on + target: + entity_id: '{{ crossover_night_lights_light_on }}' + data: '{{ night_light_data }}' + - choose: + - alias: Sun, ambient & time above setting to turn off + conditions: + - condition: trigger + id: + - t9 + - t10 + - t11 + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + data: + transition: '{{ night_glow_light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' }}' + sequence: + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_switch }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the input boolean night lights + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Turn off normal lights when trigger by start night lights conditions + conditions: + - condition: trigger + id: + - t4 + - t5 + - t6 + sequence: + - choose: + - alias: light - switch - scene - script is ON + conditions: + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', + ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + data: + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_lights_switch }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - choose: + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the input boolean normal lights + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn ON lights + action: light.turn_on + target: + entity_id: '{{ crossover_night_lights_light_on }}' + data: '{{ night_light_data }}' + - alias: Turn on night glow lights if all lights are off + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' }}' + - condition: template + value_template: '{{ (expand(light_switch.entity_id) | selectattr(''state'', + ''=='', ''off'') | list | count > 0) or (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''off'') | list | count > 0) or (is_state(boolean_scenes_scripts, + ''off'')) }} + + ' + sequence: + - alias: Turn on the lights + action: light.turn_on + target: + entity_id: '{{ night_glow_light_entities }}' + data: '{{ night_glow_light_data }}' + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_glow_light_control + }}' + sequence: + - alias: Turn on the scenes + action: scene.turn_on + data: + entity_id: '{{ night_glow_scene_entities }}' + transition: '{{ night_glow_light_transition_on }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_glow_light_control + }}' + sequence: + - alias: Turn on the scenes + action: scene.turn_on + data: + entity_id: '{{ night_glow_scene_entities }}' + - alias: Turn on the switches + action: switch.turn_on + target: + entity_id: '{{ night_glow_switch_entities }}' + - alias: Turn on the script + action: script.turn_on + data: + entity_id: '{{ night_glow_script_entities }}' + - stop: Stop the automation + - alias: If manage scripts is enabled + conditions: + - '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', + ''off'') | list | count > 0) or (expand(night_lights.entity_id) | selectattr(''state'', + ''=='', ''off'') | list | count > 0) or (is_state(boolean_scenes_scripts, + ''off'')) }}' + sequence: + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - stop: Stop the automation + - alias: Safe Guard when HA restarts + conditions: + - condition: trigger + id: t17 + sequence: + - choose: + - alias: Check all by-pass are off and check conditions if enabled + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + sequence: + - alias: Small time delay required + delay: + seconds: 1 + - alias: Check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass auto off is enabled and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time + delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + - choose: + - alias: If night glow is disabled + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_disabled'' }}' + sequence: + - alias: Turn ON lights + action: light.turn_on + target: + entity_id: '{{ night_light_entities_off }}' + data: '{{ night_light_data }}' + - choose: + - alias: Check if normal lights scenes or scripts helper is on - This is + for scenes only when crossing over + conditions: + - condition: template + value_template: "{% if boolean_scenes_scripts == [] %}\n false\n{% + elif is_state(boolean_scenes_scripts, 'on') %}\n true\n{% else %}\n + \ false\n{% endif %}" + sequence: + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: '{{ boolean_scenes_scripts }}' + - choose: + - alias: Check if normal lights scenes or scripts helper is on - This + is for scenes only when crossing over + conditions: + - '{{ night_boolean_scenes_scripts != [] }}' + sequence: + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ night_boolean_scenes_scripts_helper }}' + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn on the night scenes + action: scene.turn_on + target: + entity_id: '{{ night_scene_entities }}' + data: + transition: '{{ night_light_transition_on }}' + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ night_boolean_scenes_scripts_helper }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn on the night scenes + action: scene.turn_on + target: + entity_id: '{{ night_scene_entities }}' + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ night_boolean_scenes_scripts_helper }}' + - alias: Turn on the night switches + action: switch.turn_on + target: + entity_id: '{{ night_switch_entities_off }}' + - alias: Turn on the night scripts + action: script.turn_on + target: + entity_id: '{{ night_script_entities }}' + - alias: If night glow is enabled + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' }}' + - condition: not + conditions: + - condition: trigger + id: + - t15 + - t16 + sequence: + - alias: Turn ON lights + action: light.turn_on + target: + entity_id: '{{ night_light_entities }}' + data: '{{ night_light_data }}' + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_glow_light_control + }}' + sequence: + - alias: Turn OFF crossover night glow lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + data: + transition: '{{ night_glow_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_glow_light_control + }}' + sequence: + - alias: Turn OFF crossover night glow lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + - choose: + - alias: Check if normal lights scenes or scripts helper is on - This is + for scenes only when crossing over + conditions: + - condition: template + value_template: "{% if boolean_scenes_scripts == [] %}\n false\n{% + elif is_state(boolean_scenes_scripts, 'on') %}\n true\n{% else %}\n + \ false\n{% endif %}" + sequence: + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: '{{ boolean_scenes_scripts }}' + - choose: + - alias: Check if normal lights scenes or scripts helper is on - This + is for scenes only when crossing over + conditions: + - '{{ night_boolean_scenes_scripts != [] }}' + sequence: + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ night_boolean_scenes_scripts_helper }}' + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn on the night scenes + action: scene.turn_on + target: + entity_id: '{{ night_scene_entities }}' + data: + transition: '{{ night_light_transition_on }}' + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ night_boolean_scenes_scripts_helper }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn on the night scenes + action: scene.turn_on + target: + entity_id: '{{ night_scene_entities }}' + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ night_boolean_scenes_scripts_helper }}' + - alias: Turn on the night switches + action: switch.turn_on + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off crossover night glow switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_switch }}' + - alias: Turn on the night scripts + action: script.turn_on + target: + entity_id: '{{ night_script_entities }}' + - choose: + - alias: By-pass is enabled & check by-pass option - Turn lights on + conditions: + - condition: trigger + id: t7_on + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' + in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - choose: + - alias: Check if the trigger is on and wait for it to go off + conditions: + - condition: state + entity_id: !input motion_trigger + state: 'on' + match: any + sequence: + - alias: Wait until motion sensor is off + wait_for_trigger: + - trigger: state + entity_id: !input motion_trigger + from: 'on' + to: 'off' + - alias: Wait the number of minutes set in the night lights time delay + delay: + minutes: !input night_time_delay + - choose: + - alias: If night glow is disabled + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_disabled'' }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - alias: If night glow is enabled + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' }}' + sequence: + - alias: Turn on the lights + action: light.turn_on + target: + entity_id: '{{ night_glow_light_entities }}' + data: '{{ night_glow_light_data }}' + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn OFF crossover night lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_to_night_glow_light }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn OFF crossover night lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_to_night_glow_light }}' + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_glow_light_control + }}' + sequence: + - alias: Turn on the scenes + action: scene.turn_on + data: + entity_id: '{{ night_glow_scene_entities }}' + transition: '{{ night_glow_light_transition_on }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn on the scenes + action: scene.turn_on + data: + entity_id: '{{ night_glow_scene_entities }}' + - alias: Turn on the switches + action: switch.turn_on + target: + entity_id: '{{ night_glow_switch_entities }}' + - alias: Turn OFF crossover night switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_lights_to_night_glow_switch }}' + - alias: Turn on the script + action: script.turn_on + data: + entity_id: '{{ night_glow_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + default: + - choose: + - alias: By-pass is turned on & check by-pass option - Turn lights off + conditions: + - condition: trigger + id: t7_off + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If night lights are disabled + conditions: + - condition: template + value_template: '{{ include_night_lights == ''night_lights_disabled'' + }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - alias: If night lights are enabled + conditions: + - condition: template + value_template: '{{ include_night_lights == ''night_lights_enabled'' }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + data: + transition: '{{ light_transition_off }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_lights_switch }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' }}' + sequence: + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_switch }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - alias: By-pass is turned on & check by-pass option - Keep the current lights + state + conditions: + - condition: trigger + id: t7_stop + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' + in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - choose: + - alias: By-pass is turned off & check if the motion trigger is off + conditions: + - condition: trigger + id: + - t8_on + - t8_off + - t8_stop + - condition: state + entity_id: !input motion_trigger + match: all + state: 'off' + sequence: + - choose: + - alias: Check all by-pass are off + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If night lights are disabled + conditions: + - condition: template + value_template: '{{ include_night_lights == ''night_lights_disabled'' + }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ + is_state(dynamic_lighting_boolean, 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - stop: Stop the automation + - alias: If night lights are enabled + conditions: + - condition: template + value_template: '{{ include_night_lights == ''night_lights_enabled'' + }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the night lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + data: + transition: '{{ light_transition_off }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light + }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light + }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_lights_switch }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_switch }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ + is_state(dynamic_lighting_boolean, 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - stop: Stop the automation + - alias: Motion trigger is off and check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + - alias: By-pass is turned off & check if the motion trigger is on + conditions: + - condition: trigger + id: + - t8_on + - t8_off + - t8_stop + - condition: state + entity_id: !input motion_trigger + match: any + state: 'on' + sequence: + - choose: + - alias: Check all by-pass are off and check conditions if enabled + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: or + conditions: + - '{{ (include_sun == ''sun_enabled'') and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation | float(90)) }}' + - '{{ (include_ambient == ''ambient_enabled'') and (ambient_light_options + == ''ambient_light_option_disabled'') and ((states(ambient_light_sensor) + | int) > (ambient_light_value | int)) }}' + - '{{ (include_ambient == ''ambient_enabled'') and (ambient_light_options + == ''ambient_light_option_enabled'') and ((states(ambient_light_sensor) + | int) > (ambient_light_value | int)) and (expand(light_switch.entity_id) + | selectattr(''state'', ''=='', ''off'') | list | count > 0) }}' + - '{{ (include_ambient == ''ambient_enabled'') and (ambient_light_options + == ''ambient_light_option_enabled'') and ((states(ambient_light_sensor) + | int) > (ambient_light_value | int)) and (is_state(boolean_scenes_scripts, + ''off'')) }}' + - condition: and + conditions: + - condition: time + after: !input before_time + before: !input after_time + - '{{ include_time == ''time_enabled'' }}' + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - stop: Stop the automation + - alias: Motion trigger is on and check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + - alias: By-pass is turned off & reset lights to the site conditions + conditions: + - condition: trigger + id: + - t8_on + - t8_off + - t8_stop + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + data: + transition: '{{ night_light_transition_off }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + data: + transition: '{{ night_glow_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_light }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_lights_switch }}' + - choose: + - alias: If night glow is selected + conditions: + - condition: template + value_template: '{{ include_night_glow == ''night_glow_enabled'' }}' + sequence: + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_glow_to_night_lights_switch }}' + - choose: + - alias: Check if night lights scenes & scripts - toggle helper is on + conditions: + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ + is_state(night_boolean_scenes_scripts, 'on') }}\n{% endif %}" + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the input boolean night lights + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - choose: + - alias: Turn ON lights + conditions: + - alias: Check the dynamic lighting options + condition: or + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''disable_dynamic_lighting'' + }}' + - alias: Check that dynamic lighting is active and the state control + option is enabled + condition: and + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: '{{ dynamic_lighting_state_control == ''enable_state_control'' + }}' + - condition: template + value_template: '{{ states(dynamic_lighting_state_control_entity) + == dynamic_lighting_state_control_activation_state_inv }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ crossover_lights_light_on }}' + data: '{{ light_data }}' + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ + is_state(dynamic_lighting_boolean, 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - choose: + - alias: Sun, ambient & time above setting to turn off + conditions: + - condition: trigger + id: + - t9 + - t10 + - t11 + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the input boolean night lights + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - stop: Stop the automation + - alias: Turn off night lights when trigger by end night lights conditions + conditions: + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - choose: + - alias: If the night glow is enabled + conditions: + - '{{ include_night_glow == ''night_glow_enabled'' }}' + - condition: state + entity_id: !input motion_trigger + state: 'off' + for: + minutes: !input night_time_delay + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_glow_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_glow_light_entities }}' + data: + transition: '{{ night_glow_light_transition_off}}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_glow_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_glow_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ night_glow_light_entities }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ night_glow_switch_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - stop: Stop the automation + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ (expand(light_switch.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) or (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) or (is_state(night_boolean_scenes_scripts, + ''on'')) }} + + ' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ crossover_night_lights_switch }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - choose: + - alias: Adjust the lights settings when crossing over if lights are ON + is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the input boolean night lights + action: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - choose: + - alias: Turn ON lights + conditions: + - alias: Check the dynamic lighting options + condition: or + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''disable_dynamic_lighting'' + }}' + - alias: Check that dynamic lighting is active and the state control + option is enabled + condition: and + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: '{{ dynamic_lighting_state_control == ''enable_state_control'' + }}' + - condition: template + value_template: '{{ states(dynamic_lighting_state_control_entity) + == dynamic_lighting_state_control_activation_state_inv }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ crossover_lights_light_on }}' + data: '{{ light_data }}' + - alias: If manage scripts is enabled + conditions: + - '{{ ''manage_scripts_crossing_over'' in include_night_light_control }}' + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''off'') + | list | count > 0) or (expand(night_lights.entity_id) | selectattr(''state'', + ''=='', ''off'') | list | count > 0) or (is_state(night_boolean_scenes_scripts, + ''off'')) }}' + sequence: + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - stop: Stop the automation + - alias: Safe Guard when HA restarts + conditions: + - condition: trigger + id: t17 + sequence: + - choose: + - alias: Check all by-pass are off and check conditions if enabled + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + state: 'off' + sequence: + - alias: Small time delay required + delay: + seconds: 1 + - alias: Check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input motion_bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass auto off is enabled and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + - alias: Parallel actions for dynamic lighting and normal lights + parallel: + - sequence: + - alias: Check if the dynamic lighting is enabled + condition: and + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - alias: Check the dynamic lighting state control option + condition: or + conditions: + - condition: template + value_template: '{{ dynamic_lighting_state_control == ''disable_state_control'' + }}' + - alias: Check if the dynamic lighting state control option is enabled + condition: and + conditions: + - condition: template + value_template: '{{ dynamic_lighting_state_control == ''enable_state_control'' + }}' + - condition: state + entity_id: !input dynamic_lighting_state_control_entity + state: !input dynamic_lighting_state_control_activation_state + - choose: + - alias: Dynamic Lighting - Input Boolean Helper + conditions: + - condition: template + value_template: '{{ dynamic_lighting_boolean != [] }}' + sequence: + - alias: Turn on the boolean for dynamic lighting + action: input_boolean.turn_on + data: + entity_id: !input dynamic_lighting_boolean + - choose: + - alias: 1 - Dynamic Lighting - Lux Controlled Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_lux_controled_brightness''}}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_max_lux - dynamic_lighting_min_lux) %} {% + set ak = (( slope * dynamic_lighting_min_lux) * -1) + dynamic_lighting_max_brightness + %} {% set le = light_entities %} {% set lec = expand(le) | map(attribute='entity_id') + | list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 * 100 + / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux %}\n + \ {% set bv = dynamic_lighting_max_brightness %}\n{% elif lux + >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if lp <= 0 %}\n {% set bv = bv %}\n{% elif + (bv > lp) and (dynamic_lighting_max_brightness - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% elif + (lp > bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n {% set bv = dynamic_lighting_min_brightness %}\n{% elif + (bv > lp) and (bv - lp) <= dynamic_lighting_dead_zone %}\n {% + set bv = lp %}\n{% elif (lp > bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n {% set bv = lp %}\n{% elif bv > (lp + dynamic_lighting_step_value) + %}\n {% set bv = lp + dynamic_lighting_step_value %}\n{% elif + bv < (lp - dynamic_lighting_step_value) %}\n {% set bv = lp - + dynamic_lighting_step_value %}\n{% endif %} {{ bv | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n {% + set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbw_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbw_color': light_rgbw_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbww_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbww_color': light_rgbww_colour }) %}\n{% endif %} {{ light.data + }}\n" + in_dead_zone: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_max_lux - dynamic_lighting_min_lux) %} {% + set ak = (( slope * dynamic_lighting_min_lux) * -1) + dynamic_lighting_max_brightness + %} {% set le = light_entities %} {% set lec = expand(le) | map(attribute='entity_id') + | list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 * 100 + / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux %}\n + \ {% set bv = dynamic_lighting_max_brightness %}\n{% elif lux + >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if (bv > lp) and (dynamic_lighting_max_brightness + - lp) <= dynamic_lighting_dead_zone %}\n false\n{% elif (lp > + bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n false\n{% elif (bv >= lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n true\n{% elif (lp >= bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n true\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If dynamic lighting brightness is in the dead zone + conditions: '{{ in_dead_zone }}' + sequence: + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set the dynamic lighting brightness for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_lux_controled_brightness'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - choose: + - alias: 2 - Dynamic Lighting - Lux Controlled Brightness - Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_lux_controled_brightness_inv''}}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_min_lux - dynamic_lighting_max_lux) %} {% + set ak = (( slope * dynamic_lighting_min_lux) * -1) + dynamic_lighting_min_brightness + %} {% set le = light_entities %} {% set lec = expand(le) | map(attribute='entity_id') + | list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 * 100 + / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux %}\n + \ {% set bv = dynamic_lighting_min_brightness %}\n{% elif lux + >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if lp <= 0 %}\n {% set bv = bv %}\n{% elif + (bv > lp) and (dynamic_lighting_max_brightness - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% elif + (lp > bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n {% set bv = dynamic_lighting_min_brightness %}\n{% elif + (bv > lp) and (bv - lp) <= dynamic_lighting_dead_zone %}\n {% + set bv = lp %}\n{% elif (lp > bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n {% set bv = lp %}\n{% elif bv > (lp + dynamic_lighting_step_value) + %}\n {% set bv = lp + dynamic_lighting_step_value %}\n{% elif + bv < (lp - dynamic_lighting_step_value) %}\n {% set bv = lp - + dynamic_lighting_step_value %}\n{% endif %} {{ bv | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n {% + set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbw_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbw_color': light_rgbw_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbww_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbww_color': light_rgbww_colour }) %}\n{% endif %} {{ light.data + }}\n" + in_dead_zone: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_min_lux - dynamic_lighting_max_lux) %} {% + set ak = (( slope * dynamic_lighting_min_lux) * -1) + dynamic_lighting_min_brightness + %} {% set le = light_entities %} {% set lec = expand(le) | map(attribute='entity_id') + | list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 * 100 + / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux %}\n + \ {% set bv = dynamic_lighting_min_brightness %}\n{% elif lux + >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if (bv > lp) and (dynamic_lighting_max_brightness + - lp) <= dynamic_lighting_dead_zone %}\n true\n{% elif (lp > + bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n false\n{% elif (bv >= lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n true\n{% elif (lp >= bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n true\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If dynamic lighting brightness is in the dead zone + conditions: '{{ in_dead_zone }}' + sequence: + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting brightness inverted for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_lux_controled_brightness_inv'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - choose: + - alias: 3 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope = + (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation >= + -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set colour_temp_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set colour_temp_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n {% elif elevation <= + dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n{% endif %} {{ colour_temp_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'color_temp_kelvin': dynamic_kelvin + }) %} {% if 'use_transition' in include_light_control %}\n {% + set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if 'use_brightness' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'brightness_pct': + light_brightness }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if elevation >= (dynamic_lighting_sun_elevation_start_rising + - 2) and elevation <= (dynamic_lighting_sun_elevation_end_rising + + 2) and is_state_attr('sun.sun', 'rising', true) %}\n false\n + \ {% elif elevation <= (dynamic_lighting_sun_elevation_start_falling + + 2) and elevation >= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n false\n + \ {% elif elevation >= (dynamic_lighting_sun_elevation_end_rising + + 2) and elevation >= (dynamic_lighting_sun_elevation_start_falling + - 2) %}\n true\n {% elif elevation <= (dynamic_lighting_sun_elevation_start_rising + - 2) and is_state_attr('sun.sun', 'rising', true) %}\n true\n + \ {% elif elevation <= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n true\n + \ {% else %}\n true\n {% endif %}\n{% else %}\n false\n{% + endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If sun elevation is outside the adjustment zone and the lights + are ON + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation colour temperature for the normal + lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 4 - Dynamic Lighting - Sun Elevation Lighting - Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_brightness'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_brightness %} {% set end_slope = + (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_brightness %} {% if elevation >= + -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set brightness_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n {% else %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% endif + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n{% endif %} {{ brightness_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n {% + set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbw_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbw_color': light_rgbw_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbww_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbww_color': light_rgbww_colour }) %}\n{% endif %} {{ light.data + }}\n" + should_wait: "{% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set lp = (expand(le) + | map(attribute='attributes.brightness') | reject('equalto', None) + | sum | float(default=255) / 255 * 100 / lec) | round(0) %} {% + set elevation = state_attr('sun.sun', 'elevation') | float %} + {% set start_slope = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_start_rising - dynamic_lighting_sun_elevation_end_rising) + %} {% set start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_brightness %} {% set end_slope = + (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_brightness %} {% if elevation >= + -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set brightness_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n {% else %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% endif + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n{% endif %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if (brightness_value >= + (lp * 0.99) and brightness_value <= (lp * 1.01)) %}\n true\n + \ {% else %}\n false\n {% endif %}\n{% else %}\n false\n{% + endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If lights are ON and the lights are within 1% of the actual + brightness + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation lighting brightness for the normal + lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_brightness'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 5 - Dynamic Lighting - Sun Elevation Lighting - Brightness Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_brightness_inv'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_max_brightness + - dynamic_lighting_min_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_min_brightness %} {% set end_slope = + (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_max_brightness %} {% if elevation > -10 + %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set brightness_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n {% else %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% endif + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n{% endif %} {{ brightness_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n {% + set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbw_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbw_color': light_rgbw_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbww_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbww_color': light_rgbww_colour }) %}\n{% endif %} {{ light.data + }}\n" + should_wait: "{% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set lp = (expand(le) + | map(attribute='attributes.brightness') | reject('equalto', None) + | sum | float(default=255) / 255 * 100 / lec) | round(0) %} {% + set elevation = state_attr('sun.sun', 'elevation') | float %} + {% set start_slope = (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_start_rising - dynamic_lighting_sun_elevation_end_rising) + %} {% set start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_min_brightness %} {% set end_slope = + (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_max_brightness %} {% if elevation > -10 + %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set brightness_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n {% else %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% endif + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n{% endif %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if (brightness_value >= + (lp * 0.99) and brightness_value <= (lp * 1.01)) %}\n true\n + \ {% else %}\n false\n {% endif %}\n{% else %}\n false\n{% + endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If lights are ON and the lights are within 1% of the actual + brightness + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation lighting brightness inverted for + the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_brightness_inv'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 6 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_brightness'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope = + (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation >= + -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set colour_temp_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set colour_temp_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n {% elif elevation <= + dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n{% endif %} {{ colour_temp_value | round(0) }}\n" + dynamic_brightness_pct: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_brightness %} {% set end_slope = + (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_brightness %} {% if elevation >= + -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set brightness_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n {% else %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% endif + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n{% endif %} {{ brightness_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'color_temp_kelvin': dynamic_kelvin + }) %} {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set lp = (expand(le) + | map(attribute='attributes.brightness') | reject('equalto', None) + | sum | float(default=255) / 255 * 100 / lec) | round(0) %} {% + set elevation = state_attr('sun.sun', 'elevation') | float %} + {% set start_slope = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_start_rising - dynamic_lighting_sun_elevation_end_rising) + %} {% set start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_brightness %} {% set end_slope = + (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_brightness %} {% if elevation >= + -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set brightness_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n {% else %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% endif + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n{% endif %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if (brightness_value >= + (lp * 0.99) and brightness_value <= (lp * 1.01)) %}\n true\n + \ {% else %}\n false\n {% endif %}\n{% else %}\n false\n{% + endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If lights are ON and the lights are within 1% of the actual + brightness + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation lighting brightness and dynamic + sun elevation colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_brightness'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 7 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + Brightness + Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_brightness_inv'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope = + (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation >= + -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set colour_temp_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set colour_temp_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n {% elif elevation <= + dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n{% endif %} {{ colour_temp_value | round(0) }}\n" + dynamic_brightness_pct: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_max_brightness + - dynamic_lighting_min_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_min_brightness %} {% set end_slope = + (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_max_brightness %} {% if elevation > -10 + %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set brightness_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n {% else %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% endif + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n{% endif %} {{ brightness_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'color_temp_kelvin': dynamic_kelvin + }) %} {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set lp = (expand(le) + | map(attribute='attributes.brightness') | reject('equalto', None) + | sum | float(default=255) / 255 * 100 / lec) | round(0) %} {% + set elevation = state_attr('sun.sun', 'elevation') | float %} + {% set start_slope = (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_start_rising - dynamic_lighting_sun_elevation_end_rising) + %} {% set start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_min_brightness %} {% set end_slope = + (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_max_brightness %} {% if elevation > -10 + %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising and + is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = ((start_slope * elevation) + start_ak) | round(1) %}\n {% elif + elevation <= dynamic_lighting_sun_elevation_start_falling and + elevation >= dynamic_lighting_sun_elevation_end_falling and is_state_attr('sun.sun', + 'rising', false) %}\n {% set brightness_value = ((end_slope + * elevation) + end_ak) | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n {% else %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% endif + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n{% endif %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if (brightness_value >= + (lp * 0.99) and brightness_value <= (lp * 1.01)) %}\n true\n + \ {% else %}\n false\n {% endif %}\n{% else %}\n false\n{% + endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If lights are ON and the lights are within 1% of the actual + brightness + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation lighting brightness and dynamic + sun elevation colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_brightness_inv'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 8 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + Lux + Controlled Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness'' + }}' + sequence: + - alias: Parallel actions for dynamic lighting + parallel: + - sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_max_lux + - dynamic_lighting_min_lux) %} {% set ak = (( slope * dynamic_lighting_min_lux) + * -1) + dynamic_lighting_max_brightness %} {% set le = light_entities + %} {% set lec = expand(le) | map(attribute='entity_id') | + list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 + * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% elif + lux >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if lp <= 0 %}\n {% set bv = bv %}\n{% + elif (bv > lp) and (dynamic_lighting_max_brightness - lp) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% elif (lp > bv) and (lp - dynamic_lighting_min_brightness) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% elif (bv > lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = lp %}\n{% elif (lp > bv) and (lp - bv) <= + dynamic_lighting_dead_zone %}\n {% set bv = lp %}\n{% elif + bv > (lp + dynamic_lighting_step_value) %}\n {% set bv = + lp + dynamic_lighting_step_value %}\n{% elif bv < (lp - dynamic_lighting_step_value) + %}\n {% set bv = lp - dynamic_lighting_step_value %}\n{% + endif %} {{ bv | round(0) }}\n" + dynamic_kelvin: "{% if expand(light_entities) | selectattr('state', + '==', 'off') | list | count > 0 %}\n {% set elevation = state_attr('sun.sun', + 'elevation') | float %}\n {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %}\n {% set + start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %}\n {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %}\n {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %}\n {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = ((end_slope * elevation) + end_ak) + | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n {% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n {% endif %}\n {{ + colour_temp_value | round(0) }}\n{% endif %}\n" + dynamic_light_off_data: "{% set light = namespace(data={}) %} + {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% set light.data = dict(light.data, + **{ 'color_temp_kelvin': dynamic_kelvin }) %} {% if 'use_transition' + in include_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': light_transition_on }) %}\n{% endif %} {{ + light.data }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n + \ {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {{ light.data }}\n" + in_dead_zone: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_max_lux + - dynamic_lighting_min_lux) %} {% set ak = (( slope * dynamic_lighting_min_lux) + * -1) + dynamic_lighting_max_brightness %} {% set le = light_entities + %} {% set lec = expand(le) | map(attribute='entity_id') | + list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 + * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% elif + lux >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if (bv > lp) and (dynamic_lighting_max_brightness + - lp) <= dynamic_lighting_dead_zone %}\n false\n{% elif (lp + > bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n false\n{% elif (bv >= lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n true\n{% elif (lp >= bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n true\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Check if the light is off + conditions: '{{ expand(light_entities) | selectattr(''state'', + ''=='', ''off'') | list | count > 0 }}' + sequence: + - alias: Delay for parallel actions needed for when the light + is off + delay: + milliseconds: 100 + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - alias: Delay for parallel actions needed for when the light + is on + delay: + milliseconds: 100 + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Adjust the light settings when all bypasses are off or + state control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - alias: Delay for parallel actions needed for when the light + is on + delay: + milliseconds: 100 + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If dynamic lighting brightness is in the dead zone + conditions: '{{ in_dead_zone }}' + sequence: + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting lux brightness and dynamic sun elevation + colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness'' + }}' + - '{{ expand(light_entities) | selectattr(''state'', ''=='', + ''off'') | list | count > 0 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_off_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting lux brightness for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% endif %} {{ colour_temp_value + | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'color_temp_kelvin': + dynamic_kelvin }) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': + light_transition_on }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if elevation >= (dynamic_lighting_sun_elevation_start_rising + - 2) and elevation <= (dynamic_lighting_sun_elevation_end_rising + + 2) and is_state_attr('sun.sun', 'rising', true) %}\n false\n + \ {% elif elevation <= (dynamic_lighting_sun_elevation_start_falling + + 2) and elevation >= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n false\n + \ {% elif elevation >= (dynamic_lighting_sun_elevation_end_rising + + 2) and elevation >= (dynamic_lighting_sun_elevation_start_falling + - 2) %}\n true\n {% elif elevation <= (dynamic_lighting_sun_elevation_start_rising + - 2) and is_state_attr('sun.sun', 'rising', true) %}\n true\n + \ {% elif elevation <= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n true\n + \ {% else %}\n true\n {% endif %}\n{% else %}\n false\n{% + endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Adjust the light settings when all bypasses are off or + state control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If sun elevation is outside the adjustment zone and the + lights are ON + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If the lights are OFF + conditions: '{{ expand(light_entities) | selectattr(''state'', + ''=='', ''off'') | list | count > 0 }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Set sun elevation colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 9 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + Lux + Controlled Brightness Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness_inv'' + }}' + sequence: + - alias: Parallel actions for dynamic lighting + parallel: + - sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_min_lux + - dynamic_lighting_max_lux) %} {% set ak = (( slope * dynamic_lighting_min_lux) + * -1) + dynamic_lighting_min_brightness %} {% set le = light_entities + %} {% set lec = expand(le) | map(attribute='entity_id') | + list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 + * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_min_brightness %}\n{% elif + lux >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if lp <= 0 %}\n {% set bv = bv %}\n{% + elif (bv > lp) and (dynamic_lighting_max_brightness - lp) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% elif (lp > bv) and (lp - dynamic_lighting_min_brightness) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% elif (bv > lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = lp %}\n{% elif (lp > bv) and (lp - bv) + <= dynamic_lighting_dead_zone %}\n {% set bv = lp %}\n{% + elif bv > (lp + dynamic_lighting_step_value) %}\n {% set + bv = lp + dynamic_lighting_step_value %}\n{% elif bv < (lp + - dynamic_lighting_step_value) %}\n {% set bv = lp - dynamic_lighting_step_value + %}\n{% endif %} {{ bv | round(0) }}\n" + dynamic_kelvin: "{% if expand(light_entities) | selectattr('state', + '==', 'off') | list | count > 0 %}\n {% set elevation = state_attr('sun.sun', + 'elevation') | float %}\n {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %}\n {% set + start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %}\n {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %}\n {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %}\n {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = ((end_slope * elevation) + end_ak) + | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n {% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n {% endif %}\n {{ + colour_temp_value | round(0) }}\n{% endif %}\n" + dynamic_light_off_data: "{% set light = namespace(data={}) %} + {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% set light.data = dict(light.data, + **{ 'color_temp_kelvin': dynamic_kelvin }) %} {% if 'use_transition' + in include_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': light_transition_on }) %}\n{% endif %} {{ + light.data }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n + \ {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {{ light.data }}\n" + in_dead_zone: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_min_lux + - dynamic_lighting_max_lux) %} {% set ak = (( slope * dynamic_lighting_min_lux) + * -1) + dynamic_lighting_min_brightness %} {% set le = light_entities + %} {% set lec = expand(le) | map(attribute='entity_id') | + list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 + * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_min_brightness %}\n{% elif + lux >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if (bv > lp) and (dynamic_lighting_max_brightness + - lp) <= dynamic_lighting_dead_zone %}\n true\n{% elif (lp + > bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n false\n{% elif (bv >= lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n true\n{% elif (lp >= bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n true\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Check if the light is off + conditions: '{{ expand(light_entities) | selectattr(''state'', + ''=='', ''off'') | list | count > 0 }}' + sequence: + - alias: Delay for parallel actions needed for when the light + is off + delay: + milliseconds: 100 + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - alias: Delay for parallel actions needed for when the light + is on + delay: + milliseconds: 100 + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Adjust the light settings when all bypasses are off or + state control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - alias: Delay for parallel actions needed for when the light + is on + delay: + milliseconds: 100 + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If dynamic lighting brightness is in the dead zone + conditions: '{{ in_dead_zone }}' + sequence: + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting lux brightness and dynamic sun elevation + colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness_inv'' + }}' + - '{{ expand(light_entities) | selectattr(''state'', ''=='', + ''off'') | list | count > 0 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_off_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting lux brightness for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness_inv'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% endif %} {{ colour_temp_value + | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'color_temp_kelvin': + dynamic_kelvin }) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': + light_transition_on }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if elevation >= (dynamic_lighting_sun_elevation_start_rising + - 2) and elevation <= (dynamic_lighting_sun_elevation_end_rising + + 2) and is_state_attr('sun.sun', 'rising', true) %}\n false\n + \ {% elif elevation <= (dynamic_lighting_sun_elevation_start_falling + + 2) and elevation >= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n false\n + \ {% elif elevation >= (dynamic_lighting_sun_elevation_end_rising + + 2) and elevation >= (dynamic_lighting_sun_elevation_start_falling + - 2) %}\n true\n {% elif elevation <= (dynamic_lighting_sun_elevation_start_rising + - 2) and is_state_attr('sun.sun', 'rising', true) %}\n true\n + \ {% elif elevation <= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n true\n + \ {% else %}\n true\n {% endif %}\n{% else %}\n false\n{% + endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Adjust the light settings when all bypasses are off or + state control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If sun elevation is outside the adjustment zone and the + lights are ON + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: If the lights are OFF + conditions: '{{ expand(light_entities) | selectattr(''state'', + ''=='', ''off'') | list | count > 0 }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - alias: Set sun elevation colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness_inv'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - trigger: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 10 - Dynamic Lighting - Time Controlled - Colour Temp + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_time_controled_colour'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set current_time = now().hour + (now().minute + / 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (start_time_morning - end_time_morning) + %} {% set start_ak = (start_slope * (-end_time_morning)) + dynamic_lighting_max_colour_temp + %} {% set end_slope = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (end_time_evening - start_time_evening) %} {% set end_ak = (end_slope + * (-end_time_evening)) + dynamic_lighting_min_colour_temp %} {% + if current_time >= start_time_morning and current_time <= end_time_morning + %}\n {% set colour_temp_value = (start_slope * current_time + + start_ak) | round(1) %}\n{% elif current_time >= start_time_evening + and current_time <= end_time_evening %}\n {% set colour_temp_value + = (end_slope * current_time + end_ak) | round(1) %}\n{% elif current_time + > end_time_morning and current_time < start_time_evening %}\n + \ {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n{% elif current_time < start_time_morning %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% elif current_time > + end_time_evening %}\n {% set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n{% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n{% endif %} {{ colour_temp_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'color_temp_kelvin': dynamic_kelvin + }) %} {% if 'use_transition' in include_light_control %}\n {% + set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if 'use_brightness' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'brightness_pct': + light_brightness }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set current_time = now().hour + (now().minute / + 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set lights_on = expand(light_entities) + | selectattr('state', '==', 'on') | list | count > 0 %} {% set + in_morning = start_time_morning <= current_time <= end_time_morning + %} {% set in_evening = start_time_evening <= current_time <= end_time_evening + %} {{ lights_on and not (in_morning or in_evening) }}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If time is outside the adjustment zone and the lights are + ON + conditions: '{{ should_wait }}' + sequence: + - choose: + - conditions: + - condition: time + after: !input dynamic_lighting_time_end_morning + before: !input dynamic_lighting_time_start_evening + sequence: + - wait_for_trigger: + - trigger: time + at: !input dynamic_lighting_time_start_evening + - conditions: + - condition: time + after: !input dynamic_lighting_time_end_evening + before: !input dynamic_lighting_time_start_morning + sequence: + - wait_for_trigger: + - trigger: time + at: !input dynamic_lighting_time_start_morning + - alias: Set the dynamic lighting brightness for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_time_controled_colour'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - choose: + - alias: 11 - Dynamic Lighting - Time Controlled - Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_time_controled_brightness'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set current_time = now().hour + (now().minute + / 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set start_slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (start_time_morning - end_time_morning) + %} {% set start_ak = (start_slope * (-end_time_morning)) + dynamic_lighting_max_brightness + %} {% set end_slope = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (end_time_evening - start_time_evening) %} {% set end_ak = (end_slope + * (-end_time_evening)) + dynamic_lighting_min_brightness %} {% + if current_time >= start_time_morning and current_time <= end_time_morning + %}\n {% set brightness_value = (start_slope * current_time + + start_ak) | round(1) %}\n{% elif current_time >= start_time_evening + and current_time <= end_time_evening %}\n {% set brightness_value + = (end_slope * current_time + end_ak) | round(1) %}\n{% elif current_time + > end_time_morning and current_time < start_time_evening %}\n + \ {% set brightness_value = dynamic_lighting_max_brightness %}\n{% + elif current_time < start_time_morning %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n{% elif current_time > end_time_evening + %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n{% endif %} {{ brightness_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n {% + set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbw_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbw_color': light_rgbw_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbww_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbww_color': light_rgbww_colour }) %}\n{% endif %} {{ light.data + }}\n" + should_wait: "{% set current_time = now().hour + (now().minute / + 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set lights_on = expand(light_entities) + | selectattr('state', '==', 'on') | list | count > 0 %} {% set + in_morning = start_time_morning <= current_time <= end_time_morning + %} {% set in_evening = start_time_evening <= current_time <= end_time_evening + %} {{ lights_on and not (in_morning or in_evening) }}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If time is outside the adjustment zone and the lights are + ON + conditions: '{{ should_wait }}' + sequence: + - choose: + - conditions: + - condition: time + after: !input dynamic_lighting_time_end_morning + before: !input dynamic_lighting_time_start_evening + sequence: + - wait_for_trigger: + - trigger: time + at: !input dynamic_lighting_time_start_evening + - conditions: + - condition: time + after: !input dynamic_lighting_time_end_evening + before: !input dynamic_lighting_time_start_morning + sequence: + - wait_for_trigger: + - trigger: time + at: !input dynamic_lighting_time_start_morning + - alias: Set the dynamic lighting brightness for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_time_controled_brightness'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - choose: + - alias: 12 - Dynamic Lighting - Time Controlled - Brightness Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_time_controled_brightness_inv'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set current_time = now().hour + (now().minute + / 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set start_slope = (dynamic_lighting_max_brightness + - dynamic_lighting_min_brightness) / (start_time_morning - end_time_morning) + %} {% set start_ak = (start_slope * (-end_time_morning)) + dynamic_lighting_min_brightness + %} {% set end_slope = (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (end_time_evening - start_time_evening) %} {% set end_ak = (end_slope + * (-end_time_evening)) + dynamic_lighting_max_brightness %} {% + if current_time >= start_time_morning and current_time <= end_time_morning + %}\n {% set brightness_value = (start_slope * current_time + + start_ak) | round(1) %}\n{% elif current_time >= start_time_evening + and current_time <= end_time_evening %}\n {% set brightness_value + = (end_slope * current_time + end_ak) | round(1) %}\n{% elif current_time + > end_time_morning and current_time < start_time_evening %}\n + \ {% set brightness_value = dynamic_lighting_min_brightness %}\n{% + elif current_time < start_time_morning %}\n {% set brightness_value + = dynamic_lighting_max_brightness %}\n{% elif current_time > end_time_evening + %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n{% endif %} {{ brightness_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n {% + set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbw_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbw_color': light_rgbw_colour }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgbww_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgbww_color': light_rgbww_colour }) %}\n{% endif %} {{ light.data + }}\n" + should_wait: "{% set current_time = now().hour + (now().minute / + 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set lights_on = expand(light_entities) + | selectattr('state', '==', 'on') | list | count > 0 %} {% set + in_morning = start_time_morning <= current_time <= end_time_morning + %} {% set in_evening = start_time_evening <= current_time <= end_time_evening + %} {{ lights_on and not (in_morning or in_evening) }}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If time is outside the adjustment zone and the lights are + ON + conditions: '{{ should_wait }}' + sequence: + - choose: + - conditions: + - condition: time + after: !input dynamic_lighting_time_end_morning + before: !input dynamic_lighting_time_start_evening + sequence: + - wait_for_trigger: + - trigger: time + at: !input dynamic_lighting_time_start_evening + - conditions: + - condition: time + after: !input dynamic_lighting_time_end_evening + before: !input dynamic_lighting_time_start_morning + sequence: + - wait_for_trigger: + - trigger: time + at: !input dynamic_lighting_time_start_morning + - alias: Set the dynamic lighting brightness for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_time_controled_brightness_inv'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - choose: + - alias: 13 - Dynamic Lighting - Time Controlled - Colour Temp & Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_time_controled_colour_brightness'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) or ((dynamic_lighting_boolean + != []) and (is_state(dynamic_lighting_boolean, ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set current_time = now().hour + (now().minute + / 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set start_slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (start_time_morning - end_time_morning) + %} {% set start_ak = (start_slope * (-end_time_morning)) + dynamic_lighting_max_brightness + %} {% set end_slope = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (end_time_evening - start_time_evening) %} {% set end_ak = (end_slope + * (-end_time_evening)) + dynamic_lighting_min_brightness %} {% + if current_time >= start_time_morning and current_time <= end_time_morning + %}\n {% set brightness_value = (start_slope * current_time + + start_ak) | round(1) %}\n{% elif current_time >= start_time_evening + and current_time <= end_time_evening %}\n {% set brightness_value + = (end_slope * current_time + end_ak) | round(1) %}\n{% elif current_time + > end_time_morning and current_time < start_time_evening %}\n + \ {% set brightness_value = dynamic_lighting_max_brightness %}\n{% + elif current_time < start_time_morning %}\n {% set brightness_value + = dynamic_lighting_min_brightness %}\n{% elif current_time > end_time_evening + %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n{% else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n{% endif %} {{ brightness_value | round(0) }}\n" + dynamic_kelvin: "{% set current_time = now().hour + (now().minute + / 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (start_time_morning - end_time_morning) + %} {% set start_ak = (start_slope * (-end_time_morning)) + dynamic_lighting_max_colour_temp + %} {% set end_slope = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (end_time_evening - start_time_evening) %} {% set end_ak = (end_slope + * (-end_time_evening)) + dynamic_lighting_min_colour_temp %} {% + if current_time >= start_time_morning and current_time <= end_time_morning + %}\n {% set colour_temp_value = (start_slope * current_time + + start_ak) | round(1) %}\n{% elif current_time >= start_time_evening + and current_time <= end_time_evening %}\n {% set colour_temp_value + = (end_slope * current_time + end_ak) | round(1) %}\n{% elif current_time + > end_time_morning and current_time < start_time_evening %}\n + \ {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n{% elif current_time < start_time_morning %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% elif current_time > + end_time_evening %}\n {% set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n{% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n{% endif %} {{ colour_temp_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% set + light.data = dict(light.data, **{ 'color_temp_kelvin': dynamic_kelvin + }) %} {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set current_time = now().hour + (now().minute / + 60) %} {% set start_time_morning = dynamic_lighting_time_start_morning.split(':')[0] + | int + (dynamic_lighting_time_start_morning.split(':')[1] | int + / 60) %} {% set end_time_morning = dynamic_lighting_time_end_morning.split(':')[0] + | int + (dynamic_lighting_time_end_morning.split(':')[1] | int + / 60) %} {% set start_time_evening = dynamic_lighting_time_start_evening.split(':')[0] + | int + (dynamic_lighting_time_start_evening.split(':')[1] | int + / 60) %} {% set end_time_evening = dynamic_lighting_time_end_evening.split(':')[0] + | int + (dynamic_lighting_time_end_evening.split(':')[1] | int + / 60) %} {% if end_time_morning < start_time_morning %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_morning + = end_time_morning + 24 %}\n {% set start_time_evening = start_time_evening + + 24 %}\n {% set end_time_evening = end_time_evening + 24 %}\n{% + endif %} {% if end_time_evening < start_time_evening %}\n {% + if current_time < end_time_evening %}\n {% set current_time + = current_time + 24 %}\n {% endif %}\n {% set end_time_evening + = end_time_evening + 24 %}\n{% endif %} {% set lights_on = expand(light_entities) + | selectattr('state', '==', 'on') | list | count > 0 %} {% set + in_morning = start_time_morning <= current_time <= end_time_morning + %} {% set in_evening = start_time_evening <= current_time <= end_time_evening + %} {{ lights_on and not (in_morning or in_evening) }}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t12 + - t13 + - t14 + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Adjust the light settings when all bypasses are off or state + control is enabled, and the entity is turned ON + conditions: + - condition: trigger + id: + - t15 + - t8_on + - t8_off + - t8_stop + - condition: template + value_template: '{{ repeat.index == 1 }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If time is outside the adjustment zone and the lights are + ON + conditions: '{{ should_wait }}' + sequence: + - choose: + - conditions: + - condition: time + after: !input dynamic_lighting_time_end_morning + before: !input dynamic_lighting_time_start_evening + sequence: + - wait_for_trigger: + - trigger: time + at: !input dynamic_lighting_time_start_evening + - conditions: + - condition: time + after: !input dynamic_lighting_time_end_evening + before: !input dynamic_lighting_time_start_morning + sequence: + - wait_for_trigger: + - trigger: time + at: !input dynamic_lighting_time_start_morning + - alias: Set the dynamic lighting brightness for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_time_controled_colour_brightness'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - stop: Stop the automation + - sequence: + - choose: + - alias: Turn ON lights + conditions: + - alias: Check the dynamic lighting options + condition: or + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''disable_dynamic_lighting'' + }}' + - alias: Check that dynamic lighting is active and the state control option + is enabled + condition: and + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: '{{ dynamic_lighting_state_control == ''enable_state_control'' + }}' + - condition: template + value_template: '{{ states(dynamic_lighting_state_control_entity) + == dynamic_lighting_state_control_activation_state_inv }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities_off }}' + data: '{{ light_data }}' + - choose: + - alias: Dynamic Lighting State Control Turning OFF + conditions: + - condition: trigger + id: t16 + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + sequence: + - action: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ light_data }}' + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - choose: + - alias: Check if normal lights scenes or scripts helper is on - This is for + scenes only when crossing over + conditions: + - condition: template + value_template: "{% if night_boolean_scenes_scripts == [] %}\n false\n{% + elif is_state(night_boolean_scenes_scripts, 'on') %}\n true\n{% else + %}\n false\n{% endif %}" + sequence: + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: '{{ night_boolean_scenes_scripts }}' + - choose: + - alias: Check if normal lights scenes or scripts helper is on - This + is for scenes only when crossing over + conditions: + - '{{ boolean_scenes_scripts != [] }}' + sequence: + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ boolean_scenes_scripts_helper }}' + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn on the scenes + action: scene.turn_on + target: + entity_id: '{{ scene_entities }}' + data: + transition: '{{ light_transition_on }}' + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ boolean_scenes_scripts_helper }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control }}' + sequence: + - alias: Turn on the scenes + action: scene.turn_on + target: + entity_id: '{{ scene_entities }}' + - alias: Turn on the boolean for scenes and scripts + action: input_boolean.turn_on + data: + entity_id: '{{ boolean_scenes_scripts_helper }}' + - alias: Turn on the switches + action: switch.turn_on + target: + entity_id: '{{ switch_entities_off }}' + - alias: Turn on the scripts + action: script.turn_on + target: + entity_id: '{{ script_entities }}' + - choose: + - alias: By-pass is enabled & check by-pass option - Turn lights on + conditions: + - condition: trigger + id: t7_on + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + action: homeassistant.turn_off + entity_id: !input motion_bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - choose: + - alias: Check if the trigger is on and wait for it to go off + conditions: + - condition: state + entity_id: !input motion_trigger + state: 'on' + match: any + sequence: + - alias: Wait until motion sensor is off + wait_for_trigger: + - trigger: state + entity_id: !input motion_trigger + from: 'on' + to: 'off' + - alias: Wait the number of minutes set in the normal lights time delay + delay: + minutes: !input time_delay + - choose: + - alias: Dynamic Lighting - Check if Input Boolean Helper is ON + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - condition: template + value_template: "{% if dynamic_lighting_boolean != [] %}\n {{ is_state(dynamic_lighting_boolean, + 'on') }}\n{% endif %}" + sequence: + - alias: Turn off the boolean for dynamic lighting + action: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control }}' + sequence: + - alias: Turn off the lights + action: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + action: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + action: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + action: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + action: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts diff --git a/blueprints/automation/Blackshome/smart-light.yaml b/blueprints/automation/Blackshome/smart-light.yaml new file mode 100644 index 0000000..714bca1 --- /dev/null +++ b/blueprints/automation/Blackshome/smart-light.yaml @@ -0,0 +1,6818 @@ +blueprint: + name: Smart Light + description: "# \U0001F506 Smart Light\n**Version: 2.3**\n\nSmart Lighting: Your + Lights, Your Way - Take Control and Customize it to Perfection!\U0001F4A1✨\n\n**If + you like my blueprints, and would like to show your support or just say thank + you?** [Click Here](https://www.paypal.com/donate/?hosted_button_id=WAZS3QSDTPGA8) + \U0001F642\n\n
The Automation Process: \U0001F448\n\n\n + \ - **Trigger Options:**\n\n Activate lights, switches, scenes, and scripts + when the automation is triggered.\n\n - **Entity State:**\n - Turn lights + ON when an entity changes from OFF to ON.\n - Turn lights OFF when an entity + changes from ON to OFF.\n\n - **Sun Elevation:**\n - Turn lights ON when + the sun's elevation falls below a set value.\n - Turn lights OFF when the + sun's elevation rises above a set value. \n\n - **Ambient Light:**\n - + Turn lights ON when ambient light falls below a set LUX value.\n - Turn lights + OFF when ambient light rises above a set LUX value. \n\n - **Time-Based Option:**\n + \ - Define start and end times to turn lights ON and OFF.\n\n - **Light Control + Options:**\n - Utilize \"Light Control\" to adjust brightness, colour temperature, + colour, and transition times.\n\n - **Dynamic Lighting Options:**\n - Opt + for 'Dynamic Lighting' to continuously adjust brightness based on lux values or + adapt colour temperature and brightness according to the sun's changing elevation.\n\n + \ - **Night Lights Mode:**\n - Enable 'Night Lights' for softer illumination + during night time activities, automatically enabled based on conditions.\n\n - + **Manual Override:**\n - Use \"Bypass Options\" for manual bypassing of trigger + sensors, providing manual control and additional customization.\n\n - **Weekdays + Condition Option:**\n - Specify the weekday selections to define when the automation + can run.\n
\n\nNeed help?\n- FAQ: [Click Here](https://community.home-assistant.io/t/527354/2)\n- + Community Support Including Updates: [Click Here](https://community.home-assistant.io/t/527354)\n\nRequired + = *\n" + domain: automation + input: + trigger_entity: + name: Trigger - Entity State + icon: mdi:cog-outline + collapsed: true + input: + include_entity_state: + name: Use The Entity State Option (Optional) + description: "This option adds triggers that activate when the specified + entity changes its state. When the entity changes its state from OFF to + ON, it will turn your lights ON. When the entity changes its state from + ON to OFF, it will turn your lights OFF.\n\nYou can choose the ON trigger, + the OFF trigger, or both. For more information about triggers and how + they can be used, [Click Here](https://community.home-assistant.io/t/527354/110)\n\nIf + you would like to use a motion sensor, please consider using my \U0001F4A1Sensor + Light Blueprint [Click Here](https://community.home-assistant.io/t/481048)\n" + default: [] + selector: + select: + multiple: true + options: + - label: ON State + value: entity_on + - label: OFF State + value: entity_off + sort: false + custom_value: false + entity_input: + name: Input Entity + description: 'Please select an entity with an ON/OFF state. + + ' + default: [] + selector: + entity: {} + trigger_sun: + name: Trigger - Sun Elevation + icon: mdi:weather-sunny + collapsed: true + input: + include_sun: + name: Use The Sun Option (Optional) + description: 'This option adds triggers that activate when the sun''s elevation + value crosses its set threshold. When it goes below the **Sun Elevation + Falling** value, it will turn your lights ON. When it goes above the **Sun + Elevation Rising** value, it will turn your lights OFF. + + + You can choose the ON trigger, the OFF trigger, or both. For more information + about triggers and how they can be used, [Click Here](https://community.home-assistant.io/t/527354/110) + + + For more information on sun settings [Click Here](https://community.home-assistant.io/t/527354/83) + + ' + default: [] + selector: + select: + multiple: true + options: + - label: Sun Elevation Falling - ON + value: sun_falling + - label: Sun Elevation Rising - OFF + value: sun_rising + sort: false + custom_value: false + sun_elevation: + name: Sun Elevation Falling + description: 'The sun elevation falling refers to the angle between the + sun and the horizon when the sun is setting. A negative value indicates + that the sun is BELOW the horizon. For example, a setting guide of -1.5 + corresponds to dusk + + ' + default: -1.5 + selector: + number: + min: -10.0 + max: 5.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + sun_elevation_rising: + name: Sun Elevation Rising + description: 'The sun elevation rising refers to the angle between the sun + and the horizon during sunrise. A negative value indicates that the sun + is BELOW the horizon. For example, a setting guide of -4.0 corresponds + to dawn. + + ' + default: -4.0 + selector: + number: + min: -10.0 + max: 5.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + trigger_ambient: + name: Trigger - Ambient + icon: mdi:theme-light-dark + collapsed: true + input: + include_ambient: + name: Use The Ambient Option (Optional) + description: 'This option adds triggers that activate when the ambient light + LUX value crosses its set threshold. When it goes below the **Low LUX + Value**, it will turn your lights ON. When it goes above the **High LUX + Value**, it will turn your lights OFF. + + + You can choose the ON trigger, the OFF trigger, or both. For more information + about triggers and how they can be used, [Click Here](https://community.home-assistant.io/t/527354/110) + + ' + default: [] + selector: + select: + multiple: true + options: + - label: Low Lux Value - ON + value: ambient_low + - label: High Lux Value - OFF + value: ambient_high + sort: false + custom_value: false + ambient_light_sensor: + name: Ambient Light Sensor + description: 'Select the ambient light sensor. + + ' + default: [] + selector: + entity: + filter: + - domain: + - sensor + device_class: + - illuminance + multiple: false + ambient_light_value: + name: Ambient Light - Low Lux Value + description: 'Set the Ambient Light Low Lux Value. The light will turn ON + when the lux level is below the set value. This value must be equal or + lower than the "High Lux Value" below. Guide is 20 lux (dusk). + + ' + default: 20 + selector: + number: + min: 0.0 + max: 500.0 + step: 10.0 + unit_of_measurement: lux + mode: slider + ambient_light_value_off: + name: Ambient Light - High Lux Value + description: 'Set the Ambient Light High Lux Value. The light will turn + OFF when the lux level is above the set value. This value must be equal + or higher than the "Low Lux Value" above. Setting a value higher than + the low value allows for an offset. Guide is 80 lux (dawn). + + ' + default: 80 + selector: + number: + min: 0.0 + max: 1000.0 + step: 10.0 + unit_of_measurement: lux + mode: slider + trigger_time: + name: Trigger - Time + icon: mdi:clock-outline + collapsed: true + input: + include_time: + name: Use The Time Options (Optional) + description: 'This option adds triggers that activate when the specified + time value crosses its set threshold. When the time is below the **ON + Time** setting, it will turn your lights ON. When the time is above the + **OFF Time** setting, it will turn your lights OFF. + + + You can choose the ON trigger, the OFF trigger, or both. For more information + about triggers and how they can be used, [Click Here](https://community.home-assistant.io/t/527354/110) + + ' + default: [] + selector: + select: + multiple: true + options: + - label: ON Time + value: time_on + - label: OFF Time + value: time_off + sort: false + custom_value: false + after_time: + name: ON Time + description: 'Set the on time. + + ' + default: 00:00:00 + selector: + time: {} + before_time: + name: OFF Time + description: 'Set the off time. + + ' + default: 00:00:00 + selector: + time: {} + lights: + name: Lights * + icon: mdi:lightbulb-outline + collapsed: true + input: + light_switch: + name: Lights - Switches - Scenes - Scripts * + description: 'The lights, switches, scenes, and scripts that will be activated + by the trigger. If adding a scene or script, please refer to the ''Scenes + & Scripts - Toggle Helper'' section and the ''Scenes & Scripts - Turn + OFF'' section below. + + + **NOTE** - You can only use entities. Areas, devices and labels are not + supported. + + ' + default: [] + selector: + target: + entity: + - domain: + - light + - switch + - scene + - script + boolean_scenes_scripts: + name: Scenes & Scripts - Toggle Helper + description: 'To ensure the smooth operation of the automation, it''s recommended + to create an independent toggle helper when selecting a scene or script + in "Lights - Switches - Scenes - Scripts" above and then enter it here. + + ' + default: [] + selector: + entity: + filter: + - domain: + - input_boolean + multiple: false + end_scenes: + name: Scenes - Scripts To Turn OFF + description: 'If you have selected a scene or a script to be turned ON above + in "Lights - Switches - Scenes - Scripts" and you would like it to be + turned OFF after the time delay, then you must create a scene or a script + with everything OFF and enter it here. + + ' + default: [] + selector: + entity: + multiple: true + filter: + - domain: + - scene + - script + lights_settings: + name: Light Control + icon: mdi:lightbulb-on-outline + collapsed: true + input: + include_light_control: + name: Use The Light Control Options (Optional) + description: 'Select if you would like to use brightness or transition. + These settings will only affect a ''light'' entity that supports each + selected option. The settings for brightness and transition are provided + below. + + ' + default: [] + selector: + select: + multiple: true + options: + - label: Use Brightness + value: use_brightness + - label: Use Transition + value: use_transition + sort: false + custom_value: false + light_brightness: + name: Brightness + description: 'The brightness setting for the lights when they are turned + ON. + + ' + default: 100 + selector: + number: + min: 1.0 + max: 100.0 + mode: slider + step: 1.0 + unit_of_measurement: '%' + light_transition_on: + name: Transition - ON + description: 'The transition setting for the lights when they are turned + ON. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 5.0 + mode: slider + step: 0.5 + unit_of_measurement: seconds + light_transition_off: + name: Transition - OFF + description: 'The transition setting for the lights when they are turned + OFF. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 30.0 + mode: slider + step: 1.0 + unit_of_measurement: seconds + include_light_colour_control: + name: Use The Light Colour Control Options (Optional) + description: 'Select if you would like to use colour temperature, RGB, RGBW + or RGBWW colour. These settings will only affect a ''light'' entity that + supports each selected option. The settings for colour temperature, RGB, + RGBW and RGBWW colour are provided below. + + ' + default: disable_colour_control + selector: + select: + options: + - label: Use Colour Temperature + value: use_colour_temperature + - label: Use RGB Colour + value: use_rgb_colour + - label: Use RGBW Colour + value: use_rgbw_colour + - label: Use RGBWW Colour + value: use_rgbww_colour + - label: Disable Colour Control + value: disable_colour_control + multiple: false + sort: false + custom_value: false + light_colour_temperature: + name: Colour Temperature + description: 'The colour temperature setting for the lights when they are + turned ON. + + ' + default: 5000 + selector: + number: + min: 2000.0 + max: 8000.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + light_rgb_colour: + name: RGB Colour + description: 'The RGB colour setting for the lights when they are turned + ON. + + ' + default: + - 255 + - 255 + - 255 + selector: + color_rgb: {} + light_rgbw_colour: + name: RGBW Colour + description: 'The RGBW colour setting for the lights when they are turned + ON. Enter four integers between 0 and 255 to define the values for Red, + Green, Blue, and White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + light_rgbww_colour: + name: RGBWW Colour + description: 'The RGBWW colour setting for the lights when they are turned + ON. Enter five integers between 0 and 255 to define the values for Red, + Green, Blue, Cold White and Warm White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + dynamic_lighting_settings: + name: Dynamic Lighting + icon: mdi:creation-outline + collapsed: true + input: + include_dynamic_lighting: + name: Use The Dynamic Lighting Options (Optional) + description: 'This option aims to make continual adjustments in your lighting + setup by modulating brightness according to floating lux values or adapting + colour temperature and brightness based on the sun''s changing elevation. + Choose from nine preset options available in the dropdown menu. + + + 1 - Lux Controlled Brightness + + 2 - Lux Controlled Brightness Inverted + + 3 - Sun Elevation Lighting - Colour Temp + + 4 - Sun Elevation Lighting - Brightness + + 5 - Sun Elevation Lighting - Brightness Inverted + + 6 - Sun Elevation Lighting - Colour Temp + Brightness + + 7 - Sun Elevation Lighting - Colour Temp + Brightness Inverted + + 8 - Sun Elevation Lighting - Colour Temp + Lux Controlled Brightness + + 9 - Sun Elevation Lighting - Colour Temp + Lux Controlled Brightness Inverted + + + A numbering system has been implemented to facilitate navigation within + the dropdown selections. Each number corresponds to a specific configuration, + aiding users in identifying and adjusting the settings used within each + selection. For instance, when selecting "3 - Sun Elevation Lighting - + Colour Temp" as a dropdown option, settings marked "Used in options 3, + 6, 7, 8 or 9" are required for that respective selection because number + 3 is included in those options. + + + For more information on dynamic lighting settings [Click Here](https://community.home-assistant.io/t/481048/837) + + ' + default: disable_dynamic_lighting + selector: + select: + mode: dropdown + options: + - label: Disable Dynamic Lighting + value: disable_dynamic_lighting + - label: 1 - Lux Controlled Brightness + value: enable_lux_controled_brightness + - label: 2 - Lux Controlled Brightness Inverted + value: enable_lux_controled_brightness_inv + - label: 3 - Sun Elevation Lighting - Colour Temp + value: enable_sun_elevation_colour + - label: 4 - Sun Elevation Lighting - Brightness + value: enable_sun_elevation_brightness + - label: 5 - Sun Elevation Lighting - Brightness Inverted + value: enable_sun_elevation_brightness_inv + - label: 6 - Sun Elevation Lighting - Colour Temp + Brightness + value: enable_sun_elevation_colour_brightness + - label: 7 - Sun Elevation Lighting - Colour Temp + Brightness Inverted + value: enable_sun_elevation_colour_brightness_inv + - label: 8 - Sun Elevation Lighting - Colour Temp + Lux Controlled Brightness + value: enable_sun_elevation_colour_lux_brightness + - label: 9 - Sun Elevation Lighting - Colour Temp + Lux Controlled Brightness + Inverted + value: enable_sun_elevation_colour_lux_brightness_inv + multiple: false + sort: false + custom_value: false + dynamic_lighting_lux_sensor: + name: Dynamic Lighting - Ambient Light Sensor + description: '**Used in options 1, 2, 8 or 9** - Enter the specific ambient + light sensor to be used, based on the chosen settings. + + ' + default: [] + selector: + entity: + filter: + - domain: + - sensor + device_class: + - illuminance + multiple: false + dynamic_lighting_max_lux: + name: Dynamic Lighting - Max Lux Value + description: '**Used in options 1, 2, 8 or 9** - Specify the maximum lux + value. Once the lux level meets or surpasses this set point, your lights + will adjust to either their maximum or minimum brightness, depending on + your chosen option. This value indicates when you prefer your lights + to start turning on or off. + + ' + default: 400 + selector: + number: + min: 10.0 + max: 900.0 + step: 10.0 + unit_of_measurement: lux + mode: slider + dynamic_lighting_min_lux: + name: Dynamic Lighting - Min Lux Value + description: '**Used in options 1, 2, 8 or 9** - Specify the minimum lux + value. Once the lux level meets or surpasses this set point, your lights + will adjust to either their maximum or minimum brightness, depending on + your chosen option. This value indicates when you prefer your lights + to start turning on or off. + + ' + default: 40 + selector: + number: + min: 0.0 + max: 600.0 + step: 10.0 + unit_of_measurement: lux + mode: slider + dynamic_lighting_max_brightness: + name: Dynamic Lighting - Max Brightness Value + description: '**Used in options 1, 2, 4, 5, 6, 7, 8 or 9** - Define the + maximum brightness value, determines the peak brightness level for your + lights. + + ' + default: 100 + selector: + number: + min: 10.0 + max: 100.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + dynamic_lighting_min_brightness: + name: Dynamic Lighting - Min Brightness Value + description: '**Used in options 1, 2, 4, 5, 6, 7, 8 or 9** - Specify the + minimum brightness value, establishing the lowest brightness level for + your lights. When setting the brightness to 0%, the light will be turned + off. + + ' + default: 0 + selector: + number: + min: 0.0 + max: 100.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + dynamic_lighting_boolean: + name: Dynamic Lighting - Toggle Helper (Optional) + description: '**Used in options 1, 2, 3, 4, 5, 6, 7, 8 or 9** - If you have + chosen a brightness level of 0%, then it''s essential to create an independent + toggle helper and enter it here. This toggle helper can also address issues + if the automation doesn''t behave as expected due to the occasional unavailability + of light entities. While it may not always be necessary, if you''re open + to creating toggle helpers, it''s highly recommended to utilize this option + to ensure proper functionality of the automation. + + ' + default: [] + selector: + entity: + filter: + - domain: + - input_boolean + multiple: false + dynamic_lighting_max_colour_temp: + name: Dynamic Lighting - Max Colour Temperature + description: '**Used in options 3, 6, 7, 8 or 9** - Specify the highest + colour temperature value, determining the coolest colour setting for your + lights. + + ' + default: 5000 + selector: + number: + min: 2500.0 + max: 8000.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + dynamic_lighting_min_colour_temp: + name: Dynamic Lighting - Min Colour Temperature + description: '**Used in options 3, 6, 7, 8 or 9** - Set the lowest colour + temperature value, defining the warmest colour setting for your lights. + + ' + default: 3000 + selector: + number: + min: 2000.0 + max: 7500.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + dynamic_lighting_sun_elevation_start_rising: + name: Dynamic Lighting - Sun Elevation Rising - Start Point + description: '**Used in options 3, 4, 5, 6, 7, 8 or 9** - When the sun rises + above the sensor''s starting point, both the colour temperature and brightness + percentage will transition linearly from their minimum to maximum values + until they reach the rising endpoint, based on the chosen settings. + + ' + default: -1.5 + selector: + number: + min: -10.0 + max: 30.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + dynamic_lighting_sun_elevation_end_rising: + name: Dynamic Lighting - Sun Elevation Rising - End Point + description: '**Used in options 3, 4, 5, 6, 7, 8 or 9** - When the sun rises + above the sensor''s endpoint, the maximum colour temperature and maximum + brightness value will be established according to the chosen settings. + + ' + default: 15 + selector: + number: + min: 0.0 + max: 90.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + dynamic_lighting_sun_elevation_start_falling: + name: Dynamic Lighting - Sun Elevation Descending - Start Point + description: '**Used in options 3, 4, 5, 6, 7, 8 or 9** - When the sun descends + below the sensor''s starting point, both the colour temperature and brightness + percentage will transition linearly from their maximum to minimum values + until they reach the descending endpoint, based on the chosen settings. + + ' + default: 15 + selector: + number: + min: 0.0 + max: 90.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + dynamic_lighting_sun_elevation_end_falling: + name: Dynamic Lighting - Sun Elevation Descending - End Point + description: '**Used in options 3, 4, 5, 6, 7, 8 or 9** - When the sun descends + below the sensor''s endpoint, the minimum colour temperature and minimum + brightness value will be established according to the chosen settings. + + ' + default: -4.0 + selector: + number: + min: -10.0 + max: 30.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + dynamic_lighting_heartbeat: + name: Dynamic Lighting - Heartbeat + description: "**Used in options 1, 2, 8 or 9** - Define the heartbeat rate, + setting the operational speed. During each heartbeat, it evaluates the + lux value, light brightness, and targeted brightness based on the chosen + settings,\n adjusting the brightness as per the selections.\n" + default: 1 + selector: + number: + min: 0.25 + max: 10.0 + step: 0.25 + unit_of_measurement: minutes + mode: slider + dynamic_lighting_step_value: + name: Dynamic Lighting - Step Value + description: '**Used in options 1, 2, 8 or 9** - With every heartbeat, the + system evaluates the lux value, light brightness, and the targeted brightness. + If the targeted brightness surpasses the predefined step value, the system + applies the set step value to gracefully moderate the rate of changes, + facilitating a smoother transition in light brightness. + + ' + default: 4 + selector: + number: + min: 1.0 + max: 100.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + dynamic_lighting_dead_zone: + name: Dynamic Lighting - Dead Zone (±) + description: '**Used in options 1, 2, 8 or 9** - With every heartbeat, the + system evaluates the current light brightness and the targeted brightness. + If the targeted brightness falls within the defined dead zone, the system + maintains the current light brightness. This prevents minor fluctuations + in light intensity that could be bothersome to the eye. + + ' + default: 7 + selector: + number: + min: 0.0 + max: 15.0 + step: 1.0 + unit_of_measurement: '%' + mode: slider + bypass_settings: + name: Bypass + icon: mdi:cog-pause-outline + collapsed: true + input: + include_bypass: + name: Use The Bypass Options (Optional) + description: 'Select if you would like to enable an option. Each option + allows manual control of your lights but alters its behaviour by either + turning ON, OFF, or keeping its current state when the bypass is switched + ON. When enabling an option, please enter the bypass switch into the corresponding + input below. + + ' + default: [] + selector: + select: + options: + - label: 1 - Enable the Bypass - Turn lights ON + value: bypass_enabled_turn_on + - label: 2 - Enable the Bypass - Turn lights OFF + value: bypass_enabled_turn_off + - label: 3 - Enable the Bypass - Keep the lights current state + value: bypass_enabled_stop + multiple: true + sort: false + custom_value: false + bypass_lights_on: + name: Bypass Switch - Turn lights ON + description: 'Select the switches that will turn your lights ON, bypass + the trigger sensor, and allow your lights to function as normal. Please + note that the entity cannot be included in the ''Lights - Switches - Scenes + - Scripts'' or ''Night Lights'' selections. + + ' + default: [] + selector: + entity: + multiple: true + bypass_lights_off: + name: Bypass Switch - Turn lights OFF + description: 'Select the switches that will turn your lights OFF, bypass + the trigger sensor, and allow your lights to function as normal. Please + note that the entity cannot be included in the ''Lights - Switches - Scenes + - Scripts'' or ''Night Lights'' selections. + + ' + default: [] + selector: + entity: + multiple: true + bypass_lights_stop: + name: Bypass Switch - Keep The Lights Current State + description: 'Select the switches that will keep your lights current state, + bypass the trigger sensor, and allow your lights to function as normal. + Please note that the entity cannot be included in the ''Lights - Switches + - Scenes - Scripts'' or ''Night Lights'' selections. + + ' + default: [] + selector: + entity: + multiple: true + bypass_time_delay: + name: Bypass - Time Delay + description: 'This is only used in two bypass scenarios; when the lights + are already ON and when the automation turns the lights OFF. The first + scenario occurs when you have selected option 2 above to "Enable the Bypass + - Turn lights OFF". The second scenario occurs when you turn the bypass + OFF, the trigger sensor is OFF, and your lights are ON. In both scenarios, + the automation will turn your lights OFF after the set time delay. + + ' + default: 0 + selector: + number: + min: 0.0 + max: 10.0 + step: 0.25 + unit_of_measurement: minutes + mode: slider + include_bypass_auto_off: + name: Use The Bypass Auto OFF Option (Optional) + description: 'This is used when you turn ON the bypass and want it to automatically + turn OFF after a set time delay. You can choose which bypass option you + would like to apply the auto OFF feature to + + ' + default: [] + selector: + select: + options: + - label: Enable the auto OFF for the bypass switch - Turn lights ON + value: bypass_auto_off_enabled_on + - label: Enable the auto OFF for the bypass switch - Turn lights OFF + value: bypass_auto_off_enabled_off + - label: Enable the auto OFF for the bypass switch - Keep The Lights + Current State + value: bypass_auto_off_enabled_stop + multiple: true + sort: false + custom_value: false + bypass_auto_off_delay: + name: Bypass Auto OFF - Time Delay + description: 'Set the bypass auto OFF time delay. The time delay starts + from the last bypass that was turned ON. + + ' + default: 60 + selector: + number: + min: 1.0 + max: 240.0 + step: 1.0 + unit_of_measurement: minutes + mode: slider + weekdays: + name: Weekdays + icon: mdi:calendar-week + collapsed: true + input: + include_weekdays: + name: Use The Weekdays Option (Optional) + description: 'This option adds a condition to operate only on specified + weekdays. It applies globally to all triggers. + + ' + default: weekday_disabled + selector: + select: + options: + - label: Enable the weekday option + value: weekday_enabled + - label: Disable the weekday option + value: weekday_disabled + multiple: false + sort: false + custom_value: false + weekday_options: + name: Weekdays + description: 'Select the days of the week on which you want the automation + to run. + + ' + default: + - mon + - tue + - wed + - thu + - fri + - sat + - sun + selector: + select: + multiple: true + mode: list + options: + - label: Monday + value: mon + - label: Tuesday + value: tue + - label: Wednesday + value: wed + - label: Thursday + value: thu + - label: Friday + value: fri + - label: Saturday + value: sat + - label: Sunday + value: sun + sort: false + custom_value: false + night_lights_trigger_settings: + name: Night Lights Settings + icon: mdi:weather-night + collapsed: true + input: + include_night_lights: + name: Use The Night Lights Options (Optional) + description: 'Enabling the night lights option allows for separate and customizable + lighting control when night light conditions are met. Night light condition + options include entity state, time, or sun elevation. Please choose the + condition option you would like to use below. + + + This feature is particularly beneficial for creating a soothing ambiance + with softer lighting at night. The flexibility of these options allows + for personalized adjustments, enhancing your nighttime ambiance and comfort, + especially when moving around during the night. + + ' + default: night_lights_disabled + selector: + select: + options: + - label: Enable the night lights options + value: night_lights_enabled + - label: Disable the night lights options + value: night_lights_disabled + multiple: false + sort: false + custom_value: false + night_lights_conditions: + name: Night Lights Conditions (Required For Night Lights) + description: 'Select any night light condition from the options provided. + Ensure you''ve enabled **Use the night lights options** in the section + above and choose at least one **Night Lights Condition** for night lights + to operate. + + + Settings for each option are provided below. + + ' + default: [] + selector: + select: + multiple: true + options: + - label: Enable entity state option + value: entity_state_enabled + - label: Enable time option + value: time_enabled + - label: Enable sun elevation option + value: sun_enabled + sort: false + custom_value: false + night_lights_entity_state: + name: Night Lights - Entity State + description: 'Select an entity that will trigger the activation of night + lights when turned ON. This could be your phone on **Do Not Disturb**, + a helper, a bed sensor, etc. Please note that the selected entity cannot + be included in the **Lights - Switches - Scenes - Scripts** or **Night + Lights** selections. + + ' + default: [] + selector: + entity: + multiple: true + night_lights_after_time: + name: Night Lights - Start Time + description: 'Set the start time. + + ' + default: 00:00:00 + selector: + time: {} + night_lights_before_time: + name: Night Lights - End Time + description: 'Set the end time. + + ' + default: 00:00:00 + selector: + time: {} + night_lights_sun_elevation: + name: Night Lights - Sun Elevation Falling + description: 'The sun elevation falling refers to the angle between the + sun and the horizon when the sun is setting. A negative value indicates + that the sun is BELOW the horizon. For example, a setting guide of -1.5 + corresponds to dusk. + + ' + default: -1.5 + selector: + number: + min: -10.0 + max: 5.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + night_lights_sun_elevation_rising: + name: Night Lights - Sun Elevation Rising + description: 'The sun elevation rising refers to the angle between the sun + and the horizon during sunrise. A negative value indicates that the sun + is BELOW the horizon. For example, a setting guide of -4.0 corresponds + to dawn. + + ' + default: -4.0 + selector: + number: + min: -10.0 + max: 5.0 + step: 0.5 + unit_of_measurement: degrees + mode: slider + night_lights_settings: + name: Night Lights + icon: mdi:lightbulb-night-outline + collapsed: true + input: + night_lights: + name: Night Lights + description: 'The lights, switches, scenes, and scripts that will be activated + by the trigger sensor/s. If adding a scene or script, please refer to + the ''Night Lights - Scenes & Scripts - Toggle Helper'' section below + and the ''Scenes - Scripts To Turn OFF'' section above. + + + **NOTE** - You can only use entities. Areas, devices and labels are not + supported. + + ' + default: {} + selector: + target: + entity: + - domain: + - light + - switch + - scene + - script + night_boolean_scenes_scripts: + name: Night Lights - Scenes & Scripts - Toggle Helper + description: 'To ensure the smooth operation of the automation, it''s recommended + to create an independent toggle helper when selecting a scene or script + in "Night Lights" above and then enter it here. + + ' + default: [] + selector: + entity: + filter: + - domain: + - input_boolean + multiple: false + night_lights_light_control_settings: + name: Night Light Control + icon: mdi:lightbulb-on-80 + collapsed: true + input: + include_night_light_control: + name: Night Lights - Use The Light Control Options (Optional) + description: "Select if you would like to use brightness or transition. + These settings will only affect a 'light' entity that supports each selected + option. The settings for brightness and transition are provided below. + \n\nSelecting **If lights are ON, adjust the lights when crossing over** + option ensures that the light control settings will be applied to any + lights that are already ON during the transition from normal lights to + night lights and vice versa.\n\nSelecting **Yes - Manage OFF script when + crossing over** is beneficial for scripts containing if-then-else actions + that toggle various functions on and off during the transition from normal + lights to night lights and vice versa.\n" + default: [] + selector: + select: + multiple: true + options: + - label: Use brightness + value: use_brightness + - label: Use transition + value: use_transition + - label: If lights are ON, adjust the lights when crossing over + value: if_lights_are_on_adjust_when_crossing_over + - label: Yes - Manage OFF script when crossing over + value: manage_scripts_crossing_over + sort: false + custom_value: false + night_light_brightness: + name: Night Lights - Brightness + description: 'The brightness setting of the night lights when they are turned + ON. + + ' + default: 20 + selector: + number: + min: 1.0 + max: 100.0 + mode: slider + step: 1.0 + unit_of_measurement: '%' + night_light_transition_on: + name: Night Lights - Transition - ON + description: 'The transition of the night lights when they are turned ON. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 5.0 + mode: slider + step: 0.5 + unit_of_measurement: seconds + night_light_transition_off: + name: Night Lights - Transition - OFF + description: 'The transition of the night lights when they are turned OFF. + + ' + default: 1 + selector: + number: + min: 0.0 + max: 30.0 + mode: slider + step: 1.0 + unit_of_measurement: seconds + include_night_light_colour_control: + name: Use The Night Light Colour Control Options (Optional) + description: 'Select if you would like to use colour temperature, RGB, RGBW + or RGBWW colour. These settings will only affect a ''light'' entity that + supports each selected option. The settings for colour temperature, RGB, + RGBW and RGBWW colour are provided below + + ' + default: disable_colour_control + selector: + select: + options: + - label: Use Colour Temperature + value: use_colour_temperature + - label: Use RGB Colour + value: use_rgb_colour + - label: Use RGBW Colour + value: use_rgbw_colour + - label: Use RGBWW Colour + value: use_rgbww_colour + - label: Disable Colour Control + value: disable_colour_control + multiple: false + sort: false + custom_value: false + night_light_colour_temperature: + name: Night Lights - Colour Temperature + description: 'The colour temperature setting for the night lights when they + are turned ON. + + ' + default: 5000 + selector: + number: + min: 2000.0 + max: 8000.0 + mode: slider + step: 100.0 + unit_of_measurement: kelvin + night_light_rgb_colour: + name: Night Lights - RGB Colour + description: 'The RGB colour setting for the lights when they are turned + ON. + + ' + default: + - 255 + - 255 + - 255 + selector: + color_rgb: {} + night_light_rgbw_colour: + name: Night Lights - RGBW Colour + description: 'The RGBW colour setting for the lights when they are turned + ON. Enter four integers between 0 and 255 to define the values for Red, + Green, Blue, and White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + night_light_rgbww_colour: + name: Night Lights - RGBWW Colour + description: 'The RGBWW colour setting for the lights when they are turned + ON. Enter five integers between 0 and 255 to define the values for Red, + Green, Blue, Cold White and Warm White. + + ' + default: + - 255 + - 255 + - 255 + - 255 + - 255 + selector: + object: {} + source_url: https://gist.github.com/Blackshome/e6c8f1bf846bab2fa4431934a0a85770 +mode: restart +max_exceeded: silent +variables: + include_entity_state: !input include_entity_state + entity_input: !input entity_input + include_sun: !input include_sun + sun_elevation: !input sun_elevation + sun_elevation_rising: !input sun_elevation_rising + include_ambient: !input include_ambient + ambient_light_sensor: !input ambient_light_sensor + ambient_light_value: !input ambient_light_value + ambient_light_value_off: !input ambient_light_value_off + include_time: !input include_time + after_time: !input after_time + before_time: !input before_time + light_switch: !input light_switch + boolean_scenes_scripts: !input boolean_scenes_scripts + end_scenes: !input end_scenes + include_light_control: !input include_light_control + light_brightness: !input light_brightness + light_transition_on: !input light_transition_on + light_transition_off: !input light_transition_off + include_light_colour_control: !input include_light_colour_control + light_colour_temperature: !input light_colour_temperature + light_rgb_colour: !input light_rgb_colour + light_rgbw_colour: !input light_rgbw_colour + light_rgbww_colour: !input light_rgbww_colour + include_dynamic_lighting: !input include_dynamic_lighting + dynamic_lighting_lux_sensor: !input dynamic_lighting_lux_sensor + dynamic_lighting_max_lux: !input dynamic_lighting_max_lux + dynamic_lighting_min_lux: !input dynamic_lighting_min_lux + dynamic_lighting_max_brightness: !input dynamic_lighting_max_brightness + dynamic_lighting_min_brightness: !input dynamic_lighting_min_brightness + dynamic_lighting_boolean: !input dynamic_lighting_boolean + dynamic_lighting_max_colour_temp: !input dynamic_lighting_max_colour_temp + dynamic_lighting_min_colour_temp: !input dynamic_lighting_min_colour_temp + dynamic_lighting_sun_elevation_start_rising: !input dynamic_lighting_sun_elevation_start_rising + dynamic_lighting_sun_elevation_end_rising: !input dynamic_lighting_sun_elevation_end_rising + dynamic_lighting_sun_elevation_start_falling: !input dynamic_lighting_sun_elevation_start_falling + dynamic_lighting_sun_elevation_end_falling: !input dynamic_lighting_sun_elevation_end_falling + dynamic_lighting_heartbeat: !input dynamic_lighting_heartbeat + dynamic_lighting_step_value: !input dynamic_lighting_step_value + dynamic_lighting_dead_zone: !input dynamic_lighting_dead_zone + include_bypass: !input include_bypass + bypass_lights_on: !input bypass_lights_on + bypass_lights_off: !input bypass_lights_off + bypass_lights_stop: !input bypass_lights_stop + bypass_time_delay: !input bypass_time_delay + include_bypass_auto_off: !input include_bypass_auto_off + bypass_auto_off_delay: !input bypass_auto_off_delay + include_weekdays: !input include_weekdays + weekday_options: !input weekday_options + include_night_lights: !input include_night_lights + night_lights_conditions: !input night_lights_conditions + night_lights_entity_state: !input night_lights_entity_state + night_lights_after_time: !input night_lights_after_time + night_lights_before_time: !input night_lights_before_time + night_lights_sun_elevation: !input night_lights_sun_elevation + night_lights_sun_elevation_rising: !input night_lights_sun_elevation_rising + night_lights: !input night_lights + night_boolean_scenes_scripts: !input night_boolean_scenes_scripts + include_night_light_control: !input include_night_light_control + night_light_brightness: !input night_light_brightness + night_light_transition_on: !input night_light_transition_on + night_light_transition_off: !input night_light_transition_off + include_night_light_colour_control: !input include_night_light_colour_control + night_light_colour_temperature: !input night_light_colour_temperature + night_light_rgb_colour: !input night_light_rgb_colour + night_light_rgbw_colour: !input night_light_rgbw_colour + night_light_rgbww_colour: !input night_light_rgbww_colour + light_entities_off: '{{ expand(light_switch.entity_id) | selectattr(''domain'', + ''eq'', ''light'') | selectattr(''state'', ''eq'', ''off'') | map(attribute=''entity_id'') + | list }}' + switch_entities_off: '{{ expand(light_switch.entity_id) | selectattr(''domain'', + ''eq'', ''switch'') | selectattr(''state'', ''eq'', ''off'') | map(attribute=''entity_id'') + | list }}' + light_data: "{% set light = namespace(data={}) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if 'use_brightness' in include_light_control %}\n {% set + light.data = dict(light.data, **{ 'brightness_pct': light_brightness }) %}\n{% + endif %} {% if include_light_colour_control == 'use_colour_temperature' %}\n {% + set light.data = dict(light.data, **{ 'color_temp_kelvin': light_colour_temperature + }) %}\n{% endif %} {% if include_light_colour_control == 'use_rgb_colour' %}\n + \ {% set light.data = dict(light.data, **{ 'rgb_color': light_rgb_colour }) %}\n{% + endif %} {% if include_light_colour_control == 'use_rgbw_colour' %}\n {% set + light.data = dict(light.data, **{ 'rgbw_color': light_rgbw_colour }) %}\n{% endif + %} {% if include_light_colour_control == 'use_rgbww_colour' %}\n {% set light.data + = dict(light.data, **{ 'rgbww_color': light_rgbww_colour }) %}\n{% endif %} {{ + light.data }}" + light_entities: '{{ expand(light_switch.entity_id) | selectattr(''domain'', ''eq'', + ''light'') | map(attribute=''entity_id'') | list }}' + switch_entities: '{{ expand(light_switch.entity_id) | selectattr(''domain'', ''eq'', + ''switch'') | map(attribute=''entity_id'') | list }}' + scene_entities: "{% set a = light_switch.entity_id %} {% if boolean_scenes_scripts + == [] %}\n {{ expand(a) | selectattr('domain', 'eq', 'scene') | map(attribute='entity_id') + | list }}\n{% elif is_state(boolean_scenes_scripts, 'off') %}\n {{ expand(a) + | selectattr('domain', 'eq', 'scene') | map(attribute='entity_id') | list }}\n{% + else %}\n []\n{% endif %}" + script_entities: "{% set a = light_switch.entity_id %} {% if boolean_scenes_scripts + == [] %}\n {{ expand(a) | selectattr('domain', 'eq', 'script') | map(attribute='entity_id') + | list }}\n{% elif is_state(boolean_scenes_scripts, 'off') %}\n {{ expand(a) + | selectattr('domain', 'eq', 'script') | map(attribute='entity_id') | list }}\n{% + else %}\n []\n{% endif %}" + boolean_scenes_scripts_helper: "{% if boolean_scenes_scripts | length > 0 and states(boolean_scenes_scripts) + == 'off' %}\n {{ boolean_scenes_scripts }}\n{% else %}\n []\n{% endif %}" + end_scene_entities: '{{ end_scenes | select(''match'', ''^scene\..*'') | list }}' + end_script_entities: '{{ end_scenes | select(''match'', ''^script\..*'') | list + }}' + night_light_entities_off: "{% set b = night_lights and night_lights.entity_id %} + {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'light') | selectattr('state', + 'eq', 'off') | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif + %}" + night_switch_entities_off: "{% set b = night_lights and night_lights.entity_id + %} {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'switch') | selectattr('state', + 'eq', 'off') | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif + %}" + night_light_data: "{% set light = namespace(data={}) %} {% if 'use_transition' in + include_night_light_control %}\n {% set light.data = dict(light.data, **{ 'transition': + night_light_transition_on }) %}\n{% endif %} {% if 'use_brightness' in include_night_light_control + %}\n {% set light.data = dict(light.data, **{ 'brightness_pct': night_light_brightness + }) %}\n{% endif %} {% if include_night_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': night_light_colour_temperature + }) %}\n{% endif %} {% if include_night_light_colour_control == 'use_rgb_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgb_color': night_light_rgb_colour + }) %}\n{% endif %} {% if include_night_light_colour_control == 'use_rgbw_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgbw_color': night_light_rgbw_colour + }) %}\n{% endif %} {% if include_night_light_colour_control == 'use_rgbww_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgbww_color': night_light_rgbww_colour + }) %}\n{% endif %} {{ light.data }}" + night_light_entities: "{% set b = night_lights and night_lights.entity_id %} {% + if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'light') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_switch_entities: "{% set b = night_lights and night_lights.entity_id %} {% + if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'switch') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_scene_entities: "{% set b = night_lights and night_lights.entity_id %} {% + if night_boolean_scenes_scripts == [] %}\n {{ expand(b) | selectattr('domain', + 'eq', 'scene') | map(attribute='entity_id') | list }}\n{% elif is_state(night_boolean_scenes_scripts, + 'off') %}\n {{ expand(b) | selectattr('domain', 'eq', 'scene') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_script_entities: "{% set b = night_lights and night_lights.entity_id %} {% + if night_boolean_scenes_scripts == [] %}\n {{ expand(b) | selectattr('domain', + 'eq', 'script') | map(attribute='entity_id') | list }}\n{% elif is_state(night_boolean_scenes_scripts, + 'off') %}\n {{ expand(b) | selectattr('domain', 'eq', 'script') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + night_boolean_scenes_scripts_helper: "{% if night_boolean_scenes_scripts | length + > 0 and states(night_boolean_scenes_scripts) == 'off' %}\n {{ night_boolean_scenes_scripts + }}\n{% else %}\n []\n{% endif %}" + crossover_lights_light: "{% set a = light_switch.entity_id %} {% set b = night_lights + and night_lights.entity_id %} {% if a and b %}\n {{ expand(a) | reject('in', + expand(b)) | selectattr('domain', 'eq', 'light') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_lights_switch: "{% set a = light_switch.entity_id %} {% set b = night_lights + and night_lights.entity_id %} {% if a and b %}\n {{ expand(a) | reject('in', + expand(b)) | selectattr('domain', 'eq', 'switch') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_night_lights_light_on: "{% set b = night_lights and night_lights.entity_id + %} {% if b %}\n {{ expand(b) | selectattr('domain', 'eq', 'light') | selectattr('state', + 'eq', 'on') | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif + %}" + crossover_night_lights_light: "{% set a = light_switch.entity_id %} {% set b = night_lights + and night_lights.entity_id %} {% if a and b %}\n {{ expand(b) | reject('in', + expand(a)) | selectattr('domain', 'eq', 'light') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_night_lights_switch: "{% set a = light_switch.entity_id %} {% set b = + \ night_lights and night_lights.entity_id %} {% if a and b %}\n {{ expand(b) + | reject('in', expand(a)) | selectattr('domain', 'eq', 'switch') | map(attribute='entity_id') + | list }}\n{% else %}\n []\n{% endif %}" + crossover_lights_light_on: "{% set a = light_switch.entity_id %} {% if a %}\n {{ + expand(a) | selectattr('domain', 'eq', 'light') | selectattr('state', 'eq', 'on') + | map(attribute='entity_id') | list }}\n{% else %}\n []\n{% endif %}" +trigger: +- platform: state + id: t0 + entity_id: !input entity_input + from: 'off' + to: 'on' +- platform: state + id: t1 + entity_id: !input entity_input + from: 'on' + to: 'off' +- platform: numeric_state + id: t2 + entity_id: sun.sun + attribute: elevation + below: !input sun_elevation +- platform: numeric_state + id: t3 + entity_id: sun.sun + attribute: elevation + above: !input sun_elevation_rising +- platform: numeric_state + id: t4 + entity_id: !input ambient_light_sensor + below: !input ambient_light_value +- platform: numeric_state + id: t5 + entity_id: !input ambient_light_sensor + above: !input ambient_light_value_off +- platform: time + id: t6 + at: !input after_time +- platform: time + id: t7 + at: !input before_time +- platform: state + id: t8_on + entity_id: !input bypass_lights_on + from: 'off' + to: 'on' +- platform: state + id: t8_off + entity_id: !input bypass_lights_off + from: 'off' + to: 'on' +- platform: state + id: t8_stop + entity_id: !input bypass_lights_stop + from: 'off' + to: 'on' +- platform: state + id: t9_on + entity_id: !input bypass_lights_on + from: 'on' + to: 'off' +- platform: state + id: t9_off + entity_id: !input bypass_lights_off + from: 'on' + to: 'off' +- platform: state + id: t9_stop + entity_id: !input bypass_lights_stop + from: 'on' + to: 'off' +- platform: state + id: t10 + entity_id: !input night_lights_entity_state + from: 'off' + to: 'on' +- platform: time + id: t11 + at: !input night_lights_after_time +- platform: numeric_state + id: t12 + entity_id: sun.sun + attribute: elevation + below: !input night_lights_sun_elevation +- platform: state + id: t13 + entity_id: !input night_lights_entity_state + from: 'on' + to: 'off' +- platform: time + id: t14 + at: !input night_lights_before_time +- platform: numeric_state + id: t15 + entity_id: sun.sun + attribute: elevation + above: !input night_lights_sun_elevation_rising +- platform: homeassistant + id: t16 + event: start +condition: +- condition: or + conditions: + - condition: and + conditions: + - '{{ ''entity_on'' in include_entity_state }}' + - condition: trigger + id: t0 + - condition: state + entity_id: !input entity_input + match: any + state: 'on' + - condition: and + conditions: + - '{{ ''entity_off'' in include_entity_state }}' + - condition: trigger + id: t1 + - condition: state + entity_id: !input entity_input + match: any + state: 'off' + - condition: and + conditions: + - '{{ ''sun_falling'' in include_sun }}' + - condition: trigger + id: t2 + - condition: and + conditions: + - '{{ ''sun_rising'' in include_sun }}' + - condition: trigger + id: t3 + - condition: and + conditions: + - '{{ ''ambient_low'' in include_ambient }}' + - condition: trigger + id: t4 + - condition: and + conditions: + - '{{ ''ambient_high'' in include_ambient }}' + - condition: trigger + id: t5 + - condition: and + conditions: + - '{{ ''time_on'' in include_time }}' + - condition: trigger + id: t6 + - condition: and + conditions: + - '{{ ''time_off'' in include_time }}' + - condition: trigger + id: t7 + - condition: and + conditions: + - condition: trigger + id: t8_on + - '{{ ''bypass_enabled_turn_on'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t8_off + - '{{ ''bypass_enabled_turn_off'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t8_stop + - '{{ ''bypass_enabled_stop'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t9_on + - '{{ ''bypass_enabled_turn_on'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t9_off + - '{{ ''bypass_enabled_turn_off'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t9_stop + - '{{ ''bypass_enabled_stop'' in include_bypass }}' + - condition: and + conditions: + - condition: trigger + id: t16 + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t10 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''entity_state_enabled'' in night_lights_conditions }}' + - condition: state + entity_id: !input night_lights_entity_state + match: any + state: 'on' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t11 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''time_enabled'' in night_lights_conditions }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t12 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''sun_enabled'' in night_lights_conditions }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t13 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''entity_state_enabled'' in night_lights_conditions }}' + - condition: state + entity_id: !input night_lights_entity_state + state: 'off' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t14 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''time_enabled'' in night_lights_conditions }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t15 + - '{{ include_night_lights == ''night_lights_enabled'' }}' + - '{{ ''sun_enabled'' in night_lights_conditions }}' + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - condition: trigger + id: t16 + - alias: Check if any triggers are ON + condition: or + conditions: + - condition: or + conditions: + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''on'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''on'') }}' + - condition: or + conditions: + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', ''rising'', + false)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation | float(90)) + }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', ''rising'', + true)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation_rising + | float(90)) }}' + - condition: or + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value | int) }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value_off | int) and (expand(light_switch.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: or + conditions: + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' in + include_time) }}' + - condition: time + after: !input after_time + before: !input before_time + - condition: and + conditions: + - condition: trigger + id: t16 + - condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ is_state(night_boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: and + conditions: + - '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_off'' + in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + - condition: trigger + id: t16 + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' +- condition: or + conditions: + - '{{ include_bypass == [] }}' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) }}' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: trigger + id: + - t8_on + - t8_off + - t8_stop + - t9_on + - t9_off + - t9_stop + - t16 +- condition: or + conditions: + - '{{ include_weekdays == ''weekday_disabled'' }}' + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') | + list | count > 0) }}' + - '{{ (expand(night_lights.entity_id) | selectattr(''state'', ''=='', ''on'') | + list | count > 0) }}' + - condition: and + conditions: + - condition: time + weekday: !input weekday_options + - '{{ include_weekdays == ''weekday_enabled'' }}' + - condition: trigger + id: + - t8_on + - t8_off + - t8_stop + - t9_on + - t9_off + - t9_stop +action: +- choose: + - alias: Check if night lights are enabled and within conditions + conditions: + - condition: and + conditions: + - alias: Check if night lights is enabled + condition: or + conditions: + - '{{ ''night_lights_enabled'' in include_night_lights }}' + - alias: Check if night lights entity state is enabled + condition: or + conditions: + - '{{ (''time_enabled'' in night_lights_conditions) and not (''entity_state_enabled'' + in night_lights_conditions) }}' + - '{{ (''sun_enabled'' in night_lights_conditions) and not (''entity_state_enabled'' + in night_lights_conditions) }}' + - condition: and + conditions: + - '{{ ''entity_state_enabled'' in night_lights_conditions }}' + - condition: state + entity_id: !input night_lights_entity_state + state: 'on' + match: any + - alias: Check if night lights time is enabled + condition: or + conditions: + - '{{ (''entity_state_enabled'' in night_lights_conditions) and not (''time_enabled'' + in night_lights_conditions) }}' + - '{{ (''sun_enabled'' in night_lights_conditions) and not (''time_enabled'' + in night_lights_conditions) }}' + - condition: and + conditions: + - '{{ ''time_enabled'' in night_lights_conditions }}' + - condition: time + after: !input night_lights_after_time + before: !input night_lights_before_time + - alias: Check if night lights sun is enabled + condition: or + conditions: + - '{{ (''entity_state_enabled'' in night_lights_conditions) and not (''sun_enabled'' + in night_lights_conditions) }}' + - '{{ (''time_enabled'' in night_lights_conditions) and not (''sun_enabled'' + in night_lights_conditions) }}' + - '{{ (''sun_enabled'' in night_lights_conditions) and (((is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') <= night_lights_sun_elevation + | float(90))) or ((is_state_attr(''sun.sun'', ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') + <= night_lights_sun_elevation_rising | float(90)))) }}' + sequence: + - choose: + - alias: By-pass is turned on & check by-pass option - Turn lights off + conditions: + - condition: trigger + id: t8_off + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' + in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - alias: By-pass is turned on & check by-pass option - Keep the current lights + state + conditions: + - condition: trigger + id: t8_stop + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' + in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - choose: + - alias: By-pass is turned off & check if the trigger is off + conditions: + - condition: trigger + id: + - t9_on + - t9_off + - t9_stop + - alias: Check if all triggers are OFF + condition: and + conditions: + - condition: or + conditions: + - '{{ include_entity_state == [] }}' + - '{{ (''entity_on'' not in include_entity_state ) }}' + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - condition: or + conditions: + - '{{ include_sun == [] }}' + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') >= sun_elevation + | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') >= sun_elevation_rising + | float(90)) }}' + - condition: or + conditions: + - '{{ include_ambient == [] }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value_off | int) }}' + - condition: and + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value | int) }}' + - '{{ (''ambient_high'' not in include_ambient) }}' + - condition: or + conditions: + - '{{ include_time == [] }}' + - '{{ (''time_on'' not in include_time) and (''time_off'' in include_time) + }}' + - '{{ (''time_on'' in include_time) and (''time_off'' not in include_time) + }}' + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input before_time + before: !input after_time + sequence: + - choose: + - alias: Check all by-pass are off + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Trigger is off and check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time + delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + - alias: By-pass is turned off & check if the trigger is on + conditions: + - condition: trigger + id: + - t9_on + - t9_off + - t9_stop + - alias: Check if any triggers are ON + condition: or + conditions: + - condition: or + conditions: + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''on'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''on'') }}' + - condition: or + conditions: + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation + | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation_rising + | float(90)) }}' + - condition: or + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value | int) }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value_off | int) and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: or + conditions: + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input after_time + before: !input before_time + sequence: + - choose: + - alias: Check weekdays + conditions: + - condition: and + conditions: + - '{{ include_weekdays == ''weekday_enabled'' }}' + - condition: not + conditions: + - condition: time + weekday: !input weekday_options + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Check all by-pass are off and check conditions if enabled + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - alias: Check if all triggers are OFF + condition: and + conditions: + - condition: or + conditions: + - '{{ include_entity_state == [] }}' + - '{{ (''entity_on'' not in include_entity_state ) }}' + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - condition: or + conditions: + - '{{ include_sun == [] }}' + - condition: and + conditions: + - '{{ (''sun_falling'' not in include_sun) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation_rising | float(90)) }}' + - condition: and + conditions: + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation_rising | float(90)) }}' + - condition: or + conditions: + - '{{ include_ambient == [] }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value_off | int) }}' + - condition: and + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value | int) }}' + - '{{ (''ambient_high'' not in include_ambient) }}' + - condition: or + conditions: + - '{{ include_time == [] }}' + - '{{ (''time_on'' not in include_time) and (''time_off'' in include_time) + }}' + - '{{ (''time_on'' in include_time) and (''time_off'' not in include_time) + }}' + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input before_time + before: !input after_time + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Motion trigger is on and check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time + delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + - choose: + - alias: Turn off normal lights when trigger by start night lights conditions + conditions: + - condition: trigger + id: + - t10 + - t11 + - t12 + sequence: + - choose: + - alias: light - switch - scene - script is ON + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', + ''on'') | list | count > 0) or (expand(night_lights.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) or (is_state(boolean_scenes_scripts, + ''on'')) }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + data: + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ crossover_lights_light }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ crossover_lights_switch }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - choose: + - alias: Adjust the lights settings when crossing over if lights are + ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the input boolean normal lights + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn ON lights + service: light.turn_on + target: + entity_id: '{{ crossover_night_lights_light_on }}' + data: '{{ night_light_data }}' + - alias: light - switch - scene - script is ON + conditions: + - '{{ ''manage_scripts_crossing_over'' in include_night_light_control + }}' + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', + ''off'') | list | count > 0) or (expand(night_lights.entity_id) | selectattr(''state'', + ''=='', ''off'') | list | count > 0) or (is_state(boolean_scenes_scripts, + ''off'')) }}' + sequence: + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - stop: Stop the automation + - alias: Safe Guard when HA restarts + conditions: + - condition: trigger + id: t16 + sequence: + - choose: + - alias: Check all by-pass are off and check conditions if enabled + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + sequence: + - choose: + - alias: Check if any light are ON and all triggers are OFF + conditions: + - alias: Check if any lights are ON + condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', + ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n + \ {{ is_state(night_boolean_scenes_scripts, 'on') }}\n{% endif + %}" + - alias: Check if all triggers are OFF + condition: and + conditions: + - condition: or + conditions: + - '{{ include_entity_state == [] }}' + - '{{ (''entity_on'' not in include_entity_state ) }}' + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - condition: or + conditions: + - '{{ include_sun == [] }}' + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation_rising | float(90)) }}' + - condition: or + conditions: + - '{{ include_ambient == [] }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value_off | int) }}' + - condition: and + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value | int) }}' + - '{{ (''ambient_high'' not in include_ambient) }}' + - condition: or + conditions: + - '{{ include_time == [] }}' + - '{{ (''time_on'' not in include_time) and (''time_off'' in include_time) + }}' + - '{{ (''time_on'' in include_time) and (''time_off'' not in include_time) + }}' + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input before_time + before: !input after_time + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the normal lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the night lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the normal lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the night lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the normal switches + service: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the night switches + service: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for normal lights scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for night lights scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass auto off is enabled and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time + delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + - choose: + - conditions: + - condition: or + conditions: + - condition: trigger + id: + - t0 + - t2 + - t4 + - t6 + - t8_on + - t10 + - t11 + - t12 + - t16 + - condition: and + conditions: + - condition: trigger + id: + - t9_on + - t9_off + - t9_stop + - condition: or + conditions: + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''on'') }}' + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') <= + sun_elevation | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation_rising + | float(90)) }}' + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value | int) }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value_off | int) and (expand(light_switch.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input after_time + before: !input before_time + sequence: + - alias: Turn ON lights + service: light.turn_on + target: + entity_id: '{{ night_light_entities_off }}' + data: '{{ night_light_data }}' + - choose: + - alias: Check if normal lights scenes or scripts helper is on - This is + for scenes only when crossing over + conditions: + - condition: template + value_template: "{% if boolean_scenes_scripts == [] %}\n false\n{% + elif is_state(boolean_scenes_scripts, 'on') %}\n true\n{% else %}\n + \ false\n{% endif %}" + sequence: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn on the night scenes + service: scene.turn_on + target: + entity_id: '{{ night_scene_entities }}' + data: + transition: '{{ night_light_transition_on }}' + - alias: Turn on the boolean for scenes and scripts + service: input_boolean.turn_on + data: + entity_id: '{{ night_boolean_scenes_scripts_helper }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn on the night scenes + service: scene.turn_on + target: + entity_id: '{{ night_scene_entities }}' + - alias: Turn on the boolean for scenes and scripts + service: input_boolean.turn_on + data: + entity_id: '{{ night_boolean_scenes_scripts_helper }}' + - alias: Turn on the night switches + service: switch.turn_on + target: + entity_id: '{{ night_switch_entities_off }}' + - alias: Turn on the night scripts + service: script.turn_on + target: + entity_id: '{{ night_script_entities }}' + - choose: + - alias: By-pass is enabled & check by-pass option - Turn lights on + conditions: + - condition: trigger + id: t8_on + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time + delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - conditions: + - condition: trigger + id: + - t1 + - t3 + - t5 + - t7 + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + default: + - choose: + - alias: By-pass is turned on & check by-pass option - Turn lights off + conditions: + - condition: trigger + id: t8_off + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' + in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - alias: By-pass is turned on & check by-pass option - Keep the current lights + state + conditions: + - condition: trigger + id: t8_stop + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or (''bypass_auto_off_enabled_stop'' + in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - choose: + - alias: By-pass is turned off & check if the trigger is off + conditions: + - condition: and + conditions: + - condition: trigger + id: + - t9_on + - t9_off + - t9_stop + - alias: Check if all triggers are OFF + condition: and + conditions: + - condition: or + conditions: + - '{{ include_entity_state == [] }}' + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - condition: or + conditions: + - '{{ include_sun == [] }}' + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') >= sun_elevation + | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') >= sun_elevation_rising + | float(90)) }}' + - condition: or + conditions: + - '{{ include_ambient == [] }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value_off | int) }}' + - condition: and + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value | int) }}' + - '{{ (''ambient_high'' not in include_ambient) }}' + - condition: or + conditions: + - '{{ include_time == [] }}' + - '{{ (''time_on'' not in include_time) and (''time_off'' in include_time) + }}' + - '{{ (''time_on'' in include_time) and (''time_off'' not in include_time) + }}' + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input before_time + before: !input after_time + sequence: + - choose: + - alias: Check all by-pass are off + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Trigger is off and check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + - alias: By-pass is turned off & check if the trigger is on + conditions: + - condition: and + conditions: + - condition: trigger + id: + - t9_on + - t9_off + - t9_stop + - alias: Check if any triggers are ON + condition: or + conditions: + - condition: or + conditions: + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''on'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''on'') }}' + - condition: or + conditions: + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation + | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation_rising + | float(90)) }}' + - condition: or + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value | int) }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value_off | int) and (expand(light_switch.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: or + conditions: + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input after_time + before: !input before_time + sequence: + - choose: + - alias: Check weekdays + conditions: + - condition: and + conditions: + - '{{ include_weekdays == ''weekday_enabled'' }}' + - condition: not + conditions: + - condition: time + weekday: !input weekday_options + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - stop: Stop the automation + - alias: Check all by-pass are off and check conditions if enabled + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - alias: Check if all triggers are OFF + condition: and + conditions: + - condition: or + conditions: + - '{{ include_entity_state == [] }}' + - '{{ (''entity_on'' not in include_entity_state ) }}' + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - condition: or + conditions: + - '{{ include_sun == [] }}' + - condition: and + conditions: + - '{{ (''sun_falling'' not in include_sun) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') >= + sun_elevation_rising | float(90)) }}' + - condition: and + conditions: + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') >= + sun_elevation | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') >= + sun_elevation_rising | float(90)) }}' + - condition: or + conditions: + - '{{ include_ambient == [] }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value_off | int) }}' + - condition: and + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value | int) }}' + - '{{ (''ambient_high'' not in include_ambient) }}' + - condition: or + conditions: + - '{{ include_time == [] }}' + - '{{ (''time_on'' not in include_time) and (''time_off'' in include_time) + }}' + - '{{ (''time_on'' in include_time) and (''time_off'' not in include_time) + }}' + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input before_time + before: !input after_time + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - stop: Stop the automation + - alias: Trigger is on and check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + - choose: + - alias: Turn off night lights when trigger by end night lights conditions + conditions: + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - choose: + - alias: If transition is selected + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''on'') + | list | count > 0) or (expand(night_lights.entity_id) | selectattr(''state'', + ''=='', ''on'') | list | count > 0) or (is_state(night_boolean_scenes_scripts, + ''on'')) }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + data: + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ crossover_night_lights_light }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ crossover_night_lights_switch }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - choose: + - alias: Adjust the lights settings when crossing over if lights are ON + is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + sequence: + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_night_light_control + }}' + sequence: + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ night_light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_night_light_control + }}' + sequence: + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the input boolean night lights + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - choose: + - alias: Turn ON lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''disable_dynamic_lighting'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ crossover_lights_light_on }}' + data: '{{ light_data }}' + - alias: If transition is selected + conditions: + - '{{ ''manage_scripts_crossing_over'' in include_night_light_control }}' + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', ''off'') + | list | count > 0) or (expand(night_lights.entity_id) | selectattr(''state'', + ''=='', ''off'') | list | count > 0) or (is_state(night_boolean_scenes_scripts, + ''off'')) }}' + sequence: + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - stop: Stop the automation + - alias: Safe Guard when HA restarts + conditions: + - condition: trigger + id: t16 + sequence: + - choose: + - alias: Check all by-pass are off and check conditions if enabled + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + state: 'off' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + state: 'off' + sequence: + - choose: + - alias: Check if any light are ON and all triggers are OFF + conditions: + - alias: Check if any lights are ON + condition: or + conditions: + - '{{ (expand(light_switch.entity_id) | selectattr(''state'', ''=='', + ''on'') | list | count > 0) }}' + - '{{ (include_night_lights == ''night_lights_enabled'') and (expand(night_lights.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: template + value_template: "{% if boolean_scenes_scripts != [] %}\n {{ is_state(boolean_scenes_scripts, + 'on') }}\n{% endif %}" + - condition: template + value_template: "{% if night_boolean_scenes_scripts != [] %}\n {{ + is_state(night_boolean_scenes_scripts, 'on') }}\n{% endif %}" + - alias: Check if all triggers are OFF + condition: and + conditions: + - condition: or + conditions: + - '{{ include_entity_state == [] }}' + - '{{ (''entity_on'' not in include_entity_state ) }}' + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - '{{ (''entity_off'' in include_entity_state ) and (states[entity_input].state + == ''off'') }}' + - condition: or + conditions: + - '{{ include_sun == [] }}' + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') + >= sun_elevation_rising | float(90)) }}' + - condition: or + conditions: + - '{{ include_ambient == [] }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value_off | int) }}' + - condition: and + conditions: + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int > ambient_light_value | int) }}' + - '{{ (''ambient_high'' not in include_ambient) }}' + - condition: or + conditions: + - '{{ include_time == [] }}' + - '{{ (''time_on'' not in include_time) and (''time_off'' in include_time) + }}' + - '{{ (''time_on'' in include_time) and (''time_off'' not in include_time) + }}' + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input before_time + before: !input after_time + sequence: + - alias: Wait the number of minutes set in the by-pass time delay + delay: + minutes: !input bypass_time_delay + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control + }}' + sequence: + - alias: Turn off the normal lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the night lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn off the normal lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the night lights + service: light.turn_off + target: + entity_id: '{{ night_light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the normal switches + service: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the night switches + service: switch.turn_off + target: + entity_id: '{{ night_switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for normal lights scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts + - alias: Turn off the boolean for night lights scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input night_boolean_scenes_scripts + - stop: Stop the automation + - alias: Check if any by-passes are on + conditions: + - condition: or + conditions: + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: or + conditions: + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_on + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + in include_bypass) and (''bypass_enabled_stop'' not in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_off + match: any + state: 'on' + - condition: and + conditions: + - '{{ (''bypass_enabled_turn_on'' not in include_bypass) and (''bypass_enabled_turn_off'' + not in include_bypass) and (''bypass_enabled_stop'' in include_bypass) + }}' + - condition: state + entity_id: !input bypass_lights_stop + match: any + state: 'on' + sequence: + - alias: Check by-pass auto off is enabled and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) or + (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + - choose: + - conditions: + - condition: or + conditions: + - condition: trigger + id: + - t0 + - t2 + - t4 + - t6 + - t8_on + - t13 + - t14 + - t15 + - t16 + - condition: and + conditions: + - condition: trigger + id: + - t9_on + - t9_off + - t9_stop + - condition: or + conditions: + - '{{ (''entity_on'' in include_entity_state ) and (states[entity_input].state + == ''on'') }}' + - '{{ (''sun_falling'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', false)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation + | float(90)) }}' + - '{{ (''sun_rising'' in include_sun) and (is_state_attr(''sun.sun'', + ''rising'', true)) and (state_attr(''sun.sun'',''elevation'') <= sun_elevation_rising + | float(90)) }}' + - '{{ (''ambient_low'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value | int) }}' + - '{{ (''ambient_high'' in include_ambient) and (states[ambient_light_sensor].state + | int < ambient_light_value_off | int) and (expand(light_switch.entity_id) + | selectattr(''state'', ''=='', ''on'') | list | count > 0) }}' + - condition: and + conditions: + - condition: template + value_template: '{{ (''time_on'' in include_time) and (''time_off'' + in include_time) }}' + - condition: time + after: !input after_time + before: !input before_time + sequence: + - alias: Parallel actions for dynamic lighting and normal lights + parallel: + - sequence: + - alias: Check if the dynamic lighting is enabled + condition: and + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting != ''disable_dynamic_lighting'' + }}' + - choose: + - alias: Dynamic Lighting - Input Boolean Helper + conditions: + - condition: template + value_template: '{{ dynamic_lighting_boolean != [] }}' + sequence: + - alias: Turn on the boolean for dynamic lighting + service: input_boolean.turn_on + data: + entity_id: !input dynamic_lighting_boolean + - choose: + - alias: 1 - Dynamic Lighting - Lux Controlled Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_lux_controled_brightness''}}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_max_lux + - dynamic_lighting_min_lux) %} {% set ak = (( slope * dynamic_lighting_min_lux) + * -1) + dynamic_lighting_max_brightness %} {% set le = light_entities + %} {% set lec = expand(le) | map(attribute='entity_id') | + list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 + * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% elif + lux >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if lp <= 0 %}\n {% set bv = bv %}\n{% + elif (bv > lp) and (dynamic_lighting_max_brightness - lp) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% elif (lp > bv) and (lp - dynamic_lighting_min_brightness) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% elif (bv > lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = lp %}\n{% elif (lp > bv) and (lp - bv) <= + dynamic_lighting_dead_zone %}\n {% set bv = lp %}\n{% elif + bv > (lp + dynamic_lighting_step_value) %}\n {% set bv = + lp + dynamic_lighting_step_value %}\n{% elif bv < (lp - dynamic_lighting_step_value) + %}\n {% set bv = lp - dynamic_lighting_step_value %}\n{% + endif %} {{ bv | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n + \ {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if + include_light_colour_control == 'use_rgbw_colour' %}\n {% + set light.data = dict(light.data, **{ 'rgbw_color': light_rgbw_colour + }) %}\n{% endif %} {% if include_light_colour_control == 'use_rgbww_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgbww_color': + light_rgbww_colour }) %}\n{% endif %} {{ light.data }}\n" + in_dead_zone: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_max_lux + - dynamic_lighting_min_lux) %} {% set ak = (( slope * dynamic_lighting_min_lux) + * -1) + dynamic_lighting_max_brightness %} {% set le = light_entities + %} {% set lec = expand(le) | map(attribute='entity_id') | + list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 + * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% elif + lux >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if (bv > lp) and (dynamic_lighting_max_brightness + - lp) <= dynamic_lighting_dead_zone %}\n false\n{% elif (lp + > bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n false\n{% elif (bv >= lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n true\n{% elif (lp >= bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n true\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If dynamic lighting brightness is in the dead zone + conditions: '{{ in_dead_zone }}' + sequence: + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set the dynamic lighting brightness for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_lux_controled_brightness'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - choose: + - alias: 2 - Dynamic Lighting - Lux Controlled Brightness - Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_lux_controled_brightness_inv''}}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_min_lux + - dynamic_lighting_max_lux) %} {% set ak = (( slope * dynamic_lighting_min_lux) + * -1) + dynamic_lighting_min_brightness %} {% set le = light_entities + %} {% set lec = expand(le) | map(attribute='entity_id') | + list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 + * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_min_brightness %}\n{% elif + lux >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if lp <= 0 %}\n {% set bv = bv %}\n{% + elif (bv > lp) and (dynamic_lighting_max_brightness - lp) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% elif (lp > bv) and (lp - dynamic_lighting_min_brightness) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% elif (bv > lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = lp %}\n{% elif (lp > bv) and (lp - bv) + <= dynamic_lighting_dead_zone %}\n {% set bv = lp %}\n{% + elif bv > (lp + dynamic_lighting_step_value) %}\n {% set + bv = lp + dynamic_lighting_step_value %}\n{% elif bv < (lp + - dynamic_lighting_step_value) %}\n {% set bv = lp - dynamic_lighting_step_value + %}\n{% endif %} {{ bv | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n + \ {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if + include_light_colour_control == 'use_rgbw_colour' %}\n {% + set light.data = dict(light.data, **{ 'rgbw_color': light_rgbw_colour + }) %}\n{% endif %} {% if include_light_colour_control == 'use_rgbww_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgbww_color': + light_rgbww_colour }) %}\n{% endif %} {{ light.data }}\n" + in_dead_zone: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_min_lux + - dynamic_lighting_max_lux) %} {% set ak = (( slope * dynamic_lighting_min_lux) + * -1) + dynamic_lighting_min_brightness %} {% set le = light_entities + %} {% set lec = expand(le) | map(attribute='entity_id') | + list | length %} {% set lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / 255 + * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_min_brightness %}\n{% elif + lux >= dynamic_lighting_max_lux %}\n {% set bv = dynamic_lighting_max_brightness + %}\n{% else %}\n {% set bv = ((slope * lux) + ak) | round(1) + %}\n{% endif %} {% if (bv > lp) and (dynamic_lighting_max_brightness + - lp) <= dynamic_lighting_dead_zone %}\n true\n{% elif (lp + > bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n false\n{% elif (bv >= lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n true\n{% elif (lp >= bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n true\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If dynamic lighting brightness is in the dead zone + conditions: '{{ in_dead_zone }}' + sequence: + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting brightness inverted for the normal + lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_lux_controled_brightness_inv'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - choose: + - alias: 3 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% endif %} {{ colour_temp_value + | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'color_temp_kelvin': + dynamic_kelvin }) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': + light_transition_on }) %}\n{% endif %} {% if 'use_brightness' + in include_light_control %}\n {% set light.data = dict(light.data, + **{ 'brightness_pct': light_brightness }) %}\n{% endif %} + {{ light.data }}\n" + should_wait: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if elevation >= (dynamic_lighting_sun_elevation_start_rising + - 2) and elevation <= (dynamic_lighting_sun_elevation_end_rising + + 2) and is_state_attr('sun.sun', 'rising', true) %}\n false\n + \ {% elif elevation <= (dynamic_lighting_sun_elevation_start_falling + + 2) and elevation >= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n false\n + \ {% elif elevation >= (dynamic_lighting_sun_elevation_end_rising + + 2) and elevation >= (dynamic_lighting_sun_elevation_start_falling + - 2) %}\n true\n {% elif elevation <= (dynamic_lighting_sun_elevation_start_rising + - 2) and is_state_attr('sun.sun', 'rising', true) %}\n true\n + \ {% elif elevation <= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n true\n + \ {% else %}\n true\n {% endif %}\n{% else %}\n false\n{% + endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If sun elevation is outside the adjustment zone and the + lights are ON + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation colour temperature for the + normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 4 - Dynamic Lighting - Sun Elevation Lighting - Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_brightness'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_brightness %} {% set end_slope + = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_brightness %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% + elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% + else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% endif %}\n{% else %}\n {% set brightness_value = + dynamic_lighting_min_brightness %}\n{% endif %} {{ brightness_value + | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n + \ {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if + include_light_colour_control == 'use_rgbw_colour' %}\n {% + set light.data = dict(light.data, **{ 'rgbw_color': light_rgbw_colour + }) %}\n{% endif %} {% if include_light_colour_control == 'use_rgbww_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgbww_color': + light_rgbww_colour }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set lp + = (expand(le) | map(attribute='attributes.brightness') | reject('equalto', + None) | sum | float(default=255) / 255 * 100 / lec) | round(0) + %} {% set elevation = state_attr('sun.sun', 'elevation') | + float %} {% set start_slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_brightness %} {% set end_slope + = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_brightness %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% + elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% + else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% endif %}\n{% else %}\n {% set brightness_value = + dynamic_lighting_min_brightness %}\n{% endif %} {% if expand(light_entities) + | selectattr('state', '==', 'on') | list | count > 0 %}\n + \ {% if (brightness_value >= (lp * 0.99) and brightness_value + <= (lp * 1.01)) %}\n true\n {% else %}\n false\n {% + endif %}\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If lights are ON and the lights are within 1% of the + actual brightness + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation lighting brightness for the + normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_brightness'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 5 - Dynamic Lighting - Sun Elevation Lighting - Brightness Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_brightness_inv'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_max_brightness + - dynamic_lighting_min_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_min_brightness %} {% set end_slope + = (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_max_brightness %} {% if elevation + > -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% + elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% + else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% endif %}\n{% else %}\n {% set brightness_value = + dynamic_lighting_max_brightness %}\n{% endif %} {{ brightness_value + | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'brightness_pct': dynamic_brightness_pct + }) %} {% if 'use_transition' in include_light_control %}\n + \ {% set light.data = dict(light.data, **{ 'transition': light_transition_on + }) %}\n{% endif %} {% if include_light_colour_control == 'use_colour_temperature' + %}\n {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + light_colour_temperature }) %}\n{% endif %} {% if include_light_colour_control + == 'use_rgb_colour' %}\n {% set light.data = dict(light.data, + **{ 'rgb_color': light_rgb_colour }) %}\n{% endif %} {% if + include_light_colour_control == 'use_rgbw_colour' %}\n {% + set light.data = dict(light.data, **{ 'rgbw_color': light_rgbw_colour + }) %}\n{% endif %} {% if include_light_colour_control == 'use_rgbww_colour' + %}\n {% set light.data = dict(light.data, **{ 'rgbww_color': + light_rgbww_colour }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set lp + = (expand(le) | map(attribute='attributes.brightness') | reject('equalto', + None) | sum | float(default=255) / 255 * 100 / lec) | round(0) + %} {% set elevation = state_attr('sun.sun', 'elevation') | + float %} {% set start_slope = (dynamic_lighting_max_brightness + - dynamic_lighting_min_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_min_brightness %} {% set end_slope + = (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_max_brightness %} {% if elevation + > -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% + elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% + else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% endif %}\n{% else %}\n {% set brightness_value = + dynamic_lighting_max_brightness %}\n{% endif %} {% if expand(light_entities) + | selectattr('state', '==', 'on') | list | count > 0 %}\n + \ {% if (brightness_value >= (lp * 0.99) and brightness_value + <= (lp * 1.01)) %}\n true\n {% else %}\n false\n {% + endif %}\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If lights are ON and the lights are within 1% of the + actual brightness + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation lighting brightness inverted + for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_brightness_inv'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 6 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + + Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_brightness'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% endif %} {{ colour_temp_value + | round(0) }}\n" + dynamic_brightness_pct: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_brightness %} {% set end_slope + = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_brightness %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% + elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% + else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% endif %}\n{% else %}\n {% set brightness_value = + dynamic_lighting_min_brightness %}\n{% endif %} {{ brightness_value + | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'color_temp_kelvin': + dynamic_kelvin }) %} {% set light.data = dict(light.data, + **{ 'brightness_pct': dynamic_brightness_pct }) %} {% if 'use_transition' + in include_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': light_transition_on }) %}\n{% endif %} {{ + light.data }}\n" + should_wait: "{% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set lp + = (expand(le) | map(attribute='attributes.brightness') | reject('equalto', + None) | sum | float(default=255) / 255 * 100 / lec) | round(0) + %} {% set elevation = state_attr('sun.sun', 'elevation') | + float %} {% set start_slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_brightness %} {% set end_slope + = (dynamic_lighting_min_brightness - dynamic_lighting_max_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_brightness %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% + elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = dynamic_lighting_min_brightness %}\n {% + else %}\n {% set brightness_value = dynamic_lighting_max_brightness + %}\n {% endif %}\n{% else %}\n {% set brightness_value = + dynamic_lighting_min_brightness %}\n{% endif %} {% if expand(light_entities) + | selectattr('state', '==', 'on') | list | count > 0 %}\n + \ {% if (brightness_value >= (lp * 0.99) and brightness_value + <= (lp * 1.01)) %}\n true\n {% else %}\n false\n {% + endif %}\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If lights are ON and the lights are within 1% of the + actual brightness + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation lighting brightness and dynamic + sun elevation colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_brightness'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 7 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + + Brightness Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_brightness_inv'' + }}' + sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > 0)) + or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + colour_temp_value = dynamic_lighting_min_colour_temp %}\n + \ {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% endif %} {{ colour_temp_value + | round(0) }}\n" + dynamic_brightness_pct: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_max_brightness + - dynamic_lighting_min_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_min_brightness %} {% set end_slope + = (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_max_brightness %} {% if elevation + > -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% + elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% + else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% endif %}\n{% else %}\n {% set brightness_value = + dynamic_lighting_max_brightness %}\n{% endif %} {{ brightness_value + | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} {% + set light.data = dict(light.data, **{ 'color_temp_kelvin': + dynamic_kelvin }) %} {% set light.data = dict(light.data, + **{ 'brightness_pct': dynamic_brightness_pct }) %} {% if 'use_transition' + in include_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': light_transition_on }) %}\n{% endif %} {{ + light.data }}\n" + should_wait: "{% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set lp + = (expand(le) | map(attribute='attributes.brightness') | reject('equalto', + None) | sum | float(default=255) / 255 * 100 / lec) | round(0) + %} {% set elevation = state_attr('sun.sun', 'elevation') | + float %} {% set start_slope = (dynamic_lighting_max_brightness + - dynamic_lighting_min_brightness) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set start_ak + = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_min_brightness %} {% set end_slope + = (dynamic_lighting_max_brightness - dynamic_lighting_min_brightness) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_max_brightness %} {% if elevation + > -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = ((end_slope * elevation) + end_ak) | round(1) + %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% + elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% set + brightness_value = dynamic_lighting_max_brightness %}\n {% + else %}\n {% set brightness_value = dynamic_lighting_min_brightness + %}\n {% endif %}\n{% else %}\n {% set brightness_value = + dynamic_lighting_max_brightness %}\n{% endif %} {% if expand(light_entities) + | selectattr('state', '==', 'on') | list | count > 0 %}\n + \ {% if (brightness_value >= (lp * 0.99) and brightness_value + <= (lp * 1.01)) %}\n true\n {% else %}\n false\n {% + endif %}\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if lights + are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If lights are ON and the lights are within 1% of the + actual brightness + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: Set dynamic sun elevation lighting brightness and dynamic + sun elevation colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_brightness_inv'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 8 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + + Lux Controlled Brightness + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness'' + }}' + sequence: + - alias: Parallel actions for dynamic lighting + parallel: + - sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > + 0)) or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_max_lux + - dynamic_lighting_min_lux) %} {% set ak = (( slope * + dynamic_lighting_min_lux) * -1) + dynamic_lighting_max_brightness + %} {% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set + lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / + 255 * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% + elif lux >= dynamic_lighting_max_lux %}\n {% set bv = + dynamic_lighting_min_brightness %}\n{% else %}\n {% set + bv = ((slope * lux) + ak) | round(1) %}\n{% endif %} {% + if lp <= 0 %}\n {% set bv = bv %}\n{% elif (bv > lp) + and (dynamic_lighting_max_brightness - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% + elif (lp > bv) and (lp - dynamic_lighting_min_brightness) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% elif (bv > lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = lp %}\n{% elif (lp > bv) and (lp - bv) + <= dynamic_lighting_dead_zone %}\n {% set bv = lp %}\n{% + elif bv > (lp + dynamic_lighting_step_value) %}\n {% + set bv = lp + dynamic_lighting_step_value %}\n{% elif + bv < (lp - dynamic_lighting_step_value) %}\n {% set bv + = lp - dynamic_lighting_step_value %}\n{% endif %} {{ + bv | round(0) }}\n" + dynamic_kelvin: "{% if expand(light_entities) | selectattr('state', + '==', 'off') | list | count > 0 %}\n {% set elevation + = state_attr('sun.sun', 'elevation') | float %}\n {% + set start_slope = (dynamic_lighting_min_colour_temp - + dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %}\n {% + set start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %}\n {% set + end_slope = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %}\n {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %}\n {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = ((end_slope * elevation) + end_ak) + | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n {% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n {% endif %}\n + \ {{ colour_temp_value | round(0) }}\n{% endif %}\n" + dynamic_light_off_data: "{% set light = namespace(data={}) + %} {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% set light.data = dict(light.data, + **{ 'color_temp_kelvin': dynamic_kelvin }) %} {% if 'use_transition' + in include_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': light_transition_on }) %}\n{% endif + %} {{ light.data }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} + {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% if 'use_transition' in + include_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': light_transition_on }) %}\n{% endif + %} {{ light.data }}\n" + in_dead_zone: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_max_lux + - dynamic_lighting_min_lux) %} {% set ak = (( slope * + dynamic_lighting_min_lux) * -1) + dynamic_lighting_max_brightness + %} {% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set + lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / + 255 * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% + elif lux >= dynamic_lighting_max_lux %}\n {% set bv = + dynamic_lighting_min_brightness %}\n{% else %}\n {% set + bv = ((slope * lux) + ak) | round(1) %}\n{% endif %} {% + if (bv > lp) and (dynamic_lighting_max_brightness - lp) + <= dynamic_lighting_dead_zone %}\n false\n{% elif (lp + > bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n false\n{% elif (bv >= lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n true\n{% elif (lp >= bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n true\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Check if the light is off + conditions: '{{ expand(light_entities) | selectattr(''state'', + ''=='', ''off'') | list | count > 0 }}' + sequence: + - alias: Delay for parallel actions needed for when the + light is off + delay: + milliseconds: 100 + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if + lights are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - alias: Delay for parallel actions needed for when the + light is on + delay: + milliseconds: 100 + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If dynamic lighting brightness is in the dead zone + conditions: '{{ in_dead_zone }}' + sequence: + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting lux brightness and dynamic sun + elevation colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness'' + }}' + - '{{ expand(light_entities) | selectattr(''state'', ''=='', + ''off'') | list | count > 0 }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_off_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting lux brightness for the normal + lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > + 0)) or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set + start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = ((end_slope * elevation) + end_ak) + | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% endif %} {{ + colour_temp_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} + {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + dynamic_kelvin }) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': + light_transition_on }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if elevation >= + (dynamic_lighting_sun_elevation_start_rising - 2) and + elevation <= (dynamic_lighting_sun_elevation_end_rising + + 2) and is_state_attr('sun.sun', 'rising', true) %}\n + \ false\n {% elif elevation <= (dynamic_lighting_sun_elevation_start_falling + + 2) and elevation >= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n + \ false\n {% elif elevation >= (dynamic_lighting_sun_elevation_end_rising + + 2) and elevation >= (dynamic_lighting_sun_elevation_start_falling + - 2) %}\n true\n {% elif elevation <= (dynamic_lighting_sun_elevation_start_rising + - 2) and is_state_attr('sun.sun', 'rising', true) %}\n + \ true\n {% elif elevation <= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n + \ true\n {% else %}\n true\n {% endif %}\n{% else + %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if + lights are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If sun elevation is outside the adjustment zone and + the lights are ON + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If the lights are OFF + conditions: '{{ expand(light_entities) | selectattr(''state'', + ''=='', ''off'') | list | count > 0 }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: Set sun elevation colour temperature for the normal + lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - choose: + - alias: 9 - Dynamic Lighting - Sun Elevation Lighting - Colour Temp + + Lux Controlled Brightness Inverted + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness_inv'' + }}' + sequence: + - alias: Parallel actions for dynamic lighting + parallel: + - sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > + 0)) or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_brightness_pct: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_min_lux + - dynamic_lighting_max_lux) %} {% set ak = (( slope * + dynamic_lighting_min_lux) * -1) + dynamic_lighting_min_brightness + %} {% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set + lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / + 255 * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_min_brightness %}\n{% + elif lux >= dynamic_lighting_max_lux %}\n {% set bv = + dynamic_lighting_max_brightness %}\n{% else %}\n {% set + bv = ((slope * lux) + ak) | round(1) %}\n{% endif %} {% + if lp <= 0 %}\n {% set bv = bv %}\n{% elif (bv > lp) + and (dynamic_lighting_max_brightness - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = dynamic_lighting_max_brightness %}\n{% + elif (lp > bv) and (lp - dynamic_lighting_min_brightness) + <= dynamic_lighting_dead_zone %}\n {% set bv = dynamic_lighting_min_brightness + %}\n{% elif (bv > lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n {% set bv = lp %}\n{% elif (lp > bv) and (lp - + bv) <= dynamic_lighting_dead_zone %}\n {% set bv = lp + %}\n{% elif bv > (lp + dynamic_lighting_step_value) %}\n + \ {% set bv = lp + dynamic_lighting_step_value %}\n{% + elif bv < (lp - dynamic_lighting_step_value) %}\n {% + set bv = lp - dynamic_lighting_step_value %}\n{% endif + %} {{ bv | round(0) }}\n" + dynamic_kelvin: "{% if expand(light_entities) | selectattr('state', + '==', 'off') | list | count > 0 %}\n {% set elevation + = state_attr('sun.sun', 'elevation') | float %}\n {% + set start_slope = (dynamic_lighting_min_colour_temp - + dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %}\n {% + set start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %}\n {% set + end_slope = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %}\n {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %}\n {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = ((end_slope * elevation) + end_ak) + | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n {% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n {% endif %}\n + \ {{ colour_temp_value | round(0) }}\n{% endif %}\n" + dynamic_light_off_data: "{% set light = namespace(data={}) + %} {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% set light.data = dict(light.data, + **{ 'color_temp_kelvin': dynamic_kelvin }) %} {% if 'use_transition' + in include_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': light_transition_on }) %}\n{% endif + %} {{ light.data }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} + {% set light.data = dict(light.data, **{ 'brightness_pct': + dynamic_brightness_pct }) %} {% if 'use_transition' in + include_light_control %}\n {% set light.data = dict(light.data, + **{ 'transition': light_transition_on }) %}\n{% endif + %} {{ light.data }}\n" + in_dead_zone: "{% set lux = states(dynamic_lighting_lux_sensor) + | float %} {% set slope = (dynamic_lighting_min_brightness + - dynamic_lighting_max_brightness) / (dynamic_lighting_min_lux + - dynamic_lighting_max_lux) %} {% set ak = (( slope * + dynamic_lighting_min_lux) * -1) + dynamic_lighting_min_brightness + %} {% set le = light_entities %} {% set lec = expand(le) + | map(attribute='entity_id') | list | length %} {% set + lp = (expand(le) | map(attribute='attributes.brightness') + | reject('equalto', None) | sum | float(default=255) / + 255 * 100 / lec) | round(0) %} {% if lux <= dynamic_lighting_min_lux + %}\n {% set bv = dynamic_lighting_min_brightness %}\n{% + elif lux >= dynamic_lighting_max_lux %}\n {% set bv = + dynamic_lighting_max_brightness %}\n{% else %}\n {% set + bv = ((slope * lux) + ak) | round(1) %}\n{% endif %} {% + if (bv > lp) and (dynamic_lighting_max_brightness - lp) + <= dynamic_lighting_dead_zone %}\n true\n{% elif (lp + > bv) and (lp - dynamic_lighting_min_brightness) <= dynamic_lighting_dead_zone + %}\n false\n{% elif (bv >= lp) and (bv - lp) <= dynamic_lighting_dead_zone + %}\n true\n{% elif (lp >= bv) and (lp - bv) <= dynamic_lighting_dead_zone + %}\n true\n{% else %}\n false\n{% endif %}\n" + - choose: + - alias: Check if the light is off + conditions: '{{ expand(light_entities) | selectattr(''state'', + ''=='', ''off'') | list | count > 0 }}' + sequence: + - alias: Delay for parallel actions needed for when the + light is off + delay: + milliseconds: 100 + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if + lights are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - alias: Delay for parallel actions needed for when the + light is on + delay: + milliseconds: 100 + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: If dynamic lighting brightness is in the dead zone + conditions: '{{ in_dead_zone }}' + sequence: + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting lux brightness and dynamic sun + elevation colour temperature for the normal lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness_inv'' + }}' + - '{{ expand(light_entities) | selectattr(''state'', ''=='', + ''off'') | list | count > 0 }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_off_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - alias: Set dynamic lighting lux brightness for the normal + lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness_inv'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Dynamic lighting heartbeat + delay: + minutes: !input dynamic_lighting_heartbeat + - sequence: + - alias: Dynamic Lighting Control + repeat: + until: '{{ ((dynamic_lighting_boolean == []) and (expand(light_entities) + | selectattr(''state'', ''=='', ''off'') | list | count > + 0)) or ((dynamic_lighting_boolean != []) and (is_state(dynamic_lighting_boolean, + ''off''))) }}' + sequence: + - variables: + dynamic_kelvin: "{% set elevation = state_attr('sun.sun', + 'elevation') | float %} {% set start_slope = (dynamic_lighting_min_colour_temp + - dynamic_lighting_max_colour_temp) / (dynamic_lighting_sun_elevation_start_rising + - dynamic_lighting_sun_elevation_end_rising) %} {% set + start_ak = (( start_slope * dynamic_lighting_sun_elevation_end_rising) + * -1) + dynamic_lighting_max_colour_temp %} {% set end_slope + = (dynamic_lighting_min_colour_temp - dynamic_lighting_max_colour_temp) + / (dynamic_lighting_sun_elevation_end_falling - dynamic_lighting_sun_elevation_start_falling) + %} {% set end_ak = (( end_slope * dynamic_lighting_sun_elevation_end_falling) + * -1) + dynamic_lighting_min_colour_temp %} {% if elevation + >= -10 %}\n {% if elevation >= dynamic_lighting_sun_elevation_start_rising + and elevation <= dynamic_lighting_sun_elevation_end_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = ((start_slope * elevation) + start_ak) + | round(1) %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_falling + and elevation >= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = ((end_slope * elevation) + end_ak) + | round(1) %}\n {% elif elevation >= dynamic_lighting_sun_elevation_end_rising + and elevation >= dynamic_lighting_sun_elevation_start_falling + \ %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_start_rising + and is_state_attr('sun.sun', 'rising', true) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% elif elevation <= dynamic_lighting_sun_elevation_end_falling + and is_state_attr('sun.sun', 'rising', false) %}\n {% + set colour_temp_value = dynamic_lighting_min_colour_temp + %}\n {% else %}\n {% set colour_temp_value = dynamic_lighting_max_colour_temp + %}\n {% endif %}\n{% else %}\n {% set colour_temp_value + = dynamic_lighting_min_colour_temp %}\n{% endif %} {{ + colour_temp_value | round(0) }}\n" + dynamic_light_data: "{% set light = namespace(data={}) %} + {% set light.data = dict(light.data, **{ 'color_temp_kelvin': + dynamic_kelvin }) %} {% if 'use_transition' in include_light_control + %}\n {% set light.data = dict(light.data, **{ 'transition': + light_transition_on }) %}\n{% endif %} {{ light.data }}\n" + should_wait: "{% set elevation = state_attr('sun.sun', 'elevation') + | float %} {% if expand(light_entities) | selectattr('state', + '==', 'on') | list | count > 0 %}\n {% if elevation >= + (dynamic_lighting_sun_elevation_start_rising - 2) and + elevation <= (dynamic_lighting_sun_elevation_end_rising + + 2) and is_state_attr('sun.sun', 'rising', true) %}\n + \ false\n {% elif elevation <= (dynamic_lighting_sun_elevation_start_falling + + 2) and elevation >= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n + \ false\n {% elif elevation >= (dynamic_lighting_sun_elevation_end_rising + + 2) and elevation >= (dynamic_lighting_sun_elevation_start_falling + - 2) %}\n true\n {% elif elevation <= (dynamic_lighting_sun_elevation_start_rising + - 2) and is_state_attr('sun.sun', 'rising', true) %}\n + \ true\n {% elif elevation <= (dynamic_lighting_sun_elevation_end_falling + - 2) and is_state_attr('sun.sun', 'rising', false) %}\n + \ true\n {% else %}\n true\n {% endif %}\n{% else + %}\n false\n{% endif %}\n" + - choose: + - alias: Stop when crossing over + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + not in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - stop: Stop the automation + - alias: Adjust the lights settings when crossing over if + lights are ON is selected in night light control + conditions: + - condition: template + value_template: '{{ ''if_lights_are_on_adjust_when_crossing_over'' + in include_night_light_control }}' + - condition: trigger + id: + - t13 + - t14 + - t15 + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If sun elevation is outside the adjustment zone and + the lights are ON + conditions: '{{ should_wait }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: If the lights are OFF + conditions: '{{ expand(light_entities) | selectattr(''state'', + ''=='', ''off'') | list | count > 0 }}' + sequence: + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - alias: Set sun elevation colour temperature for the normal + lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''enable_sun_elevation_colour_lux_brightness_inv'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities }}' + data: '{{ dynamic_light_data }}' + - alias: Wait for sun elevation to change + wait_for_trigger: + - platform: state + entity_id: sun.sun + attribute: elevation + - stop: Stop the automation + - sequence: + - choose: + - alias: Turn ON lights + conditions: + - condition: template + value_template: '{{ include_dynamic_lighting == ''disable_dynamic_lighting'' + }}' + sequence: + - service: light.turn_on + target: + entity_id: '{{ light_entities_off }}' + data: '{{ light_data }}' + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn on the scenes + service: scene.turn_on + target: + entity_id: '{{ scene_entities }}' + data: + transition: '{{ light_transition_on }}' + - alias: Turn on the boolean for scenes and scripts + service: input_boolean.turn_on + data: + entity_id: '{{ boolean_scenes_scripts_helper }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control + }}' + sequence: + - alias: Turn on the scenes + service: scene.turn_on + target: + entity_id: '{{ scene_entities }}' + - alias: Turn on the boolean for scenes and scripts + service: input_boolean.turn_on + data: + entity_id: '{{ boolean_scenes_scripts_helper }}' + - alias: Turn on the switches + service: switch.turn_on + target: + entity_id: '{{ switch_entities_off }}' + - alias: Turn on the scripts + service: script.turn_on + target: + entity_id: '{{ script_entities }}' + - choose: + - alias: By-pass is enabled & check by-pass option - Turn lights on + conditions: + - condition: trigger + id: t8_on + sequence: + - alias: Check by-pass settings and preform the correct action + if: + - alias: Check if the by-pass auto off is enabled + condition: template + value_template: '{{ (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + or (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + then: + - alias: Wait the number of minutes set in the by-pass auto off time + delay + delay: + minutes: !input bypass_auto_off_delay + - alias: Parallel Actions for the by-pass auto off + parallel: + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_on'' in include_bypass) + and (''bypass_auto_off_enabled_on'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_on + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_turn_off'' in include_bypass) + and (''bypass_auto_off_enabled_off'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_off + - sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ (''bypass_enabled_stop'' in include_bypass) + and (''bypass_auto_off_enabled_stop'' in include_bypass_auto_off) + }}' + sequence: + - alias: Turn off the by-pass + service: homeassistant.turn_off + entity_id: !input bypass_lights_stop + - stop: Stop the automation + else: + - stop: Stop the automation + - conditions: + - condition: trigger + id: + - t1 + - t3 + - t5 + - t7 + sequence: + - choose: + - alias: Dynamic Lighting - Input Boolean Helper + conditions: + - condition: template + value_template: '{{ dynamic_lighting_boolean != [] }}' + sequence: + - alias: Turn off the boolean for dynamic lighting + service: input_boolean.turn_off + data: + entity_id: !input dynamic_lighting_boolean + - choose: + - alias: If transition is selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' in include_light_control }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + data: + transition: '{{ light_transition_off }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + transition: '{{ light_transition_off }}' + - alias: If transition is not selected + conditions: + - condition: template + value_template: '{{ ''use_transition'' not in include_light_control }}' + sequence: + - alias: Turn off the lights + service: light.turn_off + target: + entity_id: '{{ light_entities }}' + - alias: Turn off the scenes + service: scene.turn_on + data: + entity_id: '{{ end_scene_entities }}' + - alias: Turn off the switches + service: switch.turn_off + target: + entity_id: '{{ switch_entities }}' + - alias: Turn off the script + service: script.turn_on + data: + entity_id: '{{ end_script_entities }}' + - alias: Turn off the boolean for scenes and scripts + service: input_boolean.turn_off + data: + entity_id: !input boolean_scenes_scripts diff --git a/blueprints/automation/Blackshome/state-notifications-and-actions.yaml b/blueprints/automation/Blackshome/state-notifications-and-actions.yaml new file mode 100644 index 0000000..cc9bd5d --- /dev/null +++ b/blueprints/automation/Blackshome/state-notifications-and-actions.yaml @@ -0,0 +1,1684 @@ +blueprint: + name: State Notifications & Actions + description: "# \U0001F4CD State Notifications & Actions\n**Version: 1.5**\n\nState + Your Trigger: Turn State Changes Into Notifications and Actions! \U0001F680\n\n**If + you like my blueprints, and would like to show your support or just say thank + you?** [Click Here](https://www.paypal.com/donate/?hosted_button_id=WAZS3QSDTPGA8) + \U0001F642\n
The Automation Process: \U0001F448\n\n + \ - **State Triggers:**\n - Define triggers based on entity state changes or + numeric state changes (above or below a specified value).\n - Specify entities + for both \"Start Entities\" and \"End Entities.\" Both start and end entities + serve as triggers.\n\n - **Notification Options:**\n - Option to send notifications + to one or multiple devices when triggered.\n - Action Buttons (Optional):\n + \ - Notifications can include up to 3 action buttons.\n - Each action + button is linked to a designated custom action group.\n - Option to send + a confirmation message when an action button is pressed.\n - The automation + will continue after a specified time delay if an action button is not pressed.\n + \ - Compatible with Apple iOS and Android devices.\n\n - **Custom Actions:**\n + \ - Option to define up to 3 different action groups when triggered.\n - + Each action group can contain as many actions as desired.\n - Each action group + can be linked to a notification action button or executed automatically when the + automation is triggered.\n\n - **Custom Conditions:**\n - Enter any custom + conditions to further customize the automation process\n
\n\nNeed help? + \n- FAQ: [Click Here](https://community.home-assistant.io/t/state-notifications-actions/612617/2?u=blacky)\n- + The Settings & Best Practice Guidelines: [Click Here](https://community.home-assistant.io/t/state-notifications-actions/612617/3?u=blacky)\n- + Community Support Including Updates: [Click Here](https://community.home-assistant.io/t/state-notifications-actions/612617?u=blacky)\n" + domain: automation + input: + start_trigger_settings: + name: Start Trigger + icon: mdi:cog-outline + collapsed: true + input: + start_trigger_state: + name: Start Trigger - State + description: 'Please select what state you would like for your trigger. + You can choose from ten options available in the dropdown menu. If your + option is not in the dropdown menu then just type the state you would + like to use in and click the save button. + + + 1 - Button Or Any State + + 2 - ON + + 3 - OFF + + 4 - Unavailable + + 5 - Unknown + + 6 - Home + + 7 - Not Home + + 8 - Numeric State - Above + + 9 - Numeric State - Below + + 10 - Numeric State - Above & Below + + + A numbering system has been implemented to facilitate navigation within + the dropdown selections. Each number corresponds to a specific configuration, + aiding users in identifying and adjusting the settings used within each + selection. For instance, when selecting "8 - Numeric State - Above" as + a dropdown option, settings marked "Used in options 8, 9 or 10" are required + for that respective selection because number 8 is included in those options. + + ' + default: button_any_state + selector: + select: + custom_value: true + mode: dropdown + options: + - label: 1 - Button Or Any State + value: button_any_state + - label: 2 - ON + value: 'on' + - label: 3 - OFF + value: 'off' + - label: 4 - Unavailable + value: unavailable + - label: 5 - Unknown + value: unknown + - label: 6 - Home + value: home + - label: 7 - Not Home + value: not_home + - label: 8 - Numeric State - Above + value: numeric_state_above + - label: 9 - Numeric State - Below + value: numeric_state_below + - label: 10 - Numeric State - Above & Below + value: numeric_state_above_below + multiple: false + sort: false + start_trigger_state_entity: + name: State Entities + description: '**Used in options 1, 2, 3, 4, 5, 6 or 7**. Enter the entities + that will trigger the automation on state change. + + ' + default: [] + selector: + entity: + multiple: true + start_trigger_numeric_entity: + name: Numeric State Entities + description: '**Used in options 8, 9 or 10**. Enter the entities that will + trigger the automation numeric state change. + + ' + default: [] + selector: + entity: + multiple: true + start_above_state: + name: Numeric State Above Option + description: '**Used in options 8, or 10**. Set the above value. The numeric + state trigger will be when the value crosses over to above the set value. + + ' + default: 0 + selector: + number: + min: -20.0 + max: 100.0 + step: 1.0 + unit_of_measurement: value + mode: slider + start_below_state: + name: Numeric State Below Option + description: '**Used in options 9 or 10**. Set the below value. The numeric + state trigger will be when the value crosses over to below the set value. + + ' + default: 0 + selector: + number: + min: -20.0 + max: 100.0 + step: 1.0 + unit_of_measurement: value + mode: slider + start_time_delay_state: + name: Time Delay + description: '**Used in options 1, 2, 3, 4, 5, 6, 7, 8, 9 or 10** Specifies + the duration that the trigger must maintain its state before the automation + is executed. The automation will only proceed if the trigger condition + is met continuously for this entire time delay. + + ' + default: + hours: 0 + minutes: 0 + seconds: 0 + selector: + duration: {} + start_notify_settings: + name: Start Notify + icon: mdi:bell-check-outline + collapsed: true + input: + include_start_notify: + name: Use The Start Notify Options (Optional) + description: "Select \"Enable\" if you would like to receive notifications + when the entity changes to its set state. \n" + default: disable_start_notify_options + selector: + select: + options: + - label: Enable Start Notify Options + value: enable_start_notify_options + - label: Disable Start Notify Options + value: disable_start_notify_options + multiple: false + custom_value: false + sort: false + start_notify_device: + name: Devices To Notify + description: 'Select the devices to receive notifications when the entity + changes to its set state. + + ' + default: [] + selector: + device: + filter: + - integration: mobile_app + multiple: true + start_title: + name: Title + description: 'Specify the notification title for when the entity changes + to its set state. + + ' + default: None + selector: + text: {} + start_message: + name: Message + description: 'Specify the notification message for when the entity changes + to its set state. + + + For more information on including the sensor name that triggered the automation + in your message [Click Here](https://community.home-assistant.io/t/612617/63) + + ' + default: [] + selector: + text: {} + start_notify_interruption_level: + name: Interruption Level - iOS Only + description: 'On devices running iOS 15 and later, you can configure the + interruption level for your notifications to ensure they are delivered + according to your preferences. Choose the desired interruption level from + the dropdown list. Critical and time-sensitive notifications must be turned + ON within the Home Assistant App, and time-sensitive notifications must + be allowed in your Focus settings. + + + For more information on interruption levels [Click Here](https://community.home-assistant.io/t/612617/69) + + ' + default: active + selector: + select: + mode: dropdown + options: + - label: Default + value: active + - label: Critical Notifications + value: critical + - label: Time Sensitive Notifications + value: time-sensitive + - label: Quiet Notifications Without Waking Screen + value: passive + multiple: false + custom_value: false + sort: false + start_notify_data: + name: Android Only Options (Optional) + description: '**High Priority** notifications ensure that you receive important + alerts immediately. These notifications bypass normal delivery delays + and are delivered instantly to your device. + + + **Sticky Notification** ensure that important alerts are not dismissed + when selected. They remain on your notification screen until you actively + clear or swipe them away, ensuring that critical information stays visible + until addressed. + + + **Notification Channel** allow you to easily organize different notification + settings, including notification sounds, vibrations, and other device-specific + features. If you choose this option, please enter your desired channel + name below. + + ' + default: [] + selector: + select: + multiple: true + options: + - label: High Priority + value: high_priority + - label: Sticky Notification + value: sticky + - label: Notification Channel + value: channel + custom_value: false + sort: false + start_notify_channel: + name: Notification Channel - Android Only + description: 'If you have chosen to use a notification channel, simply enter + the name of an existing channel to apply its pre-configured settings. + If you don''t have a channel yet, you can create a new one by entering + a name here. When your automation sends the first notification, it will + create the channel on your device. After the channel is created, you can + customize its notification settings to your preference directly on your + device. + + + For more information on notification channels [Click Here](https://community.home-assistant.io/t/612617/69) + + ' + default: [] + selector: + text: {} + start_action_settings: + name: Start Actions Buttons + icon: mdi:gesture-tap + collapsed: true + input: + include_start_action_buttons: + name: Start Notify - Action Buttons Options (Optional) + description: 'Enable the action buttons you would like to use. These buttons + will be shown in the notification, allowing you to perform actions when + clicked. + + + **NOTE** - You do not need to select any notification action buttons to + execute a start action. If you prefer to run a start action without using + any action buttons, simply enable any ''Start Auto Action'' located in + the ''Start Actions'' section below. + + + ⚠️ Enter the names for all enabled **Action Buttons** below, including + the **Cancel Action Button**. Missing any button names may prevent the + automation from functioning correctly and could result in not receiving + the notification. + + ' + default: [] + selector: + select: + options: + - label: Enable action button 1 + value: enable_start_action_button_1 + - label: Enable action button 2 + value: enable_start_action_button_2 + - label: Enable action button 3 + value: enable_start_action_button_3 + multiple: true + custom_value: false + sort: false + start_action_button_1: + name: Action Button 1 + description: 'Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "Start - Action + 1" input located in the ''Start Actions'' section below. + + ' + default: [] + selector: + text: {} + start_action_button_2: + name: Action Button 2 + description: 'Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "Start - Action + 2" input located in the ''Start Actions'' section below. + + ' + default: [] + selector: + text: {} + start_action_button_3: + name: Action Button 3 + description: 'Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "Start - Action + 3" input located in the ''Start Actions'' section below. + + ' + default: [] + selector: + text: {} + start_action_button_stop: + name: Cancel Action Button + description: 'Enter the Cancel button name to be displayed in the notification. + This button will cancel all actions and stop the automation. It will be + automatically included in the notification if you have enabled an action + button above. + + ' + default: Cancel + selector: + text: {} + start_time_delay: + name: Time Delay + description: 'Specify the time delay after which the automation will resume + its execution if an action button has not been pressed. + + ' + default: + hours: 0 + minutes: 30 + seconds: 0 + selector: + duration: {} + include_start_action_button_confirmation: + name: Action Button Confirmation Option (Optional) + description: 'Select "Enable" if you would like to notify all devices when + an action button has been pressed. This is useful when notifying multiple + devices, informing others that an action has been selected. + + + ⚠️ If Action Button Confirmation is enabled, you must include the **Confirmation + Title**, **Confirmation Message** for each enabled action button, **Cancel + Confirmation Message**, and **Time Delay Confirmation Message**. Missing + any of these may prevent the automation from functioning correctly and + could result in not receiving the notification. + + ' + default: disable_start_action_button_confirmation + selector: + select: + options: + - label: Enable Action Button Confirmation + value: enable_start_action_button_confirmation + - label: Disable Action Button Confirmation + value: disable_start_action_button_confirmation + multiple: false + custom_value: false + sort: false + start_action_button_confirmation_title: + name: Action Button Confirmation Title + description: 'Specify the notification title when an action button is pressed + or when the time delay has passed. + + ' + default: [] + selector: + text: {} + start_action_button_confirmation_message: + name: Action Button 1 Confirmation Message + description: 'Specify the notification message when action button 1 is pressed. + + ' + default: [] + selector: + text: {} + start_action_button_2_confirmation_message: + name: Action Button 2 Confirmation Message + description: 'Specify the notification message when action button 2 is pressed. + + ' + default: [] + selector: + text: {} + start_action_button_3_confirmation_message: + name: Action Button 3 Confirmation Message + description: 'Specify the notification message when action button 3 is pressed. + + ' + default: [] + selector: + text: {} + start_action_button_stop_confirmation_message: + name: Action Button Cancel Confirmation Message + description: 'Specify the notification message when the cancel button is + pressed. + + ' + default: [] + selector: + text: {} + start_time_delay_confirmation_message: + name: Time Delay Confirmation Message + description: 'Specify the notification message when the time delay has passed. + + ' + default: [] + selector: + text: {} + start_auto_action_settings: + name: Start Actions + icon: mdi:code-tags + collapsed: true + input: + include_start_auto_actions: + name: Use The Start Auto Action Options (Optional) + description: 'Enabling the auto option will trigger the **Start Actions** + below when the entity changes to its specified state. + + + When using action buttons in a notification, enabling an auto action is + not necessary for the start actions to work, as pressing an action button + will execute the selected action. However, if you miss the notification + and don''t press an action button, enabling the auto action option ensures + the start actions will still run after the specified time delay. + + ' + default: [] + selector: + select: + options: + - label: Enable start auto action 1 + value: enable_start_action_1 + - label: Enable start auto action 2 + value: enable_start_action_2 + - label: Enable start auto action 3 + value: enable_start_action_3 + multiple: true + custom_value: false + sort: false + start_action_1: + name: Action 1 + description: 'Enter the actions you want to run when the entity changes + state. + + + These actions are also linked to the **Start Notify - Action Button 1** + input located in the **Start Actions Buttons** section above. + + ' + default: [] + selector: + action: {} + start_action_2: + name: Action 2 + description: 'Enter the actions you want to run when the entity changes + state. + + + These actions are also linked to the **Start Notify - Action Button 2** + input located in the **Start Actions Buttons** section above. + + ' + default: [] + selector: + action: {} + start_action_3: + name: Action 3 + description: 'Enter the actions you want to run when the entity changes + state. + + + These actions are also linked to the **Start Notify - Action Button 3** + input located in the **Start Actions Buttons** section above. + + ' + default: [] + selector: + action: {} + end_trigger_settings: + name: End Trigger + icon: mdi:cog-outline + collapsed: true + input: + end_trigger_state: + name: End Trigger - State + description: 'Please select what state you would like for your trigger. + You can choose from ten options available in the dropdown menu. If your + option is not in the dropdown menu then just type the state you would + like to use in and click the save button. + + + 1 - Button Or Any State + + 2 - ON + + 3 - OFF + + 4 - Unavailable + + 5 - Unknown + + 6 - Home + + 7 - Not Home + + 8 - Numeric State - Above + + 9 - Numeric State - Below + + 10 - Numeric State - Above & Below + + + A numbering system has been implemented to facilitate navigation within + the dropdown selections. Each number corresponds to a specific configuration, + aiding users in identifying and adjusting the settings used within each + selection. For instance, when selecting "8 - Numeric State - Above" as + a dropdown option, settings marked "Used in options 8, 9 or 10" are required + for that respective selection because number 8 is included in those options. + + ' + default: button_any_state + selector: + select: + custom_value: true + mode: dropdown + options: + - label: 1 - Button Or Any State + value: button_any_state + - label: 2 - ON + value: 'on' + - label: 3 - OFF + value: 'off' + - label: 4 - Unavailable + value: unavailable + - label: 5 - Unknown + value: unknown + - label: 6 - Home + value: home + - label: 7 - Not Home + value: not_home + - label: 8 - Numeric State - Above + value: numeric_state_above + - label: 9 - Numeric State - Below + value: numeric_state_below + - label: 10 - Numeric State - Above & Below + value: numeric_state_above_below + multiple: false + sort: false + end_trigger_state_entity: + name: State Entities + description: '**Used in options 1, 2, 3, 4, 5, 6 or 7**. Enter the entities + that will trigger the automation on state change. + + ' + default: [] + selector: + entity: + multiple: true + end_trigger_numeric_entity: + name: Numeric State Entities + description: '**Used in options 8, 9 or 10**. Enter the entities that will + trigger the automation numeric state change. + + ' + default: [] + selector: + entity: + multiple: true + end_above_state: + name: Numeric State Above Option + description: '**Used in options 8, or 10**. Set the above value. The numeric + state trigger will be when the value crosses over to above the set value. + + ' + default: 0 + selector: + number: + min: -20.0 + max: 100.0 + step: 1.0 + unit_of_measurement: value + mode: slider + end_below_state: + name: Numeric State Below Option + description: '**Used in options 9 or 10**. Set the below value. The numeric + state trigger will be when the value crosses over to below the set value. + + ' + default: 0 + selector: + number: + min: -20.0 + max: 100.0 + step: 1.0 + unit_of_measurement: value + mode: slider + end_time_delay_state: + name: Time Delay + description: '**Used in options 1, 2, 3, 4, 5, 6, 7, 8, 9 or 10** Specifies + the duration that the trigger must maintain its state before the automation + is executed. The automation will only proceed if the trigger condition + is met continuously for this entire time delay. + + ' + default: + hours: 0 + minutes: 0 + seconds: 0 + selector: + duration: {} + end_notify_settings: + name: End Notify + icon: mdi:bell-ring-outline + collapsed: true + input: + include_end_notify: + name: Use The End Notify Options (Optional) + description: "Select \"Enable\" if you would like to receive notifications + when the entity changes to its set state. \n" + default: disable_end_notify_options + selector: + select: + options: + - label: Enable End Notify Options + value: enable_end_notify_options + - label: Disable End Notify Options + value: disable_end_notify_options + multiple: false + custom_value: false + sort: false + end_notify_device: + name: Devices To Notify + description: 'Select the devices to receive notifications when the entity + changes to its set state. + + ' + default: [] + selector: + device: + filter: + - integration: mobile_app + multiple: true + end_title: + name: Title + description: 'Specify the notification title for when the entity changes + to its set state. + + ' + default: [] + selector: + text: {} + end_message: + name: Message + description: 'Specify the notification message for when the entity changes + to its set state. + + + For more information on including the sensor name that triggered the automation + in your message [Click Here](https://community.home-assistant.io/t/612617/63) + + ' + default: [] + selector: + text: {} + end_notify_interruption_level: + name: Interruption Level - iOS Only + description: 'On devices running iOS 15 and later, you can configure the + interruption level for your notifications to ensure they are delivered + according to your preferences. Choose the desired interruption level from + the dropdown list. Critical and time-sensitive notifications must be turned + ON within the Home Assistant App, and time-sensitive notifications must + be allowed in your Focus settings. + + + For more information on interruption levels [Click Here](https://community.home-assistant.io/t/612617/69) + + ' + default: active + selector: + select: + mode: dropdown + options: + - label: Default + value: active + - label: Critical Notifications + value: critical + - label: Time Sensitive Notifications + value: time-sensitive + - label: Quiet Notifications Without Waking Screen + value: passive + multiple: false + custom_value: false + sort: false + end_notify_data: + name: Android Only Options (Optional) + description: '**High Priority** notifications ensure that you receive important + alerts immediately. These notifications bypass normal delivery delays + and are delivered instantly to your device. + + + **Sticky Notification** ensure that important alerts are not dismissed + when selected. They remain on your notification screen until you actively + clear or swipe them away, ensuring that critical information stays visible + until addressed. + + + **Notification Channel** allow you to easily organize different notification + settings, including notification sounds, vibrations, and other device-specific + features. If you choose this option, please enter your desired channel + name below. + + ' + default: [] + selector: + select: + multiple: true + options: + - label: High Priority + value: high_priority + - label: Sticky Notification + value: sticky + - label: Notification Channel + value: channel + custom_value: false + sort: false + end_notify_channel: + name: Notification Channel - Android Only + description: 'If you have chosen to use a notification channel, simply enter + the name of an existing channel to apply its pre-configured settings. + If you don''t have a channel yet, you can create a new one by entering + a name here. When your automation sends the first notification, it will + create the channel on your device. After the channel is created, you can + customize its notification settings to your preference directly on your + device. + + + For more information on notification channels [Click Here](https://community.home-assistant.io/t/612617/69) + + ' + default: [] + selector: + text: {} + end_action_settings: + name: End Actions Buttons + icon: mdi:gesture-double-tap + collapsed: true + input: + include_end_action_buttons: + name: End Notify - Action Buttons Options (Optional) + description: 'Enable the action buttons you would like to use. These buttons + will be shown in the notification, allowing you to perform actions when + clicked. + + + **NOTE** - You do not need to select any notification action buttons to + execute an end action. If you prefer to run an end action without using + any action buttons, simply enable any ''End Auto Action'' located in the + ''End Actions'' section below. + + + ⚠️ Enter the names for all enabled **Action Buttons** below, including + the **Cancel Action Button**. Missing any button names may prevent the + automation from functioning correctly and could result in not receiving + the notification. + + ' + default: [] + selector: + select: + options: + - label: Enable action button 1 + value: enable_end_action_button_1 + - label: Enable action button 2 + value: enable_end_action_button_2 + - label: Enable action button 3 + value: enable_end_action_button_3 + multiple: true + custom_value: false + sort: false + end_action_button_1: + name: Action Button 1 + description: 'Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "End - Action 1" + input located in the ''End Actions'' section below. + + ' + default: [] + selector: + text: {} + end_action_button_2: + name: Action Button 2 + description: 'Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "End - Action 2" + input located in the ''End Actions'' section below. + + ' + default: [] + selector: + text: {} + end_action_button_3: + name: Action Button 3 + description: 'Enter the button name to be displayed in the notification. + This button will execute all actions entered into the "End - Action 3" + input located in the ''End Actions'' section below. + + ' + default: [] + selector: + text: {} + end_action_button_stop: + name: Cancel Action Button + description: 'Enter the Cancel button name to be displayed in the notification. + This button will cancel all actions and stop the automation. It will be + automatically included in the notification if you have enabled an action + button above. + + ' + default: Cancel + selector: + text: {} + end_time_delay: + name: Time Delay + description: 'Specify the time delay after which the automation will resume + its execution if an action button has not been pressed. + + ' + default: + hours: 0 + minutes: 30 + seconds: 0 + selector: + duration: {} + include_end_action_button_confirmation: + name: Action Button Confirmation Option (Optional) + description: 'Select "Enable" if you would like to notify all devices when + an action button has been pressed. This is useful when notifying multiple + devices, informing others that an action has been selected. + + + ⚠️ If Action Button Confirmation is enabled, you must include the **Confirmation + Title**, **Confirmation Message** for each enabled action button, **Cancel + Confirmation Message**, and **Time Delay Confirmation Message**. Missing + any of these may prevent the automation from functioning correctly and + could result in not receiving the notification. + + ' + default: disable_end_action_button_confirmation + selector: + select: + options: + - label: Enable Action Button Confirmation + value: enable_end_action_button_confirmation + - label: Disable Action Button Confirmation + value: disable_end_action_button_confirmation + multiple: false + custom_value: false + sort: false + end_action_button_confirmation_title: + name: Action Button Confirmation Title + description: 'Specify the notification title when an action button is pressed + or when the time delay has passed. + + ' + default: [] + selector: + text: {} + end_action_button_confirmation_message: + name: Action Button 1 Confirmation Message + description: 'Specify the notification message when action button 1 is pressed. + + ' + default: [] + selector: + text: {} + end_action_button_2_confirmation_message: + name: Action Button 2 Confirmation Message + description: 'Specify the notification message when action button 2 is pressed. + + ' + default: [] + selector: + text: {} + end_action_button_3_confirmation_message: + name: Action Button 3 Confirmation Message + description: 'Specify the notification message when action button 3 is pressed. + + ' + default: [] + selector: + text: {} + end_action_button_stop_confirmation_message: + name: Action Button Cancel Confirmation Message + description: 'Specify the notification message when the cancel button is + pressed. + + ' + default: [] + selector: + text: {} + end_time_delay_confirmation_message: + name: Time Delay Confirmation Message + description: 'Specify the notification message when the time delay has passed. + + ' + default: [] + selector: + text: {} + end_auto_action_settings: + name: End Actions + icon: mdi:code-tags + collapsed: true + input: + include_end_auto_actions: + name: Use The End Auto Action Options (Optional) + description: 'Enabling the auto option will trigger the **End Actions** + below when the entity changes to its specified state. + + + When using action buttons in a notification, enabling an auto action is + not necessary for the end actions to work, as pressing an action button + will execute the selected action. However, if you miss the notification + and don''t press an action button, enabling the auto action option ensures + the end actions will still run after the specified time delay. + + ' + default: [] + selector: + select: + options: + - label: Enable auto end action 1 option + value: enable_end_action_1 + - label: Enable auto end action 2 option + value: enable_end_action_2 + - label: Enable auto end action 3 option + value: enable_end_action_3 + multiple: true + custom_value: false + sort: false + end_action_1: + name: Action 1 + description: 'Enter the actions you want to run when the entity changes + state. + + + These actions are also linked to the **End Notify - Action Button 1** + input located in the **End Actions Buttons** section above. + + ' + default: [] + selector: + action: {} + end_action_2: + name: EAction 2 + description: 'Enter the actions you want to run when the entity changes + state. + + + These actions are also linked to the **End Notify - Action Button 2** + input located in the **End Actions Buttons** section above. + + ' + default: [] + selector: + action: {} + end_action_3: + name: Action 3 + description: 'Enter the actions you want to run when the entity changes + state. + + + These actions are also linked to the **End Notify - Action Button 3** + input located in the **End Actions Buttons** section above. + + ' + default: [] + selector: + action: {} + global_conditions_settings: + name: Global Conditions + icon: mdi:earth + collapsed: true + input: + global_conditions: + name: Global Conditions + description: 'Enter any global conditions you would like to apply to the + automation. + + ' + default: [] + selector: + condition: {} + source_url: https://gist.github.com/Blackshome/06f6f28e76299267b813dac48608f549 +mode: restart +max_exceeded: silent +variables: + start_trigger_state: !input start_trigger_state + start_trigger_state_entity: !input start_trigger_state_entity + start_trigger_numeric_entity: !input start_trigger_numeric_entity + start_above_state: !input start_above_state + start_below_state: !input start_below_state + start_time_delay_state: !input start_time_delay_state + include_start_notify: !input include_start_notify + start_notify_device: !input start_notify_device + start_title: !input start_title + start_message: !input start_message + start_notify_interruption_level: !input start_notify_interruption_level + start_notify_data: !input start_notify_data + start_notify_channel: !input start_notify_channel + include_start_action_buttons: !input include_start_action_buttons + start_action_button_1: !input start_action_button_1 + start_action_button_2: !input start_action_button_2 + start_action_button_3: !input start_action_button_3 + start_action_button_stop: !input start_action_button_stop + start_time_delay: !input start_time_delay + include_start_action_button_confirmation: !input include_start_action_button_confirmation + start_action_button_confirmation_title: !input start_action_button_confirmation_title + start_action_button_confirmation_message: !input start_action_button_confirmation_message + start_action_button_2_confirmation_message: !input start_action_button_2_confirmation_message + start_action_button_3_confirmation_message: !input start_action_button_3_confirmation_message + start_action_button_stop_confirmation_message: !input start_action_button_stop_confirmation_message + start_time_delay_confirmation_message: !input start_time_delay_confirmation_message + include_start_auto_actions: !input include_start_auto_actions + start_action_1: !input start_action_1 + start_action_2: !input start_action_2 + start_action_3: !input start_action_3 + end_trigger_state: !input end_trigger_state + end_trigger_state_entity: !input end_trigger_state_entity + end_trigger_numeric_entity: !input end_trigger_numeric_entity + end_above_state: !input end_above_state + end_below_state: !input end_below_state + end_time_delay_state: !input end_time_delay_state + include_end_notify: !input include_end_notify + end_notify_device: !input end_notify_device + end_title: !input end_title + end_message: !input end_message + end_notify_interruption_level: !input end_notify_interruption_level + end_notify_data: !input end_notify_data + end_notify_channel: !input end_notify_channel + include_end_action_buttons: !input include_end_action_buttons + end_action_button_1: !input end_action_button_1 + end_action_button_2: !input end_action_button_2 + end_action_button_3: !input end_action_button_3 + end_action_button_stop: !input end_action_button_stop + end_time_delay: !input end_time_delay + include_end_action_button_confirmation: !input include_end_action_button_confirmation + end_action_button_confirmation_title: !input end_action_button_confirmation_title + end_action_button_confirmation_message: !input end_action_button_confirmation_message + end_action_button_2_confirmation_message: !input end_action_button_2_confirmation_message + end_action_button_3_confirmation_message: !input end_action_button_3_confirmation_message + end_action_button_stop_confirmation_message: !input end_action_button_stop_confirmation_message + end_time_delay_confirmation_message: !input end_time_delay_confirmation_message + include_end_auto_actions: !input include_end_auto_actions + end_action_1: !input end_action_1 + end_action_2: !input end_action_2 + end_action_3: !input end_action_3 + global_conditions: !input global_conditions +trigger: +- platform: state + id: t0 + entity_id: !input start_trigger_state_entity + for: !input start_time_delay_state +- platform: state + id: t1 + entity_id: !input start_trigger_state_entity + to: !input start_trigger_state + for: !input start_time_delay_state +- platform: numeric_state + id: t2 + entity_id: !input start_trigger_numeric_entity + above: !input start_above_state + for: !input start_time_delay_state +- platform: numeric_state + id: t3 + entity_id: !input start_trigger_numeric_entity + below: !input start_below_state + for: !input start_time_delay_state +- platform: state + id: t4 + entity_id: !input end_trigger_state_entity + for: !input end_time_delay_state +- platform: state + id: t5 + entity_id: !input end_trigger_state_entity + to: !input end_trigger_state + for: !input end_time_delay_state +- platform: numeric_state + id: t6 + entity_id: !input end_trigger_numeric_entity + above: !input end_above_state + for: !input end_time_delay_state +- platform: numeric_state + id: t7 + entity_id: !input end_trigger_numeric_entity + below: !input end_below_state + for: !input end_time_delay_state +condition: +- condition: or + conditions: + - condition: and + conditions: + - condition: trigger + id: t0 + - '{{ start_trigger_state == ''button_any_state'' }}' + - condition: and + conditions: + - condition: trigger + id: t1 + - '{{ (start_trigger_state != ''button_any_state'') or (start_trigger_state != + ''numeric_state_above'') or (start_trigger_state != ''numeric_state_below'') + or (start_trigger_state != ''numeric_state_above_below'') }}' + - condition: and + conditions: + - condition: trigger + id: t2 + - '{{ (start_trigger_state == ''numeric_state_above'') or (start_trigger_state + == ''numeric_state_above_below'') }}' + - condition: and + conditions: + - condition: trigger + id: t3 + - '{{ (start_trigger_state == ''numeric_state_below'') or (start_trigger_state + == ''numeric_state_above_below'') }}' + - condition: and + conditions: + - condition: trigger + id: t4 + - '{{ end_trigger_state == ''button_any_state'' }}' + - condition: and + conditions: + - condition: trigger + id: t5 + - '{{ (end_trigger_state != ''button_any_state'') or (end_trigger_state != ''numeric_state_above'') + or (end_trigger_state != ''numeric_state_below'') or (end_trigger_state != ''numeric_state_above_below'') + }}' + - condition: and + conditions: + - condition: trigger + id: t6 + - '{{ (end_trigger_state == ''numeric_state_above'') or (end_trigger_state == + ''numeric_state_above_below'') }}' + - condition: and + conditions: + - condition: trigger + id: t7 + - '{{ (end_trigger_state == ''numeric_state_below'') or (end_trigger_state == + ''numeric_state_above_below'') }}' +- condition: and + conditions: !input global_conditions +action: +- choose: + - alias: Start trigger + conditions: + - condition: trigger + id: + - t0 + - t1 + - t2 + - t3 + sequence: + - choose: + - alias: Check if the start of notification is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_notify_options'' in include_start_notify + }}' + sequence: + - variables: + action_button_1: '{{ ''BUTTON_1_'' ~ context.id }}' + action_button_2: '{{ ''BUTTON_2_'' ~ context.id }}' + action_button_3: '{{ ''BUTTON_3_'' ~ context.id }}' + action_button_stop: '{{ ''BUTTON_STOP_'' ~ context.id }}' + start_message_data: "{% set message = namespace(data={}) %} {% if start_notify_interruption_level + in ['active', 'critical', 'time-sensitive', 'passive'] %}\n {% set + message.data = dict(message.data, **{ 'push': {'interruption-level': + start_notify_interruption_level }}) %}\n{% endif %} {% if 'high_priority' + in start_notify_data %}\n {% set message.data = dict(message.data, + **{ 'ttl': 0, 'priority': 'high' }) %}\n{% endif %} {% if 'channel' + in start_notify_data %}\n {% set message.data = dict(message.data, + **{ 'channel': start_notify_channel }) %}\n{% endif %} {% if 'sticky' + in start_notify_data %}\n {% set message.data = dict(message.data, + **{ 'sticky': \"true\" }) %}\n{% endif %} {{ message.data }}" + start_message_data_action_button: "{% set message = namespace(data={}) + %} {% set actions = [] %} {% if start_notify_interruption_level in ['active', + 'critical', 'time-sensitive', 'passive'] %}\n {% set message.data = + dict(message.data, **{ 'push': {'interruption-level': start_notify_interruption_level + }}) %}\n{% endif %} {% if 'high_priority' in start_notify_data %}\n + \ {% set message.data = dict(message.data, **{ 'ttl': 0, 'priority': + 'high' }) %}\n{% endif %} {% if 'channel' in start_notify_data %}\n + \ {% set message.data = dict(message.data, **{ 'channel': start_notify_channel + }) %}\n{% endif %} {% if 'sticky' in start_notify_data %}\n {% set + message.data = dict(message.data, **{ 'sticky': \"true\" }) %}\n{% endif + %} {% if 'enable_start_action_button_1' in include_start_action_buttons + %}\n {% set actions = actions + [{ 'action': action_button_1, 'title': + start_action_button_1 }] %}\n{% endif %} {% if 'enable_start_action_button_2' + in include_start_action_buttons %}\n {% set actions = actions + [{ + 'action': action_button_2, 'title': start_action_button_2 }] %}\n{% + endif %} {% if 'enable_start_action_button_3' in include_start_action_buttons + %}\n {% set actions = actions + [{ 'action': action_button_3, 'title': + start_action_button_3 }] %}\n{% endif %} {% if ('enable_start_action_button_1' + in include_start_action_buttons) or \n ('enable_start_action_button_2' + in include_start_action_buttons) or \n ('enable_start_action_button_3' + in include_start_action_buttons) %}\n {% set actions = actions + [{ + 'action': action_button_stop, 'title': start_action_button_stop }] %}\n{% + endif %} {% if actions | length > 0 %}\n {% set message.data = dict(message.data, + **{ 'actions': actions }) %}\n{% endif %} {{ message.data }}" + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify + }} + data: + title: !input start_title + message: !input start_message + data: '{{ start_message_data_action_button }}' + - choose: + - alias: Check if any start action buttons are enabled + conditions: + - condition: template + value_template: '{{ (''enable_start_action_button_1'' in include_start_action_buttons) + or (''enable_start_action_button_2'' in include_start_action_buttons) + or (''enable_start_action_button_3'' in include_start_action_buttons) + }}' + sequence: + - alias: Wait for a response from the action button + wait_for_trigger: + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_1 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_2 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_3 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_stop }}' + timeout: !input start_time_delay + - choose: + - alias: Check if confirmation message is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_action_button_confirmation'' + in include_start_action_button_confirmation }}' + sequence: + - if: + - alias: Confirmation from the actions buttons + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - choose: + - alias: Check if action button 1 is pressed + conditions: + - condition: template + value_template: '{{ action_button_1 in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_action_button_confirmation_message + data: '{{ start_message_data }}' + - alias: Check if action button 2 is pressed + conditions: + - condition: template + value_template: '{{ action_button_2 in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_action_button_2_confirmation_message + data: '{{ start_message_data }}' + - alias: Check if action button 3 is pressed + conditions: + - condition: template + value_template: '{{ action_button_3 in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_action_button_3_confirmation_message + data: '{{ start_message_data }}' + - alias: Check if action stop button is pressed + conditions: + - condition: template + value_template: '{{ action_button_stop in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_action_button_stop_confirmation_message + data: '{{ start_message_data }}' + - alias: Perform the action button selected action + choose: + - conditions: '{{ wait.trigger.event.data.action == action_button_1 + }}' + sequence: !input start_action_1 + - conditions: '{{ wait.trigger.event.data.action == action_button_2 + }}' + sequence: !input start_action_2 + - conditions: '{{ wait.trigger.event.data.action == action_button_3 + }}' + sequence: !input start_action_3 + - conditions: '{{ wait.trigger.event.data.action == action_button_stop + }}' + sequence: + - stop: Stop the automation + else: + - alias: Send a notification to each device + repeat: + for_each: !input start_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') + | slugify }} + data: + title: !input start_action_button_confirmation_title + message: !input start_time_delay_confirmation_message + data: '{{ start_message_data }}' + - alias: Check if confirmation message is disabled + conditions: + - condition: template + value_template: '{{ ''disable_start_action_button_confirmation'' + in include_start_action_button_confirmation }}' + sequence: + - if: + - alias: Confirmation from the actions buttons + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - alias: Perform the action button selected action + choose: + - conditions: '{{ wait.trigger.event.data.action == action_button_1 + }}' + sequence: !input start_action_1 + - conditions: '{{ wait.trigger.event.data.action == action_button_2 + }}' + sequence: !input start_action_2 + - conditions: '{{ wait.trigger.event.data.action == action_button_3 + }}' + sequence: !input start_action_3 + - conditions: '{{ wait.trigger.event.data.action == action_button_stop + }}' + sequence: + - stop: Stop the automation + - choose: + - alias: Check if auto action 1 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_action_1'' in include_start_auto_actions + }}' + sequence: !input start_action_1 + - choose: + - alias: Check if auto action 2 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_action_2'' in include_start_auto_actions + }}' + sequence: !input start_action_2 + - choose: + - alias: Check if auto action 3 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_start_action_3'' in include_start_auto_actions + }}' + sequence: !input start_action_3 + default: + - choose: + - alias: End Trigger + conditions: + - condition: trigger + id: + - t4 + - t5 + - t6 + - t7 + sequence: + - choose: + - alias: Check if end of notification is enabled + conditions: + - condition: template + value_template: '{{ include_end_notify == ''enable_end_notify_options'' + }}' + sequence: + - variables: + action_button_1: '{{ ''BUTTON_1_'' ~ context.id }}' + action_button_2: '{{ ''BUTTON_2_'' ~ context.id }}' + action_button_3: '{{ ''BUTTON_3_'' ~ context.id }}' + action_button_stop: '{{ ''BUTTON_STOP_'' ~ context.id }}' + end_message_data: "{% set message = namespace(data={}) %} {% if end_notify_interruption_level + in ['active', 'critical', 'time-sensitive', 'passive'] %}\n {% set + message.data = dict(message.data, **{ 'push': {'interruption-level': + end_notify_interruption_level }}) %}\n{% endif %} {% if 'high_priority' + in end_notify_data %}\n {% set message.data = dict(message.data, + **{ 'ttl': 0, 'priority': 'high' }) %}\n{% endif %} {% if 'channel' + in end_notify_data %}\n {% set message.data = dict(message.data, + **{ 'channel': end_notify_channel }) %}\n{% endif %} {% if 'sticky' + in end_notify_data %}\n {% set message.data = dict(message.data, + **{ 'sticky': \"true\" }) %}\n{% endif %} {{ message.data }}" + end_message_data_action_button: "{% set message = namespace(data={}) + %} {% set actions = [] %} {% if end_notify_interruption_level in ['active', + 'critical', 'time-sensitive', 'passive'] %}\n {% set message.data + = dict(message.data, **{ 'push': {'interruption-level': end_notify_interruption_level + }}) %}\n{% endif %} {% if 'high_priority' in end_notify_data %}\n + \ {% set message.data = dict(message.data, **{ 'ttl': 0, 'priority': + 'high' }) %}\n{% endif %} {% if 'channel' in end_notify_data %}\n + \ {% set message.data = dict(message.data, **{ 'channel': end_notify_channel + }) %}\n{% endif %} {% if 'sticky' in end_notify_data %}\n {% set + message.data = dict(message.data, **{ 'sticky': \"true\" }) %}\n{% + endif %} {% if 'enable_end_action_button_1' in include_end_action_buttons + %}\n {% set actions = actions + [{ 'action': action_button_1, 'title': + end_action_button_1 }] %}\n{% endif %} {% if 'enable_end_action_button_2' + in include_end_action_buttons %}\n {% set actions = actions + [{ + 'action': action_button_2, 'title': end_action_button_2 }] %}\n{% + endif %} {% if 'enable_end_action_button_3' in include_end_action_buttons + %}\n {% set actions = actions + [{ 'action': action_button_3, 'title': + end_action_button_3 }] %}\n{% endif %} {% if ('enable_end_action_button_1' + in include_end_action_buttons) or \n ('enable_end_action_button_2' + in include_end_action_buttons) or \n ('enable_end_action_button_3' + in include_end_action_buttons) %}\n {% set actions = actions + [{ + 'action': action_button_stop, 'title': end_action_button_stop }] %}\n{% + endif %} {% if actions | length > 0 %}\n {% set message.data = dict(message.data, + **{ 'actions': actions }) %}\n{% endif %} {{ message.data }}" + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify + }} + data: + title: !input end_title + message: !input end_message + data: '{{ end_message_data_action_button }}' + - choose: + - alias: Check if any start action buttons are enabled + conditions: + - condition: template + value_template: '{{ (''enable_end_action_button_1'' in include_end_action_buttons) + or (''enable_end_action_button_2'' in include_end_action_buttons) + or (''enable_end_action_button_3'' in include_end_action_buttons) + }}' + sequence: + - alias: Wait for a response from the action button + wait_for_trigger: + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_1 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_2 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_3 }}' + - platform: event + event_type: mobile_app_notification_action + event_data: + action: '{{ action_button_stop }}' + timeout: !input end_time_delay + - choose: + - alias: Check if confirmation message is enabled + conditions: + - condition: template + value_template: '{{ ''enable_end_action_button_confirmation'' + in include_end_action_button_confirmation }}' + sequence: + - if: + - alias: Confirmation from the actions buttons + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - choose: + - alias: Check if action button 1 is pressed + conditions: + - condition: template + value_template: '{{ action_button_1 in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_action_button_confirmation_message + data: '{{ end_message_data }}' + - alias: Check if action button 2 is pressed + conditions: + - condition: template + value_template: '{{ action_button_2 in wait.trigger.event.data.action }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_action_button_2_confirmation_message + data: '{{ end_message_data }}' + - alias: Check if action button 3 is pressed + conditions: + - condition: template + value_template: '{{ action_button_3 in wait.trigger.event.data.action }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_action_button_3_confirmation_message + data: '{{ end_message_data }}' + - alias: Check if action stop button is pressed + conditions: + - condition: template + value_template: '{{ action_button_stop in wait.trigger.event.data.action + }}' + sequence: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, + 'name') | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_action_button_stop_confirmation_message + data: '{{ end_message_data }}' + - alias: Perform the action button selected action + choose: + - conditions: '{{ action_button_1 in wait.trigger.event.data.action + }}' + sequence: !input end_action_1 + - conditions: '{{ action_button_2 in wait.trigger.event.data.action + }}' + sequence: !input end_action_2 + - conditions: '{{ action_button_3 in wait.trigger.event.data.action + }}' + sequence: !input end_action_3 + - conditions: '{{ action_button_stop in wait.trigger.event.data.action + }}' + sequence: + - stop: Stop the automation + else: + - alias: Send a notification to each device + repeat: + for_each: !input end_notify_device + sequence: + - service: notify.mobile_app_{{ device_attr(repeat.item, 'name') + | slugify }} + data: + title: !input end_action_button_confirmation_title + message: !input end_time_delay_confirmation_message + data: '{{ end_message_data }}' + - alias: Check if confirmation message is disabled + conditions: + - condition: template + value_template: '{{ ''disable_end_action_button_confirmation'' + in include_end_action_button_confirmation }}' + sequence: + - if: + - alias: Perform the action button selected action + condition: template + value_template: '{{ wait.trigger.idx is defined }}' + then: + - alias: Perform the action + choose: + - conditions: '{{ action_button_1 in wait.trigger.event.data.action + }}' + sequence: !input end_action_1 + - conditions: '{{ action_button_2 in wait.trigger.event.data.action + }}' + sequence: !input end_action_2 + - conditions: '{{ action_button_3 in wait.trigger.event.data.action + }}' + sequence: !input end_action_3 + - conditions: '{{ action_button_stop in wait.trigger.event.data.action + }}' + sequence: + - stop: Stop the automation + - choose: + - alias: Check if auto action 1 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_end_action_1'' in include_end_auto_actions + }}' + sequence: !input end_action_1 + - choose: + - alias: Check if auto action 2 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_end_action_2'' in include_end_auto_actions + }}' + sequence: !input end_action_2 + - choose: + - alias: Check if auto action 3 is enabled + conditions: + - condition: template + value_template: '{{ ''enable_end_action_3'' in include_end_auto_actions + }}' + sequence: !input end_action_3 diff --git a/blueprints/automation/Blackshome/timer.yaml b/blueprints/automation/Blackshome/timer.yaml new file mode 100644 index 0000000..dc6f8fb --- /dev/null +++ b/blueprints/automation/Blackshome/timer.yaml @@ -0,0 +1,307 @@ +blueprint: + name: Timer + description: "# ⏲️ Timer\n**Version: 1.2**\n\nEffortless Control: Schedule \U0001F4C5, + Trigger ⚡, and Automate Your Home \U0001F3E0 with Precision \U0001F3AF!\n\n**If + you like my blueprints, and would like to show your support or just say thank + you?** [Click Here](https://www.paypal.com/donate/?hosted_button_id=WAZS3QSDTPGA8) + \U0001F642\n\n
The Automation Process: \U0001F448\n\n + \ - **Automation:**\n - Turn ON and OFF any entity you like on a set time.\n + \ - You must use a timer helper for this automation to work.\n\n - **Multiple + Timers:**\n - You can set up to 4 different time triggers, each with its own + time period.\n\n - **Weekdays Condition Option:**\n - Specify the weekdays + to define when the automation can run.\n
\n\nNeed help? \n- FAQ: [Click + Here](https://community.home-assistant.io/t/time-clock/651151/2)\n- Community + Support Including Updates: [Click Here](https://community.home-assistant.io/t/time-clock/651151)\n\nRequired + = *\n" + domain: automation + input: + settings: + name: Settings * + icon: mdi:cog-outline + collapsed: true + input: + timer_helper: + name: Timer - Timer Helper * + description: 'Select the timer helper to be used for all timers. Using a + timer helper provides flexibility to start, pause, and cancel timers, + persistence across reboots, and real-time monitoring, making your automations + more robust and adaptable. + + + For more information on how to create a timer helper [Click Here](https://community.home-assistant.io/t/651151/4?u=blacky)' + default: [] + selector: + entity: + filter: + - domain: + - timer + multiple: false + entity_input: + name: Entities To Turn ON * + description: Specify the entities that will be turned ON by a start time + and OFF after the set duration. + default: [] + selector: + target: {} + triggers: + name: Triggers + icon: mdi:clock-outline + collapsed: true + input: + trigger_one_time: + name: Timer One - Start Time + description: Set the start time. + default: 00:00:00 + selector: + time: {} + trigger_one_duration: + name: Timer One - Duration + description: Set the duration for Timer One. + default: 00:00:00 + selector: + duration: {} + include_extra_triggers: + name: Use The Extra Timers (Optional) + description: Select any additional timers you want to include. + default: [] + selector: + select: + multiple: true + options: + - label: Use timer two + value: use_trigger_two + - label: Use timer three + value: use_trigger_three + - label: Use timer four + value: use_trigger_four + sort: false + custom_value: false + trigger_two_time: + name: Timer Two - Start Time + description: Set the start time. + default: 00:00:00 + selector: + time: {} + trigger_two_duration: + name: Timer Two - Duration + description: Set the duration for Timer Two. + default: 00:00:00 + selector: + duration: {} + trigger_three_time: + name: Timer Three - Start Time + description: Set the start time. + default: 00:00:00 + selector: + time: {} + trigger_three_duration: + name: Timer Three - Duration + description: Set the duration for Timer Three. + default: 00:00:00 + selector: + duration: {} + trigger_four_time: + name: Timer Four - Start Time + description: Set the start time. + default: 00:00:00 + selector: + time: {} + trigger_four_duration: + name: Timer Four - Duration + description: Set the duration for Timer Four. + default: 00:00:00 + selector: + duration: {} + weekdays: + name: Weekdays + icon: mdi:calendar-week + collapsed: true + input: + include_weekdays: + name: Use The Weekdays Option (Optional) + description: This option adds a condition to operate only on specified weekdays. + It applies globally to all start times. + default: weekday_disabled + selector: + select: + options: + - label: Enable the weekday option + value: weekday_enabled + - label: Disable the weekday option + value: weekday_disabled + sort: false + custom_value: false + multiple: false + weekday_options: + name: Weekdays + description: Select the days of the week on which you want the automation + to run. + default: + - mon + - tue + - wed + - thu + - fri + - sat + - sun + selector: + select: + multiple: true + mode: list + options: + - label: Monday + value: mon + - label: Tuesday + value: tue + - label: Wednesday + value: wed + - label: Thursday + value: thu + - label: Friday + value: fri + - label: Saturday + value: sat + - label: Sunday + value: sun + sort: false + custom_value: false + source_url: https://gist.github.com/Blackshome/ec0afd31dce7fc6b106e378d06b01940 +mode: restart +max_exceeded: silent +variables: + trigger_one_duration: !input trigger_one_duration + entity_input: !input entity_input + timer_helper: !input timer_helper + trigger_one_time: !input trigger_one_time + include_extra_triggers: !input include_extra_triggers + trigger_two_time: !input trigger_two_time + trigger_two_duration: !input trigger_two_duration + trigger_three_time: !input trigger_three_time + trigger_three_duration: !input trigger_three_duration + trigger_four_time: !input trigger_four_time + trigger_four_duration: !input trigger_four_duration + include_weekdays: !input include_weekdays + weekday_options: !input weekday_options +trigger: +- platform: time + at: !input trigger_one_time + id: t1 +- platform: time + at: !input trigger_two_time + id: t2 +- platform: time + at: !input trigger_three_time + id: t3 +- platform: time + at: !input trigger_four_time + id: t4 +- platform: state + entity_id: !input timer_helper + to: idle + id: t5 +condition: +- condition: or + conditions: + - condition: and + conditions: + - condition: trigger + id: + - t1 + - condition: and + conditions: + - condition: trigger + id: t2 + - '{{ ''use_trigger_two'' in include_extra_triggers }}' + - condition: and + conditions: + - condition: trigger + id: t3 + - '{{ ''use_trigger_three'' in include_extra_triggers }}' + - condition: and + conditions: + - condition: trigger + id: t4 + - '{{ ''use_trigger_four'' in include_extra_triggers }}' + - condition: and + conditions: + - condition: trigger + id: t5 +- condition: or + conditions: + - '{{ include_weekdays == ''weekday_disabled'' }}' + - condition: and + conditions: + - condition: time + weekday: !input weekday_options + - '{{ include_weekdays == ''weekday_enabled'' }}' + - condition: trigger + id: + - t5 +action: +- choose: + - alias: Check if the time helper is enabled + conditions: + - condition: trigger + id: + - t1 + - t2 + - t3 + - t4 + sequence: + - alias: Turn ON the entities + service: homeassistant.turn_on + target: !input entity_input + - choose: + - alias: By-pass is turned on & check by-pass option - turn lights off + conditions: + - condition: trigger + id: + - t1 + sequence: + - alias: Turn ON the timer helper + service: timer.start + entity_id: !input timer_helper + data: + duration: !input trigger_one_duration + - alias: By-pass is turned on & check by-pass option - turn lights off + conditions: + - condition: trigger + id: + - t2 + sequence: + - alias: Turn ON the timer helper + service: timer.start + entity_id: !input timer_helper + data: + duration: !input trigger_two_duration + - alias: By-pass is turned on & check by-pass option - turn lights off + conditions: + - condition: trigger + id: + - t3 + sequence: + - alias: Turn ON the timer helper + service: timer.start + entity_id: !input timer_helper + data: + duration: !input trigger_three_duration + - alias: By-pass is turned on & check by-pass option - turn lights off + conditions: + - condition: trigger + id: + - t4 + sequence: + - alias: Turn ON the timer helper + service: timer.start + entity_id: !input timer_helper + data: + duration: !input trigger_four_duration + - alias: Turn OFF entities after the timer helper is finished + conditions: + - condition: trigger + id: + - t5 + sequence: + - alias: Turn OFF the entities + service: homeassistant.turn_off + target: !input entity_input diff --git a/blueprints/automation/Drafted/aqara-buttons-all-in-one-zigbee2mqtt.yaml b/blueprints/automation/Drafted/aqara-buttons-all-in-one-zigbee2mqtt.yaml new file mode 100644 index 0000000..1cb6be9 --- /dev/null +++ b/blueprints/automation/Drafted/aqara-buttons-all-in-one-zigbee2mqtt.yaml @@ -0,0 +1,251 @@ +blueprint: + name: Aqara Buttons All-In-One [Zigbee2MQTT] + description: 'All-In-One package for Aqara Switches. + + Supports: WXKG11LM, WXKG12LM, WXKG03LM, WXKG06LM, WXKG07LM, WXKG02LM, QBKG03LM, + QBKG04LM, QBKG11LM, QBKG12LM, QBKG21LM, QBKG22LM, QBKG25LM, etc..). + + Does not supports Opple. + + Some actions may be unsupported by your switch. Check the available actions of + your device on zigbee2mqtt.io. + + ' + domain: automation + input: + action_sensor: + name: Action Sensor + description: Zigbee2mqtt action sensor + selector: + entity: + integration: mqtt + domain: + - sensor + multiple: false + single_action: + name: Single Click + description: Single click for devices with one button + default: [] + selector: + action: {} + double_action: + name: Double Click + description: Double click for devices with one button + default: [] + selector: + action: {} + triple_action: + name: Triple Click + description: Triple click for devices with one button + default: [] + selector: + action: {} + quadruple_action: + name: Quadruple Click + description: Triple click for devices with one button + default: [] + selector: + action: {} + hold_action: + name: Hold Action + description: Hold action for devices with one button + default: [] + selector: + action: {} + release_action: + name: Release Action + description: Release action for devices with one button + default: [] + selector: + action: {} + shake_action: + name: Shake Action + description: Shake action for devices with one button + default: [] + selector: + action: {} + single_left_action: + name: Single Left Click + description: Left click for devices with multiple buttons + default: [] + selector: + action: {} + double_left_action: + name: Double Left Click + description: Double left click for devices with multiple buttons + default: [] + selector: + action: {} + triple_left_action: + name: Triple Left Click + description: Triple left click for devices with multiple buttons + default: [] + selector: + action: {} + hold_left_action: + name: Hold Left Action + description: Hold left action for devices with multiple buttons + default: [] + selector: + action: {} + release_left_action: + name: Release Left Action + description: Release left action for devices with multiple buttons + default: [] + selector: + action: {} + single_center_action: + name: Single Center Click + description: Center click for devices with three buttons + default: [] + selector: + action: {} + double_center_action: + name: Double Center Click + description: Double center click for devices with three buttons + default: [] + selector: + action: {} + triple_center_action: + name: Triple Center Click + description: Triple center click for devices with three buttons + default: [] + selector: + action: {} + hold_center_action: + name: Hold Center Action + description: Hold center action for devices with three buttons + default: [] + selector: + action: {} + release_center_action: + name: Release Center Action + description: Release center action for devices with three buttons + default: [] + selector: + action: {} + single_right_action: + name: Single Right Click + description: Right click for devices with multiple buttons + default: [] + selector: + action: {} + double_right_action: + name: Double Right Click + description: Double right click for devices with multiple buttons + default: [] + selector: + action: {} + triple_right_action: + name: Triple Right Click + description: Triple right click for devices with multiple buttons + default: [] + selector: + action: {} + hold_right_action: + name: Hold Right Action + description: Hold right action for devices with multiple buttons + default: [] + selector: + action: {} + release_right_action: + name: Release Right Action + description: Release right action for devices with multiple buttons + default: [] + selector: + action: {} + single_both_action: + name: Single Both Click + description: Both click for devices with multiple buttons + default: [] + selector: + action: {} + double_both_action: + name: Double Both Click + description: Double both click for devices with multiple buttons + default: [] + selector: + action: {} + triple_both_action: + name: Triple Both Click + description: Triple both click for devices with multiple buttons + default: [] + selector: + action: {} + hold_both_action: + name: Hold Both Action + description: Hold both action for devices with multiple buttons + default: [] + selector: + action: {} + release_both_action: + name: Release Both Action + description: Release both action for devices with multiple buttons + default: [] + selector: + action: {} + source_url: https://community.home-assistant.io/t/aqara-buttons-all-in-one-zigbee2mqtt/268717 +mode: parallel +max_exceeded: silent +trigger: + platform: state + entity_id: !input action_sensor +action: +- variables: + command: '{{ trigger.to_state.attributes.action if trigger.to_state.state == "" + else trigger.to_state.state }}' +- choose: + - conditions: '{{ command == ''single'' }}' + sequence: !input single_action + - conditions: '{{ command == ''double'' }}' + sequence: !input double_action + - conditions: '{{ command == ''triple'' }}' + sequence: !input triple_action + - conditions: '{{ command == ''quadruple'' }}' + sequence: !input quadruple_action + - conditions: '{{ command == ''hold'' }}' + sequence: !input hold_action + - conditions: '{{ command == ''release'' }}' + sequence: !input release_action + - conditions: '{{ command == ''shake'' }}' + sequence: !input shake_action + - conditions: '{{ command == ''single_left'' }}' + sequence: !input single_left_action + - conditions: '{{ command == ''double_left'' }}' + sequence: !input double_left_action + - conditions: '{{ command == ''triple_left'' }}' + sequence: !input triple_left_action + - conditions: '{{ command == ''hold_left'' }}' + sequence: !input hold_left_action + - conditions: '{{ command == ''release_left'' }}' + sequence: !input release_left_action + - conditions: '{{ command == ''single_center'' }}' + sequence: !input single_center_action + - conditions: '{{ command == ''double_center'' }}' + sequence: !input double_center_action + - conditions: '{{ command == ''triple_center'' }}' + sequence: !input triple_center_action + - conditions: '{{ command == ''hold_center'' }}' + sequence: !input hold_center_action + - conditions: '{{ command == ''release_center'' }}' + sequence: !input release_center_action + - conditions: '{{ command == ''single_right'' }}' + sequence: !input single_right_action + - conditions: '{{ command == ''double_right'' }}' + sequence: !input double_right_action + - conditions: '{{ command == ''triple_right'' }}' + sequence: !input triple_right_action + - conditions: '{{ command == ''hold_right'' }}' + sequence: !input hold_right_action + - conditions: '{{ command == ''release_right'' }}' + sequence: !input release_right_action + - conditions: '{{ command == ''single_both'' }}' + sequence: !input single_both_action + - conditions: '{{ command == ''double_both'' }}' + sequence: !input double_both_action + - conditions: '{{ command == ''triple_both'' }}' + sequence: !input triple_both_action + - conditions: '{{ command == ''hold_both'' }}' + sequence: !input hold_both_action + - conditions: '{{ command == ''release_both'' }}' + sequence: !input release_both_action diff --git a/blueprints/automation/EPMatt/light.yaml b/blueprints/automation/EPMatt/light.yaml new file mode 100644 index 0000000..6142cba --- /dev/null +++ b/blueprints/automation/EPMatt/light.yaml @@ -0,0 +1,628 @@ +blueprint: + name: Hook - Light + description: "# Hook - Light\n\nHook automation which allows to control a light. + Supports brightness and color control both for white temperature and rgb lights. + Features may vary depending on the selected Controller.\n\nAn automation created + with this blueprint must be linked to a [Controller](https://epmatt.github.io/awesome-ha-blueprints/blueprints/controllers) + automation.\nControllers are blueprints which allow to easily integrate a wide + range of controllers (wall switches, remotes, dimmers, etc.) and use them to run + a set of actions when interacting with them.\nThey expose an abstract interface + used by Hooks to create controller-based automations.\n\nA list of controllers + supported by this hook is available [here](https://epmatt.github.io/awesome-ha-blueprints/docs/blueprints/hooks/light#supported-controllers).\n\n\U0001F4D5 + Full documentation regarding this blueprint is available [here](https://epmatt.github.io/awesome-ha-blueprints/docs/blueprints/hooks/light).\n\n\U0001F680 + This blueprint is part of the **[Awesome HA Blueprints](https://epmatt.github.io/awesome-ha-blueprints) + project**.\n\nℹ️ Version 2022.07.30\n" + source_url: https://raw.githubusercontent.com/EPMatt/awesome-ha-blueprints/main/blueprints/hooks/light/light.yaml + domain: automation + input: + controller_device: + name: (Required) Controller Device + description: The controller device which will control the Light. Choose a value + only if the integration used to connect the controller to Home Assistant exposes + it as a Device. This value should match the one specified in the corresponding + Controller automation. + default: '' + selector: + device: {} + controller_entity: + name: (Required) Controller Entity + description: The controller entity which will control the Light. Choose a value + only if the integration used to connect the controller to Home Assistant exposes + it as an Entity. This value should match the one specified in the corresponding + Controller automation. + default: '' + selector: + entity: + domain: + - sensor + multiple: false + controller_model: + name: (Required) Controller model + description: The model for the controller used in this automation. Choose a + value from the list of supported controllers. + selector: + select: + options: + - IKEA E1524/E1810 TRÅDFRI Wireless 5-Button Remote + - IKEA E1743 TRÅDFRI On/Off Switch & Dimmer + - IKEA E1743 TRÅDFRI On/Off Switch & Dimmer (#2) + - IKEA E1744 SYMFONISK Rotary Remote + - IKEA E1766 TRÅDFRI Open/Close Remote + - IKEA E1766 TRÅDFRI Open/Close Remote (#2) + - IKEA E1812 TRÅDFRI Shortcut button + - IKEA E2001/E2002 STYRBAR Remote control + - IKEA E2001/E2002 STYRBAR Remote control (#2) + - IKEA ICTC-G-1 TRÅDFRI wireless dimmer + - OSRAM AC025XX00NJ SMART+ Switch Mini + - Philips 324131092621 Hue Dimmer switch + - Philips 8718699693985 Hue Smart Button + - Philips 929002398602 Hue Dimmer switch v2 + - SONOFF SNZB-01 Wireless Switch + - Xiaomi WXCJKG11LM Aqara Opple 2 button remote + - Xiaomi WXCJKG12LM Aqara Opple 4 button remote + - Xiaomi WXCJKG12LM Aqara Opple 4 button remote (#2) + - Xiaomi WXCJKG13LM Aqara Opple 6 button remote + - Xiaomi WXCJKG13LM Aqara Opple 6 button remote (#2) + - Xiaomi WXCJKG13LM Aqara Opple 6 button remote (#3) + - Xiaomi WXKG11LM Aqara Wireless Switch Mini + sort: false + custom_value: false + multiple: false + light: + name: (Required) Light + description: Light which will be controlled with this automation. + selector: + entity: + domain: + - light + multiple: false + light_color_mode: + name: (Optional) Light color mode + description: Specify how the controller will set the light color. Choose "Color + Temperature" and "Hue - Saturation" depending on the features supported by + your light. If you are not sure you can select "Auto". "None" will disable + color control features. + default: Auto + selector: + select: + options: + - Auto + - Color Temperature + - Hue - Saturation + - None + sort: false + custom_value: false + multiple: false + light_transition: + name: (Optional) Light Transition + description: Number that represents the time (in milliseconds) the light should + take turn on or off, if the light supports it. + default: 250 + selector: + number: + min: 0.0 + max: 60000.0 + step: 1.0 + unit_of_measurement: milliseconds + mode: box + min_brightness: + name: (Optional) Light minimum brightness + description: The minimum brightness the light can be set with this automation. + default: 1 + selector: + number: + min: 1.0 + max: 255.0 + step: 1.0 + unit_of_measurement: brightness + mode: slider + max_brightness: + name: (Optional) Light maximum brightness + description: The maximum brightness the light can be set with this automation. + default: 255 + selector: + number: + min: 0.0 + max: 255.0 + step: 1.0 + unit_of_measurement: brightness + mode: slider + brightness_steps_short: + name: (Optional) Light brightness steps - short actions + description: Number of steps from min to max brightness when controlling brightness + with short actions (eg. button press). + default: 10 + selector: + number: + min: 1.0 + max: 255.0 + step: 1.0 + unit_of_measurement: steps + mode: box + brightness_steps_long: + name: (Optional) Light brightness steps - long actions + description: Number of steps from min to max brightness when controlling brightness + with long actions (eg. button hold or controller rotation). + default: 10 + selector: + number: + min: 1.0 + max: 255.0 + step: 1.0 + unit_of_measurement: steps + mode: box + force_brightness: + name: (Optional) Force brightness value at turn on + description: Force brightness to the "On brightness" input value, when the light + is being turned on. + default: false + selector: + boolean: {} + on_brightness: + name: (Optional) On brightness + description: Brightness value to force when turning on the light + default: 1 + selector: + number: + min: 0.0 + max: 255.0 + step: 1.0 + unit_of_measurement: brightness + mode: slider + smooth_power_on: + name: (Optional) Smooth power on + description: Force the light to turn on at minimum brightness when a brightness + up command (single or continuous) is triggered and light is off. + default: true + selector: + boolean: {} + smooth_power_off: + name: (Optional) Smooth power off + description: Allow a brightness down command (single or continuous) to turn + off the light when at minimum brightness. Disabling this will prevent the + light from being turned off by brightness down commands. + default: true + selector: + boolean: {} +variables: + controller_model: !input controller_model + controller_mapping: + IKEA E1524/E1810 TRÅDFRI Wireless 5-Button Remote: + brightness_up: button_up_short + brightness_up_repeat: button_up_long + brightness_down: button_down_short + brightness_down_repeat: button_down_long + color_down: button_left_short + color_down_repeat: button_left_long + color_up: button_right_short + color_up_repeat: button_right_long + toggle: button_center_short + IKEA E1743 TRÅDFRI On/Off Switch & Dimmer: + turn_on: button_up_short + brightness_up_repeat: button_up_long + color_up: button_up_double + turn_off: button_down_short + brightness_down_repeat: button_down_long + color_down: button_down_double + IKEA E1743 TRÅDFRI On/Off Switch & Dimmer (#2): + brightness_up: button_up_short + brightness_up_repeat: button_up_long + turn_on: button_up_double + brightness_down: button_down_short + brightness_down_repeat: button_down_long + turn_off: button_down_double + IKEA E1744 SYMFONISK Rotary Remote: + brightness_down_repeat: rotate_left + brightness_up_repeat: rotate_right + toggle: click_short + color_up: click_double + color_down: click_triple + IKEA E1766 TRÅDFRI Open/Close Remote: + brightness_up: button_up_short + turn_on: button_up_double + brightness_down: button_down_short + turn_off: button_down_double + IKEA E1766 TRÅDFRI Open/Close Remote (#2): + brightness_up: button_up_short + color_up: button_up_double + brightness_down: button_down_short + color_down: button_down_double + IKEA E1812 TRÅDFRI Shortcut button: + toggle: button_short + color_up: button_double + IKEA E2001/E2002 STYRBAR Remote control: + turn_on: button_up_short + brightness_up_repeat: button_up_long + turn_off: button_down_short + brightness_down_repeat: button_down_long + color_down: button_left_short + color_down_repeat: button_left_long + color_up: button_right_short + color_up_repeat: button_right_long + IKEA E2001/E2002 STYRBAR Remote control (#2): + brightness_up: button_up_short + brightness_up_repeat: button_up_long + turn_on: button_up_double + brightness_down: button_down_short + brightness_down_repeat: button_down_long + turn_off: button_down_double + color_down: button_left_short + color_down_repeat: button_left_long + color_up: button_right_short + color_up_repeat: button_right_long + IKEA ICTC-G-1 TRÅDFRI wireless dimmer: + brightness_down_repeat: rotate_left + brightness_up_repeat: rotate_right + OSRAM AC025XX00NJ SMART+ Switch Mini: + brightness_up: button_up_short + brightness_up_repeat: button_up_long + color_up: button_up_double + brightness_down: button_down_short + brightness_down_repeat: button_down_long + color_down: button_down_double + toggle: button_center_short + Philips 324131092621 Hue Dimmer switch: + turn_on: button_on_short + color_up_repeat: button_on_long + turn_off: button_off_short + color_down_repeat: button_off_long + brightness_up: button_up_short + brightness_up_repeat: button_up_long + brightness_down: button_down_short + brightness_down_repeat: button_down_long + Philips 8718699693985 Hue Smart Button: + toggle: button_short + Philips 929002398602 Hue Dimmer switch v2: + turn_on: button_on_short + color_up_repeat: button_on_long + turn_off: button_off_short + color_down_repeat: button_off_long + brightness_up: button_up_short + brightness_up_repeat: button_up_long + brightness_down: button_down_short + brightness_down_repeat: button_down_long + SONOFF SNZB-01 Wireless Switch: + toggle: button_short + color_up: button_double + Xiaomi WXCJKG11LM Aqara Opple 2 button remote: + turn_on: button_1_short + brightness_up_repeat: button_1_long + color_up: button_1_double + turn_off: button_2_short + brightness_down_repeat: button_2_long + color_down: button_2_double + Xiaomi WXCJKG12LM Aqara Opple 4 button remote: + turn_on: button_1_short + brightness_up_repeat: button_1_long + color_up: button_1_double + turn_off: button_2_short + brightness_down_repeat: button_2_long + color_down: button_2_double + Xiaomi WXCJKG12LM Aqara Opple 4 button remote (#2): + turn_on: button_3_short + brightness_up_repeat: button_3_long + color_up: button_3_double + turn_off: button_4_short + brightness_down_repeat: button_4_long + color_down: button_4_double + Xiaomi WXCJKG13LM Aqara Opple 6 button remote: + turn_on: button_1_short + brightness_up_repeat: button_1_long + color_up: button_1_double + turn_off: button_2_short + brightness_down_repeat: button_2_long + color_down: button_2_double + Xiaomi WXCJKG13LM Aqara Opple 6 button remote (#2): + turn_on: button_3_short + brightness_up_repeat: button_3_long + color_up: button_3_double + turn_off: button_4_short + brightness_down_repeat: button_4_long + color_down: button_4_double + Xiaomi WXCJKG13LM Aqara Opple 6 button remote (#3): + turn_on: button_5_short + brightness_up_repeat: button_5_long + color_up: button_5_double + turn_off: button_6_short + brightness_down_repeat: button_6_long + color_down: button_6_double + Xiaomi WXKG11LM Aqara Wireless Switch Mini: + toggle: button_short + color_up: button_double + brightness_up: '{{ controller_mapping[controller_model]["brightness_up"] | default(None) + }}' + brightness_up_repeat: '{{ controller_mapping[controller_model]["brightness_up_repeat"] + | default(None) }}' + brightness_down: '{{ controller_mapping[controller_model]["brightness_down"] | default(None) + }}' + brightness_down_repeat: '{{ controller_mapping[controller_model]["brightness_down_repeat"] + | default(None) }}' + color_up: '{{ controller_mapping[controller_model]["color_up"] | default(None) }}' + color_up_repeat: '{{ controller_mapping[controller_model]["color_up_repeat"] | default(None) + }}' + color_down: '{{ controller_mapping[controller_model]["color_down"] | default(None) + }}' + color_down_repeat: '{{ controller_mapping[controller_model]["color_down_repeat"] + | default(None) }}' + toggle: '{{ controller_mapping[controller_model]["toggle"] | default(None) }}' + turn_on: '{{ controller_mapping[controller_model]["turn_on"] | default(None) }}' + turn_off: '{{ controller_mapping[controller_model]["turn_off"] | default(None) }}' + light_color_mode: !input light_color_mode + light: !input light + light_transition: !input light_transition + min_brightness: !input min_brightness + max_brightness: !input max_brightness + brightness_steps_short: !input brightness_steps_short + brightness_steps_long: !input brightness_steps_long + force_brightness: !input force_brightness + on_brightness: !input on_brightness + smooth_power_on: !input smooth_power_on + smooth_power_off: !input smooth_power_off + color_modes: + Auto: auto + Color Temperature: color_temp + Hue - Saturation: hs_color + None: none + light_color_mode_id: '{%- if light_color_mode == "Auto" -%} {% set supported_color_modes + = state_attr(light, "supported_color_modes") -%} {%- if "hs" in supported_color_modes + or "xy" in supported_color_modes or "rgbw" in supported_color_modes or "rgbww" + in supported_color_modes -%} {{ color_modes["Hue - Saturation"] }} {%- elif "color_temp" + in supported_color_modes -%} {{ color_modes["Color Temperature"] }} {%- else -%} + {{ color_modes["None"] }} {%- endif -%} {%- else -%} {{ color_modes[light_color_mode] + }} {%- endif -%}' + step_short: '{{ (max_brightness-min_brightness)/brightness_steps_short }}' + step_long: '{{ (max_brightness-min_brightness)/brightness_steps_long }}' +mode: restart +max_exceeded: silent +trigger: +- platform: event + event_type: ahb_controller_event + event_data: + controller: !input controller_device +- platform: event + event_type: ahb_controller_event + event_data: + controller: !input controller_entity +condition: [] +action: +- variables: + action: '{{ trigger.event.data.action }}' +- choose: + - conditions: '{{ action == toggle }}' + sequence: + - choose: + - conditions: '{{ force_brightness }}' + sequence: + - service: light.toggle + entity_id: !input light + data: + brightness: !input on_brightness + transition: '{{ light_transition / 1000 }}' + default: + - service: light.toggle + entity_id: !input light + data: + transition: '{{ light_transition / 1000 }}' + - conditions: '{{ action == turn_on }}' + sequence: + - choose: + - conditions: '{{ force_brightness }}' + sequence: + - service: light.turn_on + entity_id: !input light + data: + brightness: !input on_brightness + transition: '{{ light_transition / 1000 }}' + default: + - service: light.turn_on + entity_id: !input light + data: + transition: '{{ light_transition / 1000 }}' + - conditions: '{{ action == turn_off }}' + sequence: + - service: light.turn_off + entity_id: !input light + data: + transition: '{{ light_transition / 1000 }}' + - conditions: '{{ action == brightness_up }}' + sequence: + - choose: + - conditions: '{{ states(light) == "off" and not smooth_power_on}}' + sequence: + - service: light.turn_on + data: + transition: '{{ light_transition / 1000 }}' + entity_id: !input light + - conditions: '{{ states(light) == "off" and smooth_power_on}}' + sequence: + - service: light.turn_on + data: + brightness: '{{ min_brightness }}' + transition: '{{ light_transition / 1000 }}' + entity_id: !input light + default: + - service: light.turn_on + data: + brightness: '{{ [ [state_attr(light,"brightness")+step_short, min_brightness] + | max, max_brightness] | min }}' + transition: 0.25 + entity_id: !input light + - conditions: '{{ action == brightness_down and states(light) == "on" }}' + sequence: + - choose: + - conditions: '{{ smooth_power_off and state_attr(light,"brightness") == min_brightness + }}' + sequence: + - service: light.turn_off + data: + transition: '{{ light_transition / 1000 }}' + entity_id: !input light + default: + - service: light.turn_on + data: + brightness: '{{ [ [state_attr(light,"brightness")-step_short, min_brightness] + | max, max_brightness] | min }}' + transition: 0.25 + entity_id: !input light + - conditions: '{{ action == brightness_up_repeat }}' + sequence: + - choose: + - conditions: '{{ states(light) == "off" and not smooth_power_on}}' + sequence: + - service: light.turn_on + data: + transition: '{{ light_transition / 1000 }}' + entity_id: !input light + - delay: + milliseconds: !input light_transition + - conditions: '{{ states(light) == "off" and smooth_power_on}}' + sequence: + - service: light.turn_on + data: + brightness: '{{ min_brightness }}' + transition: '{{ light_transition / 1000 }}' + entity_id: !input light + - delay: + milliseconds: !input light_transition + - repeat: + while: '{{ true }}' + sequence: + - service: light.turn_on + data: + brightness: '{{ [ [state_attr(light,"brightness")+step_long, min_brightness] + | max, max_brightness] | min }}' + transition: 0.25 + entity_id: !input light + - delay: + milliseconds: 250 + - conditions: '{{ action == brightness_down_repeat and states(light) == "on" }}' + sequence: + - choose: + - conditions: '{{ smooth_power_off }}' + sequence: + - repeat: + while: '{{ states(light) != "off" }}' + sequence: + - choose: + - conditions: '{{ state_attr(light,"brightness") == min_brightness }}' + sequence: + - service: light.turn_off + data: + transition: '{{ light_transition / 1000 }}' + entity_id: !input light + - delay: + milliseconds: !input light_transition + default: + - service: light.turn_on + data: + brightness: '{{ [ [state_attr(light,"brightness")-step_long, min_brightness] + | max, max_brightness] | min }}' + transition: 0.25 + entity_id: !input light + - delay: + milliseconds: 250 + default: + - repeat: + while: '{{ states(light) != "off" }}' + sequence: + - service: light.turn_on + data: + brightness: '{{ [ [state_attr(light,"brightness")-step_long, min_brightness] + | max, max_brightness] | min }}' + transition: 0.25 + entity_id: !input light + - delay: + milliseconds: 250 + - conditions: '{{ action == color_up and light_color_mode_id != "none" }}' + sequence: + choose: + - conditions: '{{ light_color_mode_id == "color_temp" }}' + sequence: + - service: light.turn_on + data: + color_temp: '{{ state_attr(light,"color_temp")|int + 50 }}' + transition: 0.25 + entity_id: !input light + - conditions: '{{ light_color_mode_id == "hs_color" }}' + sequence: + - service: light.turn_on + data: + hs_color: '{{ [((state_attr(light,"hs_color")[0] or 0) + 15) % 360, 100] + }}' + transition: 0.25 + entity_id: !input light + - conditions: '{{ action == color_down and light_color_mode_id != "none" }}' + sequence: + choose: + - conditions: '{{ light_color_mode_id == "color_temp" }}' + sequence: + - service: light.turn_on + data: + color_temp: '{{ [state_attr(light,"color_temp")|int - 50, 1]|max }}' + transition: 0.25 + entity_id: !input light + - conditions: '{{ light_color_mode_id == "hs_color" }}' + sequence: + - service: light.turn_on + data: + hs_color: '{{ [((state_attr(light,"hs_color")[0] or 0) - 15) % 360, 100] + }}' + transition: 0.25 + entity_id: !input light + - conditions: '{{ action == color_up_repeat and light_color_mode_id != "none" }}' + sequence: + choose: + - conditions: '{{ light_color_mode_id == "color_temp" }}' + sequence: + - repeat: + while: '{{ true }}' + sequence: + - service: light.turn_on + data: + color_temp: '{{ state_attr(light,"color_temp")|int + 50 }}' + transition: 0.25 + entity_id: !input light + - delay: + milliseconds: 250 + - conditions: '{{ light_color_mode_id == "hs_color" }}' + sequence: + - repeat: + while: '{{ true }}' + sequence: + - service: light.turn_on + data: + hs_color: '{{ [((state_attr(light,"hs_color")[0] or 0) + 15) % 360, + 100] }}' + transition: 0.25 + entity_id: !input light + - delay: + milliseconds: 250 + - conditions: '{{ action == color_down_repeat and light_color_mode_id != "none" + }}' + sequence: + choose: + - conditions: '{{ light_color_mode_id == "color_temp" }}' + sequence: + - repeat: + while: '{{ true }}' + sequence: + - service: light.turn_on + data: + color_temp: '{{ [state_attr(light,"color_temp")|int - 50, 1]|max }}' + transition: 0.25 + entity_id: !input light + - delay: + milliseconds: 250 + - conditions: '{{ light_color_mode_id == "hs_color" }}' + sequence: + - repeat: + while: '{{ true }}' + sequence: + - service: light.turn_on + data: + hs_color: '{{ [((state_attr(light,"hs_color")[0] or 0) - 15) % 360, + 100] }}' + transition: 0.25 + entity_id: !input light + - delay: + milliseconds: 250 diff --git a/blueprints/automation/Tahutipai/report_zigbee_zwave_devices_gone_offline.yaml b/blueprints/automation/Tahutipai/report_zigbee_zwave_devices_gone_offline.yaml new file mode 100644 index 0000000..4170057 --- /dev/null +++ b/blueprints/automation/Tahutipai/report_zigbee_zwave_devices_gone_offline.yaml @@ -0,0 +1,67 @@ +blueprint: + name: Report offline zigbee/zwave/battery/smart plug devices + description: Works with Smart Plugs, ZWave, Zigbee etc (Works with ZHA & Z2M) + domain: automation + input: + time: + name: Time to test on + description: Test is run at configured time + default: '10:00:00' + selector: + time: {} + day: + name: Weekday to test on + description: 'Test is run at configured time either everyday (0) or on a given + weekday (1: Monday ... 7: Sunday)' + default: 0 + selector: + number: + min: 0.0 + max: 7.0 + mode: slider + step: 1.0 + exclude: + name: Excluded Sensors + description: Battery sensors (e.g. smartphone) to exclude from detection. Only + entities are supported, devices must be expanded! + default: + entity_id: [] + selector: + target: + entity: + - device_class: + - battery + - switch + actions: + name: Actions + description: Call your notification here. {{offline_devices}} will replaced + with the name of any offline devices + selector: + action: {} + source_url: https://gist.github.com/Tahutipai/971bf0e07e50ce6190e0dacd73262e2e +variables: + day: !input day + exclude: !input exclude + offline_devices: "{% set result = namespace(offline_devices=[]) %} {% for sensor + in states.sensor | selectattr('attributes.device_class', 'defined') | selectattr('attributes.device_class', + '==', 'battery') %}\n {% if \"unavailable\" in sensor | string and not sensor.entity_id + in exclude.entity_id %}\n {% set result.offline_devices = result.offline_devices + + [device_attr(device_id(sensor.entity_id), \"name\")] %}\n {% endif %}\n{% endfor + %} {% for binary_sensor in states.binary_sensor | selectattr('attributes.device_class', + 'defined') | selectattr('attributes.device_class', '==', 'battery') %}\n {% if + \"unavailable\" in binary_sensor | string and not binary_sensor.entity_id in exclude.entity_id + %}\n {% set result.offline_devices = result.offline_devices + [device_attr(device_id(binary_sensor.entity_id), + \"name\")] %}\n {% endif %}\n{% endfor %} {% for switch in states.switch | selectattr('state','eq','unavailable') + %}\n {% if switch.entity_id not in exclude.entity_id %}\n {% set result.offline_devices + = result.offline_devices + [device_attr(device_id(switch.entity_id), \"name\")] + %}\n {% endif %}\n{% endfor %} {{result.offline_devices|sort|unique|join('\\n')}}" +trigger: +- platform: time + at: !input time +condition: +- '{{ offline_devices != '''' and (day | int == 0 or day | int == now().isoweekday()) + }}' +action: +- choose: [] + default: !input actions +mode: single diff --git a/blueprints/automation/edwardtfn/auto_update_scheduled.yaml b/blueprints/automation/edwardtfn/auto_update_scheduled.yaml new file mode 100644 index 0000000..519a6ef --- /dev/null +++ b/blueprints/automation/edwardtfn/auto_update_scheduled.yaml @@ -0,0 +1,932 @@ +blueprint: + name: Home Assistant Auto-update on a schedule base + author: Edward Firmo (https://github.com/edwardtfn) + homeassistant: + min_version: 2024.8.0 + description: '# Home Assistant Auto-update + + Update Home Assistant automatically when a new update is available. + + v2024.9.30.0 + + Attention: + + * Home Assistant may restart automatically as part of the update process. + + * **Use under your own risk!** Please see the discussions on Home Assistant community + around the risks involved on auto-updating the system. + + For questions and suggestions, please use [this thread in Home Assistant Community + portal](https://community.home-assistant.io/t/scheduled-auto-update-for-home-assistant/459281). + + ' + domain: automation + source_url: https://raw.githubusercontent.com/edwardtfn/HomeAssistant-Config/main/blueprints/automation/EdwardTFN/auto_update_scheduled.yaml + input: + when_section: + name: When (Schedule, etc.) + icon: mdi:calendar + description: Select when the updates should happen and when it should be skipped. + collapsed: false + input: + schedule_entity: + name: Schedule entity + description: 'You can create an [Schedule](https://www.home-assistant.io/integrations/schedule) + under [Settings > Devices & Services > Helpers](https://my.home-assistant.io/redirect/config_flow_start/?domain=schedule). + + Note => The schedule windows will define when an update will start. It + is possible that a backup, an update or a restart process finishes after + the schedule window, but new updates won''t stars outside the schedule + windows.' + default: [] + selector: + entity: + multiple: false + domain: + - schedule + reorder: false + schedule_monthday: + name: Earliest day in the month to update Home Assistant (optional) + description: 'Usually a new major version of Home Assistant is available + on the begining of every month. Some people consider those releases as + not stable enough and prefer to avoid those versions, not updating the + system until the mid of the month (day 15). + + Note => If you select a day higher than 28 the updates won''t run every + month. + + ' + default: 1 + selector: + number: + min: 1.0 + max: 31.0 + step: 1.0 + mode: slider + update_out_of_schedule: + name: Out of schedule entities (optional) + description: You can select update entities which will be updated as soon + an update is detected, even if outside the schedule windows. + default: [] + selector: + entity: + multiple: true + domain: + - update + reorder: false + pause_entities: + name: Pause update entities (optional) + description: 'You can select one or more entities to pause the updates. + If any of the selected entities is "On" or "True" the system won''t be + updated on the schedule time. + + You can use this to hold your updates when you have a party at home, or + when you are on vacations and don''t want to be concerned about updates + on Home Assistant.' + default: [] + selector: + entity: + multiple: true + domain: + - input_boolean + - binary_sensor + - switch + reorder: false + what_section: + name: What (optional) + icon: mdi:update + description: Select what should be updated and what shouldn't be updated. + collapsed: true + input: + core_os_update_mode: + name: Update mode for Home Assistant core and OS updates + description: "Please select the update mode for Home Assistant core and + Home Assistan OS updates.\n* All: All updates will be installed.\n* Patches + only:\n ✅ From 2022.11.0 to 2022.11.3 will be updated (patches).\n ❌ + From 2022.11.x to 2022.12.x will be ignored (minor update).\n ❌ From + 2022.12.x to 2023.1.x will be ignored (major update).\n* Don't update: + Ignore any update.\n" + default: all + selector: + select: + multiple: false + options: + - label: All + value: all + - label: Patches only + value: patches + - label: Don't update + value: ignore + sort: false + custom_value: false + firmware_update_mode: + name: Update mode for device's firmware + description: "Please select the update mode for firmware in the devices + connected to Home Assistant (when supported as an HA update).\n* All: + All updates will be installed.\n* Patches only:\n ✅ From 2022.11.0 to + 2022.11.3 will be updated (patches).\n ❌ From 2022.11.x to 2022.12.x + will be ignored (minor update).\n ❌ From 2022.12.x to 2023.1.x will be + ignored (major update).\n* Don't update: Ignore any update.\n" + default: all + selector: + select: + multiple: false + options: + - label: All + value: all + - label: Patches only + value: patches + - label: Don't update + value: ignore + sort: false + custom_value: false + general_update_mode: + name: Update mode for general updates + description: "Please select the update mode for everything that is not a + device's firmware or a core component of Home Assistant.\nThis includes + add-ons, HACS componentes, custom cards, etc.\n* All: All updates will + be installed.\n* Patches only:\n ✅ From 2022.11.0 to 2022.11.3 will be + updated (patches).\n ❌ From 2022.11.x to 2022.12.x will be ignored (minor + update).\n ❌ From 2022.12.x to 2023.1.x will be ignored (major update).\n* + Don't update: Ignore any update.\n" + default: all + selector: + select: + multiple: false + options: + - label: All + value: all + - label: Patches only + value: patches + - label: Don't update + value: ignore + sort: false + custom_value: false + update_exclusions: + name: Exclusions (optional) + description: 'Select the items that should NOT be included on the automated + updates. + + + => Use this if you want to keep some add-on on an specific version and + avoid auto-updates to it.' + default: [] + selector: + entity: + multiple: true + domain: + - update + reorder: false + backup_section: + name: Backup (optional) + icon: mdi:backup-restore + description: Select the back-up options + collapsed: true + input: + backup_bool: + name: Create a full backup before start the updates? + default: true + selector: + boolean: {} + backup_wait_time: + name: Backup wait time + description: 'Select how long the system should wait for the backup to be + completed after it is triggered. + + Please make sure the schedule covers the waiting time, otherwise the update + will never be triggered after a backup.' + default: 60 + selector: + number: + min: 0.0 + max: 3600.0 + step: 10.0 + mode: box + unit_of_measurement: minutes + max_backup_age: + name: Maximum backup age (or zero, to disable) (optional) + description: This requires the [Google Drive Backup](https://github.com/sabeechen/hassio-google-drive-backup) + addon to be installed and configured to automatically backup, and provide + a sensor with the backup states. The setting only checks for the age of + the automatic backups from that addon, not the backups created internally + by this blueprint. + default: + days: 0 + hours: 0 + minutes: 0 + seconds: 0 + selector: + duration: + enable_day: true + actions_section: + name: Actions (optional) + icon: mdi:shoe-print + description: Select actions to run at diferent moments on the update process + collapsed: true + input: + actions_pre_update: + name: Pre-update actions (optional) + description: 'Actions to execute before the backup or any update starts. + + You can use this to send notifications, turn on/off devices or activate + scenes before starting the updates. + + + Note => Please be aware that all actions will run right before the update + process, which can happens over-night. Take this in account when selecting + your actions. + + Note => The variable "\{\{ updates_list \}\}" is available for your actions + and contains the list of pending updates.' + default: [] + selector: + action: {} + actions_pre_restart: + name: Pre-restart actions (optional) + description: 'Actions to execute before the automation requests a restart. + + You can use this to send notifications, turn on/off devices or stopping + automations before restarting Home Assistant. + + + Note => Please be aware that all actions will run right before the restart + process, which can happens over-night. Take this in account when selecting + your actions. + + Note => Note all the restarts will be controlled by this automation, like + when a Core update is installed. These actions might not be executed in + those cases. + + Note => The variable "\{\{ updates_list \}\}" is available for your actions + and contains the list of pending updates.' + default: [] + selector: + action: {} + actions_pos_update: + name: Post-update actions (optional) + description: 'Actions to execute AFTER the update process finishes. + + You can use this to send notifications, turn on/off devices or activate + scenes after applying the updates. + + + Note => Please be aware that all actions will run right after the update + process, which can happens over-night. Take this in account when selecting + your actions. + + Note => The variable "\{\{ updates_list \}\}" is available for your actions + and contains the list of pending updates. + + **IMPORTANT** => Some updates will automatically restart Home Assistant, + causing the automation to interrupt before finishing, preventing the pos-updates + actions to be executed. If you have critical actions to run after an update, + consider including also in another automation based on Home Assistant + start. + + ' + default: [] + selector: + action: {} + general_settings: + name: General settings (optional) + icon: mdi:cog + description: Select additional settings to the update process + collapsed: true + input: + restart_type: + name: Force Home Assistant to restart if required by any update? + description: This won't affect updates where a restart is automatic, but + for updates requiring a manual restart (quite common on HACS) this blueprint + can automatically force a restart by the end of the updates. + default: no-restart + selector: + select: + multiple: false + options: + - label: Don't force a restart + value: no-restart + - label: Restart Home Assistant core + value: core + - label: Restart Home Assistant host + value: host + sort: false + custom_value: false + verbose_logging_bool: + name: Log addon progress with verbosity to the logbook? (optional) + default: false + selector: + boolean: {} +mode: single +max_exceeded: warning +trigger_variables: + input_update_exclusions: !input update_exclusions + input_update_out_of_schedule: !input update_out_of_schedule + updates_available: "{% set updates_available_obj =\n states.update\n | default([])\n + \ | selectattr(\"state\", \"eq\", \"on\")\n | rejectattr(\"entity_id\", \"in\", + input_update_exclusions)\n%} {{\n updates_available_obj | map(attributes=\"entity_id\") + | list\n if (updates_available_obj | list | count | int(0) > 0)\n else []\n}}\n" +trigger: +- id: HA Schedule based + platform: state + entity_id: !input schedule_entity +- id: HA Start + platform: homeassistant + event: start +- id: New update + platform: template + value_template: "{{\n states.update\n | default([])\n | selectattr(\"state\", + \"eq\", \"on\")\n | rejectattr(\"entity_id\", \"in\", input_update_exclusions)\n + \ | list | count | int(0) > 0\n}}" +- id: New day + platform: time + at: 00:00:01 +- id: Automations reloaded + platform: event + event_type: automation_reloaded +- id: Pause entities changed + platform: state + entity_id: !input pause_entities +condition: +- condition: template + value_template: "{{\n states.update\n | default([])\n | selectattr(\"state\", + \"eq\", \"on\")\n | rejectattr(\"entity_id\", \"in\", input_update_exclusions)\n + \ | list | count | int(0) > 0\n}}" +- condition: or + conditions: + - condition: and + conditions: + - condition: template + value_template: '{{ now().day >= ((input_schedule_monthday) | int(0)) }} + + ' + - condition: state + entity_id: !input schedule_entity + state: 'on' + - condition: template + value_template: "{% set ns = namespace(found=false) %} {% for item in input_update_out_of_schedule + %}\n {% if item in updates_available %}\n {% set ns.found = true %}\n {% + break %}\n {% endif %}\n{% endfor %} {{ ns.found }}\n" +- condition: or + conditions: + - '{{ input_pause_entities | default([]) | list | count | int(0) < 1 }}' + - condition: state + entity_id: !input pause_entities + state: 'off' +variables: + input_schedule_monthday: !input schedule_monthday + input_pause_entities: !input pause_entities + temp_input_update_exclusions: !input update_exclusions + input_verbose_logging_bool: !input verbose_logging_bool + input_backup_bool: !input backup_bool + input_restart_type: !input restart_type + temp_max_backup_age: !input max_backup_age + input_max_backup_age_seconds: "{{\n timedelta(\n days=temp_max_backup_age.days,\n + \ hours=temp_max_backup_age.hours,\n minutes=temp_max_backup_age.minutes,\n + \ seconds=temp_max_backup_age.seconds\n ).total_seconds()\n}}\n" + input_core_os_update_mode: !input core_os_update_mode + input_firmware_update_mode: !input firmware_update_mode + input_general_update_mode: !input general_update_mode + updates_list: [] + pending_updates_list: [] +action: +- &id001 + variables: + core_update_entity: "{% for u in integration_entities('hassio') | select('search', + '^update[.]')\n if (device_attr(u, 'identifiers') | first)[1] == 'core' + %}\n {{ u }}\n{% endfor %}\n" + os_update_entity: "{% for u in integration_entities('hassio') | select('search', + '^update[.]')\n if (device_attr(u, 'identifiers') | first)[1] == 'OS' + %}\n {{ u }}\n{% endfor %}\n" + supervisor_update_entity: "{% for u in integration_entities('hassio') | select('search', + '^update[.]')\n if (device_attr(u, 'identifiers') | first)[1] == 'supervisor' + %}\n {{ u }}\n{% endfor %}\n" + firmware_update_entities: "{{ states.update\n | default([])\n | selectattr('attributes.device_class', + 'defined')\n | selectattr('attributes.device_class', 'eq', 'firmware')\n | + map(attribute='entity_id')\n | list\n}}\n" + general_update_entities: "{{ states.update\n | default([])\n | rejectattr('entity_id', + 'in', core_update_entity)\n | rejectattr('entity_id', 'in', os_update_entity)\n + \ | rejectattr('entity_id', 'in', supervisor_update_entity)\n | rejectattr('entity_id', + 'in', firmware_update_entities)\n | map(attribute='entity_id')\n | list\n}}\n" + input_update_exclusions: "{% set exclusions = temp_input_update_exclusions | default([]) + %}\n{% if input_core_os_update_mode == 'ignore' %}\n {% set exclusions = exclusions + + [core_update_entity | default(\"\"), os_update_entity | default(\"\")] %}\n{% + else %}\n {% set core_installed_version = states.update | default([]) | selectattr('entity_id', + 'in', core_update_entity | default(\"\")) | map(attribute='attributes.installed_version') + | list | first | default('') %}\n {% set core_latest_version = states.update + | default([]) | selectattr('entity_id', 'in', core_update_entity | default(\"\")) + | map(attribute='attributes.latest_version') | list | first | default('') %}\n + \ {% set core_version_change = version(core_latest_version) - version(core_installed_version) + %}\n {% set os_installed_version = states.update | default([]) | selectattr('entity_id', + 'in', os_update_entity | default(\"\")) | map(attribute='attributes.installed_version') + | list | first | default('') %}\n {% set os_latest_version = states.update + | default([]) | selectattr('entity_id', 'in', os_update_entity | default(\"\")) + | map(attribute='attributes.latest_version') | list | first | default('') %}\n + \ {% set os_version_change = version(os_latest_version) - version(os_installed_version) + %}\n\n {% if input_core_os_update_mode == 'patches' %}\n {% if core_version_change.major + or core_version_change.minor or (not core_version_change.patch) %}\n {% + set exclusions = exclusions + [core_update_entity | default(\"\")] %}\n {% + endif %}\n {% if os_version_change.major or os_version_change.minor or (not + os_version_change.patch) %}\n {% set exclusions = exclusions + [os_update_entity + | default(\"\")] %}\n {% endif %}\n {% elif input_core_os_update_mode == + 'major_and_minor' %}\n {% if (not core_version_change.major) and (not core_version_change.minor) + %}\n {% set exclusions = exclusions + [core_update_entity | default(\"\")] + %}\n {% endif %}\n {% if (not os_version_change.major) and (not os_version_change.minor) + %}\n {% set exclusions = exclusions + [os_update_entity | default(\"\")] + %}\n {% endif %}\n {% endif %}\n{% endif %}\n{% if input_firmware_update_mode + == 'ignore' %}\n {% set exclusions = exclusions + [ firmware_update_entities + ] %}\n{% else %}\n {% set ns = namespace(entities = []) %}\n {% for entity + in expand(firmware_update_entities) | rejectattr('entity_id', 'in', exclusions) + | selectattr('state', 'eq', 'on') %}\n {% set entity_version_change = version(entity.attributes.latest_version + | default('')) - version(entity.attributes.installed_version | default('')) + %}\n {% if input_firmware_update_mode == 'patches' %}\n {% if entity_version_change.major + or entity_version_change.minor or (not entity_version_change.patch) %}\n {% + set ns.entities = ns.entities + [ entity.entity_id ] %}\n {% endif %}\n + \ {% elif input_firmware_update_mode == 'major_and_minor' %}\n {% if + (not entity_version_change.major) and (not entity_version_change.minor) %}\n + \ {% set ns.entities = ns.entities + [ entity.entity_id ] %}\n {% + endif %}\n {% endif %}\n {% endfor %}\n {% set exclusions = exclusions + + [ ns.entities ] %}\n{% endif %}\n{% if input_general_update_mode == 'ignore' + %}\n {% set exclusions = exclusions + [ general_update_entities ] %}\n{% else + %}\n {% set ns = namespace(entities = []) %}\n {% for entity in expand(general_update_entities) + | rejectattr('entity_id', 'in', exclusions) | selectattr('state', 'eq', 'on') + %}\n {% set entity_version_change = version(entity.attributes.latest_version + | default('')) - version(entity.attributes.installed_version | default('')) + %}\n {% if input_general_update_mode == 'patches' %}\n {% if entity_version_change.major + or entity_version_change.minor or (not entity_version_change.patch) %}\n {% + set ns.entities = ns.entities + [ entity.entity_id ] %}\n {% endif %}\n + \ {% elif input_general_update_mode == 'major_and_minor' %}\n {% if (not + entity_version_change.major) and (not entity_version_change.minor) %}\n {% + set ns.entities = ns.entities + [ entity.entity_id ] %}\n {% endif %}\n + \ {% endif %}\n {% endfor %}\n {% set exclusions = exclusions + [ ns.entities + ] %}\n{% endif %}\n{{ exclusions }}\n" + pending_updates_list: "{{ states.update\n | default([])\n | selectattr('state','eq','on')\n + \ | rejectattr('entity_id', 'in', input_update_exclusions)\n | map(attribute='entity_id')\n + \ | list\n}}\n" +- alias: Refresh update list - update entities + action: homeassistant.update_entity + data: {} + target: + entity_id: '{{ states.update | map(attribute=''entity_id'') | list }}' + continue_on_error: true +- variables: + updates_list: '{{ pending_updates_list }}' +- alias: Re-check conditions + continue_on_error: true + if: "{{\n (\n states.update\n | selectattr('state','eq','on')\n | selectattr('entity_id', + 'in', pending_updates_list)\n | rejectattr('entity_id', 'in', input_update_exclusions)\n + \ | list\n | count\n | int(0)\n ) < 1\n}}\n" + then: + - stop: Nothing to update +- &id002 + if: '{{ input_verbose_logging_bool }}' + then: + - alias: Logbook - variables + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: "Variables:\n input_backup_bool: {{ input_backup_bool }},\n input_restart_type: + {{ input_restart_type }},\n input_verbose_logging_bool: {{ input_verbose_logging_bool + }},\n input_max_backup_age_seconds: {{ input_max_backup_age_seconds }},\n + \ input_schedule_monthday: {{ input_schedule_monthday }},\n input_update_exclusions: + {{ input_update_exclusions }},\n input_core_os_update_mode: {{ input_core_os_update_mode + }},\n input_firmware_update_mode: {{ input_firmware_update_mode }},\n input_general_update_mode: + {{ input_general_update_mode }},\n core_update_entity: {{ core_update_entity + }},\n os_update_entity: {{ os_update_entity }},\n supervisor_update_entity: + {{ supervisor_update_entity }},\n firmware_update_entities: {{ firmware_update_entities + }},\n general_update_entities: {{ general_update_entities }},\n updates_list: + {{ updates_list }}" + continue_on_error: true +- if: '{{ input_verbose_logging_bool }}' + then: + - alias: Logbook - A new update is available + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + domain: update + message: A new update is available for Home Assistant. + - alias: Logbook - List of updates + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + domain: update + message: "List of updates: - {{ states.update\n | selectattr('state','eq','on')\n + \ | selectattr('entity_id', 'in', pending_updates_list)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='name') | list | join('\n\n- + ') }}\n" + - alias: 'Logbook: Running pre-update actions' + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Running pre-update actions... + continue_on_error: true +- alias: Run pre-update actions + continue_on_error: true + choose: + - conditions: + - '{{ true }}' + sequence: !input actions_pre_update + default: [] +- alias: Check existing backups uploaded + continue_on_error: true + if: + - '{{ input_max_backup_age_seconds > 0 }}' + then: + - variables: + last_backup_timestamp_list: "{{\n states.sensor\n | selectattr(\"attributes.last_backup\", + \"defined\")\n | map(attribute=\"attributes.last_backup\")\n | list\n}}\n" + last_backup_timestamp: '{{ last_backup_timestamp_list | max if last_backup_timestamp_list + | count > 0 else None }}' + - alias: Check if backup state is defined + if: '{{ last_backup_timestamp != None }}' + then: + - stop: Backup State sensor not found + - alias: Check age of last uploaded backup + if: '{{ as_timestamp(now()) - as_timestamp(last_backup_timestamp) > input_max_backup_age_seconds + }}' + then: + - stop: Last uploaded backup is too old +- alias: Backup + continue_on_error: true + if: + - '{{ input_backup_bool }}' + then: + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Logbook - Backing up + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Backing up Home Assistant. + - alias: Call backup service + continue_on_error: true + action: hassio.backup_full + data: + compressed: true + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Backup triggered + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Backup triggered. + - variables: + backup_wait_time: !input backup_wait_time + backup_wait_time_seconds: '{{ (backup_wait_time | int(60)) * 60 }}' + - if: '{{ backup_wait_time_seconds > 0 }}' + then: + - alias: Give selected wait time for the backup + delay: '{{ backup_wait_time_seconds }}' + else: [] +- *id001 +- *id002 +- alias: Update - Standard + continue_on_error: true + repeat: + while: + - '{{ input_general_update_mode in [''patches'', ''major_and_minor'', ''all''] + }}' + - condition: state + entity_id: !input schedule_entity + state: 'on' + - condition: template + value_template: "{{ ( states.update\n | selectattr('state','eq','on')\n | + selectattr('entity_id', 'in', pending_updates_list)\n | rejectattr('entity_id', + 'in', core_update_entity)\n | rejectattr('entity_id', 'in', os_update_entity)\n + \ | rejectattr('entity_id', 'in', firmware_update_entities)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='entity_id')\n | list | + count | int(0) ) > 0\n}}\n" + sequence: + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Update - Standard - Logbook - Starting + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Starting sequence of standard updates... + - variables: + pending_update_list: "{{ states.update\n | selectattr('state','eq','on')\n + \ | selectattr('entity_id', 'in', pending_updates_list)\n | rejectattr('entity_id', + 'in', core_update_entity)\n | rejectattr('entity_id', 'in', os_update_entity)\n + \ | rejectattr('entity_id', 'in', firmware_update_entities)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='entity_id')\n | list\n + \ }}\n" + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Update - Standard - Logbook - Updating + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Updating {{pending_update_list[0]}} of {{pending_update_list}} + ... + - alias: Update - Standard - Install + continue_on_error: true + action: update.install + data: {} + target: + entity_id: '{{ pending_update_list[0] }}' + - alias: Update - Standard - Wait + continue_on_error: true + wait_template: '{{ is_state(pending_update_list[0], ''off'') }}' + continue_on_timeout: true + timeout: '3600' +- *id001 +- *id002 +- alias: Update - Core + continue_on_error: true + repeat: + while: + - '{{ input_core_os_update_mode in [''patches'', ''major_and_minor'', ''all''] + }}' + - condition: state + entity_id: !input schedule_entity + state: 'on' + - condition: template + value_template: "{{ ( states.update\n | selectattr('state','eq','on')\n | + selectattr('entity_id', 'in', pending_updates_list)\n | selectattr('entity_id', + 'in', core_update_entity)\n | rejectattr('entity_id', 'in', os_update_entity)\n + \ | rejectattr('entity_id', 'in', firmware_update_entities)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='entity_id')\n | list | + count | int(0) ) > 0\n}}\n" + sequence: + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Update - Core - Logbook - Starting + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Starting sequence of core items updates... + - variables: + pending_update_list: "{{ states.update\n | selectattr('state','eq','on')\n + \ | selectattr('entity_id', 'in', pending_updates_list)\n | selectattr('entity_id', + 'in', core_update_entity)\n | rejectattr('entity_id', 'in', os_update_entity)\n + \ | rejectattr('entity_id', 'in', firmware_update_entities)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='entity_id')\n | list\n + \ }}\n" + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Update - Core - Logbook - Updating + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Updating {{pending_update_list[0]}} of {{pending_update_list}} + ... + - alias: Update - Core - Install + continue_on_error: true + action: update.install + data: {} + target: + entity_id: '{{ pending_update_list[0] }}' + - alias: Update - Core - Wait + continue_on_error: true + wait_template: '{{ is_state(pending_update_list[0], ''off'') }}' + continue_on_timeout: true + timeout: '3600' +- *id001 +- *id002 +- alias: Update - OS + continue_on_error: true + repeat: + while: + - '{{ input_core_os_update_mode in [''patches'', ''major_and_minor'', ''all''] + }}' + - condition: state + entity_id: !input schedule_entity + state: 'on' + - condition: template + value_template: "{{ ( states.update\n | selectattr('state','eq','on')\n | + selectattr('entity_id', 'in', pending_updates_list)\n | rejectattr('entity_id', + 'in', core_update_entity)\n | selectattr('entity_id', 'in', os_update_entity)\n + \ | rejectattr('entity_id', 'in', firmware_update_entities)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='entity_id')\n | list | + count | int(0) ) > 0\n}}\n" + sequence: + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Update - OS - Logbook - Starting + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Starting sequence of OS update... + - variables: + pending_update_list: "{{ states.update\n | selectattr('state','eq','on')\n + \ | selectattr('entity_id', 'in', pending_updates_list)\n | rejectattr('entity_id', + 'in', core_update_entity)\n | selectattr('entity_id', 'in', os_update_entity)\n + \ | rejectattr('entity_id', 'in', firmware_update_entities)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='entity_id')\n | list\n + \ }}\n" + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Update - OS - Logbook - Updating + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Updating {{pending_update_list[0]}} of {{pending_update_list}} + ... + - alias: Update - OS - Install + continue_on_error: true + action: update.install + data: {} + target: + entity_id: '{{ pending_update_list[0] }}' + - alias: Update - OS - Wait + continue_on_error: true + wait_template: '{{ is_state(pending_update_list[0], ''off'') }}' + continue_on_timeout: true + timeout: '3600' +- *id001 +- alias: Update - Devices firmware + continue_on_error: true + repeat: + while: + - '{{ input_firmware_update_mode in [''patches'', ''major_and_minor'', ''all''] + }}' + - condition: state + entity_id: !input schedule_entity + state: 'on' + - condition: template + value_template: "{{ ( states.update\n | selectattr('state','eq','on')\n | + selectattr('entity_id', 'in', pending_updates_list)\n | rejectattr('entity_id', + 'in', core_update_entity)\n | rejectattr('entity_id', 'in', os_update_entity)\n + \ | selectattr('entity_id', 'in', firmware_update_entities)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='entity_id')\n | list | + count | int(0) ) > 0\n}}\n" + sequence: + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Update - Devices firmware - Logbook - Starting + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Starting sequence of Devices firmware update... + - variables: + pending_update_list: "{{ states.update\n | selectattr('state','eq','on')\n + \ | selectattr('entity_id', 'in', pending_updates_list)\n | rejectattr('entity_id', + 'in', core_update_entity)\n | rejectattr('entity_id', 'in', os_update_entity)\n + \ | selectattr('entity_id', 'in', firmware_update_entities)\n | map(attribute='entity_id')\n + \ | list\n }}\n" + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Update - Devices firmware - Logbook - Updating + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Updating {{pending_update_list[0]}} of {{pending_update_list}} + ... + - alias: Update - Devices firmware - Install + continue_on_error: true + action: update.install + data: {} + target: + entity_id: '{{ pending_update_list[0] }}' + - alias: Update - Devices firmware - Wait + continue_on_error: true + wait_template: '{{ is_state(pending_update_list[0], ''off'') }}' + continue_on_timeout: true + timeout: '3600' +- *id001 +- *id002 +- if: '{{ input_verbose_logging_bool }}' + then: + - alias: Logbook - Updating + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Finishing update process. + - alias: Logbook - Remaining updates + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: "Remaining updates: - {{ states.update\n | selectattr('state','eq','on')\n + \ | selectattr('entity_id', 'in', pending_updates_list)\n | rejectattr('entity_id', + 'in', input_update_exclusions)\n | map(attribute='name') | list | join('\n\n- + ') }}\n" +- if: '{{ input_verbose_logging_bool }}' + then: + - alias: Restart - Logbook + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: "{{ states.update\n | selectattr('attributes.release_summary', 'defined')\n + \ | selectattr('attributes.release_summary', 'search', \"\")\n + \ | map(attribute='entity_id')\n | list\n | count\n | int(0) }} item(s) + pending a restart:\n- {{ states.update\n | selectattr('attributes.release_summary', + 'defined')\n | selectattr('attributes.release_summary', 'search', \"\")\n | map(attribute='entity_id')\n | list\n | join('\n- + ') }}\n" +- alias: Restart + continue_on_error: true + if: + - "{{ input_restart_type != \"no-restart\" and (\n (\n states.update\n | + selectattr('attributes.release_summary', 'defined')\n | selectattr('attributes.release_summary', + 'search', \"\")\n | map(attribute='entity_id')\n + \ | list | count | int(0)\n ) > 0)\n}}" + then: + - alias: Restart - Check config + continue_on_error: true + action: homeassistant.check_config + - if: + - '{{ states.persistent_notification.invalid_config.state | default(''unavailable'') + == ''notifying'' or states.persistent_notification.homeassistant_check_config.state + | default(''unavailable'') == ''notifying'' }}' + then: + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: 'Logbook: Running pre-restart actions' + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Running pre-restart actions... + - alias: Run pre-restart actions + continue_on_error: true + choose: + - conditions: + - '{{ true }}' + sequence: !input actions_pre_restart + default: [] + - if: '{{ input_verbose_logging_bool }}' + then: + - alias: Restart - Logbook - Skipping restart + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: 'Skipping restart process due to notifications of invalid configurations: + states.persistent_notification.invalid_config.state = {{ states.persistent_notification.invalid_config.state + | default(''unavailable'') }}, states.persistent_notification.homeassistant_check_config.state + = {{ states.persistent_notification.homeassistant_check_config.state | + default(''unavailable'') }} + + ' + else: + - alias: Restart - Logbook - Restarting + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Restarting Home Assistant {{ input_restart_type }}... + - alias: Restart - Call reboot service + continue_on_error: true + action: '{{ "hassio.host_reboot" if input_restart_type == "host" else "homeassistant.restart" + }}' +- if: '{{ input_verbose_logging_bool }}' + then: + - alias: Logbook - Post-actions + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Running post-update actions... +- alias: Run post-update actions + continue_on_error: true + choose: + - conditions: + - '{{ true }}' + sequence: !input actions_pos_update + default: [] +- if: '{{ input_verbose_logging_bool }}' + then: + - alias: Logbook - Done + continue_on_error: true + action: logbook.log + data: + name: Auto-update + entity_id: '{{ this.entity_id }}' + message: Done! diff --git a/blueprints/automation/giannisigalotti/motion-activated-light.yaml b/blueprints/automation/giannisigalotti/motion-activated-light.yaml new file mode 100644 index 0000000..0698b59 --- /dev/null +++ b/blueprints/automation/giannisigalotti/motion-activated-light.yaml @@ -0,0 +1,200 @@ +blueprint: + name: Motion-activated Light extended + description: Turn on a light when a movement occurs + domain: automation + source_url: https://github.com/giannisigalotti/HomeAssistant/blob/main/blueprints/motion-activated-light.yaml + input: + motion_entity: + name: Motion sensor + selector: + entity: + domain: + - binary_sensor + multiple: false + light_target: + name: Light + selector: + target: + entity: + - domain: + - light + no_motion_wait: + name: Wait time + description: + The time to wait before turning off the light after no movement + is detected. + default: 120 + selector: + number: + min: 0.0 + max: 3600.0 + unit_of_measurement: seconds + mode: slider + step: 1.0 + skip_if_light_is_on: + name: + Do not execute if the light is already on (prevent auto-turn-off if the + light was turned on manually). + selector: + boolean: {} + default: false + no_sunset_handling: + name: Disable day/night management? + description: + When you turn this option to off, you can define how many hour + before sunset and after sunshine the automation turn on the light + selector: + boolean: {} + default: false + wheater_handling: + name: Enable meteo managment? + description: + Does it also take weather into account to enable/disable motion + sensors (use the time range only when it's sunny and disable otherwise)? + selector: + boolean: {} + default: false + wheater_entity: + name: Weather status sensor + description: + This sensor is used only with the Meteo managment option to determine + if the weather is sunny or not (useful if you are in a room usually dark in + case of bad weather condition + selector: + entity: + domain: + - weather + multiple: false + default: weather.home + sunset_start_hh: + name: Pre-sunset hours activation + description: + How many hours before sunset does the management activate (negative + numbers indicate a delay)? + selector: + number: + min: 0.0 + max: 12.0 + unit_of_measurement: hours + mode: slider + step: 1.0 + default: 0 + sunset_start_mm: + name: Pre-sunset minutes activation + description: + How many minutes before sunset does the management activate (negative + numbers indicate a delay)? + selector: + number: + min: 0.0 + max: 59.0 + unit_of_measurement: minutes + mode: slider + step: 1.0 + default: 0 + sunrise_start_hh: + name: Post-sunrise hours activation + description: + How many hours after sunrise does the management deactivate (negative + numbers indicate an advance)? + selector: + number: + min: 0.0 + max: 12.0 + unit_of_measurement: hours + mode: slider + step: 1.0 + default: 0 + sunrise_start_mm: + name: Post-sunrise minutes activation + description: + How many minutes after sunrise does the management deactivate (negative + numbers indicate an advance)? + selector: + number: + min: 0.0 + max: 59.0 + unit_of_measurement: minutes + mode: slider + step: 1.0 + default: 0 + timeout: + name: Deactivation timeout? + description: + After how many minutes does the switch turn off automatically (-1 + if no timeout) + selector: + number: + min: -1.0 + max: 60.0 + unit_of_measurement: minutes + mode: slider + step: 1.0 + default: 5 +mode: restart +max_exceeded: silent +trigger: + platform: state + entity_id: !input motion_entity + from: "off" + to: "on" +variables: + dusk_start_hour: !input sunset_start_hh + dusk_start_minute: !input sunset_start_mm + rising_start_hour: !input sunrise_start_hh + rising_start_minute: !input sunrise_start_mm + use_weather: !input wheater_handling + skip_light_on: !input skip_if_light_is_on + target_lights: !input light_target + force_skip: + "{{ skip_light_on and\n (expand(\n (([] if target_lights.device_id + is undefined\n else target_lights.device_id | device_entities\n if + target_lights.device_id is string\n else target_lights.device_id + | map('device_entities') | sum(start=[]) )\n +\n ([] + if target_lights.entity_id is undefined\n else [target_lights.entity_id]\n + \ if target_lights.entity_id is string\n else target_lights.entity_id + ))\n | select('search', '^light\\.') | list\n ) | selectattr('state', + 'eq', 'on') | list | length > 0)\n}}\n" + sun_rising: + "{% if (states.sun.sun.state == \"above_horizon\") %}\n {{ states.sun.sun.last_changed + }}\n{%- else -%}\n {{ states.sun.sun.attributes.next_rising }}\n{%- endif %}\n" + sun_dusk: + "{% if (states.sun.sun.state == \"above_horizon\") %}\n {{ states.sun.sun.attributes.next_dusk + }}\n{%- else -%}\n {{ states.sun.sun.last_changed }}\n{%- endif %}\n" + wheater_sensor: !input wheater_entity + no_sunny_wheater: "{{ states(wheater_sensor) != 'sunny' and use_weather }} + + " +condition: + - condition: and + conditions: + - condition: template + value_template: "{{ not force_skip }}" + - condition: or + conditions: + - condition: template + value_template: !input no_sunset_handling + - condition: template + value_template: "{{ no_sunny_wheater }}" + - condition: and + conditions: + - "{{ as_timestamp(now()) >= ((as_timestamp(sun_dusk) - dusk_start_hour*3600 + - dusk_start_minute*60) )}}" + - "{{ as_timestamp(now()) < ((as_timestamp(sun_rising) + rising_start_hour*3600 + + rising_start_minute*60) )}}" +action: + - service: light.turn_on + target: !input light_target + - wait_for_trigger: + platform: state + entity_id: !input motion_entity + from: "on" + to: "off" + timeout: + hours: 0 + minutes: !input timeout + seconds: 0 + milliseconds: 0 + - delay: !input no_motion_wait + - service: light.turn_off + target: !input light_target diff --git a/blueprints/automation/hectorzin/synchronize_todo_list.yaml b/blueprints/automation/hectorzin/synchronize_todo_list.yaml new file mode 100644 index 0000000..01a918c --- /dev/null +++ b/blueprints/automation/hectorzin/synchronize_todo_list.yaml @@ -0,0 +1,322 @@ +blueprint: + name: Synchronize 2 Home Assistant todo list + description: This blueprint for Home Assistant synchronizes 2 to-do lists. Originally + designed to sync Google Keep lists with the Bring shopping list, its versatility + allows syncing any list. Currently limited to syncing only active items, those + marked as completed are removed from the lists + source_url: https://gist.github.com/hectorzin/53f9811ff2d6a673af9ff3a9801df7af + domain: automation + input: + google_list: + name: List 1 + description: Select your first list to synchronize + selector: + entity: + domain: + - todo + multiple: false + bring_list: + name: List 2 + description: Select your second list to synchronize + selector: + entity: + domain: + - todo + multiple: false + temp_list: + name: Temp List + description: Select the temporary list that is used as the basis of the synchronization + selector: + entity: + domain: + - todo + multiple: false +trigger: +- platform: state + entity_id: + - !input google_list +- platform: state + entity_id: + - !input bring_list +condition: [] +action: +- variables: + google_list: !input google_list + bring_list: !input bring_list + temp_list: !input temp_list +- service: todo.get_items + target: + entity_id: '{{ google_list }}' + data: + status: needs_action + response_variable: google_incompleted +- service: todo.get_items + target: + entity_id: '{{ bring_list }}' + data: + status: needs_action + response_variable: bring_incompleted +- service: todo.get_items + target: + entity_id: '{{ temp_list }}' + data: + status: needs_action + response_variable: temp_incompleted +- variables: + deleted_items: "{%- for temp_item in temp_incompleted[temp_list]['items'] -%}\n + \ {% set ns = namespace(found=false) %}\n {%- for google_item in google_incompleted[google_list]['items'] + -%}\n {%- if google_item.summary|capitalize == temp_item.summary|capitalize + -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- endfor -%}\n + \ {%- if not ns.found -%}\n {{ temp_item.summary }};\n {%- endif -%}\n{%- + endfor -%}\n{%- for temp_item in temp_incompleted[temp_list]['items'] -%}\n + \ {% set ns = namespace(found=false) %}\n {%- for bring_item in bring_incompleted[bring_list]['items'] + -%}\n {%- if bring_item.summary|capitalize == temp_item.summary|capitalize + -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- endfor -%}\n + \ {%- if not ns.found -%}\n {{ temp_item.summary }};\n {%- endif -%}\n{%- + endfor -%}\n" +- variables: + deleted_items: "{% for index in range(deleted_items.split(';')|length-1) -%}\n + \ {%- set item = deleted_items.split(';')[index] -%}\n {%- for google_item + in google_incompleted[google_list]['items'] -%}\n {%- if item|capitalize + == google_item.summary|capitalize -%}\n {{ google_item.summary }};\n {%- + endif -%}\n {%- endfor -%}\n{%- endfor -%}\n{% for index in range(deleted_items.split(';')|length-1) + -%}\n {%- set item = deleted_items.split(';')[index] -%}\n {%- for bring_item + in bring_incompleted[bring_list]['items'] -%}\n {%- if item|capitalize == + bring_item.summary|capitalize -%}\n {{ bring_item.summary }};\n {%- + endif -%}\n {%- endfor -%}\n{%- endfor -%}\n" +- repeat: + for_each: '{{ (deleted_items).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.update_item + target: + entity_id: + - '{{ temp_list }}' + data: + item: '{{ repeat.item }}' + status: completed + - variables: + delete_google: "{% set ns = namespace(found=\"\") %} {%- for google_item in\ngoogle_incompleted[google_list]['items'] + -%}\n {%- if google_item.summary|capitalize == repeat.item|capitalize -%}\n + \ {% set ns.found = google_item.summary %}\n {%- endif -%}\n{%- endfor + -%}\n{%- if ns.found != \"\" -%}\n {{ ns.found }}\n{%- else -%}\n \n{%- + endif -%}\n" + - if: + - condition: template + value_template: '{{ delete_google != "" }}' + then: + - service: todo.remove_item + target: + entity_id: + - '{{ google_list }}' + data: + item: '{{ delete_google }}' + - variables: + delete_bring: "{% set ns = namespace(found=\"\") %}\n{%- for bring_item in + bring_incompleted[bring_list]['items'] -%}\n {%- if bring_item.summary|capitalize + == repeat.item|capitalize -%}\n {% set ns.found = bring_item.summary + %}\n {%- endif -%}\n{%- endfor -%}\n{%- if ns.found != \"\" -%}\n {{ ns.found + }}\n{%- else -%}\n \n{%- endif -%}\n" + - if: + - condition: template + value_template: '{{ delete_bring != "" }}' + then: + - service: todo.update_item + target: + entity_id: + - '{{ bring_list }}' + data: + item: '{{ delete_bring }}' + status: completed +- service: todo.get_items + target: + entity_id: '{{ temp_list }}' + data: + status: needs_action + response_variable: temp_incompleted +- service: todo.get_items + target: + entity_id: '{{ google_list }}' + data: + status: needs_action + response_variable: google_incompleted +- service: todo.get_items + target: + entity_id: '{{ bring_list }}' + data: + status: needs_action + response_variable: bring_incompleted +- variables: + items_to_buy_rep: "{%- for google_item in google_incompleted[google_list]['items'] + -%}\n {% set ns = namespace(found=false) %}\n {%- for temp_item in temp_incompleted[temp_list]['items'] + -%}\n {%- if google_item.summary|capitalize == temp_item.summary|capitalize + -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- endfor -%}\n + \ {%- if not ns.found -%}\n {{ google_item.summary|capitalize }};\n {%- + endif -%}\n{%- endfor -%}\n{%- for bring_item in bring_incompleted[bring_list]['items'] + -%}\n {% set ns = namespace(found=false) %}\n {%- for temp_item in temp_incompleted[temp_list]['items'] + -%}\n {%- if bring_item.summary|capitalize == temp_item.summary|capitalize + -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- endfor -%}\n + \ {%- if not ns.found -%}\n {{ bring_item.summary|capitalize }};\n {%- endif + -%}\n{%- endfor -%}\n" +- variables: + items_to_buy: "{% for index in range(items_to_buy_rep.split(';')|length-1) -%}\n + \ {% set ns = namespace(found=false) %}\n {%- set item = items_to_buy_rep.split(';')[index] + -%}\n {% for index2 in range(index+1,items_to_buy_rep.split(';')|length-1) + -%}\n {%- set item2 = items_to_buy_rep.split(';')[index2] -%}\n {%- if + item == item2 -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- + endfor -%}\n {%- if not ns.found -%}\n {{ item }};\n {%- endif -%}\n{%- + endfor -%}\n" +- service: todo.get_items + target: + entity_id: '{{ temp_list }}' + data: + status: completed + response_variable: temp_completed +- variables: + items_to_delete: "{% for index in range(items_to_buy.split(';')|length-1) -%}\n + \ {% set ns = namespace(found=false) %}\n {%- set item = items_to_buy.split(';')[index] + -%}\n {%- for temp_item in temp_completed[temp_list]['items'] -%}\n {%- + if item|capitalize == temp_item.summary|capitalize -%}\n {{ temp_item.summary + }};\n {%- endif -%}\n {%- endfor -%}\n{%- endfor -%}\n" +- repeat: + for_each: '{{ (items_to_delete).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.remove_item + target: + entity_id: '{{ temp_list }}' + data: + item: '{{ repeat.item }}' +- repeat: + for_each: '{{ (items_to_buy).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.add_item + target: + entity_id: '{{ temp_list }}' + data: + item: '{{ repeat.item }}' +- service: todo.get_items + target: + entity_id: '{{ temp_list }}' + data: + status: needs_action + response_variable: temp_incompleted +- variables: + items_to_bring: "{%- for temp_item in temp_incompleted[temp_list]['items'] -%}\n + \ {% set ns = namespace(found=false) %}\n {%- for bring_item in bring_incompleted[bring_list]['items'] + -%}\n {%- if bring_item.summary|capitalize == temp_item.summary|capitalize + -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- endfor -%}\n + \ {%- if not ns.found -%}\n {{ temp_item.summary|capitalize }};\n {%- endif + -%}\n{%- endfor -%}\n" +- service: todo.get_items + target: + entity_id: '{{ bring_list }}' + data: + status: completed + response_variable: bring_completed +- variables: + items_to_delete: "{% for index in range(items_to_bring.split(';')|length-1) -%}\n + \ {% set ns = namespace(found=false) %}\n {%- set item = items_to_bring.split(';')[index] + -%}\n {%- for bring_item in bring_completed[bring_list]['items'] -%}\n {%- + if item|capitalize == bring_item.summary|capitalize -%}\n {{ bring_item.summary + }};\n {%- endif -%}\n {%- endfor -%}\n{%- endfor -%}\n" +- repeat: + for_each: '{{ (items_to_delete).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.remove_item + target: + entity_id: '{{ bring_list }}' + data: + item: '{{ repeat.item }}' +- repeat: + for_each: '{{ (items_to_bring).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.add_item + target: + entity_id: '{{ bring_list }}' + data: + item: '{{ repeat.item }}' +- variables: + items_delete_bring: "{%- for bring_item in bring_incompleted[bring_list]['items'] + -%}\n {% set ns = namespace(found=false) %}\n {%- for temp_item in temp_incompleted[temp_list]['items'] + -%}\n {%- if bring_item.summary|capitalize == temp_item.summary|capitalize + -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- endfor -%}\n + \ {%- if not ns.found -%}\n {{ bring_item.summary }};\n {%- endif -%}\n{%- + endfor -%}\n" +- repeat: + for_each: '{{ (items_delete_bring).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.update_item + target: + entity_id: '{{ bring_list }}' + data: + item: '{{ repeat.item }}' + status: completed +- variables: + items_to_google: "{%- for temp_item in temp_incompleted[temp_list]['items'] -%}\n + \ {% set ns = namespace(found=false) %}\n {%- for google_item in google_incompleted[google_list]['items'] + -%}\n {%- if google_item.summary|capitalize == temp_item.summary|capitalize + -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- endfor -%}\n + \ {%- if not ns.found -%}\n {{ temp_item.summary|capitalize }};\n {%- endif + -%}\n{%- endfor -%}\n" +- service: todo.get_items + target: + entity_id: '{{ google_list }}' + data: + status: completed + response_variable: google_completed +- variables: + items_to_delete: "{% for index in range(items_to_google.split(';')|length-1) -%}\n + \ {% set ns = namespace(found=false) %}\n {%- set item = items_to_google.split(';')[index] + -%}\n {%- for google_item in google_completed[google_list]['items'] -%}\n {%- + if item|capitalize == google_item.summary|capitalize -%}\n {{ google_item.summary + }};\n {%- endif -%}\n {%- endfor -%}\n{%- endfor -%}\n" +- repeat: + for_each: '{{ (items_to_delete).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.remove_item + target: + entity_id: '{{ google_list }}' + data: + item: '{{ repeat.item }}' +- repeat: + for_each: '{{ (items_to_google).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.add_item + target: + entity_id: '{{ google_list }}' + data: + item: '{{ repeat.item }}' +- variables: + items_delete_google: "{%- for google_item in google_incompleted[google_list]['items'] + -%}\n {% set ns = namespace(found=false) %}\n {%- for temp_item in temp_incompleted[temp_list]['items'] + -%}\n {%- if google_item.summary|capitalize == temp_item.summary|capitalize + -%}\n {% set ns.found = true %}\n {%- endif -%}\n {%- endfor -%}\n + \ {%- if not ns.found -%}\n {{ google_item.summary }};\n {%- endif -%}\n{%- + endfor -%}\n" +- repeat: + for_each: '{{ (items_delete_google).split('';'') | list }}' + sequence: + - condition: template + value_template: '{{ repeat.item != "" }}' + - service: todo.update_item + target: + entity_id: '{{ google_list }}' + data: + item: '{{ repeat.item }}' + status: completed +mode: single diff --git a/blueprints/automation/homeassistant/notify_leaving_zone.yaml b/blueprints/automation/homeassistant/notify_leaving_zone.yaml new file mode 100644 index 0000000..e1e3bd5 --- /dev/null +++ b/blueprints/automation/homeassistant/notify_leaving_zone.yaml @@ -0,0 +1,50 @@ +blueprint: + name: Zone Notification + description: Send a notification to a device when a person leaves a specific zone. + domain: automation + source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/notify_leaving_zone.yaml + author: Home Assistant + input: + person_entity: + name: Person + selector: + entity: + filter: + domain: person + zone_entity: + name: Zone + selector: + entity: + filter: + domain: zone + notify_device: + name: Device to notify + description: Device needs to run the official Home Assistant app to receive notifications. + selector: + device: + filter: + integration: mobile_app + +trigger: + platform: state + entity_id: !input person_entity + +variables: + zone_entity: !input zone_entity + # This is the state of the person when it's in this zone. + zone_state: "{{ states[zone_entity].name }}" + person_entity: !input person_entity + person_name: "{{ states[person_entity].name }}" + +condition: + condition: template + # The first case handles leaving the Home zone which has a special state when zoning called 'home'. + # The second case handles leaving all other zones. + value_template: "{{ zone_entity == 'zone.home' and trigger.from_state.state == 'home' and trigger.to_state.state != 'home' or trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}" + +action: + - alias: "Notify that a person has left the zone" + domain: mobile_app + type: notify + device_id: !input notify_device + message: "{{ person_name }} has left {{ zone_state }}" diff --git a/blueprints/automation/lsismeiro/philips_324131137411.yaml b/blueprints/automation/lsismeiro/philips_324131137411.yaml new file mode 100644 index 0000000..523510e --- /dev/null +++ b/blueprints/automation/lsismeiro/philips_324131137411.yaml @@ -0,0 +1,680 @@ +blueprint: + name: Controller - Philips 324131137411 Hue Dimmer switch + description: "# Controller - Philips 324131137411 Hue Dimmer switch\n\nAutomations + created with this blueprint can be connected with one or more [Hooks](https://epmatt.github.io/awesome-ha-blueprints/docs/blueprints/hooks) + supported by this controller.\nHooks allow to easily create controller-based automations + for interacting with media players, lights, covers and more.\nSee the list of + [Hooks available for this controller](https://epmatt.github.io/awesome-ha-blueprints/docs/blueprints/controllers/philips_324131137411#available-hooks) + for additional details.\n\n\U0001F4D5 Full documentation regarding this blueprint + is available [here](https://epmatt.github.io/awesome-ha-blueprints/docs/blueprints/controllers/philips_324131137411).\n\n\U0001F680 + This blueprint is part of the **[Awesome HA Blueprints](https://epmatt.github.io/awesome-ha-blueprints) + project**.\n\nℹ️ Version 2024.07.10\n" + source_url: https://raw.githubusercontent.com/lsismeiro/awesome-ha-blueprints/main/blueprints/controllers/philips_324131137411/philips_324131137411.yaml + domain: automation + input: + integration: + name: (Required) Integration + description: Integration used for connecting the remote with Home Assistant. + Select one of the available values. + selector: + select: + options: + - deCONZ + - ZHA + - Zigbee2MQTT + sort: false + custom_value: false + multiple: false + controller_device: + name: (deCONZ, ZHA) Controller Device + description: The controller device to use for the automation. Choose a value + only if the remote is integrated with deCONZ, ZHA. + default: '' + selector: + device: {} + controller_entity: + name: (Zigbee2MQTT) Controller Entity + description: The action sensor of the controller to use for the automation. + Choose a value only if the remote is integrated with Zigbee2MQTT. + default: '' + selector: + entity: + domain: + - sensor + multiple: false + helper_last_controller_event: + name: (Required) Helper - Last Controller Event + description: Input Text used to store the last event fired by the controller. + You will need to manually create a text input entity for this, please read + the blueprint Additional Notes for more info. + default: '' + selector: + entity: + domain: + - input_text + multiple: false + action_button_on_short: + name: (Optional) On button short press + description: Action to run on short on button press. + default: [] + selector: + action: {} + action_button_on_long: + name: (Optional) On button long press + description: Action to run on long on button press. + default: [] + selector: + action: {} + action_button_on_release: + name: (Optional) On button release + description: Action to run on on button release after long press. + default: [] + selector: + action: {} + action_button_on_double: + name: (Optional) On button double press + description: Action to run on double on button press. + default: [] + selector: + action: {} + action_button_off_short: + name: (Optional) Off button short press + description: Action to run on short off button press. + default: [] + selector: + action: {} + action_button_off_long: + name: (Optional) Off button long press + description: Action to run on long off button press. + default: [] + selector: + action: {} + action_button_off_release: + name: (Optional) Off button release + description: Action to run on off button release after long press. + default: [] + selector: + action: {} + action_button_off_double: + name: (Optional) Off button double press + description: Action to run on double off button press. + default: [] + selector: + action: {} + action_button_up_short: + name: (Optional) Up button short press + description: Action to run on short up button press. + default: [] + selector: + action: {} + action_button_up_long: + name: (Optional) Up button long press + description: Action to run on long up button press. + default: [] + selector: + action: {} + action_button_up_release: + name: (Optional) Up button release + description: Action to run on up button release after long press. + default: [] + selector: + action: {} + action_button_up_double: + name: (Optional) Up button double press + description: Action to run on double up button press. + default: [] + selector: + action: {} + action_button_down_short: + name: (Optional) Down button short press + description: Action to run on short down button press. + default: [] + selector: + action: {} + action_button_down_long: + name: (Optional) Down button long press + description: Action to run on long down button press. + default: [] + selector: + action: {} + action_button_down_release: + name: (Optional) Down button release + description: Action to run on down button release after long press. + default: [] + selector: + action: {} + action_button_down_double: + name: (Optional) Down button double press + description: Action to run on double down button press. + default: [] + selector: + action: {} + button_on_long_loop: + name: (Optional) On button long press - loop until release + description: Loop the on button action until the button is released. + default: false + selector: + boolean: {} + button_on_long_max_loop_repeats: + name: (Optional) On button long press - Maximum loop repeats + description: Maximum number of repeats for the custom action, when looping is + enabled. Use it as a safety limit to prevent an endless loop in case the corresponding + stop event is not received. + default: 500 + selector: + number: + min: 1.0 + max: 5000.0 + mode: slider + step: 1.0 + button_off_long_loop: + name: (Optional) Off button long press - loop until release + description: Loop the off button action until the button is released. + default: false + selector: + boolean: {} + button_off_long_max_loop_repeats: + name: (Optional) Off button long press - Maximum loop repeats + description: Maximum number of repeats for the custom action, when looping is + enabled. Use it as a safety limit to prevent an endless loop in case the corresponding + stop event is not received. + default: 500 + selector: + number: + min: 1.0 + max: 5000.0 + mode: slider + step: 1.0 + button_up_long_loop: + name: (Optional) Up button long press - loop until release + description: Loop the up button action until the button is released. + default: false + selector: + boolean: {} + button_up_long_max_loop_repeats: + name: (Optional) Up button long press - Maximum loop repeats + description: Maximum number of repeats for the custom action, when looping is + enabled. Use it as a safety limit to prevent an endless loop in case the corresponding + stop event is not received. + default: 500 + selector: + number: + min: 1.0 + max: 5000.0 + mode: slider + step: 1.0 + button_down_long_loop: + name: (Optional) Down button long press - loop until release + description: Loop the down button action until the button is released. + default: false + selector: + boolean: {} + button_down_long_max_loop_repeats: + name: (Optional) Down button long press - Maximum loop repeats + description: Maximum number of repeats for the custom action, when looping is + enabled. Use it as a safety limit to prevent an endless loop in case the corresponding + stop event is not received. + default: 500 + selector: + number: + min: 1.0 + max: 5000.0 + mode: slider + step: 1.0 + button_on_double_press: + name: (Optional) Expose on button double press event + description: Choose whether or not to expose the virtual double press event + for the on button. Turn this on if you are providing an action for the on + button double press event. + default: false + selector: + boolean: {} + button_off_double_press: + name: (Optional) Expose off button double press event + description: Choose whether or not to expose the virtual double press event + for the off button. Turn this on if you are providing an action for the off + button double press event. + default: false + selector: + boolean: {} + button_up_double_press: + name: (Optional) Expose up button double press event + description: Choose whether or not to expose the virtual double press event + for the up button. Turn this on if you are providing an action for the up + button double press event. + default: false + selector: + boolean: {} + button_down_double_press: + name: (Optional) Expose down button double press event + description: Choose whether or not to expose the virtual double press event + for the down button. Turn this on if you are providing an action for the down + button double press event. + default: false + selector: + boolean: {} + helper_double_press_delay: + name: (Optional) Helper - Double Press delay + description: Max delay between the first and the second button press for the + double press event. Provide a value only if you are using a double press action. + Increase this value if you notice that the double press action is not triggered + properly. + default: 500 + selector: + number: + min: 100.0 + max: 5000.0 + unit_of_measurement: milliseconds + mode: box + step: 10.0 + helper_debounce_delay: + name: (Optional) Helper - Debounce delay + description: Delay used for debouncing RAW controller events, by default set + to 0. A value of 0 disables the debouncing feature. Increase this value if + you notice custom actions or linked Hooks running multiple times when interacting + with the device. When the controller needs to be debounced, usually a value + of 100 is enough to remove all duplicate events. + default: 0 + selector: + number: + min: 0.0 + max: 1000.0 + unit_of_measurement: milliseconds + mode: box + step: 10.0 +variables: + integration: !input integration + button_on_long_loop: !input button_on_long_loop + button_on_long_max_loop_repeats: !input button_on_long_max_loop_repeats + button_on_double_press: !input button_on_double_press + button_off_long_loop: !input button_off_long_loop + button_off_long_max_loop_repeats: !input button_off_long_max_loop_repeats + button_off_double_press: !input button_off_double_press + button_up_long_loop: !input button_up_long_loop + button_up_long_max_loop_repeats: !input button_up_long_max_loop_repeats + button_up_double_press: !input button_up_double_press + button_down_long_loop: !input button_down_long_loop + button_down_long_max_loop_repeats: !input button_down_long_max_loop_repeats + button_down_double_press: !input button_down_double_press + helper_last_controller_event: !input helper_last_controller_event + helper_double_press_delay: !input helper_double_press_delay + helper_debounce_delay: !input helper_debounce_delay + integration_id: '{{ integration | lower }}' + adjusted_double_press_delay: '{{ [helper_double_press_delay - helper_debounce_delay, + 100] | max }}' + actions_mapping: + deconz: + button_on_short: + - '1000' + button_on_long: + - '1001' + button_on_release: + - '1003' + button_off_short: + - '4000' + button_off_long: + - '4001' + button_off_release: + - '4003' + button_up_short: + - '2000' + button_up_long: + - '2001' + button_up_release: + - '2003' + button_down_short: + - '3000' + button_down_long: + - '3001' + button_down_release: + - '3003' + zha: + button_on_short: + - on_press + button_on_long: + - on_hold + button_on_release: + - on_long_release + button_off_short: + - off_press + button_off_long: + - off_hold + button_off_release: + - off_long_release + button_up_short: + - up_press + button_up_long: + - up_hold + button_up_release: + - up_long_release + button_down_short: + - down_press + button_down_long: + - down_hold + button_down_release: + - down_long_release + zigbee2mqtt: + button_on_short: + - on_press + button_on_long: + - on_hold + button_on_release: + - on_hold_release + button_off_short: + - off_press + button_off_long: + - off_hold + button_off_release: + - off_hold_release + button_up_short: + - up_press + button_up_long: + - up_hold + button_up_release: + - up_hold_release + button_down_short: + - down_press + button_down_long: + - down_hold + button_down_release: + - down_hold_release + button_on_short: '{{ actions_mapping[integration_id]["button_on_short"] }}' + button_on_long: '{{ actions_mapping[integration_id]["button_on_long"] }}' + button_on_release: '{{ actions_mapping[integration_id]["button_on_release"] }}' + button_off_short: '{{ actions_mapping[integration_id]["button_off_short"] }}' + button_off_long: '{{ actions_mapping[integration_id]["button_off_long"] }}' + button_off_release: '{{ actions_mapping[integration_id]["button_off_release"] }}' + button_up_short: '{{ actions_mapping[integration_id]["button_up_short"] }}' + button_up_long: '{{ actions_mapping[integration_id]["button_up_long"] }}' + button_up_release: '{{ actions_mapping[integration_id]["button_up_release"] }}' + button_down_short: '{{ actions_mapping[integration_id]["button_down_short"] }}' + button_down_long: '{{ actions_mapping[integration_id]["button_down_long"] }}' + button_down_release: '{{ actions_mapping[integration_id]["button_down_release"] + }}' + controller_entity: !input controller_entity + controller_device: !input controller_device + controller_id: '{% if integration_id=="zigbee2mqtt" %}{{controller_entity}}{% else + %}{{controller_device}}{% endif %}' +mode: restart +max_exceeded: silent +trigger: +- platform: event + event_type: state_changed + event_data: + entity_id: !input controller_entity +- platform: event + event_type: + - deconz_event + - zha_event + event_data: + device_id: !input controller_device +condition: +- condition: and + conditions: + - '{%- set trigger_action -%} {%- if integration_id == "zigbee2mqtt" -%} {{ trigger.event.data.new_state.state + }} {%- elif integration_id == "deconz" -%} {{ trigger.event.data.event }} {%- + elif integration_id == "zha" -%} {{ trigger.event.data.command }} {%- endif -%} + {%- endset -%} {{ trigger_action not in ["","None"] }}' + - '{{ integration_id != "zigbee2mqtt" or trigger.event.data.new_state.state != trigger.event.data.old_state.state + }}' +action: +- delay: + milliseconds: !input helper_debounce_delay +- variables: + trigger_action: '{%- if integration_id == "zigbee2mqtt" -%} {{ trigger.event.data.new_state.state + }} {%- elif integration_id == "deconz" -%} {{ trigger.event.data.event }} {%- + elif integration_id == "zha" -%} {{ trigger.event.data.command }} {%- endif + -%}' + trigger_delta: '{{ (as_timestamp(now()) - ((states(helper_last_controller_event) + | from_json).t if helper_last_controller_event is not none and (states(helper_last_controller_event) + | regex_match("^\{((\"a\": \".*\"|\"t\": \d+\.\d+)(, )?){2}\}$")) else as_timestamp("1970-01-01 + 00:00:00"))) * 1000 }}' +- service: input_text.set_value + data: + entity_id: !input helper_last_controller_event + value: '{{ {"a":trigger_action,"t":as_timestamp(now())} | to_json }}' +- choose: + - conditions: '{{ trigger_action | string in button_on_short }}' + sequence: + - choose: + - conditions: '{{ button_on_double_press }}' + sequence: + - choose: + - conditions: '{{ trigger_action | string in states(helper_last_controller_event) + and trigger_delta | int <= helper_double_press_delay | int }}' + sequence: + - service: input_text.set_value + data: + entity_id: !input helper_last_controller_event + value: '{{ {"a":"double_press","t":as_timestamp(now())} | to_json + }}' + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_on_double + - choose: + - conditions: [] + sequence: !input action_button_on_double + default: + - delay: + milliseconds: '{{ adjusted_double_press_delay }}' + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_on_short + - choose: + - conditions: [] + sequence: !input action_button_on_short + default: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_on_short + - choose: + - conditions: [] + sequence: !input action_button_on_short + - conditions: '{{ trigger_action | string in button_on_long }}' + sequence: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_on_long + - choose: + - conditions: '{{ button_on_long_loop }}' + sequence: + - repeat: + while: '{{ repeat.index < button_on_long_max_loop_repeats | int }}' + sequence: !input action_button_on_long + default: !input action_button_on_long + - conditions: '{{ trigger_action | string in button_on_release }}' + sequence: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_on_release + - choose: + - conditions: [] + sequence: !input action_button_on_release + - conditions: '{{ trigger_action | string in button_off_short }}' + sequence: + - choose: + - conditions: '{{ button_off_double_press }}' + sequence: + - choose: + - conditions: '{{ trigger_action | string in states(helper_last_controller_event) + and trigger_delta | int <= helper_double_press_delay | int }}' + sequence: + - service: input_text.set_value + data: + entity_id: !input helper_last_controller_event + value: '{{ {"a":"double_press","t":as_timestamp(now())} | to_json + }}' + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_off_double + - choose: + - conditions: [] + sequence: !input action_button_off_double + default: + - delay: + milliseconds: '{{ adjusted_double_press_delay }}' + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_off_short + - choose: + - conditions: [] + sequence: !input action_button_off_short + default: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_off_short + - choose: + - conditions: [] + sequence: !input action_button_off_short + - conditions: '{{ trigger_action | string in button_off_long }}' + sequence: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_off_long + - choose: + - conditions: '{{ button_off_long_loop }}' + sequence: + - repeat: + while: '{{ repeat.index < button_off_long_max_loop_repeats | int }}' + sequence: !input action_button_off_long + default: !input action_button_off_long + - conditions: '{{ trigger_action | string in button_off_release }}' + sequence: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_off_release + - choose: + - conditions: [] + sequence: !input action_button_off_release + - conditions: '{{ trigger_action | string in button_up_short }}' + sequence: + - choose: + - conditions: '{{ button_up_double_press }}' + sequence: + - choose: + - conditions: '{{ trigger_action | string in states(helper_last_controller_event) + and trigger_delta | int <= helper_double_press_delay | int }}' + sequence: + - service: input_text.set_value + data: + entity_id: !input helper_last_controller_event + value: '{{ {"a":"double_press","t":as_timestamp(now())} | to_json + }}' + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_up_double + - choose: + - conditions: [] + sequence: !input action_button_up_double + default: + - delay: + milliseconds: '{{ adjusted_double_press_delay }}' + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_up_short + - choose: + - conditions: [] + sequence: !input action_button_up_short + default: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_up_short + - choose: + - conditions: [] + sequence: !input action_button_up_short + - conditions: '{{ trigger_action | string in button_up_long }}' + sequence: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_up_long + - choose: + - conditions: '{{ button_up_long_loop }}' + sequence: + - repeat: + while: '{{ repeat.index < button_up_long_max_loop_repeats | int }}' + sequence: !input action_button_up_long + default: !input action_button_up_long + - conditions: '{{ trigger_action | string in button_up_release }}' + sequence: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_up_release + - choose: + - conditions: [] + sequence: !input action_button_up_release + - conditions: '{{ trigger_action | string in button_down_short }}' + sequence: + - choose: + - conditions: '{{ button_down_double_press }}' + sequence: + - choose: + - conditions: '{{ trigger_action | string in states(helper_last_controller_event) + and trigger_delta | int <= helper_double_press_delay | int }}' + sequence: + - service: input_text.set_value + data: + entity_id: !input helper_last_controller_event + value: '{{ {"a":"double_press","t":as_timestamp(now())} | to_json + }}' + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_down_double + - choose: + - conditions: [] + sequence: !input action_button_down_double + default: + - delay: + milliseconds: '{{ adjusted_double_press_delay }}' + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_down_short + - choose: + - conditions: [] + sequence: !input action_button_down_short + default: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_down_short + - choose: + - conditions: [] + sequence: !input action_button_down_short + - conditions: '{{ trigger_action | string in button_down_long }}' + sequence: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_down_long + - choose: + - conditions: '{{ button_down_long_loop }}' + sequence: + - repeat: + while: '{{ repeat.index < button_down_long_max_loop_repeats | int }}' + sequence: !input action_button_down_long + default: !input action_button_down_long + - conditions: '{{ trigger_action | string in button_down_release }}' + sequence: + - event: ahb_controller_event + event_data: + controller: '{{ controller_id }}' + action: button_down_release + - choose: + - conditions: [] + sequence: !input action_button_down_release diff --git a/blueprints/automation/sagilo/ha_blueprint_notify_entity_became_unavailable.yaml b/blueprints/automation/sagilo/ha_blueprint_notify_entity_became_unavailable.yaml new file mode 100644 index 0000000..e73e089 --- /dev/null +++ b/blueprints/automation/sagilo/ha_blueprint_notify_entity_became_unavailable.yaml @@ -0,0 +1,49 @@ +blueprint: + name: Notify when an entity becomes unavailable + description: Notify via a selected service when an entity becomes unavailable (version + 1.0) + source_url: https://gist.github.com/sagilo/9c5f85794fa8ee3427e7cdab27f573f5 + domain: automation + input: + entities_filter_regex: + name: Entities filter + description: Optional, whitelist filter entities by regex, leave empty to include + all entities. Can be a domain, an entity name or both, for example 'sensor.' + for sensors or for example 'sensor|light.main_light' for all sensors and 'main_light' + entity + default: '' + actions: + name: Actions + description: Notifications or similar to execute, {{ trigger.event.data.entity_id + }} is replaced with the unavailable entitiy name. + selector: + action: {} + max_queued: + name: Maximum queued notifications + description: A number, in case of multiple events, what should be the queue + max depth, defaults to 10 + default: 10 + selector: + number: + min: 1.0 + max: 100.0 + step: 1.0 + mode: slider +variables: + filter: !input entities_filter_regex +alias: Notify if an entity becomes unavailable +description: '' +trigger: +- platform: event + event_type: state_changed + event_data: {} +condition: +- condition: template + value_template: '{{ trigger.event.data.entity_id |regex_search(filter, ignorecase=False) + and trigger.event.data.new_state.state == ''unavailable'' and trigger.event.data.old_state.state + != ''unknown'' }}' +action: +- choose: [] + default: !input actions +mode: queued +max: !input max_queued diff --git a/blueprints/script/homeassistant/confirmable_notification.yaml b/blueprints/script/homeassistant/confirmable_notification.yaml new file mode 100644 index 0000000..c5f4249 --- /dev/null +++ b/blueprints/script/homeassistant/confirmable_notification.yaml @@ -0,0 +1,86 @@ +blueprint: + name: Confirmable Notification + description: >- + A script that sends an actionable notification with a confirmation before + running the specified action. + domain: script + source_url: https://github.com/home-assistant/core/blob/master/homeassistant/components/script/blueprints/confirmable_notification.yaml + author: Home Assistant + input: + notify_device: + name: Device to notify + description: Device needs to run the official Home Assistant app to receive notifications. + selector: + device: + filter: + integration: mobile_app + title: + name: "Title" + description: "The title of the button shown in the notification." + default: "" + selector: + text: + message: + name: "Message" + description: "The message body" + selector: + text: + confirm_text: + name: "Confirmation Text" + description: "Text to show on the confirmation button" + default: "Confirm" + selector: + text: + confirm_action: + name: "Confirmation Action" + description: "Action to run when notification is confirmed" + default: [] + selector: + action: + dismiss_text: + name: "Dismiss Text" + description: "Text to show on the dismiss button" + default: "Dismiss" + selector: + text: + dismiss_action: + name: "Dismiss Action" + description: "Action to run when notification is dismissed" + default: [] + selector: + action: + +mode: restart + +sequence: + - alias: "Set up variables" + variables: + action_confirm: "{{ 'CONFIRM_' ~ context.id }}" + action_dismiss: "{{ 'DISMISS_' ~ context.id }}" + - alias: "Send notification" + domain: mobile_app + type: notify + device_id: !input notify_device + title: !input title + message: !input message + data: + actions: + - action: "{{ action_confirm }}" + title: !input confirm_text + - action: "{{ action_dismiss }}" + title: !input dismiss_text + - alias: "Awaiting response" + wait_for_trigger: + - platform: event + event_type: mobile_app_notification_action + event_data: + action: "{{ action_confirm }}" + - platform: event + event_type: mobile_app_notification_action + event_data: + action: "{{ action_dismiss }}" + - choose: + - conditions: "{{ wait.trigger.event.data.action == action_confirm }}" + sequence: !input confirm_action + - conditions: "{{ wait.trigger.event.data.action == action_dismiss }}" + sequence: !input dismiss_action diff --git a/blueprints/script/panhans/scene_toggle.yaml b/blueprints/script/panhans/scene_toggle.yaml new file mode 100644 index 0000000..aef2ad1 --- /dev/null +++ b/blueprints/script/panhans/scene_toggle.yaml @@ -0,0 +1,118 @@ +blueprint: + name: Scene Toggle + description: ' + + ## Features + + - toggle through scenes by order or last activated timestamp + + - define a transition time + + - select scenes by area and/or include/exclude specific scenes + + - timeout for beginning at start if using static order + + + **Help & FAQ**: [Simple Scene Toggle](https://community.home-assistant.io/t/simple-scene-toggle) + + + **Version**: 3.2 + + + If you like my work and support feel free to support me. + + + [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Q5Q3QEH52) + + ' + source_url: https://github.com/panhans/homeassistant/blob/main/blueprints/script/scene_toggle.yaml + domain: script + input: + area: + name: Name of area + description: Name of the area where you want to toggle scenes + default: '' + selector: + area: {} + included_scenes: + name: Included Scenes + description: Additional scenes to be included + default: [] + selector: + entity: + filter: + - domain: + - scene + multiple: true + excluded_scenes: + name: Excluded Scenes + description: Scenes to be excluded + default: [] + selector: + entity: + filter: + - domain: + - scene + multiple: true + transition: + name: Transition time + description: Transition time when switching between scenes + default: 1 + selector: + number: + min: 0.0 + max: 10.0 + step: 0.1 + unit_of_measurement: s + mode: slider + use_static_order: + name: Static order + description: This automation is based on timestamps of the activation of the + scenes. If this option is enabled a static order will be used instead of the + dynamic one. + default: false + selector: + boolean: {} + reset_after: + name: Reset After + description: If the static order is activated, a timeout can be set after which + the order is reset to the first scene. If 0 is set the timeout is disabled. + default: 0 + selector: + number: + min: 0.0 + max: 120.0 + step: 1.0 + unit_of_measurement: s + mode: slider +mode: queued +variables: + area: !input area + included_scenes: !input included_scenes + excluded_scenes: !input excluded_scenes + reset_after: !input reset_after + use_static_order: !input use_static_order + last_triggered: '{{ as_timestamp(iif(state_attr(this.entity_id,''last_triggered'') + == none, now(), state_attr(this.entity_id,''last_triggered''))) }}' +sequence: +- service: scene.turn_on + data_template: + transition: !input transition + entity_id: "{% set area_scenes = states.scene | selectattr('entity_id', 'in', + area_entities(area)) | map(attribute='entity_id') | list %} {% set all_scenes + = (area_scenes | reject('in', excluded_scenes) | list) + included_scenes %}\n{% + if use_static_order == true %}\n\n {% set is_reset = reset_after > 0 and (as_timestamp(now()) + - last_triggered >= reset_after) %}\n\n {% if is_reset == true %}\n {{ all_scenes[0] + }}\n {% else %}\n \n {% set unknown_scenes = expand(all_scenes) | selectattr('state', + 'eq', 'unknown') | map(attribute='entity_id') | list %}\n {% set scenes_sorted_by_activation + = expand(all_scenes) | sort(attribute='state', reverse = true) | map(attribute='entity_id') + | reject('in', unknown_scenes) | list %}\n {% set last_activated_scene = + scenes_sorted_by_activation[0] %}\n {% set index_of_last_activated_scene + = all_scenes.index(last_activated_scene) %}\n {% set new_index = index_of_last_activated_scene + + 1 %}\n\n {% if new_index == all_scenes | count %}\n {{ all_scenes[0] + }}\n {% else %}\n {{ all_scenes[new_index] }}\n {% endif %}\n {% + endif %}\n{% else %} \n {% set unknown_scenes = expand(all_scenes) | selectattr('state', + 'eq', 'unknown') | map(attribute='entity_id') | list %}\n {% if unknown_scenes + | count > 0 %}\n {{ unknown_scenes[0] }} \n {% else %}\n {% set known_scenes + = expand(all_scenes) | sort(attribute='state', reverse = false) | map(attribute='entity_id') + | list %}\n {{ known_scenes[0] }}\n {% endif %}\n{% endif %}\n" diff --git a/blueprints/template/homeassistant/inverted_binary_sensor.yaml b/blueprints/template/homeassistant/inverted_binary_sensor.yaml new file mode 100644 index 0000000..5be1840 --- /dev/null +++ b/blueprints/template/homeassistant/inverted_binary_sensor.yaml @@ -0,0 +1,27 @@ +blueprint: + name: Invert a binary sensor + description: Creates a binary_sensor which holds the inverted value of a reference binary_sensor + domain: template + source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/template/blueprints/inverted_binary_sensor.yaml + input: + reference_entity: + name: Binary sensor to be inverted + description: The binary_sensor which needs to have its value inverted + selector: + entity: + domain: binary_sensor +variables: + reference_entity: !input reference_entity +binary_sensor: + state: > + {% if states(reference_entity) == 'on' %} + off + {% elif states(reference_entity) == 'off' %} + on + {% else %} + {{ states(reference_entity) }} + {% endif %} + # delay_on: not_used in this example + # delay_off: not_used in this example + # auto_off: not_used in this example + availability: "{{ states(reference_entity) not in ('unknown', 'unavailable') }}" diff --git a/configuration.yaml b/configuration.yaml new file mode 100644 index 0000000..2b467b7 --- /dev/null +++ b/configuration.yaml @@ -0,0 +1,37 @@ +# Loads default set of integrations. Do not remove. +default_config: + +# Load frontend themes from the themes folder +frontend: + themes: !include_dir_merge_named themes + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +http: + use_x_forwarded_for: true + trusted_proxies: + - 192.168.1.0/24 + +powercalc: +# monitor_docker: +# - name: Docker +# url: http://192.168.1.40:2375 + +recorder: + purge_keep_days: 21 + +input_datetime: + whirlpool_letzte_wasserpflege: + name: "Whirlpool – Letzte Wasserpflege" + has_date: true + has_time: false + whirlpool_letzter_kartuschenwechsel: + name: "Whirlpool – Letzter Kartuschenwechsel" + has_date: true + has_time: false + whirlpool_letzter_wasserwechsel: + name: "Whirlpool – Letzter Wasserwechsel" + has_date: true + has_time: false diff --git a/custom_icons/iconify-icon-sets-master.zip b/custom_icons/iconify-icon-sets-master.zip new file mode 100644 index 0000000..6a91b39 Binary files /dev/null and b/custom_icons/iconify-icon-sets-master.zip differ diff --git a/esphome/.gitignore b/esphome/.gitignore new file mode 100644 index 0000000..d8b4157 --- /dev/null +++ b/esphome/.gitignore @@ -0,0 +1,5 @@ +# Gitignore settings for ESPHome +# This is an example and may include too much for your use-case. +# You can modify this file to suit your needs. +/.esphome/ +/secrets.yaml diff --git a/esphome/archive/esp32-bluetooth-proxy-0951c0.yaml b/esphome/archive/esp32-bluetooth-proxy-0951c0.yaml new file mode 100644 index 0000000..0eaa215 --- /dev/null +++ b/esphome/archive/esp32-bluetooth-proxy-0951c0.yaml @@ -0,0 +1,17 @@ +substitutions: + name: esp32-bluetooth-proxy-0951c0 + friendly_name: Bluetooth Proxy Küche +packages: + esphome.bluetooth-proxy: github://esphome/bluetooth-proxies/esp32-generic/esp32-generic.yaml@main +esphome: + name: ${name} + name_add_mac_suffix: false + friendly_name: ${friendly_name} +api: + encryption: + key: tCgIpe235uTVcMc/je7gVaUud7xStoBXLfr28XPCR7U= + + +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password diff --git a/esphome/archive/home-assistant-voice-0911a0.yaml b/esphome/archive/home-assistant-voice-0911a0.yaml new file mode 100644 index 0000000..869548f --- /dev/null +++ b/esphome/archive/home-assistant-voice-0911a0.yaml @@ -0,0 +1,42 @@ +substitutions: + name: home-assistant-voice-0911a0 + friendly_name: Home Assistant Voice +packages: + Nabu Casa.Home Assistant Voice PE: github://esphome/home-assistant-voice-pe/home-assistant-voice.yaml + esphome.bluetooth-proxy: github://esphome/bluetooth-proxies/esp32-generic/esp32-generic.yaml@main +esphome: + name: ${name} + name_add_mac_suffix: false + friendly_name: ${friendly_name} + +esp32: + board: esp32-s3-devkitc-1 + cpu_frequency: 240MHz + variant: esp32s3 + flash_size: 16MB + framework: + type: esp-idf + version: recommended + sdkconfig_options: + CONFIG_ESP32S3_DATA_CACHE_64KB: "y" + CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y" + CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB: "y" + + # Moves instructions and read only data from flash into PSRAM on boot. + # Both enabled allows instructions to execute while a flash operation is in progress without needing to be placed in IRAM. + # Considerably speeds up mWW at the cost of using more PSRAM. + CONFIG_SPIRAM_RODATA: "y" + CONFIG_SPIRAM_FETCH_INSTRUCTIONS: "y" + + CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST: "y" + CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY: "y" + + CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC: "y" + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" # TLS1.3 support isn't enabled by default in IDF 5.1.5 + +api: + encryption: + key: Aq3VMHZJ7q0FpT3pvWAW/DjbYERV4ABDio3oke3McHE= +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password diff --git a/esphome/archive/home-assistant-voice.yaml b/esphome/archive/home-assistant-voice.yaml new file mode 100644 index 0000000..825ace7 --- /dev/null +++ b/esphome/archive/home-assistant-voice.yaml @@ -0,0 +1,77 @@ +packages: + # This is an inline package to prefix the on_client_connected with the wait_until action + # It must appear before the actual package so it becomes the orignal config and the + # on_client_connected list from the package config is appended onto this one. + va_connected_wait_for_ble: + voice_assistant: + on_client_connected: + - wait_until: + not: ble.enabled + - delay: 2s + wifi: + on_disconnect: + - ble.enable: + home-assistant-voice: !include home-assistant-voice.yaml + esphome.bluetooth-proxy: github://esphome/bluetooth-proxies/esp32-generic/esp32-generic.yaml@main + +esphome: + project: + name: Nabu Casa.Home Assistant Voice PE + version: dev + +ota: + - platform: http_request + id: ota_http_request + +http_request: + +update: + - platform: http_request + name: None + id: update_http_request + source: https://firmware.esphome.io/home-assistant-voice-pe/home-assistant-voice/manifest.json + +dashboard_import: + package_import_url: github://esphome/home-assistant-voice-pe/home-assistant-voice.yaml + +wifi: + on_connect: + - delay: 5s # Gives time for improv results to be transmitted + - ble.disable: + - script.execute: control_leds + +improv_serial: + +esp32_ble: + name: ha-voice-pe + +esp32_improv: + authorizer: center_button + on_start: + - lambda: id(improv_ble_in_progress) = true; + - script.execute: control_leds + on_provisioned: + - lambda: id(improv_ble_in_progress) = false; + - script.execute: control_leds + on_stop: + - lambda: id(improv_ble_in_progress) = false; + - script.execute: control_leds + +switch: + - platform: template + id: beta_firmware + name: Beta firmware + icon: "mdi:test-tube" + disabled_by_default: true + entity_category: config + optimistic: true + restore_mode: RESTORE_DEFAULT_OFF + on_turn_on: + - logger.log: "OTA updates set to use Beta firmware" + - lambda: id(update_http_request).set_source_url("https://firmware.esphome.io/home-assistant-voice-pe/home-assistant-voice/manifest-beta.json"); + - component.update: update_http_request + on_turn_off: + - logger.log: "OTA updates set to use Production firmware" + - lambda: id(update_http_request).set_source_url("https://firmware.esphome.io/home-assistant-voice-pe/home-assistant-voice/manifest.json"); + - component.update: update_http_request + diff --git a/esphome/archive/nuclide.yaml b/esphome/archive/nuclide.yaml new file mode 100644 index 0000000..d84eef3 --- /dev/null +++ b/esphome/archive/nuclide.yaml @@ -0,0 +1,199 @@ +esphome: + name: esp32s3 + friendly_name: ReSpeakerv3 + platformio_options: + board_build.flash_mode: dio + board_build.mcu: esp32s3 + # on_boot: + # then: + # - if: + # condition: + # switch.is_on: + # then: + # - voice_assistant.start_continuous: + + +esp32: + board: esp32-s3-devkitc-1 + variant: esp32s3 + framework: + type: esp-idf + version: recommended + +logger: + level: VERY_VERBOSE + +api: + + +wifi: + ssid: "nuclide" + password: "tapirnase" + +captive_portal: + +external_components: + - source: github://QingWind6/ESPHome_XIAO-ESP32S3 + +i2s_audio_xiao: + i2s_lrclk_pin: GPIO7 + i2s_bclk_pin: GPIO8 + i2s_mclk_pin: GPIO9 + + +microphone: + - platform: i2s_audio_xiao + id: xiao_mic + adc_type: external + i2s_din_pin: GPIO44 + pdm: false + bits_per_sample: 32bit + channel: left + +speaker: + - platform: i2s_audio_xiao + id: xiao_speaker + dac_type: external + i2s_dout_pin: GPIO43 + mode: stereo + +voice_assistant: + microphone: xiao_mic + use_wake_word: true + noise_suppression_level: 0 + auto_gain: 0dBFS + volume_multiplier: 1 + speaker: xiao_speaker + id: assist + on_listening: + - light.turn_on: + id: led + blue: 100% + red: 0% + green: 0% + effect: "Slow Pulse" + on_stt_vad_end: + - light.turn_on: + id: led + blue: 100% + red: 0% + green: 0% + effect: "Fast Pulse" + on_tts_start: + - light.turn_on: + id: led + blue: 100% + red: 0% + green: 0% + brightness: 100% + effect: none + on_end: + - delay: 100ms + - wait_until: + not: + speaker.is_playing: + - script.execute: reset_led + on_error: + - light.turn_on: + id: led + red: 100% + green: 0% + blue: 0% + brightness: 100% + effect: none + - delay: 1s + - script.execute: reset_led + on_client_connected: + - if: + condition: + switch.is_on: use_wake_word + then: + - voice_assistant.start_continuous: + - script.execute: reset_led + on_client_disconnected: + - if: + condition: + switch.is_on: use_wake_word + then: + - voice_assistant.stop: + - light.turn_off: led + +light: + - platform: esp32_rmt_led_strip + id: led + name: None + disabled_by_default: true + entity_category: config + pin: GPIO1 + default_transition_length: 0s + chipset: ws2812 + num_leds: 1 + rgb_order: grb + rmt_channel: 0 + effects: + - pulse: + name: "Slow Pulse" + transition_length: 250ms + update_interval: 250ms + min_brightness: 50% + max_brightness: 100% + - pulse: + name: "Fast Pulse" + transition_length: 100ms + update_interval: 100ms + min_brightness: 50% + max_brightness: 100% + +output: + - platform: ledc + id: light_output + pin: GPIO21 + inverted: true + + +script: + - id: reset_led + then: + - if: + condition: + - switch.is_on: use_wake_word + - switch.is_on: use_listen_light + then: + - light.turn_on: + id: led + effect: none + + else: + - light.turn_off: + id: led + + +switch: + - platform: template + name: Use wake word + id: use_wake_word + optimistic: true + restore_mode: RESTORE_DEFAULT_ON + entity_category: config + on_turn_on: + - lambda: id(assist).set_use_wake_word(true); + - if: + condition: + not: + - voice_assistant.is_running + then: + - voice_assistant.start_continuous + on_turn_off: + - voice_assistant.stop + - lambda: id(assist).set_use_wake_word(false); + + - platform: template + name: Use Listen Light + id: use_listen_light + optimistic: true + restore_mode: RESTORE_DEFAULT_ON + entity_category: config + on_turn_on: + - script.execute: reset_led + on_turn_off: + - script.execute: reset_led \ No newline at end of file diff --git a/esphome/archive/respeaker-s3.yaml b/esphome/archive/respeaker-s3.yaml new file mode 100644 index 0000000..6ac70a8 --- /dev/null +++ b/esphome/archive/respeaker-s3.yaml @@ -0,0 +1,201 @@ +esphome: + name: esp32s3 + friendly_name: ReSpeakerv3 + platformio_options: + board_build.flash_mode: dio + board_build.mcu: esp32s3 + # on_boot: + # then: + # - if: + # condition: + # switch.is_on: + # then: + # - voice_assistant.start_continuous: + + +esp32: + board: esp32-s3-devkitc-1 + variant: esp32s3 + framework: + type: esp-idf + version: recommended + +logger: +# level: VERY_VERBOSE + +api: + +ota: + - platform: esphome + +wifi: + ssid: "nuclide" + password: "tapirnase" + +captive_portal: + +external_components: + - source: github://QingWind6/ESPHome_XIAO-ESP32S3 + +i2s_audio_xiao: + i2s_lrclk_pin: GPIO7 + i2s_bclk_pin: GPIO8 + i2s_mclk_pin: GPIO9 + + +microphone: + - platform: i2s_audio_xiao + id: xiao_mic + adc_type: external + i2s_din_pin: GPIO44 + pdm: false + bits_per_sample: 32bit + channel: left + +speaker: + - platform: i2s_audio_xiao + id: xiao_speaker + dac_type: external + i2s_dout_pin: GPIO43 + mode: stereo + +voice_assistant: + microphone: xiao_mic + use_wake_word: true + noise_suppression_level: 0 + auto_gain: 0dBFS + volume_multiplier: 1 + speaker: xiao_speaker + id: assist + on_listening: + - light.turn_on: + id: led + blue: 100% + red: 0% + green: 0% + effect: "Slow Pulse" + on_stt_vad_end: + - light.turn_on: + id: led + blue: 100% + red: 0% + green: 0% + effect: "Fast Pulse" + on_tts_start: + - light.turn_on: + id: led + blue: 100% + red: 0% + green: 0% + brightness: 100% + effect: none + on_end: + - delay: 100ms + - wait_until: + not: + speaker.is_playing: + - script.execute: reset_led + on_error: + - light.turn_on: + id: led + red: 100% + green: 0% + blue: 0% + brightness: 100% + effect: none + - delay: 1s + - script.execute: reset_led + on_client_connected: + - if: + condition: + switch.is_on: use_wake_word + then: + - voice_assistant.start_continuous: + - script.execute: reset_led + on_client_disconnected: + - if: + condition: + switch.is_on: use_wake_word + then: + - voice_assistant.stop: + - light.turn_off: led + +light: + - platform: esp32_rmt_led_strip + id: led + name: None + disabled_by_default: true + entity_category: config + pin: GPIO1 + default_transition_length: 0s + chipset: ws2812 + num_leds: 1 + rgb_order: grb + rmt_channel: 0 + effects: + - pulse: + name: "Slow Pulse" + transition_length: 250ms + update_interval: 250ms + min_brightness: 50% + max_brightness: 100% + - pulse: + name: "Fast Pulse" + transition_length: 100ms + update_interval: 100ms + min_brightness: 50% + max_brightness: 100% + +output: + - platform: ledc + id: light_output + pin: GPIO21 + inverted: true + + +script: + - id: reset_led + then: + - if: + condition: + - switch.is_on: use_wake_word + - switch.is_on: use_listen_light + then: + - light.turn_on: + id: led + effect: none + + else: + - light.turn_off: + id: led + + +switch: + - platform: template + name: Use wake word + id: use_wake_word + optimistic: true + restore_mode: RESTORE_DEFAULT_ON + entity_category: config + on_turn_on: + - lambda: id(assist).set_use_wake_word(true); + - if: + condition: + not: + - voice_assistant.is_running + then: + - voice_assistant.start_continuous + on_turn_off: + - voice_assistant.stop + - lambda: id(assist).set_use_wake_word(false); + + - platform: template + name: Use Listen Light + id: use_listen_light + optimistic: true + restore_mode: RESTORE_DEFAULT_ON + entity_category: config + on_turn_on: + - script.execute: reset_led + on_turn_off: + - script.execute: reset_led \ No newline at end of file diff --git a/esphome/esp32-bluetooth-proxy-bb4b84.yaml b/esphome/esp32-bluetooth-proxy-bb4b84.yaml new file mode 100644 index 0000000..bdae8ad --- /dev/null +++ b/esphome/esp32-bluetooth-proxy-bb4b84.yaml @@ -0,0 +1,53 @@ +substitutions: + name: esp32-bluetooth-proxy-bb4b84 + friendly_name: Bluetooth Proxy Garage +packages: + esphome.bluetooth-proxy: github://esphome/bluetooth-proxies/esp32-generic/esp32-generic.yaml@main +esphome: + name: ${name} + name_add_mac_suffix: false + friendly_name: ${friendly_name} +api: + encryption: + key: 0QWYS2snebtCGQCUxJiCSbRiSoKP1q57J80uCelH1xM= + + +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password + +logger: + level: DEBUG # This is critical to see the raw hex codes + +esp32_ble_tracker: + scan_parameters: + active: true # We need active scanning to "see" the controller better + +esp32_ble: + max_connections: 4 + +ble_client: + # - mac_address: D5:E9:5E:AF:FB:65 + # id: vLinker_MC_IOS + + - mac_address: 80:F1:B2:B3:B6:48 + id: spider_farmer + on_connect: + then: + - lambda: |- + ESP_LOGD("custom", "Connected to Spider Farmer! Exploration starting..."); + # This part below is a 'sniffer' that will dump the services to logs + on_passkey_request: + - lambda: ESP_LOGD("custom", "Passkey requested"); +# sensor: +# # RSSI based on MAC address +# - platform: ble_rssi +# mac_address: D5:E9:5E:AF:FB:65 +# name: "VLinker MC IOS RSSI" + +# binary_sensor: +# - platform: ble_presence +# mac_address: D5:E9:5E:AF:FB:65 +# name: "VLinker MC IOS Presence" + + diff --git a/esphome/esphome-web-bb4b84.yaml b/esphome/esphome-web-bb4b84.yaml new file mode 100644 index 0000000..bdae8ad --- /dev/null +++ b/esphome/esphome-web-bb4b84.yaml @@ -0,0 +1,53 @@ +substitutions: + name: esp32-bluetooth-proxy-bb4b84 + friendly_name: Bluetooth Proxy Garage +packages: + esphome.bluetooth-proxy: github://esphome/bluetooth-proxies/esp32-generic/esp32-generic.yaml@main +esphome: + name: ${name} + name_add_mac_suffix: false + friendly_name: ${friendly_name} +api: + encryption: + key: 0QWYS2snebtCGQCUxJiCSbRiSoKP1q57J80uCelH1xM= + + +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password + +logger: + level: DEBUG # This is critical to see the raw hex codes + +esp32_ble_tracker: + scan_parameters: + active: true # We need active scanning to "see" the controller better + +esp32_ble: + max_connections: 4 + +ble_client: + # - mac_address: D5:E9:5E:AF:FB:65 + # id: vLinker_MC_IOS + + - mac_address: 80:F1:B2:B3:B6:48 + id: spider_farmer + on_connect: + then: + - lambda: |- + ESP_LOGD("custom", "Connected to Spider Farmer! Exploration starting..."); + # This part below is a 'sniffer' that will dump the services to logs + on_passkey_request: + - lambda: ESP_LOGD("custom", "Passkey requested"); +# sensor: +# # RSSI based on MAC address +# - platform: ble_rssi +# mac_address: D5:E9:5E:AF:FB:65 +# name: "VLinker MC IOS RSSI" + +# binary_sensor: +# - platform: ble_presence +# mac_address: D5:E9:5E:AF:FB:65 +# name: "VLinker MC IOS Presence" + + diff --git a/go2rtc.yaml b/go2rtc.yaml new file mode 100644 index 0000000..54685c6 --- /dev/null +++ b/go2rtc.yaml @@ -0,0 +1,4 @@ +streams: + #roborock_curv_camera: roborock://mqtt-eu-3.roborock.com:8883?u=38SWMJptxB8rNxUrAXJJNw&s=XugGLe&k=RlafA6A0&did=6ubNiyju0W16iQVWuKDItt&key=SX3PQ904wAmQUcvk&pin=14789 + roborock_curv_map: "http://homeassistant.local:8123/api/image_proxy/image.roborock_qrevo_curv_zuhause?token=f33da7378b8862741c24c19e3164af4ff42c8e068072afaff8fb538e20754018#header=Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhOGVkZmQ5ZmE5ZWI0OTJkYmYzZTAyYzY3ZTUzNmJkOCIsImlhdCI6MTczMjM2NTU4OSwiZXhwIjoyMDQ3NzI1NTg5fQ.sC2G4WNV2nSupOpshSZysxYWs1LwfwcvhHHjTpmp5rc" + tc_test: "ffmpeg:roborock_curv_map#video=h264" diff --git a/powerocean_powerpulse/__init__.py b/powerocean_powerpulse/__init__.py new file mode 100644 index 0000000..f82c610 --- /dev/null +++ b/powerocean_powerpulse/__init__.py @@ -0,0 +1,86 @@ +"""__init__.py: The PowerOcean integration.""" +from __future__ import annotations + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr + +from .const import DOMAIN, PLATFORMS, _LOGGER, DOMAIN, ISSUE_URL_ERROR_MESSAGE, STARTUP_MESSAGE +from .ecoflow import Ecoflow + + +_LOGGER.info(STARTUP_MESSAGE) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up PowerOcean from a config entry.""" + + # Setup DOMAIN as default + hass.data.setdefault(DOMAIN, {}) + + # Setup device specific sensor list (used in updates) on HASS so it is available within integration (reuqired for unload) + hass.data[DOMAIN]["device_specific_sensors"] = {} + + # Store an instance of the API instance in hass.data[domain] + user_input = entry.data["user_input"] # This user_input object was stored after the device + # was setup and has the user/pass/serial info + device_info = entry.data.get("device_info") # This device_info object was stored after the device + # was setup and has the name and serial needed etc. + options = entry.data["options"] # These are the options during setup, including custom device name + ecoflow = Ecoflow(user_input["serialnumber"], user_input["username"], user_input["password"]) + + if device_info: + ecoflow.device = device_info # Store the device information + ecoflow.options = options # Store the options + hass.data[DOMAIN][entry.entry_id] = ecoflow + + # Forward to sensor platform + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + # Get the device registry + device_registry = dr.async_get(hass) + + # Fetching device info from device registry + # During the config flow, the device info is saved in the entry's data under 'device_info' key. + device_info = entry.data.get("device_info") + + # If the device_info was provided, register the device + if device_info: + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, device_info["serial"])}, + manufacturer=device_info.get("vendor", "ECOFLOW"), + serial_number=device_info.get("serial"), + name=options.get("custom_device_name"), # Custom device name from user step 2 (options) + model=device_info.get("product"), + sw_version=device_info.get("version"), + configuration_url="https://api-e.ecoflow.com", + suggested_area="Boiler Room", + ) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + # Unload all platforms associated with this entry + unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + + if unload_ok: + # Clean up hass.data if any reference exists + hass.data[DOMAIN].pop(entry.entry_id, None) + + # Additionally, clear the device-specific sensors list if it exists + device_id = entry.data.get("device_info").get("serial") + device_name = entry.data.get("options").get("custom_device_name") + if device_id in hass.data.get(DOMAIN, {}).get("device_specific_sensors", {}): + hass.data[DOMAIN]["device_specific_sensors"].pop(device_id, None) + _LOGGER.debug( + f"{device_id}: Cleared sensor update list for device with custom name '{device_name}'" + ) + + return unload_ok + + +async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: + await hass.config_entries.async_reload(entry.entry_id) diff --git a/powerocean_powerpulse/additional datapoints.json b/powerocean_powerpulse/additional datapoints.json new file mode 100644 index 0000000..172716e --- /dev/null +++ b/powerocean_powerpulse/additional datapoints.json @@ -0,0 +1,25 @@ +, + "evSn", + "workMode", + "useGridFirst", + "evOnoffSet", + "errorCode", + "evPhaseSet", + "orderState", + "orderTime", + "orderEndTimestamp", + "defaultVehicleId", + "expectChargingEnergy", + + "switchBits", + "evDetectPhase", + "orderStartTimestamp", + "onlineBits", + "updateTime", + "evUserManual", + "evChargingEnergy", + "evCurrSet", + "chargeVehicleId", + "evPlugAndPlay", + "evExeMode", + "stopChargingSoc" \ No newline at end of file diff --git a/powerocean_powerpulse/config_flow.py b/powerocean_powerpulse/config_flow.py new file mode 100644 index 0000000..c9bf830 --- /dev/null +++ b/powerocean_powerpulse/config_flow.py @@ -0,0 +1,189 @@ +"""config_flow.py: Config flow for PowerOcean integration.""" +from __future__ import annotations + +import re +from typing import Any +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResult +from homeassistant.exceptions import HomeAssistantError +from homeassistant.exceptions import IntegrationError + +from .const import _LOGGER, DOMAIN, ISSUE_URL_ERROR_MESSAGE +from .ecoflow import Ecoflow, AuthenticationFailed + + +# This is the first step's schema when setting up the integration, or its devices +# The second schema is defined inside the ConfigFlow class as it has dynamic default values set via API call +STEP_USER_DATA_SCHEMA = vol.Schema( + { + vol.Required("serialnumber", default=""): str, + vol.Required("username", default=""): str, + vol.Required("password", default=""): str, + } +) + + +async def validate_input_for_device(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]: + """Validate the user input allows us to connect.""" + + ecoflow = Ecoflow(data["serialnumber"], data["username"], data["password"]) + + try: + # Check for authentication + # auth_check = await hass.async_add_executor_job(ecoflow.fetch_data) # TODO what else is needed from fetch_data? + auth_check = await hass.async_add_executor_job(ecoflow.authorize) + if not auth_check: + # If authentication check returns False, raise an authentication failure exception + raise AuthenticationFailed("Invalid authentication!") + + # Get device info + device = await hass.async_add_executor_job(ecoflow.get_device) + + # Return the device object with the device information + return device + + # Exception if device cannot be found + except IntegrationError as e: + _LOGGER.error(f"Failed to connect to PowerOcean device: {e}" + ISSUE_URL_ERROR_MESSAGE) + raise CannotConnect from e + + # Exception if authentication fails + except AuthenticationFailed as e: + _LOGGER.error(f"Authentication failed: {e}" + ISSUE_URL_ERROR_MESSAGE) + raise InvalidAuth from e + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for PowerOcean.""" + + VERSION = 1.3 + + # Make sure user input data is passed from one step to the next using user_input_from_step_user + def __init__(self): + self.user_input_from_step_user = None + + # This is step 1 for the host/port/user/pass function. + async def async_step_user(self, user_input: dict[str, Any] | None = None) -> FlowResult: + """Handle the initial step.""" + errors: dict[str, str] = {} + if user_input is not None: + try: + # Valide the user input whilst setting up integration or adding new devices. + # validate_input_for_devices will try to detect the device and get more info from it, + # and authenticate and deal with exceptions + device = await validate_input_for_device(self.hass, user_input) + except CannotConnect: + errors["base"] = "cannot_connect" + except InvalidAuth: + errors["base"] = "invalid_auth" + return self.async_show_form(step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors) + except Exception: + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + else: + # Create a unique device id as a combination of the device's product and its serial number (which should be unique on its own) + # The host can be udpated if necessary, as the IP address of the device may have changed + unique_id = f"{device['product']}_{device['serial']}" + + # Checks that the device is actually unique, otherwise abort + await self.async_set_unique_id(unique_id) + # self._abort_if_unique_id_configured( + # updates={"host": user_input["host"]} + # ) + + # Before creating the entry in the config_entry registry, go to step 2 for the options + # However, make sure the steps from the user input are passed on to the next step + self.user_input_from_step_user = user_input + self.device_info = device + + # Now call the second step but set user_input to None for the first time to force data entry in step 2 + return await self.async_step_device_options(user_input=None) + + # Show the form for step 1 with the user/host/pass as defined in STEP_USER_DATA_SCHEMA + return self.async_show_form(step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors) + + # This is step 2 for the options such as custom name, group and disable sensors + async def async_step_device_options(self, user_input: dict[str, Any] | None = None,) -> FlowResult: + """Handle the device options step.""" + errors: dict[str, str] = {} + + if user_input is not None: + try: + # Sanitize the user provided custom device name, which is used for entry and device registry name + user_input["custom_device_name"] = sanitize_device_name( + user_input["custom_device_name"], self.device_info["name"] + ) + + # Since we have already set the unique ID and updated host if necessary create + # the entry with the additional options. + # The title of the integration is the custom friendly device name given by the user in step 2 + title = user_input["custom_device_name"] + return self.async_create_entry(title=title, + data={ + "user_input": self.user_input_from_step_user, # from previous step + "device_info": self.device_info, # from device detection + "options": user_input, # new options from this step + }, + ) + except Exception as e: + _LOGGER.error(f"Failed to handle device options: {e}" + ISSUE_URL_ERROR_MESSAGE) + errors["base"] = "option_error" + + # Prepare the second form's schema as it has dynamic values based on the API call + # Use the name from the detected device as default device name + default_device_name = ( + self.device_info["name"] + if self.device_info and "name" in self.device_info + else "New Device" + ) + step_device_options_schema = vol.Schema( + { + vol.Required("custom_device_name", default=default_device_name): str, + vol.Required("polling_time", default=10): vol.All( + vol.Coerce(int), vol.Clamp(min=5) + ), + vol.Required("group_sensors", default=True): bool, + vol.Required("disable_sensors", default=False): bool, + } + ) + + # Show the form for step 2 with the device name and other options as defined in STEP_DEVICE_OPTIONS_SCHEMA + return self.async_show_form( + step_id="device_options", + data_schema=step_device_options_schema, + errors={}, + ) + + +class CannotConnect(HomeAssistantError): + """Error to indicate we cannot connect.""" + + +class InvalidAuth(HomeAssistantError): + """Error to indicate there is invalid auth.""" + + +# Helper function to sanitize +def sanitize_device_name(device_name: str, fall_back: str, max_length=255) -> str: + # Trim whitespace + name = device_name.strip() + + # Remove special characters but keep spaces + name = re.sub(r"[^\w\s-]", "", name) + + # Replace multiple spaces with a single space + name = re.sub(r"\s+", " ", name) + + # Length check + if len(name) > max_length: + # Split at the last space to avoid cutting off in the middle of a word + name = name[:max_length].rsplit(" ", 1)[0] + + # Fallback name + if not name: + name = fall_back + + return name diff --git a/powerocean_powerpulse/const.py b/powerocean_powerpulse/const.py new file mode 100644 index 0000000..6ecbcc8 --- /dev/null +++ b/powerocean_powerpulse/const.py @@ -0,0 +1,38 @@ +"""Constants for the PowerOcean integration.""" + +import logging + +from homeassistant.const import Platform + +DOMAIN = "powerocean" +NAME = "Ecoflow PowerOcean" +VERSION = "2024.08.27" +ISSUE_URL = "https://github.com/niltrip/powerocean/issues" +ISSUE_URL_ERROR_MESSAGE = " Please log any issues here: " + ISSUE_URL + + +PLATFORMS: list[Platform] = [Platform.SENSOR] + +_LOGGER = logging.getLogger(f"custom_components.{DOMAIN}") + +ATTR_PRODUCT_DESCRIPTION = "Product Description" +ATTR_DESTINATION_NAME = "Destination Name" +ATTR_SOURCE_NAME = "Source Name" +ATTR_UNIQUE_ID = "Internal Unique ID" +ATTR_PRODUCT_VENDOR = "Vendor" +ATTR_PRODUCT_SERIAL = "Vendor Product Serial" +ATTR_PRODUCT_NAME = "Device Name" +ATTR_PRODUCT_VERSION = "Vendor Firmware Version" +ATTR_PRODUCT_BUILD = "Vendor Product Build" +ATTR_PRODUCT_FEATURES = "Vendor Product Features" + + +STARTUP_MESSAGE = f""" +---------------------------------------------------------------------------- +{NAME} +Version: {VERSION} +Domain: {DOMAIN} +If you have any issues with this custom component please open an issue here: +{ISSUE_URL} +---------------------------------------------------------------------------- +""" diff --git a/powerocean_powerpulse/datapoints.json b/powerocean_powerpulse/datapoints.json new file mode 100644 index 0000000..bd53e04 --- /dev/null +++ b/powerocean_powerpulse/datapoints.json @@ -0,0 +1,72 @@ +{ + "data": [ + "sysLoadPwr", + "sysGridPwr", + "mpptPwr", + "bpPwr", + "online", + "dcdcPwr", + "todayElectricityGeneration", + "monthElectricityGeneration", + "yearElectricityGeneration", + "totalElectricityGeneration", + "systemName", + "createTime" + ], + "JTS1_ENERGY_STREAM_REPORT": [ + "pv1Pwr", + "pvInvPwr", + "pv2Pwr" + ], + "JTS1_EMS_CHANGE_REPORT": [ + "bpTotalChgEnergy", + "bpTotalDsgEnergy", + "bpSoc", + "bpOnlineSum", + "emsCtrlLedBright" + ], + "JTS1_BP_STA_REPORT": [ + "bpPwr", + "bpSoc", + "bpSoh", + "bpVol", + "bpAmp", + "bpCycles", + "bpSysState", + "bpRemainWatth" + ], + "JTS1_EMS_HEARTBEAT": [ + "bpRemainWatth", + "emsBpAliveNum", + "emsBpPower", + "pcsActPwr", + "pcsMeterPower" + ], + "JTS1_EVCHARGING_REPORT": [ + "chargingStatus", + "evPwr", + "evSn", + "workMode", + "useGridFirst", + "evOnoffSet", + "errorCode", + "evPhaseSet", + "orderState", + "orderTime", + "orderEndTimestamp", + "defaultVehicleId", + "expectChargingEnergy", + "switchBits", + "evDetectPhase", + "orderStartTimestamp", + "onlineBits", + "updateTime", + "evUserManual", + "evChargingEnergy", + "evCurrSet", + "chargeVehicleId", + "evPlugAndPlay", + "evExeMode", + "stopChargingSoc" + ] +} \ No newline at end of file diff --git a/powerocean_powerpulse/ecoflow.py b/powerocean_powerpulse/ecoflow.py new file mode 100644 index 0000000..6179ee1 --- /dev/null +++ b/powerocean_powerpulse/ecoflow.py @@ -0,0 +1,501 @@ +"""ecoflow.py: API for PowerOcean integration.""" + +import base64 +import re +from collections import namedtuple +from pathlib import Path + +import requests +from homeassistant.exceptions import IntegrationError +from homeassistant.util.json import json_loads +from requests.exceptions import RequestException + +from .const import _LOGGER, ISSUE_URL_ERROR_MESSAGE, DOMAIN + +# Mock path to response.json file +mocked_response = Path("documentation/response.json") +# Dict with entity names to use +SENSSELECT = Path(f"custom_components/{DOMAIN}/datapoints.json") + +# Better storage of PowerOcean endpoint +PowerOceanEndPoint = namedtuple( + "PowerOceanEndPoint", + "internal_unique_id, serial, name, friendly_name, value, unit, description, icon", +) + + +# ecoflow_api to detect device and get device info, fetch the actual data from the PowerOcean device, and parse it +# Rename, there is an official API since june +class Ecoflow: + """Class representing Ecoflow""" + + def __init__(self, serialnumber: str, username: str, password: str): + self.sn = serialnumber + self.unique_id = serialnumber + self.ecoflow_username = username + self.ecoflow_password = password + self.token = None + self.device = None + self.session = requests.Session() + self.url_iot_app = "https://api.ecoflow.com/auth/login" + self.url_user_fetch = f"https://api-e.ecoflow.com/provider-service/user/device/detail?sn={self.sn}" + + def get_device(self): + """Get device info.""" + self.device = { + "product": "PowerOcean", + "vendor": "Ecoflow", + "serial": self.sn, + "version": "5.1.25", # TODO: woher bekommt man diese Info? + "build": "8", # TODO: wo finde ich das? + "name": "PowerOcean", + "features": "Photovoltaik", + } + + return self.device + + def authorize(self): + """Function authorize""" + auth_ok = False # default + headers = {"lang": "en_US", "content-type": "application/json"} + data = { + "email": self.ecoflow_username, + "password": base64.b64encode(self.ecoflow_password.encode()).decode(), + "scene": "IOT_APP", + "userType": "ECOFLOW", + } + + try: + url = self.url_iot_app + _LOGGER.info("Login to EcoFlow API %s", {url}) + request = requests.post(url, json=data, headers=headers, timeout=10) + response = self.get_json_response(request) + + except ConnectionError: + error = f"Unable to connect to {self.url_iot_app}. Device might be offline." + _LOGGER.warning(error + ISSUE_URL_ERROR_MESSAGE) + raise IntegrationError(error) + + try: + self.token = response["data"]["token"] + # self.user_id = response["data"]["user"]["userId"] + # user_name = response["data"]["user"].get("name", "") + auth_ok = True + except KeyError as key: + raise Exception(f"Failed to extract key {key} from response: {response}") + + _LOGGER.info("Successfully logged in.") + + self.get_device() # collect device info + + return auth_ok + + def get_json_response(self, request): + """Function get json response""" + if request.status_code != 200: + raise Exception( + f"Got HTTP status code {request.status_code}: {request.text}" + ) + try: + response = json_loads(request.text) + response_message = response["message"] + except KeyError as key: + raise Exception( + f"Failed to extract key {key} from {json_loads(request.text)}" + ) + except Exception as error: + raise Exception(f"Failed to parse response: {request.text} Error: {error}") + + if response_message.lower() != "success": + raise Exception(f"{response_message}") + + return response + + # Fetch the data from the PowerOcean device, which then constitues the Sensors + def fetch_data(self): + """Fetch data from Url.""" + url = self.url_user_fetch + try: + headers = { + "authorization": f"Bearer {self.token}", + # "accept": "application/json, text/plain, */*", + "user-agent": "Firefox/133.0", + # "platform": "web", + "product-type": "83", + } + request = requests.get(self.url_user_fetch, headers=headers, timeout=30) + response = self.get_json_response(request) + + try: + # TESTING!!! create response file and use it as data + with Path.open(mocked_response, "r", encoding="utf-8") as datei: + response = json_loads(datei.read()) + except FileExistsError: + error = f"Data from: {mocked_response}" + _LOGGER.warning(error) + except FileNotFoundError: + error = f"Responsefile not present: {mocked_response}" + _LOGGER.debug(error) + + _LOGGER.debug(f"{response}") + return self._get_sensors(response) + + except ConnectionError: + error = f"ConnectionError in fetch_data: Unable to connect to {url}. Device might be offline." + _LOGGER.warning(error + ISSUE_URL_ERROR_MESSAGE) + raise IntegrationError(error) + + except RequestException as e: + error = f"RequestException in fetch_data: Error while fetching data from {url}: {e}" + _LOGGER.warning(error + ISSUE_URL_ERROR_MESSAGE) + raise IntegrationError(error) + + def __get_unit(self, key): + """Get unit from key name.""" + if key.endswith(("pwr", "Pwr", "Power")): + unit = "W" + elif key.endswith(("amp", "Amp")): + unit = "A" + elif key.endswith(("soc", "Soc", "soh", "Soh")): + unit = "%" + elif key.endswith(("vol", "Vol")): + unit = "V" + elif key.endswith(("Watth", "Energy")): + unit = "Wh" + elif "Generation" in key: + unit = "kWh" + elif key.startswith("bpTemp"): + unit = "°C" + else: + unit = None + + return unit + + def __get_description(self, key): + # TODO: hier könnte man noch mehr definieren bzw ein translation dict erstellen +1 + # Comment: Ich glaube hier brauchen wir n + description = key # default description + if key == "sysLoadPwr": + description = "Hausnetz" + if key == "sysGridPwr": + description = "Stromnetz" + if key == "mpptPwr": + description = "Solarertrag" + if key == "bpPwr": + description = "Batterieleistung" + if key == "bpSoc": + description = "Ladezustand der Batterie" + if key == "online": + description = "Online" + if key == "systemName": + description = "System Name" + if key == "createTime": + description = "Installations Datum" + # Battery descriptions + if key == "bpVol": + description = "Batteriespannung" + if key == "bpAmp": + description = "Batteriestrom" + if key == "bpCycles": + description = "Ladezyklen" + if key == "bpTemp": + description = "Temperatur der Batteriezellen" + + return description + + def __get_sens_select(self, report): + # open File an read + with Path.open(SENSSELECT) as file: + config = json_loads(file.read()) + + return config[report] + + def _get_sensors(self, response): + # get sensors from response['data'] + sensors = self.__get_sensors_data(response) + + # get sensors from 'JTS1_ENERGY_STREAM_REPORT' + sensors = self.__get_sensors_energy_stream(response, sensors) + + # get sensors from 'JTS1_EMS_CHANGE_REPORT' + sensors = self.__get_sensors_ems_change(response, sensors) + + # get info from batteries => JTS1_BP_STA_REPORT + sensors = self.__get_sensors_battery(response, sensors) + + # get info from PV strings => JTS1_EMS_HEARTBEAT + sensors = self.__get_sensors_ems_heartbeat(response, sensors) + + # get info from "JTS1_EVCHARGING_REPORT" + sensors = self.__get_sensors_evcharging_report(response, sensors) + + return sensors + + def __get_sensors_data(self, response): + d = response["data"].copy() + + sens_select = self.__get_sens_select("data") + + sensors = dict() # start with empty dict + for key, value in d.items(): + if key in sens_select: # use only sensors in sens_select + if not isinstance(value, dict): + # default uid, unit and descript + unique_id = f"{self.sn}_{key}" + special_icon = None + if key == "mpptPwr": + special_icon = "mdi:solar-power" + if key == "online": + special_icon = "mdi:cloud-check" + if key == "sysGridPwr": + special_icon = "mdi:transmission-tower-import" + if key == "sysLoadPwr": + special_icon = "mdi:home-import-outline" + + sensors[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{key}", + friendly_name=key, + value=value, + unit=self.__get_unit(key), + description=self.__get_description(key), + icon=special_icon, + ) + + return sensors + + def __get_sensors_evcharging_report(self, response, sensors): + report = "JTS1_EVCHARGING_REPORT" + #_LOGGER.warning(f"{report=}") + d = response["data"]["quota"][report] + sens_select = self.__get_sens_select(report) + + data = {} + for key, value in d.items(): + if key in sens_select: + # default uid, unit and descript + unique_id = f"{self.sn}_{report}_{key}" + description_tmp = self.__get_description(key) + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{key}", + friendly_name=key, + value=value, + unit=self.__get_unit(key), + description=description_tmp, + icon=None, + ) + + dict.update(sensors, data) + + return sensors + + def __get_sensors_energy_stream(self, response, sensors): + report = "JTS1_ENERGY_STREAM_REPORT" + d = response["data"]["quota"][report] + sens_select = self.__get_sens_select(report) + + data = {} + for key, value in d.items(): + if key in sens_select: + # default uid, unit and descript + unique_id = f"{self.sn}_{report}_{key}" + special_icon = None + if key.startswith(("pv1", "pv2")): + special_icon = "mdi:solar-power" + description_tmp = self.__get_description(key) + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{key}", + friendly_name=key, + value=value, + unit=self.__get_unit(key), + description=description_tmp, + icon=special_icon, + ) + + dict.update(sensors, data) + + return sensors + + def __get_sensors_ems_change(self, response, sensors): + report = "JTS1_EMS_CHANGE_REPORT" + d = response["data"]["quota"][report] + + sens_select = self.__get_sens_select(report) + + # add mppt Warning/Fault Codes + keys = d.keys() + r = re.compile("mppt.*Code") + wfc = list(filter(r.match, keys)) # warning/fault code keys + sens_select += wfc + + data = {} + for key, value in d.items(): + if key in sens_select: # use only sensors in sens_select + # default uid, unit and descript + unique_id = f"{self.sn}_{report}_{key}" + + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{key}", + friendly_name=key, + value=value, + unit=self.__get_unit(key), + description=self.__get_description(key), + icon=None, + ) + dict.update(sensors, data) + + return sensors + + def __get_sensors_battery(self, response, sensors): + report = "JTS1_BP_STA_REPORT" + d = response["data"]["quota"][report] + keys = list(d.keys()) + + # loop over N batteries: + batts = [s for s in keys if len(s) > 12] + bat_sens_select = self.__get_sens_select(report) + + data = {} + prefix = "bpack" + for ibat, bat in enumerate(batts): + name = prefix + "%i_" % (ibat + 1) + d_bat = json_loads(d[bat]) + for key, value in d_bat.items(): + if key in bat_sens_select: + # default uid, unit and descript + unique_id = f"{self.sn}_{report}_{bat}_{key}" + description_tmp = f"{name}" + self.__get_description(key) + special_icon = None + if key == "bpAmp": + special_icon = "mdi:current-dc" + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{name + key}", + friendly_name=name + key, + value=value, + unit=self.__get_unit(key), + description=description_tmp, + icon=special_icon, + ) + # compute mean temperature of cells + key = "bpTemp" + temp = d_bat[key] + value = sum(temp) / len(temp) + unique_id = f"{self.sn}_{report}_{bat}_{key}" + description_tmp = f"{name}" + self.__get_description(key) + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{name + key}", + friendly_name=name + key, + value=value, + unit=self.__get_unit(key), + description=description_tmp, + icon=None, + ) + + dict.update(sensors, data) + + return sensors + + def __get_sensors_ems_heartbeat(self, response, sensors): + report = "JTS1_EMS_HEARTBEAT" + d = response["data"]["quota"][report] + sens_select = self.__get_sens_select(report) + + data = {} + for key, value in d.items(): + if key in sens_select: + # default uid, unit and descript + unique_id = f"{self.sn}_{report}_{key}" + description_tmp = self.__get_description(key) + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{key}", + friendly_name=key, + value=value, + unit=self.__get_unit(key), + description=description_tmp, + icon=None, + ) + + # special for phases + phases = ["pcsAPhase", "pcsBPhase", "pcsCPhase"] + if phases[1] in d: + for i, phase in enumerate(phases): + for key, value in d[phase].items(): + name = phase + "_" + key + unique_id = f"{self.sn}_{report}_{name}" + + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{name}", + friendly_name=f"{name}", + value=value, + unit=self.__get_unit(key), + description=self.__get_description(key), + icon=None, + ) + + # special for mpptPv + if "mpptHeartBeat" in d: + n_strings = len(d["mpptHeartBeat"][0]["mpptPv"]) # TODO: auch als Sensor? + mpptpvs = [] + for i in range(1, n_strings + 1): + mpptpvs.append(f"mpptPv{i}") + mpptPv_sum = 0.0 + for i, mpptpv in enumerate(mpptpvs): + for key, value in d["mpptHeartBeat"][0]["mpptPv"][i].items(): + unique_id = f"{self.sn}_{report}_mpptHeartBeat_{mpptpv}_{key}" + special_icon = None + if key.endswith("amp"): + special_icon = "mdi:current-dc" + if key.endswith("pwr"): + special_icon = "mdi:solar-power" + + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{mpptpv}_{key}", + friendly_name=f"{mpptpv}_{key}", + value=value, + unit=self.__get_unit(key), + description=self.__get_description(key), + icon=special_icon, + ) + # sum power of all strings + if key == "pwr": + mpptPv_sum += value + + # create total power sensor of all strings + name = "mpptPv_pwrTotal" + unique_id = f"{self.sn}_{report}_mpptHeartBeat_{name}" + + data[unique_id] = PowerOceanEndPoint( + internal_unique_id=unique_id, + serial=self.sn, + name=f"{self.sn}_{name}", + friendly_name=f"{name}", + value=mpptPv_sum, + unit=self.__get_unit(key), + description="Solarertrag aller Strings", + icon="mdi:solar-power", + ) + + dict.update(sensors, data) + + return sensors + + +class AuthenticationFailed(Exception): + """Exception to indicate authentication failure.""" diff --git a/powerocean_powerpulse/icon.png b/powerocean_powerpulse/icon.png new file mode 100644 index 0000000..4c4c498 Binary files /dev/null and b/powerocean_powerpulse/icon.png differ diff --git a/powerocean_powerpulse/manifest.json b/powerocean_powerpulse/manifest.json new file mode 100644 index 0000000..328b9d6 --- /dev/null +++ b/powerocean_powerpulse/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "powerocean", + "name": "Ecoflow PowerOcean", + "codeowners": [ + "@niltrip" + ], + "config_flow": true, + "documentation": "https://github.com/niltrip/powerocean", + "iot_class": "cloud_polling", + "issue_tracker": "https://github.com/niltrip/powerocean/issues", + "version": "2025.01.07" +} \ No newline at end of file diff --git a/powerocean_powerpulse/samplereport.json b/powerocean_powerpulse/samplereport.json new file mode 100644 index 0000000..3091c51 --- /dev/null +++ b/powerocean_powerpulse/samplereport.json @@ -0,0 +1,635 @@ +{ + "code": "0", + "message": "Success", + "data": { + "sysLoadPwr": 823, + "sysGridPwr": -13, + "mpptPwr": 0, + "bpPwr": -836, + "bpSoc": 88, + "sysBatChgUpLimit": 0, + "sysBatDsgDownLimit": 0, + "sysGridSta": 0, + "sysOnOffMachineStat": 0, + "online": 1, + "todayElectricityGeneration": "95.30", + "monthElectricityGeneration": "192.67", + "yearElectricityGeneration": "5298.98", + "totalElectricityGeneration": "7243.73", + "systemName": "Florian Krebs", + "createTime": "2024-09-21 20:36:04", + "location": "Germany-Bayern-Augsburg-Klostergasse 20a", + "timezone": "Europe/Berlin", + "quota": { + "JTS1_ENERGY_STREAM_REPORT": { + "bpSoc": 88, + "mpptPwr": 0, + "bpPwr": -836, + "updateTime": "2025-05-03 03:15:22", + "sysLoadPwr": 823, + "sysGridPwr": -13, + "pv1Pwr": 0, + "pvInvPwr": 0, + "pv2Pwr": 0 + }, + "JTS1_EMS_CHANGE_REPORT": { + "sys14aEnable": false, + "afciFaultMaxValueCh1": 0, + "pcsHighVolOnGrid": 253, + "bpChgDsgSta": 0, + "pcsLowVolTime2": 300, + "duration": 0, + "pcsOngridReconnectFlag": 0, + "emsSgReadyEn": false, + "pcsLowFreqTime1": 100, + "wireless4gIccid": "", + "pcsSendEnd": 0, + "sysBatDsgDownLimit": 15, + "bpOnlineSum": 2, + "emsWorkState": 0, + "pcsOverVolDeratingSwitch": 0, + "emsSgRunStat": 0, + "mppt1WarningCode": 0, + "pcsAntiBackFlowImportCurrentLimit": 0, + "pcsAntiBackFlowSwitch": 1, + "pcsOverFreqDeratingStartDelay": 0, + "pcsActivePowerDeratingPercent": 1, + "mppt1FaultCode": 2, + "pcs10minOverVolTime": 100, + "pcsOverVolTime1": 100, + "pcsAvgOvpProtectValue": 0, + "pcsLowVolOnGrid": 195.5, + "afciFaultValueCh1": 0, + "ethWanStat": 0, + "pcsCospPf4": 0, + "pcsAcWarningCode": 0, + "pcs10minOverVol": 253, + "bpLineOffFlag": 0, + "pcsUnderFreqIncrementRecoverSlope": 0.09, + "pcsReactPwrModeSelect": 0, + "pcsPowerDeratingSet": 200, + "pcsLowVolTime3": 240, + "afciProtectValueCh2": 0, + "devMaxPower": 0, + "sysRateCtrlTime": 60, + "pcsUnderFreqIncrementStart": 49.8, + "afciFaultValueCh2": 0, + "pcsLowVolRideThroughStart1": 184, + "chgDsgMode": 0, + "iot4gSta": 2, + "pcsOfpProtectCnt": 0, + "pcsCospP1": 0.1, + "pcsFaultRecoverOnGridWaitTime": 60000, + "batRelayCloseFailFlag": 0, + "mppt2FaultCode": 2, + "pcsLowVol2": 103.5, + "endTimestamp": 0, + "afciFaultFlagCh2": 0, + "pcsFunctionEnable": 0, + "pcsOverVolDeratingTimeConst": 10, + "pcsLowVolRideThroughRecover": 195.5, + "pcsLowFreqTime2": 100, + "pcsLowVolRideThroughProtectTime3": 1000, + "pcsLowVolRideThroughProtectTime2": 3000, + "sysCalStat": 0, + "pcsReactPwrPercent": 0, + "pcsLowVolRideThroughProtectTime1": 5200, + "pcsOverVolRideThroughStart1": 265.65, + "pcsAutoTestPercent": 0, + "pcsRelayStateShow": 13732943, + "pcsOverVolDeratingStart": 253, + "pcsQuQ3": 0, + "pcsUvp2ProtectValue": 0, + "pcsFaultRecoverHighFreqOnGrid": 50.1, + "pcsPowerDeratingFlag": 5, + "pcsOverFreqDeratingCutoffPower": 0, + "pcsActivePowerSoftstartSwitch": 1, + "pcsCospPf2": -1, + "wifiStaStat": 0, + "pcsFastCheck": 0, + "pcsQuLockinPower": 0, + "sysMulPeakSwitch": false, + "pcsUvp1ProtectCnt": 0, + "pcsOverFreqDeratingRecoverSlopeSwitch": 1, + "pcsFaultRecoverLowFreqOnGrid": 47.53, + "pcsCospPf1": -1, + "iot4gErr": 7, + "pcsCospP3": 1, + "iot4gOn": 1, + "pcsOverVolTime2": 100, + "emsFeedPwr": 0, + "afciEnSet": 0, + "pcsOvpProtectCnt": 0, + "pcsCospP4": 0, + "bpReverseFlag": 0, + "pcsActivePowerSoftstartTime": 666, + "afciSelfTestCmdState": 0, + "afciProtectValueCh1": 0, + "parallelTypeCur": 0, + "emsFeedRatio": 100, + "emsCtrlLedBright": 10, + "userRole": 0, + "pcsOverFreqTime2": 100, + "pcsOverVolDeratingStartingPower": 1, + "afciFaultClearState": 0, + "pcsFaultRecoverHighVolOnGrid": 253, + "pcsUfpProtectCnt": 0, + "sysStateBit": 0, + "pcsOverVolTime3": 0, + "pcsRunSta": "RUNSTA_RUN", + "virtualHardEdition": 1, + "afciSwitchFreqCh1": 0, + "emsFeedMode": 1, + "batRealyStatus": 2, + "pcsQuQ1": 0.6, + "pcsReconnectGridDetectSwitch": 1, + "pcsUnderFreqIncrementRecoverSlopeSwitch": 1, + "pcsRunFsmState": 8669282, + "pcsHvrtLvrtSwitch": 1, + "emsBackupEvent": 0, + "pcsCospP2": 0.5, + "pcsOfpProtectValue": 0, + "sysOnOffMachineStat": 0, + "pcsFaultRecoverLowVolOnGrid": 195.5, + "pcsOverFreqDeratingRecoverSlope": 0.09, + "parallelTypeSet": 0, + "afciEnableCmdState": 0, + "pcsLowVolRideThroughStart2": 103.5, + "afciIsExist": 0, + "afciEn": 0, + "pcsUnderFreqIncrementEnd": 49.8, + "pcsOverFreqDeratingFrozeSwitch": 0, + "bpTotalDsgEnergy": 1368561, + "bpTotalChgEnergy": 1419246, + "sys14aType": 1, + "rateCtrlSwtich": false, + "pcsActivePowerNormalRampUpRate": 60, + "pcsOverVolDeratingEnd": 257.6, + "emsStopAll": 0, + "updateTime": "2025-05-03 03:17:49", + "bpSoc": 87, + "pcsOnGridWaitTime": 60000, + "pcsActivePowerDeratingSwitch": 0, + "pcsReactPwrCompensation": 0.0062, + "pcsOverVolRideThroughProtectTime2": 1000, + "pcsQuMinimumCosphi": 0.4, + "pcsPcsAntiBackFlowProtectSwitch": 0, + "pcsVolRecoverTime": 1000, + "pcsAutoTestState": 0, + "pcsOverFreq1": 51.5, + "parallelType": 0, + "pcsActivePowerSoftStartRate": 0.1, + "pcsLowVolTime1": 3000, + "pcsOverVolDeratingEndPower": 0, + "parallelAllowState": false, + "sysHeatStat": 0, + "bpRestartFlag": 1, + "pcsOverVol2": 287.5, + "pcsUnderFreqIncrementSlope": 0.4, + "pcsAcErrCode": 0, + "pcsOverFreqDeratingEndDelay": 0, + "pcsLowVolRecover": 195.5, + "pcsFreqRecoverTime": 1000, + "pcs10minOverVolSwitch": 1, + "sysMulPeakTime": 1200, + "pcsOverVolDeratingDaleyTime": 0, + "pcsOverFreqTime1": 100, + "afciFaultCntCh1": 0, + "pcsUvp1ProtectValue": 0, + "pcsOverVolRideThroughProtectTime1": 5500, + "pcsHighVolRideThroughRecover": 253, + "pcsOverFreq2": 51.5, + "pcsQuV2": 223.1, + "pcsUnderFreqIncrementFrozeSwitch": 0, + "pcsFreqLocalCommand": 1, + "pcsHighFreqOnGrid": 50.1, + "pcsUvp2ProtectCnt": 0, + "pcsDcErrCode": 0, + "pcsAntiBackFlowExportCurrentLimit": 0, + "pcsOverVol1": 287.5, + "sysTypeCfg": 0, + "pcsOverVol3": 0, + "pcsPfValue": 1, + "sysMeterCfg": 0, + "emsWordMode": "WORKMODE_SELFUSE", + "pcsOverFreqDeratingPowerBased": 2, + "relay14a": 0, + "pcsLowVol1": 184, + "emsCtrlLedType": 1, + "afciFaultCntCh2": 0, + "pcsOverVolRecover": 253, + "afciFaultFlagCh1": 0, + "pcsIslandDetectSwitch": 1, + "pcsActivePowerGradient": 0.0033, + "pcsOverVolRideThroughStart2": 287.5, + "pcsOverFreqRecover": 50.1, + "pcsQuQ2": 0, + "afciSwitchFreqCh2": 0, + "pcsCospPf3": -0.9, + "pcsQuTimeConst": 10, + "pcsQuV4": 246.1, + "pcsLowFreq2": 47.5, + "pcsLowVol3": 57.5, + "pcsLowFreqRecover": 47.53, + "pcsOvpProtectValue": 0, + "pcsLowFreq1": 47.5, + "pcsQuV1": 213.90001, + "pcsAutoTestFlag": 0, + "pcsOverFreqDeratingEnd": 50.2, + "pcsQuQ4": -0.6, + "pcsOverFreqDeratingSlope": 0.4, + "pcsFreqExternalSignal": 0, + "chgDsgPwr": 700, + "pcsOverFreqDeratingSwitch": 0, + "evBindList": { + "evSn": [ + "AC31ZEH4AG130052" + ] + }, + "pcsUfpProtectValue": 0, + "pcsLowFreqOnGrid": 47.53, + "mppt2WarningCode": 0, + "pcsUnderFreqIncrementEndDelay": 0, + "afciFaultMaxValueCh2": 0, + "pcsUnderFreqIncrementSwitch": 0, + "pcsQuV3": 236.9, + "pcsQuLockoutPower": 0, + "afciSellfTestResult": 0, + "iot4gPdp": -1, + "pcsLowVolRideThroughStart3": 34.5, + "pcsSafetyCountryCodeSelection": 4, + "pcsRelaySelfCheckSta": 0, + "pcsAvgOvpProtectCnt": 0, + "sysGridSta": 0, + "batSoftRelayStatus": 0, + "pcsUnderFreqIncrementStartDelay": 0, + "pcsOverFreqDeratingStart": 50.2 + }, + "JTS1_BP_STA_REPORT": { + "HJ3AZDH5ZG3G0490": "{\n \"bpPwr\": -0.8074197,\n \"bpSoc\": 88,\n \"bpSoh\": 100,\n \"bpTemp\": [29.0, 30.0, 29.0, 30.0, 30.0, 29.0, 29.0, 29.0, 29.0],\n \"bpCellMaxVol\": 3319.0,\n \"bpCellMinVol\": 3318.0,\n \"bpRunSta\": \"RUNSTA_RUN\",\n \"bpVol\": 53.066,\n \"bpAmp\": -0.015215387,\n \"bpBusVol\": 799.216,\n \"bpErrCode\": 0,\n \"bpCellVol\": [3319.0, 3319.0, 3318.0, 3318.0, 3318.0, 3319.0, 3318.0, 3318.0, 3318.0, 3318.0, 3318.0, 3318.0, 3318.0, 3319.0, 3318.0, 3318.0],\n \"bpDsrc\": 1,\n \"bpSn\": \"SEozQVpESDVaRzNHMDQ5MA==\",\n \"bpCycles\": 155,\n \"bpBalanceState\": 0,\n \"bpHvMosTemp\": 40.0,\n \"bpLvMosTemp\": 38.0,\n \"bpPtcTemp\": 29.0,\n \"bpHtsTemp\": 34.0,\n \"bpBusNegTemp\": 35.0,\n \"bpBusPosTemp\": 36.0,\n \"bpEnvTemp\": 35.0,\n \"bpAccuChgCap\": 13263555,\n \"bpAccuDsgCap\": 13246290,\n \"bpDesignCap\": 100000,\n \"bpFullCap\": 100000,\n \"bpMaxCellTemp\": 30.0,\n \"bpMinCellTemp\": 29.0,\n \"bpMaxMosTemp\": 40.0,\n \"bpMinMosTemp\": 38.0,\n \"bpBmsFault\": 0,\n \"bpEcloundSoc\": 65535,\n \"bpHeartbeatVer\": 33,\n \"bpTimestamp\": 1746213449,\n \"bpRealSoc\": ...", + "updateTime": "2025-05-03 03:17:39", + "HJ3AZDH5ZG3G0384": "{\n \"bpPwr\": -841.428,\n \"bpSoc\": 88,\n \"bpSoh\": 100,\n \"bpTemp\": [30.0, 31.0, 31.0, 31.0, 31.0, 30.0, 30.0, 31.0, 31.0],\n \"bpCellMaxVol\": 3315.0,\n \"bpCellMinVol\": 3313.0,\n \"bpRunSta\": \"RUNSTA_RUN\",\n \"bpVol\": 53.005,\n \"bpAmp\": -15.874502,\n \"bpBusVol\": 798.7922,\n \"bpErrCode\": 0,\n \"bpCellVol\": [3315.0, 3314.0, 3314.0, 3314.0, 3314.0, 3313.0, 3313.0, 3314.0, 3313.0, 3314.0, 3314.0, 3314.0, 3314.0, 3314.0, 3314.0, 3314.0],\n \"bpDsrc\": 2,\n \"bpSn\": \"SEozQVpESDVaRzNHMDM4NA==\",\n \"bpCycles\": 160,\n \"bpBalanceState\": 0,\n \"bpHvMosTemp\": 38.0,\n \"bpLvMosTemp\": 37.0,\n \"bpPtcTemp\": 31.0,\n \"bpHtsTemp\": 35.0,\n \"bpBusNegTemp\": 35.0,\n \"bpBusPosTemp\": 36.0,\n \"bpEnvTemp\": 35.0,\n \"bpAccuChgCap\": 13259968,\n \"bpAccuDsgCap\": 13221698,\n \"bpDesignCap\": 100000,\n \"bpFullCap\": 100000,\n \"bpMaxCellTemp\": 31.0,\n \"bpMinCellTemp\": 30.0,\n \"bpMaxMosTemp\": 38.0,\n \"bpMinMosTemp\": 37.0,\n \"bpBmsFault\": 0,\n \"bpEcloundSoc\": 65535,\n \"bpHeartbeatVer\": 33,\n \"bpTimestamp\": 1746213455,\n \"bpRealSoc\": 88....", + "": "{\n \"bpTemp\": [],\n \"bpCellVol\": []\n}" + }, + "JTS1_LOGY_DEV_REPORT": { + "HPReport": { + "devSn": "", + "online": 0, + "errorCode": "" + }, + "updateTime": "2025-05-03 03:12:27" + }, + "JTS1_EMS_PARAM_CHANGE_REPORT": { + "smartCtrl": false, + "updateTime": "2025-05-03 03:12:27", + "breakerCapacityMax": 32, + "energyEfficientEnable": false, + "breakerEnableState": true, + "bpBurst": false, + "lowerPowerStat": false, + "sysZone": 0, + "sysTimeTab": 0, + "devSoc": 80 + }, + "JTS1_ERROR_CODE_MASK_REPORT": { + "errorCode": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "updateTime": "2025-05-03 02:01:35" + }, + "JTS1_EDEV_BIND_LIST_REPORT": { + "213": "{\n \"devAddr\": 213,\n \"devItem\": []\n}", + "214": "{\n \"devAddr\": 214,\n \"devItem\": []\n}", + "updateTime": "2025-05-03 02:01:37" + }, + "JTS1_EMS_ALL_TIMER_TASK_REPORT": { + "timeTaskCfg": [], + "updateTime": "2025-05-02 15:34:18" + }, + "JT303_EDEV_SYS_REPORT": { + "devLastInfo": 31, + "devInfoFix": { + "devSubInfoFix": [ + { + "devAttrCur": 197424, + "bindType": 1, + "devInfo": { + "devAddr": 209, + "devSn": "AC31ZEH4AG130052", + "subAddr": 1 + }, + "devFlag": 131175, + "errorCode": 0, + "onlineBits": 2, + "modbusAddr": 52, + "warnCode": 0, + "memDevIdx": 0, + "targetMax": 11000, + "faultCode": 0, + "sceneType": 3, + "currentPrio": 1, + "devStateInfo": 5 + } + ] + }, + "freeNum": 35, + "devInfoVar": { + "devSubInfoVar": [ + { + "allocatedPower": 0, + "realPowerLock": 0, + "refPower": 0, + "devInfo": { + "devAddr": 209, + "devSn": "AC31ZEH4AG130052", + "subAddr": 1 + } + } + ] + }, + "updateTime": "2025-05-03 03:11:48", + "usedNum": 1, + "sysFlag": 35, + "devLastMinInfo": 31, + "socDev": 80, + "dispatchType": 2, + "socCur": 88.93504, + "stratType": 1, + "solarFlag": 985, + "devFirstInfo": 32, + "startState": 9, + "pclPwrBase": -851.92706, + "feedPwrCap": 12000 + }, + "JTS1_EMS_HEARTBEAT": { + "emsBusVoltRipple": 1.241394, + "pcsInterruptOccupancyRate": 88, + "bpRemainWatth": 9355, + "emsStartFsmState": 1078644260, + "emsAcMakeupMinSoc": 0, + "pcsActivePowerLimitDn": -12000, + "emsMpptStartupState": 1, + "pcsAverageVoltage": 1.0132734, + "updateTime": "2025-05-03 03:18:33", + "pcsActivePowerRef": 663.75104, + "pcsAPhase": { + "vol": 232.77829, + "amp": 1.2337433, + "actPwr": -220.07922, + "reactPwr": 184.506, + "apparentPwr": 287.18866 + }, + "pcsRelayStateShow": 13732943, + "emsMpptRunState": 0, + "pcsBpPower": -816.97534, + "pcsVbusRef": 770, + "pcsVgridThd": 0.0016662398, + "pcsLoadInfo": [ + { + "vol": 232.77829, + "amp": 0, + "freq": 49.990864, + "pwr": 0 + }, + { + "vol": 232.9028, + "amp": 0, + "freq": 49.99184, + "pwr": 0 + }, + { + "vol": 232.37206, + "amp": 0, + "freq": 49.99131, + "pwr": 0 + } + ], + "emsBpSelfcheckState": 1, + "meterHeartBeat": [ + { + "meterAddr": 106, + "meterType": 15, + "meterData": [ + -135.6, + 234.4, + -110.5, + -11.700012 + ] + } + ], + "pcsActPwr": -656.1483, + "emsBpStartupState": 1, + "pcsDci": 0.0012805257, + "emsBpPower": -813.38104, + "emsBusVoltErrSlidFilter": 0.66655725, + "emsLpStateFlag": 0, + "pcsBpPowerChgLimit": 4999.1465, + "emsBpChgRequest": 0, + "pcsAcFreq": 49.99, + "pcsGridSafetyFuncRecord": 234905714, + "emsMpptSelfcheckState": 1, + "pcsBusVolt": 797.13464, + "emsPvInvPwr": 0, + "pcsActivePowerLimitUp": 663.75104, + "mpptHeartBeat": [ + { + "mpptPv": [ + { + "vol": 42.0932, + "amp": 0.05277126, + "lightSta": true, + "pwr": 2.221311 + }, + { + "vol": 41.54573, + "amp": 0.08966458, + "lightSta": true, + "pwr": 3.7251804 + } + ], + "mpptTempVal": [ + 41.2, + 43.5, + 43.1 + ], + "mpptInsResist": 0 + } + ], + "emsBpChg": 5000.0005, + "emsMpptHbState": 17, + "emsLpMpptCnt": 0, + "pcsMeterPower": -13.199997, + "emsAcMakeupCnt": 0, + "emsBpDsg": -6600, + "emsLpState": 0, + "emsLpType": 0, + "emsNtcTempMax": 43.5, + "emsAcMakeupTriggleSoc": 17, + "emsLpBpCnt": 0, + "pcsBPhase": { + "vol": 232.9028, + "amp": 1.2401944, + "actPwr": -218.97543, + "reactPwr": 188.36415, + "apparentPwr": 288.84476 + }, + "pcsGridSafetyStateRecord": 9486428, + "emsMpptModStat": 1, + "pcsReactivePowerRef": 0, + "pcsDcv": 0, + "emsBpAliveNum": 2, + "emsAcMakeupExitSoc": 19, + "emsBusVolt": 800.071, + "emsActiveOffGridCmd": 0, + "emsSocCalibState": 0, + "pcsGridInvErrorRms": 6.710472, + "emsSysCfg": 7, + "bpDsgTime": 672, + "pcsLeakAmp": -6.4453125, + "pcsCommInterfaceState": 17120, + "emsSocCalibRequest": 0, + "emsPcsSelfcheckState": 1, + "pcsCPhase": { + "vol": 232.37206, + "amp": 1.2358344, + "actPwr": -217.09364, + "reactPwr": 187.98643, + "apparentPwr": 287.17337 + }, + "emsPcsStartupState": 1 + }, + "JTS1_EMS_PV_INV_ENERGY_STREAM_REPORT": { + "pvInvPwr": 0, + "updateTime": "2025-05-03 03:15:22" + }, + "JT303_EDEV_PRIORITY_LIST_REPORT": { + "devPrioReport": [ + { + "devPrio": 1, + "data": "Aw==", + "enable": true, + "devInfo": { + "devAddr": 209, + "devSn": "AC31ZEH4AG130052", + "subAddr": 1 + } + } + ], + "updateTime": "2025-05-03 02:11:24" + }, + "JTS1_EVCHARGING_REPORT": { + "evSn": "AC31ZEH4AG130052", + "workMode": 2, + "useGridFirst": 1, + "evOnoffSet": 0, + "errorCode": "AAAAAAAAAAA=", + "evPhaseSet": 1, + "orderState": 2, + "orderTime": 29670, + "orderEndTimestamp": 1746213532, + "defaultVehicleId": "534", + "chargingStatus": "EV_CHG_STS_SUSPENDED_EVSE", + "expectChargingEnergy": 0, + "evPwr": 0, + "switchBits": 0, + "evDetectPhase": 3, + "orderStartTimestamp": 1746183862, + "onlineBits": 2, + "updateTime": "2025-05-03 03:18:53", + "evUserManual": 0, + "evChargingEnergy": 17256, + "evCurrSet": 0, + "chargeVehicleId": "534", + "evPlugAndPlay": 1, + "evExeMode": 2, + "stopChargingSoc": 255 + }, + "JTS1_ERROR_CHANGE_REPORT": { + "bpErrCode": [ + { + "errCode": [], + "moduleSn": "SEozQVpESDVaRzNHMDQ5MA==" + }, + { + "errCode": [], + "moduleSn": "SEozQVpESDVaRzNHMDM4NA==" + } + ], + "emsErrCode": { + "errCode": [], + "moduleSn": "SEozN1pESDVaRzVXMDEwOQ==" + }, + "pcsErrCode": { + "errCode": [], + "moduleSn": "SEozMTIxMDJCRzRKMTMzMQ==" + }, + "updateTime": "2025-05-03 03:12:28" + }, + "JTS1_ECOLOGY_DEV_BIND_LIST_REPORT": { + "devItem": [], + "updateTime": "2025-05-03 02:01:36" + }, + "JTS1_EV_CHARGING_ENERGY_STREAM_REPORT": { + "updateTime": "2025-05-03 03:15:24", + "evStreamShow": [ + { + "evSn": "AC31ZEH4AG130052", + "evPwr": 0 + } + ] + }, + "JTS1_EV_CHARGING_TIMER_TASK_REPORT": { + "timeTaskCfg": [ + { + "taskIndex": 1, + "timeParam": 0, + "param": 16, + "isCfg": 0, + "isEffect": false, + "timeMode": 65, + "timeTable": [ + 19660800 + ], + "type": 1, + "isEnable": false + } + ], + "evSn": "AC31ZEH4AG130052", + "updateTime": "2025-05-03 02:01:36" + } + } + }, + "eagleEyeTraceId": "ea1a2a582917462135513308071d0007", + "tid": "" + } \ No newline at end of file diff --git a/powerocean_powerpulse/sensor.py b/powerocean_powerpulse/sensor.py new file mode 100644 index 0000000..8152d8f --- /dev/null +++ b/powerocean_powerpulse/sensor.py @@ -0,0 +1,375 @@ +from datetime import timedelta +from collections import defaultdict + +from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import SensorDeviceClass +from homeassistant.components.sensor import SensorStateClass +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers import entity_registry +from homeassistant.exceptions import IntegrationError + +from .const import ( + DOMAIN, + _LOGGER, + ATTR_PRODUCT_DESCRIPTION, + ATTR_DESTINATION_NAME, + ATTR_SOURCE_NAME, + ATTR_UNIQUE_ID, + ATTR_PRODUCT_SERIAL, + ATTR_PRODUCT_NAME, + ATTR_PRODUCT_VENDOR, + ATTR_PRODUCT_BUILD, + ATTR_PRODUCT_VERSION, + ATTR_PRODUCT_FEATURES, + ISSUE_URL_ERROR_MESSAGE, +) + +from .ecoflow import Ecoflow, AuthenticationFailed + + +# Setting up the adding and updating of sensor entities +async def async_setup_entry(hass, config_entry, async_add_entities): + # Retrieve the API instance from the config_entry data + ecoflow = hass.data[DOMAIN][config_entry.entry_id] + device_id = ecoflow.device["serial"] + + # Call EcoFlow to get access to the API data + try: + auth_check = await hass.async_add_executor_job(ecoflow.authorize) + + if not auth_check: + # If device returns False or is empty, log an error and return + _LOGGER.warning( + f"{device_id}: It appears the PowerOcean device is offline or has changed host." + + ISSUE_URL_ERROR_MESSAGE + ) + + except AuthenticationFailed as error: + _LOGGER.warning(f"{device_id}: Authentication failed: {error}") + return + + try: + # Fetch the sensor data from the device + data = await hass.async_add_executor_job(ecoflow.fetch_data) + + if not data: + # If data returns False or is empty, log an error and return + _LOGGER.warning( + f"{device_id}: Failed to fetch sensor data => authentication failed or no data." + + ISSUE_URL_ERROR_MESSAGE + ) + return + + # Exception if data cannot be fetched + except IntegrationError as error: + _LOGGER.warning( + f"{device_id}: Failed to fetch sensor data: {error}" + + ISSUE_URL_ERROR_MESSAGE + ) + return + + # Get device id and then reset the device specific list of sensors for updates + # to ensure it's empty before adding new entries + + # Initialize or clear the sensor list for this device + hass.data[DOMAIN]["device_specific_sensors"][device_id] = [] + + # Register entities and add them to the list for schedule updates on each device + # which is stored within hass.data + for unique_id, endpoint in data.items(): + # Get individual sensor entry from API + sensor = PowerOceanSensor(ecoflow, endpoint) + + # Add sensors to the device specific list of sensors to be updated, via hass.data as also used in unload + hass.data[DOMAIN]["device_specific_sensors"][device_id].append(sensor) + + # Register sensor + async_add_entities([sensor], False) + + device_specific_sensors = hass.data[DOMAIN]["device_specific_sensors"] + _LOGGER.debug( + f"{device_id}: List of device_specific_sensors[device_id]: " + f"{device_specific_sensors[device_id]}" + ) + + # Log the number of sensors registered (and added to the update list) + _LOGGER.debug( + f"{device_id}: All '{len(device_specific_sensors[device_id])}' sensors have registered." + ) + + # Schedule updates + async def async_update_data(now): + # If device deleted but HASS not restarted, then don't bother continuing + if device_id not in hass.data.get(DOMAIN, {}).get( + "device_specific_sensors", {} + ): + return False + + _LOGGER.debug(f"{device_id}: Preparing to update sensors at {now}") + + # Fetch the full dataset once from the API + try: + full_data = await hass.async_add_executor_job(ecoflow.fetch_data) + + except Exception as e: + _LOGGER.error( + f"{device_id}: Error fetching data from the device: {e}" + + ISSUE_URL_ERROR_MESSAGE + ) + return + + # Fetch the registry and check if sensors are enabled + registry = entity_registry.async_get(hass) + + # Set counters to zero + counter_updated = 0 # Successfully updated sensors + counter_disabled = 0 # Disabled sensors, not to be updated + counter_unchanged = 0 # Skipped sensors since value has not changed + counter_error = 0 # Skipped sensors due to some error, such as registry not found or no data from API + + # Get the list of device specific sensors from hass.data + if device_id in hass.data.get(DOMAIN, {}).get("device_specific_sensors", {}): + device_specific_sensors = hass.data[DOMAIN]["device_specific_sensors"] + + # ---------------------------------------------- + # Now loop through the sensors to be updated + # ---------------------------------------------- + for sensor in device_specific_sensors[device_id]: + entity_id = registry.async_get_entity_id( + "sensor", DOMAIN, sensor.unique_id + ) + if entity_id: + entity = registry.entities.get(entity_id) # get entity + # entity is enabled + if entity and not entity.disabled_by: + sensor_data = full_data.get(sensor.unique_id) + # _LOGGER.debug(f"{device_id}: Sensor {sensor.name} enabled.") + if sensor_data: + # _LOGGER.debug( + # f"{device_id}: Sensor {sensor.name} has API data to update {sensor_data}" + # ) + + # Check if current state value differs from new API value, + # or current state has not initialized + if ( + str(sensor._state).strip() + != str(sensor_data.value).strip() + ): + # _LOGGER.debug( + # f"{device_id}: Sensor {sensor.name} marked for update: current state = " + # f"{sensor._state} with new value = {sensor_data.value}" + # ) + # Now update the sensor with new values + # update_status returns 1 for upated, 0 for skipped or error + update_status = await sensor.async_update(sensor_data) + counter_updated = counter_updated + update_status + else: + # _LOGGER.debug( + # f"{device_id}: Sensor {sensor.name} skipped update! Current value = " + # f"{sensor._state}, new value = {sensor_data.value}" + # ) + counter_unchanged = counter_unchanged + 1 + else: + _LOGGER.warning( + f"{device_id}: Sensor {sensor.name}: found no data for update!" + + ISSUE_URL_ERROR_MESSAGE + ) + counter_error = counter_error + 1 + else: + # _LOGGER.debug( + # f"{device_id}: Sensor {sensor.name} is disabled, skipping update" + # ) + counter_disabled = counter_disabled + 1 + else: + _LOGGER.warning( + f"{device_id}: Sensor {sensor.name} not found in the registry, skipping update" + + ISSUE_URL_ERROR_MESSAGE + ) + counter_error = counter_error + 1 + + # Log summary of updates + _LOGGER.debug( + f"{device_id}: A total of {counter_updated} sensors have been updated. " + f"Number of disabled sensors or skipped updates = {counter_disabled} " + f"Number of sensors with constant values = {counter_unchanged} " + f"Number of sensors with errors = {counter_error}" + ) + + # Device not in list: must have been deleted, will resolve post re-start + else: + _LOGGER.warning( + f"{device_id}: Sensor must have been deleted, re-start of HA recommended." + ) + + # Get the polling interval from the options, defaulting to 5 seconds if not set + polling_interval = timedelta( + seconds=config_entry.options.get("polling_interval", 5) + ) + + async_track_time_interval(hass, async_update_data, polling_interval) + + +# This is the actual instance of SensorEntity class +class PowerOceanSensor(SensorEntity): + """Representation of a PowerOcean Sensor.""" + + def __init__(self, ecoflow: Ecoflow, endpoint): + """Initialize the sensor.""" + # Make Ecoflow and the endpoint parameters from the Sensor API available + self.ecoflow = ecoflow + self.endpoint = endpoint + + # Set Friendly name when sensor is first created + self._attr_unique_id = endpoint.name + self._attr_has_entity_name = True + self._attr_name = endpoint.friendly_name + self._name = endpoint.friendly_name + + # The unique identifier for this sensor within Home Assistant + # has nothing to do with the entity_id, it is the internal unique_id of the sensor entity registry + self._unique_id = endpoint.internal_unique_id + + # Set the icon for the sensor based on its unit, ensure the icon_mapper is defined + # Default handled in function + # self._icon = PowerOceanSensor.icon_mapper.get(endpoint.unit) + self._icon = endpoint.icon + + # The initial state/value of the sensor + self._state = endpoint.value + + # The unit of measurement for the sensor + self._unit = endpoint.unit + + # Set entity category to diagnostic for sensors with no unit + if ecoflow.options.get("group_sensors") and not endpoint.unit: + self._attr_entity_category = EntityCategory.DIAGNOSTIC + + # If diagnostics entity then disable sensor by default + if ecoflow.options.get("disable_sensors") and not endpoint.unit: + self._attr_entity_registry_enabled_default = False + + @property + def should_poll(self): + """async_track_time_intervals handles updates.""" + return False + + @property + def unique_id(self): + """Return the unique ID of the sensor.""" + return self._unique_id + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit + + @property + def device_class(self): + """Return the device class of this entity, if any.""" + if self._unit == "°C": + return SensorDeviceClass.TEMPERATURE + elif self._unit == "%": + return SensorDeviceClass.BATTERY + elif self._unit in {"Wh", "kWh"}: + return SensorDeviceClass.ENERGY + elif self._unit == "W": + return SensorDeviceClass.POWER + elif self._unit == "V": + return SensorDeviceClass.VOLTAGE + elif self._unit == "A": + return SensorDeviceClass.CURRENT + else: + return None + + @property + def state_class(self): + """Return the state class of this entity, if any.""" + if self._unit in {"°C", "h", "W", "V", "A"}: + return SensorStateClass.MEASUREMENT + elif self._unit in {"Wh", "kWh"}: + return SensorStateClass.TOTAL_INCREASING + else: + return None + + @property + def extra_state_attributes(self): + """Return the state attributes of this device.""" + attr = {} + + attr[ATTR_PRODUCT_DESCRIPTION] = self.endpoint.description + attr[ATTR_UNIQUE_ID] = self.endpoint.internal_unique_id + attr[ATTR_PRODUCT_VENDOR] = self.ecoflow.device["vendor"] + attr[ATTR_PRODUCT_NAME] = self.ecoflow.device["name"] + attr[ATTR_PRODUCT_SERIAL] = self.endpoint.serial + attr[ATTR_PRODUCT_VERSION] = self.ecoflow.device["version"] + attr[ATTR_PRODUCT_BUILD] = self.ecoflow.device["build"] + attr[ATTR_PRODUCT_FEATURES] = self.ecoflow.device["features"] + + return attr + + @property + def device_info(self): + """Return device specific attributes.""" + # The unique identifier of the device is the serial number + return { + "identifiers": {(DOMAIN, self.ecoflow.device["serial"])}, + "name": self.ecoflow.device["name"], + "manufacturer": "ECOFLOW", + } + + @property + def icon(self): + """Return the icon of the sensor.""" + return self._icon + + # icon_mapper = defaultdict( + # lambda: "mdi:alert-circle", + # { + # "°C": "mdi:thermometer", + # "%": "mdi:flash", + # "s": "mdi:timer", + # "Wh": "mdi:solar-power-variant-outline", + # "h": "mdi:timer-sand", + # }, + # ) + + # This is to register the icon settings + async def async_added_to_hass(self): + """Call when the sensor is added to Home Assistant.""" + self.async_write_ha_state() + + # Update of Sensor values + async def async_update(self, sensor_data=None): + """Update the sensor with the provided data.""" + if sensor_data is None: + _LOGGER.warning( + f"{self.ecoflow.device['serial']}: No new data provided for sensor '{self.name}' update" + + ISSUE_URL_ERROR_MESSAGE + ) + update_status = 0 + return + + try: + self._state = sensor_data.value + update_status = 1 + self.async_write_ha_state() + + except Exception as error: + _LOGGER.error( + f"{self.ecoflow.device['serial']}: Error updating sensor {self.name}: {error}" + + ISSUE_URL_ERROR_MESSAGE + ) + update_status = 0 + + return update_status diff --git a/powerocean_powerpulse/strings.json b/powerocean_powerpulse/strings.json new file mode 100644 index 0000000..749956d --- /dev/null +++ b/powerocean_powerpulse/strings.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "description": "[%key:common::config_flow::data::description%]", + "data": { + "serialnumber": "[%key:common::config_flow::data::serialnumber%]", + "username": "[%key:common::config_flow::data::username%]", + "password": "[%key:common::config_flow::data::password%]" + } + }, + "device_options": { + "description": "[%key:common::config_flow::data::description%]", + "data": { + "custom_device_name": "[%key:common::config_flow::data::custom_device_name%]", + "polling_time": "[%key:common::config_flow::data::polling_time%]", + "group_sensors": "[%key:common::config_flow::data::group_sensors%]", + "disable_sensors": "[%key:common::config_flow::data::disable_sensors%]" + } + } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + } + } +} diff --git a/powerocean_powerpulse/translations/de.json b/powerocean_powerpulse/translations/de.json new file mode 100644 index 0000000..d0fa882 --- /dev/null +++ b/powerocean_powerpulse/translations/de.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Das Gerät ist bereits konfiguriert" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ungültige Authentifizierung", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "user": { + "description": "Bitte geben Sie unten Ihre PowerOcean Geräteinformationen ein:", + "data": { + "serialnumber": "Seriennummer des Wechselrichters", + "password": "Passwort", + "username": "Benutzername" + } + }, + "device_options": { + "description": "Bitte wählen Sie die folgenden Optionen aus:", + "data": { + "custom_device_name": "Benutzerfreundlicher Gerätename", + "polling_time": "Abfragezeit (in Sekunden), um Sensoren vom Gerät zu aktualisieren", + "group_sensors": "Gruppieren Sie Sensoren auf der Geräteseite", + "disable_sensors": "Diagnosesensoren deaktivieren" + } + } + } + } +} \ No newline at end of file diff --git a/powerocean_powerpulse/translations/en.json b/powerocean_powerpulse/translations/en.json new file mode 100644 index 0000000..a9fa5b5 --- /dev/null +++ b/powerocean_powerpulse/translations/en.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "unknown": "Unexpected error" + }, + "step": { + "user": { + "description": "Please enter your PowerOcean device information below:", + "data": { + "serialnumber": "Inverter serial number", + "password": "Password", + "username": "Username" + } + }, + "device_options": { + "description": "Please select the following options:", + "data": { + "custom_device_name": "Friendly device name", + "polling_time": "Polling time (in seconds) to update sensors from device", + "group_sensors": "Group sensors on device page", + "disable_sensors": "Disable diagnostics sensors" + } + } + } + } +} \ No newline at end of file diff --git a/powerocean_powerpulse/variants/dcfit.json b/powerocean_powerpulse/variants/dcfit.json new file mode 100644 index 0000000..caab8ee --- /dev/null +++ b/powerocean_powerpulse/variants/dcfit.json @@ -0,0 +1,100 @@ +{ + "data": [ + "sysLoadPwr", + "sysGridPwr", + "mpptPwr", + "bpPwr", + "bpSoc", + "online", + "systemName", + "createTime", + "location", + "timezone" + ], + "JTS1_ENERGY_STREAM_REPORT": [], + "JTS1_EMS_CHANGE_REPORT": [], + "JTS1_BP_STA_REPORT": [], + "JTS1_ENERGY_STREAM_REPORT": [ + "bpSoc", + "heatingPower", + "dcdcPwr", + "bpPwr", + "pvPwr", + "gridPwr", + "loadPwr", + "timezone", + "updateTime", + "timestamp" + ], + "JTS1_EMS_HEARTBEAT": [ + "emsSystemState", + "mpptRatePower", + "gridOutDayEnergy", + "meterACurrent", + "meterBCurrent", + "bpInDayEnergy", + "meterPowerFactor", + "isPvToInvDirectly", + "bpRemainWatth", + "bp1SocCoefficient", + "sysRunMeterFeedPower", + "inverterBand", + "loadDayEnergy", + "startDischargePower", + "bpDichargeAbilityAdjustValueLv2", + "bpDichargeAbilityAdjustValueLv1", + "meterAddress", + "gridFeedRate", + "currentNetif", + "batterySocLowerLimit", + "bpDsgTime", + "bpChgDsgSta", + "autoDetectStartPowerEn", + "meterPhasePower", + "sysErrCode", + "meterTotalPower", + "bpBusVoltageBaseCoefficient", + "bpSaveSocStopDelay", + "bpOutDayEnergy", + "bpBusVoltCoefficient", + "pvInvWiringMode", + "underVoltageProtectPoint", + "batterySocUpperLimit", + "bpRunDelay", + "meterBVoltage", + "bpStopDelay", + "errCode", + "gridInDayEnergy", + "sysRunMeterTakePower", + "bpSoc", + "bp3SocCoefficient", + "bpErrorCodeMask", + "workingMode", + "appCtrlState", + "bpSocBaseCoefficient", + "emsErrorCodeMask", + "meterRatio", + "sysWorkSta", + "pv2ErrorCodeMask", + "pv1ErrorCodeMask", + "manualSetStartChargePower", + "pvWiringType", + "emsErrCode", + "bp2SocCoefficient", + "mpptWithstandVoltage", + "meterCVoltage", + "updateTime", + "pvaErrorCodeMask", + "meterAVoltage", + "invWiringType", + "disOrChargeAbilityStepValue", + "reportCycleTime", + "pvInDayEnergy", + "meterType", + "meterCCurrent", + "mpptVoltageMinimum", + "mpptVoltageMaximum", + "gridFeedPowerMinimum", + "epoSwitchState" + ] +} \ No newline at end of file diff --git a/powerocean_powerpulse/variants/powerocean.json b/powerocean_powerpulse/variants/powerocean.json new file mode 100644 index 0000000..61a86f5 --- /dev/null +++ b/powerocean_powerpulse/variants/powerocean.json @@ -0,0 +1,45 @@ +{ + "data": [ + "sysLoadPwr", + "sysGridPwr", + "mpptPwr", + "bpPwr", + "online", + "dcdcPwr", + "todayElectricityGeneration", + "monthElectricityGeneration", + "yearElectricityGeneration", + "totalElectricityGeneration", + "systemName", + "createTime" + ], + "JTS1_ENERGY_STREAM_REPORT": [ + "pv1Pwr", + "pvInvPwr", + "pv2Pwr" + ], + "JTS1_EMS_CHANGE_REPORT": [ + "bpTotalChgEnergy", + "bpTotalDsgEnergy", + "bpSoc", + "bpOnlineSum", + "emsCtrlLedBright" + ], + "JTS1_BP_STA_REPORT": [ + "bpPwr", + "bpSoc", + "bpSoh", + "bpVol", + "bpAmp", + "bpCycles", + "bpSysState", + "bpRemainWatth" + ], + "JTS1_EMS_HEARTBEAT": [ + "bpRemainWatth", + "emsBpAliveNum", + "emsBpPower", + "pcsActPwr", + "pcsMeterPower" + ] +} \ No newline at end of file diff --git a/scenes.yaml b/scenes.yaml new file mode 100644 index 0000000..df1cb3a --- /dev/null +++ b/scenes.yaml @@ -0,0 +1,106 @@ +- id: '1737097386778' + name: 'Day - Shutters ' + entities: + cover.kuche_rolladen: + current_position: 100 + friendly_name: Küche Rolladen + supported_features: 15 + state: open + cover.buro_rollladen: + current_position: 100 + friendly_name: Büro Rollladen + supported_features: 15 + state: open + cover.esszimmer_rollladen_fenster: + current_position: 100 + friendly_name: Esszimmer Rollladen Fenster + supported_features: 15 + state: open + cover.gastezimmer_rollladen: + current_position: 100 + friendly_name: Gästezimmer Rollladen + supported_features: 15 + state: open + cover.kinderzimmer_rollladen: + current_position: 100 + friendly_name: Kinderzimmer Rollladen + supported_features: 15 + state: open + cover.schlafzimmer_rolladen: + current_position: 100 + friendly_name: Schlafzimmer Rolladen + supported_features: 15 + state: open + cover.esszimmer_rolladen_terrasse: + friendly_name: Esszimmer Rolladen Terrasse + supported_features: 15 + current_position: 100 + state: open + metadata: + cover.kuche_rolladen: + entity_only: true + cover.buro_rollladen: + entity_only: true + cover.esszimmer_rollladen_fenster: + entity_only: true + cover.gastezimmer_rollladen: + entity_only: true + cover.kinderzimmer_rollladen: + entity_only: true + cover.schlafzimmer_rolladen: + entity_only: true + cover.esszimmer_rolladen_terrasse: + entity_only: true +- id: '1737276651334' + name: Night - Shutters + entities: + cover.kuche_rolladen: + current_position: 0 + friendly_name: Küche Rolladen + supported_features: 15 + state: closed + cover.buro_rollladen: + current_position: 0 + friendly_name: Büro Rollladen + supported_features: 15 + state: closed + cover.esszimmer_rollladen_fenster: + current_position: 0 + friendly_name: Esszimmer Rollladen Fenster + supported_features: 15 + state: closed + cover.gastezimmer_rollladen: + current_position: 0 + friendly_name: Gästezimmer Rollladen + supported_features: 15 + state: closed + cover.kinderzimmer_rollladen: + current_position: 0 + friendly_name: Kinderzimmer Rollladen + supported_features: 15 + state: closed + cover.schlafzimmer_rolladen: + current_position: 0 + friendly_name: Schlafzimmer Rolladen + supported_features: 15 + state: closed + cover.esszimmer_rolladen_terrasse: + friendly_name: Esszimmer Rolladen Terrasse + supported_features: 15 + current_position: 0 + state: closed + metadata: + cover.kuche_rolladen: + entity_only: true + cover.buro_rollladen: + entity_only: true + cover.esszimmer_rollladen_fenster: + entity_only: true + cover.gastezimmer_rollladen: + entity_only: true + cover.kinderzimmer_rollladen: + entity_only: true + cover.schlafzimmer_rolladen: + entity_only: true + cover.esszimmer_rolladen_terrasse: + entity_only: true diff --git a/scripts.yaml b/scripts.yaml new file mode 100644 index 0000000..e69de29 diff --git a/themes/.gitkeep b/themes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/voice_satellite/models/hey_baby.json b/voice_satellite/models/hey_baby.json new file mode 100644 index 0000000..100a882 --- /dev/null +++ b/voice_satellite/models/hey_baby.json @@ -0,0 +1,18 @@ +{ + "type": "micro", + "wake_word": "hey_baby", + "author": "Tater Totterson", + "website": "https://github.com/TaterTotterson/microWakeWord-Trainer-AppleSilicon", + "model": "hey_baby.tflite", + "trained_languages": [ + "en" + ], + "version": 2, + "micro": { + "probability_cutoff": 0.97, + "sliding_window_size": 5, + "feature_step_size": 10, + "tensor_arena_size": 30000, + "minimum_esphome_version": "2024.7.0" + } +} \ No newline at end of file diff --git a/voice_satellite/models/hey_baby.tflite b/voice_satellite/models/hey_baby.tflite new file mode 100644 index 0000000..52067b9 Binary files /dev/null and b/voice_satellite/models/hey_baby.tflite differ diff --git a/zigbee2mqtt/configuration.yaml b/zigbee2mqtt/configuration.yaml new file mode 100644 index 0000000..b29b981 --- /dev/null +++ b/zigbee2mqtt/configuration.yaml @@ -0,0 +1,368 @@ +homeassistant: + enabled: true + experimental_event_entities: true + legacy_action_sensor: true + base_topic: zigbee2mqtt + force_disable_retain: false + include_device_information: false + keepalive: 60 + maximum_packet_size: 1048576 + reject_unauthorized: true + server: mqtt://core-mosquitto:1883 + user: addons + version: 4 +availability: + enabled: true + active: + timeout: 10 + passive: + timeout: 1500 +advanced: + homeassistant_legacy_entity_attributes: false + homeassistant_legacy_triggers: false + legacy_api: false + legacy_availability_payload: false + network_key: + - 209 + - 102 + - 42 + - 236 + - 154 + - 123 + - 82 + - 223 + - 180 + - 189 + - 107 + - 167 + - 92 + - 0 + - 233 + - 127 + pan_id: 30948 + ext_pan_id: + - 144 + - 38 + - 177 + - 109 + - 231 + - 56 + - 16 + - 168 + log_level: info + transmit_power: 20 + last_seen: ISO_8601_local +mqtt: + server: mqtt://core-mosquitto:1883 + user: addons + password: eirae2Wiizoge0Iev4rahd7xo3thieh2shee5Aodequ9roosePeef8aimu7quei9 +frontend: + enabled: true + port: 8099 +serial: + port: /dev/ttyUSB0 + adapter: zstack +device_options: + legacy: false +devices: + '0xb0c7defffe8d2d76': + friendly_name: Büro Schreibtisch + icon: device_icons/279d5c66fe7be03a35dff517d445f68f.png + '0x0cae5ffffe6502b5': + friendly_name: Büro Decke + icon: device_icons/1b42d9faf6a3a68873e2d2d3ae62753a.png + '0x881a14fffe21e8e6': + friendly_name: Technikraum Decke + icon: device_icons/19752934b101febac061fc800bb1ef32.png + '0x881a14fffe5838c0': + friendly_name: Badezimmer Decke + icon: device_icons/19752934b101febac061fc800bb1ef32.png + '0x881a14fffe4a0d37': + friendly_name: Badezimmer Spiegel + icon: device_icons/d4d2fe7a16c5182d974a97bb4d8723de.png + '0x881a14fffe58381a': + friendly_name: WC Decke + icon: device_icons/19752934b101febac061fc800bb1ef32.png + '0x881a14fffe301b68': + friendly_name: WC Spiegel + icon: device_icons/d4d2fe7a16c5182d974a97bb4d8723de.png + '0x881a14fffe873b31': + friendly_name: Gästezimmer Decke + icon: device_icons/19752934b101febac061fc800bb1ef32.png + '0x38398ffffe252cef': + friendly_name: Garderobe Spot 4 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x0cae5ffffed69332': + friendly_name: Garderobe Spot 2 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0xe8e07efffe5c7d63': + friendly_name: Garderobe Spot 3 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x60b647fffe816f64': + friendly_name: Garderobe Decke + icon: device_icons/19752934b101febac061fc800bb1ef32.png + '0x38398ffffe2569cc': + friendly_name: Garderobe Spot 1 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x881a14fffe20c4ef': + friendly_name: Speis Decke + icon: device_icons/19752934b101febac061fc800bb1ef32.png + '0x38398ffffe2551b5': + friendly_name: Gang Spot 2 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0xb0c7defffe3a4879': + friendly_name: Wohnzimmer Spot 1 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0xb0c7defffe38536c': + friendly_name: Gang Spot 1 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x842712fffee4c558': + friendly_name: Wohnzimmer Spot 4 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x3410f4fffe94a7b0': + friendly_name: Wohnzimmer Spot 2 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x38398ffffe252caf': + friendly_name: Gang Spot 3 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x842712fffec8b164': + friendly_name: Gang Spot 4 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x0cae5ffffed68ded': + friendly_name: Wohnzimmer Spot 3 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x60b647fffe6850b8': + friendly_name: Kinderzimmer Spot 3 + icon: device_icons/b4b94fa4c743049e843f4700d2aa8485.png + '0x60b647fffe67f860': + friendly_name: Kinderzimmer Spot 4 + icon: device_icons/b4b94fa4c743049e843f4700d2aa8485.png + '0x94b216fffe7a6dd2': + friendly_name: Kinderzimmer Spot 1 + icon: device_icons/b4b94fa4c743049e843f4700d2aa8485.png + '0x30fb10fffee4675e': + friendly_name: Kinderzimmer Spot 2 + icon: device_icons/b4b94fa4c743049e843f4700d2aa8485.png + '0x0017880109190b00': + friendly_name: WC Bewegungsmelder + icon: device_icons/59f9bba43e92f119514fa3b13149743e.png + '0x0cae5ffffe4494d8': + friendly_name: Esszimmer Decke + icon: device_icons/72334974f993fbc903080baccebfd47f.png + '0x001788010200c370': + friendly_name: Badezimmer Bewegungsmelder + icon: device_icons/59f9bba43e92f119514fa3b13149743e.png + '0x00158d000af413d7': + friendly_name: Technikraum Wassermelder + icon: device_icons/2d19c1b8e82d9b72921bbd1fcbde4541.png + '0x00158d000af0945b': + friendly_name: WC Wassermelder + icon: device_icons/2d19c1b8e82d9b72921bbd1fcbde4541.png + '0x001788010ca5c7d1': + friendly_name: Büro Monitor + icon: device_icons/9b66ae5f0b2e337c78ee34b8fdeb669b.png + '0x0017880102028b14': + friendly_name: Speis Bewegungsmelder + icon: device_icons/59f9bba43e92f119514fa3b13149743e.png + '0x84182600000b487f': + friendly_name: Badezimmer Handtuchheizung + icon: device_icons/1421cda56f9bab7f8ab6e504e036e3e0.png + '0x54ef441000d907c5': + friendly_name: Büro Lichtschalter + state_action: true + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x54ef4410004a0fef': + friendly_name: Badezimmer Wireless Lichtschalter + icon: device_icons/c458d2c1dc03fb6338a08827c853eee4.png + '0x54ef441000c2565d': + friendly_name: WC Wireless Lichtschalter + icon: device_icons/c458d2c1dc03fb6338a08827c853eee4.png + '0x38398ffffe252d61': + friendly_name: Küche Spot 2 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0xb0c7defffe386710': + friendly_name: Küche Spot 4 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x38398ffffe252e5a': + friendly_name: Küche Spot 1 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x38398ffffe2566e3': + friendly_name: Küche Spot 3 + icon: device_icons/143b23d781c24f6f93164b455deee34c.png + '0x54ef441000d45e91': + friendly_name: Badezimmer Lichtschalter + state_action: true + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x54ef441000cc0a69': + friendly_name: WC Trockner + icon: device_icons/582d246299c1c7e864d47cecce4b7d7b.png + '0x54ef441000cbf479': + friendly_name: WC Waschmaschine + icon: device_icons/582d246299c1c7e864d47cecce4b7d7b.png + '0x54ef441000d46044': + friendly_name: WC Lichtschalter + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x54ef441000ce22ce': + friendly_name: Speis Lichtschalter + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x54ef441000ce2f59': + friendly_name: Kinderzimmer Lichtschalter + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + energy_precision: 3 + '0x00158d00018b4c44': + friendly_name: Büro Schreibtischschalter + icon: device_icons/c7706a8b7bac2de2a6d18635d4047f81.png + '0x54ef441000d4611c': + friendly_name: Gästezimmer Lichtschalter + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x54ef441000d45dd6': + friendly_name: Schlafzimmer Lichtschalter + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x001788010275e8c7': + friendly_name: Schlafzimmer Bulb 2 + icon: device_icons/547eb0da2c9c9be1b23cc2a5793f2669.png + '0x0017880100deecf8': + friendly_name: Schlafzimmer Bulb 3 + icon: device_icons/547eb0da2c9c9be1b23cc2a5793f2669.png + '0x001788010486863b': + friendly_name: Schlafzimmer Bulb 1 + icon: device_icons/547eb0da2c9c9be1b23cc2a5793f2669.png + '0x54ef441000d4586a': + friendly_name: Gang Lichtschalter + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x54ef441000c1b5b8': + friendly_name: Gang Wohnzimmer Lichtschalter + icon: device_icons/65a14e8c946a0aec4d4bb5d68759cc47.png + '0x00158d00019c9640': + friendly_name: Wohnzimmer Button + icon: device_icons/c7706a8b7bac2de2a6d18635d4047f81.png + '0x8418260000102127': + friendly_name: Küche Anrichte + icon: device_icons/1421cda56f9bab7f8ab6e504e036e3e0.png + '0x00158d000504a95c': + friendly_name: Wohnzimmer Decke + icon: device_icons/9b3d5ce25b5583be19620c7b236e01db.png + '0x00158d00057f197f': + friendly_name: FB Licht + icon: device_icons/decf9036e786698ce1b920821a9efcc5.png + '0x000d6f0019c8fce5': + friendly_name: Büro Rauchmelder + icon: device_icons/b2fd41fa7942e78905c95cdd25b8f34e.png + '0x000d6f0019c8fd03': + friendly_name: Kinderzimmer Rauchmelder + icon: device_icons/b2fd41fa7942e78905c95cdd25b8f34e.png + '0x000d6f0019c8fb76': + friendly_name: Schlafzimmer Rauchmelder + icon: device_icons/b2fd41fa7942e78905c95cdd25b8f34e.png + '0x000d6f0019b76d86': + friendly_name: Gästezimmer Rauchmelder + icon: device_icons/b2fd41fa7942e78905c95cdd25b8f34e.png + '0x000d6f0019b76d84': + friendly_name: Technikraum Rauchmelder + icon: device_icons/b2fd41fa7942e78905c95cdd25b8f34e.png + '0x000d6f0019c8fce8': + friendly_name: Wohnbereich Rauchmelder + icon: device_icons/b2fd41fa7942e78905c95cdd25b8f34e.png + '0x00158d000af3fc3e': + friendly_name: Küche Wassermelder + icon: device_icons/2d19c1b8e82d9b72921bbd1fcbde4541.png + '0x00124b002f8ac291': + friendly_name: Wohnzimmer Zigbee Router + icon: device_icons/3246337b3393ab837a1d83be05685c19.png + '0x6c5cb1fffe8c4e12': + friendly_name: Büro Rollladen + icon: device_icons/fc9f67ae2f31d9ae20d849b784475c51.png + '0xb43522fffe4ba401': + friendly_name: Küche Rolladen + icon: device_icons/fc9f67ae2f31d9ae20d849b784475c51.png + '0xb43522fffe4bcf1d': + friendly_name: Esszimmer Rollladen Fenster + invert_cover: false + state_action: true + cover_position_tilt_disable_report: false + icon: device_icons/fc9f67ae2f31d9ae20d849b784475c51.png + '0x6c5cb1fffe11df5e': + friendly_name: Kinderzimmer Rollladen + icon: device_icons/fc9f67ae2f31d9ae20d849b784475c51.png + '0xb43522fffe372e10': + friendly_name: Schlafzimmer Rolladen + icon: device_icons/fc9f67ae2f31d9ae20d849b784475c51.png + '0x6c5cb1fffe134db9': + friendly_name: Gästezimmer Rollladen + icon: device_icons/fc9f67ae2f31d9ae20d849b784475c51.png + '0x001788010e12b30f': + friendly_name: Grillplatz Wandleuchte + icon: device_icons/91da8f9564a389e29dd343f9ba04a04c.png + '0xb43522fffe36df66': + friendly_name: Wohnzimmer Rolladen + icon: device_icons/fc9f67ae2f31d9ae20d849b784475c51.png + '0x54ef441000ce3bef': + friendly_name: Wohnzimmer Lichtschalter Terrasse + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x001788010202bd83': + friendly_name: Technikraum Bewegungsmelder + icon: device_icons/59f9bba43e92f119514fa3b13149743e.png + '0x54ef441000d458b4': + friendly_name: Esszimmer Lichtschalter Terrasse + icon: device_icons/7a58c1b1618a518c74cbec9b3e50556f.png + '0x54ef441000db20f6': + friendly_name: Esszimmer Gang Lichtschalter + icon: device_icons/65a14e8c946a0aec4d4bb5d68759cc47.png + '0x54ef441000eb3343': + friendly_name: Wohnzimmer Küche Lichtschalter + icon: device_icons/65a14e8c946a0aec4d4bb5d68759cc47.png + '0x001788010e53f99f': + friendly_name: Wohnzimmer Stehlampe + icon: device_icons/d51b46305bd46275c16b3a590ab68d73.png + color_sync: true + '0x001788010e12f3c6': + friendly_name: Terrasse Wandleuchte + icon: device_icons/91da8f9564a389e29dd343f9ba04a04c.png + '0x001788010e125e29': + friendly_name: Eingang + icon: device_icons/91da8f9564a389e29dd343f9ba04a04c.png + '0x00158d00018b4ac7': + friendly_name: Kinderzimmer Bettschalter + icon: device_icons/c7706a8b7bac2de2a6d18635d4047f81.png + '0x08ddebfffea5bd89': + friendly_name: Kinderzimmer Bewegungsmelder + '0x0017880101230e97': + friendly_name: Wohnzimmer Iris + '0xc4d8c8fffe0100fc': + friendly_name: Terrasse Fernbedienung + '0x54ef441000d95689': + friendly_name: Küche Geschirrspüler + '0x54ef441000cc18b9': + friendly_name: Technikraum Netzwerkschrank + '0xf0d1b8be24094010': + friendly_name: Ruheplatz Wegbeleuchtung + '0x0017880101197173': + friendly_name: Esszimmer Hue Go + '0x54ef441001179b5a': + friendly_name: '0x54ef441001179b5a' + '0xb43522fffe36d958': + friendly_name: Esszimmer Rolladen Terrasse + invert_cover: false + cover_position_tilt_disable_report: false + '0x00178801105f75a0': + friendly_name: '0x00178801105f75a0' + '0x54ef441001179a48': + friendly_name: Grow Tent + '0x00158d0002c9fa62': + friendly_name: Grow Env Sensor 1 + '0x00158d0002c9fa8b': + friendly_name: Grow Env Sensor 2 + '0xa4c13885ab49997f': + friendly_name: Grow Plant 1 + '0xa4c13873cd30c328': + friendly_name: Grow Plant 2 + '0xa4c13872fc3df2ae': + friendly_name: Grow Plant 3 + '0xa4c138d7df5bd0ac': + friendly_name: Grow Plant 5 + '0xa4c1386cf3ec0413': + friendly_name: Grow Plant 4 + '0x00158d0002529bd7': + friendly_name: Technikraum Wasserticker +groups: {} +version: 5