Spot the error... nested template

clock seems alright…

        {% macro getalarm(offsetday=0) %}
        {% set day = (now().weekday() + offsetday) % 7 %}
        {% set offset = offsetday * 86400 %}
        {% set fmat = 'd' if day in [0,1,2,3,4] and states('input_boolean.alarmclock_wd_enabled') == 'on' 
                 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 %}

        {% if is_state('input_boolean.alarmclock_wd_enabled','on') or
              is_state('input_boolean.alarmclock_we_enabled','on') %}
        {% set in_days = states('sensor.number_of_days_next_alarm') %}
        {% set today_alarm = getalarm() | float %}
        {% set next_alarm = getalarm(in_days|int) | float %}
        {% set alarm = next_alarm if in_days|int > 0 else today_alarm %}
        {% set days = alarm // 86400 %}

          {% if days %} {{days|int}}:{{ next_alarm | timestamp_custom('%-H:%-M', False) }}
          {% else %} {{ alarm | timestamp_custom('%-H:%-M', False) }}
          {% endif %}

        {% elif is_state('input_boolean.alarmclock_wd_enabled','off') and 
                is_state('input_boolean.alarmclock_we_enabled','off') %} Not set, relax
        {% endif %}

this was your original:

        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 today_alarm = getalarm() | float %}
          {% set tomorrow_alarm = getalarm(1) | float %}
          {% set alarm = tomorrow_alarm if today_alarm < 0 else today_alarm %}
          {% set days = alarm // 86400 %}
          {% if days %}
            {{ alarm | timestamp_custom('%-d day %H:%M', False) }}
          {% else %}
            {{ alarm | timestamp_custom('%H:%M', False) }}
          {% endif %}

it wouldn’t go over to the next weekend either if weekend alarm was set, and current day being a weekday:

how it should be:

macro original:

reading the weekday alarm, while it isn’t set…

Why did you add states('input_boolean.alarmclock_wd_enabled') == 'on' into the macro again? That cannot be there. I removed it in every post reply to you… Keep it out. The macro is agnostic to your on off flags, as it should be. The on off flags should be handled outside the macro.

paste this into your template editor

{%- macro getalarm(offsetday=0) %}
{%- set day = (now().weekday() + offsetday) % 7 %}
{%- set offset = offsetday * 86400 %}
{%- set fmat = 'd' if day in [0,1,2,3,4] 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 %}
{{ getalarm() | int }}
{{ getalarm(1) | int }}

Tell me what your input_numbers are set to and show me the results.

I did that because it otherwise sets the alarm to weekday alarm, even if that is not enabled.

01

whats your time right now?

01

my utc offset:

33

According to those timestamps returned from the macro. Everything is correct. Use that macro in the post with all that logic and you’ll get the correct time.

literally copy and paste this:

ok i will do so, and see what happens in a week (have to meet the weekend again to see if it checks all options correctly).

this did happen with the original:

wonder why that would have been.

but please see this:

and your macro:


exact copy. showing time to next alarm on a weekday, while weekday is off.
thanks again for reassuring, and your explanation.

Both on using 9:30 for weekday and 10 for weekend:

your weekday alarm is set to earlier than current time. If I do so, the template checks alright. Setting it to later today, makes it behave incorrectly. While it shouldn’t be used at all, since weekday alarm is set off

Ah ok, minor issue then.

    {%- 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 current_day = now().weekday() %}

    {% if weekday and not weekend %}
      {% set offset = 7-current_day if current_day in range(4,7) else 1 %}
    {% elif not weekday and weekend %}
      {% if current_day in range(4) %}
        {% set offset = 5 - current_day %}
      {% else %}
        {% set offset = 6 if current_day == 6 else 1 %}
      {% endif %}
    {% elif weekday and weekend %}
       {% set offset = 1 %}
    {% else %}
       {% set offset = None %}
    {% endif %}
    
    {% if offset != None %}
      {% set today_alarm = getalarm() | float %}
      {% set next_alarm = getalarm(offset) | float %}
      {% set alarm = next_alarm if today_alarm < 0 else today_alarm %}
      {% if offset > 1 %}
        {{ next_alarm // 86400 }}:{{ next_alarm | timestamp_custom('%-H:%-M', False) }}
      {% else %}
        {{ alarm | timestamp_custom('%-H:%-M', False) }}
      {% endif %}
    {% else %}
      None 
    {% endif %}

yes, that works now… tY! Cool.
Minor issues can be nasty… :wink:

hope it gets around the full weekend also.

btw @petro please allow me this final question:

why do we need to do
{% if days %} {{(next_alarm // 86400) |int }}:{{ next_alarm | timestamp_custom('%-H:%-M', False) }}
and can’t we use
{% if days %} {{ next_alarm | timestamp_custom('%-d:%-H:%-M', False) }}

the %d is always +1

because %d is the day of the month, not the day of the week or a count.

yes, realize that, but it is what you had in your first iteration, counting the days if days exist. I changed it then, because I knew no other way to get the days counted using the days as a separate template.
Still doesn’t really explain why it is always one up, to put more precisely, I still don’t grasp that …

if % H and %M are valid options, isn’t there an option for days that could be used, other than specifying it as an extra template like now?

the only option is %d, you claimed it wasn’t working for you and always outputting one. Which is fine, no big deal. So I changed it to this new method which is int division.

I do not know why it was always one for you, it should actually work and increment the days. This way is a work-around and it will work 100% of the time.

right now it is indeed. tried the original again, but it is off, so won’t look back at it again, no use when having this at hand!

have another request though.

I am still using this bit of python in my system monitor (summary):

##########################################################################################
# Alarm clock
# /config/packages/package_alarmclock.yaml
##########################################################################################

alarms_prefix = ['alarmclock_wd', 'alarmclock_we']
alarms_wfilter = [range(1,6), range(6,8)]
alarms_desc = ''
mydict = {'hidden':hidden}

for entity_id, filter in zip(alarms_prefix, alarms_wfilter):
    state = hass.states.get('input_boolean.{}_enabled'.format(entity_id))
    if state:
        if state.state is 'on' and datetime.datetime.now().isoweekday() in filter:
            state = hass.states.get('sensor.{}_time'.format(entity_id))
            alarms_desc = '{}{}, '.format(alarms_desc, state.state)

if (alarms_desc == ''):
    mydict['entity_picture'] = '/local/badges/alarm_off.png'
    mydict['friendly_name'] =  'Relax'
    mydict['unit_of_measurement'] = 'Off'
    mydict['theme'] = 'grey_badge'
    mydict['love'] = 'Alarm clock is not set, relax'
    alarms_desc = '%- Alarm clock is not set, relax'

else:
    mydict['entity_picture'] = '/local/badges/alarm.png'
    mydict['friendly_name'] = alarms_desc[:-2]
    mydict['unit_of_measurement'] = 'On'
    mydict['theme'] = 'orange_badge'
    mydict['love'] = 'Alarm clock set at ' + alarms_desc[:-2]
    alarms_desc = '/- Alarm clock set at ' + alarms_desc[:-2]

hass.states.set('sensor.alarms_badge', '', mydict)

this only sets the alarm to be ‘on’ and displays the state, when on weekday and weekday alarm is on, or on weekend day and weekend alarm is on.

Would it be possible to have it behave like the sensor.time_until_next_alarm: have it show a weekend alarm on a weekday too (and vice versa), preferably with the alarm day in the alarms_desc?

and, just to give full feedback and thanks, this is what I am using now and things seem to be working perfectly.
Did have to create an automation to update all sensors, immediately and not await sensor.time. I had the commented entity_ids in first, but they wouldn’t make a difference for the updating of the sensors. Automation does it better. Now only have the entities listed which are used in the template, and even they might not be necessary , safe the sensor.time.

      number_of_days_next_alarm:
        friendly_name: Days to 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: >
          {% if is_state('input_boolean.alarmclock_wd_enabled','on') %}
            {% if now().weekday() in [0,1,2,3] %}
              {% if now().time().strftime('%H:%M') < states('sensor.alarmclock_wd_time') %} 0
              {% else %} 1
              {% endif %}

            {% elif now().weekday() == 4 %}
              {% if is_state('input_boolean.alarmclock_we_enabled','on') %} 
                {% if now().time().strftime('%H:%M') < states('sensor.alarmclock_wd_time') %} 0
                {% else %} 1
                {% endif %}
              {% else %} 3
              {% endif %}

            {% elif now().weekday() == 5 %}
              {% if is_state('input_boolean.alarmclock_we_enabled','on') %}
                {% if now().time().strftime('%H:%M') < states('sensor.alarmclock_we_time') %} 0
                {% else %} 1
                {% endif %}
              {% else %} 2
              {% endif %}

            {% elif now().weekday() == 6 %}
              {% if is_state('input_boolean.alarmclock_we_enabled','on') %}
                {% if now().time().strftime('%H:%M') < states('sensor.alarmclock_we_time') %} 0
                {% else %} 1
                {% endif %}
              {% else %} 1
              {% endif %}
            {% endif %}

          {% elif is_state('input_boolean.alarmclock_we_enabled','on') %}
              {% if now().weekday() in [0,1,2,3] %} {{5 - now().weekday()}}
              {% elif now().weekday() in [4] %} 1

              {% elif now().weekday() in [5] %}
                {% if now().time().strftime('%H:%M') < states('sensor.alarmclock_we_time') %} 0
                {% else %} 1
                {% endif %}

              {% elif now().weekday() == 6 %}
                {% if now().time().strftime('%H:%M') < states('sensor.alarmclock_we_time') %} 0
                {% else %} 6
                {% endif %}
              {% endif %}

          {% else %} Not set
          {% endif %}

      next_alarm_day:
#        icon_template: >
#          {% if not is_state('sensor.next_alarm_day','Not set') %} mdi:calendar-clock
#          {% else %} mdi:alarm-off
#          {% endif %}
        friendly_name: Next alarm day
        entity_id:
          - sensor.time
          - sensor.number_of_days_next_alarm
          - sensor.next_alarm_daytype
#          - sensor.time_until_next_alarm
#          - 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 offset = states('sensor.number_of_days_next_alarm')|int %}

          {% if is_state('sensor.next_alarm_daytype','Today,')%}
            {{states('sensor.next_alarm_daytype')}} {{ as_timestamp(now().replace(day=now().day)) | timestamp_custom('%A') }}
          {% elif states('sensor.next_alarm_daytype') in ['Tomorrow,', 'The day after tomorrow,'] %}
            {{states('sensor.next_alarm_daytype')}} {{ as_timestamp(now().replace(day=now().day + offset)) | timestamp_custom('%A')}}

          {% elif is_state('sensor.number_of_days_next_alarm','Not set') and is_state('sensor.next_alarm_daytype','Not set') %}
            Not set
          {% else %}
            {{ as_timestamp(now().replace(day=now().day + offset)) | timestamp_custom('%A')}}
          {% endif %}

      next_alarm_daytype:
        friendly_name: Next alarm daytype
        entity_id:
          - sensor.time
          - sensor.time_until_next_alarm
          - 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 days = states('sensor.number_of_days_next_alarm') %}
          {% if is_state('input_boolean.alarmclock_wd_enabled','off') and
                is_state('input_boolean.alarmclock_we_enabled','off') %} Not set
          {% elif days == '0' %} Today,
          {% elif days == '1' %} Tomorrow,
          {% elif days == '2' %} The day after tomorrow,
          {% else %} More than 2: ({{days}}) days
          {% endif %}

      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 weekday = is_state('input_boolean.alarmclock_wd_enabled','on') %}
          {% set weekend = is_state('input_boolean.alarmclock_we_enabled','on') %}
          {% set current_day = now().weekday() %}

          {% if weekday and not weekend %}
            {% set offset = 7-current_day if current_day in range(4,7) else 1 %}
          {% elif not weekday and weekend %}
            {% if current_day in range(4) %}
              {% set offset = 5 - current_day %}
            {% else %}
              {% set offset = 6 if current_day == 6 else 1 %}
            {% endif %}
          {% elif weekday and weekend %}
             {% set offset = 1 %}
          {% else %}
             {% set offset = None %}
          {% endif %}

          {% if offset != None %}
            {% set today_alarm = getalarm() | float %}
            {% set next_alarm = getalarm(offset) | float %}
            {% set alarm = next_alarm if today_alarm < 0 else today_alarm %}
            {% if offset > 1 %}
              {{ (next_alarm // 86400) |int }}:{{ next_alarm | timestamp_custom('%-H:%-M', False) }}
            {% else %}
              {{ alarm | timestamp_custom('%-H:%-M', False) }}
            {% endif %}
          {% else %}
            Not set, relax
          {% endif %}

      next_alarm_text:
        friendly_name: Next alarm text
        entity_id:
          - sensor.time_until_next_alarm
          - sensor.next_alarm_day
#          - sensor.number_of_days_next_alarm
#          - sensor.next_alarm_daytype

        value_template: >
          {% set time = states('sensor.time_until_next_alarm') %}
          {% 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) > 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' %} {{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%}

          {% 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%}
          {% endif %}

and automation:

  - alias: 'Update next alarm'
    id: 'Update next alarm'
    initial_state: 'on'
    trigger:
      platform: state
      entity_id:
        - sensor.time_until_next_alarm
        - 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
    action:
      service: homeassistant.update_entity
      entity_id:
        - sensor.next_alarm_day
        - sensor.next_alarm_daytype
        - sensor.number_of_days_next_alarm

you made me twist my mind here. After having reread this many times, I must conclude I did not…?
I had only added the states('input_boolean.alarmclock_wd_enabled') == 'on' condition outside the macro.

Its gone there now too, and the latest ‘minor issue’ then out makes it behave as it should. Very nice!

I have btw changed/compressed the separate day counter (I need for showing the days in a separate sensor, and for calculating some wordings for TTS) to this now:

      number_of_days_next_alarm_alt:
        friendly_name: Days to next alarm alt
        entity_id: sensor.time
        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 now = now().weekday() %}

          {% if not end_on and not week_on %} Not set 

          {% elif now in [0,1,2,3] %}
            {% if week_on %} {{'0' if week_time else '1' }}
            {% elif end_on %} {{5 - now}}
            {% endif %}

          {% elif now == 4 %}
            {% if week_on %}
              {% if week_time %} 0
              {% else %} {{ '1' if end_on else '3'}}
              {% endif %}
            {% else %} {{ '1' if end_on}}
            {% endif %}

          {% elif now == 5 %}
            {% if end_on %} {{'0' if end_time else '1'}}
            {% else %} 
              {% if week_on %}2
              {% endif %}
            {% endif %} 

          {% elif now == 6 %}
            {% if end_on %} {{'0' if end_time else '6'}}
            {% else %} 
              {% if week_on %}1
              {% endif %}
            {% endif %}

          {% endif %}

Ive tried to use the bit in the bigger template sensor

          {% set weekday = is_state('input_boolean.alarmclock_wd_enabled','on') %}
          {% set weekend = is_state('input_boolean.alarmclock_we_enabled','on') %}
          {% set current_day = now().weekday() %}

          {% if weekday and not weekend %}
            {% set offset = 7-current_day if current_day in range(4,7) else 1 %}
          {% elif not weekday and weekend %}
            {% if current_day in range(4) %}
              {% set offset = 5 - current_day %}
            {% else %}
              {% set offset = 6 if current_day == 6 else 1 %}
            {% endif %}
          {% elif weekday and weekend %}
             {% set offset = 1 %}
          {% else %}
             {% set offset = None %}
          {% endif %}

but that won’t account for today, or 0, since it needs the extra alarmtime to current time comparison.

You must have added it accidentally? Everytime you posted the macro in this post and the other post you had the check inside it

I’m not sure what you are trying to do, that doesn’t account for today ever. That will only account for tomorrow (or whenever the next alarm is). The remaing portion of the if statement in the template accounted for today, but for some reason you keep omitting pieces of the templates! Why aren’t you using the one that has proven to work? The all in one template that just tells you the next alarm?

    {%- 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 current_day = now().weekday() %}

    {% if weekday and not weekend %}
      {% set offset = 7-current_day if current_day in range(4,7) else 1 %}
    {% elif not weekday and weekend %}
      {% if current_day in range(4) %}
        {% set offset = 5 - current_day %}
      {% else %}
        {% set offset = 6 if current_day == 6 else 1 %}
      {% endif %}
    {% elif weekday and weekend %}
       {% set offset = 1 %}
    {% else %}
       {% set offset = None %}
    {% endif %}
    
    {% if offset != None %}
      {% set today_alarm = getalarm() | float %}
      {% set next_alarm = getalarm(offset) | float %}
      {% set alarm = next_alarm if today_alarm < 0 else today_alarm %}
      {% if offset > 1 %}
        {{ next_alarm // 86400 }}:{{ next_alarm | timestamp_custom('%-H:%-M', False) }}
      {% else %}
        {{ alarm | timestamp_custom('%-H:%-M', False) }}
      {% endif %}
    {% else %}
      None 
    {% endif %}

Ah yes! you’re right. I completely missed that… my apologies…
not using it anymore, that was a c&p from my temporary testing package… so sorry for the confusion.