#!/usr/bin/python3 import paho.mqtt.client as mqtt from dbHelper import MaDbHelper from pairingHandler import MaPairingHandler import logging, coloredlogs, os, sys, random, string coloredlogs.install(level='INFO', fmt='%(asctime)s - [%(levelname)s] %(message)s') class MlmAccess: def __init__(self): logging.info("=== MlmAccess ===") self._config = self._readConfig() self._initDb() self._initMqtt() self._pairingHanlder = MaPairingHandler(self._mqtt, self._db) self._mqtt.loop_forever() def _initDb(self): logging.info("Initializing Database") self._db = MaDbHelper(self._config) initUsers = [ ("backend", self._config["MQTT_BACKEND_PASSWORD"], ["mlmAccess/#"], ["mlmAccess/#"]), ("pair", self._config["PAIR_SECRET"], ["mlmAccess/pair/request/#"], ["mlmAccess/pair/response/#"]) ] for username, password, publishAclPatterns, subscribeAclPatterns in initUsers: if not self._db.addUser(username, password, publishAclPatterns, subscribeAclPatterns): self._db.updateUser(username, password, publishAclPatterns, subscribeAclPatterns) obsoleteSystemUsers = ["pair-actor"] for username in obsoleteSystemUsers: self._db.deleteUser(username) def _initMqtt(self): logging.info("Initializing MQTT") self._mqtt = client = mqtt.Client(client_id="backend") self._mqtt.username_pw_set("backend", self._config["MQTT_BACKEND_PASSWORD"]) self._mqtt.on_connect = self._mqttOnConnect self._mqtt.on_message = self._mqttOnMessage self._mqtt.connect(self._config["MQTT_HOST"], self._config["MQTT_PORT"], 60) def _mqttOnConnect(self, client, userdata, flags, rc): if rc != 0: logging.error(f"Error connecting to MQTT broker: {rc}") return logging.info("Successfully connected to MQTT broker") logging.info("Subscribing to actor subjects:") for user in self._db.getAllUsers(): if user.startswith("actor-"): actorSubject = f"mlmAccess/actor/{user.replace('actor-', '')}/status" elif user.startswith("panel-"): actorSubject = "mlmAccess/panel" else: continue logging.info(f"* {actorSubject}") client.subscribe(actorSubject) # call hooks of child objects self._pairingHanlder._mqttOnConnect(client, userdata, flags, rc) def _mqttOnMessage(self, client, userdata, message): topic = message.topic topic = topic.replace("mlmAccess/", "") messageContent = str(message.payload.decode("utf-8")) logging.info(f"Message on topic {topic}: {messageContent}") if topic.startswith("pair"): self._pairingHanlder.handlePairingRequest(topic, messageContent) def _readConfig(self): configKeyPrefix = "MLMACCESS_" requiredConfigKeys = [ 'MYSQL_USER', 'MYSQL_PASSWORD', 'MYSQL_DATABASE', 'PAIR_SECRET' ] allowedConfigKeys = [ 'MYSQL_HOST', 'MYSQL_PORT', 'MQTT_HOST', 'MQTT_PORT', ] backendPassword = ''.join(random.choices(string.ascii_uppercase + string.ascii_lowercase + string.digits, k=20)) config = { "MYSQL_HOST": "mysql", "MYSQL_PORT": 4306, "MQTT_HOST": "mqtt", "MQTT_PORT": 1883, "MQTT_BACKEND_PASSWORD": backendPassword } for configKey in requiredConfigKeys: if configKeyPrefix + configKey not in os.environ: logging.error(f"Required environment value {configKeyPrefix + configKey} is not set") sys.exit(1) config[configKey] = os.environ[configKeyPrefix + configKey] for configKey in allowedConfigKeys: if configKeyPrefix + configKey in os.environ: config[configKey] = os.environ[configKeyPrefix + configKey] logging.info("CONFIG:") for key, value in config.items(): logging.info(" * {:25}: {}".format(key, value)) return config if __name__ == "__main__": MlmAccess()