mirror of
https://github.com/NickoScope/nickol-knx-mcp.git
synced 2026-09-29 19:31:12 +02:00
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>
62 lines
2.8 KiB
Python
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()
|