The Rice Does Not Need a Language Model.
Native timers, phone Live Activities, Bluetooth speakers, a silent audio bridge, and a printable cradle—with architecture, implementation notes, and links for building your own.
The kitchen timer waited twenty minutes before starting its twenty-minute countdown.
This is an impressive interpretation of patience. It is less useful for rice.
Since the last post, we have been working on timers, live printer notifications, repurposed speakers, and a small printed cradle that might eventually keep a speaker off the counter. The common question became: which part of this system should be allowed to improvise?
The answer, increasingly, is “not the clock.”
A timer should start when you ask
The logs showed a timer being acknowledged and its countdown beginning much later. We suspected the voice AI had used a delayed-command option to schedule a command that then started a timer. The original tool call was unavailable, so that explanation remains a hypothesis.
Our first change was a prompt safeguard: ordinary countdowns should call the native timer intent immediately with a duration and optional name. They should not use conversation_command.
Then we made the stronger change. Standard timer-creation phrases now use explicit sentence rules and native timers, bypassing the language model for that step.
“Set a timer called rice for twenty minutes” does not need literary interpretation.
How the pieces connect
The implemented flow has two branches:
- Control: Kiosk Satellite voice endpoint → Home Assistant Assist sentence matching → native timer creation.
- Display: Kiosk timer events → Home Assistant automation → Companion app → one phone Live Activity per timer.
The language model remains available for richer requests. Pause, cancel, and status requests still use our existing AI-to-native-tool path; this change does not remove AI from every timer interaction. It also does not establish that every stage of speech processing is local.
The display automation listens for esphome.kiosk_satellite_timer. It derives the notification tag from the timer ID: voice-timer-<timer_id>. That identifier is what keeps the pasta timer from becoming the rice timer when an update arrives.
For a running countdown, it calculates an ending timestamp from the event time plus seconds remaining. Pausing stops the displayed countdown, resuming supplies a new deadline, and finishing or canceling clears that timer’s activity.
We tested immediate starts, multiple named timers, independent cancellation, pause/resume, and a timed expiry. I also confirmed that the phone activities appeared.
Turn this into your own project
Start with a working Assist endpoint and one named timer. Use Home Assistant’s custom-sentence documentation to understand sentence matching and extending intents. The timer event above comes from our Kiosk Satellite setup; check your own endpoint’s events rather than assuming every voice device emits it.
Build the phone display separately. First confirm that timer events contain a stable ID, action, and remaining duration. Then map those events to start, update, pause, and clear operations. Finally, test two timers at once and cancel just one.
A single successful countdown is a demonstration. Canceling the rice without canceling the pasta is an integration test.
The printer gets one ongoing status report
We applied a similar idea to the 3D printer: show progress, status, and estimated completion in a native iPhone Live Activity instead of treating every percentage change as news.
The architecture is straightforward:
Bambu printer → Home Assistant Bambu integration → status/progress automation → Companion Live Activity.
The Bambu Lab Home Assistant integration provides the device entities. Our automation decides when a notification should exist. The phone presents it.
That middle step is the project.
Our active-state allowlist is prepare, running, pause, and slicing. Progress updates are capped at once every two minutes; status changes take the immediate path. Terminal conditions clear the activity. A subsequent percentage or ETA change must not recreate it while the printer is idle.
We removed initialization from the active states. Turning on a printer is not the same as printing something, even if both involve optimism.
A small starting point
With a compatible Home Assistant and Companion setup, this is a minimal manual notification action to adapt in Developer Tools. Replace the placeholder with your phone’s actual notification action:
action: notify.mobile_app_your_phone
data:
title: "Printer"
message: "Running"
data:
tag: printer_demo
live_update: trueThis illustrates the notification payload only; it is not the complete printer automation. Follow the official Live Activities documentation for requirements, progress fields, countdowns, and cleanup.
For the automation itself, the routing logic is:
on status, progress, or ETA change:
if terminal, idle, disconnected, or error:
clear the activity
else if status is in the active allowlist:
update on status change
otherwise update only when the throttle permitsUse a separate tag for previews so a delayed test cleanup cannot erase the real print.
All eleven simulated routing scenarios passed. We also observed a real print reach 100%, transition to finished, send the clear command, and issue its normal completion notification. The phone’s display had been confirmed earlier, but its disappearance at completion was not independently observed.
This rollout is on my phone only. Another limitation remains: in the installed versions, swiping away an activity can allow a later update to recreate it. We accepted that behavior for this iteration; it is not a solved per-print dismissal feature.
The little speaker has a better speaker now
We brought another second-generation Echo Dot into the setup and upgraded the kitchen Dot to the newer Kiosk firmware. The office Dot now outputs to a Tribit StormBox Micro 2; the kitchen Dot outputs to a JBL Clip 4.
The audio path is:
Music Assistant → Sendspin → Kiosk Satellite on the Dot → Bluetooth → portable speaker.
The Dot remains the named player in Music Assistant. Bluetooth changes its output, not the identity used by our voice routing.
This lets the small reused device handle voice and network playback while another speaker handles the sound. The relevant starting points are the Echo Dot firmware and migration guide and Kiosk’s media-player and synchronization settings.
We backed up settings before the kitchen firmware migration and restored its connections afterward. Pairing alone was not enough to prove success: the office speaker initially remained silent despite a reported connection. A restart restored audio, which I confirmed by listening.
Wi-Fi instability was another separate problem. The kitchen’s first attempted fix did not hold. Recreating its saved connection and removing an access-point pin restored connectivity; a later one-minute check in place received 59 of 60 replies without new disconnects. That is encouraging, not proof of long-term stability.
For a reader building this, prove one room first: voice response, audible playback, reconnection after restart, and sustained playback in the device’s final location. Add synchronization afterward. Bluetooth latency and network reliability are different problems, and both can sound like “the music is weird.”
A silent player does the translating
Grouping the kitchen with the living-room stereo required a workaround. The WiiM Ultra did not offer a usable Sendspin path in our configuration.
We used an AirPlay-capable WiiM as a muted bridge. Music Assistant coordinates the group through that compatible player while the intended two rooms remain audible. This uses the bridging approach described in Music Assistant’s WiiM documentation.
I confirmed that the two rooms sounded synchronized and that the bridge room stayed silent. We also verified restoration of its previous mute setting after playback stopped.
The tradeoff is real: the bridge player cannot simultaneously play independent music elsewhere. It is doing a job even when it is making no sound.
If you try this, inventory the actual protocols of each player before creating a group. “Supports wireless music” is not a synchronization protocol. Test the audible result, then verify cleanup: playback stops, temporary mute changes are restored, and the borrowed player is usable again.
The software project acquired a physical object
Once the JBL was working, I wanted a compact cradle combining the speaker and its intact charger into one arrangement.
The design brief was one printed part, curved edges, minimal plastic, and geometry that avoids unnecessary supports. We used visual concepts to explore the shape, then moved into measured CAD. The horizontal outlet orientation mattered; a convincing picture was not enough.
The latest test STL uses measured charger dimensions and published speaker dimensions with fitting clearance. It passed checks for one connected, watertight part. Geometric checks found no steep unsupported undersides in the proposed print orientation, but the slicer and physical fit still get a vote.
This remains a prototype. Charger retention, speaker stability, and unplugged fit testing are outstanding. A valid mesh is not proof that something should hang from an electrical outlet.
For a similar project, OpenSCAD is the code-based CAD tool used in our cradle work. Keep body dimensions and clearances as parameters, inspect the actual exported mesh, and test fit before treating the model as finished. Our files are still local prototypes, not a published build kit.
Dinner needed a schema repair too
An imported Vegetable Maafé recipe had lost ingredient information during parsing. We corrected its eighteen ingredient amounts and restored the missing names and units.
The useful architectural distinction is between an original ingredient line and a structured ingredient with quantity, unit, and food references. Our Mealie Foods and Units libraries were empty. We added the recipe’s food entries and initialized units; the larger food import was still underway at the last check.
For your own setup, preserve the original lines unless you need structured scaling and shopping-list behavior. When parsing, accept or create missing Food and Unit suggestions before saving, then inspect the result. Mealie documents that workflow here.
The recipe did not need more creativity. It needed its ingredients back.
A useful place to start
If you want one small project from this weekend, build a named timer with a phone activity.
It has a clear input, an observable state, a stable identifier, and an ending you can test in ninety seconds. Once that works, the same separation of control, state, and display becomes useful for a printer or appliance.
Codex helped write and inspect the configuration, trace failures, and build the tests. The best result was giving the predictable work to predictable code.
The rice can keep its twenty minutes. It no longer needs twenty minutes to think about them first.