Is there any life hack for using calendar for events less than 15 minutes in the future?

Here’s a rough draft using trigger based template sensors, that seems to be working:

Sensors config
template:
  - trigger:
      - trigger: event
        event_type: custom_button_timers
        event_data:
          type: 40sec
          timer_id: 1
    sensor:
      - name: Button timer 40s 1
        state: "{{ now() + timedelta(seconds=40) }}"
        device_class: timestamp
  - trigger:
      - trigger: event
        event_type: custom_button_timers
        event_data:
          type: 40sec
          timer_id: 2
    sensor:
      - name: Button timer 40s 2
        state: "{{ now() + timedelta(seconds=40) }}"
        device_class: timestamp
  - trigger:
      - trigger: event
        event_type: custom_button_timers
        event_data:
          type: 40sec
          timer_id: 3
    sensor:
      - name: Button timer 40s 3
        state: "{{ now() + timedelta(seconds=40) }}"
        device_class: timestamp
  - trigger:
      - trigger: event
        event_type: custom_button_timers
        event_data:
          type: 40sec
          timer_id: 4
    sensor:
      - name: Button timer 40s 4
        state: "{{ now() + timedelta(seconds=40) }}"
        device_class: timestamp
Automation to set Sensor states
alias: Set 40s button
description: ""
triggers:
  - trigger: state
    entity_id:
      - input_button.40_second_timer
    not_to:
      - unknown
      - unavailable
conditions: []
actions:
  - if:
      - condition: template
        value_template: "{{ first_available.split('_')[-1]|int('None') != 'None' }}"
    then:
      - event: custom_button_timers
        event_data:
          type: 40sec
          timer_id: "{{ first_available.split('_')[-1]|int('None') }}"
    else:
      # You might want to add a notification action here so it doesn't fail silently while you're busy boiling your bagels.
      - stop: "No Available Sensors"
variables:
  first_available: >
    {% set ent_pool = integration_entities('template') | select('match',
    'sensor.button_timer_40s') | list %} {% if ent_pool|select('is_state',
    'unknown')|list %}
      {{ ent_pool|select('is_state', 'unknown')|first }}
    {% elif expand(ent_pool) | selectattr('state', '<',
    utcnow().isoformat())|list %}
      {{ expand(ent_pool) | selectattr('state', '<', utcnow().isoformat())|map(attribute='entity_id')| first  }}
    {% else %}
      None
    {% endif %}
mode: queued

It would probably be a good idea to incorporate some kind of notification if the

Automation for timer completion
alias: Button Timer Time trigger
description: ""
triggers:
  - trigger: time
    at:
      - sensor.button_timer_40s_1
      - sensor.button_timer_40s_2
      - sensor.button_timer_40s_3
      - sensor.button_timer_40s_4
conditions: []
actions:
  - action: persistent_notification.create
    metadata: {}
    data:
      message: Button Timer Complete
mode: single
Sensor configuration builder template

You can paste this into the Template editor to have it “write” the configuration for the sensors into the Results box, which you can then copy/paste into your actual config.

{% set types = 
{"sourdough": {'30m':1800, '37m':2200, '7m':420},
"bagel": {'40s':40, '2h':7200, '3h':10800},
"all purpose": {'1m':60, '5m':300, '10m':600, '15m':900} } %}
{% set copies = 4%}


template:
{%- for k,v in types.items() %}
{%- for k2, v2 in v.items() %}
{%- for i in range(1,copies+1) %}
  - trigger:
      - trigger: event
        event_type: custom_button_timers
        event_data:
          type: {{k2}}
          timer_id: {{i}}
    sensor:
      - name: Button timer {{k2}} {{i}}
        state: {%raw%}"{{ now() + timedelta(seconds={%endraw%}{{v2}}{%raw%}) }}"{%endraw%}
        device_class: timestamp
{%- endfor %}{%- endfor %}{%- endfor %}
2 Likes

You could also just store your timers within trigger-based sensor. The main drawback is the /1 sec pooling but as I tested it for a bit, it seems not to have any impact on processor use. The condition stops processing of the template if there are no timers “inside”.

Code
- trigger:
  # pooling each second but it seems fine
  - trigger: time_pattern
    seconds: '/1'
    id: timer
  # the event to add new timer, e.g.
  # - event: add_new_timer
  #   data:
  #     seconds: 40
  #     summary: Bagles
  - trigger: event
    event_type: add_new_timer
    id: new

  # skip processing of template if there's no need
  condition:
    - condition: template
      value_template: >
        {% if trigger.id == 'new' %}
          {{ trigger.event.data.seconds is defined and trigger.event.data.seconds|int(0) > 0
             and trigger.event.data.summary is defined
          }}
        {% else %}
          {{ (state_attr('sensor.my_timers','count') or 0) > 0 }}
        {% endif %}

  action:
    # there are timers
    - if: "{{ trigger.id == 'timer' }}"
      then:
        - variables:
            # Timestamp of this moment
            nTs: "{{ now().timestamp() }}"
            
            # all timers
            all: "{{ state_attr('sensor.my_timers', 'timers') or {} }}"
           
            # elapsed timers
            elapsed: "{{ all.items()|selectattr('0','le',nTs|string)|list }}"
            
            newCount: "{{ all|count - elapsed|count }}"
      
        # do something for elapsed timers, e.g. send a message
        - if: "{{ elapsed|count > 0 }}"
          then:
            - repeat:
                for_each: "{{ elapsed }}"
                sequence:
                  - action: notify.mobile_app_iphone_maciej
                    data:
                      title: Timer
                      message: "{{ repeat.item[1] }}"
                      
    # a new timer will be added
    - if: "{{ trigger.id == 'new' }}"
      then:
        - variables:
            newCount: "{{ (state_attr('sensor.my_timers','count') or 0) + 1 }}"
                  
  sensor:
    - name: My timers
      unique_id: my_timers_id
      state: ""
      attributes:
        # Storage for timers; k (key) is the timestamp when timer will be elapsed
        # Here we add new timers and get rid of elapsed timers
        timers: >-
          {% set current = this.attributes.get('timers', {}) %}
          {% if trigger.id == 'new' %}
            {% set delta = timedelta(seconds= trigger.event.data.seconds|int) %}
            {% set k = (now() + delta).timestamp()|string %}
            {% set v = trigger.event.data.summary %}
            {% set new = {k: v} %}
            {{ dict(current, **new) }}
          {% elif trigger.id == 'timer' and elapsed|count > 0 %}
            {{ dict(current.items()|rejectattr('0','le',nTs|string)) }}
          {% else %}
            {{ current }}
          {% endif %}
        # number of timers stored
        count: "{{ newCount }}"

Then you add new timers by firing an event:

- event: add_new_timer
  data:
    seconds: 40
    summary: Bagles

The time must be in seconds, but can be any period shorter than 24 hours. E.g. an hour is 3600 seconds, etc. Also, you can’t fire an event from tap_action directly, so you’d need to write a proxy script for that.