wren node takes switches (plug, socket, appliances), refused when the model lacks the kind
Docs / docs (push) Successful in 24s
Playwright Tests / test-playwright (1, 2) (push) Failing after 32s
Playwright Tests / test-playwright (2, 2) (push) Failing after 21s
pre-commit / pre-commit (push) Failing after 2m56s
Test Backend / test-backend (3.12) (push) Failing after 4m53s
Test Backend / test-backend (3.14) (push) Failing after 4m37s
Compose Smoke Test / test-compose (push) Failing after 24s
Playwright Tests / merge-reports (push) Failing after 2m47s

This commit is contained in:
Melvin Strobl
2026-10-22 17:48:41 +02:00
parent a7a18bf19f
commit 0d4ff89302
4 changed files with 43 additions and 7 deletions
+25 -1
View File
@@ -57,6 +57,10 @@ KIND_WORDS = {
"water": "water",
"watering": "water",
"sprinkler": "water",
"switch": "switch",
"plug": "switch",
"socket": "switch",
"appliances": "switch",
}
#: What the node reads, and the types each may be. Only the prompt wakes it.
@@ -85,6 +89,7 @@ _TOOLS = {
"heat": "heating",
"shutter": "shutters",
"water": "watering",
"switch": "switches",
}
#: What a call sets a target to, by the target's type: (bool, number, text).
@@ -102,6 +107,8 @@ _VALUES: dict[str, tuple[bool | None, float | None, str | None]] = {
"shutters.stop": (None, None, "stop"),
"watering.start": (True, None, "on"),
"watering.stop": (False, None, "off"),
"switches.on": (True, 1, "on"),
"switches.off": (False, 0, "off"),
}
#: An applied call, said: "kitchen light on". `heating.set` says its number.
@@ -117,6 +124,8 @@ _SAID = {
"shutters.stop": "shutters stopped",
"watering.start": "watering on",
"watering.stop": "watering off",
"switches.on": "switch on",
"switches.off": "switch off",
}
#: What every area held in training: a light, and in the living room, which
@@ -160,15 +169,24 @@ _GROUP_KINDS = {
"heating": "heat",
"watering": "water",
"sprinklers": "water",
"switches": "switch",
"plugs": "switch",
"appliances": "switch",
}
#: The call a group command makes on each kind, by its action word.
_GROUP_TOOLS = {
"on": {"light": "lights.on", "media": "media.play", "water": "watering.start"},
"on": {
"light": "lights.on",
"media": "media.play",
"water": "watering.start",
"switch": "switches.on",
},
"off": {
"light": "lights.off",
"media": "media.stop",
"heat": "heating.off",
"water": "watering.stop",
"switch": "switches.off",
},
"open": {"shutter": "shutters.up"},
"up": {"shutter": "shutters.up"},
@@ -194,6 +212,8 @@ _GROUP_SAID = {
"shutters.stop": "shutters stopped",
"watering.start": "watering on",
"watering.stop": "watering off",
"switches.on": "switches on",
"switches.off": "switches off",
}
@@ -426,6 +446,10 @@ class WrenNode(Node):
f"<area>_<kind>, the area one of {', '.join(areas)}, the kind "
f"one of {', '.join(KIND_WORDS)}."
)
if device[1] not in wren.KINDS: # switches came with wren's s10
raise ValueError(
f"'{spec.port}' is a {device[1]}, which this wren does not know."
)
if spec.dtype not in _TARGET_TYPES:
raise ValueError(
f"'{spec.port}' is {spec.dtype.value}; a target is bool, int, "
+1 -1
View File
@@ -34,7 +34,7 @@ class FakeWren:
*("living room", "kitchen", "bathroom", "bedroom", "office"),
*("garden", "patio", "garage", "driveway", "balcony"),
]
KINDS = ["light", "media", "heat", "shutter", "water"]
KINDS = ["light", "media", "heat", "shutter", "water", "switch"]
Device = _Device
House = _House
+9
View File
@@ -40,13 +40,21 @@ def test_targets_of_one_area_and_kind_are_one_device(fake_wren):
("living_room_setpoint", DType.FLOAT),
("living_room_boost", DType.BOOL),
("kitchen_lamp", DType.BOOL),
("office_plug", DType.BOOL),
)
assert node.devices == {
("living room", "heat"): ["living_room_setpoint", "living_room_boost"],
("kitchen", "light"): ["kitchen_lamp"],
("office", "switch"): ["office_plug"],
}
def test_a_kind_the_model_does_not_know_is_refused(fake_wren, monkeypatch):
monkeypatch.setattr(fake_wren, "KINDS", ["light"])
with pytest.raises(ValueError, match="switch"):
_node(("office_plug", DType.BOOL))
@pytest.mark.parametrize("port", ["hallway_light", "kitchen_fridge", "light"])
def test_a_port_naming_no_trained_device_is_refused(fake_wren, port):
with pytest.raises(ValueError, match="living_room") as refused:
@@ -87,6 +95,7 @@ def test_the_model_reads_the_command_plain():
("heating.set", {"temp": 22}, DType.BOOL, None),
("heating.boost", {}, DType.BOOL, True),
("heating.boost", {}, DType.INT, None),
("switches.off", {}, DType.INT, 0),
("sensors.temperature", {"area": "kitchen"}, DType.FLOAT, None),
],
)