"""Workflow execution engine. When a board input fires a webhook, this module evaluates all matching enabled workflows and triggers the configured relay action. """ import logging from datetime import datetime from app import db, socketio from app.models.workflow import Workflow from app.models.board import Board from app.services import board_service logger = logging.getLogger(__name__) def process_input_event(board_id: int, input_num: int, new_state: bool) -> None: """Called from the webhook route. Finds and fires matching workflows.""" # Determine event type from state event = "press" if new_state else "release" workflows = Workflow.query.filter_by( trigger_board_id=board_id, trigger_input=input_num, is_enabled=True, ).all() for wf in workflows: if wf.trigger_event not in (event, "both"): continue target = db.session.get(Board, wf.action_board_id) if target is None: logger.warning("Workflow %d: target board %d not found", wf.id, wf.action_board_id) continue success = False new_relay_state = None if wf.action_type == "on": success = board_service.set_relay(target, wf.action_relay, True) new_relay_state = True elif wf.action_type == "off": success = board_service.set_relay(target, wf.action_relay, False) new_relay_state = False elif wf.action_type == "toggle": new_relay_state = board_service.toggle_relay(target, wf.action_relay) success = new_relay_state is not None if success: wf.last_triggered = datetime.utcnow() # Update cached relay state in DB if new_relay_state is not None: states = target.relay_states states[f"relay_{wf.action_relay}"] = new_relay_state target.relay_states = states db.session.commit() logger.info( "Workflow '%s' fired: Board#%d.input%d=%s → Board#%d.relay%d [%s]", wf.name, board_id, input_num, new_state, target.id, wf.action_relay, wf.action_type, ) # Push update so dashboard reflects new relay state instantly socketio.emit("board_update", { "board_id": target.id, "relay_states": target.relay_states, "is_online": target.is_online, }) else: logger.warning("Workflow '%s' failed to reach board %s", wf.name, target.name)