Need template to calculate time between alarm and now in weekdays and weekend

IM really sorry, but I must have missed that, otherwise I wouldn’t have asked…
changed set up, but can’t test right now, because my input_number won’t allow to go far enough now.
Thanks! and I’ll report back.

SInce you’re into it now… (I hope):

what would happen if the weekend alarm was switched off, and the next alarm would ship over the weekend…

A test for input_boolean.alarmclock_we_enabled == ‘off’ is available:

would be way cool if at all possible.

14

44

50

something must be incorrect, because it shows 2 days, when in fact it should be 1, going from a weekend day (sunday) to weekday, first monday:

strange thing is, it doesn’t really matter what is entered in the {% set days = alarm // 86400 %} line. each number or quoted string causes days to exist and display day in the output?

That’s odd, it increments for me. Unfortunately I won’t be able to debug because I’m not at home

ok, cool, no worries. I will be patient :wink:

it does however check with my earlier finding that when adding &d it always showed 1 for day, even when the alarm was set to the same day.
It now adds 1 correctly, so always shows 2… if the alarm is set to more than 24 hours from now.

please let me ask a related question.
I’ve changed the output of the sensor to:

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

so I can use that for my tts.google_say script:

  say_sleep_left:
    alias: Say Sleep Left
    sequence:
      - service: tts.google_say
        data_template:
          entity_id:
            - >
              {{states('sensor.intercom')}}
            - >
              {{states('sensor.sleep_radio')}}
          message: >
            Good {{states('sensor.day_phase')|lower}},
            {% set time = states('sensor.time_until_next_alarm') %}
            {% if time == 'Not set, relax' %} alarm is {{time|lower}}.
            
            {% elif time|length > 5%}
               next alarm will be in 
                 {% if time.split(':')[1] == '0' %} {{time.split(':')[0] }} days {{time.split(':')[2] }} minutes.
                 {% elif time.split(':')[2] == '0' %} {{time.split(':')[0] }} days and {{time.split(':')[1] }} hours exactly.
                 {% else %} {{time.split(':')[0] }} days, {{ time.split(':')[1] }} and {{time.split(':')[2] }} minutes.
                 {% endif%}
                 
            {% else %}
               next alarm will be in 
                 {% if time.split(':')[0] == '0' %} {{time.split(':')[1] }} minutes.
                 {% elif time.split(':')[1] == '0' %} {{time.split(':')[0] }} hours exactly.
                 {% else %} {{time.split(':')[0] }} hours and {{time.split(':')[1] }} minutes.
                 {% endif%}
            {% endif %}

            {% set phase = states('sensor.day_phase') %}
            {% if phase in ['Morning','Day']  %} Have a nice {{phase|lower}}.
            {% elif phase == 'Evening' %} Enjoy your {{phase|lower}}
            {% else %} Sleep well.
            {% endif %}

this does work but might not be as intelligent as it could be (since it is hard coded), using a |length filter in the template to decide which message is spoken.
Would you change that?

Also, it now shows:

35

so I changed the customize template for the unit_of_measurement in to

    sensor.time_until_next_alarm:
      templates:
        icon: >
          if (entities['input_boolean.alarmclock_wd_enabled'].state === 'on' || 
              entities['input_boolean.alarmclock_we_enabled'].state === 'on') return 'mdi:clock-end';
          return 'mdi:alarm-off';
        unit_of_measurement: >
          if (state ==='Not set, relax') return null;
          if (state.length > 5) return 'D:H:M';
          return 'H:M';

building on this, and this might be a bit of a challenge, I was wondering it it could be perfected and take into account the booleans for enabling alarm on weekdays and weekend days:
input_boolean.alarmclock_wd_enabled
input_boolean.alarmclock_we_enabled

scenario:

  • if both ‘on’, the current template would be perfect (only check the strange day count of 2)
  • if both 'off, simply state ‘Not set, relax’
  • if only weekend enabled, have it skip the weekdays and on sunday add the 6 days to the next saterday
  • if only weekday enabled, have it skip the weekend days and on friday add the 3 days to jump to monday

carefully expanding like this:

        value_template: >
          {% if is_state('input_boolean.alarmclock_wd_enabled','on') or
                is_state('input_boolean.alarmclock_we_enabled','on') %}
            {%- 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:%-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 %}

but the big challenge lies ahead…(still using the ‘or’ in the first template, which has to be changed obviously )

HI @petro,

not to be harassing you, but did you ever get a chance to have a look? Hope you can find a moment, because I really need you…
would real really be cool if the template could take into account the input_booleans, so it can show the correct time at all times
(right now it calculates the weekend time, while boolean is set to off, and it should ideally jump to the weekday time, adding a day…

the other thing was the current template is always showing 2 for days when it in fact is 1. have a look, ive added {{days}} and {{alarm}} in the if section of the template:

the numbers seem to be correct, but days is incorrectly calculated?
88088/86400 should lead to 1 and not 2. how can this be happening in the forst place.

Of course %d is the weekday as a decimal number, and we don’t want the weekday here, but the number of days. Wouldn’t this then be the correct was of calculating that:

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

Yes, I’d probably make a sensor that just contains the timestamp instead of the parsed values. Then use that for messages and make a separate sensor that displays it for the interface.

This would require a total rework of the current sensors you have. It’s possible, but wouldn’t be easy in Jinja.

a ok.
Will have a look if I can conjure that up.
in the mean time have been looking at this template made earlier in coalition between @klogg and yourself, to which I added the getalarm(). It seems to work…:

            {%- 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 up_time = getalarm(1) | float %}
          {% set minutes = (up_time // 60) | int %}
          {% set hours = (minutes // 60) %}
          {% set days = (hours // 24) %}
          {% set weeks = (days // 7) %}
          {% set minutes = (minutes % 60) %}
          {% set hours = (hours % 24) %}
          {% set days = (days % 7) %}

          {% macro phrase(value, name) %}
                    {%- set value = value | int %}
                    {%- set end = 's' if value > 1 else '' %}
                    {{- '{} {}{}'.format(value, name, end) if value | int > 0 else '' }}
          {%- endmacro %}
              
          {% set text = [ phrase(weeks, 'week'), phrase(days, 'day'), phrase(hours, 'hr'), 
                          phrase(minutes, 'min') ] | select('!=','') | list | join(', ') %}
          {% set last_comma = text.rfind(',') %}
          {% set text = text[:last_comma] + ' and' + text[last_comma + 1:] %}

          {{ text }}

maybe that could also be used to check for my endgoal, jumping over the weekdays/weekend days if necessary?

would you understand why this is happening:

would this be in any way useful, at least it counts the number of days until the next alarm (provided it isn’t within the next 24 hours)

number_of_days:
  value_template: >
    {% if is_state('input_boolean.alarmclock_wd_enabled','on') %}
      {% if now().weekday() in [0,1,2,3,6] %} 1
      {% elif now().weekday() == 4 %}
        {% if is_state('input_boolean.alarmclock_we_enabled','on') %} 1
        {% else %} 3
        {% endif %}
      {% elif now().weekday()  == 5 %}
        {% if is_state('input_boolean.alarmclock_we_enabled','on') %} 1
        {% else %} 2
        {% 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,5] %} 1
        {% elif now().weekday() == 6 %} 6
        {% endif %}
    {% else %} Not set
    {% endif %}

Not sure if this is the optimal approach, but would have thought this to be more efficient than checking per day. Maybe this could be used for the offset in your template?

this seems to work alright:

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

so my setup now is:

            {% macro getalarm(offsetday=0) %}
            {% set day = (now().weekday() + offsetday) % 7 %}
            {% set offset = offsetday * 86400 %}
            {% set fmat = 'd' if day in range(5) 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 today_alarm < 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 %}

of course this also still uses my little ‘hack’ using days|int since the %-d still is always 1 day off… wish I could spot the error there…

–edit—

timing issue had disappeared using the extra condition, and now working perfectly.

(2things I know to be incorrect, but not bothering now: day count is not in hours but counting to the day the alarm is set . So if tomorrow, day count is 1 even if less than 24 hours. Also, the original %-d is still off… )

If you’re going to do that, what’s the point of the day sensor? Just build it into it the next alarm timestamp/time.

    {%- 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 %}
        {{ alarm // 86400 }}:{{ alarm | timestamp_custom('%-H:%-M', False) }}
      {% else %}
        {{ alarm | timestamp_custom('%-H:%-M', False) }}
      {% endif %}
    {% else %}
      None 
    {% endif %}

Thanks!
Made the day sensor to see what the algorithm needed to be, and to hopefully incorporate that later on into the next alarm sensor… as you did now :wink:
Had some quirks with the adapted next alarm sensor, so changed it to hold another condition:

{% set fmat = 'd' if day in range(5) and states('input_boolean.alarmclock_wd_enabled') == 'on' 
                     else 'e' %}

without this, the sensor will still show the weekdays alarmtime, even when only weekend alarm was enabled.
of course your newest template is a huge step-up so thank you very much! Will test it immediately after my restart has finished.

side question:

why is

next_alarm_day:
        friendly_name: Next alarm day
        value_template:
          {% set day = now().weekday()|int + states('sensor.next_alarm_offset')|int  %}
      {{(day)| timestamp_custom('%A')}}

thursday and not saturday… {{day}} is 5, so I guess it makes that the 5th day of the week, but I want it to be saturday…

today (wednesday) + 3 is saturday.

I know it is the locales weekday, but can’t find the correct timestamp?

monday is 0
tuesday is 1
wednesday is 2
thursday is 3
friday is 4
saturday is 5
sunday is 6

so saturday is 5.

The reason it’s not correct in your timestamp_custom is because you don’t have a timestamp. You have an integer. Timestamps are an integer, but its based on a time in seconds from jan 1st 1970. You’re essentially asking for the day of the week that jan 1st 1970 was.

so I thought yes, and yes, I have been struggling with the addition… but even If I use |timestamp_custom(‘%A’) it remains Thursday… while it should be 5, so saturday. :confused:

Did you read my last post? It explains why it stays thursday

You’re feeding the timestamp function 5. So you are requesting the day of the week 5 seconds after midnight on january 1st 1970.

no, sure I read your post, ditched the |int and trying with as_timestamp and timestamp_custom('%a').

no result just yet though.

{% set day = as_timestamp(now().weekday())  +   as_timestamp(states('sensor.next_alarm_offset')) %}
{{as_timestamp(day)| timestamp_custom('%a') }}
{{ as_timestamp(now().replace(day=now().day+3)) | timestamp_custom('%A') }}

ok thanks,
using the offset as a variable that would make this then:

{% set offset = states('sensor.next_alarm_offset') %}
{{ as_timestamp(now().replace(day=now().day+offset|int)) | timestamp_custom('%A') }}

or

{% set offset = states('sensor.next_alarm_offset')|int %}
{{ as_timestamp(now().replace(day=now().day+offset)) | timestamp_custom('%A') }}

otherwise the addition isn’t made

Yeah, I’d do the int on the offset line, not the output line.

1 Like

cool! been a grand help, as always!

now only add a condition to not have it add the offset if alarmtime is on this day…pff seems to never be easy …

how can I add the now() time to the time next alarm, to see if they are <24:00 meaning still this day?

I have

now().time().strftime('%H:%M')
states('sensor.time_until_next_alarm')


or

I have a related sensor:

     now_master_bedroom_schedule_day:
        friendly_name: 'Now Master bedroom schedule day'
        entity_id: sensor.time
        value_template: >
          {{now().time().strftime('%H:%M') == states('sensor.master_bedroom_schedule_day')}}

which works just fine…
had a go with timestamps (setting it to the time_until_next_alarm) but it either shows nothing, or this outcome (see screenshots)

another way calculating this woud be to see if alarmtime< now ( in which case it would have to be next day…

seems to be simple but is it correct?

06
17

use that like:

      next_alarm_today:
        friendly_name: Next alarm today
        value_template: >
          {% set alarm = states('sensor.alarmclock_wd_time') if now().weekday() in range(4) 
                    else states('sensor.alarmclock_we_time') %}
          {% set now = now().time().strftime('%H:%M')%}

          {% if alarm < now %} Tomorrow
          {% elif alarm == 'Not set' %} Not set
          {% else %} Today
          {% endif %}

but I’d need it to use the time_until_next_alarm, to have it take all logic in to account of the weekdays and weekend days…

so, hopefully (need to see what will happen around the weekend) made it with another(…) sensor using the above:

      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
        value_template: >
          {% set offset = states('sensor.next_alarm_offset')|int %}
          {% if not is_state('sensor.next_alarm_offset','Not set') and
                not is_state('sensor.next_alarm_today','Not set') %}
            {% if is_state('sensor.next_alarm_today','Today')%}
             {{states('sensor.next_alarm_today')}}, {{ as_timestamp(now().replace(day=now().day)) | timestamp_custom('%A')|lower }}
            {% else %}
            {{states('sensor.next_alarm_today')}}, {{ as_timestamp(now().replace(day=now().day + offset)) | timestamp_custom('%A')|lower }}
            {% endif %}
          {% elif not is_state('sensor.next_alarm_offset','Not set') %}
            {{ as_timestamp(now().replace(day=now().day  + offset)) | timestamp_custom('%A') }}
          {% else %} Not set
          {% endif %}

which give me the option to have TTS say the alarm to me :wink: as you suggested, via an extra intermediary sensor, which state is used as a variable in the speech script:

      next_alarm_text:
        friendly_name: Next alarm text
        entity_id:
          - sensor.time_until_next_alarm
          - sensor.next_alarm_day
        icon_template: >
          {% if is_state('sensor.next_alarm_text','Alarm is not set, relax') %} mdi:alarm-off
          {% else %} mdi:alarm
          {% endif %}
        value_template: >
          {% set time = states('sensor.time_until_next_alarm_full') %}
          {% 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 %}