Files
nickol-knx-mcp/tests/test_council_fixes.py
Nikolay1andClaude Opus 4.8 c53f388dcb fix: council-review noise cuts — scenes/central-macros exempt, language-matched repairs
External expert review (real run on the demo house) flagged: scenes (18.001)
wrongly marked missing_status + an unsafe DPT-1.011 status repair for them;
'All blinds down' warned while 'All lights off' was correctly INFO; and a Russian
'(статус)' suffix synthesised on an English project. Fixes: scene_no_status INFO
+ no synth status for scenes; broadened central-macro detection (generic 'all
blinds/shutters/...'); language-matched repair suffixes. Demo missing_status
warnings ~7 -> 1 genuine. tests/test_council_fixes.py; full suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:09:57 +02:00

62 lines
2.8 KiB
Python

"""Fixes from the external-council review (run on the demo house):
* scene-control GAs (17/18) must NOT be flagged as missing_status (they have no
single real state) -> a scene_no_status INFO instead, and no synthesised status;
* generic group commands like "All blinds down" are central macros (INFO), like
"All lights off" -> not a missing_status warning;
* synthesised repair names match the GA's language (no Russian "(статус)" on an
English project).
"""
from nickol_knx_mcp.project import build_loaded_from_raw
from nickol_knx_mcp.analyze import detect_missing_status
from nickol_knx_mcp.repair import suggest_repairs
def _ga(addr, name, dmain, dsub):
return {"name": name, "identifier": f"GA-{addr}", "raw_address": 0, "address": addr,
"project_uid": None, "dpt": {"main": dmain, "sub": dsub}, "data_secure": False,
"communication_object_ids": [], "description": "", "comment": ""}
def _proj(gas):
raw = {"info": {"group_address_style": "ThreeLevel", "schema_version": "21"},
"group_addresses": gas, "communication_objects": {}, "devices": {},
"functions": {}, "topology": {}, "group_ranges": {}}
return build_loaded_from_raw(raw, "t.knxproj")
def main():
gas = {
"0/0/1": _ga("0/0/1", "Scene Movie", 18, 1), # scene -> not missing_status
"0/1/2": _ga("0/1/2", "All blinds down", 1, 8), # central macro -> INFO
"1/0/1": _ga("1/0/1", "Kitchen light on", 1, 1), # EN switch, no status
"3/0/1": _ga("3/0/1", "Кухня свет вкл", 1, 1), # RU switch, no status
}
p = _proj(gas)
ms = {(f["code"], f["address"]) for f in detect_missing_status(p)}
# scene -> scene_no_status INFO, never missing_status_address
assert ("scene_no_status", "0/0/1") in ms, ms
assert ("missing_status_address", "0/0/1") not in ms
# "All blinds down" -> central macro INFO (was wrongly a warning before)
assert ("central_macro_no_status", "0/1/2") in ms, ms
assert ("missing_status_address", "0/1/2") not in ms
# the plain switches DO still get flagged
assert ("missing_status_address", "1/0/1") in ms
assert ("missing_status_address", "3/0/1") in ms
# repair language matches the GA name; and scenes get no synthesised status
props = suggest_repairs(p)["proposals"]
names = {p_["for"]: p_["name"] for p_ in props if p_.get("action") == "add_ga"}
assert names.get("1/0/1", "").endswith("(status)"), names # EN
assert names.get("3/0/1", "").endswith("(статус)"), names # RU
assert "0/0/1" not in names, "a scene must not get a synthesised status GA"
print("test_council_fixes: OK — scenes exempt (scene_no_status, no synth status), "
"'All blinds down' recognised as central macro, repair suffix matches language.")
if __name__ == "__main__":
main()