load_ga_export, entity naming, climate setpoint shift; fix suggest_repairs crash; CI runs every test

Three things found by auditing a TapPlan export, plus one regression found on the way.

load_ga_export(path): new tool. Reads an ETS ga-export/01 XML (ETS "Export Group
Addresses", or the import file a planning tool writes) into a project without
devices, through safe_fromstring, capped at 50 MB and 8 levels of range nesting.
Invalid or duplicate addresses and unknown DPT tokens go to import_warnings, never
silently. Round trip with our own generate_ets_xml is covered by a test.

Entity naming: lights, covers and climates are named after the common word prefix
of their member names, cutting only function words. The first version of the rule
turned "01. <room> - All Blinds - Move" into "01" on a real project, so anything not
in the function vocabulary now stays. On six real projects 214 of 1189 entities got
a shorter name, each rename reviewed, no address mapping changed. Regenerated
packages show different names; noted in the changelog.

Setpoint shift: 9.002 / 6.010 with a shift word maps to setpoint_shift_address,
setpoint_shift_state_address and setpoint_shift_mode (keys checked against the HA
KNX climate docs). It used to become a plain sensor.

Fixed: suggest_repairs raised UnboundLocalError on any project with a missing status
GA. My local rename in the 12.09 typing cleanup left two references to the old name.
test_council_fixes covers it but was not in CI, so it shipped. CI now runs all 21
test files instead of a hand-picked 10; the 11 added ones pass from a clean clone.

Verified: all 21 tests, ruff, mypy with the package installed, corpus guard no drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nikolay Miroshnichenko
2026-09-14 00:11:45 +02:00
co-authored by Claude Opus 5
parent 29bfba7684
commit ec63bcf106
8 changed files with 583 additions and 8 deletions
+33
View File
@@ -6,8 +6,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- **`load_ga_export(path)`: audit a group-address list without a `.knxproj`.** Reads an ETS
ga-export/01 XML (ETS "Export Group Addresses", or the import file a planning tool such as TapPlan
writes) into a project without devices: names, addresses, DPTs (`DPST-x-y`, `DPT-x`), descriptions,
the `Security` flag and the range tree. GA-level checks and both generators work on it; device,
Function and topology tools have nothing to read and say so. Parsed through the same hardened XML
reader as `.knxproj` members, size-capped at 50 MB. Invalid or duplicate addresses and unknown DPT
tokens are skipped with an entry in `import_warnings`, never silently.
- **Climate: setpoint shift.** A DPT 9.002 or 6.010 group address whose name says shift
(shift / Sollwertverschiebung / смещение / сдвиг) in a climate zone now maps to
`setpoint_shift_address` / `setpoint_shift_state_address` with `setpoint_shift_mode` `DPT9002` or
`DPT6010` taken from the DPT (keys per the Home Assistant KNX climate docs). It used to fall through
to a plain sensor.
### Changed
- **Multi-address entities are named after what their addresses share.** A light built from
"Kitchen Spots On-Off", "Kitchen Spots B.Value" and their feedbacks is now "Kitchen Spots", not
"Kitchen Spots B.Value". Lights, covers and climates take the longest common word prefix of their
member names, but only when it still carries an identity token and does not drop a word containing a
digit; two entities that would collide keep their original names. Found by auditing a TapPlan export.
Only function words are ever cut (value, brightness, up/down, mode, Яркость, Абсолютное диммирование,
Движение, Вкл…); a room, device type, channel or anything else stays. On the maintainer's six real
projects 214 of 1189 entities got a shorter name and no address mapping changed. **A regenerated
package will therefore show different entity names than one generated before** — review the diff
before replacing a package that is already deployed.
### Fixed
- **`suggest_repairs` crashed on any project with a missing status GA** (`UnboundLocalError` in
`repair.py`). Introduced by a local-variable rename in the 12.09 typing cleanup; the test that covers
it was not part of CI, which is why it shipped. CI now runs every test file in `tests/` (11 were
missing), not a hand-picked subset.
- **`suggest.py`: multi-output actuators are split by their vendor object marker.** Zennio-style
devices (Lumento DX4, MAXinBOX, KLIC-DI…) put every output of a device into ONE ETS channel and
separate them only in the object text (`[1] Switch On/Off`, `[2] On/Off (Status)`). The channel