Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.qubit.energy/llms.txt

Use this file to discover all available pages before exploring further.

State Machines

The coordination layer uses finite state machines to track and guard asset lifecycles. Each controllable asset has a state machine instance that ensures commands are only dispatched when the asset is in a valid state.

Generic State Machine

The StateMachine class is a reusable, configurable FSM:
from coordinator import StateMachine, EventBus

sm = StateMachine(
    asset_id="ast_custom_001",
    states={"OFF", "STARTING", "RUNNING", "STOPPING"},
    initial_state="OFF",
    transitions={
        "OFF": {"start": "STARTING"},
        "STARTING": {"ready": "RUNNING", "fail": "OFF"},
        "RUNNING": {"stop": "STOPPING"},
        "STOPPING": {"stopped": "OFF"},
    },
    event_bus=EventBus()  # Optional
)

sm.trigger("start")        # → "STARTING"
sm.can_trigger("ready")    # → True
sm.available_triggers()    # → ["ready", "fail"]

Transition Guards

Invalid transitions raise InvalidTransition with diagnostic info:
from coordinator import InvalidTransition

try:
    sm.trigger("stop")  # Not valid from STARTING
except InvalidTransition as e:
    print(e.current_state)     # "STARTING"
    print(e.trigger)           # "stop"
    print(e.valid_triggers)    # ["ready", "fail"]

History Tracking

Every transition is recorded:
sm.trigger("start")
sm.trigger("ready")
print(sm.history)
# [{"from_state": "OFF", "to_state": "STARTING", "trigger": "start", "timestamp": ...},
#  {"from_state": "STARTING", "to_state": "RUNNING", "trigger": "ready", "timestamp": ...}]

EV Charger State Machine

OCPP-aligned lifecycle with 7 states:
from coordinator import create_ev_charger_state_machine

charger = create_ev_charger_state_machine("ast_charger_001")

charger.trigger("plug_in")       # AVAILABLE → PREPARING
charger.trigger("start_charge")  # PREPARING → CHARGING
charger.trigger("complete")      # CHARGING → FINISHING
charger.trigger("unplug")        # FINISHING → AVAILABLE

Key Scenarios

AVAILABLE → PREPARING → CHARGING → FINISHING → AVAILABLEThe happy path: vehicle plugs in, charging starts, completes, vehicle unplugs.
CHARGING → SUSPENDED → CHARGINGCharging paused (e.g., grid signal, user request) then resumed.
CHARGING → AVAILABLE (via unplug)Vehicle unplugs before charging completes. The dispatch engine should detect this and mark remaining commands as cancelled.
Any State → FAULTED → AVAILABLE (via reset)Hardware fault detected. After reset, charger returns to available.

Battery State Machine

5-state battery management with direct mode switching:
from coordinator import create_battery_state_machine

battery = create_battery_state_machine("ast_batt_001")

battery.trigger("charge")     # IDLE → CHARGING
battery.trigger("discharge")  # CHARGING → DISCHARGING (direct switch)
battery.trigger("stop")       # DISCHARGING → IDLE

Direct Mode Switching

The battery state machine supports direct transitions between charging and discharging without requiring a stop in between. This enables the dispatch engine to switch modes immediately when the optimization schedule transitions from charge to discharge.

Event Bus Integration

All state machines can publish STATE_CHANGED events:
from coordinator import EventBus, EventType, create_battery_state_machine

bus = EventBus()
bus.subscribe(EventType.STATE_CHANGED, lambda e: print(
    f"{e.payload['asset_id']}: {e.payload['from_state']}{e.payload['to_state']}"
))

battery = create_battery_state_machine("ast_batt_001", event_bus=bus)
battery.trigger("charge")
# Prints: ast_batt_001: IDLE → CHARGING