Best way to get ZHA device ieee address?

Hello,

There is currently no direct way to use the zigbee timed switch feature (aka timed_with_on_off) on a Zigbee device. For the curious, this is a more secure way to turn on then turn off a device when you want to be sure that the device will be turned off after a amount of time despite an hass restart/crash/murphy law/etc. For example, you want to be sure your water/gaz valve will be turned off and not flood your neighborhood.

To workaround that I issue a zha.issue_zigbee_cluster_command directly to the device which requires the ieee address of the device.
This service doesnā€™t accept target or entity args and unfortunately device_attr("light.room3", "ieee") does not work.

Currently to extract the device ieee I use {{ (device_attr("light.room3", "identifiers")|list).0.1 }}. Is this the best way to get the ieee attribute or to match a device with zha.issue_zigbee_cluster_command ?

Here is a complete example:

service: zha.issue_zigbee_cluster_command
data:
  ieee: '{{ (device_attr("light.room3", "identifiers")|list).0.1 }}'
  endpoint_id: 11
  cluster_id: 6
  cluster_type: in
  command: 66
  command_type: server
  args:
    - 0
    - 70
    - 0
2 Likes

Examining the code that sets up the templating mechanism, we can see that not that many things are exposed:

So this seems to be close to the best approach. I think that one could verify that the first item in the list item is ā€˜zhaā€™, and a more general implementation would be to go through the list to find the item where the first value is ā€˜zhaā€™. But I guess that in this case you kind of know what you are talking to and it does not have to be fool-proof.

What I donā€™t understand is why device_attr(device_id, ā€œieeeā€) does not return the ieee address.

The device_attr function is:

def device_attr(hass: HomeAssistant, device_or_entity_id: str, attr_name: str) -> Any:
    """Get the device specific attribute."""
    device_reg = device_registry.async_get(hass)
    if not isinstance(device_or_entity_id, str):
        raise TemplateError("Must provide a device or entity ID")
    device = None
    if (
        "." in device_or_entity_id
        and (_device_id := device_id(hass, device_or_entity_id)) is not None
    ):
        device = device_reg.async_get(_device_id)
    elif "." not in device_or_entity_id:
        device = device_reg.async_get(device_or_entity_id)
    if device is None or not hasattr(device, attr_name):
        return None
    return getattr(device, attr_name) 

It basically returns getattr(device, attr_name) after finding the device object and a ZHA Device has this attribute:

class ZHADevice(LogMixin):
    """ZHA Zigbee device object."""
    ...
    @property
    def ieee(self):
        """Return ieee address for device."""
        return self._zigpy_device.ieee

And getattr() on a property should works in python.

$ ipython
In [1]: class Toto:
ā€¦: @property
ā€¦: def foo(self):
ā€¦: return 42
ā€¦:
In [2]: getattr(Toto(), ā€œfooā€)
Out[2]: 42

I think a ZHADeivce is not the same as a HA device.

I found this code suggesting that youā€™re doing it the same way ZHA is doing it:

FYI: I revisited this thread to check the method to get the IEEE of a device.

I added an implementation accepting entity_names and short network addrssses to zha_custom.

That will also allow you to do what you wanted as I added the functionnality to send Cluster commands. But apparently that also exists in ZHA (I just (re)discovered that here).

2 Likes

Is there something similar in ZHA-toolkit for endpoints? Iā€™ve been working on a Blueprint that uses ZHA-toolkit, and Iā€™d love to be able to only need the entity-ID as an input. Thanks!

Edit: Sorry. I just read the documentation and saw that itā€™s optional. Itā€™s working really well. Thank you!

{{ (device_attr("light.room3", "identifiers")|list).0.1 }}

I was playing with this, I just wanted to share a slightly improved version of it:

"{{ device_attr('light.room3', 'identifiers') | selectattr(0,'eq','zha') | map(attribute=1) | first }}"

This is more resilient in that if in a future version of ZHA the identifiers attributes (which is an array) returns more than one ID, this will select the one that contains the IEEE, whichever order theyā€™re in.

Also, if someoneā€™s got a progammatical way to get the endpoint_id Iā€™d be game (Iā€™m trying to use zha.issue_zigbee_cluster_command in a blueprint, and trying to see if I can avoid asking for the endpoint id in an input)! Although it seems to always be 1 or 11?

2 Likes

zha-toolkit actually determines the endpoint by matching the cluster_id to the available clusters.
That works if only one endpoint has the cluster id.

As you are trying to send a command, you need zha_toolkit.zcl_cmd. The documentation still show examples using zha_toolkit.execute, but you can use use the zcl_cmd service instead which is more convenient.
The endpoint is an optional parameter, while the cluster is mandatory.

1 Like

May I build upon this question, and ask if there is a way through jinja to get all the zigbee info so i can extract ieee aswell as check quirk for siren?
image

Hi,
I found the following solution to the problem of getting the ieee from the entity_id:

{{ (device_attr(device_id('your_entity_id'), 'connections')|list)[0][1] }}

device_attr() returns a list of the form:
[ [ "zigbee", "the_actual_ieee" ] ]
and I couldnā€™t figure out how to access the content of that other than my solution, let me know if Iā€™m stupid.

The list comes from the core.device_registry, where at least in my configuration all (zigbee) devices have the ā€œconnectionsā€ item, it still feels like quite the fiddly workaround, there should definitely be a built-in function or attribute for this.
Anyways maybe itā€™ll be good for somethingā€¦