2023-02-22 07:34:06 +01:00
|
|
|
import json
|
2023-01-20 08:03:06 +01:00
|
|
|
import logging
|
2023-05-07 17:49:46 +02:00
|
|
|
import task
|
2023-01-20 08:03:06 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
from config import APP_NAME as ROOT_LOGGER_NAME
|
|
|
|
except ImportError:
|
|
|
|
ROOT_LOGGER_NAME = 'root'
|
|
|
|
|
|
|
|
|
|
|
|
class common_base(dict):
|
|
|
|
DEFAULT_VALUES = {}
|
|
|
|
|
2023-01-25 07:40:33 +01:00
|
|
|
def __init__(self, default_values=None):
|
|
|
|
super().__init__(default_values or self.DEFAULT_VALUES)
|
2023-01-20 08:03:06 +01:00
|
|
|
#
|
|
|
|
self.__callback_list__ = []
|
2023-02-10 10:37:35 +01:00
|
|
|
self.logger = logging.getLogger(ROOT_LOGGER_NAME).getChild("devices")
|
2023-01-20 08:03:06 +01:00
|
|
|
|
2023-05-07 17:49:46 +02:00
|
|
|
def add_callback(self, key, data, callback, on_change_only=False, init_now=False):
|
2023-01-20 08:03:06 +01:00
|
|
|
"""
|
|
|
|
key: key or None for all keys
|
|
|
|
data: data or None for all data
|
|
|
|
"""
|
|
|
|
cb_tup = (key, data, callback, on_change_only)
|
|
|
|
if cb_tup not in self.__callback_list__:
|
|
|
|
self.__callback_list__.append(cb_tup)
|
2023-05-07 17:49:46 +02:00
|
|
|
if init_now and self.get(key) is not None:
|
|
|
|
callback(self, key, self[key])
|
2023-01-20 08:03:06 +01:00
|
|
|
|
2023-01-25 07:40:33 +01:00
|
|
|
def set(self, key, data, block_callback=[]):
|
2023-02-10 10:37:35 +01:00
|
|
|
if key in self.keys():
|
|
|
|
value_changed = self[key] != data
|
|
|
|
self[key] = data
|
|
|
|
for cb_key, cb_data, callback, on_change_only in self.__callback_list__:
|
|
|
|
if cb_key is None or key == cb_key: # key fits to callback definition
|
|
|
|
if cb_data is None or cb_data == self[key]: # data fits to callback definition
|
|
|
|
if value_changed or not on_change_only: # change status fits to callback definition
|
|
|
|
if not callback in block_callback: # block given callbacks
|
|
|
|
callback(self, key, self[key])
|
|
|
|
else:
|
|
|
|
self.logger.warning("Unexpected key %s", key)
|
2023-01-20 08:03:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
class mqtt_base(common_base):
|
2023-01-25 07:40:33 +01:00
|
|
|
def __init__(self, mqtt_client, topic, default_values=None):
|
|
|
|
super().__init__(default_values)
|
2023-01-20 08:03:06 +01:00
|
|
|
#
|
|
|
|
self.mqtt_client = mqtt_client
|
|
|
|
self.topic = topic
|
|
|
|
for entry in self.topic.split('/'):
|
|
|
|
self.logger = self.logger.getChild(entry)
|
2023-02-22 07:34:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
class videv_base(mqtt_base):
|
|
|
|
KEY_INFO = '__info__'
|
|
|
|
#
|
|
|
|
SET_TOPIC = "set"
|
|
|
|
|
|
|
|
def __init__(self, mqtt_client, topic, default_values=None):
|
|
|
|
super().__init__(mqtt_client, topic, default_values=default_values)
|
|
|
|
self.__display_dict__ = {}
|
|
|
|
self.__control_dict__ = {}
|
2023-05-07 17:49:46 +02:00
|
|
|
self.__periodic__ = task.periodic(300, self.send_all)
|
|
|
|
self.__periodic__.run()
|
|
|
|
|
|
|
|
def send_all(self, rt):
|
|
|
|
for key in self:
|
|
|
|
if self[key] is not None:
|
|
|
|
self.__tx__(key, self[key])
|
2023-02-22 07:34:06 +01:00
|
|
|
|
|
|
|
def add_display(self, my_key, ext_device, ext_key, on_change_only=True):
|
|
|
|
"""
|
|
|
|
listen to data changes of ext_device and update videv information
|
|
|
|
"""
|
|
|
|
if my_key not in self.keys():
|
|
|
|
self[my_key] = None
|
|
|
|
if ext_device.__class__.__name__ == "group":
|
|
|
|
# store information to identify callback from ext_device
|
|
|
|
self.__display_dict__[(id(ext_device[0]), ext_key)] = my_key
|
|
|
|
# register a callback to listen for data from external device
|
2023-05-07 17:49:46 +02:00
|
|
|
ext_device[0].add_callback(ext_key, None, self.__rx_ext_device_data__, on_change_only, init_now=True)
|
2023-02-22 07:34:06 +01:00
|
|
|
else:
|
|
|
|
# store information to identify callback from ext_device
|
|
|
|
self.__display_dict__[(id(ext_device), ext_key)] = my_key
|
|
|
|
# register a callback to listen for data from external device
|
2023-05-07 17:49:46 +02:00
|
|
|
ext_device.add_callback(ext_key, None, self.__rx_ext_device_data__, on_change_only, init_now=True)
|
2023-03-25 19:23:00 +01:00
|
|
|
# send initial display data to videv interface
|
2023-03-26 11:00:18 +02:00
|
|
|
data = ext_device.get(ext_key)
|
|
|
|
if data is not None:
|
|
|
|
self.__tx__(my_key, data)
|
2023-02-22 07:34:06 +01:00
|
|
|
|
|
|
|
def __rx_ext_device_data__(self, ext_device, ext_key, data):
|
|
|
|
my_key = self.__display_dict__[(id(ext_device), ext_key)]
|
|
|
|
self.set(my_key, data)
|
|
|
|
self.__tx__(my_key, data)
|
|
|
|
|
|
|
|
def __tx__(self, key, data):
|
|
|
|
if type(data) not in (str, ):
|
|
|
|
data = json.dumps(data)
|
|
|
|
self.mqtt_client.send('/'.join([self.topic, key]), data)
|
|
|
|
|
|
|
|
def add_control(self, my_key, ext_device, ext_key, on_change_only=True):
|
|
|
|
"""
|
|
|
|
listen to videv information and pass data to ext_device
|
|
|
|
"""
|
|
|
|
if my_key not in self.keys():
|
|
|
|
self[my_key] = None
|
|
|
|
# store information to identify callback from videv
|
|
|
|
self.__control_dict__[my_key] = (ext_device, ext_key, on_change_only)
|
|
|
|
# add callback for videv changes
|
|
|
|
self.mqtt_client.add_callback('/'.join([self.topic, my_key, self.SET_TOPIC]), self.__rx_videv_data__)
|
|
|
|
|
|
|
|
def __rx_videv_data__(self, client, userdata, message):
|
|
|
|
my_key = message.topic.split('/')[-2]
|
|
|
|
try:
|
|
|
|
data = json.loads(message.payload)
|
|
|
|
except json.decoder.JSONDecodeError:
|
|
|
|
data = message.payload
|
|
|
|
ext_device, ext_key, on_change_only = self.__control_dict__[my_key]
|
|
|
|
if my_key in self.keys():
|
|
|
|
if data != self[my_key] or not on_change_only:
|
|
|
|
ext_device.send_command(ext_key, data)
|
|
|
|
else:
|
|
|
|
self.logger.info("Ignoring rx message with topic %s", message.topic)
|
|
|
|
|
|
|
|
def add_routing(self, my_key, ext_device, ext_key, on_change_only_disp=True, on_change_only_videv=True):
|
|
|
|
"""
|
|
|
|
listen to data changes of ext_device and update videv information
|
|
|
|
and
|
|
|
|
listen to videv information and pass data to ext_device
|
|
|
|
"""
|
|
|
|
# add display
|
|
|
|
self.add_display(my_key, ext_device, ext_key, on_change_only_disp)
|
|
|
|
self.add_control(my_key, ext_device, ext_key, on_change_only_videv)
|