Node-Red Palette: Common Choices

That kind of looks like a Finite State Machine.. here is a simple example I am using for home/away stuff...

That's what I was thinking after seeing your and others posts a few days ago before I raised the question but I wanted to get something going quick without getting up to speed with FSM nodes or more json. Past this there's no doubt a wealth of useful stuff once you get past the scary bits.

1 Like

I am not really all that familiar with FSMs having never used them. For my simple case it seems to work well. The JSON bit just defines the transitions. There is another node that doesn't use it:

The concept can be a little mind bending especially for more complex situations.

I was wondering if you or anyone has used the node-red-contrib-persistent-fsm much? The reason I ask is that have started working with it. It has a lot of things to recommend it. I like the interface for building the FSM along with the graphical representation that it generates automatically.

But..... and it is a big but, I have found that once I integrated it into a flow, I was getting some really weird behavior out of node red. For example, I had the output going to a switch node that then sent triggers to various actions I wanted to have happen when it reached specific states. So far so good. I was having debugging issues with the downstream flows and so wanted to take the FSM out of the flow temporarily and trigger the downstream manually. I did this by removing the connection from the fsm to the switch node. This is where it got bizarre. After redeploying the flow, the switch node would see and then process a duplicate of the exact same msg that it had last received from the FSM node. It would do this every time I would do a redeploy.

I have since replicated this behavior with a debug node. All in all, a strange event. I am thinking about abandoning it for one of the other fsm options or just using it think about the states but not even use an fsm.

Has anyone tried this and experienced this behavior?

I have not used the persistent-fsm node. I use the node-red-contrib-finite-state-machine as I like the ability to configure via JSON but still get the nice diagram.

I've never really used FSM in any of my prior stuff so tend to get a little confused when going beyond the simpler models.

Thanks for the quick feedback. I will probably try that one. It looks more powerful (though more complex as lots of thinking in terms of JSON). That said, I build a lot of function nodes so complexity in pursuit of a desired outcome isn't the worst thing.

1 Like

It's just that in my years of coding I never used the FSM concept so it is not something I am completely comfortable with yet. I think it is a cool idea and can be used to solve specific problems but imagine there is a complexity limit were it becomes impractical.

I do like writing function nodes too - easy compact way of getting things done the way you want. Keep in mind that the function node runs in a sandbox so can potentially slow things down if calling it iteratively etc. not sure about the impact of the sandbox so striking it out.

At home I almost always only have 2-3 states max, so no real reason to use a FSM.

FSM can be really handy when you have a LOT of states and transition scenarios, though. Or if you want to do something different specifically on the specific transition happening, and not just the new state. I use it at work a lot on complex controls. That said, I always found FSM more work than benefit with only 2-3 states.

1 Like

Well for my simple learning use-case - a 4 state transition (home/away/guest-home/guest-away) it's working pretty well. Much beyond that and I guess I would have to start mainlining ibuprofen for the epic headaches that would occur... :laughing: :dizzy_face:

I have seen a lot of references to the potential slowdown from function nodes (including ways to get around the sandboxing at the cost of security risk), but have never been able to experience a downside. There is one downside, it makes it hard to share with anyone that has less javascript experience than you have and for complex functions any programmer regardless of experience can have a hard time slogging through someone else's code.

The new nice answer is encapsulate your function nodes in a subflow then convert your subflow to a custom module!!!! Works great!!!!

1 Like

That would work great as long as you don't put any flow specific info into the function code itself (a quick and slightly hacky workaround).

Yep - but you can control a lot of that through environment variables and msg properties.

I am starting to play around with custom modules because I can create a set of nodes for my clients that are not as easily messed with and I can update via remote access . It's very promising so far.

The example situation I am trying to learn is this:
I have an old iPhone that sits in place as a podcast player. I found however that the battery eventually went dead and took the phone down with it from constantly being 100% charged. So no I have started to charge it, then turn off the outlet let it run down and then restart charging by powering the outlet back up.

I have found that when the power comes up, because it is connected a speaker via bluetooth, it starts playing, so I have to disconnect it temporarily from bluetooth before turning on the power. This is the diagram of the FSM:
image

If this works out well (and I think it is very close other than the weird redeploy behavior with the persistent-fsm node), then I plan to go after climate control where I factor in all of these things:

  1. Upstairs vs Downstairs Thermostat/System
  2. Heating/Cooling/Off
  3. Fan On/Circulate/Auto
  4. Future weather forecast (willing to let it get a little uncomfortable if nature will heat or cool my house tomorrow)
  5. Room by room temperature (potentially driving Fan condition)

I hope to eventually have automated dampers for airflow, but the list above is enough complexity to get started.

How do you detect the power situation? through the smart plug? Also how does the system know the device's bluetooth has been connected or not?

Here's my take on that FSM - but again I am still new to this concept so ymmv.

Sample FSM - requires the node-red-contrib-finite-state-machine

[{"id":"7c7d6927.99e3b8","type":"finite-state-machine","z":"af91369d.586338","name":"","fsmDefinition":"{\"state\":{\"status\":\"iPhoneCharged_BTConnected\"},\"transitions\":{\"iPhoneCharged_BTConnected\":{\"low_charge\":\"iPhoneNotCharged_BTConnected\",\"bt_disconnected\":\"iPhoneCharged_BTDisconnected\"},\"iPhoneCharged_BTDisconnected\":{\"low_charge\":\"iPhoneNotCharged_BTDisconnected\",\"bt_connected\":\"iPhoneCharged_BTConnected\"},\"iPhoneNotCharged_BTConnected\":{\"full_charge\":\"iPhoneCharged_BTConnected\",\"bt_disconnected\":\"iPhoneCharged_BTDisconnected\"},\"iPhoneNotCharged_BTDisconnected\":{\"full_charge\":\"iPhoneCharged_BTDisconnected\",\"bt_connected\":\"iPhoneNotCharged_BTConnected\"}}}","sendInitialState":true,"sendStateWithoutChange":false,"showTransitionErrors":false,"x":730,"y":2880,"wires":[["a107ad4c.acd0d"]]},{"id":"9847816c.c5905","type":"inject","z":"af91369d.586338","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"low_charge","x":410,"y":2800,"wires":[["7c7d6927.99e3b8"]]},{"id":"e5092892.105dc8","type":"inject","z":"af91369d.586338","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"full_charge","x":410,"y":2840,"wires":[["7c7d6927.99e3b8"]]},{"id":"13de600d.e37ca","type":"inject","z":"af91369d.586338","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"bt_connected","x":410,"y":2920,"wires":[["7c7d6927.99e3b8"]]},{"id":"7ee08740.a97f48","type":"inject","z":"af91369d.586338","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"bt_disconnected","x":420,"y":2960,"wires":[["7c7d6927.99e3b8"]]},{"id":"a107ad4c.acd0d","type":"switch","z":"af91369d.586338","name":"iPhoneCharged_BTConnected \\n iPhoneCharged_BTDisconnected \\n iPhoneNotCharged_BTConnected \\n iPhoneNotCharged_BTDisconnected","property":"payload.status","propertyType":"msg","rules":[{"t":"eq","v":"iPhoneCharged_BTConnected","vt":"str"},{"t":"eq","v":"iPhoneCharged_BTDisconnected","vt":"str"},{"t":"eq","v":"iPhoneNotCharged_BTConnected","vt":"str"},{"t":"eq","v":"iPhoneNotCharged_BTDisconnected","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":1090,"y":2880,"wires":[["9811a637.f37668"],["826246ab.d01458"],["6b6a2b37.708874"],["8a72cf27.6f95e"]]},{"id":"9811a637.f37668","type":"debug","z":"af91369d.586338","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.status","targetType":"msg","statusVal":"","statusType":"auto","x":1510,"y":2820,"wires":[]},{"id":"826246ab.d01458","type":"debug","z":"af91369d.586338","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.status","targetType":"msg","statusVal":"","statusType":"auto","x":1510,"y":2860,"wires":[]},{"id":"6b6a2b37.708874","type":"debug","z":"af91369d.586338","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.status","targetType":"msg","statusVal":"","statusType":"auto","x":1510,"y":2900,"wires":[]},{"id":"8a72cf27.6f95e","type":"debug","z":"af91369d.586338","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.status","targetType":"msg","statusVal":"","statusType":"auto","x":1510,"y":2940,"wires":[]}]

Unfortunately, the power situation is a pain involving extra complexity. I am using the Apple iCloud integration within Home Assistant to get the battery readings of the iPhone every 30 minutes. I have looked at Home Assistant enough to see that it could become a bottomless pit of time consumption, but I am really only using it for iCloud and to act as a Homekit Controller. I feed all of that info to Node Red.

The bluetooth speaker happens to be an Amazon Echo and so using the cakebaked Alexa nodes, I can get the bluetooth connection status into Node Red.
Edit: I also use the Alexa nodes to disconnect and reconnect the iPhone to Echo bluetooth connection.

My new FSM looks quit similar to what you have. Here is the JSON I created:

{
    "state": {
        "status": "Needs Power Bluetooth Disconnected"
    },
    "transitions": {
        "Needs Power Bluetooth Connected": {
            "Disconnected Bluetooth": "Needs Power Bluetooth Disconnected"
        },
        "Needs Power Bluetooth Disconnected": {
            "Charging Started": "Charging Bluetooth Disconnected"
        },
        "Charging Bluetooth Disconnected": {
            "Reconnected Bluetooth": "Charging Bluetooth Connected"
        },
        "Charging Bluetooth Connected": {
            "Charged to Desired Level": "Finished Charging"
        },
        "Finished Charging": {
            "Low Power Detected": "Needs Power Bluetooth Connected"
        }
    }
}

The one thing I am trying to figure out is the persistent FSM had the option of something like this to say from any state when this tranistion happens go to that state. Kind of the equivalent of this JSON inside transitions:

"*": {
    "Low Power Detected": "Needs Power Bluetooth Connected"
}

This doesn't actually seem to work here and so I am wondering if it is just syntax or if I just need to build all of the combinations into my JSON.

Edit 2: Turns out it was not that hard to essentially turn Low Power detected into a reset via a little cut and paste in the JSON:

{
    "state": {
        "status": "Needs Power Bluetooth Disconnected"
    },
    "transitions": {
        "Needs Power Bluetooth Connected": {
            "Disconnected Bluetooth": "Needs Power Bluetooth Disconnected",
            "Low Power Detected": "Needs Power Bluetooth Connected"
        },
        "Needs Power Bluetooth Disconnected": {
            "Charging Started": "Charging Bluetooth Disconnected",
            "Low Power Detected": "Needs Power Bluetooth Connected"
        },
        "Charging Bluetooth Disconnected": {
            "Reconnected Bluetooth": "Charging Bluetooth Connected",
            "Low Power Detected": "Needs Power Bluetooth Connected"
        },
        "Charging Bluetooth Connected": {
            "Charged to Desired Level": "Finished Charging",
            "Low Power Detected": "Needs Power Bluetooth Connected"
        },
        "Finished Charging": {
            "Low Power Detected": "Needs Power Bluetooth Connected"
        }
    }
}
1 Like

Okay cool - I find this stuff interesting and your take on it gives me some things to think about. My original idea was that things like "charged" and "connected" actions are transitions while device states like "BTConnected Low Power" are, well, "states".

States:
BT Connected / Low Power
BT Disconnected / Low Power
BT Connected / Charged to Desired State
BT Disconnected / Charged to Desired State

and transitions:
charging
connecting
charged
connected

Note: Added connecting and charging which just transition back to the same state until done...

{
    "state": {
        "status": "iPhoneCharged_BTConnected"
    },
    "transitions": {
        "iPhoneCharged_BTConnected": {
            "low_charge": "iPhoneNotCharged_BTConnected",
            "bt_disconnected": "iPhoneCharged_BTDisconnected"
        },
        "iPhoneCharged_BTDisconnected": {
            "low_charge": "iPhoneNotCharged_BTDisconnected",
            "bt_connected": "iPhoneCharged_BTConnected"
        },
        "iPhoneNotCharged_BTConnected": {
            "full_charge": "iPhoneCharged_BTConnected",
            "bt_disconnected": "iPhoneCharged_BTDisconnected",
			"charging": "iPhoneNotCharged_BTConnected"
        },
        "iPhoneNotCharged_BTDisconnected": {
            "full_charge": "iPhoneCharged_BTDisconnected",
            "bt_connected": "iPhoneNotCharged_BTConnected",
            "connecting": "iPhoneNotCharged_BTDisconnected"
        }
    }
}

I use the node-red-contrib-join-wait node.

It works to trigger when two events are true within a set time frame.

1 Like

You could also use the system "trigger" node.. or maybe "simple-message-queue" as well depending upon the use-case.

Saw that node but I see that it doesn't evaluate the order in which the messages arrive, which is what I needed for my particular case.