wren node takes switches (plug, socket, appliances), refused when the model lacks the kind
This commit is contained in:
@@ -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, "
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -367,9 +367,11 @@ that is not wired to the node.
|
||||
`garden`, `patio`, `garage`, `driveway`, `balcony`
|
||||
- **kind**: `light`, `lamp`, `brightness` for a light; `setpoint`, `heating`,
|
||||
`heat`, `boost` for heating; `media`, `music`, `tv`; `shutter`, `blind`,
|
||||
`cover`; `water`, `watering`, `sprinkler`
|
||||
`cover`; `water`, `watering`, `sprinkler`; `switch`, `plug`, `socket`,
|
||||
`appliances` for a switch, which only wren's s10 model knows
|
||||
|
||||
A port naming anything else is refused when the flow is built. A port is
|
||||
A port naming anything else, or a kind the installed model lacks, is refused
|
||||
when the flow is built. A port is
|
||||
named after its message unless it is given a name of its own, in the node
|
||||
panel's port name field or as `{"name": "setpoint", "port":
|
||||
"living_room_setpoint"}` through the flow API.
|
||||
@@ -387,11 +389,12 @@ reads.
|
||||
| `shutters.up` / `down` | `true` / `false` | `100` / `0` | `up` / `down` |
|
||||
| `shutters.stop` | — | — | `stop` |
|
||||
| `watering.start` / `stop` | `true` / `false` | — | `on` / `off` |
|
||||
| `switches.on` / `off` | `true` / `false` | `1` / `0` | `on` / `off` |
|
||||
|
||||
| Setting | Default | Notes |
|
||||
|---|---|---|
|
||||
| `apply` | `false` | write to the targets; off, the node only reports |
|
||||
| `min_confidence` | `0.93` | below this nothing is written |
|
||||
| `min_confidence` | `0.93` | below this nothing is written. wren's s10 model is less sure of itself, use `0.8` with it |
|
||||
| `aliases` | empty | the house's own words, or words the recogniser hears wrong, in the model's: `{"traverse": "living room light", "page too": "patio"}` |
|
||||
|
||||
Each record in `actions` contains `message`, `value`, `applied`, `delay_s`,
|
||||
@@ -412,8 +415,8 @@ The model sees every area, wired or not, each with at least a light, so "the
|
||||
bathroom light" is not taken for a light that is wired elsewhere. Such a call
|
||||
is reported but not applied, and goes on to `unhandled`. So is a call on an
|
||||
area the command does not name, unless it is the user's area or, with no area
|
||||
named, the living room: "switch on the appliances" is a word the model never
|
||||
learned, and it lands on whichever light it was shown. The command is read
|
||||
named, the living room: "switch on the appliances" is a word the pinned model
|
||||
never learned, and it lands on whichever light it was shown. The command is read
|
||||
in lower case without sentence marks and with number words as digits ("in
|
||||
five minutes" as "in 5 minutes"), as the model was trained. `aliases` are
|
||||
applied before, whole words in any case, and `unhandled` carries the prompt
|
||||
|
||||
Reference in New Issue
Block a user