I’d like to have a simple binary sensor to indicate “Sleep Mode” or call it what you will. I realise I can define a Times of the Day sensor, but this involves sort of hard-coded start and end times for this mode. I’d instead like to set the time period using “Sleep Start” and “Sleep End” input_datetimes.
I can’t work out how best to then arrive at Sleep Mode.
I have toyed with a templated binary sensor. The logic isn’t as simple as I would expect:
Something like...
state: >-
{% set timenow = now().strftime("%H:%M:%S") %}
{% set start = states('input_datetime.sleep_start') %}
{% set end = states('input_datetime.sleep_end') %}
{{ (start < end and (start < now().time() < end))
or (start > end and ( (start < now and now < "23:59:59") or now < end )) }}
And it also won’t trigger unless the input_datetimes change of course… when I really want it to trigger based on time.
Surely there is an easier way to achieve what I’m trying to do?
state: >-
{% set tnow = as_timestamp(now()) %}
{% set start = today_at(states('input_datetime.sleep_start')) | as_timestamp %}
{% set end = today_at(states('input_datetime.sleep_end')) | as_timestamp %}
{% set midnite = today_at('23:59:59') | as_timestamp %}
{{ (start < end and (start < tnow < end)) or
(start > end and ((start < tnow and tnow < midnite ) or tnow < end )) }}
I believe this will trigger once a minute as you are using now(). I think the conversion to a unix timestamp will make this easier.
Thank you. Timestamps is the approach I was hoping for but I wasn’t aware of anything like at_today. Is there something I need to do to employ this? When I input this to Developer Tools > Template Editor I get an error: UndefinedError: 'at_today' is undefined
I think you are correct re trigger on now(). Templating docs:
Using now() will cause templates to be refreshed at the start of every new minute.
Thank you, I think I’ve got it all working off the back of that. I’ve changed it around a little to simplify / make it more readable:
{% set tnow = as_timestamp(now()) %}
{% set tstart = today_at(states('input_datetime.sleep_start')) | as_timestamp %}
{% set tend = today_at(states('input_datetime.sleep_end')) | as_timestamp %}
{% if tend < tstart %}
{% set tend = (tend + 24*60*60) %}
{% endif %}
{{ tstart, tnow, tend}}
{{ tstart < tnow < tend }}
Final minor query: is there any way to tidy up the if statement there or must it be done in three separate blocks like this? I tried an inline expression but couldn’t get it working.
{% set start = today_at("20:30:00") - timedelta(days= 1 if (now().hour < 12) else 0 ) %}
{% set end = today_at("08:30:00") + timedelta(days= 1 if (now().hour >= 12) else 0 ) %}
{{ start }}
{{end }}
{{ start <= now() <= end}}
so if now.hour is less than 12 (morning) then the start will be yesterday (because timedelta removes one day) 20:30, else today at 20:30.
If the now.hour is more than 12 then the end will be set to tomorrow because timedelta adds a day on end.