From 6c1f81f0be4073cba285cb5037fcf4c442edfebb Mon Sep 17 00:00:00 2001 From: Dirk Alders Date: Tue, 31 Jan 2023 19:17:10 +0100 Subject: [PATCH] all_off functionality added --- function/__init__.py | 97 +++++++++-------------------------- function/first_floor_east.py | 12 ++++- function/first_floor_west.py | 11 +++- function/ground_floor_west.py | 10 +++- function/rooms.py | 16 ++++++ function/stairway.py | 10 +++- function/videv.py | 59 ++++++++++++++++++++- 7 files changed, 135 insertions(+), 80 deletions(-) diff --git a/function/__init__.py b/function/__init__.py index 1e95c20..1a61278 100644 --- a/function/__init__.py +++ b/function/__init__.py @@ -1,11 +1,14 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # +import config import devices from function.stairway import stairway -from function.ground_floor_west import ground_floor_west_floor, ground_floor_west_marion, ground_floor_west_dirk -from function.first_floor_west import first_floor_west_julian, first_floor_west_living, first_floor_west_bath, first_floor_west_sleep -from function.first_floor_east import first_floor_east_floor, first_floor_east_kitchen, first_floor_east_dining, first_floor_east_sleep, first_floor_east_living +from function.ground_floor_west import ground_floor_west +from function.first_floor_west import first_floor_west +from function.first_floor_east import first_floor_east +from function.rooms import room_collection +from function.videv import all_off import inspect import logging @@ -19,31 +22,20 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) # TODO: implement warning message -class all_functions(object): +class all_functions(room_collection): def __init__(self, mqtt_client): - self.mqtt_client = mqtt_client - # - self.__devices__ = None + super().__init__(mqtt_client) # # Rooms # # stairway - self.stw_stairway = stairway(self.mqtt_client) + self.stw = stairway(self.mqtt_client) # ground floor west - self.gfw_floor = ground_floor_west_floor(self.mqtt_client) - self.gfw_marion = ground_floor_west_marion(self.mqtt_client) - self.gfw_dirk = ground_floor_west_dirk(self.mqtt_client) + self.gfw = ground_floor_west(self.mqtt_client) # first floor west - self.ffw_julian = first_floor_west_julian(self.mqtt_client) - self.ffw_bath = first_floor_west_bath(self.mqtt_client) - self.ffw_living = first_floor_west_living(self.mqtt_client) - self.ffw_sleep = first_floor_west_sleep(self.mqtt_client) + self.ffw = first_floor_west(self.mqtt_client) # first floor east - self.ffe_floor = first_floor_east_floor(self.mqtt_client) - self.ffe_kitchen = first_floor_east_kitchen(self.mqtt_client) - self.ffe_dining = first_floor_east_dining(self.mqtt_client) - self.ffe_sleep = first_floor_east_sleep(self.mqtt_client) - self.ffe_living = first_floor_east_living(self.mqtt_client) + self.ffe = first_floor_east(self.mqtt_client) # # Interactions # @@ -55,71 +47,28 @@ class all_functions(object): def init_cross_room_interactions(self): # shelly dirk input 1 self.last_gfw_dirk_input_1 = None - self.gfw_dirk.main_light_shelly.add_callback(devices.shelly.KEY_INPUT_1, None, self.gfw_dirk_input_1) + self.gfw.dirk.main_light_shelly.add_callback(devices.shelly.KEY_INPUT_1, None, self.gfw_dirk_input_1) # tradfri button ffe_sleep right click - self.ffe_sleep.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, - devices.tradfri_button.ACTION_RIGHT, self.ffe_floor.main_light_shelly.toggle_output_0_mcb) + self.ffe.sleep.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, + devices.tradfri_button.ACTION_RIGHT, self.ffe.floor.main_light_shelly.toggle_output_0_mcb) def init_off_functionality(self): - ##### TEMPORARY ################################################################################################################### - # # Off Buttons - # self.gui_button_all_off = devices.nodered_gui_button(self.mqtt_client, "gui/all/common/off/button") - # self.gui_button_gfw_off = devices.nodered_gui_button(self.mqtt_client, "gui/gfw/common/off/button") - # self.gui_button_ffw_off = devices.nodered_gui_button(self.mqtt_client, "gui/ffw/common/off/button") - # self.gui_button_ffe_off = devices.nodered_gui_button(self.mqtt_client, "gui/ffe/common/off/button") - # # - # self.gui_button_all_off.add_callback(devices.nodered_gui_button.KEY_STATE, True, self.all_off) - # self.gui_button_gfw_off.add_callback(devices.nodered_gui_button.KEY_STATE, True, self.gfw_off) - # self.gui_button_ffw_off.add_callback(devices.nodered_gui_button.KEY_STATE, True, self.ffw_off) - # self.gui_button_ffe_off.add_callback(devices.nodered_gui_button.KEY_STATE, True, self.ffe_off) - ##### TEMPORARY ################################################################################################################### + # ALL OFF - Virtual device + self.videv_all_off = all_off(self.mqtt_client, config.TOPIC_ALL_OFF_VIDEV, self) # ALL OFF - Long push stairway - self.stw_stairway.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.stw_stairway.main_light_shelly.flash_0_mcb) - self.stw_stairway.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.all_off) + self.stw.stairway.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.stw.stairway.main_light_shelly.flash_0_mcb) + self.stw.stairway.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.all_off) # FFE ALL OFF - Long push ffe_floor - self.ffe_floor.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.ffe_floor.main_light_shelly.flash_0_mcb) - self.ffe_floor.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.ffe_off) + self.ffe.floor.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.ffe.floor.main_light_shelly.flash_0_mcb) + self.ffe.floor.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.ffe.all_off) # FFE ALL OFF - Long push input device - self.ffe_sleep.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG, self.ffe_off) + self.ffe.sleep.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG, self.ffe.all_off) def gfw_dirk_input_1(self, device, key, data): if self.last_gfw_dirk_input_1 is not None: if self.last_gfw_dirk_input_1 != data: - self.gfw_floor.main_light_shelly.toggle_output_0_mcb(device, key, data) + self.gfw.floor.main_light_shelly.toggle_output_0_mcb(device, key, data) self.last_gfw_dirk_input_1 = data - - def getmembers(self, prefix): - rv = [] - for name, obj in inspect.getmembers(self): - if name.startswith(prefix) and obj.__module__.split('.')[0] == 'function' and len(obj.__module__.split('.')) == 2: - rv.append(obj) - return rv - - def gfw_off(self, device=None, key=None, data=None): - logger.info("Switching \"ground floor west\" off.") - for gfw in self.getmembers('gfw'): - gfw.all_off() - - def ffw_off(self, device=None, key=None, data=None): - logger.info("Switching \"first floor west\" off.") - for ffw in self.getmembers('ffw'): - ffw.all_off() - - def ffe_off(self, device=None, key=None, data=None): - logger.info("Switching \"first floor east\" off.") - for ffe in self.getmembers('ffe'): - ffe.all_off() - - def stw_off(self, device=None, key=None, data=None): - logger.info("Switching \"stairway\" off.") - for stw in self.getmembers('stw'): - stw.all_off() - - def all_off(self, device=None, key=None, data=None): - for name, obj in inspect.getmembers(self): - parent_name = obj.__class__.__base__.__name__ - if parent_name == "room": - obj.all_off() diff --git a/function/first_floor_east.py b/function/first_floor_east.py index 1db3549..ed51fdb 100644 --- a/function/first_floor_east.py +++ b/function/first_floor_east.py @@ -5,7 +5,7 @@ import config import devices from function.modules import brightness_choose_n_action, timer_on_activation, heating_function -from function.rooms import room +from function.rooms import room, room_collection from function.videv import videv_switching, videv_switch_brightness, videv_switching_timer, videv_switch_brightness_color_temp, videv_heating, videv_multistate import logging @@ -16,6 +16,16 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) +class first_floor_east(room_collection): + def __init__(self, mqtt_client): + super().__init__(mqtt_client) + self.dining = first_floor_east_dining(mqtt_client) + self.floor = first_floor_east_floor(mqtt_client) + self.kitchen = first_floor_east_kitchen(mqtt_client) + self.livingroom = first_floor_east_living(mqtt_client) + self.sleep = first_floor_east_sleep(mqtt_client) + + class first_floor_east_floor(room): def __init__(self, mqtt_client): # diff --git a/function/first_floor_west.py b/function/first_floor_west.py index e618f35..517c746 100644 --- a/function/first_floor_west.py +++ b/function/first_floor_west.py @@ -5,7 +5,7 @@ import config import devices from function.modules import heating_function -from function.rooms import room +from function.rooms import room, room_collection from function.videv import videv_switch_brightness, videv_switch_brightness_color_temp, videv_heating import logging @@ -17,6 +17,15 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) +class first_floor_west(room_collection): + def __init__(self, mqtt_client): + super().__init__(mqtt_client) + self.bath = first_floor_west_bath(mqtt_client) + self.julian = first_floor_west_julian(mqtt_client) + self.livingroom = first_floor_west_living(mqtt_client) + self.sleep = first_floor_west_sleep(mqtt_client) + + class first_floor_west_julian(room): def __init__(self, mqtt_client): # diff --git a/function/ground_floor_west.py b/function/ground_floor_west.py index 89e9501..990fc60 100644 --- a/function/ground_floor_west.py +++ b/function/ground_floor_west.py @@ -5,7 +5,7 @@ import config import devices from function.modules import brightness_choose_n_action, heating_function, switched_light -from function.rooms import room +from function.rooms import room, room_collection from function.videv import videv_switching, videv_switch_brightness_color_temp, videv_heating, videv_multistate, videv_audio_player import logging import task @@ -17,6 +17,14 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) +class ground_floor_west(room_collection): + def __init__(self, mqtt_client): + super().__init__(mqtt_client) + self.dirk = ground_floor_west_dirk(mqtt_client) + self.floor = ground_floor_west_floor(mqtt_client) + self.marion = ground_floor_west_marion(mqtt_client) + + class ground_floor_west_floor(room): def __init__(self, mqtt_client): # diff --git a/function/rooms.py b/function/rooms.py index 5fcb4f1..24affe0 100644 --- a/function/rooms.py +++ b/function/rooms.py @@ -24,3 +24,19 @@ class room(object): obj.all_off() except AttributeError: pass # not a module or has no method all_off + + +class room_collection(object): + ALLOWED_CLASSES = ("room", "room_collection") + + def __init__(self, mqtt_client): + self.mqtt_client = mqtt_client + + def all_off(self, device=None, key=None, data=None): + logger.info("Switching all off \"%s\"", type(self).__name__) + for sub_name in dir(self): + # attribute name is not private + if not sub_name.startswith("__"): + sub = getattr(self, sub_name) + if sub.__class__.__bases__[0].__name__ in self.ALLOWED_CLASSES: + sub.all_off() diff --git a/function/stairway.py b/function/stairway.py index 47e42c4..2bd8f81 100644 --- a/function/stairway.py +++ b/function/stairway.py @@ -6,7 +6,7 @@ import config import devices import logging from function.modules import motion_sensor_light -from function.rooms import room +from function.rooms import room, room_collection from function.videv import videv_switching_motion try: @@ -16,7 +16,13 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) -class stairway(room): +class stairway(room_collection): + def __init__(self, mqtt_client): + super().__init__(mqtt_client) + self.stairway = stairway_stairway(mqtt_client) + + +class stairway_stairway(room): def __init__(self, mqtt_client): # # Device initialisation diff --git a/function/videv.py b/function/videv.py index e0e7dcd..21d32d0 100644 --- a/function/videv.py +++ b/function/videv.py @@ -11,7 +11,7 @@ Targets: """ from base import mqtt_base -import devices +from function.rooms import room, room_collection import json import time @@ -258,3 +258,60 @@ class videv_audio_player(base): super().capabilities self.__capabilities__[self.KEY_TITLE] = {'display': True} return self.__capabilities__ + + +class all_off(base): + ALLOWED_CLASSES = (room, room_collection, ) + + def __init__(self, mqtt_client, topic, room_collection): + super().__init__(mqtt_client, topic) + self.__room_collection__ = room_collection + # init __inst_dict__ + self.__inst_dict__ = {} + self.__add_instances__("all", self.__room_collection__) + # register mqtt callbacks for all my keys + for key in self.__inst_dict__: + mqtt_client.add_callback(topic + "/" + key, self.all_off) + # + self.__tx_capabilities__() + + def __check_inst_capabilities__(self, name, inst): + # fits to specified classes + if isinstance(inst, self.ALLOWED_CLASSES): + try: + # all_off method is callable + return callable(inst.all_off) + except AttributeError: + # all_off method does not exist + return False + return False + + def __add_instances__(self, name, inst, level=0): + if self.__check_inst_capabilities__(name, inst): + # add given instance to my __inst_dict__ + self.__inst_dict__[name] = inst + # iterate over all attribute names of instance + for sub_name in dir(inst): + # attribute name is not private + if not sub_name.startswith("__"): + sub = getattr(inst, sub_name) + # recurse with this object + if level == 0: + self.__add_instances__(sub_name, sub, level=level+1) + else: + self.__add_instances__(name + "/" + sub_name, sub, level=level+1) + + def all_off(self, client, userdata, message): + key = message.topic[len(self.topic) + 1:] + self.__inst_dict__[key].all_off() + self.__tx_capabilities__() + + @property + def capabilities(self): + if self.__capabilities__ is None: + self.__capabilities__ = {} + self.__capabilities__['__type__'] = self.__class__.__name__ + for key in self.__inst_dict__: + self.__capabilities__[key] = {} + self.__capabilities__[key]['control'] = True + return self.__capabilities__