Table of Contents

Adding IoT Capabilities

Lakka is an emulation environment designed to run console and arcade emulators in a kiosk-style setup. The underlying operating system for Lakka is a Linux distribution named LibreELEC that uses read-only access to the filesystem via squashfs. The following is a quick and possibly reliable method to add IoT capabilities via python and subscrbing to an MQTT topic.

Installing Requirements

pip can be installed with:

wget -c https://bootstrap.pypa.io/get-pip.py

and then by executing:

python get-pip.py

which will perform a local install of pip.

Next, the default PATH must be adjusted to take the local pyton binaries into account. This is done by editing .profile and adding:

#!/usr/bin/sh
 
export PATH=~/.local/bin:/usr/bin:/usr/sbin

and then saving the file.

A re-log is necessary to take the new path into account.

After logging in, install paho MQTT with:

pip install paho-mqtt

which will create a local install of paho MQTT.

Adding the Script

The file from the code section (mqttSwitch.py) should be placed at /storage/system/mqttSwitch/mqttSwitch.py after which the file should be made executable, for instance, by issuing chmod +x /storage/system/mqttSwitch/mqttSwitch.py. The file /storage/.config/autostart.sh should either be created or edited in order to add the following line for mqttSwitch.py:

/usr/bin/python /storage/system/mqttSwitch/mqttSwitch.py --daemon

Next, a configuration file should be created at /storage/system/mqttSwitch/config.yml with the following contents:

config.yml
pid: '/var/run/mqttSwtich.pid'

mqtt:
    host: 192.168.1.1
    port: 1883
    topic: 'arcade-engine/#'

The configuration file config.yml should be adjusted to make host and port point towards the MQTT server and to amend the MQTT topic if necessary.

Remotely Turning off Lakka

With the mqttSwitch.py script running, Lakka can now be turned off by sending the message shutdown to the MQTT topic.

Code

mqttSwtich.py
#!/usr/bin/env python
###########################################################################
##  Copyright (C) Wizardry and Steamworks 2019 - License: GNU GPLv3      ##
###########################################################################
 
import yaml, subprocess, os, argparse, signal, sys
import paho.mqtt.client as mqtt
from os import path
from daemonize import Daemonize
 
with open(os.path.join(os.path.dirname(sys.argv[0]), 'config.yml'), 'r') as ymlfile:
    cfg = yaml.load(ymlfile, Loader=yaml.FullLoader)
 
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print 'Connected with result code: ', str(rc)
 
    # Subscribing in on_connect() means that if we lose the connection and
    # reconnect then subscriptions will be renewed.
    #client.subscribe("$SYS/#")
    client.subscribe('arcade-engine/#')
 
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    if str(msg.payload) == 'shutdown':
        os.system('systemctl poweroff')
 
def main():
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
 
    client.connect(cfg['mqtt']['host'], cfg['mqtt']['port'], 60)
 
    # Blocking call that processes network traffic, dispatches callbacks and
    # handles reconnecting.
    # Other loop*() functions are available that give a threaded interface and a
    # manual interface.
    client.loop_forever()
 
def daemon():
    try:
        if path.exists(cfg['pid']):
            with open(cfg['pid'], 'r') as pid_file:
                pid = pid_file.read()
                os.kill(int(pid), signal.SIGHUP)
            os.remove(cfg['pid'])
    except Exception, e:
        print 'Unable to kill previous process: ', str(e)
        return
 
    daemon = Daemonize(app='mqttSwitch', pid=cfg['pid'], action=main)
    daemon.start()
 
parser = argparse.ArgumentParser()
parser.add_argument('--daemon', action='store_true', help='run as a daemon')
parser.add_argument('--foreground', action='store_true', help='run as a daemon')
args = parser.parse_args()
 
if(args.daemon):
    daemon()
elif(args.foreground):
    main()
 
parser.print_help()

Determining Available LibRetro Core Versions

On Lakka, terse information about the available cores can be retrieved by browsing to /tmp/cores/ where all the cores are available as shared libraries (.so) along with an information file that describes the core along with its version (.info).

Upgrading to Non-Compatible Image

Create the file /storage/.update/.nocompat:

touch /storage/.update/.nocompat

in order to upgrade to a non-comaptible image for Lakka.

Upgrading to non-compatible images is sometimes useful when changing different architecture versions of Lakka that are compatible with the hardware. For instance, upgrading from ARM to AARCH.