Fiendishly Good Reminders - proper voice reminders that fire on time

Fiendishly Good Reminders

GitHub - fiendishly-delicious/fiendishly-good-reminders · GitHub

Provides the reminder functionality that Home Assistant has long needed. Reminders are announced at the exact time scheduled to the second. This integration keeps its own store, calculates the timing internally in Python, and is completely LLM independent.

“Remind me to take the bins out every Sunday at 9 pm for the next 6 weeks.”

The integration provides six reminder actions, two reminder entities and a custom reminder
dashboard card. Also included below is a suggested set of voice sentences that can be used to
schedule, cancel, skip or list your reminders. The actions may equally be called
programmatically.

Action What it does
schedule Creates a reminder — one-off or repeating, with an optional end after a number of weeks, a number of times, or a named date
cancel Removes a reminder outright. For a repeating one that is every occurrence, which is why it reads the match back and waits for a yes
skip Drops the next occurrence, or everything up to Saturday, and leaves the series running. Asks first only when the skip would exhaust the rule and delete the reminder
list The upcoming reminders, soonest first, over however many days you ask for
find Searches by text, by a named day, or by a period — one entry per reminder however often it repeats, with the occurrences that fall inside the range
edit Changes a reminder’s wording, its time, or its recurrence. Moving a series re-anchors the whole pattern; changing the pattern leaves the hour alone

Every one of them answers: a success code and, when it is not 1, an error saying
exactly what went wrong. Nothing raises, so a script always gets to read what happened.

Add single or recurring reminders — recurring daily, weekly, or monthly. Say “Remind me again in [time]” to reset the last reminder. Edit existing reminders. Pretty much anything you want. All local, no LLM interference.

A list each, for everyone in the house. Add the integration once per person and each gets
their own reminders, kept entirely separate — nothing is shared between two lists but the
grammar. Each list is given its own speakers and its own phones, and they may overlap however
suits the house: John’s reminders speak in the study and the kitchen, Jen’s in the bedroom and
the kitchen, and the kitchen carries both. A reminder finds the right list from a name in the
sentence — “remind Jen to water the plants” — or from the speaker you said it to, and where
a shared speaker leaves it genuinely ambiguous it asks rather than guesses.

The companion dashboard card, with no styling configured — every color but the action icons
and the day headings comes from your theme. Note the two things the built-in to-do card
structurally cannot do: the recurrence in prose under the repeating reminder — “every Sunday,
until October 17” — and a skip button that appears only on the rows that repeat.


Why this exists

Home Assistant still has no native reminder. As of 2026.9 the built-in to-do intent (HassListAddItem) accepts no due date at all, and the PR to add one was closed. Prior reminder integrations and blueprints used a simple to-do list and a polling loop, but a to-do item cannot trigger an automation. That required a compromise: a reminder due at 9:00 arrives at the next poll, somewhere between 30 seconds and 15 minutes later. Others handed the sentence to an LLM to parse, which is where a request becomes a confident reply and no reminder at all.

In contrast to the above, this integration owns its own schedule, so neither compromise is necessary:

  • Reminders fire at the second.
  • No LLM anywhere in the path. Python does the arithmetic, and every action answers with a response variable saying either that the reminder was scheduled or exactly why it was not — so you get accurate feedback on your requests.
  • Reminders persist, and one that came due while Home Assistant was down is delivered on restart rather than lost.

What it does

Exact firing To the second, from a persistent store that survives restarts
Say it in two breaths Name the task with no time and it asks “When should this reminder occur?”
Late delivery Anything overdue by less than an hour at startup is still delivered
Recurrence Daily, weekly, weekdays, weekends, monthly — and series that end
Skip one Drop a single occurrence, or a whole week, leaving the series running
Cancel By text, by time, or by id — and it reads the match back and waits for a yes
Presence & quiet-hours routing Speaks in the room when you are home and awake, pushes to your phone otherwise
Degrading delivery Satellites → notify service → persistent notification. Never throws
Says whose it is With two lists, a reminder arrives as “Reminder for John: take the trash out”
Turned round to face you You say “remind me to take my pills”; it says “take your pills”
Entities A to-do list and a timestamp sensor carrying the whole list in attributes
Dashboard card A companion card that shows recurrence, which the to-do card cannot
One list per person Add it once for each of you: separate lists, separate speakers, separate phones

Requirements

  • Home Assistant 2026.8 or newer (developed and tested on 2026.8.3)

  • A way to deliver a reminder — at least one of: an assist_satellite entity (Voice PE hardware works; so does anything else exposing that domain), or one or more notify entities or services, such as the companion app

  • For the spoken cancel confirmation and the two-turn edit: a satellite supporting START_CONVERSATION (supported-features bit 2). Home Assistant Voice PE has it; the built-in microphone on a Yellow/Green does not

  • No Python dependencies beyond what ships with Home Assistant.


Installation

The integration itself is the component. It exposes six actions and two entities, and that is all you need — automations, scripts, dashboards and your own voice sentences can drive it from there.

A suggested sentence file is included for people who want the voice commands described below without writing any. It is genuinely optional: skip it and everything except the supplied phrasings still works.

Via HACS (custom repository)

  1. HACS → Integrations → ⋮ → Custom repositories
  2. Paste https://github.com/fiendishly-delicious/fiendishly-good-reminders, category
    Integration
  3. Search HACS for Fiendishly Good Reminders and download it
  4. (Optional) For the supplied voice commands, copy custom_sentences/en/reminders.yaml
    from the repository into /config/custom_sentences/en/reminders.yaml, creating the
    folders if needed. Skip this if you would rather write your own
  5. Restart Home Assistant. A custom integration is not picked up by a reload —
    automation.reload and friends will not see the new services
  6. Settings → Devices & Services → Add Integration → Fiendishly Good Reminders

Manually

  1. Copy the fiendishly_reminders folder into /config/custom_components/ so you have
    /config/custom_components/fiendishly_reminders/__init__.py

  2. (Optional) For the supplied voice commands, copy custom_sentences/en/reminders.yaml
    into /config/custom_sentences/en/

  3. Add one line to configuration.yaml:

    fiendishly_reminders:
    

    This key holds no configuration — everything is set up in the UI. It exists so the
    component is set up during bootstrap, which registers the voice intents before the
    config entry finishes loading. Without it, a reminder spoken during the first minute or
    two after a restart answers “Unknown intent” while the entry waits behind slower
    integrations. Harmless, and worth adding, even if you use none of the supplied
    sentences.

  4. Restart Home Assistant

  5. Settings → Devices & Services → Add Integration → Fiendishly Good Reminders


Configuration

All configuration is through the UI. The same form is available afterwards under Configure, and changing it reloads the integration without a restart.

The form is in six sections. Every field is optional except the name and one delivery
channel, and submitting without them comes back naming what is missing rather than saving
a half-set-up list.

Section Field Required What it does Default
Whose Reminders Whose reminders are these? Yes The list’s name. It titles the entry — John gives John’s Reminders — and it is how a spoken reminder finds the right list when there is more than one (none)
Send Reminders to: Target Satellites One of these two assist_satellite entities that speak reminders. Also the only satellites a spoken question is ever asked on (none)
Notification Recipient(s) One of these two Where a reminder goes when it cannot or should not be spoken aloud. Pick as many as you like (none)
Prevent Spoken Reminders If: This person is not home No A person entity. While it is not home, reminders go to the notification recipients instead of being spoken (none)
Quiet Hours Start No Beginning of the do-not-disturb window. Clear it with the X to switch quiet hours off (none)
Quiet Hours End No End of it. The window wraps midnight (none)
Chimes Play Chime Before Reminder Yes Whether anything sounds before a spoken reminder at all Yes
Chime No Which sound: fgReminders (the bundled one), Home Assistant’s own chime, or any file in <config>/media/Chimes fgReminders
Default Times Default Time Yes Used when a reminder names no time at all — “remind me to stretch every day”, “remind me to call on Friday” 08:15:00
Default Morning Time Yes What tomorrow morning and Monday morning resolve to 08:15:00
Default Afternoon Time Yes What this afternoon resolves to 14:00:00
Default Evening Time Yes Covers both evening and night — they are the same end of the day 21:00:00
Confirmations Confirm before canceling No Read a cancellation back and wait for a yes. The help text under it warns that turning it off also narrows what will be matched — see below Yes

The four times are marked required only so that Home Assistant does not draw a clear button
beside them: they arrive filled in, and there is no such thing as “no morning”. The two
delivery fields are the opposite — neither is required on its own, and the pair is checked by
hand, because “one or the other” is a condition across two fields rather than a property of
either.

Submitting with the name blank, or with no delivery target, comes back naming what is missing
and holding everything else you had already filled in.

Give at least one delivery channel — satellites, a notify service, or both. Either will do; what a setup cannot be is deaf and mute at once, because a reminder that fires exactly on time and has nowhere to go has achieved nothing. Every other field can only remove behavior by being absent:

  • No presence entity → reminders are always considered speakable, so the satellites always get them.
  • No quiet hours → there are no quiet hours. Clearing a field with its X is how you turn them off, and setting only one of the two is the same as setting neither.
  • Play Chime Before Reminder off → the announcement plays with no sound in front of it at all. Silence is asked for explicitly rather than left unset, because assist_satellite plays its own sound when preannounce is not specified — which is what the Home Assistant’s own chime option gives you instead.
  • No notification recipients (satellites only) → there is no phone fallback. When a reminder should not be spoken — because you are not home, or it falls inside quiet hours — it is raised as a persistent notification instead, so it is still waiting for you rather than silently dropped.
  • No satellites (recipients only) → everything goes to the notification recipients, and nothing is ever spoken aloud. Presence and quiet hours then make no difference to where a reminder lands, and the spoken confirmations described below do not apply: canceling happens immediately and says so, and edit my … reminder asks you to say the change in one sentence instead.
  • Confirm before canceling off → a cancellation happens the moment you ask for it. It also narrows what will be matched: the tolerant matching that lets “cancel my dennist reminder” find dentist is only safe because the guess is named out loud before anything happens to it, so with nothing reading it back only an exact match is acted on. The guess and the question stand or fall together.
  • A time left at its default → 08:15 for the plain one, and each part of the day keeps the hour it shipped with. The four cannot be blanked from the form; they are pre-filled and required.

Your own chimes go in <config>/media/Chimes. The integration creates that folder on
startup and serves it, so anything you drop in — .mp3, .wav, .ogg, .flac, .m4a —
appears in the picker named by its filename. It is not the media library: Home Assistant’s
/media is a separate mount and media_source cannot see inside the config directory, so
this folder is served by the integration itself. Select a sound and later delete the file
and the picker falls back to the bundled one rather than refusing to open.

A part of the day only fills in a time nobody gave. “Monday night” is whatever Default Evening Time
is set to, but “Monday night at 9” is 9pm whatever it is set to — naming an hour always
wins, and the part of the day then only decides that 9 means the evening one.

The name is the one field with no sensible absence: a list has to be called something for a reminder to be addressed to it. On a single-list setup nothing ever needs to name it, but it costs one word now and saves reconfiguring later.



What else is in the README

The above is the front half. The readme on github carries the detail:

  • The six actions in full — every field, example YAML for each, the exact shape of the
    response, and the success codes: 1 worked, 10/11/12 for a time that could not be
    read, a rule that was rejected, a time already gone, 20–24 for a target that was
    missing, matched nothing or named no list, 30/31 for a skip with nothing to skip, and
    40 for an empty answer.
  • One list per person — how a spoken reminder finds the right list, what happens on a
    speaker two people share, and what an unanswered question costs.
  • The two entities — every attribute on the sensor, including the whole reminder list as
    structured data, and what the to-do entity is for and when you can ignore it.
  • The voice sentences — the phrasings the supplied example understands, and how to write
    your own. The sentence file is a suggestion; the actions are the interface.
  • The dashboard card — installing it, its options, and what the built-in to-do card cannot
    do that it can.
  • Building your own voice commands — a worked minimal example of a custom sentence and
    the intent script behind it.
  • Known limitations, stated plainly.

There is also a Voice Flows site — a page per action showing what causes each
response and the exact words it answers with, including the questions it asks back.

Happy to answer anything here. Hope you enjoy!