Ian,
was half-way through writing this response, then saw Julian's reply... so there's some repetition here, but also some additional thoughts. There are lots of ways to do this, so don't be surprised if you get a number of suggestions :)
Building a flow that maintains the state over restarts of node-red adds a fair amount of complication if you're new to this stuff. If that isn't important to you (at this stage), here are a couple ways of doing it.
Function node
the function node can store data in a local 'context' that is maintained between each invocation of the node. This lets you store some state to help your flow decide what to do.
Here's a function that will only pass through a message the each time it drops below 26 (and not repeat).:
// If voltage drops below level and we
// haven't sent an alert...
if (msg.payload < 26 && !context.sentAlert) {
// Set the content of the alert
msg.payload = "Oh no!";
// Set the flag so we know not to alert again
context.sentAlert = true;
// Return the message so the alert is sent
return msg;
} else if (msg.payload > 26) {
// Voltage back to normal, reset alert flag
context.sentAlert = false;
}
// Return null so no message is sent
return null;
Trigger node
the trigger node (which I only got to grips with properly this week... yeah, I know) can also be used. Here's an example flow you can import:
[{"id":"aceb3f69.5314c","type":"inject","name":"","topic":"","payload":"24","payloadType":"string","repeat":"","crontab":"","once":false,"x":190,"y":396,"z":"396c2376.c693dc","wires":[["e63f7184.19c09"]]},{"id":"f7a927e7.0856d8","type":"inject","name":"","topic":"","payload":"27","payloadType":"string","repeat":"","crontab":"","once":false,"x":194,"y":448,"z":"396c2376.c693dc","wires":[["e63f7184.19c09"]]},{"id":"e63f7184.19c09","type":"switch","name":"","property":"payload","rules":[{"t":"lt","v":"26"},{"t":"else"}],"checkall":"true","outputs":2,"x":355,"y":412,"z":"396c2376.c693dc","wires":[["289ed11e.d7612e"],["7c1c26d9.83e3d8"]]},{"id":"fbece5a6.041318","type":"debug","name":"","active":true,"console":"false","complete":"false","x":838,"y":397,"z":"396c2376.c693dc","wires":[]},{"id":"289ed11e.d7612e","type":"trigger","op1":"1","op2":"0","op1type":"pay","op2type":"val","duration":"0","extend":"false","units":"ms","name":"","x":611,"y":405,"z":"396c2376.c693dc","wires":[["fbece5a6.041318"]]},{"id":"7c1c26d9.83e3d8","type":"change","name":"","rules":[{"t":"set","p":"reset","to":"1"}],"action":"","property":"","from":"","to":"","reg":false,"x":475,"y":469,"z":"396c2376.c693dc","wires":[["289ed11e.d7612e"]]}]
The initial switch node separates out payloads below 26 and those above. Those below 26 are passed straight to the trigger node. That node is set to pass the first message through and the block all subsequent messages until one arrives with the msg.reset property set. The messages above 26 get msg.reset set, so when they arrive at the trigger node, it gets reset, ready for the next <26 message to arrive and be passed through.
Between the two approaches, the first is more self-contained as it is a single Function node, but does assume you're comfortable maintaining that code. The second has a couple more nodes, but not a single line of code written.
To Julian's point, if you want to preserve the state over restarts of node-red, then you need some other mechanism in place to save that state. That wouldn't work with the Trigger node solution, but the Function node one would - you'd need a flow set to run on start up (the Inject node can do this, for example), that retrieves the state from some external source (such as a database or file), and initialises the function context flag. This adds a few more moving parts, so more in the category of 'running' rather than 'walking'.
Nick