Could I have done this logic in Script rather than using Jinja controls?

So, the code attached below does work - it’s taken me a while as I’m still new to this, but I’m very happy. At least, up to a point - it seems unnecessarily complicated and I’m wondering if, instead of looping through my data three separate times in jinja templates, I could have done it just once using scripting for the FORs and IFs.

What its doing is reading todays events from an external calendar and then extracting the first start and last end times (but excluding phone appointments) to be used for setting up a custom heating schedule. It’s automated to run just after midnight.

Was there a better way?

Some pseudo code:

Get the Events
For Each Event
  Count the Events I care About
  Note the first start and last end times
End for
If there are Events I care about
  Set the first start and last end times
  Set the heating schedule

And the real code:

alias: CheckEvents
sequence:
  - service: calendar.get_events
    target:
      entity_id:
        - calendar.xxxx_gmail_com
    data:
      duration:
        hours: 23
    response_variable: agenda
  - condition:
    - condition: template 
      value_template: >
        {% set ns = namespace(mycounter=0) %}
        {% for event in agenda["calendar.xxxx_gmail_com"]["events"] %}
          {% if event.location != "Phone"  %} 
            {% set ns.mycounter = ns.mycounter + 1 %} 
          {% endif %} 
        {% endfor %}         
        {% if ns.mycounter > 0 %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
  - service: input_datetime.set_datetime
    data:
      timestamp: >-
        {% set ns = namespace(firststart = 0, mycounter=0) %}
        {% for event in agenda["calendar.xxxx_gmail_com"]["events"] %}       
        {% if event.location != "Phone"  %}
          {% if  ns.mycounter == 0 %}
            {% set ns.firststart = event.start %}
          {% endif %}
          {% set ns.mycounter = ns.mycounter + 1 %}
        {% endif %}
        {% endfor %} 
        {{ as_timestamp(ns.firststart,0) }}
    target:
      entity_id: input_datetime.treatmentstarts
  - service: input_datetime.set_datetime
    data:
      timestamp: >-
        {% set ns = namespace(lastend = 0) %}
        {% for event in agenda["calendar.xxxx_gmail_com"]["events"] %}       
        {% if event.location != "Phone"  %}
          {% set ns.lastend = event.end %}
        {% endif %}
        {% endfor %} 
        {{ as_timestamp(ns.lastend,0) }}
    target:
      entity_id: input_datetime.treatmentends
  - service: script.treatmentspecialset
    data: {}
mode: single

A bit of combination in those loops and you could use yaml anchors and aliases to type the code once in the first one, then use the alias to repeat the code in the next 2 with changing the end part where you actually get your data… (I think anyway…)
Don’t Repeat Yourself with Anchors, Aliases and Extensions in Docker Compose Files | by King Chung Huang | Medium.
That about docker compose, but the anchors and aliases work in HA as well. More research could get you what you need.

Thanks, that’s really interesting. I’m not sure it answers the question I was asking, but I am intrigued by the possibilities of this … would it work in a Lovelace YAML file as a method of templating?

I believe so.

I just gave it a quick try … it kind of works, but only once. I used the “x-” key described on that page to define a very simple “template” of a couple of keys, and then pulled it in with “<<:” The expansion worked, but once I’d saved in the editor and gone back, the expansion was now permanent. So this technique works when you are generating code in the first place, but you can’t (AFAICS) use it as a substitute for lovelace templating, which is a shame, as that’s something else I haven’t got the hang of yet!

I suggest using a script variable.

alias: CheckEvents
sequence:
  - service: calendar.get_events
    target:
      entity_id:
        - calendar.xxxx_gmail_com
    data:
      duration:
        hours: 23
    response_variable: agenda
  - variables:
      my_events: >
        {{ agenda['calendar.xxxx_gmail_com']['events']
          | selectattr('location', 'ne', 'Phone') | list }}
  - condition: "{{ my_events | count > 0 }}"
  - service: input_datetime.set_datetime
    target:
      entity_id: input_datetime.treatmentstarts
    data:
      timestamp: "{{ my_events[0].start | as_timestamp }}"
  - service: input_datetime.set_datetime
    target:
      entity_id: input_datetime.treatmentends
    data:
      timestamp: "{{ my_events[-1].end | as_timestamp }}"
  - service: script.treatmentspecialset
    data: {}
mode: single

Ah, now that is a LOT cleaner, thank you. I will give that a go later on when the room in question isn’t in use…

So you are saying that the ui editor expands it and stores it that way, I would not be surprised. Ihaven’t run one of those thru the ui editor.

@123 … yes, that worked, thank you! Much better.

Thanks to you and @Sir_Goodenough - I’ve learned a lot in this little exchange - just think, I could have stuck with my own clunky solution and been none the wiser!

1 Like

You’re welcome!

Please consider marking my post above with the Solution tag. It will automatically place a check-mark next to the topic’s title which signals to other users that this topic has been resolved. This helps users find answers to similar questions.

For more information about the Solution tag, refer to guideline 21 in the FAQ.