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
@@ -413,11 +413,6 @@ def generate_ha_yaml(project: LoadedProject) -> dict[str, Any]:
|
||||
if ga.address in consumed:
|
||||
continue
|
||||
if ga.category == "lighting" and ga.dpt_main == 5 and ga.kind == "command":
|
||||
entity = {"name": ga.name, "brightness_address": ga.address}
|
||||
st5 = status_for_dpt(ga, 5) # brightness status (5.x)
|
||||
if st5:
|
||||
entity["brightness_state_address"] = st5.address
|
||||
consumed.add(st5.address)
|
||||
# two-pass sibling pick: EXACT identity first, subset only as a
|
||||
# fallback — else "RGBW подсветка яркость" ({living, подсветка})
|
||||
# grabs "камин подсветка" ({living, камин, подсветка}) by subset
|
||||
@@ -428,6 +423,23 @@ def generate_ha_yaml(project: LoadedProject) -> dict[str, Any]:
|
||||
my_ident = _pair_ident(ga.name)
|
||||
sib = next((s for s in sibs if _pair_ident(s.name) == my_ident), None) \
|
||||
or next((s for s in sibs if _identity_match(ga.name, s.name)), None)
|
||||
if sib is None:
|
||||
# Home Assistant requires `address` on a KNX light, so a brightness GA
|
||||
# with no on/off GA in its zone would be invalid YAML. On real projects
|
||||
# these are mostly device parameters on 5.001 (motion-detector
|
||||
# sensitivity, "daytime command"), not lights. Fail closed.
|
||||
review.append({"reason": "light_without_switch", "address": ga.address,
|
||||
"name": ga.name, "dpt": ga.dpt,
|
||||
"hint": "5.001 lighting GA with no on/off GA in its zone. A Home "
|
||||
"Assistant light needs `address`; attach the on/off GA "
|
||||
"manually, or it is a device parameter, not a light."})
|
||||
consumed.add(ga.address)
|
||||
continue
|
||||
entity = {"name": ga.name, "brightness_address": ga.address}
|
||||
st5 = status_for_dpt(ga, 5) # brightness status (5.x)
|
||||
if st5:
|
||||
entity["brightness_state_address"] = st5.address
|
||||
consumed.add(st5.address)
|
||||
if sib is not None:
|
||||
entity["address"] = sib.address
|
||||
s1 = status_for_dpt(sib, 1) # on/off status (1.x)
|
||||
@@ -466,7 +478,7 @@ def generate_ha_yaml(project: LoadedProject) -> dict[str, Any]:
|
||||
attach_colour(entity, ga)
|
||||
lights.append(entity)
|
||||
|
||||
# ---- 3. SWITCHES (1.001 command) ----
|
||||
# ---- 3. ON/OFF LIGHTS and SWITCHES (1.001 command) ----
|
||||
for ga in project.gas.values():
|
||||
if ga.address in consumed:
|
||||
continue
|
||||
@@ -477,14 +489,19 @@ def generate_ha_yaml(project: LoadedProject) -> dict[str, Any]:
|
||||
consumed.add(ga.address)
|
||||
continue
|
||||
entity = {"name": ga.name, "address": ga.address}
|
||||
# Lighting on/off is a light in Home Assistant, not a switch: the KNX light
|
||||
# platform takes a plain on/off light with `address` + `state_address`
|
||||
# ("Simple light" in the HA KNX docs), and light entities are what Assist
|
||||
# and "all lights" targeting act on.
|
||||
is_light = ga.category == "lighting"
|
||||
st = status_for_dpt(ga, 1)
|
||||
if st:
|
||||
entity["state_address"] = st.address
|
||||
consumed.add(st.address)
|
||||
else:
|
||||
review.append({"reason": "switch_without_status",
|
||||
review.append({"reason": "light_without_status" if is_light else "switch_without_status",
|
||||
"address": ga.address, "name": ga.name})
|
||||
switches.append(entity)
|
||||
(lights if is_light else switches).append(entity)
|
||||
consumed.add(ga.address)
|
||||
|
||||
# ---- 3b. CLIMATE — anchored on an HVAC mode (DPT 20.102/20.105). Zone
|
||||
|
||||
Reference in New Issue
Block a user