How to pass the text of a template to a script and force the script to render the template at each pass in a repeat cycle?

(EDIT: Here is I think a clearer way to explain my problem:
How to pass the text of a template to a script and force the script to render the template at each pass in a repeat cycle? - #3 by Dablidou)

I want to have a script that will send me all sorts of reminders (on different devices, different intervals, etc depending on context). I have no problem using the fields variables in order to set this up except for one thing.

I want the reminders to be repeated at some intervals, but ONLY UNDER SOME CONDITIONS. For example I want to be reminded every 30 minutes there are clothes in the washing machine, but only if I’m home. But I may want to be reminded of other stuff every 45 minutes, but only if I’m awake and not at home. If the condition is not met (as expressed in some template), wait for them to be True, then send the reminder at the requested interval.

Each time I will call the script the conditions will be different. So I want to pass the condition as expressed in a template to my script (that runs in parallel mode).

How can I do that?

I messed around a bit in the development tool, and realised that this code succesfully transfer the template string:

service: script.aaa
data:
  test: >-
    {{ "{{ is_state('input_boolean.mode_invite', 'on') }}" }}

Strangely, in the script.aaa, if I call the notification service, it renders the template (as true or false):

service: notify.mobile_app_gaston
data:
  message: "{{ test }}"

But if in that same script.aaa I test the template value, it tests true with this, meaning it’s not rendered and still treated as a string:

- wait_template: "{{ test == \"{{ is_state('input_boolean.mode_invite', 'on') }}\" }}"

So, is there a way to test the template in the script.aaa if it’s true or false?

Is there a better way to pass a template to script (the template, not it’s value), so it can be tested by the script in a wait action, considering that the value of the template may change (even more than once) during the course of the script?

Could macros be useful? How?

Ideas?

It doesn’t have to be a template that you pass to a script, I know you can pass objects (entity types) to a script. It would be great if you could overload them like in C++ and have several versions of scripts with the same names that accept different types of objects and the right script would be run depending upon the type of the object passed, but I am sure overloading isn’t possible here. Maybe you can use more than one script, or one script which would take a large number of parameters (every object type that you would need) and when called, call it only with one object and leave all the others as null when you pass them and then in the script your code would check to see which is null?

I had a scenario where I was doing the same kind of thing in a gazillion different automations and so after a lot of struggle figured out how to pass three different objects to a script. After the huge struggle to figure it out the many hours of testing and retesting to get it right was much more time than it took to even write those automations, but of course once I had it working I had to go back to all my automations and have them call the script instead. I have maybe 50 automations calling that one script. Not sure if it was worth the effort, and there is no way I can change the script now without breaking all of those automations (!!!) but I do it because I love programming.

Anyway here’s my scenario. I have many automations that are launched by motion detectors to turn on lights in different rooms. The paradigm I use is if motion is detected, just (re)start a dedicated timer (in the example below the timer is named “dining_room_chandelier_timer”). Another automation turns on the light when the timer (re)starts, and a third automation turns the light off when the timer finishes. Therefore I had as helpers an input_number that was the duration of the timer (so I can adjust those with sliders in the dashboard - in the example below it is called “dining_room_adjust_chandelier_timer”), the related timer for the scenario just described for that specific light (“dining_room_chandelier_timer”), and whether or not the specific automation is “Enabled” (I have input_select helpers with two values, “Enabled” and “Disabled” which I use as drop downs in my dashboard to 'enable or disable automations (named “dining_room_chandelier_automation_is” in the below example). The automations are not actually disabled, I just rely upon the value of the input_select in all of my code to do or not do things so that is essentially the same result (!)))

So for many of these automations they do the same thing, but each have a separate timer, and separate input_number and separate input_select helpers. So in trying to remove redundant code all over the place I created one script which would accept a timer object and the two helper objects, and that one single script is called from many different automations all over the place. Now that I know how to do this I need to look through my automations and see what else that might be redundant can call another generic script.

I also ran into a scenario where if someone turned a light off manually and then walked out of the room, the motion detector would sense the motion and the lights would immediately go back on again before the person was able to even get out of the room. So I added a bunch more code to only turn the lights back on if they had not been turned off MANUALLY within the last 5 minutes (and code of course to set the exact current time whenever the switch is manually turned off)). For any type of switch or light the way to detect within a condition of an automation triggered by it’s state changing to off is to use this code as the condition to save the value of when the light was manually turned off in this automation for example (note the condition which is key here):

alias: >-
  Dining Room Chandelier Turned Off -> If Done Manually -> Set Last Manual Off
  Timestamp
description: >-
  If the dining room chandelier is turned off (manually),  then make sure the
  "last_manual_off-dining_room" timestamp is updated
trigger:
  - platform: state
    entity_id:
      - light.dining_room_chandelier
    to: "off"
condition:
  - "{{ trigger.to_state.context.id != none }}"
  - "{{ trigger.to_state.context.parent_id == none }}"
  - "{{ trigger.to_state.context.user_id == none }}"
action:
  - service: input_text.set_value
    data:
      value: "{{ as_timestamp(now()) }}"
    target:
      entity_id: input_text.last_manual_off_dining_room
mode: parallel
max: 1000

Works like a charm. (Hence the “last_manual_off_dining_room” input helper (which is a text instead of a datetime because of a bug at the time in the datetime helper, but all of this is a story for another time, let’s stick to your question about passing objects to scripts.)

So this automation for example calls the script with the three objects mentioned previously. Look at the end of the automation to see how it calls the script - (in the ‘action’ section):

alias: >-
  Dining Room Motion Detected (Chandelier Automation - if not switched off
  manually within last 5 minutes)
description: ""
trigger:
  - type: motion
    platform: device
    device_id: bb102cf7fbd20ffd14ac00fe56682ca4
    entity_id: binary_sensor.dining_room_motion_sensor_motion
    domain: binary_sensor
condition:
  - condition: template
    value_template: >-
      {{ (float(as_timestamp(now())) -
      float(states('input_text.last_manual_off_dining_room'),0)) > 300 }}
action:
  - service: script.motion_detected
    data:
      enablement_input_selector_entity: input_select.dining_room_chandelier_automation_is
      timer_duration_entity: input_number.dining_room_adjust_chandelier_timer
      timer_entity: timer.dining_room_chandelier_timer
mode: parallel

And those objects shown as “data” in the above action section (enablement_input_selector_entity, timer_duration_entity, timer_entity) at the end of the automation code, get passed as above, to this script:

alias: Motion Detected
sequence:
  - condition: template
    value_template: "{{ is_state(enablement_input_selector_entity,'Enabled') }}"
  - service: timer.start
    data:
      duration: "{{ (states(timer_duration_entity)|int*60) }}"
    target:
      entity_id: "{{ timer_entity }}"
mode: parallel
max: 1000
icon: mdi:run-fast

Hope that helps! Let me know if you can figure out how to overload scripts so one can take a variety of objects (entity types?)!

The key here might be if there is a way to create your own entity type and pass that all over the place. Now THAT would be very useful!

Thoughts?

I am not sure that what you suggest can solve my problem. Maybe my explanation wasn’t clear enough. Here it is. I need reminders for different kinds of things now, and will need reminders for different stuff in the future. So I want to create a single script that will be able to take all the different “orders” (in parallel) and deliver those reminders according to the following model:

There’s an action required (let’s say scan tag1 after putting the clothes in the dryer). The action required is the expected event or state of things that will end the reminder cycle.
The reminder cycle forms a loop, where:

  1. Some time is waited. For example wait 40 minutes for the washing machine to finish.
  2. The notification condition is verified. For example, I don’t want to be notified that the clothes are ready to be put in the dryer if I’m not home. Neither if I am sleeping.
  3. If the notification condition is not met, wait for it to become true, then
  4. Send notification.
  5. Repeat until action required is done.

Now my question how do I pass the action required and the notification condition to the script so the script can test them at each cycle?

Keep in mind that in my example I simplified the notification condition. For each reminder the notification condition may be a different complex logical combination as expressed in templates. Thence the question : how to pass the text of a template to a script (that part is solved) and force the script to render the template at each cycle pass?

(I changed the title of my post in hopes of better clarity.)

In my original post, I gave the example of the notify.mobile service to prove that there is some way at least to make the script reveal the template result, although I don’t want that result to be the content of any notification, I just want it to be used in the script logics.

To pass a template via a script variable, to be evaluated by the script, would require the Jinja2 equivalent of python’s eval function and it doesn’t (currently) exist in Home Assistant.

1 Like

You can’t pass any YAML with its templates as Taras has said, but if your pattern is very standardised (e.g. a specific action with a light service call followed by a notification, all with their templates in the script), then you can pass the values into the script.

My suggestion would be to first build a few actual automations before you start generalising. My suspicion is that you’re not going to save much with this idea and the exceptions might make your script very complex.

Also explore YAML anchors and HA blueprints as alternatives, and check out the alert integration.

1 Like

Very interested in any other replies you get, but whenever you chave a situation that is “5. Repeat until action required is done” for that portion you would use an alert (look that up, they are great for reminders about a garage door being left open, etc.). I am sure you can put some conditions around the alerts such that they are only started when a specific correct set of conditions are met -

Thank you for the information. The choice is now to either (1) have the repeat logics being repeated for every reminder or (2) to try (probably mostly for fun and curiosity) to use the notify service by the script to send a notification in the void and retrieve the content of the notification in order the emulate the “eval” function you talked about.

I will probably end up doing (1), but I will follow up here if I find anything interesting.

To be honest I didn’t know about the alert integration, and apparently I am trying to recreate a similar service. It seems to be very well made and I will check if it has all the flexibility I want for my use, or maybe I will use it inside of the script I am trying to make.

I still have so much so learn. I will study YAML anchors as they may fit my need.

Thank you.

1 Like