Basic Initialization

Overview

This page describes how to develop against the Gateway API. It goes over the basics of initializing a GatewayClient object, and interacting with Device references.

Initializing GatewayClient

Initialize a GatewayClient by passing in a ConnectionInfo object, which contains details about what the connection type is (i.e. a zigbee uart port) and a string describing where to connect to it.

In the following example, we also pass an instance of a class that implements the DeviceEventHandler interface, that will handle device events by discovering them and printing out their properties.

Initializing GatewayClient
// Example of creating a GatewayClient object with an instance of the SampleDeviceEventHandler class
final GatewayClient gateway = new GatewayClient( new ConnectionInfo(ConnectionType.ZIGBEE_UART, "/dev/ttyS1"), new SampleDeviceEventHandler() );

The following is an example of a class that implements the DeviceEventHandler interface. It contains one method that must be implemented to handle when devices are added to, or removed from, the network.

SampleDeviceEventHandler
public class SampleDeviceEventHandler implements DeviceEventHandler {
    
    private final SamplePropertyUpdateHandler propertyHandler;
    
    public SampleDeviceEventHandler() {
        this.propertyHandler = new SamplePropertyUpdateHandler();
    }

    @Override
    public void onDeviceEvent(DeviceEvent event) {
        // This method gets called when a device is added to, or removed from, the network.
        // By default, client callbacks are called from a single thread managed by GAPI.
        // Therefore, avoid writing blocking code in this method, or other client callbacks
        // (such as PropertyUpdateHandlers, or ProtocolHandlers) may be blocked.
        
        final DeviceEventStatus eventStatus = event.getStatus();
        final Device device = event.getDevice();
        final String deviceId = device.getID();
        final String deviceType = device.getDeviceType().getType();
        
        System.out.println("Device Event: " + eventStatus);
        System.out.println("Device Id: " + deviceId);
        System.out.println("Device Type: " + deviceType);
        
        switch (eventStatus) {
        
        case DEVICE_ADDED:
            // Register a property handler, to be notified when a Property on the Device changes.
            device.addPropertyUpdateHandler(propertyHandler);
            
            // Automatically discover the device. This is a non-blocking call.
			// Note: this can be a long-running network operation, and in a production application,
			// you would typically replace this with individual calls to "getProperty(String)"
			// for only those properties that you are interested in, given the deviceType of the Device.
            CompletableFuture<Collection<Property>> properties = device.discoverAllProperties();
            
            // Do some extra (asynchronous) handling on the properties, once they've been discovered.
            properties.thenAccept( collection -> {
                // This code will be called from a thread managed by GAPI.
                // We should avoid writing blocking code in this section.
                for (Property p : collection) {
                    System.out.println("Discovered: " + p.toString());
                }
            });

            break;

        case DEVICE_REMOVED:
            device.removePropertyUpdateHandler(propertyHandler);
            break;

        default:
            break;
        }
    }
}


Once constructed, the GatewayClient will automatically connect to the device, configure it as a Combined Interface, and form a network. If it can't connect to or configure the device, it will throw a GatewayConnectionException.

It will call the SampleDeviceEventHandler.onDeviceEvent(DeviceEvent event) method whenever a Device is detected on the network (i.e. added to the network), or whenever a device leaves the network.

Scanning for Devices

To join a device to the network, you first have to open the permit join window on the GatewayClient.

The following example shows how to call "scanForDevices" on all known connections for a duration of 30 seconds. It also implements a callback displaying the status of the command. The expected result should be "NETWORK_OPEN".

Scanning for Devices (i.e. opening the permit join window)
for (ConnectionInfo c: gateway.getConnectionInfo()) {
    gateway.scanForDevices(c, 30).thenAccept( status -> {
        System.out.println("Status for connection " + c.getValue() + " is " + status);
    });
}

Once the permit join window is open, you can initiate the pairing process from a secondary (i.e. remote) device.

Note: You can close the permit join window by calling "scanForDevices" with 0 as the "duration" parameter.

Interacting with Devices

When a device joins the network, you should see a DeviceEvent. For example, the SampleDeviceEventHandler we passed into GatewayClient should print something like the following:

Device Event: DEVICE_ADDED
Device ID: 00244600000f1472_1

Getting Properties

When a device first joins the network, the Device class will only know some minimal information about the device (such as its device type), but it will not know which Properties exist on it.

In order to discover which Properties exist on the device, you will have to either call 1) getProperty(String) with the property name(s) of interest, or 2) call discoverAllProperties().

Once a Property is read for the first time, it will be added to a list of known Properties for the Device, which can always be accessed by calling "getCachedProperties()". Any calls to "getProperties()" will refresh the values of all known properties on the remote device.

The API has been designed so that users can choose what level of device discovery they want to perform (for example, based on the device type), and to optimize calls going out over the network. For example, some device types are sleepy battery powered devices, and may have 50+ attributes. In these cases, users would not want to call discoverAllProperties(), because it would kick-off a very long-running network operation. The GatewayClient has a small command queue and can only perform a limited number of network operations in parallel; therefore, one would not want to fill the queue with long-running network operations. In these cases, users should selectively call getProperty(String) on a few properties they want to discover.

discoverAllProperties()

You can use this method to request all the properties, and their values, from the remote device. Note the caveats listed above.

For example, to automatically discover the properties of a device the first time the GatewayClient becomes aware of it, you can run something like the following code from the DeviceEventHandler.onDeviceEvent callback:

When 'discoverAllProperties' is called, the system will perform a discovery of the remote device's properties over the network. This may take some time; however, once properties have been discovered, users have the option of calling 'getCachedProperties()' to get the last known (cached) values immediately. Cached values are updated on writes, reads, and attribute reports. For more details, see the API documentation for getProperties().

Discovering Properties
// Automatically discover the device. This is a non-blocking call.
CompletableFuture<Collection<Property>> properties = device.discoverAllProperties();

getProperty(String)

You can also get specific properties using the getProperty() method. For example, to get the "OnOff" property from a LightDevice, you could do something like the following:

Getting a Property
final Property property = device.getProperty("onOff").getFuture().get(5, TimeUnit.SECONDS);
System.out.println(p.toString());

Updating Properties

You can update a Device Property by providing the name and value.

For example, to update the "OnOff" property, you could do something like the following:

Updating a Property
Property property = new Property("onOff, "boolean", "true");
gateway.getDevice(id).updateProperty(property);

LightDevice

The Gateway API will return a specific class for certain devices. Specific device classes, such as LightDevice, will have their own convenience methods that allow you to skip using the getProperty/updateProperty API's.

For example, for a LightDevice, you could interact with it as follows:

LightDevice API
final Device d = gateway.getDevice(id);
if (d instanceof LightDevice) {
    final LightDevice light = (LightDevice) d;
    light.off();
    light.on();
	light.readLevel();
	light.moveToLevel(50);
} else {
    System.out.println("device is not a light");
}

See the LightDevice API for more info.

ThermostatDevice

The Gateway API will return a specific class for certain devices. Specific device classes, such as ThermostatDevice, will have their own convenience methods that allow you to skip using the getProperty/updateProperty API's.

For example, for a ThermostatDevice, you could interact with it as follows:

ThermostatDevice API
final Device d = gateway.getDevice(id);
if (d instanceof ThermostatDevice) {
	final ThermostatDevice device = (ThermostatDevice) d;
	device.readMode();
	device.changeMode(SystemMode.AUTO);
} else {
	System.out.println("device is not a thermostat");
}

See the ThermostatDevice API for more info.

Listening for Device Property Updates

You can be notified when a Property on a Device changes by registering a "Property Update Handler". In Java, this can be any object that implements the "Consumer" interface, or a lambda.

For example, to automatically register an update handler the first time the GatewayClient becomes aware of a device, you can run something like the following code from the DeviceEventHandler.onDeviceEvent callback, from within the DEVICE_ADDED case statement:

Adding a Property Update Listener
device.addPropertyUpdateHandler(new SamplePropertyUpdateHandler());

...where SamplePropertyUpdateHandler is an instance of a class that implements the BiConsumer<Device, Property> interface. An example of which is shown below:

SamplePropertyUpdateHandler
public class SamplePropertyUpdateHandler implements BiConsumer<Device, Property> {
    @Override
    public void accept(Device device, Property property) {
        // This method gets called whenever a Property on a device changes.
        // By default, client callbacks are called from a single thread managed by GAPI.
        // Therefore, avoid writing blocking code in this method, or other client callbacks
        // (such as DeviceEventHandlers, or ProtocolHandlers) may be blocked.
        
        System.out.println("Property update triggered for device: " + device.getID());
        System.out.println(property.toString());
    }
}


The above example will simply print out the Device Id, as well as the Property name, value, and type, whenever the Property on that Device changes.

Enable Default Reporting

Certain Device types will have the enableDefaultReporting() method implemented, which will configure the Device to report changes to its properties.

For example, for a LightDevice, you can enable reporting so that the device will notify the PropertyUpdateHandler registered above whenever its status changes.

Enable Default Reporting
final Device device = gateway.getDevice(idStr);
final String result = device.enableDefaultReporting().get(30, TimeUnit.SECONDS);
System.out.println("Status for command: " + result);

Shutdown

To perform a graceful shut down of the GatewayClient, allowing for all subsystems to save any critical data and release system resources (such as file handles, open ports, user threads), you must call the shutdown() method. Since the GatewayClient creates user threads, not performing a shutdown may keep the JVM running even after your application has exited.

Shutting down the GatewayClient
...
gateway.shutdown();
...

See the GatewayClient API for more details.

Conclusion

This programming guide has shown how to initialize a GatewayClient, join devices, get their properties, and interact with them in a simple way.

To explore in more detail, please see the API documentation. Further, much of the sample code referred to in this guide can be found in the Sample Code available on the Downloads page.

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.