How to automatically retrieve device name from entity_id?

Hello everyone,

I want to check the states of some entities using a simple combination of events:state, function and notification nodes.
The function right now is

msg.payload = {
            "message": msg.topic + ": " + msg.payload

return msg;

The goal is to get a message that also informs me of which device is sending this information.

Msg.topic gives me the entity_id, but I would like the device name instead. Is there a way to retrieve the device name instead of the entity_id (which is in this case passed as “topic”)?

Thank you all for your continuous support :slight_smile:

It’s easier to help if you share the debug output.
But generally, if it’s not in the debug message then it will be hard/manual work

sorry, of course.

3.5.2021, 14:11:25 node: 5545c7bb.2a3777
switch.ambient_light_right_dining_roo : msg : Object
topic: "switch.ambient_light_right_dining_roo"
payload: object
data: object
message: "switch.ambient_light_right_dining_roo: off"
data: object
entity_id: "switch.ambient_light_right_dining_roo"
old_state: object
entity_id: "switch.ambient_light_right_dining_roo"
state: "on"
attributes: object
last_changed: "2021-05-03T12:10:25.022712+00:00"
last_updated: "2021-05-03T12:10:25.022712+00:00"
context: object
original_state: "on"
new_state: object
entity_id: "switch.ambient_light_right_dining_roo"
state: "off"
attributes: object
last_changed: "2021-05-03T12:11:25.870442+00:00"
last_updated: "2021-05-03T12:11:25.870442+00:00"
context: object
original_state: "off"
timeSinceChangedMs: 8
_msgid: "b7c9fd7d.f0334"

So the entity_id is output as topic. But the device name is not shown anywhere.
However, HA of course knows which entity_id belongs to which device, so I was hoping that there would be an “easy” way to map the entity_id to a device.

There is no easy way to do this currently. Even in HA you can’t do this actually. Device actions and device triggers are your only way of interacting with devices currently. If you find yourself in a template with an entity ID there’s no way to find out the device, area or integration it comes from at the moment.

However Node RED does have one advantage over HA here - it can make calls to the websockets API as part of its flows and use the response. All this information is exposed in the websockets API but HA doesn’t really have a way to interact with that. You can make REST sensors for the REST API but there is no integration for a websockets API. Node RED can.

Here’s what I made to handle this:

[{"id":"16fdbd81.9d9b82","type":"subflow","name":"Store HA config","info":"Keep config of HA up to date and easily accessible in globals. That includes:\n\n- Config entries\n- Areas\n- Devices\n- Entities\n\nIt also flattens areas into entities. So any time devices or areas are updated it also updates entities by filling out their `area_id` field. Normally entities have this blank and instead expect the device to set the area.","category":"","in":[{"x":160,"y":40,"wires":[{"id":"b09b8d39.fe41e"},{"id":"bd3c0c61.c6069"},{"id":"9349652e.f4a0c"}]}],"out":[{"x":1120,"y":160,"wires":[{"id":"447143e7.58fbf4","port":0}]}],"env":[],"color":"#DDAA99","status":{"x":240,"y":360,"wires":[{"id":"4e6fd6ff.adeda8","port":0}]}},{"id":"7b548b89.a5c924","type":"ha-api","z":"16fdbd81.9d9b82","name":"Areas","server":"cc03735a.94933","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\":\"config/area_registry/list\"}","dataType":"json","location":"payload.areas","locationType":"msg","responseType":"json","x":370,"y":160,"wires":[["447143e7.58fbf4"]]},{"id":"bd3c0c61.c6069","type":"ha-api","z":"16fdbd81.9d9b82","name":"Devices","server":"cc03735a.94933","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\":\"config/device_registry/list\"}","dataType":"json","location":"payload","locationType":"msg","responseType":"json","x":320,"y":220,"wires":[["e9500159.2c764"]]},{"id":"b09b8d39.fe41e","type":"ha-api","z":"16fdbd81.9d9b82","name":"Entities","server":"cc03735a.94933","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\":\"config/entity_registry/list\"}","dataType":"json","location":"payload","locationType":"msg","responseType":"json","x":320,"y":280,"wires":[["418cca4a.cbe594"]]},{"id":"dc8fe39c.fde6d8","type":"ha-api","z":"16fdbd81.9d9b82","name":"Config entries","server":"cc03735a.94933","debugenabled":false,"protocol":"http","method":"get","path":"/api/config/config_entries/entry","data":"{}","dataType":"json","location":"payload.entries","locationType":"msg","responseType":"json","x":760,"y":220,"wires":[["447143e7.58fbf4"]]},{"id":"2c4c713b.bb30ae","type":"server-events","z":"16fdbd81.9d9b82","name":"Entity reg updated","server":"cc03735a.94933","event_type":"entity_registry_updated","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"x":110,"y":280,"wires":[["b09b8d39.fe41e"]]},{"id":"9633cbe3.e13cd8","type":"server-events","z":"16fdbd81.9d9b82","name":"Device reg updated","server":"cc03735a.94933","event_type":"device_registry_updated","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"x":110,"y":220,"wires":[["bd3c0c61.c6069"]]},{"id":"1f22fbb3.0144d4","type":"server-events","z":"16fdbd81.9d9b82","name":"Area reg updated","server":"cc03735a.94933","event_type":"area_registry_updated","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"x":100,"y":160,"wires":[["9349652e.f4a0c"]]},{"id":"447143e7.58fbf4","type":"function","z":"16fdbd81.9d9b82","name":"Update global","func":"const data = msg.payload;\n\nif(data.entries){\n    update_config_entries(data.entries);\n    node.status({text:`Updated config entries (${data.entries.length})`});\n}\nif(data.areas){\n    update_areas(data.areas);\n    node.status({text:`Updated areas (${data.areas.length})`});\n}\nif(data.devices){\n    update_devices(data.devices);\n    node.status({text:`Updated devices (${data.devices.length})`});\n}\nif(data.entities){\n    update_entities(data.entities);\n    node.status({text:`Updated entities (${data.entities.length})`});\n}\n\nreturn msg;\n\nfunction update_entities(entities){\n    if(!entities){\n        entities = global.get('homeassistant.config.entities') || [];\n    }\n    \n    devices = global.get('homeassistant.config.entities');\n    for (let e of entities){\n        if(!e.area_id && e.device_id){\n            e.area_id = devices.find(d => == e.device_id) || null;\n        }\n    }\n    \n    global.set('homeassistant.config.entities', entities);\n}\n\nfunction update_devices(devices){\n    global.set('homeassistant.config.devices', devices);\n    update_entities();\n}\n\nfunction update_areas(areas){\n    global.set('homeassistant.config.areas', areas);\n    update_entities();\n}\n\nfunction update_config_entries(entries){\n    global.set('homeassistant.config.entries', entries);\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1000,"y":160,"wires":[[]]},{"id":"fc6ceac.c313e98","type":"join","z":"16fdbd81.9d9b82","name":"Make payload","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"1","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":580,"y":220,"wires":[["dc8fe39c.fde6d8"]]},{"id":"e9500159.2c764","type":"change","z":"16fdbd81.9d9b82","name":"Set topic","rules":[{"t":"set","p":"topic","pt":"msg","to":"devices","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":415,"y":220,"wires":[["fc6ceac.c313e98"]],"l":false},{"id":"418cca4a.cbe594","type":"change","z":"16fdbd81.9d9b82","name":"Set topic","rules":[{"t":"set","p":"topic","pt":"msg","to":"entities","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":415,"y":280,"wires":[["fc6ceac.c313e98"]],"l":false},{"id":"4e6fd6ff.adeda8","type":"status","z":"16fdbd81.9d9b82","name":"","scope":["447143e7.58fbf4"],"x":140,"y":360,"wires":[[]]},{"id":"9349652e.f4a0c","type":"change","z":"16fdbd81.9d9b82","name":"Clear payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"{}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":275,"y":160,"wires":[["7b548b89.a5c924"]],"l":false},{"id":"e44a4a26.6bef2","type":"server-events","z":"16fdbd81.9d9b82","name":"HA client","server":"cc03735a.94933","event_type":"home_assistant_client","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"x":80,"y":100,"wires":[["2a46ac.c8a18954"]]},{"id":"2a46ac.c8a18954","type":"switch","z":"16fdbd81.9d9b82","name":"Connected event","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"connected","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":175,"y":100,"wires":[["b09b8d39.fe41e","bd3c0c61.c6069","9349652e.f4a0c"]],"l":false},{"id":"cc03735a.94933","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true},{"id":"61f1c6b.d44ccb8","type":"inject","z":"a74fee2d.ac9068","name":"Manual","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":870,"y":1260,"wires":[["5ed06966.73fa48"]]},{"id":"5ed06966.73fa48","type":"subflow:16fdbd81.9d9b82","z":"a74fee2d.ac9068","name":"","env":[],"x":1020,"y":1260,"wires":[[]]}]

It’s still somewhat of a work in progress but its been working pretty well for me so far. Essentially this subflow will listen for events saying that the area, device or entity registries have been updated. Then it will pull the corresponding registry and stash it in the homeassistant.config global. It will also recreate the entire homeassistant.config object when the home assistant client connects to HA or when you press the manually update button.

Then you can use this data throughout your node red flows.

1 Like

That is really cool and impressive, thank you!

So I then now need a function that compares the “topic” of the event:state node with all entries in homeassistant.config?

I have tried to create a search flow based on existing flows from other people, but it seems that the data.filter is not known in HA. Am I doing something wrong or is the function called diffferently in HA Node Red?

"TypeError: data.filter is not a function"
[{"id":"aa642c6f.01afa","type":"tab","label":"Flow 4","disabled":false,"info":""},{"id":"ce4e8cef.b3c4a","type":"inject","z":"aa642c6f.01afa","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"switch.ambient_light_right_dining_roo","payloadType":"str","x":90,"y":360,"wires":[["a328617b.422e"]]},{"id":"744fa604.4422c8","type":"function","z":"aa642c6f.01afa","name":"exact search (exists true/false)","func":"var findWhat = msg.payload;\nvar data = msg.inputdata;\nmsg.payload = data.filter(e => {\n    try {\n        var conditions = e.conditions[0].condition\n        if(conditions.includes(findWhat)) {\n            return true;\n        }\n    } catch {\n        \n    }\n    return false\n});\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":360,"wires":[["d11e148d.d87bb8","30041c9c.853d44"]]},{"id":"a328617b.422e","type":"change","z":"aa642c6f.01afa","name":"","rules":[{"t":"set","p":"inputdata","pt":"msg","to":"homeassistant.config","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":360,"wires":[["744fa604.4422c8","607e9988.3398c8"]]},{"id":"d11e148d.d87bb8","type":"debug","z":"aa642c6f.01afa","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload[0].title","targetType":"msg","statusVal":"payload","statusType":"auto","x":1090,"y":360,"wires":[]},{"id":"607e9988.3398c8","type":"function","z":"aa642c6f.01afa","name":"partial search (exists true/false) (case insensitive)","func":"var findWhat = msg.payload.toLowerCase();\nvar data = msg.inputdata;\nmsg.payload = data.filter(e => {\n    try {\n        var conditions = e.conditions[0].condition;\n        var found = conditions.some(e => {\n            return (e && e.length) ? e.toLowerCase().includes(findWhat) : false\n        })\n        return found;\n    } catch {\n    }\n    return null\n});\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":650,"y":420,"wires":[["c8cd13ba.4b378","7c039174.c795d"]]},{"id":"c8cd13ba.4b378","type":"debug","z":"aa642c6f.01afa","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload[0].title","targetType":"msg","statusVal":"payload","statusType":"auto","x":1130,"y":420,"wires":[]},{"id":"30041c9c.853d44","type":"switch","z":"aa642c6f.01afa","name":"Found?","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":810,"y":320,"wires":[["19b7b63f.b0b46a"]]},{"id":"19b7b63f.b0b46a","type":"debug","z":"aa642c6f.01afa","name":"first found item","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload[0]","targetType":"msg","statusVal":"","statusType":"auto","x":980,"y":320,"wires":[]},{"id":"7c039174.c795d","type":"switch","z":"aa642c6f.01afa","name":"Found?","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":800,"y":480,"wires":[["f6d949dd.411978"]]},{"id":"f6d949dd.411978","type":"debug","z":"aa642c6f.01afa","name":"first found item","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload[0]","targetType":"msg","statusVal":"","statusType":"auto","x":970,"y":480,"wires":[]}]

data.devices.filter. homeassistant.config is an object not an array, it looks like this:
Screen Shot 2021-05-03 at 9.29.24 AM

You’re looking for the array of devices within it.

Ah, yes, that of course makes sense.

I now set it to data.entities.filter as the entity name is only found in the entities and not devices.

But I only get

3.5.2021, 15:43:32node: d11e148d.d87bb8
msg.payload[0].title : undefined
3.5.2021, 15:43:32node: c8cd13ba.4b378
msg.payload[0].title : undefined

So it seems that it cannot find the entity that contains the entity_id matching the search string. Does it not search recursively through the entire array “entities”?

Full debug:

3.5.2021, 15:46:40node: d11e148d.d87bb8
msg : Object
_msgid: "8ac9ec38.6abc"
payload: array[0]
topic: ""
inputdata: object
areas: array[7]
entities: array[144]
entries: array[17]
devices: array[24]
3.5.2021, 15:46:40node: c8cd13ba.4b378
msg : Object
_msgid: "8ac9ec38.6abc"
payload: array[0]
topic: ""
inputdata: object
areas: array[7]
entities: array[144]
entries: array[17]
devices: array[24]

So it seems to be looking in the homeassistant.config, but just not all the way down to the correct object in entities.
Or can this not work because the homeassistant.config is
object - array - array - object and the search then stops at the last array (but the information is in one of the objects).

You’re relying on devices too much. Why do you even need that? The entity_id is what is needed in HA, not the devices. :slight_smile:

If you ever have a broken device or you need to change something, the device_id will change as well, whereas the entity_id doesn’t. It is not the best idea to work with devices in HA. :wink:

What is it in the end, what you want to achieve? Send a message to Telegram(?) to show you the state an entity is in? That should work easier if you just use the entity_id and the friendly name. You are putting load on your system that isn’t necessary…going through a list with all your entities… :woozy_face:

I am trying to send a message to the HA app.
But the entity_id contained as “topic” only makes sense to me but not my wife. And since some entities are just “shelly25_ks8j3kel9” not even I know which Shelly device that is.

So I would need the friendly name or device name that tells me “living room lights are on”. Rather than “shelly25_kdoe83ken: on”.

You should have that “shelly_whatever” in your developer tools in HA as an entity. There should be the friendly_name.

Go to developer tools and look after that shelly. It should be in there, together with the friendly_name as an attribute.

Yes, but then I need to manually look up which device has changed everytime I get a message from HA.
I want to get a simple message like “Washing machine is finished” and not a cryptic string that forces me to open HA UI and check developer tools.

So somehow I need to get Node Red to know that friendly name based only on the entity_id as Node Red only works with the entity_id.
Otherwise Node Red cannot send me a useful message.

I have opened a thread also in the Node Red forum at How to search for string and cross-check - General - Node-RED Forum in case either of you are members there also. I thought the searching might now be a more Node Red specific problem now than a HA problem. At least now that @CentralCommand solved my initial problem :slight_smile:

You don’t have to look up the name in developer tools, the friendly name is in the message data. If you have an events: state node then you can grab the friendly name of entity that changed at You can also add custom attributes to the entity using HA’s customization options if you want to add a label for the thing specifically to use in your telegram messages and then access that the same way.

1 Like

Maybe the problem is the ‘catch’ command. I just saw that the editor states that the optional catch binding is only available in ES10.

While I completely agree with you, unfortunately this does seem to be slowly changing. The reason I made that subflow at all was because I encountered a device trigger. If an integration creates device triggers then the events created only have a device ID, nothing else. So if you want to leverage them in your Node RED flow then you have to either stash the device IDs you care about or have some way of looking up info on devices to see if this is the one you wanted.

This is also true for areas. If you wanted to have a flow that did something with all devices in an area then you have to be able to look up area info. And most of the time the device is what is in an area so you have to be able to walk through devices to find the entities.

Fortunately we’ve dodged the worst of this so far since device actions are just service calls. You never need the device ID there you can always just use the service call directly and future proof yourself. Hopefully that doesn’t change but no way to be sure.

Yes, I agree.
Sometimes one just tries to find a clean and independent solution that works for every possible use case.

What I am now wondering (entity friendly name is working btw!, thank you), is if I can also pass along the initial node name?
So if i name the node according to the string I want to display in the message, can i somehow pass this name on or read the name in the function node?

Like the name you put for the node in Node RED? Not aware of any way to do that. But why not just add the name you want to use to the message object with a change node or something? Or wrap the shared logic up in a subflow and add a configuration for message to send that you can set for each place you want to use that subflow.

Yes, like but from the parent node and not the current node.

If I am not mistaken, then I would need to add a change node to each entity:state node. So I would need one entity:state node plus one change node each.

If I have a universal function node (which is what I was going for), then I have one function node and one notification node for all entity:state nodes.
So for each new entity I want to monitor I only need to add that specific event:stae node and connect it to the universal function node.

I totally ignored the NodeRED part of the question, sorry. I don’t use it so i can’t be of any help here.

But nonetheless, why do this with NodeRED? This is a simple as can be automation, nothing fancy. If a state changes on deviceX, send a message to CompanionApp with the trigger named…

Something like this:

  - id: Shelly Button1
    alias: Shelly Button1
      platform: state
      entity_id: "sensor.shellyX"
      from: "unavailable"
      - service: mobile_app_my_phone
          message: "Shelly {{ state_attr('trigger.entity_id', 'friendly_name') }} seems to have a problem."
          title: "Something's off with a Shelly"

This is not tested and just written down, so maybe there are some indentation errors, but to show the idea… :wink:

Why not just change the entity_id of the entity to what you need?
That is what I would do.

@Hellis81 : Because the entity_id is part of a whole bunch of entities belonging to the same device. I would like to keep them all in the same naming scheme.

@paddy0174 : I was thinking about making “simple” automations instead. But for me the syntax there is similarly unclear and I was hoping to start with something small in Node Red and then be able to make the flows more complex. Something that might make normal automation codes less manageable.
Because with Node Red I can simply add one node into an existing flow whereas in automations I have to make the full code for every single one.

And I don’t quite understand the syntax in the automations. So e.g. if I wanted to send the new state, would it be

message: [{{ state_attr('trigger.entity_id', 'friendly_name') }} , {{ state_attr('trigger.entity_id', 'state') }}]

Or is it wrong. And why/when do I need double brackets {{}} and when can I just use state.attr.friendly_name?

I really need to find a decent source of information for the syntax of HA and of Node Red. Youtube tutorials are just not helping and most information on the HA homepage seems to assume that all syntax is basically known. Maybe I need to do a fresh search for this.
Python, VBA, etc. seem much more straight forward to me.