In my three-part series I showed you the gates, the alarm and the car, the floor heating war and the CO₂ diagnosis, and the boring automations that actually protect the place.
What I didn't tell you is how it got built.
Here's the confession: I barely typed any of that YAML myself. Almost every line of it (the automations, the dashboard, the animated cards, the scripts that pair and rename devices) was written by Claude Code, running inside the Home Assistant box itself. I talk to it in Portuguese, usually from my phone. It edits /config, validates, reloads, checks that the thing actually worked, and writes down whatever bit it along the way.
So this one is the behind-the-scenes: how it's wired, the skills it leans on, how the dashboard went from YAML to code, where the animations came from, how you pair devices when your assistant has no hands, and then a tour of every page of the dashboard, straight from my phone.
| Since June 2026 | |
|---|---|
| Commits | 580 |
| Automations · scripts | 129 · 73 |
| Devices · entities | 147 · 1,163 |
| Dashboard | 10 tabs, 60 pop-ups, generated by 7,600 lines of Python |
| Home-made cards | 17 custom elements, no libraries |
| Notes it wrote for itself | 25 files, ~105,000 words |
Step zero: Claude Code lives in an add-on
The house runs on a Home Assistant Green with Home Assistant OS, which means you can't just go and install things on the host. What you can do is use the Advanced SSH & Web Terminal add-on: a small Alpine container with /config mounted, the ha CLI and a Supervisor token. Turns out that's all it needs.
Of course there's a catch (there always is): add-on containers get rebuilt. Anything you install with npm, apk or pip is gone after an add-on restart, and your login goes with it. The fix lives in the add-on's own configuration: reinstall on every boot, and keep Claude's folder inside /config, which survives everything.
# Settings → Add-ons → Advanced SSH & Web Terminal → Configuration
packages:
- nodejs
- npm
- git
init_commands:
- mkdir -p /config/.claude-box
- ln -sfn /config/.claude-box /root/.claude
- "[ -f /config/.claude-box/claude.json ] || echo '{\"hasCompletedOnboarding\": true}' > /config/.claude-box/claude.json"
- ln -sf /config/.claude-box/claude.json /root/.claude.json
- npm i -g @anthropic-ai/claude-code >/dev/null 2>&1 || true
Basically, every boot puts Node and Claude Code back, and the symlinks point Claude at a folder that never goes away. Login, settings, memory and skills all live in /config/.claude-box (which is in .gitignore, obviously). I open Terminal from the Home Assistant sidebar, on the laptop or on the phone, type claude, and I'm in.
From there it reaches Home Assistant through four doors:
-
Files.
/configis the git repository. What's in the repo is what runs. -
The
haCLI.ha core checkbefore anything, reload when possible, restart only when it has to. -
The websocket. Dashboards, the entity registry, template rendering. The client is
tools/haws.py, a pure-stdlib websocket client. It wrote that one after the first tooling, built onaiohttp, stopped running the day the add-on restarted (remember the catch?). Same reason every tool in the repo is stdlib only. - MCP. Home Assistant's official MCP Server integration, for live state.
The secret is what it reads first
Every session starts by loading CLAUDE.md, the rules that apply to the whole house. Each rule in there cost at least one diagnosis, and the file writes down what it cost. A couple of my favourites (the first one still stings):
condition:insidethen:/choose:does NOT stop the script — it only aborts that block, and the next action runs anyway. Guards that must stop everything go at the top level, or usestop:. This cost 5 alarm disarms with the house empty.Prefer reload. Every restart knocks the Govee lamps offline for a couple of minutes.
The details live in docs/notas/: 25 notes, one per subject (alarm, floor heating, TV, Zigbee, energy, irrigation…), all written by Claude as it went. The rule is simple: "before touching a subject, read its note". That's why a session in November doesn't repeat the mistakes a session made in August.
On top of that, Claude Code keeps its own memory: 47 small files about how I work. Some of them exist only because I got annoyed:
- Always Brazilian Portuguese, never European. It kept writing telemóvel instead of celular.
- Information is not an edit request. I once explained how the wall thermostats talk to each other, just so it would understand the house, and it went ahead and edited two packages. Now, when I explain something, it takes it in and stops there.
- Visual is decided by looking. More on that below.
Step 7 is the one that compounds: a trap found today becomes a line in a note, and the next session starts already knowing it.
The skills
Skills are packaged instructions that Claude loads when the task matches. These are the ones that did the heavy lifting:
-
home-assistant-manager by komal-SkyNET — the deploy discipline: edit →
ha core check→ reload vs restart → prove it with logs, traces and entity state. By far the most used one (85 times and counting). It assumes you SSH in from outside, so myCLAUDE.mdoverrides that part: we're already inside. - artifact-design, dataviz and artifact-diagramming — when I say "make it prettier", Claude doesn't just apply its best guess anymore. It builds a private page with 2–4 options side by side, using the real buttons, and I pick on my phone ("I like B"). It learned that one the hard way: a palette tuned "by the numbers" got an "I don't see any significant changes" from me.
-
security-review — an audit of pending changes before they go live. Security became a habit after that: the latest sweep closed the MQTT ports on the local network, removed an old HTML page that was publicly reachable under
/local/(oops), and made failed logins and IP bans notify my phone.
The dashboard is code now
The dashboard is called Prisma. 10 tabs, 60 pop-ups, and nobody edits it through the UI. It's generated by tools/build_prisma_dashboard.py (about 7,600 lines, with the reasoning behind each choice written as comments) and pushed straight into Home Assistant over the websocket. No restart needed.
python3 tools/conferir_prisma.py # does the generator still match what is live? exits 1 with a diff
python3 tools/build_prisma_dashboard.py # rebuilds the whole dashboard
cd i18n && python3 gerar.py # Spanish version, must print "sem tradução: 0"
python3 publicar.py # pushes them all over the websocket
Why the first line? Because a UI edit got silently wiped by the generator five times before we added the check.
The same dashboard also comes out in Spanish and English, generated from a dictionary of segments. The screenshots below are from the English one, which is why a few live states still show up in Portuguese: the generator translates the cards, never an entity's state.
The building blocks are Bubble Card (the pop-ups), Mushroom, navbar-card (the bottom bar), kiosk-mode (no header), a custom theme with a light/dark toggle, and the cards it wrote from scratch. Which brings me to the fun part.
Animations: drawing what nobody had drawn
The Marketplace has animated cards for washing machines, weather, energy and fans. Something that draws a TV, a speaker or a PC? Nothing. So Claude wrote casa-media-cards.js: 17 custom elements in ~4,800 lines of plain JavaScript. No library, no network calls.
The TV lights up when it turns on, takes the colour of the active input and does a slow sweep:
The PC opens its screen like an old CRT and collapses it back on power off. The trick is that the SVG is never rebuilt, only a class flips, so the CSS transition runs both ways (rebuild it, and the power-off turns into a hard cut):
The Music Frame (a speaker shaped like a picture frame) shows the album art inside a wooden frame, with a 7-bar equaliser whose height follows the volume:
The washer and the dishwasher use the community washing-machine-card, patched by Claude so the ring counts from the real start of the cycle and shows time as 1h47. Every Marketplace update overwrites that patch, so an automation quietly re-applies it.
(These GIFs were recorded in a headless browser, with the states simulated in that browser tab only. My actual TV stayed off the whole time.)
And a few smaller touches, because I wanted the dashboard to feel more alive: every button gets a soft gradient in its icon's colour, and the gate buttons vibrate on tap and again when the building's API answers, ending in Opened or Didn't open. The walking sequences count down live to the next gate.
You might be asking: how does an AI even test an animation? Two ways. The card bundle runs under Node with linkedom, so it can call setConfig() with the real config and read back what the card computed. And for the real thing, it starts a headless Chromium, logs into the actual frontend and takes screenshots. That's exactly how every image in this post was made.
Pairing devices when your AI has no hands
I press the button on the device. Claude does everything else.
The CO₂ sensors (Matter). I first added the AirGuard to the SmartThings app, and it showed up in Home Assistant through the Samsung cloud. Claude didn't like that one bit: this sensor feeds the living-room floor heating, so if the cloud is down, the room has no thermometer. It found Matter disabled by configuration (integration ignored, no Matter Server add-on), installed the add-on, and walked me through Matter multi-admin: the SmartThings app gives you a sharing code (valid for 15 minutes, with the screen open), and Home Assistant joins as a second "fabric". The device was already on Wi-Fi, so it was commissioned over IP. And that matters, because the Green has no Bluetooth at all.
Zigbee. The rule here is rename on pairing day. Factory names get truncated at 32 characters, and renaming a device doesn't rename its entities, so Claude renames both through the registry, following a naming scheme the dashboard scans for. Name a new thermometer termometro_<room> → it shows up by itself on the Sensors tab, in the daily battery report and on the Home card. No dashboard edit.
The pipe probes on the heat-pump manifold. Before they even arrived, it wrote me a mounting guide with photos, as a private page. Once they were installed, I sent photos back and it confirmed which one was supply and which was return. It also pushed back (correctly!) on an outside analysis saying one probe was loose: the other one was simply still warm from my hand, and it cooled down 8 minutes later.
Also paired this way: door contacts and a smoke detector (Zigbee), a Zigbee irrigation valve, the kitchen roller blind, the Shelly relays for the shutters and the floor heating, and a Samsung SmartTag on my keys, through a community fork of SmartThings Find that it had to patch too.
Every scenario I could think of
Parts 1–3 go deep on most of these. Here they are in one table:
| Scenario | What happens | The trap it found |
|---|---|---|
| Arriving home | The alarm disarms through home Wi-Fi, a gate + presence, or GPS with the approach seen | Bare GPS once disarmed it with nobody arriving |
| Leaving | The alarm arms and the lamps turn off | Presence needs person.*, not device_tracker.*: its last_changed is rewritten on every GPS ping |
| Night | Lights come on if someone gets up, and a sweep after midnight turns off whatever was forgotten | A motion trigger must be from: "off", or a sensor coming back from unavailable fires the alarm |
| Floor heating | Home Assistant can only subtract: a Shelly cuts the demand wire | The wall thermostat always wins, so HA detects when it's being overruled |
| Appliances | Each wash cycle is rebuilt by hand: duration, energy, cost | Samsung reports lifetime kWh and Bosch reports none |
| Irrigation | Watering time follows evapotranspiration, and it skips when it rains | The valve closes by itself at 10 minutes |
| Laundry & awning | One tap: a camera photo + Gemini answer "is there laundry outside, and is it going to rain?" | The camera stays silent while its motion detection is off, so the script turns it on just for the photo |
| Commute | Metro line 3 status on the Home tab, weekdays 07:30–08:30 | No API, so it reads the website, which blocks bots unless you look like a browser |
| Bills | Electricity bills import themselves; water comes in through a bookmarklet | The captcha is mine, the rest belongs to the house |
| Voice | Assist runs on Gemini | My Claude subscription doesn't cover API calls, and the Google key was already paid for |
And the best bug of all: "certificate not trusted" on my phone over 4G, only at certain times, while Wi-Fi was fine. Looked like a Home Assistant problem. It wasn't. My ISP intercepts Cloudflare IP ranges during football match windows, with a fake certificate. Yes, football. The fix is a DNS rewrite on the phone pointing to a different Cloudflare edge. And the rule it saved in memory: write down the time before you start theorizing.
The tour: every page, from my phone
Expenses, camera images, maps and faces are blurred. Everything else is exactly what I see.
Home
Weather, presence, alarm, climate, lights, blinds, TV & music, PC, appliances and sensors. Each section opens its own pop-up.
Climate
The three thermostats with their presets, the water temperature in the manifold, the air in each room, and the controls.
Access
Every gate in the building, the walking sequences with their stopwatch-tuned timings, the garage, and the common areas. The gates run on Alfred Smart, which has no official Home Assistant integration (part 1 has the reverse-engineering story). So I built one: ha-alfred-smart, installable from HACS as a custom repository. It discovers every gate, garage and door your account can use, gives each one an Open button and a Last opening sensor, and it never retries an opening. Why not? Because no answer from Alfred doesn't mean the gate stayed shut. If your building runs on Alfred Smart too, give it a try.
Bookings and common areas
Gym and Agora Club slots, booked with one tap, and the doors to the shared spaces.
Cameras
The Ring cameras, with motion detection that follows the alarm. The live images are blurred, for obvious reasons.
Car
Fuel, range, doors and windows, lock and unlock, health warnings and the map (blurred, of course).
Bills
Electricity, the building's heat pump and water, month by month. You get the shape, not the numbers.
Sensors
Leaks, smoke, temperature, CO₂, humidity and every battery in the house.
Irrigation
Today's run, the schedule, and the "adjust to the weather" pop-up with the evapotranspiration math.
Pop-ups
The drawn blind that follows the real position, the weather, the metro line and my keys:
The TV remote, the Music Frame, the washer and the dishwasher:
The heating presets, who is in charge right now, air quality and batteries:
Presence, the alarm, the lights and the car doors:
If you want to try this
-
Put it inside the box. As an add-on it runs
ha core check, reads the logs and reloads by itself. No SSH juggling, no copying files around. -
Make
/configa git repository before the first prompt. Every change becomes a diff you can read and revert. -
Write the rules down, and let it write the notes.
CLAUDE.mdfor what applies everywhere, one note per subject for the rest. - Demand proof, not "it should work". Real state, the automation trace, the raw log. My favourite rule in the whole file: don't say "this can't be fixed" before reading the raw log.
- When the dashboard gets big, generate it. UI edits and a 60-pop-up dashboard don't mix (five wiped edits taught me that).
- Decide visuals by looking, on the device you actually use.
-
Keep
secrets.yamloff-limits. Mine is, by rule.
Funny thing is, for years my day job has been turning manual flows into something that runs and proves itself. Here I ended up on the other side of the table: I'm the one who knows how the house should behave, and Claude is the engineer who builds it, tests it and keeps a log of every bug it ever met.
The house is still a system under test. It just has a very fast pair programmer living inside it.
Running Home Assistant Green, YAML-first (well, Claude-first), with a dashboard built on Bubble Card, Mushroom and 17 cards of my own. What about you? Would you let an AI live inside your Home Assistant box, or is that a hard no? Leave me a comment, and ask me anything about the setup. I would love to hear!




















Top comments (0)