Zigbee Low Battery Warning system

enter image description here Monitoring more than a couple Zigbee devices for battery levels can be cumbersome. So I made a system that adds new devices automatically,keeps the battery info as a dashboard and sends you an Android notification when battery needs replacing.

Features:

  • add devices automatically
  • Dashboard chart
  • notification for Android
  • custom low trigger
  • support for battery types and purchase links
  • more...

Requirements

  • node-red-contrib-dashboard-average-bars
  • node-red-dashboard

You will need the following Android Apps:

This Setup leans on the following guides: Perfect Autonotifications, Credentials in NodeRED,

Settings

  • battery - battery type for your device
  • name - friendly_name from yaml file (or ID)
  • Notification Device - name of AutoRemote device served with credential system
  • min Value - Low battery threshold value for notifications

More about me:

If you want to get the latest updates to this project you can follow me via your preferred social media:

And if you feeling like buying me a coffee or supporting me in a more continuous way:

I hope you have enjoyed the project!

[{"id":"a48db0b1.9dac6","type":"subflow","name":"Zigbee Low Battery Setting","info":"","category":"","in":[{"x":260,"y":80,"wires":[{"id":"8e65b3ed.9f24"}]}],"out":[{"x":540,"y":80,"wires":[{"id":"8e65b3ed.9f24","port":0}]}],"env":[{"name":"NotificationDevice","type":"str","value":""},{"name":"BatteryWarning","type":"num","value":"","ui":{"icon":"font-awesome/fa-battery-0","label":{"en-US":"Min Value"},"type":"spinner","opts":{"min":1,"max":25}}}],"color":"#DDAA99"},{"id":"8e65b3ed.9f24","type":"function","z":"a48db0b1.9dac6","name":"Set Values","func":"var minBattLevel = env.get(\"BatteryWarning\");\nflow.set(\"$parent.BatteryWarning\", parseInt(minBattLevel, 10));\n\nvar device = env.get(\"NotificationDevice\");\nflow.set(\"$parent.NotificationDevice\", device);\n\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":80,"wires":[[]]},{"id":"97561555.3ea248","type":"subflow","name":"Subflow 1","info":"","category":"","in":[{"x":300,"y":100,"wires":[{"id":"9b62f1c2.e1c83"}]}],"out":[{"x":580,"y":100,"wires":[{"id":"9b62f1c2.e1c83","port":0}]}],"env":[{"name":"BatteryType","type":"str","value":"CR2032","ui":{"icon":"font-awesome/fa-battery-4","label":{"en-US":"CR2450"},"type":"select","opts":{"opts":[{"l":{"en-US":"CR2450"},"v":"CR2450"},{"l":{"en-US":"CR2032"},"v":"CR2032"}]}}},{"name":"Name","type":"str","value":"","ui":{"type":"input","opts":{"types":["str","num"]},"label":{}}}],"color":"#DDAA99"},{"id":"9b62f1c2.e1c83","type":"function","z":"97561555.3ea248","name":"Update Battery","func":"var battType = env.get(\"BatteryType\");\nvar name = env.get(\"Name\");\nvar data = flow.get(\"$parent.zigbeeBattery\");\n\n\nvar battTRL = [\n  {\n    \"battery\": \"CR2032\",\n    \"url\": \"https://amzn.to/2RflnOx\"\n  },\n  {\n    \"battery\": \"CR2450\",\n    \"url\": \"https://amzn.to/2Tlaf5n\"\n  }\n];\n\nvar pos = data.map(function(e) { return e.info.name; }).indexOf(name);\nvar bat = battTRL.map(function(e) { return e.battery; }).indexOf(battType);\nif(pos !== -1){\n    if(bat => 0){data[pos].info.batteryURL = battTRL[bat].url;}\n    \n    data[pos].info.batteryType = battType;\n    flow.set(\"$parent.zigbeeBattery\", data);\n    msg.payload = battType +\" saved as default battery for \"+ name;\n}\nelse {\n    msg.payload = \"Check device name\";\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":440,"y":100,"wires":[[]]},{"id":"ff52c0df.f6082","type":"tab","label":"Zigbee Low Battery Notifications","disabled":false,"info":"\n![enter image description here](https://notenoughtech.com/wp-content/uploads/2020/01/maxresdefault-4.jpg)\nMonitoring more than a couple Zigbee devices for battery levels can be cumbersome. So I made a system that adds new devices automatically,keeps the battery info as a dashboard and sends you an Android notification when battery needs replacing.\n\n - [Complete instructions](https://notenoughtech.com/home-automation/zigbee-low-battery-warning/)\n\n**Features**:\n-   add devices automatically    \n-   Dashboard chart    \n-   notification for Android    \n-   custom low trigger    \n-   support for battery types and purchase links    \n-   more...\n\n## Requirements\n\n - node-red-contrib-dashboard-average-bars\n - node-red-dashboard\n\nYou will need the following Android Apps:\n\n - [Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm&hl=en)\n - [AutoNotification](https://play.google.com/store/apps/details?id=com.joaomgcd.autonotification&hl=en)\n - [AutoTools](https://play.google.com/store/apps/details?id=com.joaomgcd.autotools&hl=en)\n - [AutoRemote](https://play.google.com/store/apps/details?id=com.joaomgcd.autoremote&hl=en)\n - [AutoApps](https://play.google.com/store/apps/details?id=com.joaomgcd.autoappshub&hl=en)\n \n This Setup leans on the following guides:\n [Perfect Autonotifications](https://notenoughtech.com/tasker/tasker-projects/tasker-perfect-autonotifications/), [Credentials in NodeRED](https://notenoughtech.com/home-automation/nodered-home-automation/serving-credentials-with-nodered/),\n\n# Settings\n\n - **battery** - battery type for your device\n - **name**  - friendly_name from yaml file (or ID)\n - **Notification Device**  - name of AutoRemote device served with credential system\n - **min Value**  - Low battery threshold value for notifications\n \n ## More about me:\n\nIf you want to get the latest updates to this project you can follow me via your preferred social media:\n\n-   [Facebook](https://www.facebook.com/NotEnoughTECH/)\n-   [Twitter](https://twitter.com/NotEnoughTECH)\n-   [Instagram](https://www.instagram.com/notenoughtech/)\n-   [YouTube](https://www.youtube.com/user/Polepositionpage)\n\nAnd if you feeling like buying me a coffee or supporting me in a more continuous way:\n\n-   [Paypal](https://www.paypal.me/notenoughtech)\n-   [Patreon](https://www.patreon.com/NotEnoughTECH)\n\nI hope you have enjoyed the project!\n\n "},{"id":"a8ef2ce.4a169d","type":"mqtt in","z":"ff52c0df.f6082","name":"Zigbee Batteries","topic":"zigbee2mqtt/#","qos":"0","datatype":"json","broker":"6e117ba5.8026e4","x":140,"y":260,"wires":[["164ed4d4.17b46b","d7399023.389bd"]]},{"id":"164ed4d4.17b46b","type":"function","z":"ff52c0df.f6082","name":"Save Battery","func":"var battery = msg.payload.battery;\nvar batteryLow = msg.payload.battery_low;\nvar voltage = msg.payload.voltage;\nvar deviceTopic  = msg.topic;\nvar ZigbeeDeviceNames = flow.get(\"ZigbeeDeviceNames\");\n\nvar zigbeeBattery = flow.get(\"zigbeeBattery\");\n\nif(zigbeeBattery === undefined){\n    zigbeeBattery = [];\n}\nif(zigbeeBattery === undefined){\n    zigbeeBattery = [];\n}\n\nvar test = isNaN(battery);\n\n//battery info present\nif(test === false){\n    \n    //get device ID\n    var z1 = /zigbee2mqtt\\/(.*)/.exec(deviceTopic);\n    var id = z1[1];\n    \n    //get name and ID\n    var posName = ZigbeeDeviceNames.map(function(e) { return e.name; }).indexOf(id);\n    node.warn(posName);\n    var deviceId = ZigbeeDeviceNames[posName].device;\n    var deviceName = ZigbeeDeviceNames[posName].name;\n    node.warn(posName);\n    //optional voltage and low batt \n    var testBatt = isNaN(batteryLow);\n    var testVolt = isNaN(voltage);\n    \n    if(testBatt === true){batteryLow = false;}\n    if(testVolt === true){voltage = false;}\n    \n    //get time\n    var time = new Date();\n    \n    var pos = zigbeeBattery.map(function(e) { return e.info.name; }).indexOf(deviceName);\n    node.warn(pos);\n    if(pos === -1){\n        var a = {\"device\": deviceId, \n                  \"info\":{ \n                    \"battery\":    battery,\n                    \"timestamp\":  time,\n                    \"batteryType\": false,\n                    \"batteryETA\":  \"TBD\",\n                    \"battery_low\": false,\n                    \"voltage\": voltage,\n                    \"name\": deviceName}\n                    };\n        zigbeeBattery.push(a);\n        flow.set(\"zigbeeBattery\", zigbeeBattery);\n    }\n    if(pos => 0){\n        var battType = zigbeeBattery[pos].info.batteryType\n        var a = {\"device\": deviceId, \n                  \"info\":{ \n                    \"battery\":    battery,\n                    \"timestamp\":  time,\n                    \"batteryType\": battType,\n                    \"batteryETA\":  \"TBD\",\n                    \"battery_low\": batteryLow,\n                    \"voltage\": voltage,\n                    \"name\": deviceName}\n                    };\n    }\n    zigbeeBattery[pos]= a;\n    flow.set(\"zigbeeBattery\", zigbeeBattery);\n}\n\n\n   \n\nreturn msg;","outputs":1,"noerr":0,"x":750,"y":240,"wires":[[]]},{"id":"a6b8166b.c02258","type":"ui_template","z":"ff52c0df.f6082","group":"e19d291d.da6018","name":"","order":1,"width":20,"height":8,"format":"<div ng-bind-html=\"msg.payload\"></div>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":780,"y":480,"wires":[[]]},{"id":"228e9800.5ab598","type":"average-bars","z":"ff52c0df.f6082","name":"average-bars2","title":"Battery %","period":"topic","yMin":"0","yMax":"100","showBarsValue":true,"showScaleValue":true,"showLastValue":false,"showAverageValue":false,"showMinimumValue":false,"showMaximumValue":false,"maxBar":"50","topColor":"#d6fa0a","bottomColor":"#df3d20","unit":"%","fontColor":"#ffffff","barStyle":"Equalizer","decimal":"0","x":600,"y":480,"wires":[["a6b8166b.c02258"]]},{"id":"bc7957e.97d16a8","type":"function","z":"ff52c0df.f6082","name":"Update Chart","func":"var data = flow.get(\"zigbeeBattery\");\n\narr = [];\n\nvar count =  data.length;\n    for ( i=0; i < count; i++){\n        var nmsg = {payload:data[i].info.battery, topic:data[i].info.name};\n        arr.push(nmsg);\n    }\n\nreturn [arr];\n\n\n","outputs":1,"noerr":0,"x":490,"y":400,"wires":[["228e9800.5ab598"]]},{"id":"46ffe730.2ce6a8","type":"function","z":"ff52c0df.f6082","name":"Android notification","func":"var data = flow.get(\"zigbeeBattery\");\nvar batteryWarning =  flow.get(\"BatteryWarning\");\nvar device = flow.get(\"NotificationDevice\");\n\n\nvar key = global.get(device);\nvar url = \"https://autoremotejoaomgcd.appspot.com/sendmessage\";\nvar command = \"NOT20\";\n\n\n\nvar battPush = [];\nvar buttons = {};\nvar battBuy = [];\nvar battLink = [];\n\nfor (var i=0; i < data.length; i++) {\n    var batt = data[i].info.battery;\n    var battType = data[i].info.batteryType;\n    if(batt <= batteryWarning){\n        var name = data[i].info.name;\n        if(battType === false){\n            var battTypeDisplay = \"battery type configuration.\\ \";\n            \n        }\n        else { var battTypeDisplay = battType;\n                battBuy.push(battType);\n                battLink.push(data[i].info.batteryURL);\n        }\n    var lowBatt = \"<li><strong>\"+name + \":</strong><span style=\\\"color: #ff9900;\\\"> \"+ batt +\"%</span><span style=\\\"color: #333333;\\\"><em> needs </em><strong>\" + battTypeDisplay + \"</strong></span></li>\";\n    battPush.push(lowBatt);\n    }\n}\n\n\nif(battPush.length > 0){\n\nvar reg = battPush.toString();\nvar reg1 = reg.replace(/,/g, \"\");\n\nvar body = {\n  \"text\": {\n    \"text\": \"A number of your Zigbee devices need a new battery\" ,    \n    \"textexpanded\": \"<ul>\"+ reg1 + \"</ul>\"\n  },\n  \"title\": {\n    \"title\": \"Zigbee Low Battery Warning\",\n    \"titleexpanded\": \"Zigbee Low Battery Warning\"\n  },\n  \"icons\": {\n    \"navbaricon\": \"https://raw.githubusercontent.com/google/material-design-icons/master/device/2x_web/ic_battery_alert_black_48dp.png\",\n    \"bigicon\": \"https://raw.githubusercontent.com/google/material-design-icons/master/device/2x_web/ic_battery_alert_black_48dp.png\",\n    \"smallicon\": \"https://raw.githubusercontent.com/google/material-design-icons/master/device/2x_web/ic_battery_alert_black_48dp.png\",\n    \"iconexpanded\": \"https://raw.githubusercontent.com/google/material-design-icons/master/device/2x_web/ic_battery_alert_black_48dp.png\"\n  },\n  \"notificationid\": \"ZigbeeBattery\",\n  \"persistent\": false,\n  \"priority\": 1,\n  \"color\": \"#b9512c\",\n  \"backgroundcolor\": \"#fafafa\",\n  \"picture\": \"pictureurl\",\n  \"buttons\": \"\"\n};\n\n\nvar battBuy1 = [ ...new Set(battBuy) ];\nvar battLink1 = [ ...new Set(battLink) ];\n\nvar objButton ={};\nfor (var a = 0; a <battBuy1.length; a++) {\n    var b = a +1\n    var keyButton = \"button\"+ b;\n    \n    objButton =  {\n        \"icon\": \"iconURL\",\n        \"label\": \"Buy \" + battBuy1[a],\n        \"command\": \"buybattery*-*\" + battLink1[a]\n    };\n    node.warn(objButton);\n    buttons[keyButton] = objButton;\n    node.warn(buttons);\n}\n\nbody[\"buttons\"] = [buttons];\n\n\nmsg.data = body;\nvar x = JSON.stringify(body);\nvar encodedBody = encodeURIComponent(x);\n\nmsg.url = url + \"?key=\" + key + \"&message=\" +command + \"=:=\"+ encodedBody;\nreturn msg;\n}","outputs":1,"noerr":0,"x":350,"y":660,"wires":[["7b41c0bc.eba5d"]]},{"id":"34eaea50.91aab6","type":"subflow:97561555.3ea248","z":"ff52c0df.f6082","name":"Add Battery Info","env":[{"name":"BatteryType","value":"CR2450","type":"str"},{"name":"Name","value":"TUYA_temp_1","type":"str"}],"x":340,"y":140,"wires":[["f16ccf23.b206"]]},{"id":"82c403c6.93a1b","type":"inject","z":"ff52c0df.f6082","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":140,"wires":[["34eaea50.91aab6"]]},{"id":"f16ccf23.b206","type":"debug","z":"ff52c0df.f6082","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":530,"y":140,"wires":[]},{"id":"72d828b7.59b208","type":"subflow:a48db0b1.9dac6","z":"ff52c0df.f6082","name":"","env":[{"name":"NotificationDevice","value":"ARpixel3","type":"str"},{"name":"BatteryWarning","value":"78","type":"num"},{"name":"Battery warning","value":"7","type":"str"}],"x":340,"y":620,"wires":[[]]},{"id":"9f4bf161.ac641","type":"inject","z":"ff52c0df.f6082","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":620,"wires":[["72d828b7.59b208"]]},{"id":"e2838269.8935b","type":"inject","z":"ff52c0df.f6082","name":"Update 5min","topic":"","payload":"","payloadType":"date","repeat":"300","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":400,"wires":[["bc7957e.97d16a8"]]},{"id":"3c764738.1e0d68","type":"inject","z":"ff52c0df.f6082","name":"every 24","topic":"","payload":"clear","payloadType":"str","repeat":"","crontab":"00 12 * * *","once":false,"onceDelay":0.1,"x":140,"y":480,"wires":[["228e9800.5ab598","bc7957e.97d16a8"]]},{"id":"83134a13.58e6b8","type":"inject","z":"ff52c0df.f6082","name":"Once a day","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"00 13 * * *","once":false,"onceDelay":0.1,"x":130,"y":660,"wires":[["46ffe730.2ce6a8"]]},{"id":"7b41c0bc.eba5d","type":"http request","z":"ff52c0df.f6082","name":"","method":"POST","ret":"txt","paytoqs":false,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":590,"y":660,"wires":[["2ed9d566.4c7ada"]]},{"id":"2ed9d566.4c7ada","type":"http response","z":"ff52c0df.f6082","name":"","statusCode":"","headers":{},"x":790,"y":660,"wires":[]},{"id":"c8413a30.af4798","type":"file in","z":"ff52c0df.f6082","name":"Zigbee configuration.yaml","filename":"/opt/zigbee2mqtt/data/configuration.yaml","format":"utf8","chunk":false,"sendError":false,"encoding":"none","x":570,"y":320,"wires":[["efa82fdd.ff49f"]]},{"id":"efa82fdd.ff49f","type":"yaml","z":"ff52c0df.f6082","property":"payload","name":"","x":770,"y":320,"wires":[["2916361e.d54a3a"]]},{"id":"2916361e.d54a3a","type":"function","z":"ff52c0df.f6082","name":"Create Zigbee List","func":"var x = {\"devices\":msg.payload.devices};\nvar devices = [];\nvar device = Object.keys(x.devices);\nvar name = Object.values(x.devices);\n\nvar arr = Object.entries(x.devices);\n    for ([device,name] of arr) {\n        var n = name.friendly_name;\n        \n        var a = {device, \"name\": n}\n        \n        devices.push(a);\n        flow.set(\"ZigbeeDeviceNames\", devices);\n    }\n\nreturn msg;","outputs":1,"noerr":0,"x":950,"y":320,"wires":[[]]},{"id":"d7399023.389bd","type":"switch","z":"ff52c0df.f6082","name":"","property":"payload.battery","propertyType":"msg","rules":[{"t":"nnull"}],"checkall":"true","repair":false,"outputs":1,"x":370,"y":320,"wires":[["c8413a30.af4798"]]},{"id":"5a186078.f4965","type":"comment","z":"ff52c0df.f6082","name":"Settings","info":"","x":120,"y":100,"wires":[]},{"id":"e5058bb5.d75808","type":"comment","z":"ff52c0df.f6082","name":"update device list","info":"","x":420,"y":280,"wires":[]},{"id":"be6d1020.eea37","type":"comment","z":"ff52c0df.f6082","name":"Clear Average","info":"","x":390,"y":460,"wires":[]},{"id":"ed27adf4.45fa","type":"comment","z":"ff52c0df.f6082","name":"Android Notification","info":"","x":130,"y":580,"wires":[]},{"id":"6e117ba5.8026e4","type":"mqtt-broker","z":"","name":"MQTT","broker":"automation.local","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"e19d291d.da6018","type":"ui_group","z":"","name":"Zigbee Battery Status","tab":"71f75e8f.91051","disp":true,"width":"20","collapse":false},{"id":"71f75e8f.91051","type":"ui_tab","z":"","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]

Flow Info

Created 4 years, 6 months ago
Rating: not yet rated

Actions

Rate:

Node Types

Core
  • comment (x4)
  • debug (x1)
  • file in (x1)
  • function (x6)
  • http request (x1)
  • http response (x1)
  • inject (x5)
  • mqtt in (x1)
  • mqtt-broker (x1)
  • switch (x1)
  • yaml (x1)
Other
  • average-bars (x1)
  • subflow (x2)
  • subflow:97561555.3ea248 (x1)
  • subflow:a48db0b1.9dac6 (x1)
  • tab (x1)
  • ui_group (x1)
  • ui_tab (x1)
  • ui_template (x1)

Tags

  • Zigbee
  • low
  • battery
  • warning
  • android
  • notifications
  • tasker
  • sensors
  • home
  • automation
  • dashboard
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option