"zha-toolkit" - a big set of Zigbee commands on top of ZHA/zigpy

I have used Home Assistant for some time, mainly for controlling some lights. Now I intend to also control a few electric heaters. So I bought an Namrod Zigbee plug with thermostat, but this far I am only able to use it as an on/off plug. In the description that was included with the plug it is referred to a proprietary attribute that can be sett to activate it as a thermostat device , so I searched around and discovered zha-toolkit. I have installed it using hacs. However when I start to try to use it I get the same error, no mater what I do. scan_device, attr_read or attr_write all give med the same error: “‘ZHAData’ object is not subscriptable”

Probably it is just me doing something wrong,but I can’t figure out what.

A longer error message from the log when I was testing Scan Device is shown below:
(I have also tried to scan other devices, but get the same error.)

----
TypeError: 'ZHAData' object is not subscriptable
2023-10-05 22:04:33.232 INFO (MainThread) [custom_components.zha_toolkit] Running ZHA Toolkit service: <ServiceCall zha_toolkit.scan_device (c:01HC0Q28ADBSC4JHGZGRG5TA37): ieee=sensor.namron_as_4512749_n_hvac_action>
2023-10-05 22:04:33.238 ERROR (MainThread) [homeassistant.helpers.script.websocket_api_script] websocket_api script: Error executing script. Unexpected error for call_service at pos 1: 'ZHAData' object is not subscriptable
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 468, in _async_step
    await getattr(self, handler)()
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 704, in _async_call_service_step
    response_data = await self._async_run_long_action(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 666, in _async_run_long_action
    return long_task.result()
           ^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 2012, in async_call
    response_data = await coro
                    ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 2049, in _execute_service
    return await target(service_call)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/zha_toolkit/__init__.py", line 672, in toolkit_service
    zha_gw = hass_ref.data["zha"]["zha_gateway"]

----

The ZHA version in 2023.10.0 introduced an incompatibility. Hopefully this is fixed now in v1.1.2 (beta). I have not updated yet, and I had to modify a user’s suggestion to ensure backward compatibility.

I’ll wait for the update to be able to debug zha-toolkit.scan device which do not see manufacturer cluster attributes. Same for the zha_toolkit.attr_read.

The update was released - a user confirmed it worked for him.
It does not change the behavior of the services, but adjusts to a breaking change in ZHA/2023.10.

Yes, success :slight_smile:

After updating to beta version 1.1.2 and some trial and error I was able to write the right parameter to the thermostat plug and now it operates as a thermostat.

The image shows the parameters I used to enable thermostat mode on my Namron zigbee plug with thermostat

Great!

Some tips:

  • Generally you can omit the attr_type and endpoint_id as zha-toolkit will try to detect those by itself: the attr_type is detected by reading the attribute first, and the endpoint because there is generally only one cluster of the type amongst the endpoints.
  • I often use the csv_out argument to write the result of my transactions to a CSV file. This means that I can find the old and the new value in the CSV file as well as the time that happened.

I was struggling a little bit to find the right parameters, so I used read attribute first to figure out all the details, to be sure everything was correct.
I also found that all the response from the call service is listed below, so I just copied it from there and pasted it to an text editor to examine it closer. For me it is easier to use a editor on my computer and not view the output in an browser.

And now I can go out and buy more Namron thermostat plugs and make a proper remote controlled heating of my holiday house. I have been using Mill-heaters up til now, but they rely on Mills own proprietary app, which has been rather unstable lately. Lets hope Nabu Casa is more reliable.

Hi @le_top,
trying to read attribute 80 of manufacturer cluster 65281. This attribute is read/write and I can see it in ZHA manage zigbee device. But when I use zha_toolkit.attr_read I get nothing.
Same for zha_toolkit.scan_device the attributes are not there for the manufacturer cluster. I got only attributes: {}

zha_toolkit_version: v1.1.2
zigpy_version: 0.57.2
zigpy_rf_version: 0.21.1
ieee_org:
  - 220
  - 244
  - 1
  - 0
  - 64
  - 145
  - 11
  - 80
ieee: 50:0b:91:40:00:01:f4:dc
command: attr_read
command_data: null
start_time: "2023-10-07T01:06:42.229269+00:00"
errors: []
params:
  endpoint_id: 1
  cluster_id: 65281
  attr_id: 80
  dir: 0
  manf: 17672
  tries: 1
  expect_reply: true
  args: []
  event_success: my_read_success_trigger_event
  event_fail: my_read_fail_trigger_event
  event_done: my_read_done_trigger_event
  read_before_write: true
  read_after_write: true
  csvfile: ../www/test.csv
write_is_equal: false
result_read:
  - {}
  - "80": 0
warnings:
  - "Result: {80: 0} - Attribute 80 not in read ({}, {80: 0})"
success: false

For that supplier, Sinopé, it is only the light devices that hide the manufacturer cluster attribute. For all other devices, climate, sensor, switch I can see the attributes.

Ok, I assume this is the quirk:

zhaquirks/sinope/light.py:SINOPE_MANUFACTURER_CLUSTER_ID = 0xFF01

A quirk will make attributes appear in the ZHA UI and they do not always correspond to attributes at the given address… In this case, it is a simple definition of the attribute to make it known to ZHA.

Attribute 80 is 0x50 which would be a uint24 and “on_led_color”.

If you can get an actual value in the ZHA Manage Device UI then it’s strange that you do not get it using the zha-service. That could mean there is an issue with deconding the 24 bit uint but I have already read uint24 types in the past (I found a read in my logs).

zha-toolkit relies on zigpy, so maybe by setting the zigpy logging to debug levels we may learn more about what it is doing;

This is what I get in the log with zigpy debug. I’m trying to read attribute 85 min_intensity which give 600 in ZHA manage device.

2023-10-07 13:00:21.593 INFO (MainThread) [custom_components.zha_toolkit] Running ZHA Toolkit service: <ServiceCall zha_toolkit.attr_read (c:01HC5HADPQJWMGFW82F70VKJVT): ieee=50:0b:91:40:00:01:f4:dc, manf=17672, endpoint=1, cluster=65281, attribute=85, event_success=my_read_success_trigger_event, event_fail=my_read_fail_trigger_event, event_done=my_read_done_trigger_event, csvout=../www/test.csv>
2023-10-07 13:00:21.594 DEBUG (MainThread) [custom_components.zha_toolkit] Got hass.data['zha']/gateway <homeassistant.components.zha.core.gateway.ZHAGateway object at 0x7f436011d0>
2023-10-07 13:00:21.614 DEBUG (MainThread) [custom_components.zha_toolkit] module is <module 'custom_components.zha_toolkit' from '/config/custom_components/zha_toolkit/__init__.py'>
2023-10-07 13:00:21.620 DEBUG (MainThread) [custom_components.zha_toolkit.utils] Parameters '{'ieee': 50:0b:91:40:00:01:f4:dc, 'manf': 17672, 'endpoint': 1, 'cluster': 65281, 'attribute': 85, 'event_success': 'my_read_success_trigger_event', 'event_fail': 'my_read_fail_trigger_event', 'event_done': 'my_read_done_trigger_event', 'csvout': '../www/test.csv'}'
2023-10-07 13:00:21.620 DEBUG (MainThread) [custom_components.zha_toolkit.utils] Final manf '17672'
2023-10-07 13:00:21.621 DEBUG (MainThread) [custom_components.zha_toolkit] 'ieee' parameter: '50:0b:91:40:00:01:f4:dc' -> IEEE Addr: '50:0b:91:40:00:01:f4:dc'
2023-10-07 13:00:21.621 DEBUG (MainThread) [custom_components.zha_toolkit] Default handler for attr_read
2023-10-07 13:00:21.622 DEBUG (MainThread) [custom_components.zha_toolkit] Handler: <function command_handler_default at 0x7f3ce19620>
2023-10-07 13:00:21.622 DEBUG (MainThread) [custom_components.zha_toolkit] running default command: <ServiceCall zha_toolkit.attr_read (c:01HC5HADPQJWMGFW82F70VKJVT): ieee=50:0b:91:40:00:01:f4:dc, manf=17672, endpoint=1, cluster=65281, attribute=85, event_success=my_read_success_trigger_event, event_fail=my_read_fail_trigger_event, event_done=my_read_done_trigger_event, csvout=../www/test.csv>
2023-10-07 13:00:21.625 DEBUG (MainThread) [custom_components.zha_toolkit.default] Trying to import custom_components.zha_toolkit.zcl_attr to call attr_read
2023-10-07 13:00:21.629 DEBUG (MainThread) [custom_components.zha_toolkit.zcl_attr] Request attr read [85]
2023-10-07 13:00:21.630 DEBUG (MainThread) [zigpy.zcl] [0x2B33:1:0xff01] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=True, direction=<Direction.Server_to_Client: 0>, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), manufacturer=17672, tsn=103, command_id=<GeneralCommand.Read_Attributes: 0>, *direction=<Direction.Server_to_Client: 0>)
2023-10-07 13:00:21.630 DEBUG (MainThread) [zigpy.zcl] [0x2B33:1:0xff01] Sending request: Read_Attributes(attribute_ids=[85])
2023-10-07 13:00:21.657 DEBUG (MainThread) [zigpy.zcl] [0x2B33:1:0xff01] Received ZCL frame: b'\x1c\x08Eg\x0b\x00\x84'
2023-10-07 13:00:21.658 DEBUG (MainThread) [zigpy.zcl] [0x2B33:1:0xff01] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=True, direction=<Direction.Client_to_Server: 1>, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), manufacturer=17672, tsn=103, command_id=11, *direction=<Direction.Client_to_Server: 1>)
2023-10-07 13:00:21.659 DEBUG (MainThread) [zigpy.zcl] [0x2B33:1:0xff01] Decoded ZCL frame: LightManufacturerCluster:Default_Response(command_id=0, status=<Status.UNSUP_MANUF_GENERAL_COMMAND: 132>)
2023-10-07 13:00:21.661 DEBUG (MainThread) [custom_components.zha_toolkit.zcl_attr] Reading attr result (attrs, status): ({}, {85: 0})
2023-10-07 13:00:21.661 WARNING (MainThread) [custom_components.zha_toolkit.zcl_attr] Result: {85: 0} - Attribute 85 not in read ({}, {85: 0})
2023-10-07 13:00:21.668 DEBUG (MainThread) [custom_components.zha_toolkit] event_data {'zha_toolkit_version': 'v1.1.2', 'zigpy_version': '0.57.2', 'zigpy_rf_version': '0.21.1', 'ieee_org': 50:0b:91:40:00:01:f4:dc, 'ieee': '50:0b:91:40:00:01:f4:dc', 'command': 'attr_read', 'command_data': None, 'start_time': '2023-10-07T17:00:21.621637+00:00', 'errors': [], 'params': {'endpoint_id': 1, 'cluster_id': 65281, 'attr_id': 85, 'dir': 0, 'manf': 17672, 'tries': 1, 'expect_reply': True, 'args': [], 'event_success': 'my_read_success_trigger_event', 'event_fail': 'my_read_fail_trigger_event', 'event_done': 'my_read_done_trigger_event', 'read_before_write': True, 'read_after_write': True, 'csvfile': '../www/test.csv'}, 'write_is_equal': False, 'result_read': ({}, {85: 0}), 'warnings': ['Result: {85: 0} - Attribute 85 not in read ({}, {85: 0})'], 'success': False}
2023-10-07 13:00:21.668 DEBUG (MainThread) [custom_components.zha_toolkit] Fire my_read_fail_trigger_event -> {'zha_toolkit_version': 'v1.1.2', 'zigpy_version': '0.57.2', 'zigpy_rf_version': '0.21.1', 'ieee_org': 50:0b:91:40:00:01:f4:dc, 'ieee': '50:0b:91:40:00:01:f4:dc', 'command': 'attr_read', 'command_data': None, 'start_time': '2023-10-07T17:00:21.621637+00:00', 'errors': [], 'params': {'endpoint_id': 1, 'cluster_id': 65281, 'attr_id': 85, 'dir': 0, 'manf': 17672, 'tries': 1, 'expect_reply': True, 'args': [], 'event_success': 'my_read_success_trigger_event', 'event_fail': 'my_read_fail_trigger_event', 'event_done': 'my_read_done_trigger_event', 'read_before_write': True, 'read_after_write': True, 'csvfile': '../www/test.csv'}, 'write_is_equal': False, 'result_read': ({}, {85: 0}), 'warnings': ['Result: {85: 0} - Attribute 85 not in read ({}, {85: 0})'], 'success': False}
2023-10-07 13:00:21.670 DEBUG (MainThread) [custom_components.zha_toolkit] Fire my_read_done_trigger_event -> {'zha_toolkit_version': 'v1.1.2', 'zigpy_version': '0.57.2', 'zigpy_rf_version': '0.21.1', 'ieee_org': 50:0b:91:40:00:01:f4:dc, 'ieee': '50:0b:91:40:00:01:f4:dc', 'command': 'attr_read', 'command_data': None, 'start_time': '2023-10-07T17:00:21.621637+00:00', 'errors': [], 'params': {'endpoint_id': 1, 'cluster_id': 65281, 'attr_id': 85, 'dir': 0, 'manf': 17672, 'tries': 1, 'expect_reply': True, 'args': [], 'event_success': 'my_read_success_trigger_event', 'event_fail': 'my_read_fail_trigger_event', 'event_done': 'my_read_done_trigger_event', 'read_before_write': True, 'read_after_write': True, 'csvfile': '../www/test.csv'}, 'write_is_equal': False, 'result_read': ({}, {85: 0}), 'warnings': ['Result: {85: 0} - Attribute 85 not in read ({}, {85: 0})'], 'success': False}

I just remove manf: 17672 from

service: zha_toolkit.attr_read
data:
  ieee: 50:0b:91:40:00:01:f4:dc
  endpoint: 1
  cluster: 65281
  attribute: 85
  event_success: my_read_success_trigger_event
  event_fail: my_read_fail_trigger_event
  event_done: my_read_done_trigger_event
  csvout: ../www/test.csv

and the read was succesfull, same value as ZHA manage device
But for

service: zha_toolkit.scan_device
data:
  ieee: 50:0b:91:40:00:01:f4:dc
  endpoint: 1
  event_success: my_read_success_trigger_event
  event_fail: my_read_fail_trigger_event
  event_done: my_read_done_trigger_event

Nothing is reported for cluster 65281, manufacturer cluster
attributes: {}

The above shows that the device replied that the command is not supported.

While 0xFF01 is a manufacturer specific cluster, and apparently the attributes are read “normally” here, without requesting the attributes using the manufacturer id.

Again, the “scan_device” feature requires that discovery features are implemented in the zigbee device. There are devices that do not report any attribute - including standard ones. And so I am not surprised to see that this device does not report attributes in the manufacturer specific cluster.

Anyway, the mystery is solved - I was under the impression that it was certain that this attribute had to be readable using the manufacturer id, but it is not.

Ok thank you for your help and great zha-toolkit
I’ll keep sniffing :slight_smile:

**ZHA Toolkit: ezsp_backup did not work**. Is the error known?

I just tested the ezsp_backup on:

zha_toolkit_version: 1.1.3
zigpy_version: 0.57.2
zigpy_rf_version: 0.36.5

You may find more information in the home-assistant.log.
Are you sure you have an ezsp radio. Try backing up with “zha_toolkit.backup”. It will do “the same thing”, but determine which radio it is for you.

Hi @le_top, I need to send write command via zha_toolkit.attr_write for Data Type: Array (0x48). The data is as follow:

ZigBee Cluster Library Frame, Mfr: Unknown (0x119c), Command: Read Attributes Response, Seq: 94
         Frame Control Field: Profile-wide (0x1c)
             .... ..00 = Frame Type: Profile-wide (0x00)
             .... .1.. = Manufacturer Specific: True
             .... 1... = Direction: To Client
             ...1 .... = Disable Default Response: True
        Manufacturer Code: Unknown (0x119c)
        Sequence Number: 94
        Command: Read Attributes Response (0x01)
        Status Record
          Status Record
          Attribute: 0x0240
          Status: Success (0x00)
          Data Type: Array (0x48)
          Elements Type: 8-Bit Unsigned Integer (0x20)
          Elements Number: 12
          Element #1, Uint8: 194
                Uint8: 194 (0xc2)
          Element #2, Uint8: 17
                Uint8: 17 (0x11)
          Element #3, Uint8: 0
                Uint8: 0 (0x00)
          Element #4, Uint8: 0
                Uint8: 0 (0x00)
          Element #5, Uint8: 136
                Uint8: 136 (0x88)
          Element #6, Uint8: 119
                Uint8: 119 (0x77)
          Element #7, Uint8: 0
                Uint8: 0 (0x00)
          Element #8, Uint8: 0
                Uint8: 0 (0x00)
          Element #9, Uint8: 1
                Uint8: 1 (0x01)
          Element #10, Uint8: 0
                Uint8: 0 (0x00)
          Element #11, Uint8: 0
                Uint8: 0 (0x00)
          Element #12, Uint8: 0
                Uint8: 0 (0x00)

So the write commande via zha_toolkit.attr_write should be like this ?

service: zha_toolkit.attr_write
data:
  ieee: 50:0b:91:40:00:03ed:b0
  endpoint: 1
  cluster: 0xff01
  attribute: 0x0240
  attr_type: 0x48
  attr_val: [20, 0c, 00, c2, 11, 00, 00, 88, 77, 00, 00, 01, 00, 00, 00]
  read_before_write: true
  read_after_write: true
  use_cache: false

Do I need to add other parameters or change something ?

You are not too far off, but when you provide hexadecimal values, you need to prefix them with 0x.

So I can use decimal or hexadecimal like this ?
decimal:
attr_val: [32, 12, 0, 194, 17, 0, 0, 136, 119, 0, 0, 1, 0, 0, 0]

hexadecimal:
attr_val: [0x20, 0x0c, 0x00, 0xc2, 0x11, 0x00, 0x00, 0x88, 0x77, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]

Both are OK ?

Yes both are ok, and you can mix.

You will see that in the response you get a “binary” string, you should be able to provide the data like that as well.

Thank you, Does the array write already enabled in HA? and is there an option array read ?