From 57f8ce5803c60cc1a3ffc5bb63eb70aab0f63f25 Mon Sep 17 00:00:00 2001 From: matt1432 Date: Mon, 16 Sep 2024 22:54:18 -0400 Subject: [PATCH] feat(hass): add voice command to set timers --- .../homie/modules/home-assistant/assist.nix | 6 + .../modules/home-assistant/bluetooth.nix | 1 + .../homie/modules/home-assistant/default.nix | 18 + .../modules/home-assistant/docs/functions.nix | 90 +++ .../homie/modules/home-assistant/docs/prompt | 54 ++ .../homie/modules/home-assistant/timer.nix | 652 ++++++++++++++++++ flake.lock | Bin 54709 -> 54709 bytes flake.nix | Bin 9164 -> 9164 bytes inputs.nix | 6 +- legacyPackages/hass-components/default.nix | 2 +- ...n.nix => extended-ollama-conversation.nix} | 14 +- 11 files changed, 832 insertions(+), 11 deletions(-) create mode 100644 devices/homie/modules/home-assistant/docs/functions.nix create mode 100644 devices/homie/modules/home-assistant/docs/prompt create mode 100644 devices/homie/modules/home-assistant/timer.nix rename legacyPackages/hass-components/{extended-openai-conversation.nix => extended-ollama-conversation.nix} (59%) diff --git a/devices/homie/modules/home-assistant/assist.nix b/devices/homie/modules/home-assistant/assist.nix index f6e81130..9bfcf06b 100644 --- a/devices/homie/modules/home-assistant/assist.nix +++ b/devices/homie/modules/home-assistant/assist.nix @@ -1,4 +1,5 @@ { + pkgs, self, wakewords-src, ... @@ -10,10 +11,15 @@ services = { home-assistant = { + customComponents = builtins.attrValues { + inherit (self.legacyPackages.${pkgs.system}.hass-components) extended-ollama-conversation; + }; + extraComponents = [ "esphome" "ollama" "wyoming" + "scrape" ]; config = { diff --git a/devices/homie/modules/home-assistant/bluetooth.nix b/devices/homie/modules/home-assistant/bluetooth.nix index c2f100e6..89ea1273 100644 --- a/devices/homie/modules/home-assistant/bluetooth.nix +++ b/devices/homie/modules/home-assistant/bluetooth.nix @@ -82,6 +82,7 @@ "ibeacon" "led_ble" "kegtron" + "xiaomi_ble" ]; services.mpd = { enable = true; diff --git a/devices/homie/modules/home-assistant/default.nix b/devices/homie/modules/home-assistant/default.nix index 51f724a6..2c4a74dd 100644 --- a/devices/homie/modules/home-assistant/default.nix +++ b/devices/homie/modules/home-assistant/default.nix @@ -4,6 +4,7 @@ ./bluetooth.nix ./firmware.nix ./frontend.nix + ./timer.nix ]; environment.systemPackages = [ @@ -23,6 +24,23 @@ fi ''; }) + + (pkgs.writeShellApplication { + name = "nix2yaml"; + runtimeInputs = with pkgs; [remarshal]; + text = '' + input="$1" + output="''${2:-""}" + + yamlCode="$(nix eval --json --file "$input" | remarshal --if json --of yaml)" + + if [[ "$output" != "" ]]; then + echo "$yamlCode" > "$output" + else + echo "$yamlCode" + fi + ''; + }) ]; # TODO: some components / integrations / addons require manual interaction in the GUI, find way to make it all declarative diff --git a/devices/homie/modules/home-assistant/docs/functions.nix b/devices/homie/modules/home-assistant/docs/functions.nix new file mode 100644 index 00000000..18204569 --- /dev/null +++ b/devices/homie/modules/home-assistant/docs/functions.nix @@ -0,0 +1,90 @@ +# I use nix2yaml from ../default.nix to convert to to YAML and place it in the functions of extended_ollama_conversation +[ + { + function = { + name = "execute_service"; + type = "native"; + }; + + spec = { + name = "execute_services"; + description = "Use this function to execute service of devices in Home Assistant."; + + parameters = { + type = "object"; + + properties.list = { + type = "array"; + + items = { + type = "object"; + + properties = { + entity_id = { + description = "The entity_id retrieved from available devices. It must start with domain, followed by dot character."; + type = "string"; + }; + + service = { + description = "The service to be called"; + type = "string"; + }; + }; + + required = ["entity_id" "service"]; + }; + }; + }; + }; + } + + { + function = { + type = "script"; + + sequence = [ + { + service = "script.assist_TimerStart"; + + data.duration = builtins.concatStringsSep "" [ + ''{% if not hours %} {% set hours = "0" %} {% endif %}'' + ''{% if not minutes %} {% set minutes = "0" %} {% endif %}'' + ''{% if not seconds %} {% set seconds = "0" %} {% endif %}'' + + ''{{ hours | int(default=0) }}:{{ minutes | int(default=0) }}:{{ seconds | int(default=0) }}'' + ]; + + target.entity_id = "timer.assist_timer1"; + } + ]; + }; + + spec = { + name = "timer_start"; + description = "Use this function to start a timer in Home Assistant."; + + parameters = { + type = "object"; + + properties = { + hours = { + type = "string"; + description = "The amount of hours the timer should run for."; + }; + + minutes = { + type = "string"; + description = "The amount of minutes the timer should run for."; + }; + + seconds = { + type = "string"; + description = "The amount of seconds the timer should run for."; + }; + }; + + required = []; + }; + }; + } +] diff --git a/devices/homie/modules/home-assistant/docs/prompt b/devices/homie/modules/home-assistant/docs/prompt new file mode 100644 index 00000000..e5683c67 --- /dev/null +++ b/devices/homie/modules/home-assistant/docs/prompt @@ -0,0 +1,54 @@ +{%- set customize_glob_exposed_attributes = { + ".*": { + "friendly_name": true, + "temperature": true, + "current_temperature": true, + "temperature_unit": true, + "brightness": true, + "humidity": true, + "unit_of_measurement": true, + "device_class": true, + "current_position": true, + "percentage": true, + "volume_level": true, + "media_title": true, + "media_artist": true, + "media_album_name": true, + }, +} %} + +{%- macro get_exposed_attributes(entity_id) -%} + {%- set ns = namespace(exposed_attributes = {}, result = {}) %} + {%- for pattern, attributes in customize_glob_exposed_attributes.items() -%} + {%- if entity_id | regex_match(pattern) -%} + {%- set ns.exposed_attributes = dict(ns.exposed_attributes, **attributes) -%} + {%- endif -%} + {%- endfor -%} + {%- for attribute_key, should_include in ns.exposed_attributes.items() -%} + {%- if should_include and state_attr(entity_id, attribute_key) != None -%} + {%- set temp = {attribute_key: state_attr(entity_id, attribute_key)} if should_include is boolean else {attribute_key: should_include} -%} + {%- set ns.result = dict(ns.result, **temp) -%} + {%- endif -%} + {%- endfor -%} + {%- set result = ns.result | to_json if ns.result!={} else None -%} + {{"'" + result + "'" if result != None else ''}} +{%- endmacro -%} + +I want you to act as smart home manager of Home Assistant. +I will provide information of smart home along with a question, you will truthfully make correction or answer using information provided in one sentence in everyday language. + +Current Time: {{now()}} + +Available Devices: +```csv +entity_id,name,state,aliases,attributes +{% for entity in exposed_entities -%} +{{ entity.entity_id }},{{ entity.name }},{{ entity.state }},{{entity.aliases | join('/')}},{{get_exposed_attributes(entity.entity_id)}} +{% endfor -%} +``` + +The current state of devices is provided in available devices. +Use execute_services function to control devices. +If needed, you can infer the entity_id from the name and common variations. +Do not ask for confirmation to execute a service. +Do not restate or appreciate what user says, rather make a quick inquiry. diff --git a/devices/homie/modules/home-assistant/timer.nix b/devices/homie/modules/home-assistant/timer.nix new file mode 100644 index 00000000..9265f134 --- /dev/null +++ b/devices/homie/modules/home-assistant/timer.nix @@ -0,0 +1,652 @@ +# From https://github.com/don86nl/ha_intents/blob/main/config/packages/assist_timers.yaml +{...}: let + settings = { + timer_media_location = "/path/to/file.mp3"; + timer_target = "kitchen"; + timer_target_default = "media_player.music_player_daemon"; + timer_tts = true; + timer_tts_message = "A set timer has finished."; + timer_tts_service = "tts.speak"; + timer_tts_target = "tts.piper"; + timer_volume = 0.4; + }; +in { + services.home-assistant = { + config = { + # TODO: format this properly + automation = [ + { + action = [ + { + alias = "Get generic variables from script"; + variables = { + timer_media_location = "{{ settings.get('timer_media_location') }}"; + timer_target = "{%- if settings.get('timer_target')[:13] == \"\" %} {{- settings.get('timer_target_default') }} {%- elif settings.get('timer_target')[:13] == \"media_player.\" %} {{- settings.get('timer_target') }} {%- elif (settings.get('timer_target')[:7] == \"sensor.\" or settings.get('timer_target')[:11] == \"input_text.\") and (states(settings.get('timer_target'))[:13] == \"media_player.\") %} {{- states(settings.get('timer_target')) }} {%- elif (settings.get('timer_target')[:7] == \"sensor.\" or settings.get('timer_target')[:11] == \"input_text.\") and (states(settings.get('timer_target')) == \"\") %} {{- settings.get('timer_target_default') }} {%- else %} {%- set media_player_list = states.media_player | map(attribute='entity_id') | list %} {%- if \"sensor.\" in settings.get('timer_target') or \"input_text.\" in target_area %} {%- set target_area = states(settings.get('timer_target')) %} {%- else %} {%- set target_area = settings.get('timer_target') %} {%- endif %} {%- for entity_id in media_player_list %} {%- if area_name(entity_id) | lower == target_area | lower %} {{ entity_id }} {%- endif %} {%- endfor %} {%- endif %} "; + timer_tts = "{{ settings.get('timer_tts') }}"; + timer_tts_message = "{{ settings.get('timer_tts_message') }}"; + timer_tts_service = "{{ settings.get('timer_tts_service') }}"; + timer_tts_target = "{{ settings.get('timer_tts_target') }}"; + timer_volume = "{{ settings.get('timer_volume') }}"; + }; + } + { + alias = "Store current device volume"; + variables = {device_volume = "{{ state_attr(timer_target, 'volume_level') }}";}; + } + { + alias = "Set volume for timer"; + data = {volume_level = "{{ timer_volume }}";}; + service = "media_player.volume_set"; + target = {entity_id = "{{ timer_target }}";}; + } + { + alias = "Media file or TTS"; + choose = [ + { + alias = "Media file"; + conditions = [ + { + alias = "Timer is a media file"; + condition = "template"; + value_template = "{{ timer_tts == false }}"; + } + ]; + sequence = [ + { + alias = "Play media"; + data = { + announce = true; + media_content_id = "{{ timer_media_location }}"; + media_content_type = "music"; + }; + enabled = true; + service = "media_player.play_media"; + target = {entity_id = "{{ timer_target }}";}; + } + ]; + } + ]; + default = [ + {delay = {seconds = 1;};} + { + alias = "Choose TTS service"; + choose = [ + { + conditions = [ + { + alias = "tts.cloud_say"; + condition = "template"; + value_template = "{{ timer_tts_service != 'tts.speak' }}"; + } + ]; + sequence = [ + { + data = { + cache = true; + entity_id = "{{ timer_target }}"; + message = "{% if timer_tts_message[:7] == \"sensor.\" or timer_tts_message[:11] == \"input_text.\" %} {{ states(timer_tts_message) }} {% else %} {{ timer_tts_message }} {% endif %}"; + }; + service = "{{ timer_tts_service }}"; + } + ]; + } + ]; + default = [ + { + data = { + cache = true; + media_player_entity_id = "{{ timer_target }}"; + message = "{% if timer_tts_message[:7] == \"sensor.\" or timer_tts_message[:11] == \"input_text.\" %} {{ states(timer_tts_message) }} {% else %} {{ timer_tts_message }} {% endif %}"; + }; + service = "tts.speak"; + target = {entity_id = "{{ timer_tts_target }}";}; + } + ]; + } + ]; + } + { + alias = "Restore device previous volume"; + data = {volume_level = "{{ device_volume }}";}; + service = "media_player.volume_set"; + target = {entity_id = "{{ timer_target }}";}; + } + ]; + alias = "Assist - TimerReached"; + condition = [ + { + alias = "Finished timer is an assist timer"; + condition = "template"; + value_template = "{{ trigger.event.data.entity_id[:18] == 'timer.assist_timer' }}"; + } + ]; + description = "Assist automation when set timer time is reached."; + id = "assist_timerreached"; + mode = "single"; + trigger = [ + { + alias = "Any timer reached"; + event_type = "timer.finished"; + id = "timer_finished"; + platform = "event"; + } + ]; + variables = { + inherit settings; + }; + } + { + action = [ + { + alias = "Delay for Timer Reached automation"; + delay = {seconds = 3;}; + } + { + alias = "Reset timer location"; + data = {value = "";}; + service = "input_text.set_value"; + target = {entity_id = "{{ 'input_text.' + trigger.entity_id[6:] + '_location' }}";}; + } + ]; + alias = "Assist - TimerFinished"; + condition = [ + { + alias = "Timer was active or paused"; + condition = "template"; + value_template = "{{ trigger.from_state != trigger.to_state }}"; + } + ]; + description = "Assist automation when set timer time is finished."; + id = "assist_timerfinished"; + mode = "parallel"; + trigger = [ + { + alias = "Assist timer finished or cancelled"; + entity_id = ["timer.assist_timer1" "timer.assist_timer2" "timer.assist_timer3"]; + platform = "state"; + to = "idle"; + } + ]; + } + ]; + homeassistant = { + customize = { + "script.assist_timerstart" = { + inherit settings; + }; + }; + }; + input_text = { + assist_timer1_location = { + icon = "mdi:assistant"; + max = 255; + name = "Assist - Timer 1 Location"; + }; + assist_timer2_location = { + icon = "mdi:assistant"; + max = 255; + name = "Assist - Timer 2 Location"; + }; + assist_timer3_location = { + icon = "mdi:assistant"; + max = 255; + name = "Assist - Timer 3 Location"; + }; + }; + intent_script = { + TimerDuration = { + action = [{stop = "";}]; + async_action = true; + }; + TimerPause = { + action = [ + { + data = { + entity_id = "{{ entity_id }}"; + timer_action = "{{ timer_action }}"; + }; + service = "script.assist_TimerPause"; + } + ]; + async_action = true; + }; + TimerStart = { + action = [ + { + data = {duration = "{{hours | int(default=0)}}:{{ minutes | int(default=0) }}:{{ seconds | int(default=0) }}";}; + service = "script.assist_TimerStart"; + } + ]; + async_action = false; + }; + TimerStop = { + action = [ + { + data = {entity_id = "{{ entity_id }}";}; + service = "script.assist_TimerStop"; + } + ]; + async_action = true; + }; + }; + script = { + assist_timerpause = { + alias = "Assist - TimerPause"; + description = "Script for pausing a timer using HA Assist."; + icon = "mdi:assistant"; + mode = "single"; + sequence = [ + { + choose = [ + { + conditions = [ + { + alias = "Single Timer"; + condition = "template"; + value_template = "{{ entity_id[:18] == 'timer.assist_timer' }}"; + } + ]; + sequence = [ + { + alias = "Single timer: Idle or active"; + choose = [ + { + conditions = [ + { + alias = "Timer not active"; + condition = "template"; + value_template = "{{ states(entity_id) == 'idle' }}"; + } + ]; + sequence = [{stop = "Timer is not active";}]; + } + ]; + default = [ + { + alias = "Pause or resume"; + choose = [ + { + alias = "Pause"; + conditions = [ + { + alias = "Action = pause"; + condition = "template"; + value_template = "{{ timer_action == 'pause' }}"; + } + ]; + sequence = [ + { + alias = "Pause timer"; + service = "timer.pause"; + target = {entity_id = "{{ entity_id }}";}; + } + {stop = "Pause timer";} + ]; + } + ]; + default = [ + { + alias = "Resume timer"; + service = "timer.start"; + target = {entity_id = "{{ entity_id }}";}; + } + {stop = "Resume timer";} + ]; + } + ]; + } + ]; + } + { + alias = "No specific timer"; + conditions = [ + { + alias = "No specific Timer"; + condition = "template"; + value_template = "{{ entity_id == 'null' }}"; + } + { + alias = "Timer(s) are active"; + condition = "template"; + value_template = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | list\n | length > 0 }}"; + } + ]; + sequence = [ + { + alias = "No specific timer: # active?"; + choose = [ + { + conditions = [ + { + alias = "No specific timer asked"; + condition = "template"; + value_template = "{{ entity_id == 'null' }}"; + } + { + alias = "Multiple timers active"; + condition = "template"; + value_template = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | list\n | length > 1 }}"; + } + ]; + sequence = [{stop = "Multiple timers active, none specified";}]; + } + ]; + default = [ + { + alias = "Pause or resume"; + choose = [ + { + alias = "Pause"; + conditions = [ + { + alias = "Action = pause"; + condition = "template"; + value_template = "{{ timer_action == 'pause' }}"; + } + ]; + sequence = [ + { + alias = "Pause timer"; + service = "timer.pause"; + target = {entity_id = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | join(', ') }}";}; + } + {stop = "Pause timer";} + ]; + } + ]; + default = [ + { + alias = "Resume timer"; + service = "timer.start"; + target = {entity_id = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | join(', ') }}";}; + } + {stop = "Resume timer";} + ]; + } + ]; + } + ]; + } + { + alias = "All timers"; + conditions = [ + { + alias = "All timers"; + condition = "template"; + value_template = "{{ entity_id == 'all' }}"; + } + ]; + sequence = [ + { + alias = "Timers active?"; + choose = [ + { + alias = "No timers active"; + conditions = [ + { + alias = "No timers active"; + condition = "template"; + value_template = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | list\n | length == 0 }}"; + } + ]; + sequence = [{stop = "No timers active";}]; + } + ]; + default = [ + { + alias = "Pause or resume"; + choose = [ + { + alias = "Pause"; + conditions = [ + { + alias = "Action = pause"; + condition = "template"; + value_template = "{{ timer_action == 'pause' }}"; + } + ]; + sequence = [ + { + alias = "Pause timer"; + service = "timer.pause"; + target = {entity_id = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | join(', ') }}";}; + } + {stop = "Pause timer";} + ]; + } + ]; + default = [ + { + alias = "Resume timer"; + service = "timer.start"; + target = {entity_id = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | join(', ') }}";}; + } + {stop = "Resume timer";} + ]; + } + ]; + } + ]; + } + ]; + } + ]; + variables = { + entity_id = "{% if entity_id is set or entity_id != \"\" %} {{ entity_id }} {% else %} null {% endif %}"; + timer_action = "{% if timer_action is set or timer_action != \"\" %} {{ timer_action }} {% else %} resume {% endif %}"; + }; + }; + assist_timerstart = { + alias = "Assist - TimerStart"; + description = "Script for starting a timer using HA Assist."; + icon = "mdi:assistant"; + mode = "single"; + sequence = [ + { + alias = "Set variables"; + variables = {timer_location = "{%- if settings.get('timer_target')[:13] == \"media_player.\" %} {{ area_name(settings.get('timer_target')) | lower }} {% elif (settings.get('timer_target')[:7] == \"sensor.\" or settings.get('timer_target')[:11] == \"input_text.\") and states(settings.get('timer_target'))[:13] == \"media_player.\" %} {{- states(settings.get('timer_target')) }} {%- elif settings.get('timer_target')[:13] != \"media_player.\" and settings.get('timer_target')[:7] != \"sensor.\" and settings.get('timer_target')[:11] != \"input_text.\" %} {{- settings.get('timer_target') }} {%- elif (settings.get('timer_target')[:7] == \"sensor.\" or settings.get('timer_target')[:11] == \"input_text.\") and (states(settings.get('timer_target')) != \"\") and (states(settings.get('timer_target'))[:13] == \"media_player.\") %} {{ area_name(settings.get('timer_target_default')) }} {%- elif (settings.get('timer_target')[:7] == \"sensor.\" or settings.get('timer_target')[:11] == \"input_text.\") %} {% if states(settings.get('timer_target')) != \"\" and states(settings.get('timer_target')) != \"not_home\" and states(settings.get('timer_target')) != 0 %} {{ states(settings.get('timer_target')) }} {% else %} {{- area_name(settings.get('timer_target_default')) | lower }} {%- endif %} {%- else %} {{- area_name(settings.get('timer_target')) | lower }} {%- endif %}";}; + } + { + alias = "Set timer location"; + data = {value = "{{ timer_location }}";}; + service = "input_text.set_value"; + target = {entity_id = "{% if states('timer.assist_timer1') != 'active' and states('timer.assist_timer1') != 'paused' %} input_text.assist_timer1_location {% elif states('timer.assist_timer2') != 'active' and states('timer.assist_timer2') != 'paused' %} input_text.assist_timer2_location {% else %} input_text.assist_timer3_location {% endif%}";}; + } + { + alias = "Start timer"; + data_template = {duration = "{{ duration }}";}; + service = "timer.start"; + target = {entity_id = "{% if states('timer.assist_timer1') != 'active' and states('timer.assist_timer1') != 'paused' %} timer.assist_timer1 {% elif states('timer.assist_timer2') != 'active' and states('timer.assist_timer2') != 'paused' %} timer.assist_timer2 {% else %} timer.assist_timer3{% endif%}";}; + } + ]; + variables = { + inherit settings; + }; + }; + assist_timerstop = { + alias = "Assist - TimerStop"; + description = "Script for stopping a timer using HA Assist."; + icon = "mdi:assistant"; + mode = "single"; + sequence = [ + { + alias = "Set variables"; + variables = {entity_id = "{% if entity_id is set or entity_id != \"\" %} {{ entity_id }} {% else %} null {% endif %}";}; + } + { + choose = [ + { + alias = "Stop Timer music"; + conditions = [ + { + alias = "Timer is a media file"; + condition = "template"; + value_template = "{{ timer_tts == false }}"; + } + { + condition = "template"; + value_template = "{% set mediaplayer = namespace(entity=[]) %}\n{% for player in states.media_player %}\n {%- if ((state_attr(player.entity_id, 'media_content_id') |lower != 'none' and state_attr(player.entity_id, 'media_content_id')[:47][38:] == 'timer.mp3') or state_attr(player.entity_id, 'media_title') | lower == 'timer') and states(player.entity_id) == 'playing' -%}\n {%- set mediaplayer.entity = player.entity_id -%}\n {% endif -%}\n{% endfor %}\n{{ mediaplayer.entity[:12] == 'media_player' }}"; + } + ]; + sequence = [ + { + alias = "Stop timer music"; + service = "media_player.media_stop"; + target = {entity_id = "{% set mediaplayer = namespace(entity=[]) %} {% for player in states.media_player %}\n {% if ((state_attr(player.entity_id, 'media_content_id') |lower != 'none' and state_attr(player.entity_id, 'media_content_id')[:47][38:] == 'timer.mp3') or state_attr(player.entity_id, 'media_title') | lower == 'timer') and states(player.entity_id) == 'playing' %}\n {% set mediaplayer.entity = player.entity_id %}\n {% endif %}\n{% endfor %} {{ mediaplayer.entity }}";}; + } + ]; + } + { + conditions = [ + { + alias = "Single Timer"; + condition = "template"; + value_template = "{{ entity_id[:18] == 'timer.assist_timer' }}"; + } + ]; + sequence = [ + { + alias = "Single timer: Idle or active"; + choose = [ + { + conditions = [ + { + alias = "Timer not active"; + condition = "template"; + value_template = "{{ states(entity_id) == 'idle' }}"; + } + ]; + sequence = [{stop = "Timer is not active";}]; + } + ]; + default = [ + { + alias = "Cancel single timer"; + service = "timer.cancel"; + target = {entity_id = "{{ entity_id }}";}; + } + { + alias = "Reset timer location value"; + data = {value = "0";}; + service = "input_text.set_value"; + target = {entity_id = "{{ states.timer \n | selectattr('state','eq','active') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | join('_location, ') | replace('timer.', 'input_text.') }}";}; + } + {stop = "Timer cancelled";} + ]; + } + ]; + } + { + alias = "No specific timer"; + conditions = [ + { + alias = "No specific Timer"; + condition = "template"; + value_template = "{{ entity_id == 'null' }}"; + } + { + alias = "Timer(s) are active"; + condition = "template"; + value_template = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | list\n | length > 0 }}"; + } + ]; + sequence = [ + { + alias = "No specific timer: # active?"; + choose = [ + { + conditions = [ + { + alias = "No specific timer asked"; + condition = "template"; + value_template = "{{ entity_id == 'null' }}"; + } + { + alias = "Multiple timers active"; + condition = "template"; + value_template = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | list\n | length > 1 }}"; + } + ]; + sequence = [{stop = "Multiple timers active, none specified";}]; + } + ]; + default = [ + { + alias = "Cancel single timer"; + service = "timer.cancel"; + target = {entity_id = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | join(', ') }}";}; + } + { + alias = "Reset timer location value"; + data = {value = "0";}; + metadata = {}; + service = "input_text.set_value"; + target = {entity_id = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | join('_location, ') | replace('timer.', 'input_text.') }}";}; + } + {stop = "Timer cancelled";} + ]; + } + ]; + } + { + alias = "All timers"; + conditions = [ + { + alias = "All timers"; + condition = "template"; + value_template = "{{ entity_id == 'all' }}"; + } + ]; + sequence = [ + { + alias = "Timers active?"; + choose = [ + { + alias = "No timers active"; + conditions = [ + { + alias = "No timers active"; + condition = "template"; + value_template = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | list\n | length == 0 }}"; + } + ]; + sequence = [{stop = "No timers active";}]; + } + ]; + default = [ + { + alias = "Cancel all timers"; + service = "timer.cancel"; + target = {entity_id = "{{ states.timer \n | rejectattr('state','eq','idle') \n | selectattr('entity_id','match','timer.assist_timer*')\n | map(attribute='entity_id') \n | join(', ') }}";}; + } + {stop = "Cancel all timers";} + ]; + } + ]; + } + ]; + } + ]; + variables = {entity_id = "{% if entity_id is set or entity_id != \"\" %} {{ entity_id }} {% else %} null {% endif %}";}; + }; + }; + timer = { + assist_timer1 = { + icon = "mdi:assistant"; + name = "Assist - Timer 1"; + restore = true; + }; + assist_timer2 = { + icon = "mdi:assistant"; + name = "Assist - Timer 2"; + restore = true; + }; + assist_timer3 = { + icon = "mdi:assistant"; + name = "Assist - Timer 3"; + restore = true; + }; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock index fb8f70b1bbe86c013650c6301fb9fcfa2440c6da..8bdcccb8df82463f33fdee504ebf91189a605de4 100644 GIT binary patch delta 215 zcmdnGntAJL<_%B8S#xp{a}y`O6pvsxGBPqSw=|w?SSF_(?q+II5n<`#W)bL>W}Y2Y zQtVRc92^xCWR_i$YnJX;;AtLS=pF2t6%{#oqJ$VnNJgq(W^Q8EO*ApIG)Of!O|ne0Ff=kxPBAq#F)&UwH#bd5F)>d`F-kJG doV=0S2d{yfCrCz4fI9r$N_hxtv(T!=+5pcQN=E(G#GKsB$&Tfs{HYZssd*`> zDe?Ib!zV~ePJS23u3?yDWRPTPmTF;`YMN?lU}m0VZj@-0lxUP}k!EU`l46i(Zk(E$ eW;l6cs5d?XH&2j^oB(zByOr_~)@Gqqi?sn~)l1?4 diff --git a/flake.nix b/flake.nix index 9056925a01535c1ecf306114d26c8c57baee1ab9..1d510cf0f490d136f954aa5d589b7bdfcc65ea1f 100644 GIT binary patch delta 41 tcmX@(e#U)67%OW|PGWB2