#!/usr/bin/env python # -*- coding: utf-8 -*- # from base import mqtt_base from base import videv_base import json import time def is_json(data): try: json.loads(data) except json.decoder.JSONDecodeError: return False else: return True class base(mqtt_base): TX_TOPIC = "set" TX_VALUE = 0 TX_DICT = 1 TX_TYPE = -1 TX_FILTER_DATA_KEYS = [] # RX_KEYS = [] RX_IGNORE_TOPICS = [] RX_IGNORE_KEYS = [] RX_FILTER_DATA_KEYS = [] # CFG_DATA = {} def __init__(self, mqtt_client, topic): super().__init__(mqtt_client, topic, default_values=dict.fromkeys(self.RX_KEYS)) # data storage self.__cfg_by_mid__ = None # initialisations mqtt_client.add_callback(topic=self.topic, callback=self.receive_callback) mqtt_client.add_callback(topic=self.topic+"/#", callback=self.receive_callback) # self.add_callback(None, None, self.__state_logging__, on_change_only=True) def __cfg_callback__(self, key, value, mid): if self.CFG_DATA.get(key) != value and self.__cfg_by_mid__ != mid and mid is not None: self.__cfg_by_mid__ = mid self.logger.warning("Differing configuration identified: Sending default configuration to defice: %s", repr(self.CFG_DATA)) if self.TX_TYPE == self.TX_DICT: self.mqtt_client.send(self.topic + '/' + self.TX_TOPIC, json.dumps(self.CFG_DATA)) else: for key in self.CFG_DATA: self.send_command(key, self.CFG_DATA.get(key)) def set(self, key, data, mid=None, block_callback=[]): if key in self.CFG_DATA: self.__cfg_callback__(key, data, mid) if key in self.RX_IGNORE_KEYS: pass # ignore these keys elif key in self.RX_KEYS: return super().set(key, data, block_callback) else: self.logger.warning("Unexpected key %s", key) def receive_callback(self, client, userdata, message): if message.topic != self.topic + '/' + videv_base.KEY_INFO: content_key = message.topic[len(self.topic) + 1:] if content_key not in self.RX_IGNORE_TOPICS and (not message.topic.endswith(self.TX_TOPIC) or len(self.TX_TOPIC) == 0): self.logger.debug("Unpacking content_key \"%s\" from message.", content_key) if is_json(message.payload): data = json.loads(message.payload) if type(data) is dict: for key in data: self.set(key, self.__device_to_instance_filter__(key, data[key]), message.mid) else: self.set(content_key, self.__device_to_instance_filter__(content_key, data), message.mid) # String else: self.set(content_key, self.__device_to_instance_filter__(content_key, message.payload.decode('utf-8')), message.mid) else: self.logger.debug("Ignoring topic %s", content_key) def __device_to_instance_filter__(self, key, data): if key in self.RX_FILTER_DATA_KEYS: if data in [1, 'on', 'ON']: return True elif data in [0, 'off', 'OFF']: return False return data def __instance_to_device_filter__(self, key, data): if key in self.TX_FILTER_DATA_KEYS: if data is True: return "on" elif data is False: return "off" return data def send_command(self, key, data): data = self.__instance_to_device_filter__(key, data) if self.TX_TOPIC is not None: if self.TX_TYPE < 0: self.logger.error("Unknown tx type. Set TX_TYPE of class to a known value") else: self.logger.debug("Sending data for %s - %s", key, str(data)) if self.TX_TYPE == self.TX_DICT: try: self.mqtt_client.send('/'.join([self.topic, self.TX_TOPIC]), json.dumps({key: data})) except TypeError: print(self.topic) print(key.__dict__) print(key) print(data) raise TypeError else: if type(data) not in [str, bytes]: data = json.dumps(data) self.mqtt_client.send('/'.join([self.topic, key, self.TX_TOPIC] if len(self.TX_TOPIC) > 0 else [self.topic, key]), data) else: self.logger.error("Unknown tx toptic. Set TX_TOPIC of class to a known value")