All Ruby on Rails Node JS Android iOS React Native Frontend Flutter QA

Philips Hue in Android

Over the last years, we have been able to observe the growth of the Internet of Things market. Currently, a range of stores offer many "smart" products, from refrigerators, through air purifiers, to kettles.

Many items with "smart" technology seem to be bad idea, but I would like to raise the subject of a not-so-new invention which in my opinion may turn out to be something common in the near future. This invention is the intelligent lighting system.

Philips Hue is a set of colour-changing LED lamps and sensors that can be controlled wirelessly. Two communication protocols are used to control the bulbs: Wifi/Ethernet and ZigBee.  


The picture below shows a simplified diagram of communication between devices:

image1-22

This is how the process works:

  • The device you want to control your intelligent lighting with. It can be either a smartphone with Android or iOS or a desktop application
  • A router that forwards data between the application and the bridge.
  • A bridge, whose role is to mediate in transmitting information to the light bulbs using the ZigBee communication protocol.
  • Receivers - light bulbs with ZigBee modules.

Zigbee is a high-level communication protocol that is used to create personal area networks for purposes such as home automation. Its biggest advantage is low power usage. More detailed information about this technology can be found here.   

Recently, I decided to learn something about this topic, and my choice fell on the Android platform. In this article, I would like to present my thoughts and examples of use.

As a rule, in order for our application to start controlling bulbs or sensors, we must guide the user through a short configuration process that includes the following steps:

1. Display a list of available bridges within the local area network and ask the user to pick one.

2. Start the authorisation process and ask the user to press the big round button on the top of the bridge.

3. Save the local address of the bridge and the generated login in the phone’s memory.


To fulfil the first point we must create an instance of  `PHHueSDK` that can be provided with DI libraries like Dagger2:

phHueSDK = PHHueSDK.create().apply {
	appName = "AppName"
	deviceName = android.os.Build.MODEL
}

Then we need to set up a listener:

phHueSDK.notificationManager.registerSDKListener(this)

And start searching:

val searchManager = phHueSDK.getSDKService(PHHueSDK.SEARCH_BRIDGE) as PHBridgeSearchManager
searchManager.search(true, false)

First parameter inside `search()` indicates that we’re searching for bridges using `UPnP`.


After calling the search method, we can now listen for events with the `PHSDKListener`.

public interface PHSDKListener {
	void onCacheUpdated(List<Integer> cacheNotificationsList, PHBridge bridge);
	void onBridgeConnected(PHBridge bridge, String userName);
	void onAuthenticationRequired(PHAccessPoint accessPoint);
	void onAccessPointsFound(List<PHAccessPoint> accessPoints);
	void onError(int code, String message);
	void onConnectionResumed(PHBridge bridge);
	void onConnectionLost(PHAccessPoint accessPoint);
	void onParsingErrors(List<PHHueParsingError> parsingErrors);
}

Detected bridges will be delivered to the `onAccessPointsFound()` function where we can update the list shown to the user:

override fun onAccessPointsFound(accessPoints: MutableList?) {
	// (...)
    view.refreshAccessPoints(accessPoints)
}

After the user presses the button, we should connect to the selected bridge:

override fun onBridgeClicked(bridge: PHAccessPoint) {
    val selectedBridge = hueSdk.selectedBridge
    selectedBridge?.let { bridge ->
        val connectedIP = bridge.resourceCache.bridgeConfiguration.ipAddress
        if (connectedIP != null) { // since we might be already connected to another bridge
            hueSdk.disableHeartbeat(bridge)
            hueSdk.disconnect(bridge)
        }
    }
    hueSdk.connect(bridge)
}

If the selected bridge requires authentication, the `onAuthenticationRequired()` function will be called:

override fun onAuthenticationRequired(accessPoint: PHAccessPoint?) {
    hueSdk.startPushlinkAuthentication(accessPoint)
    view.showPressButtonMessage()
}

In that case we should call the `hueSdk.startPushlinkAuthentication` function and ask the user to press the button on the top of his bridge to confirm access from the application.

If everything goes well, the `onBridgeConnected` function will be called. This is where we should save the bridge’s IP address and username for authorisation in the future:

override fun onBridgeConnected(bridge: PHBridge, username: String) {
    val ipAddress = bridge.resourceCache.bridgeConfiguration.ipAddress
    bridgePrefs.ipAddress = ipAddress
    bridgePrefs.userName = username
    view.showSuccess()
}

After a successful authorisation process, we are able to connect to the bridge using the previously saved data:

fun connect() {
    val lastAccessPoint = PHAccessPoint()
    lastAccessPoint.ipAddress = userPrefs.ipAddress
    lastAccessPoint.username = userPrefs.userName

    if (!hueSdk.isAccessPointConnected(lastAccessPoint)) {
        hueSdk.connect(lastAccessPoint)
    }
}

Now we are able to start controlling the Hue devices. Using the `PHHueSDK` object we are able to get data such as lights, rooms, bridge configuration, rules, scenes, schedules, sensors, and more. For example, let’s turn on all lamps connected to the bridge:

private fun getBridge() = hueSdk.selectedBridge

private fun getLights(bridge: PHBridge) = bridge.resourceCache.allLights

fun enableLights(enabled: Boolean) {
    val bridge = hueSdk.selectedBridge // get connected bridge
    val allLights = bridge.resourceCache.allLights // get all lights
    allLights.forEach { light -> 
        val lightState = PHLightState() // create new state
        lightState.isOn = enabled // set state to enabled
        bridge.updateLightState(light, lightState, this) // update the state of bulb
    }
}

`this` is a `PHLightListener `where we can listen for events such as `onSuccess()`, when the state of light changed successfully, or `onError`, with an error code to handle it:

void onSuccess();
void onReceivingLightDetails(PHLight light);
void onSearchComplete();
void onStateUpdate( Map<string, string=""> successAttribute, List errorAttribute);
void onReceivingLights(List lights);
void onError(int code, String errorMessage);

 

In this way, we went through the simplest case of controlling smart light bulbs. Personally, I think that the idea of intelligent lighting has a great chance of becoming a standard in the coming years, as soon as their price becomes more affordable.


Below are some sources that contain more interesting information:


Photo by Rohan Makhecha on Unsplash

We're building our future. Let's do this right - join us
READ ALSO FROM Mobile
Read also
Need a successful project?
Estimate project or contact us