Across locationsSmart or pointless?Built with Claude

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.

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

  1. Have tools built, not answers given. A script that measures is repeatable and checkable. An answer in a conversation is neither.
  2. Number the scripts in the order they were created. The number is the history, and you will need it in three months.
  3. Grant read access before you grant write access. And measure what is actually reachable instead of inferring it from the configuration.
  4. Decide the exposure yourself, domain by domain. Especially before a lock, a siren or a valve joins the setup.
  5. Demand the source, not the summary — and ask for the source’s date. Both cost one line and save you a wrong article.
  6. 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.