Rename the trigger node type to debounce, migrating stored flows

The store rewrites `"type": "trigger"` to `debounce` in every published and
draft flow when it opens, bumps the version and commits once. No alias stays
behind, so `trigger` is now an unknown type.
This commit is contained in:
2026-10-15 09:34:40 +02:00
parent 4a776e52c4
commit cfb9e53d68
9 changed files with 98 additions and 35 deletions
+13 -13
View File
@@ -8,8 +8,8 @@ come up than to go down.
from fluksio.flow.messages import DType, MessageSpec
from fluksio.flow.nodes import MqttNode
from fluksio.flow.nodes.debounce import DebounceNode
from fluksio.flow.nodes.mqtt import topic_matches
from fluksio.flow.nodes.trigger import TriggerNode
# ── MQTT topic filters ────────────────────────────────────────────────────
@@ -72,7 +72,7 @@ def test_without_json_key_the_port_name_is_the_key_as_before():
assert node.json_keys == {}
# ── trigger: passthrough and a per-message wait ───────────────────────────
# ── debounce: passthrough and a per-message wait ──────────────────────────
class _Pipeline:
@@ -87,8 +87,8 @@ class _Pipeline:
return True
def _trigger(**params) -> tuple[TriggerNode, _Pipeline]:
node = TriggerNode(
def _debounce(**params) -> tuple[DebounceNode, _Pipeline]:
node = DebounceNode(
requires=[
MessageSpec(name="run", port="run", dtype=DType.STR),
MessageSpec(name="run_for", port="run_for", dtype=DType.FLOAT),
@@ -103,30 +103,30 @@ def _trigger(**params) -> tuple[TriggerNode, _Pipeline]:
def test_the_incoming_value_passes_through_and_the_wait_comes_from_a_port():
node, pipeline = _trigger()
node, pipeline = _debounce()
assert node._trigger({}, run="DOWN", run_for=28.0) == {"cmd": "DOWN"}
assert node._debounce({}, run="DOWN", run_for=28.0) == {"cmd": "DOWN"}
(outputs, seconds, _guard) = pipeline.deferred[-1]
assert outputs == {"shutters.cmd": "STOP"} and seconds == 28.0
node._trigger({}, run="UP", run_for=26.0)
node._debounce({}, run="UP", run_for=26.0)
assert pipeline.deferred[-1][1] == 26.0
def test_a_second_command_invalidates_the_stop_the_first_one_scheduled():
node, pipeline = _trigger()
node._trigger({}, run="DOWN", run_for=28.0)
node, pipeline = _debounce()
node._debounce({}, run="DOWN", run_for=28.0)
first = pipeline.deferred[-1][2]
node._trigger({}, run="UP", run_for=26.0)
node._debounce({}, run="UP", run_for=26.0)
assert pipeline.deferred[-1][2] != first
def test_a_wait_of_zero_sends_nothing_afterwards():
"""STOP is commanded once. Nothing follows it, forever."""
node, pipeline = _trigger()
node._trigger({}, run="DOWN", run_for=28.0)
node, pipeline = _debounce()
node._debounce({}, run="DOWN", run_for=28.0)
scheduled = len(pipeline.deferred)
assert node._trigger({}, run="STOP", run_for=0.0) == {"cmd": "STOP"}
assert node._debounce({}, run="STOP", run_for=0.0) == {"cmd": "STOP"}
assert len(pipeline.deferred) == scheduled
assert node.recall("armed", 0) == 0
+1 -1
View File
@@ -75,7 +75,7 @@ FIXTURES: dict[str, dict] = {
],
"provides": [MessageSpec(name="reading", dtype=DType.JSON)],
},
"trigger": {
"debounce": {
"params": {"first": True, "then": False, "wait": 120},
"requires": [MessageSpec(name="motion", dtype=DType.BOOL)],
"provides": [MessageSpec(name="light", dtype=DType.BOOL)],
+28
View File
@@ -131,3 +131,31 @@ def test_renaming_onto_an_existing_name_is_refused(store: FlowStore):
store.rename_flow("heating", "warmth")
assert store.list_flows() == ["heating", "warmth"]
def test_opening_the_store_renames_trigger_nodes_to_debounce(store: FlowStore):
"""The old name has no alias, so what is stored under it is rewritten once."""
store.write_flow(
FlowDef(
name="hall",
nodes=[NodeDef(id="hold", type="trigger"), NodeDef(id="lamp")],
)
)
store.write_draft(
FlowDef(name="hall", nodes=[NodeDef(id="hold", type="trigger")]),
base_version=1,
)
commits = commit_count(store)
reopened = FlowStore(store.root)
published = reopened.read_flow("hall")
draft = reopened.read_flow("hall", draft=True)
assert [node.type for node in published.nodes] == ["debounce", "python"]
assert [node.type for node in draft.nodes] == ["debounce"]
# Bumped, so an editor holding the old draft cannot save `trigger` back.
assert (published.version, draft.version) == (2, 3)
assert commit_count(store) == commits + 1
FlowStore(store.root)
assert commit_count(store) == commits + 1