generate_ha: one climate entity per physical device, fail closed when devices cannot be told apart

Found on a real 1312-GA house: a room with floor heating, a convector and an AC unit became
ONE climate (convector setpoint, AC controller mode, AC fan speed as the valve). Room 1.09
matched 2.09 and a central "09." GA, "Kids room 1" took "Kids room 2 temperature", and valves
came from light brightness and blind position statuses. The zone was the room alone: device
words were stripped as qualifiers, "А/С" vanishes in the tokenizer, the floor digit of "1.09"
is dropped.

Now a climate is assembled per room AND device type (floor, wall, radiator/convector, fan
coil, AC; RU/EN/DE words, removed from the zone as whole words):
- room code "N.NN" only at the start of a name, and it must match on both sides;
- standalone numbers of the anchor must be in a device member; a shared room sensor's
  numbers must be among the anchor's; digits in dotted codes and values do not count;
- an untyped GA is shared room data: any role if the room has one device, otherwise only
  the current temperature, which several devices may reuse; with a room code the room's
  untyped sensor qualifies even without the device's extra words;
- a word that sets another device of the room apart keeps its GAs away;
- control roles must be unambiguous: more than one candidate -> review climate_ambiguous;
- two mode GAs that cannot be told apart -> review climate_duplicate_anchor, not dropped;
- only 20.102 / 20.105 anchor; valve needs a valve word; AC never gets a valve;
- stable address order, so results never depend on parse order.

Two gates. Own audit on seven real projects, every changed entity inspected: no control GA
used by two climates, no address lost; house 16 -> 33 climates (one per device), manual
review 33 -> 12; flat 7 -> 9; HDL 3 -> 5; demo 6 -> 13; villa 23 -> 21 with the 3 it cannot
disambiguate sent to review instead of guessed. LLM council (three models + devil's
advocate, sanitised packet): accept with hardening; its P0s (same-type devices, silent
first pick) and P1s (mid-name codes, dotted digits, non-102/105 anchors, generic valve
words) are all in this commit, with tests E and F covering its failure catalogue.
Out of scope, recorded: identically named rooms of different flats without room codes, and
lexical identity in general -> device-channel identity ("lever 0").

Also works around a mypy 2.3.1 parse error on a comment right after a compound if.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nikolay Miroshnichenko
2026-09-16 19:20:49 +02:00
co-authored by Claude Opus 5
parent 76249469bb
commit a3f436fc74
4 changed files with 368 additions and 36 deletions
+19
View File
@@ -8,6 +8,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- **Climate entities no longer merge different devices of one room.** A room with floor heating, a
convector or radiator and an AC unit used to become a single Home Assistant climate: the convector's
setpoint, the AC's controller mode and the AC fan speed as the "valve". Valves were also taken from
unrelated 5.x statuses (light brightness, blind position), room "1.09" matched "2.09", and "Kids
room 1" took "Kids room 2 temperature". A climate is now assembled per room **and** device type
(floor / wall / radiator-convector / AC, detected in RU/EN/DE names): the room code ("1.09") and the
numbers in the anchor name must match, a shared untyped room sensor may serve every device in the
room as current temperature but never as a device's setpoint or mode, a valve needs a valve word and
an AC never gets one. Hardened after an LLM-council review: a word that sets another device of the
room apart ("shower", "hall") keeps its GAs away; if a control role still has more than one candidate
the device goes to review as `climate_ambiguous` instead of taking the first; two mode GAs that cannot
be told apart go to review as `climate_duplicate_anchor` instead of being dropped silently; room codes
only count at the start of a name ("21.5 °C", "16.10", "ДД 34.1" are not rooms); digits inside dotted
codes are not room numbers; only 20.102 and 20.105 start a climate; fan coils and VRV/VRF are their
own device types; valve words narrowed to valve / клапан / Stellwert. On seven real projects: no
control GA used by two climates, no address lost. A 1312-GA house goes from 16 climates (several
wrong) to 33 (one per device), manual climate review 33 -> 12; a 3646-GA villa keeps 21 of 23 and
sends the 3 it cannot disambiguate (three thermostats in one corridor, two identical AC mode GAs) to
review instead of guessing.
- **Home Assistant lights without `address` are no longer generated.** A 5.001 lighting GA with no
on/off GA in its zone used to become a light with only `brightness_address`, which Home Assistant
rejects (`address` is required on a KNX light). On six real projects 51 such lights were generated,