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_satelliteentity (Voice PE hardware works; so does anything else exposing that domain), or one or morenotifyentities 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)
- HACS → Integrations → ⋮ → Custom repositories
- Paste
https://github.com/fiendishly-delicious/fiendishly-good-reminders, category
Integration - Search HACS for Fiendishly Good Reminders and download it
- (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 - Restart Home Assistant. A custom integration is not picked up by a reload —
automation.reloadand friends will not see the new services - Settings → Devices & Services → Add Integration → Fiendishly Good Reminders
Manually
-
Copy the
fiendishly_remindersfolder into/config/custom_components/so you have
/config/custom_components/fiendishly_reminders/__init__.py -
(Optional) For the supplied voice commands, copy
custom_sentences/en/reminders.yaml
into/config/custom_sentences/en/ -
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. -
Restart Home Assistant
-
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_satelliteplays 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 … reminderasks 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 thesuccesscodes:1worked,10/11/12for a time that could not be
read, a rule that was rejected, a time already gone,20–24for a target that was
missing, matched nothing or named no list,30/31for a skip with nothing to skip, and
40for 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!
