#!/usr/bin/env python # -*- coding: utf-8 -*- # """ Virtual Device(s) Targets: * MQTT-Interface to control joined devices as one virtual device. * Primary signal routing * No functionality should be implemented here """ # TODO: Extend virtual devices # * Digital-Audio-Sources (Spotify, MPD, Currently playing) oder direkt von my_apps?! # * from base import mqtt_base import devices import json BASETOPIC = "videv" try: from config import APP_NAME as ROOT_LOGGER_NAME except ImportError: ROOT_LOGGER_NAME = 'root' class base(mqtt_base): EXEC_RX_FUNC_ALWAYS = [] def __init__(self, mqtt_client, topic, *args, default_values=None): super().__init__(mqtt_client, topic, default_values=default_values) self.__device_list__ = {} for videv_key, device in [reduced[:2] for reduced in args]: self.__device_list__[videv_key] = device # send initial state for key in self.keys(): self.__tx__(key, self[key]) # add receive topics mqtt_client.add_callback(self.topic + "/#", self.__rx__) def __tx__(self, key, data): if type(data) not in (str, ): data = json.dumps(data) if key in self.keys(): self.mqtt_client.send(self.topic + '/' + key, data) else: self.logger.warning("Ignoring send request for key %s (not available for this class)", key) def __rx__(self, client, userdata, message): key = message.topic.split('/')[-1] if key in self.keys(): try: data = json.loads(message.payload) except json.decoder.JSONDecodeError: data = message.payload if data != self[key] or key in self.EXEC_RX_FUNC_ALWAYS: self.__rx_functionality__(key, data) self.set(key, data) else: self.logger.info("Ignoring rx message with topic %s", message.topic) def __rx_functionality__(self, key, data): raise NotImplemented("Method __rx_functionality__ needs to be implemented in child class") def __device_data__(self, device, key, data): raise NotImplemented("Method __device_data__ needs to be implemented in child class") class base_routing(base): def __init__(self, mqtt_client, topic, *args, default_values=None): super().__init__(mqtt_client, topic, *args, default_values=default_values) # self.__device_key__ = {} index = 0 for videv_key, device, device_key in args: if self.__device_list__[videv_key] != device: raise ReferenceError("Parent class generated a deviating device list") self.__device_key__[videv_key] = device_key index += 1 # add callbacks for key in self.__device_list__: if self.__device_list__[key].__class__.__name__ == "group": self.__device_list__[key][0].add_callback(self.__device_key__[key], None, self.__device_data__, True) else: self.__device_list__[key].add_callback(self.__device_key__[key], None, self.__device_data__, True) def __rx_functionality__(self, key, data): try: self.__device_list__[key].set(self.__device_key__[key], data) except KeyError: self.logger.warning("RX passthrough not possible for key %s", key) def __device_data__(self, device, key, data): l1 = [] for k, v in self.__device_list__.items(): if v.__class__.__name__ == "group": if id(device) in [id(d) for d in v]: l1.append(k) else: if id(v) == id(device): l1.append(k) l2 = [k for k, v in self.__device_key__.items() if v == key] try: videv_key = [k for k in l1 if k in l2][0] except IndexError: self.logger.warning("videv_key not available for %s::%s", device.__class__.__name__, device.topic) else: self.set(videv_key, data) self.__tx__(videv_key, data) class videv_switching(base_routing): KEY_STATE = 'state' # DEFAULT_VALUES = { KEY_STATE: False, } def __init__(self, mqtt_client, topic, sw_device, sw_key): super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key)) class videv_switching_timer(base_routing): KEY_STATE = 'state' KEY_TIMER = 'timer' # DEFAULT_VALUES = { KEY_STATE: False, KEY_TIMER: 0 } def __init__(self, mqtt_client, topic, sw_device, sw_key, tm_device, tm_key): super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_TIMER, tm_device, tm_key)) class videv_switching_motion(base_routing): KEY_STATE = 'state' KEY_TIMER = 'timer' KEY_MOTION_SENSOR = 'motion_%d' # DEFAULT_VALUES = { KEY_STATE: False, KEY_TIMER: 0 } def __init__(self, mqtt_client, topic, sw_device, sw_key, motion_function): dv = {self.KEY_STATE: False, self.KEY_TIMER: 0} for i in range(0, len(motion_function.args)): dv[motion_function.KEY_MOTION_SENSOR % i] = False super().__init__( mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_TIMER, motion_function, motion_function.KEY_TIMER), *[[self.KEY_MOTION_SENSOR % i, motion_function, motion_function.KEY_MOTION_SENSOR % i] for i in range(0, len(motion_function.args))], default_values=dv ) class videv_switch_brightness(base_routing): KEY_STATE = 'state' KEY_BRIGHTNESS = 'brightness' # DEFAULT_VALUES = { KEY_STATE: False, KEY_BRIGHTNESS: 0 } def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key): # super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_BRIGHTNESS, br_device, br_key)) class videv_switch_brightness_color_temp(base_routing): KEY_STATE = 'state' KEY_BRIGHTNESS = 'brightness' KEY_COLOR_TEMP = 'color_temp' # DEFAULT_VALUES = { KEY_STATE: False, KEY_BRIGHTNESS: 0, KEY_COLOR_TEMP: 0, } def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key, ct_device, ct_key): # super().__init__( mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_BRIGHTNESS, br_device, br_key), (self.KEY_COLOR_TEMP, ct_device, ct_key) ) class videv_heating(base_routing): KEY_TEMPERATURE = 'temperature' KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint' KEY_VALVE_TEMPERATURE_SETPOINT = 'valve_temperature_setpoint' KEY_AWAY_MODE = 'away_mode' KEY_SUMMER_MODE = 'summer_mode' KEY_START_BOOST = 'start_boost' KEY_SET_DEFAULT_TEMPERATURE = 'set_default_temperature' KEY_BOOST_TIMER = 'boost_timer' # EXEC_RX_FUNC_ALWAYS = [KEY_START_BOOST, KEY_SET_DEFAULT_TEMPERATURE, KEY_USER_TEMPERATURE_SETPOINT] def __init__(self, mqtt_client, topic, heating_function): # super().__init__( mqtt_client, topic, (self.KEY_TEMPERATURE, heating_function, heating_function.KEY_TEMPERATURE_CURRENT), (self.KEY_USER_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_USER_TEMPERATURE_SETPOINT), (self.KEY_VALVE_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_TEMPERATURE_SETPOINT), (self.KEY_AWAY_MODE, heating_function, heating_function.KEY_AWAY_MODE), (self.KEY_SUMMER_MODE, heating_function, heating_function.KEY_SUMMER_MODE), (self.KEY_START_BOOST, heating_function, heating_function.KEY_START_BOOST), (self.KEY_SET_DEFAULT_TEMPERATURE, heating_function, heating_function.KEY_SET_DEFAULT_TEMPERATURE), (self.KEY_BOOST_TIMER, heating_function, heating_function.KEY_BOOST_TIMER), default_values={ self.KEY_TEMPERATURE: heating_function[heating_function.KEY_TEMPERATURE_CURRENT], self.KEY_VALVE_TEMPERATURE_SETPOINT: heating_function[heating_function.KEY_TEMPERATURE_SETPOINT], self.KEY_USER_TEMPERATURE_SETPOINT: heating_function[heating_function.KEY_USER_TEMPERATURE_SETPOINT], self.KEY_AWAY_MODE: heating_function[heating_function.KEY_AWAY_MODE], self.KEY_SUMMER_MODE: heating_function[heating_function.KEY_SUMMER_MODE], self.KEY_BOOST_TIMER: heating_function[heating_function.KEY_BOOST_TIMER], self.KEY_START_BOOST: True, self.KEY_SET_DEFAULT_TEMPERATURE: True, } ) class videv_multistate(base): def __init__(self, mqtt_client, topic, key_for_device, device, num_states, default_values=None): dv = dict.fromkeys(["state_%d" % i for i in range(0, num_states)]) for key in dv: dv[key] = False super().__init__(mqtt_client, topic, (key_for_device, device), default_values=dv) # device.add_callback(key_for_device, None, self.__index_rx__, True) def __index_rx__(self, device, key, data): for index, key in enumerate(self): self.set(key, index == data) self.__tx__(key, self[key]) class videv_audio_player(base_routing): KEY_ACTIVE_PLAYER = 'player_%d' KEY_TITLE = 'title' NO_TITLE = '---' def __init__(self, mqtt_client, topic, *args): dv = dict.fromkeys([self.KEY_ACTIVE_PLAYER % i for i in range(0, len(args))]) for key in dv: dv[key] = False dv[self.KEY_TITLE] = self.NO_TITLE super().__init__( mqtt_client, topic, *[[self.KEY_ACTIVE_PLAYER % i, device, devices.audio_status.KEY_STATE] for i, device in enumerate(args)], default_values=dv ) for audio_device in args: audio_device.add_callback(audio_device.KEY_TITLE, None, self.__title_rx__, True) def __title_rx__(self, device, key, data): self.set(self.KEY_TITLE, data or self.NO_TITLE) self.__tx__(self.KEY_TITLE, self[self.KEY_TITLE])