Anniversaries template sensor blueprint (to replace Anniversaries integration)

Since Home Assistant version 2025.12 the pinkywafer/Anniversaries integration is broken and it seems it is no longer maintained. I liked what it did, so I created a template blueprint to mimic its most common functionality.

The blueprint has the same attributes so it can be used as a drop in replacement for most common uses. However, since the entities are no longer made by the anniversaries integration, you might need labels to target the ones from the blueprint if you want to find them easily.

  • It keeps track of the number of days until a special yearly event, and the number of years.
  • If you do not know the start year, you can specify any year and set the flag that you do not know the exact year it started.
  • It will create calendar entrires in a calendar if you like that. The default calendar is calendar.anniversaries. If the calendar does not exists, no entries are made. Each anniversary is created only once and never deleted unless you do so yourself. If the information that is used for the calendar item changes, a new item is created.

Please note that blueprint template sensors are still in the experimental phase. At some point they will hopefully move to the UI (petro is hard at work to make this possible). But snce the integration broke I deciced to post it anyway. More information on how they are used is here:

  • In your config directory, check for a blueprint folder. If it is not there, create one.
  • In the blueprint folder, create a folder named edwin_d
  • In that folder, place a file names anniversary.yaml and put the below blueprint code in it.
blueprint:
  name: Anniversary
  description: >
    Keep track of important dates.
  domain: template
  input:
    sensor_name:
      name: Name
      description: Name of the special date to remember.
      selector:
        text:
    special_date:
      name: Special date
      description: The date to remember (e.g. wedding anniversary).
      selector:
        text:
    year_unknown:
      name: Year unknown
      description: |
        If the year of the special date is unknown then the year will be ignored 
        and the number of years will not be calculated.
      default: false
      selector:
        boolean:
    calendar_entity:
      name: Calendar entity
      description: |
        Calendar to put an appointment in if the calendar exists. 
        Each anniversary is created only once and never deleted unless you do so yourself.
        If the information that is used for the calendar item changes, a new item is created.
      default: 'calendar.anniversaries'
      selector:
        entity:
          domain: calendar
variables:
  i_sensor_name: !input sensor_name
  i_special_date: !input special_date
  i_year_unknown: !input year_unknown
  i_calendar: !input calendar_entity
trigger:
  - trigger: time
    at: "00:00:00"
  - trigger: homeassistant
    event: start
  - trigger: event
    event_type: event_template_reloaded
actions:
  - variables:
      today: "{{- today_at() -}}"
      spec: "{{- strptime(i_special_date, '%Y-%m-%d') | as_local -}}"
      nextd: |
        {%- set today_date = today | as_datetime | as_local -%}
        {%- set spec_date = spec | as_datetime | as_local -%}
        {%- if spec_date.month == 2 and spec_date.day == 29 -%} 
          {%- set next = spec_date.replace(year=today_date.year, day=28) + timedelta(days=1) -%}
          {%- if next < today_date -%}
            {%- set next = spec_date.replace(year=today_date.year+1, day=28) + timedelta(days=1) -%}
          {%- endif -%}
        {%- else -%} 
          {%- set next = spec_date.replace(year=today_date.year) -%}
          {%- if next < today_date -%}
            {%- set next = spec_date.replace(year=today_date.year+1) -%}
          {%- endif -%}
        {%- endif -%} 
        {{- next -}}
      anniv: "{{ nextd if i_year_unknown else spec }}"
      days_until: "{{ (nextd | as_datetime | as_local - today | as_datetime | as_local).days }}"
      total_days: "{{ (today | as_datetime | as_local - spec | as_datetime | as_local).days }}"
      weeks_until: "{{ days_until // 7 }}"
      yr: |
        {%- set today_date = today | as_datetime | as_local -%}
        {%- set spec_date = spec | as_datetime | as_local -%}
        {%- if spec_date.month == 2 and spec_date.day == 29 -%} 
          {%- set curr_date = spec_date.replace(year=today_date.year, day=28) + timedelta(days=1) -%}
        {%- else -%} 
          {%- set curr_date = spec_date.replace(year=today_date.year) -%}
        {%- endif -%} 
        {{ curr_date.year - spec_date.year - (1 if curr_date > today_date else 0) }}
      yr_at_anniv: "{{ yr + (1 if days_until else 0) }}"
      cal: "{{ i_calendar if i_calendar else 'calendar.anniversaries' }}"
      ent_id: |
        {%- set id = 'sensor.template_' ~ i_sensor_name | slugify -%}
        {{- this.entity_id if this is defined and this.entity_id is defined else 
            id if states(id) != 'unknown' else 
            'sensor.' ~ i_sensor_name | slugify -}}
      calitem: |
        {%- set calitem = none -%}
        {%- if states(cal) != 'unknown' -%}
          {%- set calitem = [ cal, nextd[0:10], i_sensor_name, yr_at_anniv ]|join(',') -%}
        {%- endif -%}
        {{- calitem -}}
  - if:
      - condition: template
        value_template: |
          {{  has_value(cal) and 
              calitem != state_attr(ent_id, 'last_created_event') }}
    then:
      - action: calendar.create_event
        metadata: {}
        target:
          entity_id: "{{ cal }}"
        data:
          summary: |
            {{ i_sensor_name ~ 
                ('' if i_year_unknown else
                ' (' ~ yr_at_anniv ~')') }}
          start_date: "{{ nextd[0:10] }}"
          end_date: "{{ (((nextd | as_datetime | as_local) + timedelta(days=1)) | string)[0:10] }}"
sensor:
  name: "{{ i_sensor_name }}"
  icon: |
    {{  'mdi:calendar' if weeks_until else 
        'mdi:calendar-alert' if days_until else 
        'mdi:cake-variant' }}
  unit_of_measurement: "d"
  device_class: duration
  state: "{{ days_until }}"
  attributes:
    years_at_anniversary: "{{ none if i_year_unknown else yr_at_anniv }}"
    current_years: "{{ none if i_year_unknown else yr }}"
    days_since_date: "{{ none if i_year_unknown else total_days }}"
    date: "{{ anniv | as_datetime | as_local }}"
    next_date: "{{ nextd | as_datetime | as_local }}"
    weeks_remaining: "{{ weeks_until }}"
    last_created_event: |
      {{ calitem if has_value(cal) else
          state_attr(ent_id, 'last_created_event') }}

Sensors can only be made in yaml at this time. The look like this (ignore errors from Studio Code, the helper does not understand blueprint sensors):

template:
  - use_blueprint:
      path: edwin_d/anniversary.yaml
      input:
        sensor_name: Birthday Mickey Mouse
        special_date: "1928-11-18"
        year_unknown: false
        calendar_entity: calendar.disney
    unique_id: birthday_mickey_mouse
  - use_blueprint:
      path: edwin_d/anniversary.yaml
      input:
        sensor_name: Sinterklaas
        special_date: "1970-12-05"
        year_unknown: true
        calendar_entity: calendar.anniversaries
    unique_id: sinterklaas

Note that the blueprint relies on the entity_id of the sensor it creates to prevent duplicates on calendar entities. I tried to replicate what the sensor does to generate the id (it seems to rely on unique_id somehow). If you have trouble with this, try to use the settings of the entity to regenerate the unique id. That should fix it.

This topic was made a wiki, so any one can improve on it.

3 Likes

Looks good Edwin, might give it a try…

FWIW the Anniversaries integration is working fine for me with HA 2025.12 so might be something else you’re seeing…

The config flow is broken in the old integration. Existing entities from anniversaries integration work, but when you try to edit one in the UI you get: Server got itself in trouble. It had received deprecation warnings about changes for 6 months, those were ignored.

Maybe yaml does still work, but the last edit on the repo was a year ago so I decided it wasn’t worth waiting.

1 Like

Cheers Edwin…absolutely, it’s great to have an alternative/new option…wasn’t taking away from your good work more just a FYI :+1:t2:

1 Like

Thank You for making this! I’ve noticed that a new calendar entry gets made every time I restart home assistant. Is there a way to have it check if it exists before adding another identical one or did I do something wrong when setting up?

I try to, but naming is critical. The recreation of calendar events is the consequence of that I cannot fully predict the entity id of the sensor. It should either be sensor.template_<name in lowercase with underscores> or sensor.<name in lowercase with underscores> for me to find the old one and skip creation.

I’ve noticed sometimes the entity id is derived from the unique id rather than the name. So it should have one as per the example, also derived from the the name in lowercase with special chars replaced by _ . Also note the comment at the bottom of my top post which tries to explain it and gives a way that may solve it without changing the templates you have.

If I were to make it foolproof I need to check the calendar each time, which will hurt performance. So I chose this way. If the naming is done right, there should be no problem.

Should have checked before creating my own blueprint yesterday :slight_smile:
BTW the integration is still working for me in 2026.3 beta, it looked broken for a moment (that’s when I decided to create the blueprint) but after another restart it was working again.

I didn’t add support for calendars though, as I didn’t use that from the custom integration (I did add support for count_up but I don’t use that as well and for half year anniversaries, which is something I do use myself)

BTW, your input for the calendar entity is fixed to the binary_sensor domain, guess that should be calendar (which will not be an issue until there is GUI support for template blueprints)

The integration broke last beta too on the config flow, and the owner did not respond. So I thought the same as you did. Some time after release some one fixed the problem later. By then I had replaced them by this.

A, cut and paste error. I’ll fix that. I’s a wiki, so you could have too :slight_smile:

Still have to figure out how to add the ā€˜MY’ button for importing the blueprint without needing to remove the second code block from the post.

I have near 70 events for all of my entries. I’m not sure what you mean by using the settings of the entity to regenerate the unique id? Is this not ok?

You need to look at the entity id that the blueprint generated in the GUI. The name ā€œAndrea’s Birthdayā€ is what I use to guess it (a couple of variations on sensor.andrea_s_birthday). However, since your unique id is birthday_andrea, your entity id is probably something else. In the GUI is a button to recreate the entity id based on the name. That should change the entity id to what the blueprint expects.

I am not seeing an option to recreate the entity id based on the name for these sensors, but I was able to stop it from duplicating by making the name and unique id match exactly. Thanks again for your help on this. One thing I noticed though was in the calendar when I click on an entry and "edit event’ the bottom option is for ā€œRepeatā€ and all entries are set to ā€œNo Repeatā€. Does this need to be manually set for each entry once it’s created to repeat yearly?

It creates the next one the day after the anniversary. No repeat, mainly because if I can, I put the age in the title, and that is different every year.

I appreciate the help with this, I am slowly figuring it out. I’m using the custom auto entities card to display upcoming events on a dashboard on my wall. I essentially have two levels of events and have added an attribute to the blueprint (alert window) for this. Each event has an alert window of 3 or 15 days which is then used to display the reminder either 3 or 15 days before the event. Here’s the code for the dashboard card, I’m sure there is a better way of doing it but this almost seems to be working for the last two days. I had one event at 4 days yesterday that disappeared today. Not sure why yet and I’m not sure it it’ll reappear tomorrow or if I messed something up in the yaml file.

type: custom:auto-entities
card:
  type: entities
  show_header_toggle: false
  state_color: true
filter:
  include:
    - options:
        type: custom:multiple-entity-row
        secondary_info:
          attribute: years_at_anniversary
          unit: years
      entity_id: sensor.template_*anniversary*
      state: <4
      attributes:
        alert_window: <4
    - options:
        type: custom:multiple-entity-row
        secondary_info:
          attribute: years_at_anniversary
          unit: years
      entity_id: sensor.template_*anniversary*
      state 1: <15
      state 2: ">=4"
      attributes:
        alert_window: <=15
    - options:
        type: custom:multiple-entity-row
        secondary_info:
          attribute: years_at_anniversary
          unit: years
      entity_id: sensor.template_*birthday*
      state: <4
      attributes:
        alert_window: <4
    - options:
        type: custom:multiple-entity-row
        secondary_info:
          attribute: years_at_anniversary
          unit: years
      entity_id: sensor.template_*birthday*
      state 1: <15
      state 2: ">=4"
      attributes:
        alert_window: <=15
  exclude:
    - options: {}
      state: false
sort:
  method: state
  reverse: false
  ignore_case: false
  numeric: true
show_empty: false

Found my error. I need it to always show every event with a state <4.

I feel like the right way to do this would be to consolidate it to show if the state is <= the alert window, which would remove a few lines I think, but I’m not sure if that’s possible. Either way this seems to be working now.

type: custom:auto-entities
card:
  type: entities
  show_header_toggle: false
  state_color: true
filter:
  include:
    - options:
        type: custom:multiple-entity-row
        secondary_info:
          attribute: years_at_anniversary
          unit: years
      entity_id: sensor.template_*anniversary*
      state: <4
    - options:
        type: custom:multiple-entity-row
        secondary_info:
          attribute: years_at_anniversary
          unit: years
      entity_id: sensor.template_*aniversary*
      state 1: <15
      state 2: ">=4"
      attributes:
        alert_window: <=15
    - options:
        type: custom:multiple-entity-row
        secondary_info:
          attribute: years_at_anniversary
          unit: years
      entity_id: sensor.template_*birthday*
      state: <4
    - options:
        type: custom:multiple-entity-row
        secondary_info:
          attribute: years_at_anniversary
          unit: years
      entity_id: sensor.template_*birthday*
      state 1: <15
      state 2: ">=4"
      attributes:
        alert_window: <=15
  exclude:
    - options: {}
      state: false
sort:
  method: state
  reverse: false
  ignore_case: false
  numeric: true
show_empty: false