This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend
What I Built
In our house, one person does most of the driving: soccer practice, piano, dentist, birthday parties. We have a shared calendar for all the activities but my wife keeps a lot of logistics in here head. Things like: When do I have to leave? Can I get from piano to the soccer game in time? Do I need backup tomorrow?
On top of that, every coach, school and studio sends schedule changes by email, and someone has to copy each one into the shared calendar by hand.
The goal of this is to ease the burden by having AI help ease the burden of updating the calendar and working out transportation logistics.
Herding Cats (the Family Drive Planner) is a small service on a Raspberry Pi that does two jobs:
- Nightly briefing. Every evening it emails both parents tomorrow's plan. For each event it shows when to leave and how long the drive is. It flags clashes and says which drives need the second parent.
- Email to events. Forward an activity email to a dedicated inbox. The Pi reads it, pulls out the events and replies with one calendar invite per event. Accept the invite in Outlook and the event lands on the shared family calendar.
There's no app to install and no new habit to learn. It works with email and the calendar we already use.
Demo
This is a real run of both flows against the local model (qwen2.5:1.5b-instruct). Everything is placeholder data: made-up kids, made-up places, example.com addresses. One honest caveat: I didn't have an OpenRouteService key on the dev box, so drive times in this run come from a straight-line-distance stub. In production they come from ORS and are cached in SQLite.
1. Nightly briefing: a busy Saturday
Subject: Tomorrow's plan: Sat Oct 11
Tomorrow is Saturday, October 11. Sam has a piano lesson at 9:30 AM, leaving by
9:03 AM, and the drive is 17 minutes. Alex's soccer game starts at 10:30 AM,
leaving by 9:52 AM, and the drive is 28 minutes. Sam's birthday party is at
2:00 PM, and the drive is not specified. Alex's game conflicts with Sam's piano lesson.
Second driver needed: Alex soccer game (10:30 AM)
LEAVE BY WHEN EVENT WHO WHERE DRIVE NOTES
9:03 AM 9:30 AM-10:15 AM Sam piano lesson Sam Maple Music Studio 17 min
9:52 AM 10:30 AM-11:30 AM Alex soccer game Alex Riverside Soccer Complex 28 min SECOND DRIVER; conflicts with Sam piano lesson
2:00 PM-4:00 PM Sam birthday party Sam Jumpin' Jungle unknown place: add this venue to family.yaml
The table is built by plain code. The model only writes the paragraph on top. Notice that the birthday party gets "unknown place" rather than a guessed drive time.
2. Forwarded coach email β events + invites
The forward, after unwrapping (Gmail, Outlook and forward-as-attachment all work):
FYI for Alex
Hi families,
Practice moves to next Tuesday at 5:30 pm on Field 3. Please bring water.
Saturday's game is at 9:00 am at Riverside.
Thanks,
Coach Rivera
The reply that comes back:
Subject: Re: Fall soccer schedule update
Found 2 events. Accept each invite to add it to the calendar:
- Tue Oct 14, 5:30 PM: Practice (Alex) at Riverside Soccer Complex
Notes: Please bring water
(no end time given; the invite assumes one hour)
- Sat Oct 11, 9:00 AM: Game at Riverside Soccer Complex
(no end time given; the invite assumes one hour)
Plus one .ics invite per event (METHOD:REQUEST, so Outlook shows Accept/Decline):
BEGIN:VEVENT
SUMMARY:Alex: Practice
DTSTART;TZID=America/Chicago:20251014T173000
DTEND;TZID=America/Chicago:20251014T183000
LOCATION:200 River Rd\, Springfield\, IL
DESCRIPTION:Please bring water
...
"Next Tuesday" resolved to Oct 14 because the coach sent the email on Mon Oct 6. The date is read from the forward header, not from when it was forwarded. "Field 3" matched a venue alias in family.yaml.
Code
Family Drive Planner
A Raspberry Pi service for the parent who drives the kids to most of their activities. It does two jobs:
- Nightly briefing. Every evening it emails both parents tomorrow's plan. For each event it shows where it is, when to leave and how long the drive takes. It also flags clashes and says which drives need the second parent.
- Email to events. Forward an activity email (from a coach, school, studio and so on) to a dedicated inbox. The Pi reads it, pulls out the events and replies with one calendar invite per event. Accepting an invite in Outlook adds the event to the shared Microsoft calendar, which syncs to the Skylight.
All AI runs locally on the Pi through Ollama with a small open-weight model. Kids' names, schedules and addresses never go to a hosted LLM.
The full spec is in .scratch/family-drive-planner/spec.md, and the workβ¦
make up, make pull, then make briefing / make extract FILE=.... In DRY_RUN=true mode, every email is written to out/ instead of sent, so you can try it with no accounts at all.
How I Built It
I used Claude code to ideate and then plan the app. We broke the plan down into idividual issues that can be tested and verified in small chunks.
-
Ollama in a Docker container next to the app, running a small open-weight instruct model (
qwen2.5:1.5b-instruct-q4_K_M, about 1 GB) that fits a Raspberry Pi 4 with 4 GB RAM. -
Python:
httpx,pydantic,icalendar+recurring-ical-eventsfor the published Outlook ICS feed,dateparser,apscheduler, and SQLite for the route cache and processed Message-IDs. - OpenRouteService for drive times.
The main design rule: keep the model's job small. A 1.5B model on a Pi is great at copying text out of an email and terrible at date math, so:
- For extraction, the model returns text spans only, like
"next Tuesday","5:30 pm"and"Field 3", as structured JSON. Code turns those into real dates relative to when the original email was sent, matches kids and venues against afamily.yaml, and validates everything. Anything that doesn't resolve goes into a "Couldn't read" section of the reply and is never guessed. - For the briefing, code computes leave-by times, conflicts and second-driver flags. The model only writes a 2β4 sentence summary. If Ollama is down or slow, or the summary mentions a time that isn't in the plan, the email goes out with just the table.
It isn't perfect yet, and the demo run shows it. On a different day the summary said "both drives need the second parent" when the table didn't flag that. My guard only checks times, so that claim slipped through. That's exactly why the table is the source of truth and the paragraph is a nice-to-have, and it's the next thing I'm tightening.
There's also a small eval harness (make eval): 12 labeled emails, per-field accuracy for date, time, kid and location, so I can compare candidate models on the Pi itself.
Status: this is a weekend build. Both flows work end to end in dry-run mode, and there are unit tests with no network calls and a frozen "today". Still on my list: verifying live Gmail IMAP/SMTP, checking that invites land correctly in Outlook, and running the model comparison on the Pi.
Why Does Open Innovation Matter?
This project handles my kids' names, their schedules and where they'll be at what time. That's exactly the data I don't want to send to a hosted LLM.
Running an open-weight model locally means:
- Nothing personal leaves the house except drive-time lookups by coordinates and the emails we were already sending.
- It costs nothing to run. The Pi is already on, and there's no per-token bill for parsing every coach's newsletter.
-
I can swap models.
make pull MODEL=...plusmake eval --model ...lets me compare a couple of 1B-class models on the real hardware and pick the one that's accurate and fast enough. - It's predictable. No API changes or deprecations under me. The model I tested is the model that runs.
A bigger closed model would probably extract more cleverly. But keeping the model's job small made a tiny local model good enough, and that trade is worth it for family data.
My Agent Session
I did not install the DevRelay skills before I started so I didnt capture all the session. I did write a summary of it here:
https://github.com/jsteinshouer/herding-cats/blob/main/docs/context/2026-10-03-initial-build.md
Did I hand it over?
I am finishing up the testing in dev and have not deployed it to the Pi yet so I havent handed it over yet. It is still a WIP but should be close to done.
Top comments (0)