Setting Time Slot States on Re-Start (works but ...)

First Off, what I have works, it just looks like a dogs’ breakfast : - ((((
I’ve tried inumerable ways to get the final data_template onto multiple lines : -

  • if True
    – Do This
  • elif True
    – Do That
  • else
    – Do Other stuff
  • endif

But no joy.
So I’m posting this here so that someone can point out where I’m being stupid AND so that others can at least take what I have and adapt it to their onw transient states.
The code ‘should’ be self explanatory with input_number’s and input_datetime used so that you can configure stuff from the frontend.

Anyway, its taken from a package (which I could post the 3 packages involved if needed) : -

script:
  #script to set 'between' statuses, e.g. darkslot, timeofday for lighting etc.
  sc_ha_gen_reboot:
    alias: Re-Boot Check
    sequence:
    - service_template: >
        {% if (states.sun.sun.attributes.elevation <= states('input_number.in_light_flood_on_elev_offset') | float and not states.sun.sun.attributes.rising or states.sun.sun.attributes.elevation <= states('input_number.in_light_flood_off_elev_offset') | float and states.sun.sun.attributes.rising) %}
          input_boolean.turn_on
        {% else %}
          input_boolean.turn_off
        {% endif %}
    #data_template:
      entity_id: input_boolean.ib_global_darkslot
    - service: input_select.select_option
      data_template:
        entity_id: input_select.is_aalight_segment
        option: "{% if ((states.sun.sun.attributes.elevation > states('input_number.in_aalight_day') | float) and (states.sun.sun.attributes.rising)) or ((states.sun.sun.attributes.elevation > states('input_number.in_aalight_evening') | float) and not (states.sun.sun.attributes.rising)) %}Day{% elif ((states.sun.sun.attributes.elevation < states('input_number.in_aalight_evening') | float) and not (states.sun.sun.attributes.rising)) and (states('sensor.time') < states('input_datetime.id_aalight_night')) %}Evening{% else %}Night{% endif %}"

Another thing I’ve noticed is that without the spurious ‘comment’ in the first service, the entity_id seems to become part of the template when using notepad+
No big rush, as it does work : - ))) and it’s more for my curiosity and leaning
Cheers

Have you tried adding a > after option: and then placing your template on the next line, without double-quotes, and indenting it by two spaces?

    - service: input_select.select_option
      data_template:
        entity_id: input_select.is_aalight_segment
        option: >
          {% if ((states.sun.sun.attributes.elevation > states('input_number.in_aalight_day') | float) and (states.sun.sun.attributes.rising)) or ((states.sun.sun.attributes.elevation > states('input_number.in_aalight_evening') | float) and not (states.sun.sun.attributes.rising)) %}
          Day 
          {% elif ((states.sun.sun.attributes.elevation < states('input_number.in_aalight_evening') | float) and not (states.sun.sun.attributes.rising)) and (states('sensor.time') < states('input_datetime.id_aalight_night')) %}
          Evening
          {% else %}
          Night
          {% endif %}

Taras,
Thanks for your reply, I read it in the coffee shop, was sure I had tried that … but as inaccuracy often offends (especially me !) I decided to wait till I got back home to check.
It Passes the configuration check without any quibble, but it does not set the input_select.
So nope, I noticed that the Template editor returns Day rather than ‘Day’ or “Day” so I fiddled with it to get those outputs … still no joy. It won’t set the input_select. : - (((((
Thanks anyway (proves I’m not a TOTAL moron (though my wife has doubts)

Your original goal was to get the template on ‘multiple lines’ and, given that it passes the config check, it has been achieved. The fact it doesn’t set the input_select is an entirely separate challenge.

Firstly, the general structure of what I suggested is valid. It most certainly can set an input_select. Here’s a simple example (if somewhat contrived) of setting an input_select. I’ve tested it and it works.

The input_select with three states:

input_select:
  home_mode:
    options:
      - Home
      - Away
      - Sleep
    initial: 'Sleep'

The automation, triggered by an input_boolean:

- alias: 'input_select test'
  trigger:
    platform: state
    entity_id: input_boolean.toggler
  action:
    service: input_select.select_option
    data_template:
      entity_id: input_select.home_mode
      option: >
        {% if trigger.to_state.state == 'on' %}
        Home
        {% else %}
        Away
        {% endif %}

As you can see, there’s no need to delimit the value with either single or double quotes as you had attempted:

Please post the configuration for input_select.is_aalight_segment.

input_select:
  is_aalight_segment:
    name: Light Level Selector
    options:
      - Day
      - Evening
      - Night

And one of the Automations to trigger a transition between two states : -
(which also works, as do the other two)

automation:
    #name: Light Transition To Day
  - alias: au_aalight_to_day
    trigger:
      - platform: template
        value_template: "{{ (states.sun.sun.attributes.elevation > states('input_number.in_aalight_day') | float) and (states.sun.sun.attributes.rising) }}"
    condition:
      - condition: state
        entity_id: input_boolean.ib_aalight_transitionenable
        state: 'on'
    action:
      - service: input_select.select_option
        data:
          entity_id: input_select.is_aalight_segment
          option: 'Day'

Try this automation. You’ll need to either create input_boolean.toggler or use some other one you already have. Toggling the inpout_boolean will toggle the input_select between Day and Night.

- alias: 'input_select test'
  trigger:
    platform: state
    entity_id: input_boolean.toggler
  action:
    service: input_select.select_option
    data_template:
      entity_id: input_select.is_aalight_segment
      option: >
        {% if trigger.to_state.state == 'on' %}
        Day
        {% else %}
        Night
        {% endif %}

Sorry Tara’s, what am I testing ?
I currently have 3 segments of the day and 4 groups of lights, all with their own light levels depending on segment.
When the segment changes, the level for that group changes and each light (if on) sets to the new level. Any new light switched on comes on at the current group level.
Why would I swap that for just two levels ?
I admit that your code looks clean and probably works (I can test in the morning) but I’m not clear on the why.
Do you think if we can get it working bi-state, we can then move on to Tri-state ?
Regards
Mutt

You’re carrying out an experiment. The goal is to confirm that this simple automation, containing a template for option, can successfully set the input_select.

  • If it succeeds, we can circle back to your original automation and try to determine why it failed to set the input_select.
  • If it fails, we’re facing a bigger problem …

Okay,
Some interesting results …
First I used your example in an automation : -

  - alias: 'input_select test'
    trigger:
      platform: state
      entity_id: input_boolean.ib_global_test
    action:
    - service: input_select.select_option
      data_template:
        entity_id: input_select.is_aalight_segment
        option: >
          {% if trigger.to_state.state == 'on' %}
            Day
          {% else %}
            Night
          {% endif %}

And it works, set the select to ‘evening’ then hit the bit and depending on where you left the bit before setting evening it will toggle to Day or Night or just flip with the bit as expected.
So I move the code to a Script (same code, just triggered manually via exceute)

  sc_ha_gen_test:
    alias: Test Script
    sequence:
    - service: input_select.select_option
      data_template:
        entity_id: input_select.is_aalight_segment
        option: >
          {% if trigger.to_state.state == 'on' %}
            Day
          {% else %}
            Night
          {% endif %}

BOTH passed config check, but the script, with the exact same code does NOT work, execute all day long (no matter the position of the bit), the select will stay where it was, no matter where it starts.
So it seems to be down to the fact that it’s a script and ‘may’ have a different indenting requirement or something ???

Now we’re getting somewhere.

At first glance, I don’t see anything wrong with the script … so I’ll have to experiment with it to learn if there’s some sort of limitation.


EDIT

On second glance, I do see something wrong with it. It contains a reference to the trigger object:

trigger.to_state.state

There’s no trigger in a script. The first test in the template always fails. That’s why when you manually executed the script it did not toggle the input_select’s state.

Shoot me ! I’m stupid. Obvious now you have said it …
I’ll be back in 5 Mins

Okay, It’s a case of reading what you think is there rather than what’s actually there.
Modified the ‘Script’ and now have this : -

  sc_ha_gen_test:
    alias: Test Script
    sequence:
    - service: input_select.select_option
      data_template:
        entity_id: input_select.is_aalight_segment
        option: >
          {% if states('input_boolean.ib_global_test') == 'on' %}
            Day
          {% else %}
            Night
          {% endif %}

Set to evening, set bit on and fire … Day
Set to evening, set bit off and fire … Night
It works, so what’s next ?

You’ve confirmed that it’s possible to set the input_select’s state. That means, in the original script, there must be something wrong with the template’s tests. Those long-winded ‘if this and that or the other thing’ may not be behaving the way you think.

I’ve re-written it using variables in order to make the tests more legible. Review it to ensure the logic is correct.

    - service: input_select.select_option
      data_template:
        entity_id: input_select.is_aalight_segment
        option: >
          {% set elev = state_attr('sun.sun', 'elevation') | float %}
          {% set rising = state_attr('sun.sun', 'rising') %}
          {% set day = states('input_number.in_aalight_day') | float %}
          {% set day = states('input_number.in_aalight_evening') | float %}
          {% if (elev > day and rising) or (elev > evening and not rising) %}
          Day 
          {% elif (elevation < evening and not rising) and (states('sensor.time') < states('input_datetime.id_aalight_night')) %}
          Evening
          {% else %}
          Night
          {% endif %}

Are you sure the test using sensor.time and input_datetime is correct? You’re not comparing two integers or floats but strings containing the time. In addition, both have hours and minutes but one also includes seconds.

As my ‘jinja’ skills improve, due mainly to the patience of 123, pnbruckner and petros.
I have managed to solve this issue.
Basically I have 3 ‘states’ that change during the day based on different conditions.

  1. Lighting levels based on both elevation of the sun (different on ‘sunrise’ (…ish) to ‘sunset’ (…ish) and given time spots, all adjusted through the UI. (3 periods : - Day, Evening, Night
  2. Bathroom/Ventilation Fan Slot (also used as sounder/notification permissive) just based on time. Again, all adjusted through the UI
  3. Outside Light Enable (floods, porch and patio) this based soley on sun elevation (but again different on ‘sunrise’ (…ish) to ‘sunset’ (…ish)
    This is fine and dandy if you never restart, but if you do … how do you know where you are ?
    So, my Idea was to write a script that would be triggered on a restart and use the exact same criteria to set the status of these ‘segments’. The idea being to allow it to be extended (as required) for other items and allow it’s use by others with similar needs.
    Here is the final script, please note that ‘some’ of the ‘#’ comments are REALLY close to the left margin, this was generally required to force the jinja interpretter to see them as comments and not just a mangling of the template.
script:
  #script to set 'between' statuses, e.g. darkslot, timeofday for lighting etc.
  sc_ha_gen_reboot:
    alias: Re-Boot Check Status Script
    sequence:
       ### set the 'on-slot' permissive for : - floodlights, patio & porch lights
    - service_template: >
        {% if (state_attr('sun.sun', 'elevation') <= states('input_number.in_light_flood_on_elev_offset') | float and not state_attr('sun.sun', 'rising')
          or state_attr('sun.sun', 'elevation') <= states('input_number.in_light_flood_off_elev_offset') | float and state_attr('sun.sun', 'rising')) %}
            input_boolean.turn_on
        {% else %}
            input_boolean.turn_off
        {% endif %}
  # end of template: but entitty_id follows
      entity_id: input_boolean.ib_global_darkslot
       ### set the light level segment of the day
    - service: input_select.select_option
      data_template:
        entity_id: input_select.is_aalight_segment
        option: >
          {% if (state_attr('sun.sun', 'elevation') > states('input_number.in_aalight_day') | float and state_attr('sun.sun', 'rising'))
            or (state_attr('sun.sun', 'elevation') > states('input_number.in_aalight_evening') | float and not state_attr('sun.sun', 'rising')) %}
              Day
          {% elif (state_attr('sun.sun', 'elevation') > states('input_number.in_aalight_evening') | float and not state_attr('sun.sun', 'rising'))
            and (states('sensor.time') < states('input_datetime.id_aalight_night') [0:5]) %}
              Evening
          {% else %}
              Night
          {% endif %}
 ### set the fan/noise permissive bit
    - service_template: >
        {% if (states('input_datetime.id_fans_allowed_on') [0:5] < states('sensor.time') < states('input_datetime.id_fans_forced_off') [0:5]) %}
          input_boolean.turn_on
        {% else %}
          input_boolean.turn_off
        {% endif %}
# end of template: but entitty_id follows  (this is also used as a 'noise permissive' for sirens
      entity_id: input_boolean.ib_fan_permissive

It’s pretty complete and extensively tested, but if anyone can break it or improve on it, I would be happy to help tweek it. And also help others adapt it to their needs.
Cheers
Mutt

I don’t think I helped but you’re welcome.

Personally, I’d probably go a different route only because I don’t like giving people options to directly change states. Any kind of user input can be overridden by the user and I don’t like them mucking it up. So i’d create template binary sensors and sensors with that information. You don’t even need to update on startup because the sensors will update every minute.

I’d also take @123’s approach to make it easier to read.

binary_sensor:
  - platform: template
    sensors:
      ib_global_darkslot:
        entity_id: sensor.time
        value_template: >
          {% set elevation = state_attr('sun.sun', 'elevation') | float %}
          {% set rising = state_attr('sun.sun', 'rising') %}
          {% set flood = states('input_number.in_light_flood_on_elev_offset') | float %}
          {{ (elevation <= flood and not rising) or (elevation <= flood and rising) }}
      id_fans_allowed_on:
        value_template: >
          {% set fan_on = states('input_datetime.id_fans_allowed_on')[0:5] %}
          {% set fan_off = states('input_datetime.id_fans_forced_off')[0:5] %}
          {{ fan_on < states('sensor.time') < fan_off }}
sensor:
  - platform: template
    sensors:
      is_aalight_segment:
        value_template: >
          {% set elevation = state_attr('sun.sun', 'elevation') | float %}
          {% set day = states('input_number.in_aalight_day') | float %}
          {% set rising = state_attr('sun.sun', 'rising') %}
          {% set evening = states('input_number.in_aalight_evening') | float %}
          {% set evening_time = states('input_datetime.id_aalight_night')[0:5] %}
          {% set time = states('sensor.time') %}
          {% if (elevation > day and rising) or (elevation > evening and not rising) %}
              Day
          {% elif (elevation > evening and not rising) and (time < evening_time) %}
              Evening
          {% else %}
              Night
          {% endif %}

EDIT: Then base your automations off the sensor, and binary sensors.

I’ve read a LOT of ‘template examples’ on the forum and the ones I’ve found most relevant/interesting to me (I’ve skipped others because their efforts were not linked to digging in the same holes as I have underway) were yourself Teras and Phil. So that’s where I gave the credit.
Next week somebody else will be my superhero ! ; - ))))))))))
I like the approach, and the explanation. I’ll look into this.
I was thinking about changing my uptime to minutes but was worried that I’d overrun the sensor, with changes to my config going on like this, I’ll NEVER get to 22 days, 18 hrs, 7 mins (or whatever the limit is).
It’s all ‘grist for the mill’ and once again, I thank you
Cheers

1 Like

@123 , @petro ,
I’ve noticed that during building and testing, it’s very useful to be able to flip variables around ‘A LOT’ to see how this behaves when interacting with an array of ‘inputs’.
I’ve also noticed that as they become more stable, I (historically) have moved them into ‘sensors’.
This was not a concious process, just something that happened.
You have made the case and I’ve made the association.
It is a LOT easier to test an automation or script (just hit reload) rather that having to wait for a restart (especially if any of it is z-wave network dependant.
I think I’ll move them over, after a little more testing and a re-write. (more to make sure “I” understand whats going on).
Cheers