How can I get a trigger's attribute in a service template?

I can read the contents of the YAML file can’t I? Yes, I know this is probably a bad idea. Is Python “sandboxed” when running in HA context?

I’m probably giving up on this automation anyway but I’d like to have the tools anyway.

Yes, it’s sandboxed; you can’t import modules.

I see. Well my goal with automation was sunrise-sunset based automation with state-persistence, kind of like Awesome HA Blueprint’s on-off one. But, with offset support.

So, “twenty minutes before sunrise” turn off the light, “1 hour before sunset turn on the lights”, and when HA restarts, check if you should turn on or turn off the lights, taking the offsets in consideration.

I realized quickly I couldn’t make a nice blueprint out of it, because I can’t use variables in sun.trigger offset, but now I guess even an automation will have to be written very inelegantly. I hope I’m missing something and will keep exploring…

(BTW: I know it’s “better” to use “elevation”)

Thanks @123.

When either the Sunset or Sunrise triggers are fired, either set an input_text or publish a persistent mqtt message or something. Then in the homeassistant start trigger, you simply check the state of whatever method you decide to use (in the case of a persistent mqtt, it would be on receiving the mqtt message, rather than homeassistant start). And then set the state accordingly.

Hmmm, I think I get what you mean.
The triggers sunset/sunrise are not the challenge, it’s the reboot where you want to determine if the lights should be turned on or off, based on the sunset/sunrise trigger including the offset.

For as far as I know, you cannot read other trigger values than the the value of the trigger that actually triggers the automation.

One way to achieve the described situation above is by using a helper that you give the offset you want. In this case use two helpers. I assume (never tested) you can use a template in the offset to read the helper value.

Make sure the helper is a time only helper.

Thanks @Recte. You got it exactly.

I’m aware of helpers, the goal is for the automation to be as self-contained as possible, without having to rely on external components such as helpers. But it’s definitely a good suggestion!

One more problem with it: we’re still going to violate DRY and duplicate offsets in the triggers, and in the helper values. Meaning we’ll have to keep them in sync, which is kind-of a no-no…

edit: wrong statement.

Not according to the documentation.

That’s possible.

The following automation turns on a switch at 30 minutes before sunset (if it’s off) and turns it off at 23:30 (if it’s on). If Home Assistant is restarted at any time, at startup the automation determines if it should turn the switch on or off. Most of my scheduled automations are built this way.

- alias: example
  trigger:
  - platform: sun
    event: sunset
    offset: '-00:30:00'
  - platform: time
    at: '23:30'
  - platform: homeassistant
    event: start
  action:
  - variables:
      t_sun: "{{ ((as_timestamp(state_attr('sun.sun', 'next_setting')) - 30*60) | timestamp_custom('(%H,%-M)')) }}"
      t_now: '({{ now().hour }},{{ now().minute }})'
      t_off: '{{(23,30)}}'
      switch: switch.foo
  - choose:
    - conditions:
      - "{{ t_sun <= t_now < t_off }}"
      - "{{ is_state(switch, 'off') }}"
      sequence:
      - service: switch.turn_on
        target:
          entity_id: '{{ switch }}'
    - conditions:
      - "{{ t_now >= t_off or (0,0) <= t_now < t_sun }}"
      - "{{ is_state(switch, 'on') }}"
      sequence:
      - service: switch.turn_off
        target:
          entity_id: '{{ switch }}'
    default: []

@123 is right, using a template for offset doesn’t work. I thought of the variable in the action too but then you have a double administration. How about use a helper that just sets the time you want to trigger.

So for example:

{{ as_timestamp(state_attr("sun.sun","next_rising")) - 30*60 }}

and use that as your time based trigger.

You still have an “external” part, but it’s (to me) the cleanest solution when using an automation.

If someone needs more flexibility, they can adopt the suggestion I just made in this post where they want a random offset for sunrise and sunset.

The Trigger-based Template Sensor computes the desired sunrise/sunset time with offset and then it’s used by a Time Trigger.

Sweet!
I think that is a very likely solution. One could even combine it into one automation, using choose based on the trigger ID.

Sunset and Sunrise trigger will just execute.
Reboot and Midnight will set the values and in case of Reboot it will check if it’s before or after the specified sensor time.

It’s entirely dependent on the user’s requirements.

If the offset is a fixed value then the example I posted works without any additional entity; yes, changing the offset value means editing two places within the automation but that’s a fairly trivial task.

On the other hand, if there are multiple automations that require an offset sunrise (or there’s a need to randomize the offset), then it makes sense to create a Trigger-based Template Sensor that they can all reference.

Anyway, that’s just minor details; the main point of the example was to demonstrate how to correctly set the switch’s state after a restart.

Lets see what @sk229 thinks of it :smiley:
For as far as I understand what @sk229 wants, al the ingredients to solve the challenge are here.

And…I learned something new today too, thanks :grinning_face_with_smiling_eyes:

Lots of knowledge shared here by everyone, thanks!

I did go with a more primitive solution after all. The offsets are defined twice - in the triggers, and in the service template. When the trigger is HA start, I calculate off-time and on-time start times, and depending on that (specifically, which is next), call light.turn_on/off. At least both definitions are in the automation itself. I don’t like it, but I don’t mind it too much either as it’s only one automation for the outside lights.

When/if HA starts supporting variables in offset for the sun platform, this can be simplified a lot, and made as a blueprint.

That sounds like what I had suggested in the example I posted above.

Post the automation you created.

I’ve actually just discovered a bug that I’d like to debug. Will post here once I know I’ve got something working.

Review the one I posted. It’s from an automation that successfully controls lighting in my home every evening (except the version I use also incorporates a light sensor).

OK, here’s my version:

alias: Sunrise-Sunset schedule with state persistence - outdoor lights
description: ''
trigger:
  - platform: homeassistant
    event: start
    id: ha
  - platform: sun
    event: sunrise
    offset: '-05:20:00'
    id: sunrise
  - platform: sun
    event: sunset
    offset: '-00:10:00'
    id: sunset
condition: []
action:
  - service: |
      {% if trigger.id == 'sunrise' or trigger.id == 'sunset'  %} 
        {% if state_attr('sun.sun', 'rising') %}
          light.turn_off
        {% else %}
          light.turn_on
        {% endif %}
      {% else %}
        {% set sunrise_offset = '05:20:00' %}
        {% set sunrise_offset_list = sunrise_offset.split(':') %}
        {% set sunrise_offset_in_seconds = sunrise_offset_list[0]|int|abs * 3600 + sunrise_offset_list[1]|int|abs * 60 + sunrise_offset_list[2]|int|abs %}
        
        {% set sunset_offset = '00:10:00' %}
        {% set sunset_offset_list = sunset_offset.split(':') %}
        {% set sunset_offset_in_seconds = sunset_offset_list[0]|int|abs * 3600 + sunset_offset_list[1]|int|abs * 60 + sunset_offset_list[2]|int|abs %}
        
        {% set off_time_starts = as_timestamp(state_attr('sun.sun', 'next_rising')) - sunrise_offset_in_seconds %}
        {% set on_time_starts = as_timestamp(state_attr('sun.sun', 'next_setting')) - sunset_offset_in_seconds %}
        
        {% set first_event = on_time_starts if on_time_starts < off_time_starts else off_time_starts %}
        {% set second_event = on_time_starts if on_time_starts >= off_time_starts else off_time_starts %}
        
        {% if as_timestamp(now()) >= first_event and as_timestamp(now()) < second_event %}
          light.{{"turn_on" if first_event == on_time_starts else "turn_off"}}
        {% else %}
          light.{{ "turn_on" if second_event == on_time_starts else "turn_off"}}
        {% endif %}
      {% endif %}
    target:
      entity_id: light.outdoors
mode: single

I’m sure there’s lots to improve here, but it’s working and it’s a unique/rare enough workflow for me not to care too much about reusability, etc…or even a simple refactor. For example, I started off in the UI so I was getting the offsets as strings (or at least I thought so), but I can easily redefine them as ints that represent seconds and shave off 4 lines of code. I’m sure there’s a lot more to be improved, but I haven’t gotten the time to do that yet.

1 Like

Yes.

Good to know; no need for anyone to expend time and effort demonstrating better ways to do it.