[Example] Saving and Restoring States

Moved this from here in an attempt to keep that topic cleaner and easier to follow question and answers here.


At the current moment, I believe this is pretty straight forward using existing nodes and there is no need for a specialized node to do this. Also, it would require custom mapped properties for every domain/component that is available within Home Assistant.

Maybe what is needed is more examples of how this can be accomplished with the tools available. @walt has a great example above and here’s another possible solution I put together.

[{"id":"953f4576.faa588","type":"ha-get-entities","z":"6cb9e978.bf4588","server":"8d00ebbc.99a928","name":"Get States","rules":[{"property":"entity_id","logic":"is","value":"^(light|switch|climate)\\.+","valueType":"re"}],"output_type":"array","output_empty_results":false,"output_location_type":"msg","output_location":"payload","output_results_count":1,"x":294,"y":80,"wires":[["2dab5a86.e54686"]]},{"id":"821d957.d510168","type":"inject","z":"6cb9e978.bf4588","name":"Store states","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":118,"y":80,"wires":[["953f4576.faa588"]]},{"id":"5bc878cb.d80f08","type":"comment","z":"6cb9e978.bf4588","name":"How I would do store states","info":"","x":152,"y":32,"wires":[]},{"id":"2dab5a86.e54686","type":"function","z":"6cb9e978.bf4588","name":"transform data","func":"const entities = msg.payload.map((e) => {\n    const domain = e.entity_id.split('.')[0];\n    const payload = {};\n\n    switch(domain) {\n        case 'switch':\n        case 'light':\n            payload.domain = domain;\n            payload.service = `turn_${e.state}`;\n            payload.data = { entity_id: e.entity_id };    \n            break;\n        case 'climate':\n            payload.domain = 'climate';\n            payload.service = `set_temperature`;\n            payload.data = {\n                entity_id: e.entity_id,\n                temperature: e.attributes.temperature,\n                target_temp_low: e.attributes.min_temp,\n                target_temp_high: e.attributes.max_temp,\n                operation_mode: e.attributes.operation_mode\n            };\n            break;\n    }\n    return payload;\n});\n// Save the states\nflow.set('savedStates', entities);\n\n// create a blank message object with out new payload\nmsg = { payload: entities };\nreturn msg;","outputs":1,"noerr":0,"x":480,"y":80,"wires":[["ceaf42e4.e245d"]]},{"id":"ceaf42e4.e245d","type":"debug","z":"6cb9e978.bf4588","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":646,"y":80,"wires":[]}]

[{"id":"f14f56e1.4d9a28","type":"inject","z":"6cb9e978.bf4588","name":"Store states","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":118,"y":256,"wires":[["3790fc30.12b354","18d6f55.537770b","94a3c52.9fa9738"]]},{"id":"18d6f55.537770b","type":"ha-get-entities","z":"6cb9e978.bf4588","server":"8d00ebbc.99a928","name":"Get Switches","rules":[{"property":"entity_id","logic":"starts_with","value":"switch","valueType":"str"},{"property":"state","logic":"includes","value":"on,off","valueType":"str"}],"output_type":"split","output_empty_results":false,"output_location_type":"msg","output_location":"payload","output_results_count":1,"x":290,"y":256,"wires":[["19e85c0e.f4d1d4"]]},{"id":"94a3c52.9fa9738","type":"ha-get-entities","z":"6cb9e978.bf4588","server":"8d00ebbc.99a928","name":"Get Lights","rules":[{"property":"entity_id","logic":"starts_with","value":"light","valueType":"str"},{"property":"state","logic":"includes","value":"on,off","valueType":"str"}],"output_type":"split","output_empty_results":false,"output_location_type":"msg","output_location":"payload","output_results_count":1,"x":290,"y":208,"wires":[["19e85c0e.f4d1d4"]]},{"id":"297ff244.47ca4e","type":"comment","z":"6cb9e978.bf4588","name":"Easier to follow version","info":"","x":132,"y":160,"wires":[]},{"id":"3790fc30.12b354","type":"ha-get-entities","z":"6cb9e978.bf4588","server":"8d00ebbc.99a928","name":"Get Climate","rules":[{"property":"entity_id","logic":"starts_with","value":"climate","valueType":"str"}],"output_type":"split","output_empty_results":false,"output_location_type":"msg","output_location":"payload","output_results_count":1,"x":290,"y":304,"wires":[["36705576.17a42a"]]},{"id":"19e85c0e.f4d1d4","type":"function","z":"6cb9e978.bf4588","name":"transform data","func":"const payload = {};\npayload.domain = msg.payload.entity_id.split('.')[0];\npayload.service = `turn_${msg.payload.state}`;\npayload.data = { entity_id: msg.payload.entity_id };\n\n// create a blank message object with out new payload\nmsg = { payload: payload };\nreturn msg;","outputs":1,"noerr":0,"x":476,"y":256,"wires":[["2b7e0299.1ce7ae"]]},{"id":"36705576.17a42a","type":"function","z":"6cb9e978.bf4588","name":"transform data","func":"const payloads = [];\nconst payload = {};\npayload.domain = 'climate';\npayload.service = `set_temperature`;\npayload.data = {\n    entity_id: msg.payload.entity_id,\n    temperature: msg.payload.attributes.temperature,\n    target_temp_low: msg.payload.attributes.min_temp,\n    target_temp_high: msg.payload.attributes.max_temp,\n    operation_mode: msg.payload.attributes.operation_mode\n};\n\n// create a blank message object with out new payload\nmsg = { payload: payload };\nreturn msg;","outputs":1,"noerr":0,"x":476,"y":304,"wires":[["2b7e0299.1ce7ae"]]},{"id":"e254a879.41d408","type":"comment","z":"6cb9e978.bf4588","name":"function nodes could be replaced with a change nodes","info":"","x":596,"y":208,"wires":[]},{"id":"2b7e0299.1ce7ae","type":"join","z":"6cb9e978.bf4588","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"5","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":654,"y":256,"wires":[["b3db19bf.61d8e8"]]},{"id":"472ff71c.c342c8","type":"debug","z":"6cb9e978.bf4588","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1026,"y":256,"wires":[]},{"id":"b3db19bf.61d8e8","type":"change","z":"6cb9e978.bf4588","name":"","rules":[{"t":"set","p":"savedStates","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":832,"y":256,"wires":[["472ff71c.c342c8"]]}]

[{"id":"850eabae.4938f8","type":"inject","z":"6cb9e978.bf4588","name":"Restore states","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":118,"y":432,"wires":[["11eaa9cb.2a8fc6"]]},{"id":"11eaa9cb.2a8fc6","type":"change","z":"6cb9e978.bf4588","name":"Get Saved States","rules":[{"t":"set","p":"payload","pt":"msg","to":"savedStates","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":314,"y":432,"wires":[["fffe299d.b82568"]]},{"id":"20c88906.d20846","type":"debug","z":"6cb9e978.bf4588","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":822,"y":432,"wires":[]},{"id":"fffe299d.b82568","type":"split","z":"6cb9e978.bf4588","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":482,"y":432,"wires":[["915c3c9d.448b"]]},{"id":"915c3c9d.448b","type":"api-call-service","z":"6cb9e978.bf4588","name":"Restore State","server":"ef067c6f.620e6","service_domain":"","service":"","data":"","mergecontext":"","output_location":"payload","output_location_type":"msg","x":640,"y":432,"wires":[["20c88906.d20846"]]}]

As you can see in the above examples the switch and light domains are just saving and restoring the state, on/off. But what if some users wanted to also save and restore the brightness level or not. The climate domain is saving and restoring more than just the state.

Hopefully, this gives people ideas on how to accomplish what they’re looking to do.

14 Likes

Thank you.
Very useful flow, but I want to exclude one light from restoring it’s state if it is off (but still want to save).
How I can do that?

Very useful indeed. Thx!
Trying to store attributes from my amplifier, like volume and source. But i can use a little help.

tried to following, but i can’t figure it out… Any help is really appreciated.

const entities = msg.payload.map((e) => {
    const domain = e.entity_id.split('.')[0];
    const service = e.entity_id.split('.')[0];
    const payload = {};

    switch(domain) {
        case 'switch':
        case 'light':
            payload.domain = domain;
            payload.service = `turn_${e.state}`;
            payload.data = { entity_id: e.entity_id };    
            break;
        case 'climate':
            payload.domain = 'climate';
            payload.service = `set_temperature`;
            payload.data = {
                entity_id: e.entity_id,
                temperature: e.attributes.temperature,
                target_temp_low: e.attributes.min_temp,
                target_temp_high: e.attributes.max_temp,
                operation_mode: e.attributes.operation_mode
            };
            break;
        case 'media_player':
            payload.domain = 'media_player';
                switch(service) {
                    case 'volume_set':
                        payload.data = {
                            entity_id: e.entity_id,
                            volume_level: e.attributes.volume_level
                        };
                        break;
                    case 'select_source':
                        payload.data = {
                            entity_id: e.entity_id,
                            source: e.attributes.source
                        };
                        break;
            }
            break;
    }
    return payload;
});
// Save the states
flow.set('savedStates', entities);

// create a blank message object with out new payload
msg = { payload: entities };
return msg;

this is going to be the same as domain, but not sure what you’re trying to accomplish here as it will just be the name of the entity id and not the service to call.

const entityStates = [];
msg.payload.forEach((e) => {
  const domain = e.entity_id.split(".")[0];

  switch (domain) {
    case "switch":
    case "light":
      entityStates.push({
        domain,
        service: `turn_${e.state}`,
        data: { entity_id: e.entity_id },
      });
      break;
    case "climate":
      entityStates.push({
        domain,
        service: "set_temperature",
        data: {
          entity_id: e.entity_id,
          temperature: e.attributes.temperature,
          target_temp_low: e.attributes.min_temp,
          target_temp_high: e.attributes.max_temp,
          operation_mode: e.attributes.operation_mode,
        },
      });
      break;
    case "media_player":
      entityStates.push({
        domain,
        service: "volume_set",
        data: {
          entitiy_id: e.entitiy_id,
          volume_level: e.attributes.volume_level,
        },
      });
      entityStates.push({
        domain,
        service: "select_source",
        data: {
          entitiy_id: e.entitiy_id,
          source: e.attributes.source,
        },
      });
      break;
  }
});
// Save the states
flow.set("savedStates", entityStates);
msg.payload = entityStates;

return msg;

Hi Kermit,

Thank you for your help and quick response.
I’m not a programmer and I just could figure it out, so thank you very much!

Found a little typo in

entitiy_id: e.entitiy_id,

Noticed that it doesn’t GET the payload state if the device is on or off?
And when a device is off, i’m getting a error message:

“call service node is missing api “domain” property, not found in config or payload”

It should, i suppose, only record attributes, when device is on.

cheers,
Max.

Have you looked at the ability to create scenes on the fly?

https://zachowj.github.io/node-red-contrib-home-assistant-websocket/cookbook/saving-and-restoring-states.html

Hi Kermit,
Thanks for your help and the link to the scene creation, also very useful.
But in my case i prefer the option to store multiple states from multiple entities at the same time.
media_player is working now with your input.

The only glitch is that it shouldn’t try to store/restore attributes of entities which are turned ‘off’.
Getting error messages like:
“Call-service API error. Error Message: required key not provided @ data[‘volume_level’]”
When the device is turned off.

Hi, I am able to “get state” and ‘store’ it i believe… however after my trigger is over… i am unsure how you insert the process of restoring the state and to check whether it was off or on… as if it was off… then we will turn off the light and if it was on… then we will leave it on.

This is awesome!

If you wanted to narrow it down to one area say. Is there a way to do that?

If I have a room sensor and I left that room. Anything with an on state is saved and turned off, then when I return, the on state is restored?