252 scripts later: what Claude could and could not do
Three flats, three instances, 252 tool scripts — built with Claude as a working partner. How the chain looks, and the one rule without which it does not hold.
Contents
What came out of three months can be counted:
| Flats | 3 |
| Home Assistant instances | 3 |
| Numbered tool scripts | 252 |
| Documentation files | one per topic, some over 1000 lines |
I built this with Claude as a working partner. What follows is the honest answer to the question of how you get something like that running — including the places where it went wrong.
First step: Claude has to see the installation
There are two routes, and they do different things.
Scripts over the interface. The normal way. Each script does exactly one thing and is repeatable. The number in front is not a filing system but the order of creation:
10_verbindung.py first: does the instance answer at all
13_ssh.py then: access to the file system
43_reservierungen.py much later: check fixed addresses
Read the numbers and you read the history of the house. That sounds like bookkeeping and is the most useful side effect of the whole exercise.
MCP — the direct line. Set up on 28.08.2026. With it Claude talks to the instance straight away instead of reaching for a script every time:
Claude ──stdio──▶ proxy ──SSE──▶ Home Assistant ──▶ Assist
Verified with a test script: 21 tools, among them turning things on, setting a light, setting a target temperature, and querying the current state.
Three things it hinges on
None of this is in a guide, and all three cost time.
The name has to be unique. Merano was already registered under
home-assistant. Vienna therefore got a name of its own so both can exist side
by side. With one flat that is irrelevant; with three it is not.
A version conflict that looks like a program bug. The proxy does not get along with the new major version of the underlying library:
ImportError: cannot import name 'request_ctx'
The fix is not downgrading the global installation but a separate, walled-off environment with the matching version. The same combination now runs in two flats.
The token belongs in the environment, not on the command line. The proxy reads it from there and builds the authorisation header out of it. That way it appears in no process list — a difference you only notice when somebody else is sitting at the machine.
What Claude may see is your decision
This is the part I consider most important. The reach is determined solely by the exposure setting in Home Assistant, not by Claude.
At setup time, 117 entities were exposed in Vienna:
| Count | Kind |
|---|---|
| 45 | scenes |
| 26 | lights |
| 25 | sensors |
| 12 | climate |
| 4 | switches |
| 5 | other |
Sirens and cameras are not among them — Home Assistant does not expose those by itself.
And one point I deliberately left standing and wrote down: the switch “expose new entities automatically” was on in Vienna. Add a door lock later and it would immediately be switchable by voice and by Claude — locks are among the domains Home Assistant exposes on its own.
In Merano that switch is explicitly off, for exactly that reason. In Vienna it stayed on because there is no lock in the house. That decision belongs before the first lock, not after it.
An assistant’s reach is not a property of the assistant. It is a setting in your house.
And now the honest part
Three kinds of error occurred in this project, all documented, all expensive.
The summary instead of the source
The worst one. The project contained a summarising strategy document, and the work was done from it — convenient, because it had everything in one place.
Measured afterwards: 18 files totalling 20,199 lines were newer in content than that summary. Three of the first three articles on this blog contained factual errors as a result. One described a project as ongoing that had been finished for weeks.
The countermeasure now sits in the front matter of every article: a field with source file and check date. Plus a generated index that lists which documents are never cited at all.
The return value instead of the state
An installation script reported an abort with an empty error message. The add-on was installed anyway — the interface reports an error as soon as the answer is not JSON.
Since then the rule in all three flats is: after a writing call, measure the state. More on that in The first measurement came before the first integration.
The pattern from two data points
Two network sweeps, 14 of 24 devices on new addresses, most of them by exactly
+2. A pattern, cleanly written down — and collapsed at the third data point,
because one device was back where it had started. The whole story in
For thirteen hours the shut-off hung on the cloud.
All three errors have the same shape: something plausible was taken for something measured. An AI does that readily, because plausibility is its business. So do I — it is just faster at it.
The one rule
The same sentence stands in all three project files:
Measured, not assumed. Where an assumption was disproved by a later measurement, both appear in the documentation — in the order in which it happened.
The second half is the more important one, and it is not a stylistic flourish.
Roughly half the findings on this blog come out of the contradiction between an earlier assumption and a later measurement. Overwrite the assumption while correcting it and you keep a correct result and lose the finding.
You also lose the trail on which you find the next one: three times in this project I made the same thinking error before I noticed it was the same one. That only became visible because all three were still standing next to each other.
What actually helps
- Have tools built, not answers given. A script that measures is repeatable and checkable. An answer in a conversation is neither.
- Number the scripts in the order they were created. The number is the history, and you will need it in three months.
- Grant read access before you grant write access. And measure what is actually reachable instead of inferring it from the configuration.
- Decide the exposure yourself, domain by domain. Especially before a lock, a siren or a valve joins the setup.
- Demand the source, not the summary — and ask for the source’s date. Both cost one line and save you a wrong article.
- Let yourself be contradicted. The most useful sentence in this project did not come from Claude but from me: “an integration was overlooked there.” That brought to light a 2270-line file that had never been cited.
So what?
Claude was fast at: reading a thousand lines of documentation, writing a measurement script, comparing three installations with each other, carrying a text into three languages.
It was unreliable at: knowing what is current. And at distinguishing what it measured from what it concluded.
Out of that came a division of labour I now consider right: Claude proposes and builds. The house decides. Every claim on this blog that matters has a measurement under it — and where there is none, it says so.
The question is not whether an AI can build a home automation system. It can. The question is whether you notice when it is wrong.
There is exactly one remedy for that, and it is the same one as without an AI: measure.
If you want to follow this: use RSS
No newsletter. You give me no address, I keep no list, and nobody has to agree to anything to learn that an article is finished. RSS is the older and, for both sides, the more honest solution.
How it works, and why no newsletter
- 1Get a feed reader. No account needed with an app on your phone or a browser extension; use a hosted service if you prefer.
- 2Add the address above to it.
- 3Done. New articles turn up in your reader by themselves — unread stays unread, and nothing lands between invoices and ads in your inbox.
Why that is better: your reader fetches the file. I get no address and cannot see who reads or whether anyone opened it. Unsubscribing means deleting the entry — no link, no confirmation mail, no me.
