{"slug": "herding-cats-a-raspberry-pi-that-plans-the-school-activity-drives-with-a-local", "title": "Herding Cats: a Raspberry Pi that plans the school-activity drives, with a local LLM", "summary": "A developer built Herding Cats, a Raspberry Pi service that uses a local Ollama model (qwen2.5:1.5b-instruct) to parse forwarded coach and school emails into calendar invites and to plan family drive logistics, keeping children's names, schedules and addresses off hosted LLMs. The system generates .ics invites with METHOD:REQUEST, resolves relative dates like \"next Tuesday\" from the forward header, and matches venue aliases from a family.yaml config, while deterministic code builds the drive-time table and the model only writes the summary paragraph. In the demo run, drive times came from a straight-line-distance stub rather than OpenRouteService, and an unrecognized venue was flagged as \"unknown place\" instead of guessing a time.", "body_md": "*This is a submission for the [Hacktoberfest Weekend Challenge: Build for a Friend](https://dev.to/challenges/hacktoberfest-weekend-2026-10-01)*\n\nIn 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?\n\nOn 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.\n\nThe goal of this is to ease the burden by having AI help ease the burden of updating the calendar and working out transportation logistics.\n\n**Herding Cats** (the Family Drive Planner) is a small service on a Raspberry Pi that does two jobs:\n\nThere's no app to install and no new habit to learn. It works with email and the calendar we already use.\n\nThis 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.\n\n```\nSubject: Tomorrow's plan: Sat Oct 11\n\nTomorrow is Saturday, October 11. Sam has a piano lesson at 9:30 AM, leaving by\n9:03 AM, and the drive is 17 minutes. Alex's soccer game starts at 10:30 AM,\nleaving by 9:52 AM, and the drive is 28 minutes. Sam's birthday party is at\n2:00 PM, and the drive is not specified. Alex's game conflicts with Sam's piano lesson.\n\nSecond driver needed: Alex soccer game (10:30 AM)\n\nLEAVE BY  WHEN               EVENT               WHO   WHERE                     DRIVE   NOTES\n9:03 AM   9:30 AM-10:15 AM   Sam piano lesson    Sam   Maple Music Studio        17 min\n9:52 AM   10:30 AM-11:30 AM  Alex soccer game    Alex  Riverside Soccer Complex  28 min  SECOND DRIVER; conflicts with Sam piano lesson\n          2:00 PM-4:00 PM    Sam birthday party  Sam   Jumpin' Jungle                    unknown place: add this venue to family.yaml\n```\n\nThe 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.\n\nThe forward, after unwrapping (Gmail, Outlook and forward-as-attachment all work):\n\n```\nFYI for Alex\n\nHi families,\n\nPractice moves to next Tuesday at 5:30 pm on Field 3. Please bring water.\nSaturday's game is at 9:00 am at Riverside.\n\nThanks,\nCoach Rivera\n```\n\nThe reply that comes back:\n\n```\nSubject: Re: Fall soccer schedule update\n\nFound 2 events. Accept each invite to add it to the calendar:\n\n- Tue Oct 14, 5:30 PM: Practice (Alex) at Riverside Soccer Complex\n  Notes: Please bring water\n  (no end time given; the invite assumes one hour)\n- Sat Oct 11, 9:00 AM: Game at Riverside Soccer Complex\n  (no end time given; the invite assumes one hour)\n```\n\nPlus one `.ics` invite per event (`METHOD:REQUEST`, so Outlook shows Accept/Decline):\n\n```\nBEGIN:VEVENT\nSUMMARY:Alex: Practice\nDTSTART;TZID=America/Chicago:20251014T173000\nDTEND;TZID=America/Chicago:20251014T183000\nLOCATION:200 River Rd\\, Springfield\\, IL\nDESCRIPTION:Please bring water\n...\n```\n\n\"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`.\n\nA Raspberry Pi service for the parent who drives the kids to most of their activities. It does two jobs:\n\nAll AI runs locally on the Pi through [Ollama](https://ollama.com) with a small open-weight model. Kids' names, schedules and addresses never go to a hosted LLM.\n\nThe full spec is in [`.scratch/family-drive-planner/spec.md`](https://github.com/jsteinshouer/herding-cats/.scratch/family-drive-planner/spec.md), and the work…\n\n`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.\n\nI 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.\n\n`qwen2.5:1.5b-instruct-q4_K_M`, about 1 GB) that fits a `httpx`, `pydantic`, `icalendar` + `recurring-ical-events` for the published Outlook ICS feed, `dateparser`, `apscheduler`, and SQLite for the route cache and processed Message-IDs.\nThe 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:\n\n`\"next Tuesday\"`, `\"5:30 pm\"` and `\"Field 3\"`, as structured JSON. Code turns those into real dates relative to when the `family.yaml`, and validates everything. Anything that doesn't resolve goes into a \"Couldn't read\" section of the reply and is never guessed.\nIt 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.\n\nThere'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.\n\n**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.\n\nThis 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.\n\nRunning an open-weight model locally means:\n\n`make pull MODEL=...` plus `make eval --model ...` lets me compare a couple of 1B-class models on the real hardware and pick the one that's accurate 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.\n\nI did not install the DevRelay skills before I started so I didnt capture all the session. I did write a summary of it here:\n\n[https://github.com/jsteinshouer/herding-cats/blob/main/docs/context/2026-10-03-initial-build.md](https://github.com/jsteinshouer/herding-cats/blob/main/docs/context/2026-10-03-initial-build.md)\n\nI 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.", "url": "https://wpnews.pro/news/herding-cats-a-raspberry-pi-that-plans-the-school-activity-drives-with-a-local", "canonical_source": "https://dev.to/jsteinshouer/herding-cats-a-raspberry-pi-that-plans-the-school-activity-drives-with-a-local-llm-370d", "published_at": "2026-10-05 02:32:45+00:00", "updated_at": "2026-10-05 02:42:38.659809+00:00", "lang": "en", "topics": ["large-language-models", "ai-agents", "ai-tools", "developer-tools"], "entities": ["Herding Cats", "Raspberry Pi", "Ollama", "qwen2.5:1.5b-instruct", "OpenRouteService", "GitHub", "Gmail", "Outlook"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/herding-cats-a-raspberry-pi-that-plans-the-school-activity-drives-with-a-local", "markdown": "https://wpnews.pro/news/herding-cats-a-raspberry-pi-that-plans-the-school-activity-drives-with-a-local.md", "text": "https://wpnews.pro/news/herding-cats-a-raspberry-pi-that-plans-the-school-activity-drives-with-a-local.txt", "jsonld": "https://wpnews.pro/news/herding-cats-a-raspberry-pi-that-plans-the-school-activity-drives-with-a-local.jsonld"}}