Call service from custom change in state

Hi Node RED newbie here.

I am trying to create a flow that will send me a notification based on the current state of my solar generation - see image below.

I have grouped the state change into 6 broader categories on the right using a switch node. This flow works fine, the only issue is that it triggers with every single state change, which triggers every couple of seconds, sending a new notification.

I would like a notification to only be sent if there is a change from one of the 6 broader categories to another.
My idea is to have the call service / notifcation linked to a change in state node that reads from a custom state change, but would appreciate some guidance in understand if it’s possible, how to get it set-up, or if there is an easier alternative solution.

Apologies if I haven’t explained that very well, but I’ve also attached the JSON below and am happy to clarify any confusing points.

[{“id”:“acce53c80779becd”,“type”:“api-call-service”,“z”:“e5b7524716e32d69”,“name”:“Energy Level 1”,“server”:“790d432d.fb71ac”,“version”:5,“debugenabled”:false,“domain”:“notify”,“service”:“mobile_app_fone”,“areaId”:,“deviceId”:,“entityId”:,“data”:“{\t "title": "Energy Level",\t "message": "",\t "data": {\t "notification_icon": "mdi:numeric-1-box",\t "tag": "energy",\t "channel": "energy" \t } \t}”,“dataType”:“jsonata”,“mergeContext”:“”,“mustacheAltTags”:false,“outputProperties”:,“queue”:“none”,“x”:700,“y”:350,“wires”:[]},{“id”:“6a2c70d3e54c30e1”,“type”:“api-call-service”,“z”:“e5b7524716e32d69”,“name”:“Energy Off”,“server”:“790d432d.fb71ac”,“version”:5,“debugenabled”:false,“domain”:“notify”,“service”:“mobile_app_fone”,“areaId”:,“deviceId”:,“entityId”:,“data”:“{\t "title": "Energy Off",\t "message": "",\t "data": {\t "notification_icon": "mdi:weather-sunny-off",\t "tag": "energy",\t "channel": "energy" \t } \t}”,“dataType”:“jsonata”,“mergeContext”:“”,“mustacheAltTags”:false,“outputProperties”:,“queue”:“none”,“x”:690,“y”:270,“wires”:[]},{“id”:“24c1efddae3c96c9”,“type”:“switch”,“z”:“e5b7524716e32d69”,“name”:“”,“property”:“payload”,“propertyType”:“msg”,“rules”:[{“t”:“lt”,“v”:“1000”,“vt”:“str”},{“t”:“btwn”,“v”:“1001”,“vt”:“num”,“v2”:“1999”,“v2t”:“num”},{“t”:“btwn”,“v”:“2000”,“vt”:“num”,“v2”:“2999”,“v2t”:“num”},{“t”:“btwn”,“v”:“3000”,“vt”:“num”,“v2”:“3999”,“v2t”:“num”},{“t”:“btwn”,“v”:“4000”,“vt”:“num”,“v2”:“4799”,“v2t”:“num”},{“t”:“gte”,“v”:“4800”,“vt”:“str”}],“checkall”:“true”,“repair”:false,“outputs”:6,“x”:430,“y”:520,“wires”:[[“6a2c70d3e54c30e1”],[“acce53c80779becd”],[“6958df5ecb8ad3c9”],[“f9684d8a09c59f0e”],[“c7e8ae5832f6f86c”],[“ae27565b7bf88d8c”]]},{“id”:“15ca9067a03f40bc”,“type”:“server-state-changed”,“z”:“e5b7524716e32d69”,“name”:“”,“server”:“790d432d.fb71ac”,“version”:4,“exposeToHomeAssistant”:false,“haConfig”:[{“property”:“name”,“value”:“”},{“property”:“icon”,“value”:“”}],“entityidfilter”:“sensor.pv_power”,“entityidfiltertype”:“exact”,“outputinitially”:false,“state_type”:“str”,“haltifstate”:“”,“halt_if_type”:“num”,“halt_if_compare”:“gt”,“outputs”:1,“output_only_on_state_change”:true,“for”:“15”,“forType”:“num”,“forUnits”:“minutes”,“ignorePrevStateNull”:false,“ignorePrevStateUnknown”:false,“ignorePrevStateUnavailable”:false,“ignoreCurrentStateUnknown”:false,“ignoreCurrentStateUnavailable”:false,“outputProperties”:[{“property”:“payload”,“propertyType”:“msg”,“value”:“”,“valueType”:“entityState”},{“property”:“data”,“propertyType”:“msg”,“value”:“”,“valueType”:“eventData”},{“property”:“topic”,“propertyType”:“msg”,“value”:“”,“valueType”:“triggerId”}],“x”:190,“y”:520,“wires”:[[“24c1efddae3c96c9”]]},{“id”:“6958df5ecb8ad3c9”,“type”:“api-call-service”,“z”:“e5b7524716e32d69”,“name”:“Energy Level 2”,“server”:“790d432d.fb71ac”,“version”:5,“debugenabled”:false,“domain”:“notify”,“service”:“mobile_app_fone”,“areaId”:,“deviceId”:,“entityId”:,“data”:“{\t "title": "Energy Level",\t "message": "",\t "data": {\t "notification_icon": "mdi:numeric-2-box",\t "tag": "energy",\t "channel": "energy" \t } \t}”,“dataType”:“jsonata”,“mergeContext”:“”,“mustacheAltTags”:false,“outputProperties”:,“queue”:“none”,“x”:700,“y”:450,“wires”:[]},{“id”:“f9684d8a09c59f0e”,“type”:“api-call-service”,“z”:“e5b7524716e32d69”,“name”:“Energy Level 3”,“server”:“790d432d.fb71ac”,“version”:5,“debugenabled”:false,“domain”:“notify”,“service”:“mobile_app_fone”,“areaId”:,“deviceId”:,“entityId”:,“data”:“{\t "title": "Energy Level",\t "message": "",\t "data": {\t "notification_icon": "mdi:numeric-3-box",\t "tag": "energy",\t "channel": "energy" \t } \t}”,“dataType”:“jsonata”,“mergeContext”:“”,“mustacheAltTags”:false,“outputProperties”:,“queue”:“none”,“x”:700,“y”:530,“wires”:[]},{“id”:“c7e8ae5832f6f86c”,“type”:“api-call-service”,“z”:“e5b7524716e32d69”,“name”:“Energy Level 4”,“server”:“790d432d.fb71ac”,“version”:5,“debugenabled”:false,“domain”:“notify”,“service”:“mobile_app_fone”,“areaId”:,“deviceId”:,“entityId”:,“data”:“{\t "title": "Energy Level",\t "message": "",\t "data": {\t "notification_icon": "mdi:numeric-4-box",\t "tag": "energy",\t "channel": "energy" \t } \t}”,“dataType”:“jsonata”,“mergeContext”:“”,“mustacheAltTags”:false,“outputProperties”:,“queue”:“none”,“x”:700,“y”:630,“wires”:[]},{“id”:“ae27565b7bf88d8c”,“type”:“api-call-service”,“z”:“e5b7524716e32d69”,“name”:“Energy Level 5”,“server”:“790d432d.fb71ac”,“version”:5,“debugenabled”:false,“domain”:“notify”,“service”:“mobile_app_fone”,“areaId”:,“deviceId”:,“entityId”:,“data”:“{\t "title": "Energy Level",\t "message": "",\t "data": {\t "notification_icon": "mdi:numeric-5-box",\t "tag": "energy",\t "channel": "energy" \t } \t}”,“dataType”:“jsonata”,“mergeContext”:“”,“mustacheAltTags”:false,“outputProperties”:,“queue”:“none”,“x”:700,“y”:730,“wires”:[]},{“id”:“790d432d.fb71ac”,“type”:“server”,“name”:“Home Assistant”,“addon”:true}]

I figured it out in case anyone else has the same issue.

I created an input_number in the configuration.yaml file and used that as a variable to store solar energy levels.

If you are interested in making it less cumbersome and without needing to create any extra entity this flow will do it. Fill the blanks with the appropiate info.

[{"id":"8427f251dfba7e9f","type":"server-state-changed","z":"878e74c2.7f39c8","name":"sensor.pv_power","server":"9405c3fe.d0a6c","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"light.c2c_triangulos","entityidfiltertype":"exact","outputinitially":false,"state_type":"num","haltifstate":"","halt_if_type":"num","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":true,"ignorePrevStateUnknown":true,"ignorePrevStateUnavailable":true,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"level","propertyType":"msg","value":"$entity().state <= 1000 ? \"off\" : $entity().state > 1000 and $entity().state <= 2000 ? \"Level 1\" : $entity().state > 2000 and $entity().state <= 3000 ? \"Level 2\" : $entity().state > 3000 and $entity().state <= 4000 ? \"Level 3\" : $entity().state > 4000 and $entity().state <= 4800 ? \"Level 4\" : \"Level 5\"","valueType":"jsonata"}],"x":340,"y":1760,"wires":[["e06b07d2637b2ab8"]]},{"id":"e06b07d2637b2ab8","type":"rbe","z":"878e74c2.7f39c8","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":false,"property":"level","topi":"topic","x":490,"y":1760,"wires":[["c3dce6c55abb567d"]]},{"id":"c3dce6c55abb567d","type":"api-call-service","z":"878e74c2.7f39c8","name":"","server":"9405c3fe.d0a6c","version":5,"debugenabled":false,"domain":"notify","service":"","areaId":[],"deviceId":[],"entityId":[],"data":"{\"message\": \"Energy \" & level }","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":640,"y":1760,"wires":[[]]},{"id":"9405c3fe.d0a6c","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]
1 Like

Wow thanks so much for putting that together, I gave it a go and it works great. Much sleeker version than what I slapped together.

What language is the expression / query written in, and any tips on learning more on it?

It’s JSONata, not to confuse with regular JSON, they are very similar though. I learnt it through examples in this forum but I’m sure you’ll be able to find some docs somewhere.

here’s some basic stuff

1 Like

FYI, I just realized you can simplifly my first expression to this one and it’ll work exactly the same way.

$entity().state <= 1000 ? "off" : $entity().state <= 2000 ? "Level 1" : $entity().state <= 3000 ? "Level 2" : $entity().state <= 4000 ? "Level 3" : $entity().state <= 4800 ? "Level 4" : "Level 5"
1 Like

On top of that, if you open, for example, a change node, set expression and click the three dots a new menu will open with all the available JSONata functions and a short tutorial for each one below.

1 Like

Ah yes I see, very elegent simplfication.

Thanks for that link and the tips in the expression editor. I had come across the some of the terminology before and definitely knew what I wanted to do was refer to the entity’s value so seeing it all explained is very information :smiley: