Home Assistant : Keep a plug always on (restore failed plugs)

Introduction

This simple post explains how I setup a “Restore critical plugs powering off” and “Notify about critical plugs restoration” using Home Assistant and Node-RED.

While this is hacky (in my case due to Home Assistant – Zigbee device updates : ZHA / ZHA_TOOLKIT OTA), it still fixes some real world issues like plugs turned off my mistake, not restoring their state on power loss and so on.

The post is specifically built for Zigbee plugs but would fit any use case involving relays.

Setting up a Node-RED Flow

Base logic

While this is pretty easy, we need to pay attention to a few key points. Here are the main steps to do so:

  • Create a regular polling every minute : No need for an agressive approach, save CPU time;
  • For each poll, get list of critical plugs and create a loop to process each;
  • For each plug, check if it is down for a minute or more;
  • If plug is off, turn it back on and send a notification.

 

My as-is flow

Node-RED Flow Overview

As usual, I’m relying on a “function Node” to feed data to the next “array-loop node”.

As I wanted my flow to handle multiple plugs, I’m defining an array to send multiple messages using the loop.

// Setup array
msg.plugs_array = [
    "switch.prise_13z_boiler_switch",
    "switch.prise_18z_adoucisseur_switch",
    "switch.prise_19z_lave_vaisselle_switch",
    "switch.prise_20z_machine_a_laver_switch",
    "switch.prise_21z_sechoir_switch"
    ]

// All went fine, forward the message down the pipe
return msg;

One not so obvious trick is the output of the delay going back to the “array-loop node”. This queues a message for each plug needing attention.

  • The “function node” builds an array in msg.plugs_array;
  • The “array-loop node” consumes that array;
  • The following “current state” and “call service” nodes use the payload as dynamic argument.
[{"id":"77ac09b83de52dfa","type":"inject","z":"6a4f25f2.3d2c5c","name":"Repeat every minute","props":[],"repeat":"60","crontab":"","once":false,"onceDelay":"10","topic":"","x":260,"y":800,"wires":[["0fa0135199c01a3e"]]},{"id":"7abf6cb25670f3ab","type":"comment","z":"6a4f25f2.3d2c5c","name":"Always on plugs","info":"","x":140,"y":740,"wires":[]},{"id":"daecec0cd035d21f","type":"array-loop","z":"6a4f25f2.3d2c5c","name":"Plugs","key":"aldaecec0cd035d21f","keyType":"msg","reset":false,"resetValue":"value-null","array":"plugs_array","arrayType":"msg","x":210,"y":880,"wires":[[],["94d15b7d4cf2ae8c"]]},{"id":"0fa0135199c01a3e","type":"function","z":"6a4f25f2.3d2c5c","name":"Get always on plugs","func":"// Debug flags\nvar debugMode = false;\nif (debugMode) node.warn(\"Get always on plugs - In\");\n\n// Setup array\nmsg.plugs_array = [\n    \"switch.prise_8z_switch\",\n    \"switch.prise_13z_boiler_switch\",\n    \"switch.prise_18z_adoucisseur_switch\",\n    \"switch.prise_19z_lave_vaisselle_switch\",\n    \"switch.prise_20z_machine_a_laver_switch\",\n    \"switch.prise_21z_sechoir_switch\"\n    ]\n\n// All went fine, forward the message down the pipe\nif (debugMode) node.warn(\"Get always on plugs - Out\");\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":800,"wires":[["daecec0cd035d21f"]]},{"id":"a1bbee3f158f88ac","type":"debug","z":"6a4f25f2.3d2c5c","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":630,"y":960,"wires":[]},{"id":"2ee9fa956a2f93b5","type":"api-call-service","z":"6a4f25f2.3d2c5c","name":"Turn plug on","server":"1e5e7dca.395042","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["{{payload}}"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":230,"y":960,"wires":[["90769d55fb371447"]]},{"id":"94d15b7d4cf2ae8c","type":"delay","z":"6a4f25f2.3d2c5c","name":"2s","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":350,"y":880,"wires":[["1819c56fd4ebf8e1","daecec0cd035d21f"]]},{"id":"90769d55fb371447","type":"api-call-service","z":"6a4f25f2.3d2c5c","name":"Notify","server":"1e5e7dca.395042","version":5,"debugenabled":false,"domain":"notify","service":"mobile_app_XXXXXX","areaId":[],"deviceId":[],"entityId":[],"data":"{\"title\":\"{{message}}\",\"message\":\"{{payload}} has been restored.\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":470,"y":960,"wires":[["a1bbee3f158f88ac"]]},{"id":"1819c56fd4ebf8e1","type":"api-current-state","z":"6a4f25f2.3d2c5c","name":"Switch is off","server":"1e5e7dca.395042","version":3,"outputs":2,"halt_if":"off","halt_if_type":"str","halt_if_compare":"is","entity_id":"{{payload}}","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"message","propertyType":"msg","value":"Critical switch is down","valueType":"str"}],"for":"1","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":510,"y":880,"wires":[["2ee9fa956a2f93b5"],[]]},{"id":"1e5e7dca.395042","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}]

Conclusion

While this approach is not the best possible, it ensure that part of your electrical setup stays up.

Be especially careful about the delay between detecting off state and turning it back on, doing this too quickly would pretty certainly lead to issues with the internal relay of your plug or appliance.

In the future I plan in using a similar approach to detect whether garage door is left open. A different approach is for light control using presence detection or schedule.

Update 2023-09-22

With fall 2023 arriving and sunny spells between the clouds, spikes are back causing havoc.

For really important plugs, I feel like I’ll simply hack the plugs to remove the relay entirely. While invasive, I don’t like risking the life of my thermodynamic water heater or others.

Post Author: Shut

3 thoughts on “Home Assistant : Keep a plug always on (restore failed plugs)

    Munier

    (2024-05-25 - 10:38)

    Hi,
    Thank you for this !
    Can you please post the content of each node ?
    I’m very newbie in Node-Red !
    Thank you …

      Shut

      (2024-05-25 - 13:24)

      Hi,
      You have the full JSON code just before the conclusion.
      Copy my code and within your Node-RED instance, look after “Import nodes”, you’ll be able to paste it there.

        Munier

        (2024-05-26 - 11:26)

        Thank you very much !!!
        Pretty cool !

Leave a Reply

Your email address will not be published. Required fields are marked *