Post Image

Working with AllJoyn LIFX light bulb

/content/images/2015/02/lifx-edison-screw-1.jpg

In this article, I'll explain how to control AllJoyn-enabled light bulbs from LIFX. The sample code I use is from AllJoyn Cordova Plugin project but you're totally able to re-implement it once you've understood about dealing with the bulbs.

Prerequisites

This post requires some basic knowledge about Alljoyn so if you're not familiar with it yet, here are some good tutorials:

Getting Started

Since there is currently no retail bulb with AllJoyn in-built, you can use Luminaire app from Qualcomm acting as a virtual lamp for your development.

Here's a clip about using Luminaire to test your app:

AllJoyn Lighting Service Framework

The Lighting service framework comprises two service components:

  • Lamp service: enables an embedded lighting device (such as a connected light bulb) to be controlled by the Controller service.
  • Controller service: enables AllJoyn applications (e.g., an application running on a smartphone) to control the Lamp service.

Lighting OEMs can embed the Lamp service in the firmware of their lighting products to enable Smart Lighting features. The Controller service is designed to find all devices running the Lamp service on the AllJoyn network and provide additional functionality to control the lighting devices in a variety of ways (e.g., create and control a group of lights simultaneously and applying custom lighting effects).

You can turn on/off the controller service in Luminaire app but keep in mind that the results returned from doing D-Bus introspection will be different. In short:

  • Controller service = On: your app talks to the controller service in order to control the lamp.
  • Controller service = Off: your app talks to the lamp service directly in order to control the lamp.

For simplicity's sake, I'm not using the controller service in the demo. If you're interested in the AllJoyn Lighting Service Framework, you can find more info at the Connected Lighting Working Group website.

Object path and Interfaces

You can use D-Bus introspection or study the Lamp Service Interface Definition to learn about the object path and interfaces of the lamp service.

Object path:

/org/allseen/LSF/Lamp

Main Interface:

org.allseen.LSF.LampState,
?TransitionLampState Timestamp<t NewState<a{sv} TransitionPeriod<u LampResponseCode>u,
?ApplyPulseEffect FromState<a{sv} ToState<a{sv} period<u duration<u numPulses<u timestamp<t LampResponseCode>u,
!LampStateChanged LampID>s,
@Version >u,
@OnOff =b,
@Hue =u,
@Saturation =u,
@ColorTemp =u,
@Brightness =u,

There are two important methods that you use to control the lamp:

  • TransitionLampState: control the lamp's power state, hue, saturation, color temp and brightness.
  • ApplyPulseEffect: apply special effects to the lamp.

Controlling the lamp

This might be the most tricky part since you may not be able to change any property of the lamp besides its power state (on/off). For example, a method call to increase the lamp's brightness always returns as successful but nothing has actually changed and the value is '0'.

The root of the problem stays in how you understand the min and max values of the lamp's attributes to set them properly. In OEM's range, the minimum value is mapped to '0' and the maximum value is mapped to '0xFFFFFFFF - 1'. So if you want to set brightness as 50%, you must pass '(0xFFFFFFFF - 1) / 2' as parameter instead of '50'.

The following code snippet does the conversion from normal to OEM values:

getOEMRange: function (value)
{
    return value * ((0xFFFFFFFF - 1) / 100);
},

Sample code and demo

To set the lamp's brightness, I use the following implementation:

setBrightness: function (brightness)
{
    var uBrightness = this.getOEMRange(brightness);
    lsfLampState.transitionState(true, lsfLampState.hue, lsfLampState.saturation, lsfLampState.colorTemp, uBrightness, 0);
},

TransitionLampState method is used here to change the brightness property of the lamp:

transitionState: function (bOnOff, uHue, uSaturation, uColorTemp, uBrightness, uTransitionPeriod)
{
    var transitionLampStateMsg = [2, 0, 4, 0]; // Proxy object list, object index, interface index, method index

    var args =
    [
        0,
        [
            ['OnOff', 'b', bOnOff],
            ['Hue', 'u', uHue],
            ['Saturation', 'u', uSaturation],
            ['ColorTemp', 'u', uColorTemp],
            ['Brightness', 'u', uBrightness]
        ],
        uTransitionPeriod
    ];

    var onLampStateChanged = function (returnArgs)
    {
        var uResponse = returnArgs[0];

        if (uResponse == 0) // successful
        {
            lsfLampState.onOff = bOnOff;
            lsfLampState.hue = uHue;
            lsfLampState.saturation = uSaturation;
            lsfLampState.colorTemp = uColorTemp;
            lsfLampState.brightness = uBrightness;
            lsfLampState.busy = false;
        }
    }

    app.lightbulbSession.callMethod(onLampStateChanged, app.onError('TransitionLampState'), null, null, transitionLampStateMsg, 'ta{sv}u', args, 'u');
},

And the final output of the app :)


The full sample app can be found here.