Harrison Samoy
← Projects

Kitchen News

personal project · 2026

A wall-mounted display for one household, running on a Raspberry Pi in the kitchen. It answers 'who is where, when' at a glance - but the part worth talking about is the bar across the top, which takes plain English and actually does something with it.

The home screen: today hour-by-hour, the week ahead, and the big dates coming up.

An assistant that acts, not one that answers

Most things labelled an assistant are a chat window with extra steps: you ask, it replies, you go do the work. This one is wired the other way around. "Add milk and eggs to the list", "dentist next Tuesday at 3", "Jess has class Tuesdays and Thursdays at 10 until December 12th" - each of those resolves to a real write against the app's own data, and the panels refresh themselves when it lands.

That's Gemini function calling doing the work. The model gets the app's tools rather than a blank canvas: it picks the repeat rule itself (weekly for a class, yearly for a birthday, one-off otherwise), resolves relative dates against today, looks people up by name to get their ids, and decides what's important enough to surface. The interesting engineering isn't the prompt - it's giving the model a small, sharp set of tools and letting it choose.

One validation layer, two callers

The rules about what makes a real event live in the storage layer, not in the HTTP routes. The REST API and the AI tool handlers both go through the same functions, so the model physically cannot write a record the API would have rejected - no second, laxer path in through the AI.

It also means the failure modes are boring on purpose. A bad date raises the same error whether a human typed it or a model produced it, and the assistant reports it in plain language instead of writing something malformed and moving on.

The same pattern, pointed at other inputs

Receipt scanning reuses the loop wholesale. Photograph a grocery receipt, and Gemini reads the image and reports the store, date, total, and line items back as a function call - structured output rather than prose to be parsed. If the photo isn't a receipt, or is too blurry to read a total, it says so and nothing gets recorded.

Voice does the same thing one step earlier: a clip goes to Groq's hosted Whisper, comes back as text, and is then handled by exactly the same code path as something typed. Adding a capability means adding a tool and a handler, not a new integration.

Neither the photo nor the audio is ever written to disk - both are held in memory, sent, and dropped. And with no API key set, the assistant reports that its key is missing rather than failing silently, so the display degrades to a perfectly good calendar instead of breaking.

The unglamorous parts

Everything persists as plain JSON files, written atomically - a temp file in the same directory, fsynced, then renamed over the original. A kitchen Pi gets shut down by having its power pulled, and a half-written file would take every record with it.

Work and school calendars come in as published ICS feeds instead of being retyped. Each sync expands repeats into dated occurrences and swaps out everything under that feed's tag in a single write - replacing wholesale rather than merging is what makes a moved meeting, a cancellation, and a newly-accepted invite all need no special handling.

Receipts read by the vision model, totalled by week. A week with no shopping stays on the chart as a gap rather than disappearing.
The list the assistant edits when you tell it to add something - or take something off.
Colour belongs to the person, not the event, which is what makes the day view readable from across the room.