Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Request] Persistent state of nodes #227

Closed
AlecTBM opened this issue Jan 4, 2022 · 12 comments
Closed

[Request] Persistent state of nodes #227

AlecTBM opened this issue Jan 4, 2022 · 12 comments

Comments

@AlecTBM
Copy link

AlecTBM commented Jan 4, 2022

Hello and thank you for a very good project!

Is it possible to make the state of each node persistent? As it is now, somethines when a node or management is updated the state is lost and the nodes goes to a default or "unknown" state.
On my thermostat all of them goes to the "Minimum threshold" for both ambient and setpoint until I either send them a message or interact with them via google. And since I mostly only update the ambient temperature, then the setpoint gets out of sync with the actual setpoint.

If it is not possible or priority to make this persistent in this project, do anyone have any workarounds? 😃
I was thinking about buffering the last state as a context saved to file, but I have not been able to find a way to detect when the state goes to Unknown or default.

Example on a TV node with unknown state:
image

@ckhmer1
Copy link
Contributor

ckhmer1 commented Jan 5, 2022

You are able to persiste the node state in the following way:

  • send a message in input to the node with topic "GetState", the node will respond with a message in output containing the full node state. You can store the message in a persistant storage and send it back to the node when You have to restore the previous state.
  • Configure the "set_state message delay (s)" with a value different by 0, if a node state changes, after this seconds the management node will send in output a message with all the nodes state. You can save this message to a persistent storage and sent it back when You need to restore the state.

@Paul-Reed
Copy link
Contributor

Hi @ckhmer1 - firstly, a belated 'Happy New Year' from the UK, and thanks for all the work which you have done here, especially during the past year.

Could I ask if GetState is documented anywhere? I've searched the node docs and also the Google documentation but can't find any reference, or is this a function which you have added purely for this node?
And are there any other commands which users may find useful, other than those already documented.

@AlecTBM
Copy link
Author

AlecTBM commented Jan 5, 2022

You are able to persiste the node state in the following way:

  • send a message in input to the node with topic "GetState", the node will respond with a message in output containing the full node state. You can store the message in a persistant storage and send it back to the node when You have to restore the previous state.
  • Configure the "set_state message delay (s)" with a value different by 0, if a node state changes, after this seconds the management node will send in output a message with all the nodes state. You can save this message to a persistent storage and sent it back when You need to restore the state.

Thank you for your reply @ckhmer1
I actually found out that you get the state of all nodes if you send a message to the management node with the topic "get_state", I suppose that is what you meant and not "GetState"? or am I missing something.
And you restore the state with "set_state" as topic and the payload from "get_state" response as a payload, correct?

Since I am not 100% sure when the state is lost, any suggestion to when to send the "set_state"? I was thinking as a last resort to do it every deployment, but not sure how to trigger it.

@Paul-Reed I found about the "get_state" and "set_state" from the help bar for the management node:
image

@Paul-Reed
Copy link
Contributor

Paul-Reed commented Jan 5, 2022

@AlecTBM I think the easiest way to do this is to use option 2 - using the output from the Management node, as it will save the current state to context automatically.
First set the "set_state message delay (s)" in the Management node to something short like 5 seconds.
Then add a function node to the output of the management node containing...

// Replace with the device node ID
let id = "1f998b8914071db8"
// Replace "table_lamp" with a suitable name, ie "thermostat"
// and preferably use 'file context' - to survive a reboot
// similar to global.set("table_lamp",msg.payload[id],"disk")
if (msg.payload[id]) {
    global.set("table_lamp",msg.payload[id])
}
else {
    return
    }

Where the 'device node ID' is the node ID of your thermostat, switch or whatever...
This will save the last known state to context, 5 seconds after the last command.

Then finally, add an inject node to inject the state into your thermostat whenever you do a full deploy or reboot.
inject_node

So it looks like;

flow_full

Hope this helps!

@ckhmer1
Copy link
Contributor

ckhmer1 commented Jan 6, 2022

Hi @Paul-Reed,
Happy new year to all from Italy.
The documentations of the device node is not sufficient, but it has so many feature that I need time to arrange.
I'd like to create a documentations similar to the one I've created for my other project, connecting Alexa to Node-RED, of you agree.

Regarding the persistent storage, I'll add a flag to the device node to allow the user to disable the auto save state selectively for some nodes, in this way there is no need to know the nice ID

@AlecTBM
Copy link
Author

AlecTBM commented Jan 6, 2022

Thank you for all the interests in this. I did make another solution last night that I did not get time to write.
Since I want to keep all the nodes persistent, I do not separate them for each device as @Paul-Reed do.

Everytime the management node is either sending the "set_state" or is responding on the "get_state" message, I save it to context on file.
When the management node send out a message about the server is started, I send a "set_state" message with payload from context.
image

For me this is solution should be enough 😃 the only question is if there is other circumstances where i would need to load from context other than "server start". And I know that if I for example change the setpoint on a thermostat, the management node don't send a "set_state" message, but I have so many devices that one of them is sending an update at least every 15 minutes, so this is not a big case for me.

@ckhmer1
Copy link
Contributor

ckhmer1 commented Jan 7, 2022

It should be enough to restore back the state when the server is started.

@Paul-Reed
Copy link
Contributor

Paul-Reed commented Jan 9, 2022

@AlecTBM - Your suggestion works fine, and by dropping the nodes into a subflow, it makes a neat solution, and visually only adds one node to the flow.
I've added the subflow to the 'Google Smarthome' node category using the same colour scheme.

node

group

NOTE - because it is a subflow, the saved context data is not visible in the sidepanel, as it's stored within the subflow.

Edit - updated with @AlecTBM 's comment in post below

[{"id":"7a338e422c069c30","type":"subflow","name":"Smarthome context","info":"Restores the state of Smarthome nodes, whenever the server or system is restarted.\r\n\r\nTo use, connect the output from the Smarthome Management node to the input of this node, and this node's output to the input of the Management node.","category":"Google_SmartHome-function","in":[{"x":95,"y":100,"wires":[{"id":"d764f401f8e3e54c"}]}],"out":[{"x":615,"y":125,"wires":[{"id":"66d0c6ca4b41772f","port":0}]}],"env":[],"meta":{},"color":"#C0DEED","icon":"node-red-contrib-google-smarthome/google-smarthome.png"},{"id":"d764f401f8e3e54c","type":"switch","z":"7a338e422c069c30","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"set_state","vt":"str"},{"t":"eq","v":"get_state","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":3,"x":200,"y":100,"wires":[["ad126d2171615f9a"],["ad126d2171615f9a"],["0292a904fcf74ed2"]]},{"id":"0292a904fcf74ed2","type":"switch","z":"7a338e422c069c30","name":"","property":"payload.state","propertyType":"msg","rules":[{"t":"eq","v":"start","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":335,"y":125,"wires":[["66d0c6ca4b41772f"],[]]},{"id":"66d0c6ca4b41772f","type":"change","z":"7a338e422c069c30","name":"","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload","pt":"msg","to":"smarthome","tot":"flow"},{"t":"set","p":"topic","pt":"msg","to":"set_state","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":125,"wires":[[]]},{"id":"ad126d2171615f9a","type":"change","z":"7a338e422c069c30","name":"Save context","rules":[{"t":"set","p":"smarthome","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":355,"y":80,"wires":[[]]},{"id":"862c21371ca00e83","type":"subflow:7a338e422c069c30","z":"217df193.d774ce","name":"","env":[],"x":830,"y":440,"wires":[[]]}]

@AlecTBM
Copy link
Author

AlecTBM commented Jan 9, 2022

Looks good @Paul-Reed, the only thing I would add is to only save the payload if the topic is "set_state" (and possibly "get_state") to not make it break in the future.
Also, for others that are using this subflow, be aware that it uses your default context store if you have multiple configured 😃

With the post from @ckhmer1 I think we can close this "issue" for my sake 😊 thank you for all your good work here!

@ChutneyMary
Copy link

Sorry for the newbie question, but how do you set the context store? I've opened the subflow and see the 'Save context' node and assume I need to have a directory or path there? What does 'Deep copy value' do?

If this is writing to a MicroSD card, will it have an overhead in wear levels on the card, versus having the nodes return to defaults on reboot/redeploy?

@AlecTBM
Copy link
Author

AlecTBM commented Jan 10, 2022

Sorry for the newbie question, but how do you set the context store? I've opened the subflow and see the 'Save context' node and assume I need to have a directory or path there? What does 'Deep copy value' do?

If this is writing to a MicroSD card, will it have an overhead in wear levels on the card, versus having the nodes return to defaults on reboot/redeploy?

This it saving it to the default context store, which normaly is memory.
If you want to save this to disk, then either change your default context store to file, or add another context store to file and select this inside the subflow.
You can not write a path in the subflow, this need to be changed in the node-red settings.js file explained here:
https://nodered.org/docs/user-guide/context
Hope this help 😊

The deep copy value is "new" a node-red thing that I haven't checked out yet 😊

@Paul-Reed
Copy link
Contributor

I've opened the subflow and see the 'Save context' node and assume I need to have a directory or path there?

@ChutneyMary - No, it will work without any alteration, no need to change anything in the subflow for it to work & save the context to RAM, which is probably good enough for most usage. It will restore OK if the Smarthome server is stopped/started, if you do a full deploy, or even restart the flows.
It will not restore if you do a system reboot, and if that's what is required, then follow @AlecTBM's comment above, which will then write the saved context to disk instead of RAM.
There is no need to select 'deep copy' value in this flow. (if you want to know more about 'deep copying' try a google search).

I wouldn't worry too much about this causing SD wearing, it does have a small overhead, but the frequency and amount of disk writing is very minimal, compared to system logs, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants