~martijnbraam/pyatem

d2dc828df01451fe3677dd31765ebe8fac3d4b56 — Jan Kundrát 2 months ago caf4092
proxy: mqtt: allow arbitrary writes

Since there are typically no server-side MQTT ACLs, this has to be
enabled explicitly.

There's some duplication of code between this implementation and the one
in the HTTP backend. The most straightforward way of fixing that is to
convert these early returns into proper exceptions, but I'm too lazy for
this and I'm gonna be using this feature tomorrow :).

Signed-off-by: Jan Kundrát <jkt@jankundrat.com>
2 files changed, 40 insertions(+), 4 deletions(-)

M openswitcher_proxy/frontend_mqtt.py
M proxy.toml
M openswitcher_proxy/frontend_mqtt.py => openswitcher_proxy/frontend_mqtt.py +39 -4
@@ 5,6 5,7 @@ from functools import partial

from .error import DependencyError
from .frontend_httpapi import FieldEncoder
import pyatem.command as commandmodule

try:
    import paho.mqtt.client as mqtt


@@ 74,12 75,46 @@ class MqttFrontendThread(threading.Thread):
    def on_mqtt_connect(self, flags, rc):
        self.status = 'running'
        logging.info(f'MQTT: connected ({rc})')
        # TODO: enable once on_mqtt_message() works
        # client.subscribe(f'atem/{userdata.hw_name}/#')
        self.client.subscribe(f'atem/+/set/#')

    def on_mqtt_message(self, msg):
        # TODO: propagate to the switcher, eventually
        logging.debug(f'MQTT: msg: {msg.topic} {msg.payload}')
        if not self.config.get('allow-writes', False):
            logging.error('MQTT writes disabled')
            return
        parts = msg.topic.split('/')
        if len(parts) != 4:
            logging.error(f'MQTT: malformed command topic: {msg.topic}')
            return
        hw = parts[1]
        if parts[0] != 'atem' or parts[2] != 'set':
            logging.error(f'MQTT: malformed command topic: {msg.topic}')
            return
        if hw not in self.hw_name:
            logging.error(f'MQTT: not handling writes for "{hw}"')
            return
        fieldname = parts[3]
        classname = fieldname.title().replace('-', '') + "Command"
        if not hasattr(commandmodule, classname):
            logging.error(f'MQTT: unrecognized command {fieldname}')
            return
        arguments = json.loads(msg.payload)
        if not isinstance(arguments, dict):
            logging.error(f'MQTT: mailformed payload, needs a JSON dict')
            return
        for key in arguments:
            try:
                arguments[key] = int(arguments[key])
            except:
                pass
        if 'source' in arguments:
            inputs = self.threadlist['hardware'][hw].switcher.inputs
            if arguments['source'] in inputs:
                arguments['source'] = inputs[arguments['source']]
        try:
            cmd = getattr(commandmodule, classname)(**arguments)
            self.threadlist['hardware'][hw].switcher.send_commands([cmd])
        except Exception as e:
            logging.error(f'MQTT: cannot write {fieldname}: {str(e)}')

    def get_status(self):
        if self.status == 'error':

M proxy.toml => proxy.toml +1 -0
@@ 36,3 36,4 @@ type = "mqtt"
host = "localhost:1883"
hardware = "mini"
auth = false
allow-writes = false