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