A flexible Finite-State Machine platform for ESPHome. It lets you model complex behaviours with limited inputs, such as:
- Controlling dimmable
light
with a single button. - Controlling a garage door
cover
with a single button. - Controlling a
display
with a button (e.g. flip through pages on click, and go into editing mode on hold). - And more...
external_components:
- source:
type: git
url: https://github.com/muxa/esphome-state-machine
The basic state machine configuration involves providing:
- A list of
states
- A list of
inputs
- A list of allowed
transitions
for each input.
Example for a simple on/off toggle state machine:
state_machine:
- name: On/Off Toggle State Machine
states:
- "OFF"
- "ON"
inputs:
- name: TOGGLE
transitions:
- ON -> OFF
- OFF -> ON
And to transition between states it you'll need to trigger the machine by providing input, e.g:
binary_sensor:
- platform: gpio
pin: D6
name: "Button"
filters:
- delayed_on: 100ms
on_press:
- state_machine.transition: TOGGLE
-
initial_state (Optional, string): The intial state of the state machine. Defaults to first defined state.
-
states (Required, list): The list of states that the state machine has.
- name (Required, string): The name of the state. Must not repeat.
- on_enter (Optional, Automation): An automation to perform when entering this state. Called after
on_transition
automation and beforeafter_transition
. - on_leave (Optional, Automation): An automation to perform when leaving this state. Called after
before_transition
automation and beforeon_transition
automation. - on_set (Optional, Automation): An automation to perform when setting this state using
set
action or viainitial_state
. Will not trigger if new state is the same as current state.
-
inputs (Required, list): The list of inputs that the state machine supports with allowed state transitions.
- name (Required, string): The name of the input. Must not repeat.
- transitions (Required, list): The list of allowed transitions. Short form is
FROM_STATE -> TO_STATE
, or advanced configuration:- from (Required, string): Source state that this input is allowed on.
- to (Required, string): Target state that this input transitions to.
- before_transition (Optional, Automation): An automation to perform before transition. Called after
on_input
automation and beforeon_transition
automation. - on_transition (Optional, Automation): An automation to perform on transition. Called after
on_leave
automation and beforeon_enter
automation. - after_transition (Optional, Automation): An automation to perform after transition. Called after
on_enter
automation.
- on_input (Optional, Automation): An automation to perform when this input is triggred. This automation is performed first, then
before_transition
will be called. This automation will not be called if transition is invalid.
-
diagram (Optional, boolean): If true, then a diagram of the state machine will be output to the console during validation/compilation of YAML. See Diagrams section below for more details. Defaults to
false
.
Any running state machine automations (state, input and transition) will be stopped before running next automations. This is useful when there's a delayed transition in one of the automation and it needs to be cancelled because a new input was provided which results in a different transition.
When State Machine receives input (via transition
action) it will call the automation specified in the input, transition, the "from" state and the "to" state in the following order:
on_input
before_transition
- "From" state
on_leave
on_transition
- "To" state
on_enter
after_transition
See test.yaml for a demonstration of running order of these automations within a context of simple ON-OFF state machine.
You can provide input to the state machine from elsewhere in your WAML file with the state_machine.transition
action.
# in some trigger
on_...:
# Basic:
- state_machine.transition: TOGGLE
# Advanced (if you have multiple state machines in one YAML)
- state_machine.transition:
id: sm1
input: TOGGLE
Configuration options:
- id (Optional, ID): The ID of the state machine.
- input (Required, string): The input to provide in order to transition to the next state.
This action allows resetting the state machine current state, without going through transitions. This can be useful when initial state is not really known until some sensor data is available.
Note that only the target state
on_set
automation will be triggered, and all other state machine automations (on_enter
,on_leave
andaction
of the inputs and transitions) will be skipped.
# in some trigger
on_...:
# Basic:
- state_machine.set: OPEN
# Advanced (if you have multiple state machines in one YAML)
- state_machine.set:
id: sm1
state: OPEN
Configuration options:
- id (Optional, ID): The ID of the state machine.
- state (Required, string): The state to set state machine to bypassing transitions.
This condition lets you check what state the machine is currently in.
# in some trigger
on_...:
# Basic
if:
condition:
state_machine.state: "ON"
then:
- logger.log: Is ON
# Advanced
if:
condition:
state_machine.state:
id: sm1
value: "ON"
then:
- logger.log: Is ON
This condition lets you check what transition last occurred.
# in some trigger
on_...:
# Basic
if:
condition:
state_machine.transition:
trigger: TOGGLE
then:
- logger.log: Toggled
# Advanced
if:
condition:
state_machine.transition:
id: sm1
from: "OFF"
trigger: TOGGLE
to: "ON"
then:
- logger.log: Turned on by toggle
This is a simple text sensor to expose the current state of the state machine:
text_sensor:
- platform: state_machine
name: On/Off Toggle State
- state_machine_id (Optional, ID): The ID of the state machine.
- All other options from Text Sensor.
You have an option to generate a diagram of the state machine. You can enable this by adding diagram: <format>
to the state machine yaml declaration (disabled by default). Currently supported formats are mermaid
and dot
When compiling or validating your YAML a state machine diagram will be output to the console.
Mermaid is a JavaScript based diagramming and charting tool that takes Markdown-inspired text definitions and creates diagrams dynamically in the browser.
Github supports Mermaid diagrams emdded into Martkdown as code snippets via ```mermaid ... ```
.
When using mermaid
diagram the following will be output to console:
- The "source code" of the diagram in Mermaid notation
Here's an example:
stateDiagram-v2
direction LR
[*] --> OFF
ON --> OFF: TOGGLE
OFF --> ON: TOGGLE
stateDiagram-v2
direction LR
[*] --> OFF
ON --> OFF: TOGGLE
OFF --> ON: TOGGLE
DOT DOT is a graph description language.
When using dot
diagram the following will be output to console:
- A URL of the diagram image in SVG format
- The "source code" of the diagram in DOT language
Here's an example:
INFO State Machine Diagram (for On/Off Toggle State Machine):
https://quickchart.io/graphviz?format=svg&graph=digraph%20%22On/Off%20Toggle%20State%20Machine%22%20%7B%0A%20%20node%20%5Bshape%3Dellipse%5D%3B%0A%20%20ON%20-%3E%20OFF%20%5Blabel%3DTOGGLE%5D%3B%0A%20%20OFF%20-%3E%20ON%20%5Blabel%3DTOGGLE%5D%3B%0A%7D
INFO DOT language graph:
digraph "On/Off Toggle State Machine" {
node [shape=ellipse];
ON -> OFF [label=TOGGLE];
OFF -> ON [label=TOGGLE];
}
Which corresponds to:
To get just the url use this command:
esphome config <config.yaml> 2>&1 | grep quickchart.io
To open the diagram in Chrome use this command:
esphome config <config.yaml> 2>&1 | grep quickchart.io | xargs open -n -a "Google Chrome" --args "-0"
To get a PNG image instead of SVG change the
format
parameter in the image url fromsvg
topng
.
stateDiagram-v2
direction LR
[*] --> OFF
ON --> OFF: TOGGLE
OFF --> ON: TOGGLE
This example illustrates toggling an LED using a button.
See toggle-example.yaml.
stateDiagram-v2
direction LR
[*] --> OFF
ON --> OFF: CLICK
OFF --> ON: CLICK
EDITING --> EDITING: CLICK
ON --> EDITING: HOLD
EDITING --> ON: HOLD
This example models a single button control for a dimmable light with the following functionality:
- CLICK to toggle ON of OFF
- HOLD to go into EDITING mode to adjust brightness with a CLICK.
See dimmable-light-example.yaml.
stateDiagram-v2
direction LR
[*] --> IDLE
IDLE --> WAITING_CLOSE_BUTTON: OPEN_ON
WAITING_OPEN_BUTTON --> SUN_TRACKER_ON: OPEN_ON
CLOSING --> OPENING: OPEN_ON
OPENING --> IDLE: OPEN_OFF
WAITING_CLOSE_BUTTON --> IDLE: OPEN_OFF
SUN_TRACKER_ON --> WAITING_OPEN_BUTTON: OPEN_OFF
IDLE --> WAITING_OPEN_BUTTON: CLOSE_ON
WAITING_CLOSE_BUTTON --> SUN_TRACKER_ON: CLOSE_ON
OPENING --> CLOSING: CLOSE_ON
CLOSING --> IDLE: CLOSE_OFF
WAITING_OPEN_BUTTON --> IDLE: CLOSE_OFF
SUN_TRACKER_ON --> WAITING_CLOSE_BUTTON: CLOSE_OFF
WAITING_CLOSE_BUTTON --> OPENING: OPENING_TIMEOUT
WAITING_OPEN_BUTTON --> CLOSING: CLOSING_TIMEOUT
OPENING --> IDLE: STOP
CLOSING --> IDLE: STOP
This example demonstrates using 2 switches to control a time-based cover:
- Switch 1 ON: OPEN
- Switch 2 ON: CLOSE
- Both switches ON: enable Sun Tracker