Running something once a day regardless of time zone

Hi,

I’m trying to setup an automation so that my vacuum runs when both of us have left the house, but is limited to once per day. I came up with the following snippet based on code from elsewhere on this forum.

    - alias: 'Start cleaning when everyone is away'
  trigger:
   platform: state
   entity_id: group.people
   to: 'not_home'
  condition:  
    condition: and
    conditions:
      - condition: template
        value_template: '{{ now().day != states.automation.start_cleaning_when_everyone_is_away.attributes.last_triggered.day | default(0) }}'
  action:
    - service: vacuum.set_fan_speed
      data:
       entity_id: vacuum.xiaomi_vacuum_cleaner
       fan_speed: 100  
    - service: vacuum.start
      entity_id: vacuum.xiaomi_vacuum_cleaner

This works ok and my initial testing looked like it worked, but it ran for a second time after running once in the morning. I think this is because it became a new day in UTC at around midday Australian time.

Does anyone have any advice on how to fix this up?

Have you tried utcnow(), that is equivalent to the utc timestamps on the triggered attribute

The problem is that the last_triggered attribute is a string, not a Python datetime object, so you can’t use the day method. You could try something like this:

  condition:  
    - condition: template
      value_template: >
        {% set last = state_attr('automation.start_cleaning_when_everyone_is_away',
                                 'last_triggered') %}
        {{ last is none or
           as_timestamp(last)|timestamp_custom('%j')|int !=
           now().strftime('%j')|int }}

This will be true if the automation has never been triggered, or if it has been triggered, but not today. it will be false if the automation was last triggered today.

Note that the %j format string means the day of the year (i.e., 1 through 365.) Also, I use now() instead of utcnow() because timestamp_custom, by default, uses local time.

Thanks for that. I’m new to home assistant.

Is there any way to run this interactively/debug/through a console so that one can get their head around it?
i.e. if someone wanted to know, at a glance, what the current state value of last_triggered was, or to test what as_timestamp(last) does, what would be the recommended way of doing this.

I know that you can look at the entity last_triggered using the entity browser but how would you quickly see how it behaves when passed to the as_timestamp function?

Go to the Templates page under Developer tools and use the Template Editor. I use that constantly. :slight_smile:

I’ve actually noticed that last_triggered is sometimes a string and sometimes a datetime object. It’s really odd. I can’t quite put my finger on why it’s happening.

1 Like

Thanks,

I think i’m missing something simple in the template editor -

I’ve tried this syntax
{% as_timestamp(state_attr('automation.start_cleaning_when_everyone_is_away','last_triggered')) %}

which gives the error

Error rendering template: TemplateSyntaxError: Encountered unknown tag 'as_timestamp'.

Answering my own question - the syntax is

{{ as_timestamp(state_attr('automation.start_cleaning_when_everyone_is_away','last_triggered')) }}

1 Like

Whoa! I just did this:

{% for x in states.automation if x.attributes.last_triggered is not none %}
{{ x.object_id }}:
{{ x.attributes.last_triggered }} {{ x.attributes.last_triggered is string }}
{% endfor %}

Sure enough, some are strings, and some are not. The ones that are strings look like this:

2019-02-25T13:00:00.251124+00:00

Whereas the ones that aren’t (and which are datetimes) look like this:

2019-02-25 19:38:52.049082+00:00

That is nuts! I never noticed this before.

I checked the code and it looks like the attribute is written as a datetime (from utcnow()). I’m guessing this is because of the relatively recent change to saving & restoring states. Wow, if it is, if you ask me, that’s a bug!!

EDIT: I wonder how many of my automations are broken by this???

Yeah, I stumbled on it helping @Mariusthvdb the other day. It’s the weirdest thing. I don’t know where it’s coming from… It really screwed up @Mariusthvdb’s templates too.

Lol, in typical fashion, I ignored it! I suppose I could write up a PR. It really didn’t cross my mind that it’s a bug.

I wrote up an issue about trigger.now changing from local time to UTC time, which breaks automations. The reaction by the person who made the change that caused this side effect said, well, it’s not documented, so too bad. Really makes one want to take the time to report problems. :frowning:

Interesting. I know I’ve used last_triggered in automations before, but apparently I don’t currently use it in any. So I guess the answer is zero. :slight_smile: On the bright side, converting it to a timestamp using as_timestamp works whether it’s a datetime or a string.

Yeah, that’s what we ended up finding. I’m wondering if that’s the real reason it hasn’t been found.

Some people take this to heart. I can’t believe they just didnt’ fix the issue. was it a simple fix or complicated?

See for yourself:

I haven’t tried to get more visibility on it (not even sure how) since it’s simple enough to fix, IMHO, but I’ve just been too busy to get around to doing it myself. Besides, one would think the person that broke it would want to take responsibility for what they did (rather than come up with a lame excuse.)

EDIT: BTW, to work around the problem, rather than just doing:

now().hour

in a template, I now have to do:

{% set hr = as_timestamp(trigger.now)|timestamp_custom('%H')|int %}

Just what my little Pi needs – more work for no good reason. :wink:

yeah… his response is lazy.

my final conclusion in Find the error in: automation last_triggered with now() or timestamp templates was that the last_triggered attribute isn’t set when (the conditions of the automation aren’t all met, and) the action part of the automation isn’t called.

Look in my example, where I needed the last_triggered of the actual automation itself to have passed a certain time.
Using that condition in the condition section of the automation, it won’t pass (since it hasn’t been set yet), and action section isn’t called. The attribute last_triggered isn’t set, and hence all templates relying on that will fail, and shows the T.
Using that condition in the action part though, makes it all work, since arriving at the action part of the automation sets the last_triggered attribute. T gone.

Ive learned:

  • it might be a bug, and how to resolve that:

  • using conditions in the action part, which has the real added advantage of being able to test an automation in HA by manually triggering it and evaluating all conditions (which doesnt happen in a regular setup, because manually triggering immediately jumps to the action section of an automation )

  • last_triggered, should really be called last_called_action, since that’s what it is showing right now. The last time the action section of an automation is called.

  • put differently: last_triggered isn’t truly last_triggered, since either triggering the automation manually or automated in HA real life, the attribute isn’t set if not entering the action phase of the automation, meaning it doesnt look at the trigger moment, but at the action moment…

happily using this without a hiccup now:

{{ (now() - state_attr('automation.do_something','last_triggered')).total_seconds() > 120 }}

btw, as documented in the other linked thread, it wasn’t a string either…

I achieved this by ensuring the Mode of my automation is set to “Single” and then adding a wait_for_trigger at the end of my automation, waiting for midnight. This will essentially prevent the automation from running the rest of the day.

wait_for_trigger:
  - platform: time
    at: '00:00:00'
continue_on_timeout: false

Be aware that the longer anything waits (like wait_for_trigger, wait_template, for, delay, etc) the more susceptible it is to being reset by Reload Automations or a restart.

Thanks for the tip. In my case, this is an acceptable risk as the worst case is that my vacuum starts again. But, I rarely reload automations or restart the system so I like the simplicity of the wait_for_trigger.

If you use the Automation Editor to create/modify an automation, the moment you click the Save button, it will reload all automations.