From da53b3638f024b01817cb648e12ff4b83ff50ea9 Mon Sep 17 00:00:00 2001 From: Dirk Alders Date: Thu, 22 Dec 2022 07:55:56 +0100 Subject: [PATCH] all/zone off implemented --- devices/__init__.py | 44 +++++++++++++-- function/__init__.py | 100 ++++++++++++++++++++++++++--------- function/common.py | 3 ++ function/first_floor_east.py | 21 ++++++-- function/rooms.py | 17 ++++-- 5 files changed, 147 insertions(+), 38 deletions(-) diff --git a/devices/__init__.py b/devices/__init__.py index a204780..4016d4f 100644 --- a/devices/__init__.py +++ b/devices/__init__.py @@ -191,6 +191,8 @@ class shelly(base): KEY_OUTPUT_1 = "relay/1" KEY_INPUT_0 = "input/0" KEY_INPUT_1 = "input/1" + KEY_LONGPUSH_0 = "longpush/0" + KEY_LONGPUSH_1 = "longpush/1" KEY_TEMPERATURE = "temperature" KEY_OVERTEMPERATURE = "overtemperature" # @@ -198,13 +200,14 @@ class shelly(base): TX_TYPE = base.TX_VALUE TX_FILTER_DATA_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1] # - RX_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_INPUT_0, - KEY_INPUT_1, KEY_OVERTEMPERATURE, KEY_TEMPERATURE] + RX_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1, + KEY_INPUT_0, KEY_INPUT_1, KEY_LONGPUSH_0, KEY_LONGPUSH_1, + KEY_OVERTEMPERATURE, KEY_TEMPERATURE] RX_IGNORE_TOPICS = [KEY_OUTPUT_0 + '/' + TX_TOPIC, KEY_OUTPUT_1 + '/' + TX_TOPIC, KEY_OUTPUT_0 + '/' + "energy", KEY_OUTPUT_1 + '/' + "energy", 'input_event/0', 'input_event/1'] RX_IGNORE_KEYS = ['temperature_f'] - RX_FILTER_DATA_KEYS = [KEY_INPUT_0, KEY_INPUT_1, + RX_FILTER_DATA_KEYS = [KEY_INPUT_0, KEY_INPUT_1, KEY_LONGPUSH_0, KEY_LONGPUSH_1, KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_OVERTEMPERATURE] def __init__(self, mqtt_client, topic): @@ -246,6 +249,16 @@ class shelly(base): """rv: [True, False]""" return self.get(self.KEY_INPUT_1) + @property + def longpush_0(self): + """rv: [True, False]""" + return self.get(self.KEY_LONGPUSH_0) + + @property + def longpush_1(self): + """rv: [True, False]""" + return self.get(self.KEY_LONGPUSH_1) + @property def temperature(self): """rv: numeric value""" @@ -450,13 +463,28 @@ class tradfri_light(base): class tradfri_button(base): + ACTION_TOGGLE = "toggle" + ACTION_BRIGHTNESS_UP = "brightness_up_click" + ACTION_BRIGHTNESS_DOWN = "brightness_down_click" + ACTION_RIGHT = "arrow_right_click" + ACTION_LEFT = "arrow_left_click" + ACTION_BRIGHTNESS_UP_LONG = "brightness_up_hold" + ACTION_BRIGHTNESS_UP_RELEASE = "brightness_up_release" + ACTION_BRIGHTNESS_DOWN_LONG = "brightness_down_hold" + ACTION_BRIGHTNESS_DOWN_RELEASE = "brightness_down_release" + ACTION_RIGHT_LONG = "arrow_right_hold" + ACTION_RIGHT_RELEASE = "arrow_right_release" + ACTION_LEFT_LONG = "arrow_left_hold" + ACTION_LEFT_RELEASE = "arrow_left_release" + # KEY_LINKQUALITY = "linkquality" KEY_BATTERY = "battery" KEY_ACTION = "action" + KEY_ACTION_DURATION = "action_duration" # RX_KEYS = [KEY_LINKQUALITY, KEY_BATTERY, KEY_ACTION] RX_IGNORE_TOPICS = [] - RX_IGNORE_KEYS = ['update'] + RX_IGNORE_KEYS = ['update', KEY_ACTION_DURATION] RX_FILTER_DATA_KEYS = [] def __init__(self, mqtt_client, topic): @@ -488,12 +516,13 @@ class nodered_gui(base): KEY_COLOR_TEMP = "color_temp" KEY_HEATING_BOOST = "heating_boost" KEY_HEATING_SETPOINT = "heating_setpoint" + KEY_OFF_BUTTON = "off_button" # TX_TOPIC = 'set' TX_TYPE = base.TX_VALUE TX_FILTER_DATA_KEYS = [] # - RX_KEYS = [KEY_STATE, KEY_BRIGHTNESS, KEY_COLOR_TEMP, KEY_HEATING_BOOST, KEY_HEATING_SETPOINT] + RX_KEYS = [KEY_STATE, KEY_BRIGHTNESS, KEY_COLOR_TEMP, KEY_HEATING_BOOST, KEY_HEATING_SETPOINT, KEY_OFF_BUTTON] RX_IGNORE_TOPICS = [KEY_FEEDBACK + '/' + TX_TOPIC, KEY_ENABLE + '/' + TX_TOPIC] RX_FILTER_DATA_KEYS = [] @@ -528,6 +557,11 @@ class nodered_gui(base): """rv: [5, ..., 30]""" return self.get(self.KEY_HEATING_SETPOINT) + @property + def off_button(self): + """rv: [True, False]""" + return self.get(self.KEY_OFF_BUTTON) + # # TX # diff --git a/function/__init__.py b/function/__init__.py index 60b9f86..09bdd3c 100644 --- a/function/__init__.py +++ b/function/__init__.py @@ -7,11 +7,16 @@ from function.first_floor_west import first_floor_west_julian, first_floor_west_ from function.first_floor_east import first_floor_east_floor, first_floor_east_kitchen, first_floor_east_dining, first_floor_east_sleep_madi, first_floor_east_living from function.common import common_heating, common_circulation_pump import inspect -from function import modules +import logging + +try: + from config import APP_NAME as ROOT_LOGGER_NAME +except ImportError: + ROOT_LOGGER_NAME = 'root' +logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) -# TODO: implement switch off functionality (except of switch off button transportation) -# TODO: implement garland (incl. day events like sunset, sunrise, ...) # TODO: implement existing nodered functionality "dirk" (ground floor west) +# TODO: implement garland (incl. day events like sunset, sunrise, ...) # TODO: implement warning message @@ -20,6 +25,9 @@ class all_functions(object): self.mqtt_client = mqtt_client # self.__devices__ = None + # heating and warm water + self.common_heat_sleep_madi = common_heating(self.mqtt_client) + self.common_circulation_pump = common_circulation_pump(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) @@ -33,35 +41,75 @@ class all_functions(object): self.ffe_dining = first_floor_east_dining(self.mqtt_client) self.ffe_sleep_madi = first_floor_east_sleep_madi(self.mqtt_client) self.ffe_living = first_floor_east_living(self.mqtt_client) - # heating and warm water - self.common_heat_sleep_madi = common_heating(self.mqtt_client) - self.common_circulation_pump = common_circulation_pump(self.mqtt_client) # # Interactions # + # cross_room_interactions + self.init_cross_room_interactions() + # Circulation pump + self.init_circulation_pump() + # Off Buttons + self.init_off_functionality() + + def init_off_functionality(self): + # Off Buttons + self.gui_button_all_off = devices.nodered_gui(self.mqtt_client, topic="gui/button_all_off") + self.gui_button_gfw_off = devices.nodered_gui(self.mqtt_client, topic="gui/button_gfw_off") + self.gui_button_ffw_off = devices.nodered_gui(self.mqtt_client, topic="gui/button_ffw_off") + self.gui_button_ffe_off = devices.nodered_gui(self.mqtt_client, topic="gui/button_ffe_off") + # + self.gui_button_all_off.add_callback(devices.nodered_gui.KEY_OFF_BUTTON, True, self.all_off) + self.gui_button_gfw_off.add_callback(devices.nodered_gui.KEY_OFF_BUTTON, True, self.gfw_off) + self.gui_button_ffw_off.add_callback(devices.nodered_gui.KEY_OFF_BUTTON, True, self.ffw_off) + self.gui_button_ffe_off.add_callback(devices.nodered_gui.KEY_OFF_BUTTON, True, self.ffe_off) + # Long push ffe_floor + self.ffe_floor.main_light_shelly.add_callback( + devices.shelly.KEY_LONGPUSH_0, True, self.ffe_floor.all_off_feedback) + self.ffe_floor.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.ffe_off) + # Long push input device + self.ffe_sleep_madi.button_tradfri.add_callback( + devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG, self.ffe_off) + + 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 common_off(self, device=None, key=None, data=None): + logger.info("Switching \"common\" off.") + for common in self.getmembers('common'): + common.all_off() + + 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 all_off(self, device=None, key=None, data=None): + self.common_off(device, key, data) + self.gfw_off(device, key, data) + self.ffw_off(device, key, data) + self.ffe_off(device, key, data) + + 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) - # input device - self.init_input_device_sleep_madi_functionality() - # Circulation pump - self.init_circulation_pump() - - def init_input_device_sleep_madi_functionality(self): - # - self.ffe_button_tradfri_sleep = devices.tradfri_button( - self.mqtt_client, topic="zigbee_og_e/input_device/og_east") - # - self.ffe_button_tradfri_sleep.add_callback(devices.tradfri_button.KEY_ACTION, "toggle", - self.ffe_sleep_madi.toggle_main_light) - self.ffe_button_tradfri_sleep.add_callback(devices.tradfri_button.KEY_ACTION, "brightness_up_click", - self.ffe_sleep_madi.toggle_bed_light_di) - self.ffe_button_tradfri_sleep.add_callback(devices.tradfri_button.KEY_ACTION, "brightness_down_click", - self.ffe_sleep_madi.toggle_bed_light_di) - self.ffe_button_tradfri_sleep.add_callback(devices.tradfri_button.KEY_ACTION, "arrow_right_click", - self.ffe_floor.toggle_main_light) - self.ffe_button_tradfri_sleep.add_callback(devices.tradfri_button.KEY_ACTION, None, - self.ffe_sleep_madi.fade_light) + # tradfri button sleep madi right click + self.ffe_sleep_madi.button_tradfri.add_callback( + devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT, self.ffe_floor.toggle_main_light) def init_circulation_pump(self): self.common_circulation_pump.main_light_shelly.add_callback( diff --git a/function/common.py b/function/common.py index bdb839a..3c210e0 100644 --- a/function/common.py +++ b/function/common.py @@ -54,3 +54,6 @@ class common_heating(object): def __init__(self, mqtt_client): self.ffe_heating_sleep_madi = heating_function_brennenstuhl( mqtt_client, "zigbee_og_e/radiator/sleep_madi", 20, "gui/ffe_bo_sleep_madi", "gui/ffe_ts_sleep_madi", "gui/ffe_bl_sleep_madi") + + def all_off(self): + pass # dummy method for compatibility reasons diff --git a/function/first_floor_east.py b/function/first_floor_east.py index 2e3a2f5..37b0ca4 100644 --- a/function/first_floor_east.py +++ b/function/first_floor_east.py @@ -74,10 +74,25 @@ class first_floor_east_sleep_madi(room_shelly_tradfri_light): self.gui_brightness_bed_light_di.enable(False) self.gui_brightness_bed_light_di.set_feedback(0) # + self.button_tradfri = devices.tradfri_button(mqtt_client, topic="zigbee_og_e/input_device/og_east") + # # Callback initialisation # self.init_bed_light_functions() self.init_fade_function() + # + self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_TOGGLE, + self.toggle_main_light) + self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_BRIGHTNESS_UP, + self.toggle_bed_light_di) + self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_BRIGHTNESS_DOWN, + self.toggle_bed_light_di) + self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, None, + self.fade_light) + + def all_off(self, device=None, key=None, data=None): + return super().all_off(device, key, data) + self.bed_light_di_tradfri.set_output_0(False) # bed light def init_bed_light_functions(self): @@ -143,13 +158,13 @@ class first_floor_east_sleep_madi(room_shelly_tradfri_light): target = self.bed_light_di_tradfri else: return - if (data == 'brightness_up_hold'): + if (data == devices.tradfri_button.ACTION_BRIGHTNESS_UP_LONG): logger.info("Increasing brightness \"%s\" %s", type(self).__name__, target.topic) target.brightness_inc() - elif (data == 'brightness_down_hold'): + elif (data == devices.tradfri_button.ACTION_BRIGHTNESS_DOWN_LONG): logger.info("Decreasing brightness \"%s\" %s", type(self).__name__, target.topic) target.brightness_dec() - elif (data.startswith('brightness') and data.endswith('release')): + elif (data in [devices.tradfri_button.ACTION_BRIGHTNESS_UP_RELEASE, devices.tradfri_button.ACTION_BRIGHTNESS_DOWN_RELEASE]): logger.info("Stoping brightness change \"%s\" %s", type(self).__name__, target.topic) target.brightness_stop() diff --git a/function/rooms.py b/function/rooms.py index 7cd9350..647910c 100644 --- a/function/rooms.py +++ b/function/rooms.py @@ -33,13 +33,23 @@ class room_shelly(room): self.gui_switch_main_light.add_callback(devices.nodered_gui.KEY_STATE, None, self.gui_switch_command) self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_switch_feedback) # + self.block_all_off = False self.last_flash_data = None self.delayed_task = task.delayed(.25, self.toggle_main_light, None, None, None) def all_off(self, device=None, key=None, data=None): - logger.info("Switching all off \"%s\"", type(self).__name__) - self.main_light_shelly.set_output_0(False) - self.main_light_shelly.set_output_1(False) + if not self.block_all_off: + logger.info("Switching all off \"%s\"", type(self).__name__) + self.main_light_shelly.set_output_0(False) + self.main_light_shelly.set_output_1(False) + self.block_all_off = False + + def all_off_feedback(self, device=None, key=None, data=None): + logger.info("Flashing \"%s\" main light", type(self).__name__) + if self.main_light_shelly.output_0 is False: + self.main_light_shelly.set_output_0(True) + self.block_all_off = True + self.delayed_task.run() def gui_switch_command(self, device, key, data): logger.info("Switching \"%s\" main light: %s", type(self).__name__, str(data)) @@ -50,7 +60,6 @@ class room_shelly(room): self.main_light_shelly.set_output_0("toggle") def flash_main_light(self, device, key, data): - logging.info("%s::%s", key, str(data)) if self.last_flash_data != data and data is True: logger.info("Flashing \"%s\" main light", type(self).__name__) self.toggle_main_light(device, key, data)