openHAB Command Proxy for example QR Codes

Hi,

I honestly didn’t know how to name this title exactly. So I’ll just explain the problem directly. I encounter this use case with QR codes, but the program can be used independently.

The problem

If I access openHAB via the REST API, then I can only perform a postUpdate via PUT and a sendCommand via POST. If I scan a QR code and you recognize a URL that points to an item via the REST API, for example, then I cannot operate this from the smartphone. I would open a URL in the browser. But that would be a GET request. What I do here is that I build my own REST API via Flask that takes a GET request and then forwards it to openHAB as a POST request so that a sendCommand is executed.

The solution

A Jython script with Flask that provides a REST API for commands to an openHAB item, so that you can execute a sendCommand via GET request.

The openHAB REST API would only allow you to perform a sendCommand with a POST request or a postUpdate with a PUT request. Using this proxy, you can also perform a sendCommand to an item via a GET request, which is e.g. calling up a URL in the browser. In Examples you can see that this can be useful, for example, for scanning a QR code.

This is a further development of openHAB-Command-Proxy. This is an external Python script instead of a Jython rule, which then accesses openHAB items through the REST API. The optimization of the Jython script or the rule is that I can avoid REST requests and directly perform my sendCommand action.

Installation

At first you have to install following dependencies:

sudo -u openhab python3 -m pip install flask --user

Then you can download the python script with:

git clone https://github.com/Michdo93/openHAB-Command-Proxy-Jython.git $OH_CONF/automation/jsr223/python/community/openHAB-Command-Proxy-Jython

Before you run the script, please make it executable:

sudo chown -R openhab:openhab $OH_CONF/automation/jsr223/python/community/openHAB-Command-Proxy-Jython/proxy.py
sudo chmod +x $OH_CONF/automation/jsr223/python/community/openHAB-Command-Proxy-Jython/proxy.py

At least you have to restart openhab:

sudo systemctl restart openhab

Customization

You can change the port in following line (by default it is 12345):

openHABCommandProxy = OpenHABCommandProxy("0.0.0.0", 12345)

You do not need to change "0.0.0.0" as openHAB will run under the same IP address.

Example Use Case

A simple use case would be to scan QR codes that can be used to operate devices. A commercially available QR code scanner suggests that if a URL is recognised, it can be opened in the browser. However, in order to perform a sendCommand on an item in openHAB, a POST request must be performed, whereas opening a URL in the browser is only a GET request.

QR 1 QR 2 QR 3

Keep in mind that you have to use a different port for the QR code than for openHAB. If you have not configured a port for openHAB, it will use the default port 8080. If the QR code were to contain the port 8080 accordingly, the openHAB server would report an error and not execute a sendCommand, as this is not possible with a GET request.

The Code

The code looks like this:

import time
import core
from core.rules import rule
from core.triggers import when
from flask import Flask, request

class OpenHABCommandProxy:
    def __init__(self, ip_address, port):
        self.ip_address = ip_address
        self.port = port
        self.app = Flask(__name__)
        self.register_routes()

    def register_routes(self):
        self.app.route('/rest/items/<item_name>', methods=['GET'])(self.handle_item_command)

    def handle_item_command(self, item_name):
        command = request.args.get('data')

        try:
            events.sendCommand(item_name, command)
            time.sleep(1)
            return 'Command sent successfully', 200
        except:
            return 'Error: Failed to send command', 500

    def run(self):
        self.app.run(host=self.ip_address, port=self.port)

@rule("Flask API")
@when("System started")
def start_flask_api(event):
    openHABCommandProxy = OpenHABCommandProxy("0.0.0.0", 12345)
    openHABCommandProxy.run()

Evaluation

Advantages

  • You don’t need a custom app to scan QR codes, any QR code scanner will do.
  • You can also enter the URL directly in a browser to execute a command (I usually do this via the Karaf console when I want to test something).
  • You can access openHAB very easily in software solutions.

Disadvantages

  • It is quite clear that there is no need to discuss safety here. Everything that would otherwise secure the REST API is virtually undermined. The only restriction is that it must refer to items and that, thank God, a command is also expected as a parameter.
  • You need a separate QR code for each item. This would mean e.g. for a Philips Hue lamp with its 6 items then also 6 QR codes minimum. If you now think of the command ON and the command OFF for a switch, then you can either toggle this or would actually need two QR codes. With color lamps, you can still create a lot of QR codes for color values. Also not very clever.

Outlook and conclusion

I have already started writing my own QR code scanner. For each device, I use a QR code for the item’s group. I also have an algorithm that processes this JSON object and generates my UI elements in Android. Then, I wouldn’t need multiple QR codes for my devices; just one would suffice. However, this is no longer related to this Command Proxy.

The clever ones among us have already noticed something: What? Converting a JSON object to a group and then generating UI elements. It’s quite impractical! And they are right.

Instead of building a custom solution for displaying UI elements, one can access the Sitemap. There would be no need to query items either. Instead, you could develop an algorithm that generates the URL for this group. This way, you wouldn’t require a separate app as a QR code scanner for openHAB; a regular QR code scanner could be used again. It would then open the browser with that URL, and you can interact with the standard Sitemap.

Looking ahead, we are planning two projects for Augmented Reality and Android. Firstly, using ML (Machine Learning) through Google’s ML Kit to recognize the device in Android. Secondly, with Unity and Vuforia, where Image Targets, Multiple Targets, and Area Targets would be utilized. Additionally, there might be further integrations for localization to enhance its functionality.

My conclusion is: It was a nice and very quick attempt (around 10 minutes of work). However, there are much more elegant, better, and more practical solutions that can be used instead, and they can also be made more secure. Nevertheless, I must say that I really like the idea of integrating Flask into openHAB. Another attempt will be to fully clone the REST API using Flask, considering username/password or token authentication, as well as handling headers. This is because in openHAB 3, despite configuring CORS, it cannot be allowed (at least in my experience).

There is an unofficial webhook binding which might be an easier overall approach to running a separate proxy service.

If you have BasicUI installed, you can also use the way it works to send commands to Items through a GET request, but I wouldn’t rely on that as it unofficial, not supported, and could go away without warning.

I think all your same advantages and disadvantages apply, only perhaps it not quite as undermining of the REST API security since the API security token or username/password doesn’t need to be exposed externally to OH.

I wonder if it would make sense for the Android/iOS app to include a QR code scanner. That’s not all that different from the NFC use case really. Though it probably wouldn’t even need to be a built in scanner so much as an application URL that tells the phone to open the app and that tells the app what command to send to which Item.