hufftheweevil / node-red-contrib-persistent-fsm Goto Github PK
View Code? Open in Web Editor NEWThis project forked from deancording/node-red-contrib-state-machine
A Node Red node that wraps around Javascript State Machine
License: Other
This project forked from deancording/node-red-contrib-state-machine
A Node Red node that wraps around Javascript State Machine
License: Other
I found a problem with this add-in where multiple state machines that all configure a non-zero initial delay end up emitting the exact same message on startup.
I created a reproduction scenario by creating three state machines across three flows, with an initial delay of 1, 2, and 3, all with a different persisted state. When re-starting the flows, all state machines emit the message that should only be emitted by the state machine I created last.
The problem appears only when initalDelay is non-zero, and seems to originate in this code in state-machine.js:
...
} else if (statePropertyType === 'msg') {
RED.events.on('flows:started', function () {
msg = {}
RED.util.setMessageProperty(msg, stateProperty, node.fsm.state)
if (+config.initialDelay) setTimeout(() => node.send(msg), config.initialDelay * 1000)
else node.send(msg)
})
The 'msg' that's created here in the msg = {}
statement seems to be created globally before the timeout ends. Therefore the message that's actually sent by all state machines across all your flows ends up being the same, being the one for the node that was initialized last. Suggest fixing this by replacing this by let msg = {}
Hello, I´m trying to learn and do some tests with the State Machine Node, but I´m having problems to use number comparisons. The = works fine, but the > #number seems to require a certain syntax that I´m not sure.
Below the very simple flow I´m testing:
And the configuration inside:
The >400 doesn´t work and it seems the Node waits for a string ">400" but not an actual number that is over 400.
The trigger State Node is configured to Number.
What am I doing wrong?
I have created a simple automaton consisting of three states and three triggers. Everything works fine, as long as I'm just playing with it, the states switch, no extra messages are displayed. Everything goes as it should.
But if I'm not in default state and make a deployment, the node switches to default state, despite "Persist state on re-deploy" checkbox.
What I expect from the "Persist state on re-deploy" checkbox is that the node's state will only be reset when the entire NodeRED is rebooted. I have recorded a short video with a demonstration. The version of nodered is 3.0.2, node-red-contrib-persistent-fsm is 1.2.1.
https://mega.nz/file/zwBkHKxB#ct0z15SjTgHjQnQSVAMwyNdsqxM8gIXSnufx4oHJOyA
I use Node Red as part of Home Assistant. The node-red-contrib-persistent-fsm
is amazing! It helps control many things like blinds and lights with very few buttons, as it keeps track of the state and can act intelligently with the next press on the same button.
This worked great until about two weeks ago. Suddenly, things started to act weird. For example, blinds started going into the open position in the morning. There was no change in the Node-Red flows at that time. I updated a few things (Home Assistant and Node Red itself), but I'm not sure how closely related that is.
I attach a quick screencast to demonstrate:
This is the flow export from this example:
[{"id":"bb4fd94092510052","type":"tab","label":"State Machine not persistent","disabled":false,"info":"","env":[]},{"id":"cbb840864cfb0a79","type":"inject","z":"bb4fd94092510052","name":"m","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"toggle","payloadType":"str","x":130,"y":120,"wires":[["1b3e44b669aa1d14"]]},{"id":"1b3e44b669aa1d14","type":"state-machine","z":"bb4fd94092510052","name":"Blinds","triggerProperty":"payload","triggerPropertyType":"msg","stateProperty":"payload","statePropertyType":"msg","initialDelay":"","persistOnReload":true,"outputStateChangeOnly":true,"throwException":false,"states":["closed","open"],"transitions":[{"name":"toggle","from":"closed","to":"open"},{"name":"toggle","from":"open","to":"closed"}],"x":290,"y":140,"wires":[["657d41f0b789875e"]]},{"id":"657d41f0b789875e","type":"switch","z":"bb4fd94092510052","name":"Action OPEN / CLOSE","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"OPEN","vt":"str"},{"t":"eq","v":"CLOSE","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":480,"y":140,"wires":[["1ffbcc68a82b3c6d"],["030ab2f9181a1911"]]},{"id":"1ffbcc68a82b3c6d","type":"api-call-service","z":"bb4fd94092510052","d":true,"name":"Blinds OPEN","server":"f915e846.bc0e18","version":5,"debugenabled":false,"domain":"cover","service":"set_cover_position","areaId":[],"deviceId":[],"entityId":["cover.markise_terrasse_alle"],"data":"{ \"position\": 25 }","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":750,"y":120,"wires":[[]]},{"id":"030ab2f9181a1911","type":"api-call-service","z":"bb4fd94092510052","d":true,"name":"Blinds CLOSE","server":"f915e846.bc0e18","version":5,"debugenabled":false,"domain":"cover","service":"set_cover_position","areaId":[],"deviceId":[],"entityId":["cover.markise_terrasse_alle"],"data":"{ \"position\": 100 }","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":760,"y":180,"wires":[[]]},{"id":"f915e846.bc0e18","type":"server","name":"Home Assistant","addon":true}]
I'm not quite sure where to start debugging. Maybe I'm doing something wrong? Maybe the Node Red update broke something? Maybe the underlying data storage is no longer working?
Any help would be greatly appreciated.
To see the problem add a state machine node to the flow and connect it to a debug node.
Open the state machine node, add a state and a transition specification. Leave the Initial Delay at 0.
Deploy and note that the "start" task message appears in the debug pane.
Open the state machine node and add another task. Perform a Modified Nodes deploy. Note that two "start" messages are seen.
Move the debug node a little and Deploy again, again two messages are produced.
If the state node is modified again then the number increases.
Restart node-red and then only one message is seen.
I believe the cause to be incorrect removal of the flows:started
event. If the line adding the event
RED.events.on('flows:started', function () {
is changed to
let f;
RED.events.on('flows:started', f=function () {
and the line removing it
RED.events.removeListener('flows:started', node.startup)
is changed to
RED.events.removeListener('flows:started', f)
then that appears to fix it. However I am no expert in this area so that may not be the correct way to fix it.
I am using fsm node 1.2.0, NR 3.0.2, nodejs 16.17.0
I configured a state machine node and added a Catch node to catch errors, but if I send it an erroneous payload the Catch node does not catch it. The error message does show in the debug log.
Is it intended that I should be able to catch it?
Using fsm node 1.2.0, NR 3.0.2, nodejs 16.17.0
Example flow
[{"id":"48923fbe9da4d810","type":"catch","z":"3e6e1c87317d6268","name":"","scope":["5ae741363f50a810"],"uncaught":false,"x":290,"y":580,"wires":[["6592204c93b6511e"]]},{"id":"6592204c93b6511e","type":"debug","z":"3e6e1c87317d6268","name":"ERROR","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":460,"y":580,"wires":[]},{"id":"5ae741363f50a810","type":"state-machine","z":"3e6e1c87317d6268","name":"fsm error test","triggerProperty":"payload","triggerPropertyType":"msg","stateProperty":"payload","statePropertyType":"msg","initialDelay":"0","persistOnReload":true,"outputStateChangeOnly":true,"throwException":true,"states":["start","going"],"transitions":[{"name":"Reset","from":"*","to":"start"},{"name":"Go","from":"start","to":"going"}],"x":310,"y":520,"wires":[["e7f3d459f828f671"]]},{"id":"c85a77d947810d2c","type":"inject","z":"3e6e1c87317d6268","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Go","payloadType":"str","x":110,"y":520,"wires":[["5ae741363f50a810"]]},{"id":"0a25a7398acc3198","type":"inject","z":"3e6e1c87317d6268","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Reset","payloadType":"str","x":110,"y":480,"wires":[["5ae741363f50a810"]]},{"id":"9fd5f57603dc8996","type":"inject","z":"3e6e1c87317d6268","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Error","payloadType":"str","x":110,"y":580,"wires":[["5ae741363f50a810"]]},{"id":"e7f3d459f828f671","type":"debug","z":"3e6e1c87317d6268","name":"STATE","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":450,"y":520,"wires":[]}]
Steps to reproduce:
Hi,
I created a flow with 2 state machine nodes. However, I see that the nodes create many messages during startup and even create messages for states they do not even have configured.
Does this library only support one node for each flow? Am I holding it wrong?
When serving a numeric trigger input, the code crashes with "TypeError: label.split is not a function"
as it's trying to parse the input as a string label & split it (which is not a valid operation on a number)
use-case: counting number of taps within a short time (double-tap emulation) to control transitions (while protecting from invalid states & keeping track of the current state)
Node Red 2.1.4
node-red-contrib-persistent-fsm 1.1.0
I'm using Node-Red via Home Assistant integration (10.3.0) on Raspberry Pi 3
I did some upgrades and one of my flows started sending notifications whenever I deployed any other flow (fully or partially), after some careful investigation I realized that the state machine started sending messages, I managed to recreate a very simple variant of my flow to present problem:
I use the Output Only On State Change
option but my initial state is sent now, this is something that did not happen before upgrading.
I consider it a regression, if the output is only allowed on state change then sending the initial state would suggest that we switched state to initial from something else which is incorrect.
For those looking for a quick and dirty fix, you can add change node
before state machine and add some constant value at the random, unused, field, then you can filter all messages after state machine, those without this field are not allowed to go futher - this way you only process the messages that triggered state machine in such case.
After incorrectly assuming this was an internal Node RED node, I believe this is the node responsible for the State Machine node I'm using (Installed with Home Assistant).
The state machine node does not pass along the correct payload output (when set to msg.payload
). However, when leaving the setting blank the correct output is passed along.
Expected behavior is to correctly pass along the payload output or remove the requirement from the settings panel (i.e. allow the State Output field to be blank). Currently if leaving the field blank, node red will complain that the flow is not properly configured although the state machine node behaves as expected.
Steps to reproduce is use the following example flows, then inject the following nodes in order:
In the first flow example (at the top), you'll see that the output is as follows:
2/17/2022, 3:39:57 AM[node: 3b5ce3c79e4f6d48]
msg.payload : string[12]
"started_code"
2/17/2022, 3:39:58 AM[node: 3b5ce3c79e4f6d48]
msg.payload : string[12]
"entered_code"
However in the second example flow, you'll see that the correct output (entire) object is as follows:
2/17/2022, 3:41:07 AM[node: 01eaa788477ab975]
started_code : msg.payload : Object
{ action: "started_code" }
2/17/2022, 3:41:08 AM[node: 01eaa788477ab975]
entered_code : msg.payload : Object
{ action: "entered_code", pin: "1234" }
[{"id":"a882694fff3a7c97","type":"tab","label":"State Machine Bug","disabled":false,"info":"","env":[]},{"id":"bc8915c160d7d096","type":"state-machine","z":"a882694fff3a7c97","name":"","triggerProperty":"payload.action","triggerPropertyType":"msg","stateProperty":"payload","statePropertyType":"msg","initialDelay":"0","persistOnReload":false,"outputStateChangeOnly":true,"throwException":true,"states":["default","started_code","entered_code","reset"],"transitions":[{"name":"started_code","from":"default","to":"started_code"},{"name":"entered_code","from":"started_code","to":"entered_code"},{"name":"reset","from":"started_code","to":"default"},{"name":"reset","from":"entered_code","to":"default"}],"x":480,"y":180,"wires":[["3b5ce3c79e4f6d48"]]},{"id":"3b5ce3c79e4f6d48","type":"debug","z":"a882694fff3a7c97","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":650,"y":180,"wires":[]},{"id":"76c9300f2c217d66","type":"inject","z":"a882694fff3a7c97","name":"started_code","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"action\": \"started_code\" }","payloadType":"json","x":110,"y":120,"wires":[["bc8915c160d7d096"]]},{"id":"06ae0dc1696f4521","type":"inject","z":"a882694fff3a7c97","name":"entered_code","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"action\": \"entered_code\", \"pin\": \"1234\" }","payloadType":"json","x":110,"y":160,"wires":[["bc8915c160d7d096"]]},{"id":"adee97ad1c29fec2","type":"inject","z":"a882694fff3a7c97","name":"reset","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"action\": \"reset\" }","payloadType":"json","x":90,"y":240,"wires":[["bc8915c160d7d096"]]},{"id":"a475d0265f50c707","type":"state-machine","z":"a882694fff3a7c97","name":"","triggerProperty":"payload.action","triggerPropertyType":"msg","stateProperty":"","statePropertyType":"msg","initialDelay":"0","persistOnReload":false,"outputStateChangeOnly":true,"throwException":true,"states":["default","started_code","entered_code","reset"],"transitions":[{"name":"started_code","from":"default","to":"started_code"},{"name":"entered_code","from":"started_code","to":"entered_code"},{"name":"reset","from":"started_code","to":"default"},{"name":"reset","from":"entered_code","to":"default"}],"x":480,"y":480,"wires":[["e533e96241f4c106"]]},{"id":"e533e96241f4c106","type":"debug","z":"a882694fff3a7c97","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":650,"y":480,"wires":[]},{"id":"9f5bc1591bab3d2a","type":"inject","z":"a882694fff3a7c97","name":"started_code","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"action\": \"started_code\" }","payloadType":"json","x":110,"y":420,"wires":[["a475d0265f50c707"]]},{"id":"d075c62995f6e9f7","type":"inject","z":"a882694fff3a7c97","name":"entered_code","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"action\": \"entered_code\", \"pin\": \"1234\" }","payloadType":"json","x":110,"y":460,"wires":[["a475d0265f50c707"]]},{"id":"74aa837fc903e414","type":"inject","z":"a882694fff3a7c97","name":"reset","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"action\": \"reset\" }","payloadType":"json","x":90,"y":540,"wires":[["a475d0265f50c707"]]}]
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.