Software Overview  |  Sitemap  |  Downloads  |  Developers  |  Forums

State Machines

State Machines are great for monitoring and controlling other Devices. Their behavior and their terminals are both user programmable, so they can be custom fit into many different applications. Aside from a user written Virtual Device, a State Machine is the most programmable and flexible Device in the system. Common uses for State Machines are intelligent sensor monitors, light switching systems, and control systems.

The State Machine Table

State Machines are built around a user defined element called a table. Tables specify a State Machine's behavior, input and output events (actions), and the state they monitor. Here is a simple State Machine Table.

"simple_table": [
  {When: {input: "off"}, Then: {output: "off"}},
  {When: {input: "on"}, Then: {output: "on"}}

Without going into the details, you can probably see that this table turns its output off when its input is off, and turns its output on when its input is on.

Tables have a name and a set of rows. In the example above, the table is named "simple_table", and there are 2 rows. Rows are bracketed by []s, and they are separated by commas. In the example above, the rows are:

  {When: {input: "off"}, Then: {output: "off"}},
  {When: {input: "on"}, Then: {output: "on"}}

Row Description

Rows contain from 1-3 statements. Statements are one of 3 types: When, State, and Then. Every row has one When statement, and can optionally have one State and one Then statement. In the example above each of our 2 rows has a When statement, a Then statement, but no State statement.

"When" statements define State Machine input events, State statements describe the state being monitored, and Then statements specify actions or output events. The three statements work together in a row, and they interact as follows. When an event defined in a row's When statement occurs, it causes the State Machine to check its State statement. If the state described in the State statement matches the actual state, the action defined by the row's Then statement is taken.

Here's an example of a row from a state machine table:

{When: {in1: "on", in2: "off"}, State: {out1: "on", out2:"off"}, Then: {out1: "off", out2: "on"}}

In the row above, there is a When statement, a State statement, and a Then statement. As described, when the "When" statement is satisfied, the State statement is checked. If both statements are satisfied, the Then action is taken. Here, the When is looking for either when "in1" is asserted on or when "in2" is asserted off. The State statement is looking for when the state of "out1" is on and the state of "out2" is off. (See Note) When both the "When" and "State" statements are satisfied, the Then action is taken. Here, the Then action is to set "out1" to off and "out2" to on.

As mentioned, State and Then statements are both optional, while a When statement is required. In the row above, had there been no State statement, the Then action would have been taken right after the When statement was satisfied. Had there been no Then statement, nothing would have happened, but the row would still have been considered valid and properly executed.


  • when any one of the events in a "When" statement occurs, the When statement is satisfied; in contrast, all the state in the "State" statement has to match for the "State" statement to be satisfied. If you are familiar with digital logic, loosely speaking, events are ORed together, while states are ANDed. It's a loose analogy, because though States truly are ANDed together, events are not really ORed. Events happen at points in time and disappear, so saying they are logically ORed, has to be said loosely, since you can't OR things together that exist at different times!

Multiple Rows

Most tables have more than one row. When there are multiple rows, it's possible that an event and state could cause more than one row's Then and State statements to get satisfied. What happens? Here is how multi-row tables operate.

Whenever an event occurs, a State Machine will find every row in its table that is sensitive to that event (it knows how to do this from the "When" clauses). Next, starting at the topmost found row and progressing to the bottommost, the State Machine will look for the first row whose "State" value matches the actual, current state (a row with no State always matches). If it finds such a row, it will perform that row's action. If it doesn't find any matching rows, it will take no action. If there is more than one row that matches, it will apply the Then clause from the first row (the topmost row) it finds and ignore the other (lower) rows. If the topmost row it finds has no Then clause, it will still consider that row found (and perform no actions at all).

Here is a State Machine table which describes a counting behavior. It counts higher whenever its "in" input is asserted on. If its "in" input is asserted off, it resets its outputs to off. When it hits its maximum value (both outputs on), and its input is asserted on, it sets both its outputs to off.

"my_counting_table": [
  {When: {in: "on"}, State: {out1: "off", out2: "off"}, Then: {out1: "on"}},
  {When: {in: "on"}, State: {out1: "on", out2: "off"}, Then:{out1: "off", out2: "on"}},
  {When: {in: "on"}, State: {out1: "off", out2: "on"}, Then:{out1: "on"}},
  {When: {in: "on"}, State: {out1: "on", out2: "on"}, Then:{out1: "off", out2: "off"}},
  {When: {in: "off"}, Then: {out1: "off", out2: "off"}}

Notice in the 1st and 3rd rows, the output state (the "Then" part) is only partially specified - "out2" is not mentioned. It would be OK to specify the state of both "out1" and "out2" in those rows, but since "out2" is already in the state we want it in, there is no need put it there again.


The state tables we have shown are written a syntax supported by Python, Ruby, Javascript, and a number of other modern languages. The syntax is also supported by a language called YAML (YAML ain't markup language). YAML is the language the Virtual Wiring system uses for defining tables*. YAML isn't a programming language, but a language for expressing data objects.

Hopefully, even without understanding the details, what's going on in a table is pretty clear. If not, learning the YAML language is time well spent, as it shares a common syntax with many other languages.

Because YAML's sole purpose is to express objects, it's quite good at it. Tables can be expressed in the common format we've been using, or in other formats. Among its many tricks, it also supports a syntax which has more white space and less brackets and commas.

Here is our original "simple_table" expressed using white space (indentation) instead of brackets and commas:

  When: {input: "off"}
  Then: {output: "off"}
  When: {input: "on"}
  Then: {output: "on"}

Here is our same counting table with the white spaced syntax:

  When: {in: "on"}
  State: {out1: "off", out2: "off"}
  Then: {out1: "on"}
  When: {in: "on"}
  State: {out1: "on", out2: "off"}
  Then: {out1: "off", out2: "on"}
  When: {in: "on"}
  State: {out1: "off", out2: "on"}
  Then: {out1: "on"}
  When: {in: "on"}
  State: {out1: "on", out2: "on"}
  Then: {out1: "off", out2: "off"}
  When: {in: "off"}
  Then: {out1: "off", out2: "off"}

Both syntaxes are equivalent and both are valid YAML. Sometimes, you may prefer writing YAML using white spaces, because it looks cleaner. Other times, perhaps for small tables, you may prefer writing in the more compact style. Notice that the less compact YAML starts each table row with a "-", and the When/State/Then statements are indented. For more information on YAML see:


  • in the YAML description, it points out that when writing YAML, tabs are not allowed. When you write your tables, even when indenting, use spaces. Remember: only use spaces for all your white space; tabs are bad! You've probably read this somewhere else, but we're repeating this, because you won't understand YAML's error messages if you haven't.

When writing your table files, make your file names end with ".yaml" or ".yml". Either of these two file name endings tell the system you have written your state table(s) in YAML.


  • though the Virtual Wiring System uses a YAML interpreter to parse all State Machine tables, it pre-processes the YAML first. Pre-processing removes known security issues with YAML "tags". For security reasons, YAML tags are not supported.

More Details about Table Rows

We've given an overview of the syntax for tables, table rows, and the When, State, and Then statements. For more information about Rows and When, State, and Then statements, see the Table Rows and When, State, Then Statements section.

There's a special kind of row we haven't covered yet. It has a single statement called an Initialize statement. Initialize statements cause State Machines to assume certain values at initialization time. To learn more about Initialize statements, see the Initialize Statement section.

Creating State Machines

Like all Devices, State Machines are created by running Scripts. To learn how to build State Machines, see the Building State Machines section.

Catalina Computing, LLC.

Copyright © Catalina Computing, LLC. (2013-2018)

Page last updated: Tue Jul 14 22:58:56 2015 (UTC)