Spot the error... nested template

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:

46

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… :wink:

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):

1 Like

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 %}
1 Like

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 :wink:

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 :wink:

Id like the tts sensor to show rounded numbers:

35

and not the floats it is showing now:
04

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 %}
1 Like

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 %}
1 Like

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.

1 Like

yes thats less hacky :slight_smile: 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?

1 Like

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.

1 Like