Skip to content

Instantly share code, notes, and snippets.

@notenoughtech
Created January 20, 2020 14:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save notenoughtech/b50779ea99f3daf38acc1ee6bed16c5b to your computer and use it in GitHub Desktop.
Save notenoughtech/b50779ea99f3daf38acc1ee6bed16c5b to your computer and use it in GitHub Desktop.
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}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment