mirror of
https://github.com/NickoScope/nickol-knx-mcp.git
synced 2026-09-29 19:31:12 +02:00
generate_ha: on/off lighting becomes a light; brightness-only "lights" go to review instead of invalid YAML
Found on a real 1312-GA house, measured on all seven corpus projects.
B. Lighting on/off (1.001, lighting category) is emitted as a Home Assistant light with
address + state_address, not a switch. The HA KNX light platform documents exactly
that ("Simple light"). suggest.py already proposed light for these; the two engines
disagreed. Without status it is reported as light_without_status.
D. A 5.001 lighting GA with no on/off GA in its zone produced a light with only
brightness_address. Home Assistant requires address on a KNX light, so that YAML was
invalid. 51 of them across six projects, mostly motion-detector parameters on 5.001.
They now go to review as light_without_switch, and the brightness status is no longer
consumed for an entity that does not exist, so it surfaces as not_mapped.
Corpus, before -> after: lights without address 51 -> 0 everywhere; no address lost from
YAML or review on any project; switch -> light moves of 220 (house), 205, 62, 43, 17, 10,
13. test_pipeline B1 fixture gains its on/off GAs (it encoded an invalid light) plus a
test for the review route. CHANGELOG also records fix A from 481bab0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
481bab0e0c
commit
cb226a008f
+17
-2
@@ -398,21 +398,36 @@ assert _e["color_temperature_mode"] == "absolute", _e
|
||||
print("OK: RGBW + colour-temp assembled into one light entity")
|
||||
|
||||
# -- B1: a light with no own brightness status must NOT borrow a sibling's -----
|
||||
# (both lights carry their on/off GA: Home Assistant requires `address` on a light,
|
||||
# so a brightness-only light is invalid and goes to review — checked just below)
|
||||
_rawB = {"group_addresses": {
|
||||
"1/0/11": ga("1/0/11", "Kitchen worktop LED on/off", 1, 1),
|
||||
"1/2/11": ga("1/2/11", "Kitchen worktop LED brightness", 5, 1), # no own status
|
||||
"1/0/12": ga("1/0/12", "Kitchen island pendants on/off", 1, 1),
|
||||
"1/2/12": ga("1/2/12", "Kitchen island pendants brightness", 5, 1),
|
||||
"1/5/12": ga("1/5/12", "Kitchen island pendants brightness status", 5, 1),
|
||||
}}
|
||||
_pB = build_loaded_from_raw(_rawB, "mem")
|
||||
_lB = _yaml.safe_load(generate_ha_yaml(_pB)["yaml"].split("\n\n", 1)[1])["knx"]["light"]
|
||||
_byname = {l["name"]: l for l in _lB}
|
||||
_worktop = _byname["Kitchen worktop LED brightness"]
|
||||
_island = _byname["Kitchen island pendants brightness"]
|
||||
_worktop = _byname["Kitchen worktop LED"]
|
||||
_island = _byname["Kitchen island pendants"]
|
||||
assert "brightness_state_address" not in _worktop, \
|
||||
f"B1 regression: worktop borrowed a status: {_worktop}"
|
||||
assert _island.get("brightness_state_address") == "1/5/12", _island
|
||||
assert all("address" in l for l in _lB), _lB
|
||||
print("OK: B1 — worktop took no status; island kept its own (no cross-borrow)")
|
||||
|
||||
# -- a brightness GA with no on/off GA is not a valid HA light -> review ------
|
||||
_rawD = {"group_addresses": {
|
||||
"1/2/20": ga("1/2/20", "Kitchen worktop LED brightness", 5, 1),
|
||||
}}
|
||||
_haD = generate_ha_yaml(build_loaded_from_raw(_rawD, "mem"))
|
||||
_pkgD = _yaml.safe_load(_haD["yaml"].split("\n\n", 1)[1]) or {}
|
||||
assert not (_pkgD.get("knx") or {}).get("light"), _pkgD
|
||||
assert any(r["reason"] == "light_without_switch" and r["address"] == "1/2/20" for r in _haD["review"]), _haD["review"]
|
||||
print("OK: brightness-only GA -> review light_without_switch, no invalid light in YAML")
|
||||
|
||||
# -- A-climate: thermostat zone assembles; a mode-only zone goes to review -----
|
||||
_rawT = {"group_addresses": {
|
||||
# full zone -> valid climate
|
||||
|
||||
@@ -9,6 +9,9 @@ A. subdpt_suspect false positives
|
||||
"electricity" name. (Checked against xknx 3.20.)
|
||||
- real findings survive: a bare DPT 9 named "lux threshold", a 5.x named
|
||||
"temperature control".
|
||||
B. on/off lighting (1.001, lighting category) is emitted as a Home Assistant light
|
||||
(address + state_address), not a switch. A brightness-only light, which HA would
|
||||
reject for lacking `address`, goes to review (tested in test_pipeline.py).
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
@@ -58,4 +61,19 @@ missed = [a for a, *_ in real_findings if a not in flagged]
|
||||
assert not missed, f"real findings no longer flagged: {missed}"
|
||||
print(f"OK: A — {len(false_positives)} false positives silent, {len(real_findings)} real findings still flagged")
|
||||
|
||||
# ---------------------------------------------------------------- B. on/off lighting -> light
|
||||
import yaml
|
||||
from nickol_knx_mcp.generate_ha import generate_ha_yaml
|
||||
|
||||
pB = _project([
|
||||
("1/0/1", "1.05 Bedroom - Ceiling - on/off", 1, 1),
|
||||
("1/1/1", "1.05 Bedroom - Ceiling - on/off status", 1, 11),
|
||||
("5/0/1", "Garden pump - on/off", 1, 1),
|
||||
])
|
||||
knxB = yaml.safe_load(generate_ha_yaml(pB)["yaml"].split("\n\n", 1)[1])["knx"]
|
||||
lightsB = {l["address"]: l for l in knxB.get("light", [])}
|
||||
assert "1/0/1" in lightsB and lightsB["1/0/1"].get("state_address") == "1/1/1", knxB
|
||||
assert not any(s["address"] == "1/0/1" for s in knxB.get("switch", [])), knxB
|
||||
print("OK: B — on/off lighting is a light with address + state_address, not a switch")
|
||||
|
||||
print("\nALL REAL-HOUSE REGRESSION TESTS PASSED")
|
||||
|
||||
Reference in New Issue
Block a user