Skip to main content

Philips Hue Intelligent lighting control panel demo in Python – using Riverdi IoT Displays powered by Zerynth

For the past few years, programming of embedded systems is not the exclusive job of assembler enthusiasts or low-level operations on the registers of the microcontroller. The dynamic development of the Internet of Things caused a significant part of high-level language programmers to transfer their professional interests from the area of high-performance PCs to small microprocessors and microcontrollers. This trend has not gone unnoticed by the hardware and software manufacturers, who provide developers with ready-made solutions that allow fast – and above all – high-level, preparation of a complete hardware and software project.

In this article, we will build a simple control system for Philips Hue series bulbs, using the intelligent Riverdi IoT Display module, the dedicated Zerynth programming environment, and the OKdo IoT Cloud– enabling easy and quick application development using Python. Now, let’s get started.

Table of  Contents:

  • About Riverdi IoT Display displays and the Philips Hue system
  • IoT software in Python – it’s easier than you think!
  • Start writing the code
  • Programming in Python with Zerynth Studio
  • Graphical interface
  • OKdo IoT Cloud

About Riverdi IoT Display displays and the Philips Hue system

The aim of the presented project is to build a simple Philips Hue lighting control system using an intelligent LCD display, which in the final solution will be installed in the form of an aesthetic wall module.

All the requirements necessary to implement the project:

Thanks to our partnership with Zerynth, the Zerynth programming environment will enable us to prepare applications directly in Python. Furthermore, Riverdi IoT Display modules have an integrated LCD display system (diagonal 5 inches and screen resolution 800×480), Bridgetek BT81x graphic controller and the well-known ESP32 microprocessor.

All Intelligent IoT displays provided by Riverdi have a pre-loaded Zerynth Studio license, so we can start working with the module right after removing it from the packaging.

iot-riverdi-5-inch_%281%29_fa397d43ea6ec44717eec826e0d55ff0566063ad.jpg

Figure 1 - the back of the Riverdi IoT display

The main task for the Riverdi IoT Display module will be to establish communication with the Philips Hue intelligent lighting system. In the described case, the lighting system consists of two light bulbs with simple on/off control and one RGB bulb, controlled by determining the hue, brightness, and colour saturation parameters. The heart of the system is Philips Hue Bridge, connected to the local area network using an Ethernet cable and communicating with light bulbs using the ZigBee protocol. The block diagram of the existing configuration and its planned expansion with an intelligent control panel module is shown in Figure 2.

Figure-2_7973684981ea146c5cb5c05c18bb2898d1ba611e.png

 

Figure 2. Block diagram of the projected lighting control system

 

IoT software in Python – it’s easier than you think!

The chosen hardware solution allows us to quickly move to issues related directly to the software. This process starts with the download of an integrated and cross-platform (made available for Windows, Linux, and macOS) programming environment called Zerynth Studio.

The entire installation process of Zerynth Studio runs in a standard manner for the selected operating system. When the installer is started for the first time, the user will be asked to accept the license agreement and choose the installation method (online or offline installation – if the user has previously downloaded library repositories). The last step in the installation process is the choice of the software version (at the time of creating the article, the last available version is the version r2.3.0):

The-software-version-selection-window_0f7b836cf8dd32347c760054c59050764e78b4de.png

Figure 3. The software version selection window

Now, our environment is ready for work. But before we start writing the first lines of code, it is worth creating a simple block diagram, showing the way the designed application works – Figure 4.

image7-1-1_54152b404431cdd011f1cdcfdd2bf5a5e0c74f8d.png

Figure 4. A block diagram showing the way the application design works

We start the application by configuring the display and displaying a simple logo, which will allow us to ensure that the first configuration process has run correctly. In the next step, we connect to the WiFi network defined directly in the program (saving the SSID of the network and the access password is not the safest solution, but for the needs of the home control system that is fully sufficient).

After a successful connection to the WiFi network, the system user will be asked to enter the IP address of the Philips Hue Bridge device. The correctness of the entered IP address is verified by an attempt to read the device status. The penultimate step – before displaying the main application interface – is the process of creating a new system user and its authorization (in the final solution, this stage will be executed once).

A correctly completed process for creating a new user and its authorization, allow us to go to the last level of the menu, which is the display of the control panel. As shown in Figure 2, the existing lighting system consists only of three light bulbs (including one with the possibility of colour control), so the control panel can be implemented in the form of a “single-screen”, as shown in Figure 5.

 
The-main-menu-of-the-lighting-control-panel_33943560f5d564e21e74affc92ab6ad551cadbff.jpg

Figure 5. The main menu of the lighting control panel

Start writing the code

Having prepared a complete block diagram of the implemented application, we can proceed to write the first lines of the code.

Programming in Python with Zerynth Studio

The process of preparing the application starts with the creation of a new project in Zerynth Studio.

In the main.py file created by default, we will place the main functionality of the program. According to the adopted block diagram from Figure 4, the application starts with the display configuration. In the Riverdi IoT Display module, the role of the graphics engine is played by the Bridgetek BT815 chip. This system acts as an intelligent bridge between the LCD display (connected to the BT815 system using a 24-bit RGB interface) and a microcontroller. A typical BT815 application is shown in Figure 6.

A-typical-BT81x-application_95ba845a9d29c3cb5f655dafd71d32c5b5348063.png

Figure 6. A typical BT81x application

Thanks to the use of the BT815 system, the user’s application is exempt from the obligation to create frame buffers in the RAM area and to implement low-level “drawing” functions of the interface. The BT815 system defines both a series of simple graphical objects (buttons, switches, sliders, etc.) enabling quick creation of applications as well as more complex functions related to graphics compression or media playback.

Complete documentation of the system together with the Programming Guide – presenting the possibilities of the system is available here.

With the support of the Riverdi IoT Display module, Zerynth developers have prepared a simple to use API for BT81x circuits, enabling the creation of an interface using a few simple functions. The complete Zerynth API for BT81x systems is on the documentation.

With the use of the above documentation, let’s proceed with the first changes in the main.py file. The edition starts with the import of library modules for BT81x systems and the 5-inch Riverdi display:

from bridgetek.bt81x import bt81x

from riverdi.displays.bt81x import ctp50

In the next step, we initialize the display and communication via the selected SPI interface, additionally assigning the Chip Select line, Interrupt, Power Down line and the bus speed (default – 3 MHz).

For the integrated Riverdi IoT display, these values are fixed and take the form of the following line of code:

bt81x.init(SPI0, D4, D33, D34)

After the correct initialization of the system, we can proceed to display a simple logo. For this purpose, the selected graphic must be pre-loaded into the BT81x internal GRAM memory. To place the loadImage () function in the newly created gui.py file already at the first stage of the work, put in the new file:

def loadImage(image):

    bt81x.load_image(0, 0, image)

The task of the loadImage () function is only to call the bt81x.load_image () a method whose parameters are: the address in the GRAM memory area, additional control flags and the file name with the selected graphics. In the prepared application – in the area of GRAM memory – we will store only one graphic at a time, so the address in memory will always be set to 0. For testing purposes, we will use graphics in PNG format with the Riverdi logo of 642×144 pixels – the remaining area The image (for a 5-inch display is 800×480 pixels) will be completed with a white background. In the main.py file, loading the graphic gui_riverdi_logo.png into the GRAM memory, we will execute the following set of calls:

import gui

new_resource('images/gui_riverdi_logo.png')

gui.loadImage('gui_riverdi_logo.png')

For full functionality, let’s implement the showLogo () function, which displays the loaded image in the specified coordinates. For this purpose – in the gui.py file – place the following code fragment:

def showLogo():

  # start

  bt81x.dl_start()

  bt81x.clear_color(rgb=(0xff, 0xff, 0xff))

  bt81x.clear(1, 1, 1)

  # image

  image = bt81x.Bitmap(1, 0, (bt81x.ARGB4, 642 * 2), (bt81x.BILINEAR, bt81x.BORDER, bt81x.BORDER, 642, 144))

  image.prepare_draw()

    image.draw(((bt81x.display_conf.width - 642)//2, (bt81x.display_conf.height - 144)//2), vertex_fmt=0)

  # display

  bt81x.display()

  bt81x.swap_and_empty()

Using the bt81x.dl_start () method, we start creating a new Display List that describes the currently created frame. Calling bt81x.clear_color () sets the colour for the image cleansing operation, which is done by the bt81x.clear () method. Using the Bitmap class, we define the parameters of the displayed image, including image source (in this case it is GRAM memory) and its size. Use the bt81x.prepare_draw () and bt81x.draw () calls to add the indicated graphics to the Display List. Each creation of the Display List by means of the bt81x.dl_start () call must be terminated by calling bt81x.display () – terminating its creation, and calling bt81x.swap_and_empty (), which changes the currently displayed content. The showLogo () function constructed in this way is called directly in the main.py file.

Communication between the control panel and the Philips Hue bridge will be made using the local network. For this purpose, it is necessary to connect the Riverdi IoT Display module to the WiFi network. Thanks to the use of the ESP32 system – integrating WiFi communication modules and Bluetooth Low Energy – the user does not have to connect any additional wireless modems. In order to establish a connection with the selected WiFi network (specified ssid variable and the access password – wifiPWD variable), in the main.py file, we will implement the following code fragment:

from wireless import wifi

from espressif.esp32net import esp32wifi as wifi_driver 

ssid = "ssid_value"            # this is the SSID of the WiFi network

wifiPWD = "password_value"     # this is the Password for WiFi

gui.showSpinner("Connecting with predefined WiFi network...")

wifi_driver.auto_init()

for _ in range(0,5):

    try:

        wifi.link(ssid,wifi.WIFI_WPA2,wifiPWD)

        break

    except:

        gui.showSpinner("Trying to reconnect...")

else:
    gui.showSpinner("Connection Error - restarting...")

    mcu.reset()

Using the for () loop and the wifi.link () method, the application attempts to connect to a defined network five times. If the operation fails completely, the mcu.reset () function will reset the device. Depending on the strength of the WiFi network signal, the linking process may be longer, so that the system user receives feedback from the graphical interface, it is informed about the progress of the process by calling the gui.showSpinner () function, which displays the animated Spinner object specified by the argument message. The showSpinner () function is defined in the gui.py file:

def showSpinner(msg):


    bt81x.dl_start()

    bt81x.clear(1, 1, 1)

    txt = bt81x.Text(400, 350, 30, bt81x.OPT_CENTERX | bt81x.OPT_CENTERY, msg, )

    bt81x.add_text(txt)



    bt81x.spinner(400, 240, bt81x.SPINNER_CIRCLE, 0)



    bt81x.display()

    bt81x.swap_and_empty()

Using the above function, in the newly created Display List we place elements like Spinner and Text – the whole list is closed with calls bt81x.display () and bt81x.swap_and_empty (). The resulting graphic effect is shown in Figure 7.

A-screen-with-a-Spinner-object-informing-the-user-about-the-progress-of-processes_79cbaa3496f8f3ec6bfe1411b68de9e65e8cb868.jpg

Figure 7. A screen with a Spinner object informing the user about the progress of processes

In the next step – after successfully connecting to the WiFi network – we can start to create a graphical interface that allows entering the IP address assigned to the Philips Hue Bridge device. For the construction of the interface, we use the bt81x.add_keys () method, which places a single row of buttons in Display List – for an example call:

bt81x.add_keys(450, 70, 280, 60, 30, 0, "123")

The obtained effect will look like this:

Figure 8. A single line of buttons created using bt81x.add_keys ()

Graphical interface

Let’s go back to the operation of preparing the graphical interface. The screen for entering the IP address will consist of a 4-line keyboard (digits from 0-9, a dot symbol and a button to delete incorrectly entered values), a Connect button and a simple graphic. The entire operation was implemented in the form of the showAddrScreen () function in the gui.py file:

def showAddrScreen(ip):


    # start

    bt81x.dl_start()

    bt81x.clear(1, 1, 1)
    

    # image

    image = bt81x.Bitmap(1, 0, (bt81x.ARGB4, 200 * 2), (bt81x.BILINEAR, bt81x.BORDER, bt81x.BORDER, 200, 200))

    image.prepare_draw()

    image.draw((0, 255), vertex_fmt=0)


    # text

    txt = bt81x.Text(225, 120, 29, bt81x.OPT_CENTERX | bt81x.OPT_CENTERY, "Enter IP address of HUE Bridge:", )

    bt81x.add_text(txt)

    txt.text = ip
    txt.x = 225

    txt.y = 195

    txt.font = 31

    bt81x.add_text(txt)


    # keys

    bt81x.track(450, 350, 280, 60, 0)

    bt81x.add_keys(450, 70, 280, 60, 30, 0, "123")
    bt81x.add_keys(450, 140, 280, 60, 30, 0, "456")

    bt81x.add_keys(450, 210, 280, 60, 30, 0, "789")

    bt81x.add_keys(450, 280, 280, 60, 30, 0, ".0C")


    # connect button

    btn = bt81x.Button(450, 350, 280, 60, 30, 0, "Connect")

    bt81x.tag(1)

    bt81x.add_button(btn)


    bt81x.display()

    bt81x.swap_and_empty()

By using the bt81x.tag () method in the showAddrScreen () function, the value of the TAG identifier is assigned to the Connect button. The buttons from the keypad generated by a series of calls bt81x.add_keys (), as the TAG field, return ASCII codes corresponding to the individual key labels. Handling of all events is performed in the push function pressed (), as defined in the call bt81x.touch_loop (). A fragment of the pressed () function – associated with keyboard support – is shown below:

def pressed(tag, tracked, tp):
   # entering HUE IP

    if (screenLayout == 2):


        global hueIP

        global screenLayout

        

        # emit sound

        beep()


        # remove last character

        if (tag != 67):
 

# max length of IP
           if len(hueIP) >= 15:

                return


            hueIP = hueIP + str(chr(tag))


        else:

            if len(hueIP) > 0:

                hueIP = hueIP[:-1]



        # connect -> go to next screen

        if (tag == 1):

            hueIP = hueIP[:-1]

            screenLayout = 3

The call to the showAddrScreen () function is performed in the while () loop (in the main.py file) until the Connect button is selected (this process is repeated if the entered IP address of the device is incorrect):

gui.loadImage('gui_addr_hue.png')

while True:
    

    # enter IP address of HUE gateway

    screenLayout = 2

    while (screenLayout == 2):

        gui.showAddrScreen(hueIP)


    # show spinner (looking for HUE)

    screenLayout = 3

    gui.showSpinner("Looking for HUE Bridge...")


    # check HUE availability

    status = hue.testConnection(hueIP)
        if (status):

        break;

The final effect of calling the showAddrScreen () function is shown in Figure 9.

 
Interface-to-enter-the-IP-address-of-the-Philips-Hue-Bridge-device_600344061ee0ce3a56028fdcf87ac7c41c8a608a.jpg

Figure 9. Interface to enter the IP address of the Philips Hue Bridge device

After the correct indication of the IP address of the Philips Hue Bridge device, the control panel will go to the authorization stage (issues related directly to establishing a connection, authorization and communication protocol with Philips Hue Bridge, will be discussed later in the article). At this point, the message displayed to the user will be limited only to a simple graphic and a short message asking you to press the button located on the Philips bridge housing (it is a confirmation that the system user has physical access to the device). For displaying the message and graphics, the showAuthScreen () function from the gui.py file will be responsible:

def showAuthScreen():

    bt81x.dl_start()

    bt81x.clear(1, 1, 1)


    image = bt81x.Bitmap(1, 0, (bt81x.ARGB4, 420 * 2), (bt81x.BILINEAR, bt81x.BORDER, bt81x.BORDER, 420, 480))
    image.prepare_draw()

    image.draw((0, 0), vertex_fmt=0)


    txt = bt81x.Text(590, 220, 28, bt81x.OPT_CENTERX | bt81x.OPT_CENTERY, "Press the push-link button of the Hue", )

    bt81x.add_text(txt)

    txt.text = "bridge you want to connect to"

    txt.x = 590

    txt.y = 260

    bt81x.add_text(txt)


    bt81x.display()

    bt81x.swap_and_empty()

In the main.py file – before displaying the message asking for authorization – add a short intermediate screen (using the previously prepared function showSpinner ()). During this time, to the GRAM memory of the BT81x controller, we upload the graphic file gui_auth_hue.png, which is used in the construction of the interface from the function showAuthScreen (). All operations will be performed in the while () loop until the user is properly authorized (the createUser () function will be discussed later in the article):

gui.showSpinner("Creating new user...")

gui.loadImage('gui_auth_hue.png')

while True:


    # show authScreen

    screenLayout = 5

    gui.showAuthScreen()


    # check status

    username = hue.createUser(hueIP)

    if (username):

        break;

Leaving the above while () loop is tantamount to successfully terminating the connection process with Philips Hue Bridge. So the time has come to display the main control screen, consisting of ON / OFF buttons for two bulbs and control elements for one “coloured” light bulb. Before we go on to discuss the code snippets, let’s look at the final effect presented in Figure 10 beforehand.

 
The-interface-of-the-main-control-panel_2f6cefd7a6b68f2eeb1a67b49bd1d6fa40934b4e.jpg

Figure 10. The interface of the main control panel

The main control panel is definitely more extensive than the previous screens of the application – a single addition of 10 buttons and 8 text elements would complicate the main function very much, and the possible introduction of adjustments of the positions of individual interface elements would be very tedious. For this purpose, all components of the main interface are described using the JSON array, and their placement on the Display List, grouped into two for () loops (separate for Button and Text elements). Fragments of JSON descriptions are presented below:

buttons = [

    {

        "tag_id": 2,

        "text": "ON",

        "x_cord": 50,

        "y_cord": 200,

        "width": 170,

        "height": 50,

        "size": 30

    },

    {

        "tag_id": 3,

        "text": "OFF",

        "x_cord": 50,

        "y_cord": 300,

        "width": 170,

        "height": 50,

        "size": 30

    },

/.../

    {

        "tag_id": 11,

        "text": ">",

        "x_cord": 675,

        "y_cord": 350,

        "width": 50,

        "height": 50,

        "size": 30

    },

]




labels = [

    {

        "text": "Kitchen",

        "x_cord": 136,

        "y_cord": 140,

        "size": 31,

        "options": bt81x.OPT_CENTER

    },

/.../

    {

        "text": "Brightness",

        "x_cord": 622,

        "y_cord": 330,

        "size": 28,

        "options": bt81x.OPT_CENTER

    },

]

For parsing individual elements of the JSON description, adding them to the Display List and its final display corresponds to the showMainMenu () function placed in the gui.py file:

def showMainMenu(saturation, hue, brightness):

    bt81x.dl_start()

    bt81x.clear(1, 1, 1)




    image = bt81x.Bitmap(1, 0, (bt81x.ARGB4, 800 * 2), (bt81x.BILINEAR, bt81x.BORDER, bt81x.BORDER, 800, 50))

    image.prepare_draw()

    image.draw((0, 0), vertex_fmt=0)




    btn = bt81x.Button(0, 0, 170, 70, 31, 0, "")

    for button in buttons:

        btn.text = button["text"]

        btn.font =  button["size"]

        btn.x = button["x_cord"]

        btn.y = button["y_cord"]

        btn.width =  button["width"]

        btn.height = button["height"]

        bt81x.track(btn.x, btn.y,  button["width"], button["height"], button["tag_id"])

        bt81x.tag(button["tag_id"])

        bt81x.add_button(btn)




    txt = bt81x.Text(0, 0, 0, 30, "")

    for label in labels:

        txt.text = label["text"]

        txt.x = label["x_cord"]

        txt.y = label["y_cord"]

        txt.font = label["size"]

        txt.options = label["options"]

        bt81x.add_text(txt)

    

    # saturation value label

    txt.text = str(int((saturation/240)*100)) + '%'

    txt.x = 622

    txt.y = 175

    txt.font = 30

    txt.options = bt81x.OPT_CENTER

    bt81x.add_text(txt)


    /.../


    # brightness value label

    txt.text = str(int((brightness/240)*100)) + '%'

    txt.x = 622

    txt.y = 375

    txt.font = 30

    txt.options = bt81x.OPT_CENTER

    bt81x.add_text(txt)


    # display

    bt81x.display()

    bt81x.swap_and_empty()

The cyclic call to the showMainMenu () function has been placed in the main.py file in the main program loop. The main loop updates the values presented in the graphical interface, checks the status of variables representing states of controlled bulbs and in the event of changes (resulting from touch panel operation and event handling in the call function pressed ()) sends messages to the Philips Hue Bridge device:

while True:

 gui.showMainMenu(saturation_old_value, hue_old_value, brightness_old_value)




 if (bulb_1_new_value != bulb_1_old_value):

  hue.turnLight(hueIP, username ,"4", bulb_1_new_value)

  bulb_1_old_value = bulb_1_new_value




 if (bulb_2_new_value != bulb_2_old_value):

  hue.turnLight(hueIP, username ,"2", bulb_2_new_value)

  bulb_2_old_value = bulb_2_new_value

        

 if (saturation_new_value != saturation_old_value):

  hue.changeColor(hueIP,username,"3",True, saturation_new_value,brightness_new_value, hue_new_value)

  saturation_old_value = saturation_new_value

        

 if (hue_new_value != hue_old_value):

  hue.changeColor(hueIP,username,"3",True,saturation_new_value, brightness_new_value, hue_new_value)

  hue_old_value = hue_new_value

        

 if (brightness_new_value != brightness_old_value):

  hue.changeColor(hueIP,username,"3", True,saturation_new_value,brightness_new_value, hue_new_value)

  brightness_old_value = brightness_new_value


The updated fragment of the pressed () function is shown below:

def pressed(tag, tracked, tp):




    # entering HUE IP

    if (screenLayout == 2):

    /.../

    # mainmenu screen

    elif (screenLayout == 7):




        global bulb_1_new_value

        global bulb_2_new_value




        # on/off switching

        if (tag == 2):

            bulb_1_new_value = True

        elif (tag == 3):

            bulb_1_new_value = False

        elif (tag == 4):

            bulb_2_new_value = True

        elif (tag == 5):

            bulb_2_new_value = False

        elif (tag == 6):

            if (saturation_new_value  > 0):

                saturation_new_value -= 24

        elif (tag == 7):

            if (hue_new_value  > 0):

                hue_new_value -= 6550

        /…/

        elif (tag == 11):

            if (brightness_new_value < 240):

                brightness_new_value += 24

We have successfully managed to omit all aspects of communication with the Philips Hue bridge, but it’s time to devote a few moments to this device and how it communicates with the world. In the most popular configuration, Philips Hue Bridge acts as an intermediary in communication between the mobile application and terminal devices. The programmers of the Philips Hue system, however, went a step further and instead of creating a closed system on the line mobile application <-> bridge, they decided to prepare an open and well-documented API, thus allowing external programmers to create their own control and system management devices.

A set of information related to the creation of own applications has been made available here.

Another nod to programmers is to create a simple interface that allows testing the API and system operation without creating a dedicated application. This interface – called the CLIP API Debugger – has been made available at:

https://debug/clip.html

The address of the Philips bridge can be determined by using the UPnP protocol, through https://discovery.meethue.com/ or by reading the configuration of the home router)

The appearance of the CLIP API Debugger interface window is shown in Figure 11.

The-interface-of-the-CLIP-API-Debugger-panel_151114fe8bea082ea50ddcd391f30a9ae224b929.png

Figure 11. The interface of the CLIP API Debugger panel

Communication with Philips Hue Bridge is carried out using HTTPS and REST API calls, so the interface window has been divided into the URL field, the choice of method type (GET, PUT, POST or DELETE) and the BODY section in which JSON queries are placed.

Before we move on to the question of controlling the work of light bulbs, it is necessary to create a new user and its authorization beforehand. To create a new user, make the following call:

URL: /api

Body: {"devicetype":"my_hue_app#Riverdi IoT Display"}

Method: POST

In response, we’ll get:

[

{

 "error": {

 "type": 101,

 "address": "",

 "description": "link button not pressed"

}

}

]

To increase the security of the system, it is necessary to properly authorize the user. In the received communication, the user is asked to press a button placed on the device casing – this procedure is to confirm that the person attempting to gain access to the lighting control also has physical access to the control bridge. Pressing the button and re-sending the POST request above will be confirmed by the following message:

[

{

"success": {

"username": "JxeQj8Xvi1zLDBnDz09w1aCjWA80rDxbQNYUlOCn"

}

}

]

The value of the username field is the new name of the authorized user, which will be used in further communication. However, let’s get back to the code of our application for a moment and consider how to accomplish the above tasks using Python and a set of libraries provided by Zerynth. The request module comes with help (for Python programmers this name is probably well known – this module – for the API is inspired by a quite popular module with the same name). This module enables fast implementation of HTTP support and GET, POST, PUT and DELETE calls in the form of single functions. The full API documentation for the requests module is available at the Zerynth docs.

The createUser () function – based on calls from the requests module – responsible for creating a new user is defined in the hue.py file:

def createUser(ip):


    try:

        response = requests.post("http://" + ip + "/api", json={"devicetype": "my_hue_app#Riverdi IoT Display"})



        js = json.loads(response.content)

        return js[0]["success"]["username"];



    except Exception as e:

        return None;

The above function is performed in a loop until the value of success in the response returned by the bridge is read. To the main program loop, this function returns the value read from the username field, which will be used in subsequent calls controlling the status of light bulbs:

while True:

    # show authScreen

    screenLayout = 5

    gui.showAuthScreen()


    # check status

    username = hue.createUser(hueIP)

    if (username):

        # todo save user and password to flash

        break;

Obtaining the name of an authorized user allows us to read the system configuration, i.e. list of connected bulbs, their status, identification numbers, etc .:

URL: /api//lights

Body: [none]

Method: GET

The fragment of the system response (for a configuration with three bulbs – as shown in Figure 2) is shown below:

{

"2": {

"state": {

"on": false,

"bri": 254,

"alert": "select",

"mode": "homeautomation",

"reachable": false

},

"swupdate": {

"state": "noupdates",

"lastinstall": "2019-06-06T12:55:19"

},

"type": "Dimmable light",

"name": "Hue white lamp 2",

"modelid": "LWB010",

"manufacturername": "Philips",

"productname": "Hue white lamp",

"capabilities": {

"certified": true,

"control": {

"mindimlevel": 2000,

"maxlumen": 806

},

"streaming": {

"renderer": false,

"proxy": false

}

},

"config": {

"archetype": "classicbulb",

"function": "functional",

"direction": "omnidirectional",

"startup": {

"mode": "safety",

"configured": true

}

},

/.../

},

"3": {

"state": {

"on": false,

"bri": 24,

"hue": 45850,

"sat": 240,

"effect": "none",

"xy": [

0.1666,

0.1016

],

"ct": 153,

"alert": "select",

"colormode": "hs",

"mode": "homeautomation",

"reachable": false

},

"swupdate": {

"state": "readytoinstall",

"lastinstall": "2019-07-04T12:22:06"

},

"type": "Extended color light",

"name": "Hue color lamp 1",

"modelid": "LCT015",

"manufacturername": "Philips",

"productname": "Hue color lamp",

"capabilities": {

"certified": true,

"control": {

"mindimlevel": 1000,

"maxlumen": 806,

"colorgamuttype": "C",

"colorgamut": [

[

0.6915,

0.3083

],

[

0.17,

0.7

],

[

0.1532,

0.0475

]

],

"ct": {

"min": 153,

"max": 500

}

},

"streaming": {

"renderer": true,

"proxy": true

}

},

"config": {

"archetype": "sultanbulb",

"function": "mixed",

"direction": "omnidirectional"

},

/.../

},

"4": {

"state": {

"on": false,

"bri": 254,

"alert": "select",

"mode": "homeautomation",

"reachable": false

},

"swupdate": {

"state": "transferring",

"lastinstall": "2019-07-05T12:53:52"

},

"type": "Dimmable light",

"name": "Hue white lamp 3",

"modelid": "LWB010",

"manufacturername": "Philips",

"productname": "Hue white lamp",

"capabilities": {

"certified": true,

"control": {

"mindimlevel": 5000,

"maxlumen": 806

},

"streaming": {

"renderer": false,

"proxy": false

}

},

"config": {

"archetype": "classicbulb",

"function": "functional",

"direction": "omnidirectional"

},

/.../

}

}

The current status of individual lamps can be checked by adding the device’s identification address number in the GET query, eg:

URL: /api / lights / 2

Body: [none]

Method: GET

Changing the standard status of the light bulb is carried out by calling the PUT method:

URL: /api / lights / 2 / state

Body: { "a": false}

Method: PUT

Changing the status of a coloured light requires the user to specify not only its new state of work but also to determine the value of colour, brightness and saturation:

URL: / api / lights / 2 / state

Body: {"on": true, "sat": 254, "bri": 254, "hue": 10000}

Method: PUT

Based on the above API – using the requests module – the turnLight () and changeColour () functions were prepared:

def turnLight(ip,user,num,state):

    try:

        addr = "http://" + ip + "/api/" + user + "/lights/" + num + "/state"

        requests.put(addr, json={"on":state})

    except Exception as e:

        return None;


def changeColor(ip,user,num,state, sat, bri, hue):


    try:

        addr = "http://" + ip + "/api/" + user + "/lights/" + num + "/state"

        requests.put(addr, json={"on":state, "sat":sat, "bri":bri, "hue":hue})

    except Exception as e:

        return None;
 
 

OKdo IoT Cloudiot-hero-banner_f9a5a231754edbd6352485d08877e0f3edcea469.jpg

 

Is this the end of the possibilities of this extraordinary mix which is Riverdi IoT Display and the Zerynth environment? Certainly not! To provide remote control over the lighting system, the application can be extended in several lines of code with the possibility of remote synchronization with the OKdo IoT Cloud solution [6]. This solution allows us to remotely monitor and control our lighting system by using a mobile device or web interface.

The OKdo Cloud is free, and you can easily sign up for the account, on the official page linked above. As they say on the OKdo Cloud page:

“Connect your things and interact with them for free on the OKdo Cloud.”

OKdo IoT Cloud provides connectivity endpoints for MQTT, HTTP, and UDP protocols.

Details related to the API for communication with the OKdo IoT Cloud are provided on the Zerynth docs.

The full application source code is available on the Riverdi GitHub repository.

 

Resources:

[1] https://www.meethue.com/

[2] https://www.zerynth.com/

[3] https://riverdi.com/product/ritft50iotcap/

[4] https://brtchip.com/bt81x/

[5] https://www.espressif.com/en/products/hardware/esp32/overview

[6] https://okdo.allthingstalk.com/

I’ve learned early on in my career that I love to be at the forefront of innovation. To see new technologies emerge, and be a part of their creation. From the first days on the internet and the Silicon Valley revolution to The Internet of Things — I’ve seen it all, and I’ve done it all. Now, I’m ready to use this knowledge to help young companies thrive. I have over 35 years of experience in driving revenue, encouraging new ideas, and fostering client relationships.
DesignSpark Electrical Logolinkedin