How to Send and Handle ZigBee Protocol Passthrough Messages
Introduction
The GatewayAPI aims to abstract away the underlying protocol, i.e Zigbee, but there are instances where clients/users will want direct access to the underlying protocol. This document will describe how to send ZCL/ Unicast, Broadcast Messages, and Multicast Messages. This document will describe the message format of the request and response.
API
The ZigBeeDevice
- CompletableFuture<Property> readAttribute(int clusterId, int attributeId)
- CompletableFuture<Property> readAttribute(int clusterId, int attributeId, int manufacturerCode, boolean isServer)
- CompletableFuture<Property> writeAttribute(int clusterId, int attributeId, short dataType, byte[] value)
- CompletableFuture<Property> writeAttribute(int clusterId, int attributeId, short dataType, byte[] value, int manufacturerCode, boolean isServer)
- CompletableFuture<Property> sendZclCommand(short commandId, byte[] payload, int clusterId)
- CompletableFuture<Property> sendZclCommand(short commandId, byte[] payload, int clusterId)
- CompletableFuture<Property> sendZclCommand(short commandID, byte[] payload, int clusterId, int manufacturerCode, boolean isServer, boolean isClusterSpecific, boolean requiresResponse)
- CompletableFuture<Collection<Property>> sendZclMulticastCommand(int groupID, short commandID, byte[] payload, int clusterId, int manufacturerCode, boolean isServer, boolean isClusterSpecific, boolean requiresResponse)
- CompletableFuture<Collection<Property>> sendZclMulticastCommand(String broadcastAddress, short commandID, byte[] payload, int clusterId, int manufacturerCode, boolean isServer, boolean isClusterSpecific, boolean requiresResponse)
CompletableFuture
See The Official JavaDoc For CompletableFuture on basic usages
Property
Represents a Property of a device, or action. It contains the name, value, type, and various meta-data
Note:
Every property result will be of type: json
Read Attribute
ZigBeeDevice lightDevice = ... int clusterID = 0x0000 // Basic Cluster int attributeID = 0x0000 // Version Attribute CompletableFuture<Property> results = lightDevice.readAttribute(clusterID, attributeID) results.whenComplete((propertyResult, error) -> { if (error != null) { // handle error of computation } else { String jsonResults = propertyResult.getValue() ... // parse results ... } });
An Example JSON Results. (Notice it is a json array of one)
[ { "gatewayApiVersion":"2.0.13", "protocolName":"zigbee", "protocolVersion":3, "messageType":"zcl_response", "message":{ "sourceNode":"0xCD22", "sourceEndpoint":"0x01", "localEndpoint":"0x01", "clusterId":"0x0000", "encryption":"0x00", "frameControl":"0x18", "manufacturerCode":"0x0000", "transactionNum":"0x13", "commandId":"0x01", "payload":"0x0000002003" } } ]
Write Attribute
ZigBeeDevice device = ... int clusterID = 0x0000 // Basic Cluster int attributeID = 0x0075 // random attribute with read/write access control short dataType = 0x21 // UInt16 byte[] value = new byte[]{(btye)0x34, (byte)0x12}; // writing the value 0x1234, Little-Endian CompletableFuture<Property> results = device.writeAttribute(clusterID, attributeID, dataType, value) results.whenComplete((propertyResult, error) -> { if (error != null) { // handle error of computation } else { String jsonResults = propertyResult.getValue() ... // parse results ... } });
Response
[ { "gatewayApiVersion":"2.0.14-SNAPSHOT", "protocolName":"zigbee", "protocolVersion":3, "messageType":"zcl_response", "message":{ "sourceNode":"0x4544", "sourceEndpoint":"0x01", "localEndpoint":"0x01", "clusterId":"0x0000", "encryption":"0x00", "frameControl":"0x18", "manufacturerCode":"0x0000", "transactionNum":"0x11", "commandId":"0x04", "payload":"0x00" //success } } ]
Unicast
The ZCL Unicast method is a prime candidate for sending one command to a targeted node/end device.
Example of Sending the Add Group Command
Here is an example of sending the add group command (0x00) to a node with the groups server cluster (0x0004)
ZigbeeDevice device = ... short commandId = 0x02; byte[] payload = new byte[]{(byte)0x00, (byte)0x01}; // add group id of 0x0100 int clusterId = 0x0004; // group cluster int manufacturerCode = 0; boolean isServer = true; // client to server (end device) boolean isClusterSpecific = true; boolean requiresResponse = true; CompletableFuture<Property> results = device.sendZclCommand(commandId, payload, clusterId, manufacturerCode, isServer, isClusterSpecific, requiresResponse) results.whenComplete((propertyResult, error) -> { if (error != null) { // handle error of computation } else { String jsonResults = propertyResult.getValue() ... // parse results ... } });
The Add Group Command has a response:
[ { "gatewayApiVersion":"2.0.14-SNAPSHOT", "protocolName":"zigbee", "protocolVersion":3, "messageType":"zcl_response", "message":{ "sourceNode":"0x4544", "sourceEndpoint":"0x01", "localEndpoint":"0x01", "clusterId":"0x0004", "encryption":"0x00", "frameControl":"0x19", "manufacturerCode":"0x0000", "transactionNum":"0x21", "commandId":"0x00", // Add group response commandid "payload":"0x000001" // status - 0x00 - group id - 0x0100 } } ]
If requireResponse = false, then the method will return a default response
[ { "gatewayApiVersion":"2.0.13", "protocolName":"zigbee", "protocolVersion":3, "messageType":"default_response", "message":{ "status":"0x00" } } ]
Example of Sending Toggle Command (0x02)
Here is an example of sending a toggle command (0x02) to the end device containing the On/Off Server Cluster (0x0006).
// example, toggle command for on/off ZigBeeDevice device = ... short commandId = 0x02; byte[] payload = new byte[0]; // no payload for toggle int clusterId = 0x0006; // on off cluster int manufacturerCode = 0; boolean isServer = true; // client to server (end device) boolean isClusterSpecific = true; boolean requiresResponse = false; CompletableFuture<Property> results = device.sendZclCommand(commandId, payload, clusterId, manufacturerCode, isServer, isClusterSpecific, requiresResponse) results.whenComplete((propertyResult, error) -> { if (error != null) { // handle error of computation } else { String jsonResults = propertyResult.getValue() ... // parse results ... } });
The response if requiresResponse = true or false, this command will always yield a default response as there is no actual response for the toggle command as per spec
[ { "gatewayApiVersion":"2.0.13", "protocolName":"zigbee", "protocolVersion":3, "messageType":"default_response", "message":{ "status":"0x00" } } ]
The toggle command does not generate a response therefore, even if we set requiresResponse = true, it will still generate a default response.
Broadcast
Broadcast Addresses
- 0xFF - All Addresses
- 0xFD - Non - sleepy devices
- 0xFC - all routers and coordinator
Any other value will default to 0xFD
Example of Sending the Add Group Command
This example will be broadcasting to two devices
// example add group command ZigBeeDevice device = ... String broadcastAddress = "0xFD"; short commandId = 0x02; byte[] payload = new byte[0]; // no payload for toggle int clusterId = 0x0006; // on off cluster int manufacturerCode = 0; boolean isServer = true; // client to server (end device) boolean isClusterSpecific = true; boolean requiresResponse = true; CompletableFuture<Collection<Property>> results = device.sendZclBroadcastCommand(broadcastAddresscommandId, payload, clusterId, manufacturerCode, isServer, isClusterSpecific, requiresResponse) results.whenComplete((properties, error) -> { if (error != null) { // handle error of computation } else { for (Property property : properties) { String json = property.getValue(); // parse json and handle result } } });
Each Property will have the following JSON results.
// 1st property in properties collection { "gatewayApiVersion":"2.0.13", "protocolName":"zigbee", "protocolVersion":3, "messageType":"zcl_response", "message":{ "sourceNode":"0x1FAD", "sourceEndpoint":"0x01", "localEndpoint":"0x01", "clusterId":"0x0004", "encryption":"0x00", "frameControl":"0x19", "manufacturerCode":"0x0000", "transactionNum":"0x3E", "commandId":"0x00", "payload":"0x000100" } } // 2nd property in properties collection { "gatewayApiVersion":"2.0.13", "protocolName":"zigbee", "protocolVersion":3, "messageType":"zcl_response", "message":{ "sourceNode":"0x4544", "sourceEndpoint":"0x01", "localEndpoint":"0x01", "clusterId":"0x0004", "encryption":"0x00", "frameControl":"0x19", "manufacturerCode":"0x0000", "transactionNum":"0x3E", "commandId":"0x00", "payload":"0x000100" } }
if requiresResponse = false:
{ "gatewayApiVersion":"2.0.14-SNAPSHOT", "protocolName":"zigbee", "protocolVersion":3, "messageType":"default_response", "message":{ "status":"0x00" } }
A single JSON response to indicate the broadcast command was sent (status code of success: 0x00)
Example of Sending Toggle Command (0x02)
// example, toggle command for on/off ZigBeeDevice device = ... String broadcastAddress = "0xFD"; short commandId = 0x02; byte[] payload = new byte[0]; // no payload for toggle int clusterId = 0x0006; // on off cluster int manufacturerCode = 0; boolean isServer = true; // client to server (end device) boolean isClusterSpecific = true; boolean requiresResponse = false; CompletableFuture<Collection<Property>> results = device.sendZclBroadcastCommand(broadcastAddresscommandId, payload, clusterId, manufacturerCode, isServer, isClusterSpecific, requiresResponse) results.whenComplete((properties, error) -> { if (error != null) { // handle error of computation } else { for (Property property : properties) { String json = property.getValue(); // parse json and handle result } } });
The response will be a single property in the collection with a default response indicating the message has been sent due to the fact the toggle command has no response
// property value: { "gatewayApiVersion":"2.0.14-SNAPSHOT", "protocolName":"zigbee", "protocolVersion":3, "messageType":"default_response", "message":{ "status":"0x00" } }
Multicast
Sending The Toggle Command
// example add group command ZigBeeDevice device = ... int groupID = 1; short commandId = 0x02; byte[] payload = new byte[0]; // no payload for toggle int clusterId = 0x0006; // on off cluster int manufacturerCode = 0; boolean isServer = true; // client to server (end device) boolean isClusterSpecific = true; boolean requiresResponse = true; CompletableFuture<Collection<Property>> results = device.sendZclMulticastCommand(groupID, payload, clusterId, manufacturerCode, isServer, isClusterSpecific, requiresResponse) results.whenComplete((properties, error) -> { if (error != null) { // handle error of computation } else { for (Property property : properties) { String json = property.getValue(); // parse json and handle result } } });
The response from being broadcasted to 2 nodes/end-devices:
// 1st property in properties collection { "gatewayApiVersion":"2.0.13", "protocolName":"zigbee", "protocolVersion":3, "messageType":"zcl_response", "message":{ "sourceNode":"0x1FAD", "sourceEndpoint":"0x01", "localEndpoint":"0x01", "clusterId":"0x0004", "encryption":"0x00", "frameControl":"0x19", "manufacturerCode":"0x0000", "transactionNum":"0x3E", "commandId":"0x00", "payload":"0x000100" } } // 2nd property in properties collection { "gatewayApiVersion":"2.0.13", "protocolName":"zigbee", "protocolVersion":3, "messageType":"zcl_response", "message":{ "sourceNode":"0x4544", "sourceEndpoint":"0x01", "localEndpoint":"0x01", "clusterId":"0x0004", "encryption":"0x00", "frameControl":"0x19", "manufacturerCode":"0x0000", "transactionNum":"0x3E", "commandId":"0x00", "payload":"0x000100" } }
Failed Requests
There are differing failures when it comes to ZCL:
- Failed to send to target node → Default Response
- Failure in target node processing command → ZCL Response (Default Response, if requireResponse = false)
Default Response
If there is an error with the request, you will get a default response with a status code. This means the request failed to be sent in some way. This usually means that the message failed to be sent to the device, or if requiresResponse = false, a default response will be sent to the user/client instead of a ZCL Response
e.g Default Response, Status Code 0x01 (Failure).
Note: Status Code of 0x00 mean success
[ { "gatewayApiVersion":"2.0.13", "protocolName":"zigbee", "protocolVersion":3, "messageType":"default_response", "message":{ "status":"0x01" } } ]
ZCL Response
For certain situations, you could receive a ZCL Response with a failure status. This is an error code from the target device. You must inspect the frame control, command id, and payload to decipher the meaning of this response. Please see the ZCL Specification for more details for that command.
[ { "gatewayApiVersion":"2.0.14", "protocolName":"zigbee", "protocolVersion":3, "messageType":"zcl_response", "message":{ "sourceNode":"0x4544", "sourceEndpoint":"0x01", "localEndpoint":"0x01", "clusterId":"0x0000", "encryption":"0x00", "frameControl":"0x18", "manufacturerCode":"0x0000", "transactionNum":"0x12", "commandId":"0x04", // write command "payload":"0x869900" // contains status code 0x86 - unsupported attribute }- } ]
Status Codes and Data Types
They can be found in the Zigbee Specification References
Legal Notices
Copyright © 2020 MMB Networks, Inc. All rights reserved.
Confidential materials prepared and delivered by MMB Networks for receipt and review only by any partner subject to a valid and enforceable MMB Networks confidentiality agreement. Any receipt, review, or misuse of any of the content exchanged hereunder by any party not a party to this confidential exchange shall be subject to any and all rights available under the law. All rights, title and interest to the materials shall remain with MMB Networks.
Any suggestions provided to MMB Networks with respect to MMB Networks' products or services shall be collectively deemed “Feedback.” You, on behalf of yourself, or if you are providing Feedback on behalf of your employer or another entity, represent and warrant that you have full legal authority to bind such entity to these terms, agree to grant and hereby grant to MMB Networks a nonexclusive, perpetual, irrevocable, royalty free, worldwide license to use and otherwise exploit such Feedback within any MMB Networks products and services.