Cleode.fr
Cleode.fr
Easy and Wireless Home Automation

ZCL Library 4.3 Java

Table of contents

  1. ZCL Library overview
  2. Starting communication with an UBee
  3. Subscribe to receive node events
  4. Sending a Read attributes command
  5. Sending a Write attributes command
  6. Sending a Discover attributes command
  7. Sending a Read reporting configuration command
  8. Sending a Configure reporting command
  9. Sending a Cluster Specific command
  10. Sending a ZCL Frame command
  11. Subscribe to receive notification events
  12. Create a Binding to receive notifications
  13. Create a Binding to actuate a device
  14. Set the path of the license file
  15. Set the license key by programming

1. ZCL Library overview

The ZCL library API allows facilitating the development of application which interfaces with the Cleode UBee™ dongle.
It includes a JAR ("zcl-connector.jar") and a C++ dynamic library (".dll" for Windows, ".so" for Linux or ".dynlib" for MacOSX).

This API provides services for the following functionalities:

  • Management and configuration of UBee.
  • Reception of association events, update of Zigbee nodes.
  • Reading of present Zigbee nodes in the network.
  • Emission and reception of commands ZCL.
  • Subscription to receive notifications of Zigbee nodes.
  • Creation and deletion of bindings between Zigbee nodes.
  • Authorization of Zigbee nodes to be gone into the network.

2. Starting communication with an UBee

The detection of UBee on any USB port is automatic, except in the case where the port name is specified.
The return value of startUbee() service indicates if the UBee is detected or not.

When UBee is detected, an ubeeStarted event is notified and you can check the license validity.
If UBee is unplugged, an ubeeReleased event is notified. And thereafter every 5 seconds, the API verify if UBee is replugged.
When you call the stopUbee() service, an ubeeStopped event is notified.
If UBee is awaiting reprogramming, an ubeeWaitingBoot event is notified.

public class Application implements AliveCallback
{	
	ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();

	public Application() throws IOException
	{
		// Start the communication with UBee
		boolean result = zcs.startUbee(this);

		//Display if the UBee is detected or not
		System.out.printf("UBee is %s \n", (result ? "detected" : "not detected"));

		System.out.println("Press any key to Quit the application");
		// Wait for the pressing any key
		System.in.read();
		
		// Stop the communication with UBee
		zcs.stopUbee();
	}

	public void ubeeStarted() {
		System.out.println("====> UBee Started notification");
		System.out.printf("UBee License validity for %s : %s\n",
			zcs.getCoordinator(), Boolean.toString(zcs.isLicenseValid()));
	}

	@Override
	public void ubeeReleased() {
		System.out.println("====> UBee Released notification");
	}

	@Override
	public void ubeeStopped() {
		System.out.println("====> UBee Stopped notification");
	}

	@Override
	public void ubeeWaitingBoot() {
		System.out.println("====> UBee Waiting Boot notification");
	}
}
		

Starting UBee on a specific port name

// Windows
zcs.startUbee(this, "COM22");

// Linux
zcs.startUbee(this, "/dev/ttyUSB1");
		

3. Subscribe to receive node events

This subscription allows to receive the events such as associating, updating and deleting of the Zigbee nodes.
It is strongly recommended to make a clone of the node if you want to store the node instance and to modify it.

After the UBee is starting, a discovery of Zigbee node is done and at least 3 events are notified for each node.
  - When the structure of the node has been read : Created / Discover.
  - When the model identifier of the node has been read : Updated / ModelID.
  - When the bindings of the node has been read : Updated / Bindings.

public class Application implements AliveCallback, ZigbeeConnectorNodeEvents
{	
	ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();

	public Application() throws IOException
	{
		// Subcribe to receive node events
		zcs.subscribe(this);
		
		// Start the communication with UBee
		zcs.startUbee(this);

		System.out.println("Press any key to Quit the application");
		// Wait for the pressing any key
		System.in.read();
		
		// Stop the communication with UBee
		zcs.stopUbee();
	}

	...

	@Override
	public void notifyNodeCreated(final Node node, final Reason reason) {
		System.out.printf("====> Node Created %s : %s\n", node, reason);
	}

	@Override
	public void notifyNodeUpdated(final Node node, final Reason reason) {
		System.out.printf("====> Node Updated %s : %s\n", node, reason);
	}

	@Override
	public void notifyNodeDeleted(final Node node, final Reason reason) {
		System.out.printf("====> Node Deleted %s : %s\n", node, reason);
	}
}			
        

4. Sending a Read attributes command

The Read Attributes command is used to read the attribute values of a cluster from the device.
The response of this request is received on the callback passed as parameter to the service.

A timeout expires if the device is not responding to the request.
The timeout period for router is 15 seconds by default, and 30 seconds for end-devices.

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
ZigbeeAddressResolvingServices zars = ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();

ResponseCallBack<ReadAttributeResponse[]> reader = 
								new ResponseCallBack<ReadAttributeResponse[]>() {
	@Override
	public void responseReceived(
		int nwkAddr, int epNumber, int clusterId, int seqNumber,
		final ReadAttributeResponse[] responses)
	{
		if (responses != null) {
			// Responses received
			for (ReadAttributeResponse attr : responses) {
				if (attr.getStatus() == Status.SUCCESS) {
					// Successful reading of this attribute
				} else {
					// Unsuccessful reading of this attribute
				}
			}
		} else {
			// Timeout expired !
		}
	}
};
	
// Search a device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node node = zars.searchNode(ieeeAddr);

// Read the fourth first attributes of the Basic cluster
int seqNumber = zcs.sendReadAttributesCommand(reader,
		node.getNwkAddr(),
		node.getEpByInCluster(ClusterName.Basic_Attributes.getId()),
		ClusterName.Basic_Attributes.getId(),
		new Attribute[] {
			Attribute.factory.newInstance(AttrConstants.ZCL_VERSION_ATTR),
			Attribute.factory.newInstance(AttrConstants.APP_VERSION_ATTR),
			Attribute.factory.newInstance(AttrConstants.STACK_VERSION_ATTR),
			Attribute.factory.newInstance(AttrConstants.HW_VERSION_ATTR)
		});
        

5. Sending a Write attributes command

The Write Attributes command is used to write/modify the attribute values of a cluster into the device.
The response of this request is received on the callback passed as parameter to the service.

A timeout expires if the device is not responding to the request.
The timeout period for router is 15 seconds by default, and 30 seconds for end-devices.

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
ZigbeeAddressResolvingServices zars = ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();

ResponseCallBack<WriteAttributeResponse[]> writer = 
								new ResponseCallBack<WriteAttributeResponse[]>() {
	@Override
	public void responseReceived(
		int nwkAddr, int epNumber, int clusterId, int seqNumber,
		final WriteAttributeResponse[] responses)
	{
		if (responses != null) {
			// Responses received
			for (WriteAttributeResponse attr : responses) {
				if (attr.getStatus() == Status.SUCCESS) {
					// Successful writing of this attribute
				} else {
					// Unsuccessful writing of this attribute
				}
			}
		} else {
			// Timeout expired !
		}
	}
};
		
// Search a device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node node = zars.searchNode(ieeeAddr);

// Fill the attribute to be written
WriteAttribute attr = WriteAttribute.factory.newInstance();
attr.setIdentifier(AttrConstants.LOCATION_DESCRIPTION_ATTR);
attr.setDataType(DataType.CHARACTER_STRING);
attr.setDataAsString("Office");
		
// Write the location description attribute of this device
int seqNumber = zcs.sendWriteAttributesCommand(writer,
		node.getNwkAddr(),
		node.getEpByInCluster(ClusterName.Basic_Attributes.getId()),
		ClusterName.Basic_Attributes.getId(),
		new WriteAttribute[] { attr });	
		

6. Sending a Discover attributes command

The Discover Attributes command is used to know the attributes of a cluster implemented by the device.

A timeout expires if the device is not responding to the request.
The timeout period for router is 15 seconds by default, and 30 seconds for end-devices.

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
ZigbeeAddressResolvingServices zars = ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();

ResponseCallBack<DiscoveredAttribute[]> discover = new ResponseCallBack<DiscoveredAttribute[]>() {
	@Override
	public void responseReceived(
		int nwkAddr, int epNumber, int clusterId, int seqNumber,
		final DiscoveredAttribute[] responses)
	{
		if (responses != null) {
			// Responses received
			for (DiscoveredAttribute attr : responses) {
				// Display the list of discovered attributes
				System.out.printf("AttrId(0x%04x) - DataType(%s)\n",
						attr.getIdentifier(), attr.getDataType());
			}
		} else {
			// Timeout expired !
		}
	}
};
		
// Search a device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node node = zars.searchNode(ieeeAddr);

// Send a Discover Attributes command to discover the attributes
// of Basic cluster implemented by this device
int seqNumber = zcs.sendDiscoverAttributesCommand(discover, node.getNwkAddr(),
		node.getEpByInCluster(ClusterName.Basic_Attributes.getId()),
		ClusterName.Basic_Attributes.getId(), AttrConstants.ZCL_VERSION_ATTR, 20, true);		
		

7. Sending a Read reporting configuration command

The Read Reporting Configuration command is used to read the configuration details of the reporting mechanism for one or more of the attributes of a cluster.

A timeout expires if the device is not responding to the request.
The timeout period for router is 15 seconds by default, and 30 seconds for end-devices.

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
ZigbeeAddressResolvingServices zars = ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();

ResponseCallBack<ReadReportingConfigAttributeResponse[]> reader =
		new ResponseCallBack<ReadReportingConfigAttributeResponse[]>()
{
	@Override
	public void responseReceived(
			int nwkAddr, int epNumber, int clusterId, int seqNumber,
			final ReadReportingConfigAttributeResponse[] responses)
	{
		if (responses != null) {
			// Responses received
			for (ReadReportingConfigAttributeResponse attr : responses) {
				if (attr.getStatus() == Status.SUCCESS) {
					// Successful reading of the reporting configuration of this attribute
				} else {
					// Unsuccessful reading of the reporting configuration of this attribute
				}
			}
		} else {
			// Timeout expired !
		}
	}
};
			
// Search a device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node zrc = zars.searchNode(ieeeAddr);

// Fill the attribute for which we want to read the reporting configuration
ReadReportingConfigAttribute attr =
		ReadReportingConfigAttribute.factory.newInstance(ReportingDirection.ReportsReceived);
attr.setIdentifier(AttrConstants.MEASURED_VALUE_ATTR);
		
// Read the reporting configuration 
int seqNumber = zcs.sendReadReportingConfigurationCommand(reader, zrc.getNwkAddr(),
		zrc.getEpByInCluster(ClusterName.Temperature_Measurement.getId()),
		ClusterName.Temperature_Measurement.getId(),
		new ReadReportingConfigAttribute[] { attr });
		

8. Sending a Configure reporting command

The Configure Reporting command is used to configure the reporting mechanism for one or more of the attributes of a cluster.

A timeout expires if the device is not responding to the request.
The timeout period for router is 15 seconds by default, and 30 seconds for end-devices.

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
ZigbeeAddressResolvingServices zars = ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();

ResponseCallBack<ReportingConfigurationAttributeResponse[]> writer =
		new ResponseCallBack<ReportingConfigurationAttributeResponse[]>()
{
	@Override
	public void responseReceived(
			int nwkAddr, int epNumber, int clusterId, int seqNumber,
			final ReportingConfigurationAttributeResponse[] responses)
	{
		if (responses != null) {
			// Responses received
			for (ReportingConfigurationAttributeResponse attr : responses) {
				if (attr.getStatus() == Status.SUCCESS) {
					// Successful writing of the reporting configuration of this attribute
				} else {
					// Unsuccessful writing of the reporting configuration of this attribute
				}
			}
		} else {
			// Timeout expired !
		}
	}
};
		
// Search a device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node zrc = zars.searchNode(ieeeAddr);

// Fill the attribute for which we want to write the reporting configuration
ReportingConfigurationAttribute attr =
		ReportingConfigurationAttribute.factory.newInstance(ReportingDirection.ReportsReceived);
attr.setIdentifier(AttrConstants.MEASURED_VALUE_ATTR);
attr.setDataType(DataType.SIGNED_16BITS);
attr.setMinReportingInterval((short) 300);
attr.setMaxReportingInterval((short) 3600);
attr.setReportableChange(100, DataType.SIGNED_16BITS);
		
// Write the reporting configuration 
int seqNumber = zcs.sendConfigureReportingCommand(writer, zrc.getNwkAddr(),
		zrc.getEpByInCluster(ClusterName.Temperature_Measurement.getId()),
		ClusterName.Temperature_Measurement.getId(),
		new ReportingConfigurationAttribute[] { attr });	}
		

9. Sending a Cluster Specific command

The Cluster Specific command is used to send a specific command to a cluster.
Look in the Zigbee specfication to know which cluster supports Cluster Specific command.

Examples:

  • On, Off or Toggle commands for On/Off Cluster
  • Identify or Identify Query commands for Identify Cluster
  • Lock Door or Unlock Door commands for Door Lock Cluster
  • etc.

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
ZigbeeAddressResolvingServices zars = ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();

// Search a device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node zplug = zars.searchNode(ieeeAddr);

// Send a Toggle command to a Plug device
zcs.sendClusterSpecificCommand(zplug.getNwkAddr(),
		zplug.getEpByInCluster(ClusterName.On_Off.getId()),
    	ClusterName.On_Off.getId(), AttrConstants.TOGGLE_COMMAND, null);
		

10. Sending a ZCL Frame command

The ZCL frame command is the generic command that covers all other commands.

For example:
if you want to toggle the state of a ZPlug and verify that the ZPlug received the command and well decoded it,
you have to use this generic command .

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
ZigbeeAddressResolvingServices zars = ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();

ResponseCallBack<ZCLFrame> receiver = new ResponseCallBack<ZCLFrame>() {
	@Override
	public void responseReceived(
			int nwkAddr, int epNumber, int clusterId, int seqNumber,
			final ZCLFrame response)
	{
		if (response != null) {
			// ZCL Frame response received
			if (response.getPayload().isDefaultResponse()) {
				DefaultResponsePayload payload = response.getPayload().getDefaultResponsePayload();
				if (payload.getStatus() == Status.SUCCESS) {
					// Successful decoding of the Toggle command
				} else {
					// Unsuccessful decoding of the Toggle command
				}
			}
		} else {
			// Timeout expired !
		}
	}
};
		
// Search a device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node zplug = zars.searchNode(ieeeAddr);

// Fill the ZCL Frame request
ZCLFrame frame = ZCLFrame.factory.newZCLFrameInstance();
frame.getHeader().getFrameControl().setFrameType(FrameType.SpecificCluster);
frame.getHeader().getFrameControl().setDirection(Direction.ClientToServer);
frame.getHeader().getFrameControl().setManufacturerCode(ManufacturerCode.NotIncludeInZCLFrame);
frame.getHeader().getFrameControl().setResponseStatus(ResponseStatus.ResponseReturned);
frame.getHeader().setCmdIdentifierAsValue(AttrConstants.TOGGLE_COMMAND);
		
// Send a Toggle command to a ZPlug device and verify
// that the ZPlug received the command and well decoded it
zcs.sendZCLFrameCommand(receiver, zplug.getNwkAddr(),
		zplug.getEpByInCluster(ClusterName.On_Off.getId()),
		ClusterName.On_Off.getId(), frame);
		

11. Subscribe to receive notification events

The notification events is based on the Observer pattern (also known as Listener in Java).
You have to implement the ZigbeeConnectorEvents interface for consuming the events.
You have also to create a Binding in the device to UBee

The Report Attribute is a notification sent by a device in according to the Reporting Configuration of the attribute.

The Cluster Specific command can be regarded as a notification sent by a device for any Cluster such as IAS Zone, Alarm, etc.

// Listener instance which receives notifications
ZigbeeConnectorEvents receiver = new ZigbeeConnectorEvents() {
	@Override
	public void notifyReportAttribute(
			int nwkAddr, int epNumber, int clusterID, int seqNumber,
			final ReportAttribute[] attributes)
	{
	}
			
	@Override
	public void notifyClusterSpecificCommand(
			int nwkAddr, int epNumber, int clusterID, int seqNumber,
			byte command, ClusterSpecCmdAttribute[] attributes) {
	}
};
		

You can subscribe to receive notifications from all devices.

	
ZigbeeConnectorEventsSubscribe zces = ZigbeeConnector.singleton.getZigbeeConnectorEventsSubscribe();

// Subscribe to receive notification events from all devices
zces.subscribe(receiver, ZigbeeConnectorEventsSubscribe.allNodes);

...

// Unsubscribe to receive notification events from all devices
zces.unsubscribe(receiver, ZigbeeConnectorEventsSubscribe.allNodes);
		

Or you can subscribe to receive notifications from only one device.

ZigbeeConnectorEventsSubscribe zces = ZigbeeConnector.singleton.getZigbeeConnectorEventsSubscribe();
ZigbeeAddressResolvingServices zars = ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();
		
// Search a device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node node = zars.searchNode(ieeeAddr);

// Subscribe to receive notification events from only this device
zces.subscribe(receiver, node.getNwkAddr());

...

// Unsubscribe to receive notification events from only this device
zces.unsubscribe(receiver, node.getNwkAddr());
		

12. Create a Binding to receive notifications on UBee

For receiving notifications for attributes of a cluster from a device, you have to create a binding from this device to UBee.

final ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
final ZigbeeAddressResolvingServices zars =
						ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();
		
BindResponse response = new BindResponse() {
	@Override
	public void responseReceived(int nwkAddr, final BindCmd cmd, final Status status) {
		if (status == Status.SUCCESS) {
			Node node = zars.searchNode(nwkAddr);
			for (Binding binding : node.getBindings()) {
				// Display the bindings 
			}
		}
	}
};

// Search a ZRC device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node zrc = zars.searchNode(ieeeAddr);

// Search the UBee by its network address
Node ubee = zars.searchNode(ZigbeeAddressResolvingServices.COORDINATOR_NWK_ADDR);

// Create a binding to receive notification from ZRC on UBee 
zcs.sendBindRequest(response, zrc.getNwkAddr(),
		zrc.getIEEEAddress(),
		zrc.getEpByInCluster(ClusterName.Temperature_Measurement.getId()),
		ClusterName.Temperature_Measurement.getId(),
		ubee.getIEEEAddress(),
		ubee.getEpByOutCluster(ClusterName.Temperature_Measurement.getId()));
		

13. Create a Binding to actuate a device

For actuating a device by a remote control such as ZRC, ZSC or ZKey, you have to create a binding between this two devices.

final ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();
final ZigbeeAddressResolvingServices zars = 
									ZigbeeConnector.singleton.getZigbeeAddressResolvingServices();
		
BindResponse response = new BindResponse() {
	@Override
	public void responseReceived(int nwkAddr, final BindCmd cmd, final Status status) {
		if (status == Status.SUCCESS) {
			// Verify that the binding is added
			Node node = zars.searchNode(nwkAddr);
			for (Binding binding : node.getBindings()) {
				// Display the bindings 
			}
		}
	}
};

// Search a ZRC device by its IEEE address
IEEEAddress ieeeAddr = IEEEAddress.convertString("11-22-33-44-55-66-77-88");
Node zrc = zars.searchNode(ieeeAddr);

//Search a ZPlug device by its IEEE address
ieeeAddr = IEEEAddress.convertString("12-23-34-45-56-67-78-89");
Node zplug = zars.searchNode(ieeeAddr);

// Create a binding to control the ZPlug by the ZRC 
zcs.sendBindRequest(response, zrc.getNwkAddr(),
		zrc.getIEEEAddress(),
		zrc.getEpByOutCluster(ClusterName.On_Off.getId()),
		ClusterName.On_Off.getId(),
		zplug.getIEEEAddress(),
		zplug.getEpByInCluster(ClusterName.On_Off.getId()));
		

You can also control several devices belonging to the same group by creating a binding in the ZRC to a group ID.

int groupID = 0x0001;

// Create a binding to control the devices of the group 0x0001 by the ZRC 
zcs.sendBindRequest(response, zrc.getNwkAddr(), zrc.getIEEEAddress(),
		zrc.getEpByOutCluster(ClusterName.On_Off.getId()),
		ClusterName.On_Off.getId(), groupID);
		

14. Set the path of the license file

If you are unable or do not wish to put the license file in the same directory of your application, you can specify the directory for searching the license file.
The path initialization have to be done before the "startUbee() call.

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();

// Set the path of the license file
zcs.setLicensePath("/usr/local/myappli");

// Start the communication with UBee
zcs.startUbee(...); 
		

15. Set the license key by programming

If you are unable or do not wish to read the license key in the file, you can set the license key by programming.
The license initialization have to be done before the "startUbee() call.

ZigbeeConnectorServices zcs = ZigbeeConnector.singleton.getZigbeeConnectorServices();

// Set the license key by programming
zcs.setLicenseKey("AAAAABBBBBCCCCCDDDDDEEEEE");

// Start the communication with UBee
zcs.startUbee(...); 
		
remonter