Controlling XBMC with a Yocto-Display

Controlling XBMC with a Yocto-Display

We showed you previously how to use a Yoctopuce screen to display information coming from Internet, such as an RSS feed or Google Calendar events. Today, we are going to show you how you can use a Yocto-Display to build an active control panel for an XBMC media center.





To build the control panel, we need a Yocto-Display and six buttons for top, bottom, left, right, OK, and back actions.

The Yocto-Display and the Yocto-MaxiDisplay are not simple screens: they embed the equivalent of a Yocto-Knob. On top of the display function (enabling you to interact with the screen) and of the files function (enabling you to upload images or fonts), they also have 6 anButton functions enabling you to measure the state of resistive components (switches, push buttons, potentiometers, and so on). These 6 functions can be used with the mini-buttons on the board. You can also solder your own buttons on the designed pads.

The Yocto-Display with 6 external buttons
The Yocto-Display with 6 external buttons



To build this control panel, we wrote a short Python script that retrieves the title of the movie being read, that displays it on the Yocto-Display, and that sends to the XBMC the navigation commands when a button is pushed.

The XBMC API


To interact with XBMC, we used the JSON-RPC API which is automatically installed. With this API, you can simulate any button of a remote control, but also retrieve information on the movie (or mp3) which is aired. This API follows the JSON-RPC protocol and is documented on the project web page (http://kodi.wiki/view/JSON-RPC_API). By default, this API is disabled. To use it, you must enable the "Allow control of XBMC via HTTP" option in the "Webserver" menu (available in the parameter "Service" section).

To use the JSON-RPC API, you must enable the  'Allow control of XBMC via HTTP' option
To use the JSON-RPC API, you must enable the 'Allow control of XBMC via HTTP' option



Several Python libraries implement only this protocol, but we decided to use the requests library which allows us to write any request based on the HTTP protocol.

A JSON-RPC request is nothing more than an HTTP POST request with a JSON structure containing the name of the function to be called ("method" field) and the parameters of this function ("params" field).

To communicate with XBMC, we implement a SimpleXBMC class containing a json_rpc_request method that takes as arguments the name of the XBMC function and its parameters, and that returns the result of the request. This class contains the methods up(), down(), right(), left(), ok(), back(), and get_player_info() which call the json_rpc_request method with the correct parameters.

class SimpleXMBC(object):
        def __init__(self, host, port, user, password):
        self._url = 'http://%s:%d/jsonrpc' % (host, port)

        def json_rpc_request(self, method, params):
        headers = {'content-type': 'application/json'}
        payload = {
            "method": method,
            "params": params,
            "jsonrpc": "2.0",
            "id": 0,
        }
        response = requests.post(
            self._url, data=json.dumps(payload), headers=headers).json()
        if 'error' in response:
            print(response['error'])
        return response

        def up(self):
        self.json_rpc_request('Input.Up', {})


    ....

xbmc_interface = SimpleXMBC('localhost', 80, 'xbmc', 'password')
xbmc_interface.up()




The Python script


The remainder of the script is rather similar to the Prog-EventBased example provided with the Yoctopuce Python library. We register the callback functions which are called each time a module is connected and each time the value of a function changes. If you have never used the RegisterDeviceArrivalCallback() or registerValueCallback(), we strongly advise you to read the the post describing how to use callbacks with the Yoctopuce API.

To send the commands ("up", "down", and so on) to XBMC each time a button is pushed, we must call the corresponding method of our xbmc_interface object from the an_button_callback() function.

The main function is made of an endless loop calling YAPI.UpdateDeviceList() and YAPI.Sleep(). In this endless loop, we retrieve the title with a call to xbmc_interface.get_info_to_display(), and we refresh the screen with this new information. So the main function performs the following tasks in a loop:

  1. displays on the screen the title of the movie
  2. checks whether we must call connection/disconnection callbacks
  3. checks during 1 second whether we must call value change callbacks


The screen is refreshed only once per second but, for this type of application, it is quite enough.

This script works therefore both in "polling" and in "callback" modes. Managing buttons is done in "callback" while screen refresh is done in "polling".

def an_button_callback(anbutton, value):
    if (anbutton.get_isPressed() == YAnButton.ISPRESSED_TRUE):
        last = anbutton.get_userData()
        if last == YAnButton.ISPRESSED_FALSE:
            funcid = anbutton.get_functionId()
            if funcid == 'anButton1':
                xbmc_interface.up()
            elif funcid == 'anButton2':
                xbmc_interface.down()
            elif funcid == 'anButton3':
                xbmc_interface.left()
            elif funcid == 'anButton4':
                xbmc_interface.right()
            elif funcid == 'anButton5':
                xbmc_interface.ok()
            elif funcid == 'anButton6':
                xbmc_interface.back()
    anbutton.set_userData(anbutton.get_isPressed())

....

def main():
    errmsg = YRefParam()
    YAPI.RegisterDeviceArrivalCallback(device_arrival)
    YAPI.RegisterDeviceRemovalCallback(device_removal)
    if YAPI.RegisterHub("usb", errmsg) < 0:
        print("Unable register usb :" + str(errmsg))
        return -1

    while True:
           progress, title = xbmc_interface.get_info_to_display()
        for display in display_list:
            w = display.get_displayWidth()
            h = display.get_displayHeight()
            layer0 = display.get_displayLayer(0)
            layer0.selectGrayPen(0)
            layer0.drawBar(0, 0, w - 1, h - 1)
            layer0.selectGrayPen(255)
            layer0.drawText(w / 2, h / 2, YDisplayLayer.ALIGN.CENTER, title)
            if progress > 0:
                layer0.drawBar(0, h - 1, int(progress * w / 100), h - 1)
            display.swapLayerContent(0, 1)
        YAPI.UpdateDeviceList()
        YAPI.Sleep(1000)



The complete source code is available on GitHub at the http://github.com/yoctopuce-examples/xbmc_remote address.

Conclusion


  
And here is the proof that it works :-)



In less than 200 lines, we have implemented an XBMC control panel. And as the Yoctopuce library doesn't require drivers, it works from the outset on any OS (Windows, Linux, and OS X). Dead easy :-)




1 - eti.que Tuesday,august 23,2016 14H58

Hello!

I bought a MaxiDisplay with the hope of plugging it to a KODI box. I used your script, and it works, and I'm pleased :)

Just wondering, could you have some direction on how to make this compatible with LCDProc?

Thanks!

2 - mvuilleu (Yocto-Team)Tuesday,august 23,2016 16H25

@eti.que: interesting idea... You should download lcdproc-0.5.7-dev and create a new driver module.

Since our API is available with full source code, it should not be too difficult to wrap it using the entry points defined in lcd_logical_driver structure. A few hints:
- LCDproc driver API appears to be vanilla C, not C++, but Yoctopuce API uses C++. You should therefore make your driver in C++, but use an "export C" statement to make your driver usable from LCDproc.
- the LCDproc driver API appears to be text-based, so you will have to used the fixed font and multiply input coordinates by the character width/height since Yoctopuce API is in pixels, not characters

This being said, I don't know exactly what you intend to use the screen for, but unless you want to display very complex metrics, I suspect it might be easier to rewrite the client part (that computes the value to display) and write it directly to the Yocto-Display rather than making a driver for LCDproc.

Yoctopuce, get your stuff connected.