Skip to content

Instantly share code, notes, and snippets.

@niemyjski
Last active June 8, 2023 15:33
Show Gist options
  • Save niemyjski/afc28ed47ae137fe5e245b245fd6c910 to your computer and use it in GitHub Desktop.
Save niemyjski/afc28ed47ae137fe5e245b245fd6c910 to your computer and use it in GitHub Desktop.
Report Home Assistant Battery Levels With Node-RED

I decided to share this snippet based on seeing what others were doing in this thread: https://www.reddit.com/r/homeassistant/comments/emvk9y/how_do_you_monitor_sensor_battery_levels_looking/

Basically dome device types of battery report the state in different locations and with different string values that the change node or conditional logic just doesn't work nicely. Using the conditional logic on this string state value might not be working fully how we think it is or at least didn't for me. When I was debugging it and getting 10 outputted in a 100 bucket via the gt condition. The best way I've found to handle these conditions is with a function node.

Home Assistant Feedback

It might be worth seeing if we can normalize this in Home Assistant. It would be nice if devices that can charge have their charging state normalized, battery level state in the same spot with a last changed in attribute. Additionally it would be nice to see something for dead batteries or if the device just hasn't reported in a long time. I say this because I've have many devices I know are charged and on, but they are being reported as "off".

Examples:

off, clearly the battery has a level and it lasts 10 years and it's reported in the nest app as OK

{"entity_id":"binary_sensor.nest_protect_battery_health","state":"off","attributes":{"friendly_name":"Nest Battery","device_class":"battery"},"last_changed":"2020-01-11T11:42:50.976581+00:00","last_updated":"2020-01-11T11:42:50.976581+00:00","context":{"id":"2a2f5c1503cd44c7bb3ebd9be69a4144","parent_id":null,"user_id":null},"timeSinceChangedMs":2731510}

Charging - battery level stored in attributes.

{"entity_id":"sensor.battery_state_2","state":"Charging","attributes":{"Battery Level":52,"friendly_name":"Battery State","icon":"mdi:battery-charging-40","device_class":"battery"},"last_changed":"2020-01-11T11:43:06.616903+00:00","last_updated":"2020-01-11T11:43:06.616903+00:00","context":{"id":"a9cc54cd33cf4cf1954c4cd2f2f633a8","parent_id":null,"user_id":null},"timeSinceChangedMs":2715885}

Same device as Charging example above but duplicate battery device sensor with state in the correct location.

{"entity_id":"sensor.battery_level_2","state":"52","attributes":{"Battery State":"Charging","unit_of_measurement":"%","friendly_name":"Battery Level","icon":"mdi:battery-charging-40","device_class":"battery"},"last_changed":"2020-01-11T11:43:06.599389+00:00","last_updated":"2020-01-11T11:43:06.599389+00:00","context":{"id":"60469c2964504dc4bb11cbf5a307ceb9","parent_id":null,"user_id":null},"timeSinceChangedMs":3766731}

August lock reported from august integration missing information

{"entity_id":"binary_sensor.august_door_locked_battery_level","state":"off","attributes":{"friendly_name":"August Door Lock Battery Level","device_class":"battery"},"last_changed":"2020-01-11T11:42:50.892228+00:00","last_updated":"2020-01-11T11:42:50.892228+00:00","context":{"id":"567d9c771c91440c826ed26d14187513","parent_id":null,"user_id":null},"timeSinceChangedMs":2731591}

August lock reported from zwave

{"entity_id":"sensor.august_asl_03_smart_lock_battery_level","state":"100","attributes":{"node_id":2,"value_index":0,"value_instance":1,"value_id":"72057594077773825","unit_of_measurement":"%","friendly_name":"August ASL-03 Smart Lock Battery Level","device_class":"battery"},"last_changed":"2020-01-11T11:47:39.195031+00:00","last_updated":"2020-01-11T11:47:39.195031+00:00","context":{"id":"406c8c33527a4daf8d9b23531953d54f","parent_id":null,"user_id":null},"timeSinceChangedMs":2443740}
// NOTE: We could probably look at the unit_of_measurement if specified to properly normalize the battery level.
// Check for battery level as it's set when state is Charging,Not Charging,etc...
const batteryLevelAttr = msg.payload.attributes && msg.payload.attributes["Battery Level"];
const state = !isNaN(batteryLevelAttr) ? batteryLevelAttr : msg.payload.state;
const level = parseInt(state);
if (!isFinite(level)) {
return [msg];
} else if (level < 10) {
return [null, msg];
} else if (level < 25) {
return [null, null, msg];
} else if (level < 50) {
return [null, null, null, msg];
} else if (level < 75) {
return [null, null, null, null, msg];
} else {
return [null, null, null, null, null, msg];
}
[
{
"id": "23e8e69c.44bbc2",
"type": "ha-get-entities",
"z": "b6dc27f3.c97de8",
"server": "61956bd4.93df44",
"name": "Get All Batteries",
"rules": [
{
"property": "attributes.device_class",
"logic": "is",
"value": "battery",
"valueType": "str"
}
],
"output_type": "split",
"output_empty_results": true,
"output_location_type": "msg",
"output_location": "payload",
"output_results_count": 1,
"x": 300,
"y": 100,
"wires": [
[
"7b7dfa31.f0ad14"
]
]
},
{
"id": "a86ce782.40a19",
"type": "inject",
"z": "b6dc27f3.c97de8",
"name": "",
"topic": "",
"payload": "",
"payloadType": "date",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 100,
"y": 100,
"wires": [
[
"23e8e69c.44bbc2"
]
]
},
{
"id": "5a078c35.7c3494",
"type": "debug",
"z": "b6dc27f3.c97de8",
"name": "75-100",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 760,
"y": 240,
"wires": []
},
{
"id": "f760e825.4cedf8",
"type": "debug",
"z": "b6dc27f3.c97de8",
"name": "10-25",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 750,
"y": 120,
"wires": []
},
{
"id": "211685e3.985d2a",
"type": "debug",
"z": "b6dc27f3.c97de8",
"name": "0-10",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 750,
"y": 80,
"wires": []
},
{
"id": "cef072ae.0b7c18",
"type": "debug",
"z": "b6dc27f3.c97de8",
"name": "off",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 750,
"y": 40,
"wires": []
},
{
"id": "4cf51363.714dbc",
"type": "debug",
"z": "b6dc27f3.c97de8",
"name": "25-50",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 750,
"y": 160,
"wires": []
},
{
"id": "a3d91a7.c56dbe8",
"type": "debug",
"z": "b6dc27f3.c97de8",
"name": "50-75",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 750,
"y": 200,
"wires": []
},
{
"id": "7b7dfa31.f0ad14",
"type": "function",
"z": "b6dc27f3.c97de8",
"name": "Normalize Battery Level",
"func": "const batteryLevelAttr = msg.payload.attributes && msg.payload.attributes[\"Battery Level\"];\nconst state = !isNaN(batteryLevelAttr) ? batteryLevelAttr : msg.payload.state;\nconst level = parseInt(state);\nif (!isFinite(level)) {\n return [msg];\n} else if (level < 10) {\n return [null, msg];\n} else if (level < 25) {\n return [null, null, msg];\n} else if (level < 50) {\n return [null, null, null, msg];\n} else if (level < 75) {\n return [null, null, null, null, msg];\n} else {\n return [null, null, null, null, null, msg];\n}",
"outputs": 6,
"noerr": 0,
"x": 530,
"y": 100,
"wires": [
[
"cef072ae.0b7c18"
],
[
"211685e3.985d2a"
],
[
"f760e825.4cedf8"
],
[
"4cf51363.714dbc"
],
[
"a3d91a7.c56dbe8"
],
[
"5a078c35.7c3494"
]
]
},
{
"id": "61956bd4.93df44",
"type": "server",
"z": "",
"name": "Home Assistant",
"legacy": false,
"hassio": true,
"rejectUnauthorizedCerts": true,
"ha_boolean": "y|yes|true|on|home|open",
"connectionDelay": true
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment