new template doesn’t care about days. It goes off days of the year and works year round regardless of the month
really sorry, but I don’t understand what you are saying here…
The template that uses %j
uses the yearly day number. For example, the day number today is 153. You can simply get that number, add 1 to it, then convert it back in to a date to get the date number without caring what month it is or whatever. You just need to watch out for day 365+1. It didn’t work in 0.84 but it does work in the latest.
if you mean this:
there seems to be no issue in 84.3…
the problem was with strptime not understanding ‘%j’, not strftime
ok, thanks.
for reference, this is the thread we discussed that earlier.
Still annoyed by the fact I don’t understand why my 84.3 template failed doing 31+1-31 and didn’t doing 30+2-31, 29+3-31, etc etc.
Hope time will tell if my current template (with the amended |int) is doing better now. In 2 months…
there wouldn’t be a way to use %j in the current template? given the fact it calculates the day number perfectly. It might be easier than adding a delta, subtracting the days of the current month and substituting the current month with the next…
–edit–
just remembered I can’t use the |int in the {% set%} section, because it makes ‘Not set’ into 0… which kills the template if not set…
will have to make a catch for that in a newer iteration .
personally, I think you are overcomplicating all of this. You are always doing these calculations based on number of days when you can just store the date and subtract. Instead you only know today and and the number of days til the next alarm and you have to do goofy subtracting and adding. I tried telling you this awhile ago but you ignored me. If this were my setup, i’d only have 1 sensor, next alarm date as a datetime object. Then to do all your calculations, you have simple additions or subtractions based on the current date with datetime objects.
Not really sure where you’d get the idea of me ignoring you… this is so much besides the truth I can’t even start to express how…
having said that, I don’t recall having another route to finding the alarm day next to what I have now, based on the getalarm() macro, which takes the number of days for offset.
If you say you’d use a next alarm date sensor as base, and do calculations based on that, how would you establish that? How would you arrive eg at the next weekend, without knowing and adding the number if days?
you know of my weekend and weekday booleans. there’s no date in those, and one would have to use the number of days to find the next alarm. In fact, that works perfectly now, with your help I might add…
I’m fully open to rebuilding that again to another paradigm, so please help me with the pointer to that?
Again, sorry if I gave the wrong impression, I wasn’t aware of that.
Well first, what are all the sensors that you currently have for the next_alarm time? Please post the yaml for them, not a screenshot.
Of course, here goes (have several shorter alternatives (posted 1 below) for various sensors, but to keep it all with the macro, only posted these here now)
next_alarm_day:
friendly_name: Next alarm day
entity_id:
- sensor.time
- sensor.number_of_days_next_alarm_macro
- input_boolean.alarmclock_wd_enabled
- input_boolean.alarmclock_we_enabled
value_template: >
{% set day_delta = states('sensor.number_of_days_next_alarm_macro') %}
{% set day = now().day %}
{% set month = now().month %}
{% set month_days = states('sensor.days_current_month')|int %}
{% set month_delta = 1 if month < 12 else 1-12 %}
{% if is_state('input_boolean.alarmclock_wd_enabled','off') and
is_state('input_boolean.alarmclock_we_enabled','off') %} {% set daytype = 'Not set' %}
{% elif day_delta == '0' %} {% set daytype = 'Today,'%}
{% elif day_delta == '1' %} {% set daytype = 'Tomorrow,' %}
{% elif day_delta == '2' %} {% set daytype = 'The day after tomorrow,' %}
{% elif day_delta > '2' %} {% set daytype = 'Next' %}
{% endif %}
{% if day_delta == 'Not set' %} Not set
{% elif day + day_delta|int > month_days %}
{{daytype}} {{ as_timestamp(now().replace(month= month + month_delta).replace(day=(day +
day_delta|int - month_days))) | timestamp_custom('%A, %-d %B')}}
{% else %}
{{daytype}} {{ as_timestamp(now().replace(day=(day + day_delta|int )))| timestamp_custom('%A, %-d %B') }}
{% endif %}
day counter:
number_of_days_next_alarm_macro:
friendly_name: Days to next alarm macro
entity_id:
- sensor.time
- input_boolean.alarmclock_wd_enabled
- input_boolean.alarmclock_we_enabled
- input_number.alarmclock_wd_hour
- input_number.alarmclock_wd_minute
- input_number.alarmclock_we_hour
- input_number.alarmclock_we_minute
value_template: >
{%- macro getalarm(offsetday=0) %}
{%- set day = (now().weekday() + offsetday) % 7 %}
{%- set offset = offsetday * 86400 %}
{%- set fmat = 'd' if day in range(5) else 'e' %}
{%- set hour = 'input_number.alarmclock_w{}_hour'.format(fmat) %}
{%- set minute = 'input_number.alarmclock_w{}_minute'.format(fmat) %}
{%- set alarm = states(hour) | float | multiply(3600) + states(minute) | float | multiply(60) %}
{%- set time = now().hour * 3600 + now().minute * 60 %}
{{ (offset - time) + alarm if offsetday else alarm - time }}
{%- endmacro %}
{%- set weekday = is_state('input_boolean.alarmclock_wd_enabled','on') %}
{%- set weekend = is_state('input_boolean.alarmclock_we_enabled','on') %}
{%- set weekdays = [0,1,2,3,4] %}
{%- set weekends = [5,6] %}
{%- set day = now().weekday() %}
{%- set nextday = day + 1 if day != 6 else 0 %}
{%- if nextday in weekdays %}
{%- if weekday %}
{%- set offsetday = nextday %}
{%- elif weekend %}
{%- set offsetday = weekends[0] %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{%- elif nextday in weekends %}
{%- if weekend %}
{%- set offsetday = nextday %}
{%- elif weekday %}
{%- set offsetday = weekdays[0] %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{% if offsetday != None %}
{% set offset = offsetday-day if offsetday > day else offsetday - day + 7 %}
{% if (day in weekdays and weekday) or (day in weekends and weekend) %}
{% set today_alarm = getalarm() | float %}
{% else %}
{% set today_alarm = -1 %}
{% endif %}
{% set next_alarm = getalarm(offset) | float %}
{% if today_alarm > 0 %}
0
{% else %}
{{ offset }}
{% endif %}
{% else %}
Not set
{% endif %}
time until:
time_until_next_alarm_macro:
friendly_name: 'Time until next alarm macro'
entity_id:
- sensor.time
- input_boolean.alarmclock_wd_enabled
- input_boolean.alarmclock_we_enabled
- input_number.alarmclock_wd_hour
- input_number.alarmclock_wd_minute
- input_number.alarmclock_we_hour
- input_number.alarmclock_we_minute
value_template: >
{%- macro getalarm(offsetday=0) %}
{%- set day = (now().weekday() + offsetday) % 7 %}
{%- set offset = offsetday * 86400 %}
{%- set fmat = 'd' if day in range(5) else 'e' %}
{%- set hour = 'input_number.alarmclock_w{}_hour'.format(fmat) %}
{%- set minute = 'input_number.alarmclock_w{}_minute'.format(fmat) %}
{%- set alarm = states(hour) | float | multiply(3600) + states(minute) | float | multiply(60) %}
{%- set time = now().hour * 3600 + now().minute * 60 %}
{{ (offset - time) + alarm if offsetday else alarm - time }}
{%- endmacro %}
{%- set weekday = is_state('input_boolean.alarmclock_wd_enabled','on') %}
{%- set weekend = is_state('input_boolean.alarmclock_we_enabled','on') %}
{%- set weekdays = [0,1,2,3,4] %}
{%- set weekends = [5,6] %}
{%- set day = now().weekday() %}
{%- set nextday = day + 1 if day != 6 else 0 %}
{%- if nextday in weekdays %}
{%- if weekday %}
{%- set offsetday = nextday %}
{%- elif weekend %}
{%- set offsetday = weekends[0] %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{%- elif nextday in weekends %}
{%- if weekend %}
{%- set offsetday = nextday %}
{%- elif weekday %}
{%- set offsetday = weekdays[0] %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{% if offsetday != None %}
{% set offset = offsetday-day if offsetday > day else offsetday - day + 7 %}
{% if (day in weekdays and weekday) or (day in weekends and weekend) %}
{% set today_alarm = getalarm() | float %}
{% else %}
{% set today_alarm = -1 %}
{% endif %}
{% set next_alarm = getalarm(offset) | float %}
{% if today_alarm > 0 %}
{% if today_alarm/3600 < 1 %} {{ today_alarm | timestamp_custom('%-M', False) }}
{% else %}{{ today_alarm | timestamp_custom('%-H:%-M', False) }}
{% endif %}
{% elif offset == 1 %}
{% if next_alarm/3600 < 1 %} {{ next_alarm | timestamp_custom('%-M', False) }}
{% elif next_alarm/86400 <1 %} {{ next_alarm | timestamp_custom('%-H:%-M', False) }}
{% else %} {{(next_alarm // 86400) | int }}:{{ next_alarm | timestamp_custom('%-H:%-M', False) }}
{% endif %}
{% else %} {{(next_alarm // 86400) | int }}:{{ next_alarm | timestamp_custom('%-H:%-M', False) }}
{% endif %}
{% else %}
Not set, relax
{% endif %}
or a shorter variant:
time_until_next_alarm:
friendly_name: 'Time until next alarm'
entity_id:
- sensor.time
- input_boolean.alarmclock_wd_enabled
- input_boolean.alarmclock_we_enabled
- input_number.alarmclock_wd_hour
- input_number.alarmclock_wd_minute
- input_number.alarmclock_we_hour
- input_number.alarmclock_we_minute
value_template: >
{%- macro getalarm(offsetday=0) %}
{%- set day = (now().weekday() + offsetday) % 7 %}
{%- set offset = offsetday * 86400 %}
{%- set fmat = 'd' if day in range(5) else 'e' %}
{%- set hour = 'input_number.alarmclock_w{}_hour'.format(fmat) %}
{%- set minute = 'input_number.alarmclock_w{}_minute'.format(fmat) %}
{%- set alarm = states(hour) | float | multiply(3600) + states(minute) | float | multiply(60) %}
{%- set time = now().hour * 3600 + now().minute * 60 %}
{{ (offset - time) + alarm if offsetday else alarm - time }}
{%- endmacro %}
{% set week_on = is_state('input_boolean.alarmclock_wd_enabled','on') %}
{% set end_on = is_state('input_boolean.alarmclock_we_enabled','on') %}
{% set in_days = states('sensor.number_of_days_next_alarm_alt')|int %}
{% set alarm = getalarm(in_days) | float %}
{% if week_on or end_on %}
{% if (alarm / 3600 ) < 1 %}
{{ alarm | timestamp_custom('%-M', False) }}
{% elif (alarm / 86400 ) < 1 %}
{{ alarm | timestamp_custom('%-H:%-M', False) }}
{% else %}
{{ (alarm // 86400 )|int }}:{{ alarm | timestamp_custom('%-H:%-M', False) }}
{% endif %}
{% else %} Not set, relax
{% endif %}
text for tts:
next_alarm_text:
friendly_name: Next alarm text
entity_id:
- sensor.time
- sensor.time_until_next_alarm_macro
- sensor.next_alarm_day
- sensor.alarmclock_wd_time
- sensor.alarmclock_we_time
value_template: >
{% set time = states('sensor.time_until_next_alarm_macro') %}
{% set next_alarm = states('sensor.next_alarm_day')|lower %}
{% set pickup = 'Next alarm will be ' + next_alarm + ', in ' %}
{% if time == 'Not set, relax' %} Alarm is {{time|lower}}
{% elif (time.split(':')|length - 1) == 0 %}
{{pickup}}
{% set minute = 'minute' if time == '1' else 'minutes' %}
{% if time == '1' %} {{time}} {{minute}} exactly
{% else %} {{time}} {{minute}}
{% endif %}
at {{states('sensor.next_alarm')}}
{% elif (time.split(':')|length - 1) > 1 %}
{{pickup}}
{% set day = 'day' if time.split(':')[0]== '1' else 'days' %}
{% set hour = 'hour' if time.split(':')[1]== '1' else 'hours' %}
{% set minute = 'minute' if time.split(':')[2]== '1' else 'minutes' %}
{%- if time.split(':')[1] == '0' and time.split(':')[2] == '0' -%} {{time.split(':')[0] }} {{day}} exactly
{%- elif time.split(':')[1] == '0' -%} {{time.split(':')[0] }} {{day}} {{time.split(':')[2] }} {{minute}}
{%- elif time.split(':')[2] == '0' -%} {{time.split(':')[0] }} {{day}} and {{time.split(':')[1] }} {{hour}} exactly
{%- else -%} {{time.split(':')[0] }} {{day}}, {{ time.split(':')[1] }} {{hour}} and {{time.split(':')[2] }} {{minute}}
{% endif %}
at {{states('sensor.next_alarm')}}
{% else %}
{{pickup}}
{% set hour = 'hour' if time.split(':')[0]== '1' else 'hours' %}
{% set minute = 'minute' if time.split(':')[1]== '1' else 'minutes' %}
{%- if time.split(':')[0] == '0' -%} {{time.split(':')[1] }} {{minute}}
{%- elif time.split(':')[1] == '0' -%} {{time.split(':')[0] }} {{hour}} exactly
{%- else -%} {{time.split(':')[0] }} {{hour}} and {{time.split(':')[1] }} {{minute}}
{% endif %}
at {{states('sensor.next_alarm')}}
{% endif %}
and next alarm, not yet in macro style, wasn’t that easy to change…
next_alarm:
friendly_name: 'Next alarm'
entity_id:
- sensor.time
- sensor.alarmclock_wd_time
- sensor.alarmclock_we_time
- input_boolean.alarmclock_wd_enabled
- input_boolean.alarmclock_we_enabled
- input_number.alarmclock_wd_hour
- input_number.alarmclock_wd_minute
- input_number.alarmclock_we_hour
- input_number.alarmclock_we_minute
value_template: >
{% set end_on = is_state('input_boolean.alarmclock_we_enabled','on') %}
{% set week_on = is_state('input_boolean.alarmclock_wd_enabled','on') %}
{% set week_time = now().time().strftime('%H:%M') < states('sensor.alarmclock_wd_time') %}
{% set end_time = now().time().strftime('%H:%M') < states('sensor.alarmclock_we_time') %}
{% set week_alarm = states('sensor.alarmclock_wd_time') %}
{% set end_alarm = states('sensor.alarmclock_we_time') %}
{% set now = now().weekday() %}
{% if not end_on and not week_on %} Not set
{% elif now in [0,1,2,3] %}
{% if week_on %} {{week_alarm }}
{% elif end_on %} {{end_alarm}}
{% endif %}
{% elif now == 4 %}
{% if week_on %}
{% if week_time %} {{week_alarm}}
{% else %} {{ end_alarm if end_on else week_alarm}}
{% endif %}
{% else %} {{ end_alarm if end_on}}
{% endif %}
{% elif now == 5 %}
{% if end_on %} {{end_alarm}}
{% else %} {{week_alarm if week_on }}
{% endif %}
{% elif now == 6 %}
{% if end_on and not week_on %}
{{ end_alarm }}
{% elif end_on and week_on %}
{{ end_alarm if end_time else week_alarm }}
{% else %} {{ week_alarm if week_on }}
{% endif %}
{% endif %}
showing as (in full test setup):
I’d do this the following way. You won’t be held to strptime or converting or all this crazy date math.
timestamp for alarm (hide this)
next_alarm_timestamp:
friendly_name: Next alarm timestamp
entity_id:
- sensor.time
- input_boolean.alarmclock_wd_enabled
- input_boolean.alarmclock_we_enabled
- input_number.alarmclock_wd_hour
- input_number.alarmclock_wd_minute
- input_number.alarmclock_we_hour
- input_number.alarmclock_we_minute
value_template: >
{%- macro getalarm(offsetday=0) %}
{%- set day = (now().weekday() + offsetday) % 7 %}
{%- set offset = offsetday * 86400 %}
{%- set fmat = 'd' if day in range(5) else 'e' %}
{%- set hour = 'input_number.alarmclock_w{}_hour'.format(fmat) %}
{%- set minute = 'input_number.alarmclock_w{}_minute'.format(fmat) %}
{%- set alarm = states(hour) | float | multiply(3600) + states(minute) | float | multiply(60) %}
{%- set time = now().hour * 3600 + now().minute * 60 %}
{{ (offset - time) + alarm if offsetday else alarm - time }}
{%- endmacro %}
{%- set weekday = is_state('input_boolean.alarmclock_wd_enabled','on') %}
{%- set weekend = is_state('input_boolean.alarmclock_we_enabled','on') %}
{%- set weekdays = [0,1,2,3,4] %}
{%- set weekends = [5,6] %}
{%- set day = now().weekday() %}
{%- set nextday = day + 1 if day != 6 else 0 %}
{%- if nextday in weekdays %}
{%- if weekday %}
{%- set offsetday = nextday %}
{%- elif weekend %}
{%- set offsetday = weekends[0] %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{%- elif nextday in weekends %}
{%- if weekend %}
{%- set offsetday = nextday %}
{%- elif weekday %}
{%- set offsetday = weekdays[0] %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{% if offsetday != None %}
{% set offset = offsetday-day if offsetday > day else offsetday - day + 7 %}
{% if (day in weekdays and weekday) or (day in weekends and weekend) %}
{% set today_alarm = getalarm() | float %}
{% else %}
{% set today_alarm = -1 %}
{% endif %}
{% set next_alarm = getalarm(offset) | float %}
{% set time = now().replace(second=0).replace(microsecond=0) %}
{% if today_alarm > 0 %}
{{ as_timestamp(time) + today_alarm }}
{% else %}
{{ as_timestamp(time) + next_alarm }}
{% endif %}
{% else %}
0
{% endif %}
next alarm time
next_alarm:
friendly_name: Next Alarm
value_template: "{{ states('sensor.next_alarm_timestamp') | int | timestamp_custom('%H:%M') }}"
count calculation (hide this)
number_of_days_next_alarm_calc:
friendly_name: Next alarm day calc
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% if timestamp %}
{% set time = now().replace(second=0).replace(microsecond=0) %}
{% set midnight = now().replace(hour=0).replace(minute=0).timestamp() + 86400 %}
{% if timestamp > midnight %}
{{ ((timestamp - time.timestamp()) // 86000) | int + 1 }}
{% else %} 0
{% endif %}
{% else %}
-1
{% endif %}
count
number_of_days_next_alarm:
friendly_name: Next alarm day
value_template: >
{% set days = states('sensor.number_of_days_next_alarm_calc') %}
{{ 'Not Set' if days == '-1' else days }}
Time to next alarm
time_until_next_alarm:
friendly_name: Time until next alarm
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% if timestamp %}
{% set delta = timestamp-now().replace(second=0).replace(microsecond=0).timestamp() %}
{%- if delta/3600 < 1 %} {{ delta | timestamp_custom('%-M', False) }}
{%- elif delta/86400 <1 %} {{ delta| timestamp_custom('%-H:%-M', False) }}
{%- else %} {{(delta// 86400) | int }}:{{ delta | timestamp_custom('%-H:%-M', False) }}
{%- endif %}
{% else %}
Not set
{% endif %}
Next alarm
next_alarm_day:
friendly_name: Next alarm day
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% set days = states('sensor.number_of_days_next_alarm_calc') %}
{% set day_dict = {'-1': 'Not Set', '0': 'Today', '1': 'Tomorrow', '2': 'The day after tomorrow,'} %}
{% set date = timestamp | timestamp_custom(' %A, %-d %B') %}
{% if days in day_dict %} {{ day_dict[days] + date}}
{% else %}{{ 'Next' + date }}
{% endif %}
TTS
next_alarm_text:
friendly_name: Next alarm text
entity_id:
- sensor.next_alarm_timestamp
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{%- macro pluralize(value, phrase) %}
{%- set end = 's' if value > 1 else '' %}
{{- '{} {}{}'.format(value, phrase, end) if value > 0 else '' }}
{%- endmacro %}
{% if timestamp %}
{% set time = timestamp - now().timestamp() %}
{% set pickup = 'Next alarm will be ' + states('sensor.next_alarm_day') + ', in ' %}
{%- set minutes = pluralize((time % 3600) // 60, 'minute') %}
{%- set hours = pluralize((time % 86400) // 3600, 'hour') %}
{%- set days = pluralize(time // 86400, 'day') %}
{%- set tlist = [ days, hours, minutes ] | select('!=', '') | list %}
{%- if tlist | length == 0 %}
{%- set phrase = 'less than 1 min' %}
{%- elif tlist | length == 1 %}
{%- set phrase = tlist[0] + ' exactly' %}
{%- elif tlist | length == 2 %}
{%- set phrase = tlist | join(' and ') %}
{%- else %}
{%- set phrase = tlist[:-1] | join(', ') + ', and ' + tlist[-1] %}
{%- endif %}
{{ pickup }}{{ phrase }} at {{ states('sensor.next_alarm') }}
{% else %}
Alarm is not set, relax
{% endif %}
o dear, this will take me some time to consume…
made a dedicated package from your code and restarting as we speak.
Thanks for all your amazing work and effort, this is an enormous lesson in time stamping
just asking without even having tested: this does take into account day = 365, and leap year ? (since we discussed that before…)
well, here they are, they show the exact same outcome:
and there’s something very strange going on in both:
even though both alarms are not enabled, the Next alarm sensor is 01:00, and the Next alarm day state is showing Not Set Thursday, 1 january… This wasn’t there before…
it should be like in the dev-template, Not set for both:
separate post for clarity, and update of the above:
made a mistake, and forgot to rename the new sensors. hence they were somehow duplicated, and showed the exact same outcome for both. Some good, some not so good…
renamed them now, and the spotted error is still there in the new sensors:
Have to dive into this, to see why and how to resolve that. Or maybe you would know at first glimpse?
update
still some work to do, the new sensors are a bit confused for the next alarm day, in different ways:
while they should be:
or, a bit later,
new sensors:
while they should be:
maybe it’s only a matter of adding some of the entity_id’s on which states the sensors should update. I’ll add the same as in my original setup, which was a process of careful entity_id selection indeed.
this sensor should be changed, but I won’t do it because I might be using hard-coded methods…
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% set days = states('sensor.number_of_days_next_alarm_calc') %}
{% set day_dict = {'-1': 'Not Set', '0': 'Today', '1': 'Tomorrow', '2': 'The day after tomorrow,'} %}
{% set date = timestamp | timestamp_custom(' %A, %-d %B') %}
{% if days in day_dict %} {{ day_dict[days] + date}}
{% else %}{{ 'Next' + date }}
{% endif %}
this adds the date, even when days = -1, meaning Not Set. If -1 it shouldn’t add the date to the template, and I would do something like:
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% set days = states('sensor.number_of_days_next_alarm_calc') %}
{% set day_dict = {'-1': 'Not Set', '0': 'Today', '1': 'Tomorrow', '2': 'The day after tomorrow,'} %}
{% set date = timestamp | timestamp_custom(' %A, %-d %B') %}
{% if days == '-1' %} {{day_dict[days]}}
{% elif days in day_dict %} {{ day_dict[days] + date}}
{% else %}{{ 'Next' + date }}
{% endif %}
which is a bit of a hack, because both lines are in day_dict of course, and now the second line might make one think the first line isn’t…
same goes for next_alarm which I changed for now in to:
value_template: >
{% set alarm = states('sensor.next_alarm_timestamp')|int %}
{% if alarm == 0 %} Not set
{% else %} {{ alarm |timestamp_custom('%H:%M') }}
{% endif %}
I’ll use this for now, unless you tell me it should be done differently.
and, so I understand correctly, the date it is falsely showing is the first date since epoch?
another question please, concerning formatting so no real, big deal. But still
Id like the tts sensor to show rounded numbers:
and not the floats it is showing now:
the place in the template to change that would be days, hours and minutes in:
{% if timestamp %}
{% set time = timestamp - now().timestamp() %}
{% set pickup = 'Next alarm will be ' + states('sensor.next_alarm_day_new') + ', in ' %}
{%- set minutes = pluralize((time % 3600) // 60, 'minute') %}
{%- set hours = pluralize((time % 86400) // 3600, 'hour') %}
{%- set days = pluralize(time // 86400, 'day') %}
{%- set tlist = [ days, hours, minutes ] | select('!=', '') | list %}
should I |int these, or round them, and maybe more importantly, whats the best place to do so.
I can use time|int
in each {% set %}, or use ((time % 3600) // 60)|int
also, and that’s more of a ‘issue’ please note the time difference in the minutes of the 2 screenshots. The new sensor are a few minutes Off…
They will all update when the timestamp updates and that updates every minute. They should all update when you change any of the inputs as well, which would be the on/off flags and the alarm times.
The only Issue I see is the wrong alarm time in the TTS, is that correct? I have no idea what time it is where you are so I have no basis to form what is right and what is wrong.
No, ‘-1’ is in the dictionary and sets it to not set. This template is correct.
I just left out, but you can reuse the same code that’s in every other template
next_alarm:
friendly_name: Next Alarm
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% if timestamp %}
{{ timestamp | timestamp_custom('%H:%M') }}
{% else %} Not set
{% endif %}
No, I meant that when -1 is in the dictionary, your template sets the outcome to Not set + date. The date is not needed/wanted there, because, if it is used, is incorrect… so this template needs the dedicated ‘if’ for -1.
of course… missed that. Though, if no alarm is set timestamp (which is an |int) is 0. Why does that not ‘exist’? I mean, 0 is a valid value isn’t it?
added a few triggering entity_id’s and now all seems to work fine. The time mismatch seems to boil down to the last minute to or from the alarm time set. Must check more precisely, but I think the one template calculates starting from 0 alarmtime, and the other starts calculating alarmtime +1, as in the first minute is still 0…
other than that, am I spending my time trying to figure out why/how this is working now, as a major lesson in using timestamps.
the one question left about leap year and day 365? No issues now either?
sharing the full package for reference and help to others interested:
##############################################################################################################
# Alternative package_next_alarm.yaml using timestamp calculation, opposed to earlier day based calculation
# for alarmtime settings and TTS announcements
# major architect @petro, see discussion on:
# https://community.home-assistant.io/t/spot-the-error-nested-template/112113/66?u=mariusthvdb
# package needs original package_alarmclock for input_booleans and input_numbers alarmclock_
# @mariusthvdb 20190603
##############################################################################################################
homeassistant:
customize:
sensor.time_until_next_alarm_new:
templates:
icon_color: >
if (state === 'Not set, relax') return 'grey';
return 'red';
icon: >
if (state === 'Not set, relax') return 'mdi:alarm-off';
return 'mdi:clock-end';
unit_of_measurement: >
if (state ==='Not set, relax') return null;
if (state.split(':').length - 1 === 0) return 'M';
if (state.split(':').length - 1 > 1) return 'D:H:M';
return 'H:M';
sensor.number_of_days_next_alarm_new:
templates:
icon: >
if (state === 'Not set') return 'mdi:alarm-off';
if (state === 1) return 'mdi:numeric-' + state + '-box-outline';
return 'mdi:numeric-' + state + '-box-multiple-outline';
icon_color: >
if (state === 'Not set') return 'grey';
return 'red';
sensor.next_alarm_text_new:
templates:
icon: >
if (state === 'Alarm is not set, relax') return 'mdi:alarm-off';
return 'mdi:alarm';
icon_color: >
if (state === 'Alarm is not set, relax') return 'grey';
return 'red';
# Jinja
# icon_template: >
# {% if is_state('sensor.next_alarm_text','Alarm is not set, relax') %} mdi:alarm-off
# {% else %} mdi:alarm
# {% endif %}
sensor.next_alarm_day_new:
templates:
icon: >
if (state === 'Not set') return 'mdi:alarm-off';
return 'mdi:calendar-clock';
icon_color: >
if (state === 'Not set') return 'grey';
return 'red';
#Jinja
# {% if not is_state('sensor.next_alarm_day','Not set') %} mdi:calendar-clock
# {% else %} mdi:alarm-off
# {% endif %}
sensor.next_alarm_new:
templates:
icon: >
if (state === 'Not set') return 'mdi:alarm-off';
return 'mdi:calendar-clock';
icon_color: >
if (state === 'Not set') return 'grey';
return 'red';
group:
next_alarm_new:
name: Next alarm new
icon: mdi:alarm
control: hidden
entities:
- sensor.next_alarm_text_new
- sensor.time_until_next_alarm_new
- sensor.number_of_days_next_alarm_new
- sensor.next_alarm_day_new
- sensor.next_alarm_new
- sensor.number_of_days_next_alarm_calc
- sensor.next_alarm_timestamp
sensor:
#Timestamp for alarm (hide this) #will do when all is 100%
- platform: template
sensors:
next_alarm_timestamp:
friendly_name: Next alarm timestamp
entity_id:
- sensor.time
- input_boolean.alarmclock_wd_enabled
- input_boolean.alarmclock_we_enabled
- input_number.alarmclock_wd_hour
- input_number.alarmclock_wd_minute
- input_number.alarmclock_we_hour
- input_number.alarmclock_we_minute
value_template: >
{%- macro getalarm(offsetday=0) %}
{%- set day = (now().weekday() + offsetday) % 7 %}
{%- set offset = offsetday * 86400 %}
{%- set fmat = 'd' if day in range(5) else 'e' %}
{%- set hour = 'input_number.alarmclock_w{}_hour'.format(fmat) %}
{%- set minute = 'input_number.alarmclock_w{}_minute'.format(fmat) %}
{%- set alarm = states(hour) | float | multiply(3600) + states(minute) | float | multiply(60) %}
{%- set time = now().hour * 3600 + now().minute * 60 %}
{{ (offset - time) + alarm if offsetday else alarm - time }}
{%- endmacro %}
{%- set weekday = is_state('input_boolean.alarmclock_wd_enabled','on') %}
{%- set weekend = is_state('input_boolean.alarmclock_we_enabled','on') %}
{%- set weekdays = [0,1,2,3,4] %}
{%- set weekends = [5,6] %}
{%- set day = now().weekday() %}
{%- set nextday = day + 1 if day != 6 else 0 %}
{%- if nextday in weekdays %}
{%- if weekday %}
{%- set offsetday = nextday %}
{%- elif weekend %}
{%- set offsetday = weekends[0] %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{%- elif nextday in weekends %}
{%- if weekend %}
{%- set offsetday = nextday %}
{%- elif weekday %}
{%- set offsetday = weekdays[0] %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{%- else %}
{%- set offsetday = None %}
{%- endif %}
{% if offsetday != None %}
{% set offset = offsetday-day if offsetday > day else offsetday - day + 7 %}
{% if (day in weekdays and weekday) or (day in weekends and weekend) %}
{% set today_alarm = getalarm() | float %}
{% else %}
{% set today_alarm = -1 %}
{% endif %}
{% set next_alarm = getalarm(offset) | float %}
{% set time = now().replace(second=0).replace(microsecond=0) %}
{% if today_alarm > 0 %}
{{ as_timestamp(time) + today_alarm }}
{% else %}
{{ as_timestamp(time) + next_alarm }}
{% endif %}
{% else %}
0
{% endif %}
#Next alarm time
next_alarm_new:
friendly_name: Next Alarm new
entity_id:
- sensor.next_alarm_timestamp
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% if timestamp %}
{{ timestamp | timestamp_custom('%H:%M') }}
{% else %} Not set
{% endif %}
# value_template: >
# {% set alarm = states('sensor.next_alarm_timestamp')|int %}
# {% if alarm == 0 %} Not set
# {% else %} {{ alarm |timestamp_custom('%H:%M') }}
# {% endif %}
#Count calculation (hide this)
number_of_days_next_alarm_calc:
friendly_name: Next alarm day calc
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% if timestamp %}
{% set time = now().replace(second=0).replace(microsecond=0) %}
{% set midnight = now().replace(hour=0).replace(minute=0).timestamp() + 86400 %}
{% if timestamp > midnight %}
{{ ((timestamp - time.timestamp()) // 86000) | int + 1 }}
{% else %} 0
{% endif %}
{% else %}
-1
{% endif %}
#Count days
number_of_days_next_alarm_new:
friendly_name: Days to next alarm new
entity_id:
- sensor.number_of_days_next_alarm_calc
value_template: >
{% set days = states('sensor.number_of_days_next_alarm_calc') %}
{{ 'Not set' if days == '-1' else days }}
#Time to next alarm
time_until_next_alarm_new:
friendly_name: Time until next alarm new
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% if timestamp %}
{% set delta = timestamp-now().replace(second=0).replace(microsecond=0).timestamp() %}
{%- if delta/3600 < 1 %} {{ delta | timestamp_custom('%-M', False) }}
{%- elif delta/86400 <1 %} {{ delta| timestamp_custom('%-H:%-M', False) }}
{%- else %} {{(delta// 86400) | int }}:{{ delta | timestamp_custom('%-H:%-M', False) }}
{%- endif %}
{% else %}
Not set, relax
{% endif %}
#Next alarm
next_alarm_day_new:
friendly_name: Next alarm day new
entity_id:
- sensor.next_alarm_timestamp
- sensor.number_of_days_next_alarm_new
- input_boolean.alarmclock_wd_enabled
- input_boolean.alarmclock_we_enabled
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% set days = states('sensor.number_of_days_next_alarm_calc') %}
{% set day_dict = {'0': 'Today,', '1': 'Tomorrow,', '2': 'The day after tomorrow,'} %}
{% set date = timestamp | timestamp_custom(' %A, %-d %B') %}
{% if days == '-1' %} Not set
{% elif days in day_dict %} {{ day_dict[days] + date}}
{% else %}{{ 'Next' + date }}
{% endif %}
# value_template: >
# {% set timestamp = states('sensor.next_alarm_timestamp') | int %}
# {% set days = states('sensor.number_of_days_next_alarm_calc') %}
# {% set day_dict = {'-1': 'Not set', '0': 'Today,', '1': 'Tomorrow,', '2': 'The day after tomorrow,'} %}
# {% set date = timestamp | timestamp_custom(' %A, %-d %B') %}
# {% if days == '-1' %} {{day_dict[days]}}
# {% elif days in day_dict %} {{ day_dict[days] + date}}
# {% else %}{{ 'Next' + date }}
# {% endif %}
#TTS
next_alarm_text_new:
friendly_name: Next alarm text new
entity_id:
- sensor.next_alarm_timestamp
- sensor.next_alarm_new
- sensor.next_alarm_day_new
value_template: >
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{%- macro pluralize(value, phrase) %}
{%- set end = 's' if value > 1 else '' %}
{{- '{} {}{}'.format(value, phrase, end) if value > 0 else '' }}
{%- endmacro %}
{% if timestamp %}
{% set time = timestamp - now().timestamp() %}
{% set pickup = 'Next alarm will be ' + states('sensor.next_alarm_day_new') + ', in ' %}
{%- set minutes = pluralize((time|int % 3600) // 60, 'minute') %}
{%- set hours = pluralize((time|int % 86400) // 3600, 'hour') %}
{%- set days = pluralize(time|int // 86400, 'day') %}
{%- set tlist = [ days, hours, minutes ] | select('!=', '') | list %}
{%- if tlist | length == 0 %}
{%- set phrase = 'less than 1 min' %}
{%- elif tlist | length == 1 %}
{%- set phrase = tlist[0] + ' exactly' %}
{%- elif tlist | length == 2 %}
{%- set phrase = tlist | join(' and ') %}
{%- else %}
{%- set phrase = tlist[:-1] | join(', ') + ', and ' + tlist[-1] %}
{%- endif %}
{{ pickup }}{{ phrase }} at {{ states('sensor.next_alarm_new') }}
{% else %}
Alarm is not set, relax
{% endif %}
Ah yeah, that is the case.
probably just change it to this then
{% set timestamp = states('sensor.next_alarm_timestamp') | int %}
{% set days = states('sensor.number_of_days_next_alarm_calc') %}
{% set day_dict = {'0': 'Today', '1': 'Tomorrow', '2': 'The day after tomorrow,'} %}
{% set date = timestamp | timestamp_custom(' %A, %-d %B') %}
{% if days == '-1' %} Not set
{% elif days in day_dict %} {{ day_dict[days] + date}}
{% else %}{{ 'Next' + date }}
{% endif %}
Don’t need to account for it at all because this uses timestamps. It’s built in.
yes thats less hacky thanks.
about the -1 you set: do I understand it correctly that is an arbitrary value? Simply used to signal an unset or Not set state? I used Not set in the original templates, which works fine, but needs some extra care when ‘calculating’ with a non-number. This mitigates that?
yep
I don’t think it helps with anything other than making it all numbers. -1 is a pretty good indication that things aren’t the way you want them. You could use a string but that would convert over to 0 when you use the int filter. So just set it as a negative number and then you can convert to an in and it won’t interfere with zero.