ZHA Toolkit (Zigbee Home Assistant Toolkit) helps go beyond some limitations when using the ZHA integration component in Home Assistant (an open source home automation software). It does so by providing extra methods to execute Zigbee requests and helps bridge between ZHA functionality and Home Assistant.
You can add ZHA Toolkit to Home Assistant using HACS (Home Assistant Community Store). ZHA Toolkit is available in HACS' default repository list.
The purpose of ZHA Toolkit and its Home Assistant 'Services' feature, is to provide direct control over low-level zigbee commands provided in ZHA or Zigpy that are not otherwise available or too limited for some use cases.
ZHA Toolkit can also:
-
Serve as a framework to do local low-level coding. ZHA Toolkit applies code changes immediately by reloading its python modules on each call, including the user custom modules.
-
Provide access to some higher-level commands such as ZNP backup (and restore).
-
Make it easier to perform one-time operations where (some) Zigbee knowledge is sufficient and avoid the need to understand the inner workings of ZHA or Zigpy (methods, quirks, etc).
-
Download Firmware referenced in Koenkk/zigbee-OTA.
- Reads Zigbee attributes into Home Assistant states/attributes
- Daily ZNP Coordinator backup (See blueprint)
- Provides "Low level" access to most Zigbee commands (read/write/(un)bind/report/cmd/discover)
- Purpose
- Table of Contents
- Setup
- Automations
- Using
zha-toolkit
- General recommendations
- Zigbee crash course
- Common options
- Service commands
attr_read
: Read an attribute valueattr_write
: Write(/Read) an attribute value- Binding related
conf_report
: Configure reportingconf_report_read
: Read configured reportingscan_device
: Scan a device/Read all attribute valueszdo_scan_now
: Do a topology scan- Join & Network presence related
zcl_cmd
: Send a Cluster command- Group related services
- EZSP/Bellows
- ZNP related (TI Zigbee Radio)
- Miscellaneous
- User method
- Manufacturers
- Credits/Motivation
- License
- Contributing
ZHA Toolkit uses the well-known HACS installation mechanism. It is recommended to use HACS which facilitates the installation of many other custom components as well.
If you already have HACS (Tutorial), simply look for "ZHA Toolkit" under Integrations or click this redirection to add it.
If you are not using HACS, you need add the files in
custom_components/zha_toolkit
. See
installNoHacsFromZip.sh and
installNoHacsWithGit.sh for possible
procedures.
In all cases (HACS or manual), the ZHA Toolkit integration is only active
on your Home Assistance instance after adding next line to
configuration.yaml
, and restarting Home Assistant.
zha_toolkit:
Before restarting, you may also want to enable debug verbosity.
zha-toolkit
isn't verbose when you use it occasionnaly. As it's a
service, there is no really good way to inform the user about errors other
than the log.
Logging will help verify that the commands you send have the desired effect.
Add/update the logger configuration (in the configuration.yaml
file):
logger:
# The next line sets the default logging level, for all python modules.
# It seems "recommended" to set it to avoid too much logging.
default: warning
logs:
custom_components.zha_toolkit: debug
You can also change the log configuration dynamically by calling the
logger.setlevel
service. Example that sets the debug level for this
zha_toolkit
component and for zigpy.zcl
(which helps to see some
information about actual ZCL frames sent). This method allows you to enable
debug logging only for a limited duration :
service: logger.set_level
data:
custom_components.zha_toolkit: debug
zigpy.zcl: debug
This is a list (of 1) automation:
This component provides a single service (zha_toolkit.execute
) that
provides several commands (command
parameter) providing access to
ZHA/Zigbee actions that are not otherwise available.
You can use a service as an action in automations. So you can send the commands according to a schedule or other triggers. For instance, you could plan a daily backup of your TI-ZNP USB Key configuration.
It will be more common to send a Zigbee command only once: for instance
bind one device to another, set a manufacturer attribute, ... .
You can
perform them using the developer tools.
The developer tools are handy to
test the service first before adding them to an automation.
Go to Developer Tools > Services in your instance : .
Choose the generic service zha_toolkit.execute
or - more convenient - the
specific zha_toolkit.<COMMAND>
as the service.
Most parameters can be
set using the UI, there are some cases where you may want to enable Yaml
entry - you'll have some more flexibility and all parameters fit in your
browser view. On the other hand, the UI interface makes it easier to select
the entity. You can switch back and forth!
There are several examples below for different commands. You can copy/paste them to start from.
Not all available commands are documented. The undocumented ones were in
the original repository.
Some of these undocumented commands seem to be
very specific trials from the original authors.
Feel free to propose
documentation updates.
- Check this README.
- Use
scan_device
to find out more about your device. - Use the Service Response to see what happens.
- Use Events to see what happens (if you can't use the service Response).
- Use
home-assistant.log
to see what happened. - Set the log level to debug (See Setup to get more facts.
- Check the Github open and closed issues
- Check the Home Assistant Forum
- Check the examples directory
- Check zhaquirks for hints about available attributes (available ones, meaning of their values)
- Wake up sleepy devices (generally devices on a battery) just after
sending a command so that they can receive it. It's also recommended to
set the
tries
parameter to a fairly high number for these devices (in some cases over a 100 tries (more than 10 minutes) are needed to successfully communicate with a sleepy device).
Note: this crash course's wording may deviate from Zigbee's wording.
Zigbee is a wireless communication protocol used for creating networks of devices. To understand Zigbee, we can look at it from two perspectives: the network-oriented view and the device-oriented view.
From a network perspective, there is a coordinator, which is the main
device controlling the network (often your Home Assistant instance), and
there are other devices categorized as routers and end devices.
Routers
are permanently powered devices that store and forward messages, while end
devices can be any type of device. End devices can reply to requests and
communicate autonomously if they have reporting configurations and bindings
set up. Reporting configurations determine when a device should communicate
attribute changes, and bindings specify which device or group should
receive these changes.
Commands, such as those triggered by a button
press, can also be bound to specific devices or groups.
From a device perspective, devices are organized into endpoints.
An
endpoint represents a function or feature of the device. For example, a
device with two switches would have an endpoint for each switch function,
and a device with a temperature and humidity sensor may have an endpoint
for each sensor.
Endpoints can also represent Zigbee-specific
functionalities like Over The Air (OTA) updates or Green Power
functionality.
Each endpoint has attributes associated with it.
Attributes allow you to
control the configuration of the device or retrieve values for its current
state, such as on/off status or temperature readings.
Attributes are
grouped into clusters, which are reusable sets of features.
Clusters
represent things like on/off state, color control, temperature measurement,
energy metering, and more.
In practice, clusters are defined on endpoints, and each attribute has a unique address consisting of the IEEE address (a 64-bit number), the endpoint ID (a byte), the cluster ID (a two byte word), and the attribute ID (a two byte word as well).
Attributes have different types, such as boolean, unsigned and signed byte, arrays, timestamps, and more. In most cases, the attribute type can be determined automatically by tools like zha-toolkit and ZHA.
The Zigbee Cluster Library (ZCL) document defines standard attributes and their organization in clusters. Manufacturers also have the freedom to add their own attributes that are not defined in the ZCL.
Commands and attribute read/write operations are typically initiated from the coordinator. However, Zigbee devices can also send commands to other devices, like a switch instructing a light bulb to turn on or off.
To avoid excessive network traffic caused by constantly polling devices for
their internal state, devices can be configured to report their state
changes to other devices in the network.
This reporting is accompanied by
bindings, which specify the devices or groups that should receive the data.
Binding can also make an endpoint on a device respond to commands sent to a
specific group.
For example, if you add a switch's endpoint and multiple
light bulbs to a group, the switch can control all the bulbs in that group.
When a new device is added to a Zigbee network through Home Assistant's ZHA integration, an initial configuration is sent to the device. This configuration sets up reporting and binding settings to ensure that the device notifies the coordinator about its state changes or any relevant data changes, such as energy consumption metrics or simply informing that a light has been switched on.
Zigbee routers play an important role in the network by relaying messages between devices, bridging longer distances, and temporarily storing messages for battery-powered devices that wake up periodically. Messages are typically stored for around 6 seconds.
Battery-powered devices are considered "sleepy" devices because they conserve energy by sleeping most of the time. However, this can lead to data requests and commands being lost if the device is asleep when they are sent. To ensure successful communication, requests may need to be actively repeated until the sleepy device wakes up and responds.
In some cases, it is necessary to provide the manufacturer ID to access a manufacturer-specific attribute or execute a manufacturer-specific command to use features or functionalities specific to the device manufacturer,
ZHA and zha-device-handlers and zigpy intend to wrap the zigbee attribute operations and commands so that the Zigbee device features are immediately usable in Home Assistant. The quality of these integrations and libraries are ensured through unit tests.
zigpy
is a library/gateway that bridges the gap between python and the zigbee coordinator hardware.- ZHA interfaces zigpy with Home Assistant.
- The zha-device-quirks library is delivered at the same time as ZHA and adapts "quirky" device behavior to interface with Home Assistant through ZHA. "Quirky" device behavior means that a device is providing functionality not or not as anticipated in the Zigbee specifications.
- ZHA-Toolkit is there to help with using device functionalities that are not fully supported yet in ZHA, or help implement scripts and automations that are not part of the main focus of the ZHA integration. There currently are no test cases - so there is less quality assurance and a function might accidentally drop at some point. It is not autonomously listening in on Zigbee messages to generate Home Assistant events for instance, but you can use it to poll devices and update state values directly, or send commands you can't send through ZHA. ZHA-Toolkit can help with diagnostics and device configurations.
In almost all commands you need to provide a reference to the device that you want to control.
Easiest, use an entity name:
service: zha_toolkit.SOME_SERVICE
data:
# entity name (one of them)
ieee: light.tz3000_odygigth_ts0505a_12c90efe_level_light_color_on_off
Harder, find and use the IEEE address:
service: zha_toolkit.SOME_SERVICE
data:
# Valid possibilities for the ieee address
# The full IEEE address:
ieee: 00:12:4b:00:24:42:d1:dc
Even more difficult, find and use the devices' short address, which may change over time.
service: zha_toolkit.SOME_SERVICE
data:
# The short network address
ieee: 0x2F3E
The ieee
address can be the IEEE address, the short network address
(0x1203 for instance), or the entity name (example:
light.tz3000_odygigth_ts0505a_12c90efe_level_light_color_on_off
). Be
aware that the network address can change over time but it is shorter to
enter if you know it.
There is no universal "best" way of selecting the device.
- The IEEE address is the only reference that does not change over time.
- The entity name is surely easiest to find. While as a user you can change it, it is also somewhat stable when you replace a device - you could keep the same entity name for a difference device.
- The short address might be shorter to type if you already know it, but do not rely on it if you scripts needs to continue to work over time.
Sometime the command_data
field provides a reference to a device (for
instance, when binding one device to another). It has the same flexibility
as the ieee
argument.
The zha-toolkit services return their results as response data for Home Assistant installations that have at least version 2023.7 .
This response is available in automations and script by adding
response_variable: VAR_NAME
to the zha_toolkit
service call. That will
make the data available under VAR_NAME inside the automation or script.
An
example using the response data can be found in
script_use_zha_devices_response.yaml.
The response data feature of Home Assistant also makes the response
available when calling the service interactively.
The image below shows a
an interactive attribute read using the zha-toolkit service attr_read. The
response data is shown in Yaml format when the call finishes.
Events in Home Assistant are a way to trigger or proceed in automations and scripts, and convey data.
All zha-toolkit commands support setting event names that are fired at the end of the command execution.
You can interactively listen for events in the Developer Tools>Events page which is a good way to check the result of a zha-toolkit service that you start in another tab of your browser.
You can set the event names as follows:
service: zha_toolkit.SERVICE_NAME
data:
# You can set the next events to use as a trigger.
# The event data has the result of the command
event_success: my_read_success_trigger_event
event_fail: my_read_fail_trigger_event
event_done: my_read_done_trigger_event
It's recommended to use the event_done
event during interactive use. You
can use Developer Tools > Events > Listen to events
to see the result of
the service call. You need to use Listen to events
in a separate
navigator tab, START LISTENING
and leave it open to see the data of the
events.
By listening for the event, you can see the list of groups that is found
when using zha_toolkit.get_groups
for instance.
Otherwise you need to
set the debug level and watch the home-assistant.log
. That
can be useful if you do a lot of service calls in sequence and you want to
look back what happened.
You can also simply always enable debugging for zha_toolkit
if you use it
sporadically - it is quite verbose and tends to fill up the logs if you use
it often.
service: zha_toolkit.SERVICE_CALL
data:
fail_exception: true
By default, the result of a zigbee transaction is "ignored" for the end result of the service call: it will appear as if it succeeds (unless you have the parameters wrong).
So, if you want the Developer Tools > Services > CALL SERVICE
button to
turn red in case the zigbee transaction result is not SUCCESS
, then add
fail_exception: true
to the options
service: zha_toolkit.SERVICE_CALL
data:
tries: 10
Tries indicates how many times a zigbee transaction is repeated until it succeeds. An individual zigbee transaction may fail because of radio interference or because the device is sleeping.
So by setting tries: 100
you'll request that zigbee requests are repeated
up to 100 times.
This is not applied everywhere, but it's applied for attribute reading, writing and report configuration. It's handy when you want to change the report configuration of your battery powered thermometer for instance.
You may still need to wake them up just after sending the command so that they can receive it.
Services are easy to called once or tested through Developer Tools > Services . And you can also use them in scripts, automations, etc. .
Quite a few services can be configured from the UI. And you can also start using the UI (to select the ieee/entity for instance), and then Go To YAML mode to add the other parameters.
An example of event data is shown below. The data>errors
field can be
useful to understand what went wrong. The ieee_org
fields take the
original value of the "ieee" parameter, and the ieee
field is the actual
IEEE address found.
{
"event_type": "my_write_done_trigger_event",
"data": {
"ieee_org": "sensor.test_smartenergy_metering",
"ieee": "00:12:4b:00:24:42:d1:dc",
"command": "attr_write",
"start_time": "2022-01-17T21:51:50.416725+00:00",
"errors": [],
"params": {
"cmd_id": null,
"endpoint_id": 1,
"cluster_id": 0,
"attr_id": 16,
"attr_type": 66,
"attr_val": "BureauTest",
"min_interval": 60,
"max_interval": 300,
"reportable_change": 1,
"dir": null,
"manf": null,
"tries": 1,
"expect_reply": true,
"args": [],
"state_id": "sensor.test",
"state_attr": null,
"allow_create": true,
"event_success": "my_write_success_trigger_event",
"event_fail": "my_write_fail_trigger_event",
"event_done": "my_write_done_trigger_event",
"read_before_write": true,
"read_after_write": true,
"write_if_equal": false
},
"str": "BureauTest",
"read_before": [
{
"16": "Bureau"
},
{}
],
"result_write": [
[
{
"status": 0,
"attrid": null
}
]
],
"result_read": [
{
"16": "BureauTest"
},
{}
],
"success": true
},
"origin": "LOCAL",
"time_fired": "2022-01-17T21:52:02.066310+00:00",
"context": {
"id": "c5d4d0d14f7801fda3b9ad471dcbd83b",
"parent_id": null,
"user_id": null
}
}
Home Assistant evolved and now shows the event data as a yaml structure.
Services that are not documented in the sections that follow below (not including undocumented ezsp commands):
all_routes_and_neighbours
bind_group
get_routes_and_neighbours
ieee_ping
unbind_group
zdo_flood_parent_annce
zdo_update_nwk_id
Read a zigbee attribute value, optionally write to a state.
service: zha_toolkit.attr_read
data:
ieee: sensor.zigbee_sensor
# The endpoint is optional - when missing tries to find endpoint matching the cluster
# endpoint: 1
# Cluster Id - Can be omitted if attribute is unique string.
cluster: 0xb04
# Attribute Id or String - Example "pi_heating_demand"
attribute: 0x50f
# Optional, read the value from memory cache, do not make a zigbee read request.
# Can be false, true, 0, 1 or 2 where '2' requests to fallback to a real read
# if the value is not in cache.
use_cache: true
# Optional, state to write the read value to
state_id: sensor.test
# Optional, state attribute to write the value to, when missing: writes state itself
state_attr: option
# Optional, when true, allows creating the state (if not the state must exist)
allow_create: true
# The manufacturer should be set only for manufacturer attributes
manf: 0x1212
# Write read value to CSV file
# Can be useful in automation/script
# Format: <timestamp>,<name|attr_id>,<value>,<attr_id>,<cluster_id>,<ep_id>,<ieee>,<manf_id>
# Optional: CSV file to write attribute to - located in /config/csv/...
csvout: testcsv.csv
# optional: csvlabel (default label = name from zigpy or attribute id)
csvlabel: MyAttributeLabel
service: zha_toolkit.attr_read
data:
ieee: light.texasinstruments_ti_samplelight_d77add01_level_light_color_on_off
event_done: zha_done
attribute: 0
cluster: 0
csvout: testcsv.csv
Below is the result in /config/csv/testcsv.csv
produced by the
attr_read
shown above.
2022-02-01T00:10:50.202707+00:00,zcl_version,1,0x0000,0x0000,11,00:12:4b:00:01:dd:7a:d7,,0x20
Fields in this output:
ISO8601_Timestamp,cluster_name,attribute_name,value,attr_id,cluster_id,endpoint_id,IEEE,manf,attr_type
This example reads the raw temperature value from cache into a home state attribute value.
The purpose of this example is to get the unrounded reported value from a temperature sensor.
A battery powered temperature sensor is often sleepy and doing a real attribute read may need many tries.
So this technique allows reading the value from the attribute cache. It does not use the attribute cache database table, but tries to get the value from the in-memory cache.
When use_cache
is 2, an actual read will be executed if the attribute is
not in cache.
service: zha_toolkit.attr_read
data:
ieee: sensor.temperature_chambre_x_temperature_2
cluster: 1026
attribute: 0
use_cache: true
state_id: sensor.temperature_chambre_x_temperature_2
state_attr: raw_degc
# When defined, the read attribute is converted using this template string
# before writing it to the state.
# Note that no curly braces should be used here!
state_value_template: value/100
For a real use case, see the example danfoss_ally_remote_temperature_min_delay.yaml where the automation attempts to read the temperature from the zigbee cache to get more precision (0.01°C) as ZHA rounds values to 0.1°C.
Write an attribute value to any endpoint/cluster/attribute.
You can provide the numerical value of the attribute id, or the internal zigpy name (string).
Before and after writing, the value is read from the attribute. If debug
logging is active, this will be visible in the home_assistant.log
. The
last read this can be written to a state.
Note that the format/typing of the value for attr_val
depends on the
attribute type. uint*
, int*
and enum*
types can be specified as
numbers, most other types must be specified as an array of bytes or a
string. If you are not sure how to represent the value, you could do an
attr_read
first while observing the event_done
event to check how the
attr_val
is returned upon read.
In the yaml example detailing the available parameters to attr_write
it
is shown how to specify the value for an octet_string
as an array/list.
service: zha_toolkit.attr_write
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# The endpoint is optional,
# when missing, attr_write will find the endpoint
# by matching the cluster which works if there is
# only one endpoint with that cluster.
endpoint: 11
cluster: 0x1706
attribute: 0x0000
attr_type: 0x41
# Example of octet strings (the length is added because of attr_type)
attr_val: [41, 33, 8, 45, 52, 46, 50, 191, 55, 57, 136, 60, 100, 102, 63]
# Optional manufacturer Id
# - The manufacturer should be set only for manufacturer attributes
manf: 0x1021
# Optional, state to write the read value to
state_id: sensor.test
# Optional, state attribute to write the value to, when missing: writes state itself
state_attr: option
# Optional, when true, allows creating the state (if not the state must exist)
allow_create: true
# You can set the next events to use as a trigger.
# The event data has the result of the command (currently attr_read, attr_write)
event_success: my_read_success_trigger_event
event_fail: my_read_fail_trigger_event
event_done: my_read_done_trigger_event
# Settings for attr_write
# Read attribute before writing it (defaults to True)
read_before_write: true
# Read attribute after writing it (defaults to True)
read_after_write: true
# Write attribute when the read value matches (defaults to False)
write_if_equal: false
In case ZCL Array type needs to be written, attr_val
needs to be provided
as a raw sequence of bytes, i.e. user is responsible to generate a sequence
which complies to the ZCL spec.
The following examples illustrates
configuration of Ubisys C4 (see
the device manual
- section 7.8.5.2. InputActions Attribute - example):
service: zha_toolkit.attr_write
data:
ieee: 00:1f:ee:00:00:aa:aa:aa
endpoint: 232
cluster: 64512
attribute: 1
attr_type: 0x48
# For the array type (type 0x48):
# - The first byte is the type of items. here 65 or 0x41: octet str.
# - The second and third byte compose the length (little endian)
# So here: `4, 0` is 0x0004, so four octet strings the array.
# - All the octet strings in this example have a length of 6.
attr_val: [65, 4, 0, 6, 0, 13, 1, 6, 0, 2, 6, 1, 13, 2, 6, 0, 2, 6, 2, 13, 3, 6,
0, 2, 6, 3, 13, 4, 6, 0, 2]
read_before_write: false
read_after_write: false
use_cache: false
Such a packet decoded using tshark/wireshark, the above results in:
ZigBee Cluster Library Frame, Command: Write Attributes, Seq: 40
Frame Control Field: Profile-wide (0x00)
.... ..00 = Frame Type: Profile-wide (0x0)
.... .0.. = Manufacturer Specific: False
.... 0... = Direction: Client to Server
...0 .... = Disable Default Response: False
Sequence Number: 40
Command: Write Attributes (0x02)
Attribute Field
Attribute: Unknown (0xfde8)
Data Type: Array (0x48)
Elements Type: Octet String (0x41)
Elements Number: 4
Element #1, Octets: 00:0d:01:06:00:02
Octet String: 00:0d:01:06:00:02
Element #2, Octets: 01:0d:02:06:00:02
Octet String: 01:0d:02:06:00:02
Element #3, Octets: 02:0d:03:06:00:02
Octet String: 02:0d:03:06:00:02
Element #4, Octets: 03:0d:04:06:00:02
Octet String: 03:0d:04:06:00:02
Decrypted ZigBee Payload (45 bytes) - only Array related data is shown:
0000 48 41 04 @........(...HA.
0010 00 06 00 0d 01 06 00 02 06 01 0d 02 06 00 02 06 ................
0020 02 0d 03 06 00 02 06 03 0d 04 06 00 02 .............
Using the symbolic name of the attribute, and automatic endpoint selection.
service: zha_toolkit.attr_write
data:
ieee: button.fictious_model_dcd14224_identify
cluster: 0
attribute: location_desc
attr_val: My Location
A more complex example using HA's templating feature can be found
below.
Each call will increment the previous target temperature by 0.5
degrees (increment by 50 in Zigbee's unit) up to 22.50 degrees and restart
from 20 degrees.
On the first call (when the state is not set yet), the
setpoint temperature is 21.5 degrees.
The toolkit implements a read after each write (and it is not disabled by a
read_after_write
parameter), so it will write the temperature value to
the state sensor.tgt_temperature
.
Note that a template is evaluated before calling the service, so the
read_before_write
can't influence the attribute to write during the same
service call even though it updates the attribute. The read before write
could still be useful if you want to track updates in history graphs for
instance.
This example also uses the attribute name, not the attribute id.
Tries is set to 3 to cope with some uncommon communication issues.
service: zha_toolkit.attr_write
data:
ieee: entity.my_thermostat_entity
cluster: 0x201
attribute: occupied_heating_setpoint
attr_val: "{% set t = states('sensor.tgt_temperature') %}{{ [(t|int+50) % 2300,2000]|max
if is_number(t) else 2150 }}"
state_id: sensor.tgt_temperature
allow_create: true
read_before_write: false
tries: 3
fail_exception: true
The default list of binding clusters is currently as follows:
- in clusters:
- 0x0006 - OnOff
- 0x0008 - Level
- 0x0300 - Color Control
- out clusters:
- 0x0402 - Temperature
Bind all available default and matching clusters from ieee
to
command_data
on all endpoints.
Binds to the coordinator if
command_data
is not set or 0.
By default only binds cluster types in the
internal list, i.e. OnOff, Level and Color Control clusters.
The cluster must exist on both devices, except when the coordinator is the target.
If you set the cluster
, you can bind another cluster type and only that
cluster will be bound (both in and out clusters).
Use binds_get
to verify that the configuration worked.
It's possible to attempt binding specific endpoints between ieee
and
command_data
.
The endpoint for ieee
(source) can be provided by
endpoint
.
If it's not specified then all endpoints on ieee
are
considered.
Endpoint for command_data
(destination) can be provided by
dst_endpoint
. If it's not specified then all endpoints on command_data
are considered
and the first one that matches cluster-wise will be picked.
service: zha_toolkit.bind_ieee
data:
ieee: entity.my_thermostat_entity
# Optional, when not set or 0, bind to the coordinator.
command_data: 00:12:4b:00:22:08:ed:1a
# Optional, if you want to bind a cluster not internally selected.
cluster: 0x0006
# Optional: source endpoint (for ieee)
endpoint: 2
# Optional: destination endpoint (for command_data)
dst_endpoint: 3
Get the bindings from the device.
Listen to the event, or enable debug and
check the log to get the information.
service: zha_toolkit.binds_get
data:
ieee: 00:15:8d:00:04:7b:83:69
# Optional number of tries for each sub-request,
# useful for sleepy devices
tries: 100
event_done: event_binds_get_done
Remove all bindings from the device.
This internally fetches all the
existing bindings (binds_get
service) and requests the device to remove
them.
service: zha_toolkit.binds_remove_all
data:
ieee: entity.my_thermostat_entity
# Optional - only remove binding to device
command_data: 00:12:4b:00:01:6a:41:0c
# Optional - name of generated event when done
event_done: zhat_event
# Optional - Endpoint or list of endpoints for which to remove bindings
# endpoint: [20, 30]
# Optional - Cluster or list of clusters for which to remove bindings
# cluster: [0x0006, 0x0300]
# Optional
tries: 100
Remove all bindings from the device to the coordinator. Typically, on device initialization Home Assistant sets up bindings with the main clusters to that it is informed about state changes.
This command will use binds_remove_all
and set the coordinator's ieee
address as the command_data
parameter automatically avoiding that you
have to look it up.
service: zha_toolkit.unbind_coordinator
data:
ieee: entity.my_thermostat_entity
# Optional - name of generated event when done
event_done: zhat_event
# Optional - Endpoint or list of endpoints for which to remove bindings
# endpoint: [20, 30]
# Optional - Cluster or list of clusters for which to remove bindings
# cluster: [0x0006, 0x0300]
# Optional
tries: 100
Set the minimum and maximum delay between two reports and set the level of change required to report a value (before the maximum delay is expired).
This example configures Temperature reporting on a SonOff SNZB-02 (eWeLink/TH01). Note that on some devices you (may) need to press the button on the thermometer just after requesting the command (it's a sleepy device and does not wake up often). With a temperature sensor it may be more appropriate to sure a high temperature rise to force it to report the temperature and allow ZHA to send the configuration.
After succeeding the configuration, the minimum delay was actually 20s which is likely the measurement period itself. The changes were reported when they exceeded 0.10 degrees C.
For sleepy devices, you can add the parameter 'tries' which will retry until the devices confirms (with success or error)
service: zha_toolkit.conf_report
data:
ieee: 00:12:4b:00:23:b3:da:a5
# Optional endpoint, when missing will match cluster
# endpoint: 1
cluster: 0x402
attribute: 0x0000
min_interval: 60
max_interval: 300
reportable_change: 10
# Optional manufacturer
#manf: 0x1204
# Optional number of configuration attempts
tries: 3
# You can set the next events to use as a trigger.
# The event data has the result of the command (currently attr_read, attr_write)
event_success: my_conf_success_trigger_event
event_fail: my_conf_fail_trigger_event
event_done: my_conf_done_trigger_event
Example of data available in the event report.
{
"event_type": "my_conf_done_trigger_event",
"data": {
"ieee": "00:12:4b:00:24:42:d1:dc",
"command": "conf_report",
"start_time": "2022-01-16T21:56:21.393322+00:00",
"params": {
"cmd_id": null,
"endpoint_id": 1,
"cluster_id": 513,
"attr_id": 0,
"attr_type": null,
"attr_val": null,
"min_interval": 60,
"max_interval": 300,
"reportable_change": 10,
"dir": null,
"manf": null,
"tries": 3,
"expect_reply": true,
"args": [],
"state_id": "sensor.test",
"state_attr": null,
"allow_create": true,
"event_success": "my_conf_success_trigger_event",
"event_fail": "my_conf_fail_trigger_event",
"event_done": "my_conf_done_trigger_event",
"read_before_write": true,
"read_after_write": true,
"write_if_equal": false
},
"result_conf": [
[
{
"status": 0,
"direction": null,
"attrid": null
}
]
]
},
"origin": "LOCAL",
"time_fired": "2022-01-16T21:56:28.248353+00:00",
"context": {
"id": "596b9ba7b29d76545295881ea73c5708",
"parent_id": null,
"user_id": null
}
}
Read the report configuration of a cluster.
service: zha_toolkit.conf_report_read
data:
ieee: 00:12:4b:00:23:b3:da:a5
# Optional endpoint, when missing will match cluster
# endpoint: 1
cluster: 0x402
attribute: 0x0000
# Optional manufacturer
#manf: 0x1204
event_done: my_conf_read_done_trigger_event
Example result (partial event data) where the min and max reporting intervals are provided, as well as the reportable change:
{
"result_conf": [
{
"cluster": "Metering",
"cluster_id": "0x0702",
"attr_id": "0x0000",
"direction": 0,
"type": "0x25",
"min_interval": 1,
"max_interval": 300,
"reportable_change": 1,
"status": 0
}
]
}
scan_device
will generated a report the discovered clusters, attributes,
values and commands if the device implements this feature.
This can help you in discovering what you can configure on your device and what the values are at some point in time.
If the device does not fully support the discovery features, you could
still write a script that would try to read the attributes that you want to
poke using attr_read
.
The result of the scan is written to the scan
directory located in the
configuration directory of Home Assistant (config/scan/*_result.txt
).
The result is also added to the event data in the event['data']['scan']
field which is also available in the response data.
service: zha_toolkit.scan_device
data:
ieee: 00:12:4b:00:22:08:ed:1a
# Optional: endpoint to scan, when missing: all known endpoints
# endpoint: 1
# Optional: endpoints to scan, when missing: all known endpoints
endpoint: [1, 2]
# Optional: tries Default:3 higher is useful for sleepy devices
tries: 100
Scan using the entity name:
service: zha_toolkit.scan_device
data:
ieee: light.tz3000_odygigth_ts0505a_12c90efe_level_light_color_on_off
Runs topology.scan()
.
service: zha_toolkit.execute
data:
command: zdo_scan_now
You may want to try
misc_reinitialize
as
handle_join
will not redo any joining step that already completed.
service: zha_toolkit.execute
data:
# Address of the device that joined
ieee: 00:12:4b:00:22:08:ed:1a
command: handle_join
# NWK address of device that joined (must be exact)
command_data: 0x604e
misc_reinitialize
is a pretty dirty (white-hat) hack to reinitialize a
device by making zigpy think the device is not initialized, and then
requesting an initialization.
This is more than handle_join
which is not reinitializing much when the
device is already set up in zigpy.
misc_reinitialize
sets several device attributes to None and False so
that the zigpy initialization code will proceed with initialization.
service: zha_toolkit.misc_reinitialize
data:
# Reference of the device that should be reinitialized
ieee: 00:12:4b:00:22:08:ed:1a
Send Leave Request to the device.
service: zha_toolkit.leave
data:
# Reference of the device that should be reinitialized
ieee: 00:12:4b:00:22:08:ed:1a
# (Parent ) IEEE address (router) that removes the device (required).
command_data: 00:12:4b:00:01:6a:41:0c
Send Rejoin Request to the device (=Leave with Rejoin).
service: zha_toolkit.rejoin
data:
# Reference of the device that should be rejoined
ieee: 00:12:4b:00:22:08:ed:1a
# Optional, device that will accept joining.
command_data: 00:12:4b:00:10:00:1d:1a
Currently for "bellow's" radio types.
service: zha_toolkit.zdo_join_with_code
data:
# Reference of the device that allows the join
ieee: 00:12:4b:00:22:08:ed:1a
# The code to be used in the join
code: Joining Code
Allows you to send a cluster command. Also accepts command arguments.
Note:
There is also the official core service
zha.issue_zigbee_cluster_command
. You may want to use that instead if it
suits your needs.
The zha_toolkit
version allows lists of bytes as arg
parameters, and has a hack to allow "Add Scene". It is also easier to adapt
than the core that has though release procedures and is not as easily
modifiable as a custom_component
.
service: zha_toolkit.zcl_cmd
data:
# Device IEEE address - mandatory
ieee: 5c:02:72:ff:fe:92:c2:5d
# Command id - mandatory
cmd: 0
# Cluster id - mandatory
cluster: 1006
# Endpoint - mandatory
endpoint: 111
# Optional: direction (0=to in_cluster (default), 1=to out_cluster),
dir: 0
# Optional: expect_reply (default=true - false when 0 or 'false')
expect_reply: true
# Optional: manf - manufacturer - default : None
manf: 0x0000
# Optional: tries - default : 1
tries: 1
# Optional (only add when the command requires it): arguments (default=empty)
args: [1, 3, [1, 2, 3]]
service: zha_toolkit.zcl_cmd
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
cmd: 1
cluster: 6
endpoint: 11
service: zha_toolkit.execute
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
command: zcl_cmd
cmd: 0
cluster: 6
endpoint: 11
service: zha_toolkit.execute
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
command: zcl_cmd
cmd: 4
cluster: 5
endpoint: 11
args: [2, 5]
service: zha_toolkit.execute
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
command: zcl_cmd
cmd: 5
cluster: 5
endpoint: 11
args: [2, 5]
Results in (sniffed):
ZigBee Cluster Library Frame
Frame Control Field: Cluster-specific (0x01)
.... ..01 = Frame Type: Cluster-specific (0x1)
.... .0.. = Manufacturer Specific: False
.... 0... = Direction: Client to Server
...0 .... = Disable Default Response: False
Sequence Number: 94
Command: Recall Scene (0x05)
Payload
Group ID: 0x0002
Scene ID: 0x05
This example shows that you can provide a list of bytes for an argument:
service: zha_toolkit.execute
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
command: zcl_cmd
cmd: 0
cluster: 5
endpoint: 11
args:
- 2
- 5
- 2
- Final Example
# Two bytes of cluster Id (LSB first), length, attribute value bytes
# repeat as needed (inside the list!)
- [0x06, 0x00, 1, 1]
sniffed as:
ZigBee Cluster Library Frame
Frame Control Field: Cluster-specific (0x01)
.... ..01 = Frame Type: Cluster-specific (0x1)
.... .0.. = Manufacturer Specific: False
.... 0... = Direction: Client to Server
...0 .... = Disable Default Response: False
Sequence Number: 76
Command: Add Scene (0x00)
Payload, String: Final Example
Group ID: 0x0002
Scene ID: 0x05
Transition Time: 2 seconds
Length: 13
String: Final Example
Extension Set: 06000101
kwargs
allows passing arbitrary keyword arguments to the underlying ZHA
cluster command handler. For instance, this enables sending an IR remote
code to the cluster command handler in the Tuya TS1201 quirk (see
https://github.com/ferehcarb/zha-device-handlers/blob/fd90c398bd746df22a5cd55e53cd3134fbd7e009/zhaquirks/tuya/ts1201.py#L125).
action: zha_toolkit.zcl_cmd
metadata: {}
data:
ieee: switch.ir_blaster_switch
cluster: 57348
cmd: 2
kwargs:
code: BXcjrhE/AuATAQF+BuAVA8AB4Acn4AMBQBvgBwFAE8ADB92ZdyPMCD8C
Add a group on the endpoint (or all endpoints).
service: zha_toolkit.add_group
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# Group Id
command_data: 0x0021
# Optional endpoint
endpoint: 1
event_done: zha_done
Get the groups defined on the endpoint (or all endpoints)
service: zha_toolkit.get_groups
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# Optional endpoint
endpoint: 1
# Optional event
event_done: zha_done
Remove a group defined on the endpoint (or all endpoints)
service: zha_toolkit.remove_group
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# Group Id
command_data: 0x0021
# Optional endpoint
endpoint: 1
# Optional event
event_done: zha_done
service: zha_toolkit.remove_all_groups
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# Optional endpoint
endpoint: 1
# Optional event
event_done: zha_done
Similar to add_group
but uses another method internally.
service: zha_toolkit.add_to_group
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# Group Id
command_data: 0x0021
# Optional endpoint
endpoint: 1
# Optional event
event_done: zha_done
Similar to remove_group
but uses another method internally.
service: zha_toolkit.remove_from_group
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# Group Id
command_data: 0x0021
# Optional endpoint
endpoint: 1
# Optional event
event_done: zha_done
Get groups on Zigbee Light Link cluster (uses get group identifiers
)
service: zha_toolkit.get_zll_groups
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# Optional endpoint
endpoint: 1
# Optional event
event_done: zha_done
ezsp
refers to EmberZNet Serial Protocol
proposed by Silicon Labs.
bellows
refers to the library providing the interface between zigpy
and
ezsp
compatible zigbee solutions.
This section lists the commands that are used specifically with the
bellows
library.
The following commands are not documented:
ezsp_add_key
ezsp_clear_keys
ezsp_get_config_value
ezsp_get_ieee_by_nwk
ezsp_get_keys
ezsp_get_policy
ezsp_get_token
ezsp_get_value
ezsp_set_channel
ezsp_start_mfg
Used to transfer to another coordinator later, backup or simply get network key and other info.
The output is written to
{custom_component_dir}/local/nwk_backup{command_data}.json
.
You can use the blueprint to setup daily backup: .
The name of that backup is according to the format
service: zha_toolkit.ezsp_backup
data:
# Optional command_data, string added to the basename.
# With this example the backup is written to `nwk_backup_20220105.json`
command_data: _20220105
ZNP stands for "Zigbee Network Processor" and refers to the network layer
proposed by TI's zigbee solutions. zigpy-znp
refers to the plugin/library
that provides the layer that interfaces zigpy
with the ZNP radio.
This section lists the services that specifically target ZNP processors.
The output is written to the customization directory as
local/nvram_backup.json
when command_data
is empty or not provided.
When command_data
is provided, it is added just after nvram_backup.
Note: currently under test.
service: zha_toolkit.execute
data:
command: znp_nvram_backup
# Optional command_data, string added to the basename.
# With this example the backup is written to `nwk_backup_20220105.json`
command_data: _20220105
Will restore ZNP NVRAM data from local/nvram_backup.json
where local
is
a directory in the zha_toolkit
directory.
Note: currently under test.
For safety, a backup is made of the current network before restoring
local/nvram_backup.json
. The name of that backup is according to the
format local/nvram_backup_YYmmDD_HHMMSS.json
.
service: zha_toolkit.znp_nvram_restore
Will reset ZNP NVRAM data from local/nvram_backup.json
where local
is a
directory in the zha_toolkit
directory.
Note: currently under test.
For safety, a backup is made of the current network before restoring
local/nvram_backup.json
. The name of that backup is according to the
format local/nvram_backup_YYmmDD_HHMMSS.json
.
service: zha_toolkit.znp_nvram_reset
Used to transfer to another ZNP key later, backup or simply get network key and other info.
The output is written to the customization directory as
local/nwk_backup.json
when command_data
is empty or not provided. When
command_data
is provided, it is added just after "nwk_backup".
You can use the blueprint to setup daily backup: .
The name of that backup is according to the format
nwk_backup{command_data}.json
.
service: zha_toolkit.znp_backup
data:
# Optional command_data, string added to the basename.
# With this example the backup is written to `nwk_backup_20220105.json`
command_data: _20220105
Will restore network data from local/nwk_backup.json
where local
is a
directory in the zha_toolkit
directory.
Note: currently under test.
For safety, a backup is made of the current network before restoring
local/nwk_backup.json
. The name of that backup is according to the format
local/nwk_backup_YYmmDD_HHMMSS.json
.
A typical use for this is when you migrate from one key to another.
The procedure should be:
- Backup using the
znp_backup
command in thezha_toolkit
service. Verify that thenwk_backup.json
file is generated in thelocal
directory. -
- Remove the original Coordinator from your system (e.g., remove the USB key, ...).
- Insert the new Coordinator.
- Only when migrating to a Coordinator with different port/serial
path/socket.
Remove/Disable the ZHA Integration from Home Assistant.
The alternative is to modify HA’s config file directly to update the current integration’s serial path and baudrate - Copy the zigbee.db file (for backup).
Moving/renaming it should not be needed. If you Move or Rename thezigbee.db
the Entity name are lost after the restore (which impacts your automations, UI, etc).
-
- Restart Home Assistant.
- Enable/Add the ZHA Integration to Home Assistant (needed if you disabled or removed the ZHA integration in step 2.iii.)
- Restore using the
znp_restore
command.
(If you used a custom file name for the backup then make sure you copy it tonwk_backup.json
). - Check the logs (currently the
pre_shutdown
call failed for the first successful test, but that is not critical). - Restart HA
- Check that everything is ok.
NOTES :
- Devices may take a while to rejoin the network as the Zigbee specification requires them to "back-off" in case of communication problems.
- You may speed up the process by power cycling devices.
- Devices may not be instantly responsive because the zigbee mesh needs to
be recreated (try the
zdo_scan_now
command to speed that up).
(See the Home Assistant Community Forum for a success story.)
service: zha_toolkit.execute
data:
command: znp_restore
# Optional:
# command_data = Counter_increment (for tx).
# defaults to 2500
command_data: 2500
The backup service starts a backup of the coordinator by calling upon
znp_backup
or ezsp_backup
.
It provides a radio independent service for backups.
service: zha_toolkit.backup
data:
# Optional command_data, string added to the basename.
# With this example the backup is written to `nwk_backup_20220105.json`
command_data: _20220105
Sets the time and DST configuration for a Time Cluster from HA's current time and default timezone.
The TimeStatus attribute is not set. You likely need to set it to 2 (synchronized).
Before and after writing, the attributes are read from the cluster and available in the event data, unless options disable these reads.
service: zha_toolkit.misc_settime
data:
ieee: 5c:02:72:ff:fe:92:c2:5d
# The endpoint is optional - by default the endpoint containing the Time Cluster
endpoint: 11
# You can set the next events to use as a trigger.
# The event data has the result of the command (currently attr_read, attr_write)
event_success: my_read_success_trigger_event
event_fail: my_read_fail_trigger_event
event_done: my_read_done_trigger_event
# Settings for attr_write
# Read attribute before writing it (defaults to True)
read_before_write: true
# Read attribute after writing it (defaults to True)
read_after_write: true
ota_notify
helps you update the firmware of a zigbee device without
restarting Home Assistant. It can use the already available firmware
images, or download them using
Koenkk/zigbee-OTA's list and
resources. It also notifies the device that it should issue a request to be
updated. That launches the update process.
OTA
is the acronym for "Over the Air" and we implicitly add "update" or
"upgrade".
You must have configured the otau_directory. This is where ZHA/zigpy looks for firmware images, and where downloaded firmware images will be placed.
ota_notify
will indicate to the device that an update is available, which
will trigger the device to request this update from the coordinator (in
this case zigpy).
Prior to notifying the device, ota_notify
will request all image
providers to update the list of available images. By default, this is only
done on startup, so when you add a new image to your local directory or
when a new update is available from a third party, you'd have to restart
HA. But with ota_notify
no restart is required.
For details on how to setup your images sources, check the zigpy wiki section. Also read about the possibilities to enable logging, and that some devices require to be re-associated.
To trigger the OTA update, use ota_notify
instead. The debug log is
useful to check the update progress or indication that no update is
available.
When the update starts, be patient: it can take a while.
service: zha_toolkit.ota_notify
data:
# Reference of the device that should be notified about an update.
# Using one of the entity/sensor names is so much easier !
ieee: sensor.lixee_zlinky_tic_00000000_electrical_measurement
# Optional, when true download images from info at https://github.com/Koenkk/zigbee-OTA
download: true
# Optional, directory to write OTA files to (default: same as ZHA configuration)
path: /config/zb_ota
Get Device information as event data or in a CSV file.
You can select the data fields in the CSV and the event data through the command_data parameter. If you do not provide a list, a default list is used for the CSV file, and all available data is provided in the devices field of the event data.
You also get this data in the 'devices' field of the generated events which allows you to get information about endpoints and services as well.
service: zha_toolkit.zha_devices
data:
# Optional: Device to report on, by default all devices are in the report
ieee: sensor.my_zha_sensor
# Optional list of fields to write to the CSV, all non-list fields by default.
command_data: [name, ieee, rssi, lqi]
# Optional, field the list is sorted by (example: sort by signal strength)
csvlabel: rssi
csvout: ../www/devices.csv
event_done: zha_devices
The above should write the CSV to the www directory, so it's available as 'INSTANCEURL/local/devices.csv' and you could add a button to your UI for downloading:
type: button
name: Devices CSV File
tap_action:
action: url
url_path: /local/devices.csv
See script_use_zha_devices.yaml to see how you can loop over the device list provided in the event data.
See script_use_zha_devices_response.yaml shows how a new method available since Home Assistant 2023.7 that allows us to avoid the complexity of event_data by using the response from the zha-toolkit service.
The services may have evolved after an update of the code and calling
register_services
will reload the services.yaml
file defining the
options available in the UI interface, as well as internal structures that
define the validation rules for the parameters.
Most of the time this operation will be done automatically (when upgrading through HACS), but during development no version or file changes may be detected, and a manual update may be due for testing or accessing updated interfaces.
service: zha_toolkit.register_services
Set/update any Home Assistant state.
service: zha_toolkit.ha_set_state
data:
fail_exception: true
state_id: sensor.mysensor
attr_val: 10
# optional Parameters
state_attr: some_attribute
allow_create: true
# When defined, the read attribute is converted using this template string
# before writing it to the state.
# Note that no curly braces should be used here!
state_value_template: value + 2
csvout: set_state.csv
csvlabel: Example reason
state_value_template
is a template(-like) expression that is interpreted
where attr_val
is available as value
and attr_value
. This a template
expression without the curly parentheses ({{ }}
). If the curly
parentheses are used, the expression would be expanded before the zha
service code is entered which is incompatible with this functionality.
This is not strictly a zha
specific tool, but useful in some scripting
situations.
You can add your own Python commands in local/user.py
. Your file is
reloaded on each call and will survive updates because it's inside the
local
directory.
Example of user_test
that has the expected method signature, but just
prints that it's executed:
import logging
LOGGER = logging.getLogger(__name__)
async def user_test(app, listener, ieee, cmd, data, service, params, event_data):
LOGGER.debug(f"User test called")
The service call to execute it looks like this:
service: zha_toolkit.execute
data:
command: user_test
You're free to reuse the parameters already available for the other
commands. If you add your own, you'll need to parse them yourself from
service.data
. You can check out utils.py/extractParams
to look for
ideas, and you can examine the other methods to see how you can use ZHA.
This is a powerful tool to develop your own custom tool, and propose it for
inclusion in the zha-toolkit
when it's ready and of potential use to
others.
Shame on Tuya to be a member of the Zigbee Alliance and deviate from the Zigbee Specifications.
These commands help fix some of that.
This was labelled the standard tuya "magic spell" as it makes most Tuya devices work normally.
currently only the "read" part is implemented - if needed a super_magic_spell can be added to also execute the write procedure.
It has to be done using the "execute" command it's not implemented as a searchable service.
service: zha_toolkit.execute
data:
command: tuya_magic
ieee: light.tz3000_dbou1ap4_ts0505a_level_light_color_on_off
This project was forked from Adminiguaga/zha_custom where the "hard tricks" for providing services and accessing ZHA functions were implemented/demonstrated. The original codeowners were "dmulcahey" and "Adminiuga".
The znp and ezsp backup core code is work originally created by @puddly
either available in the official zigpy/zigpy_znp
repository or the
pudly/bellows
fork.
The initial purpose of this fork was mainly to add custom attribute writes, custom reporting and more binding possibilities.
The structure was then updated to be compliant with HACS integration so that the component can be easily added to a Home Assistant setup.
I set the License the same as Home Assistant that has the ZHA component.
The original zha_custom
repository does not mention a license.
#See Contributing.md