diff --git a/scripts/tinyhouse/__main__.py b/scripts/tinyhouse/__main__.py index c88ed71..e3d4116 100644 --- a/scripts/tinyhouse/__main__.py +++ b/scripts/tinyhouse/__main__.py @@ -110,6 +110,7 @@ def check(flows: list[Flow]) -> list[str]: problems += _check_widgets(known) problems += _check_sources(flows) problems += _check_schemas(flows) + problems += _check_injects(flows) return problems @@ -308,6 +309,36 @@ def _rejects(spec: dict[str, Any], sample: Any) -> str: return "" +def _check_injects(flows: list[Flow]) -> list[str]: + """What an inject emits has to be what its port declared. + + An inject left to itself emits the current time, so a port expecting + anything but a number gets a float and the node raises — five times, and + the supervisor quarantines the flow. Nothing else catches it: an inject is + not a Python node, so it has no source to run, and both sides of the + declaration agree with each other while disagreeing with reality. + """ + problems = [] + for flow in flows: + for node in flow.nodes: + if node["type"] != "inject": + continue + params = node.get("params", {}) + payloads = dict(params.get("payloads") or {}) + for spec in node.get("provides", []): + port = spec.get("port") or spec["name"] + if port in payloads: + value = payloads[port] + elif "payload" in params: + value = params["payload"] + else: + value = 0.0 # the current time, which is what it defaults to + bad = _rejects(spec, value) + if bad: + problems.append(f"{flow.name}.{node['id']} emits: {bad}") + return problems + + def _check_schemas(flows: list[Flow]) -> list[str]: """Hand every node to the engine's own models before the API sees them. diff --git a/scripts/tinyhouse/sensing.py b/scripts/tinyhouse/sensing.py index 8bb9e79..3503577 100644 --- a/scripts/tinyhouse/sensing.py +++ b/scripts/tinyhouse/sensing.py @@ -165,7 +165,10 @@ def power(h: dict[str, Any]) -> Flow: "id": "keepalive_tick", "type": "inject", "title": "Every 30 seconds", - "params": {"interval": 30, "at_start": True}, + # An empty payload, as the reference sends: the Cerbo only wants + # to know somebody is listening. Left to itself an inject emits + # the time, which is a float going into a string port. + "params": {"interval": 30, "at_start": True, "payload": ""}, "provides": [{"name": "keepalive", "dtype": "str"}], } )