Emulating a “state machine” in Node Red

Automating lights

I recently have begun using Node Red to setup automation around my Home Assistant instance.
One of things I'm using it for is to control lighting when watching a movie in Kodi.

I wanted it to:
- Turn off the lights when starting media playback
- Bring the lights on dimly when pausing
- Return the lights to full brightness when playback stops.

First attempt

So I set up some simple flows:
- When a kodi entered a playing state, wait 3 seconds, then if it's still playing, turn off the lights.
- When a kodi entered a paused state, wait 3 seconds, then if it's still paused, dim the lights.
- When a kodi entered an idle state, turn the lights back on.

Problems

However, I soon ran into some odd movie-watching experiences. For example, ff my daughter had turned off the HTPC whilst it was still playing (which she usually does), it would be left in a bad state; when I turned my HTPC back on, it would jump to the playing state, causing the lights to turn off just as I was sitting down on the couch.

I tried adding some more switch nodes based on previous state, but now my flow was too complicated - way more connections and checks running every which way.

Using a real State Machine

My first thought was of course, "ah, this is a job for a state machine!"

There are several state machine plugins available for node red:

I tried each of them. None of them quite worked for my usecase; some were too complex, and some were more concerned with their own internal state. I couldn't get any of them to do what I wanted...

Emulating a state machine

My end fix is quite simple! I use a Function node to combine the old state and new state into a single string, such as playing_paused:

var from_s = msg.data.old_state.state;
var to_s   = msg.data.new_state.state;
msg.states = from_s + "_" + to_s;
return msg;

Then my Switcher node operates off of that combined state:

noderedflow

Now idle_playing would turn off the lights like I wanted, but, off_playing wouldn't do anything.

Here is the flow if you want to import it as a reference.

JustinAiken

I'm a Ruby/Rails developer currently working for UserTesting, living in Southern Utah.
More about the author →