diff --git a/__simulation__/devices.py b/__simulation__/devices.py index f8b40f7..b57c327 100644 --- a/__simulation__/devices.py +++ b/__simulation__/devices.py @@ -11,7 +11,7 @@ COLOR_POWERPLUG = colored.fg("light_cyan") COLOR_LIGHT_ACTIVE = colored.fg("yellow") COLOR_LIGHT_PASSIVE = COLOR_LIGHT_ACTIVE + colored.attr("dim") COLOR_MOTION_SENSOR = colored.fg("dark_orange_3b") -COLOR_RADIATOR_VALVE = colored.fg("red") +COLOR_HEATING_VALVE = colored.fg("red") COLOR_REMOTE = colored.fg("green") @@ -518,7 +518,6 @@ class gui_light(tradfri_light): self.led_names = {} # self.maxvalue = None - self.last_printed = None def __init_data__(self, enable_state, enable_brightness, enable_color_temp): data = {} @@ -595,19 +594,19 @@ class gui_light(tradfri_light): "" ) elif key == self.KEY_TIMER: - if isinstance(value, (int, float, complex)) and not isinstance(value, bool): - if self.maxvalue is None: + if value > 0: + if self.maxvalue is None and value != 0: self.maxvalue = value disp_value = value - perc = disp_value / self.maxvalue * 100 + try: + perc = disp_value / self.maxvalue * 100 + except ZeroDivisionError: + perc = 0 else: disp_value = 0 perc = 0 self.maxvalue = None - self.last_printed = None - if self.last_printed is None or abs(self.last_printed - perc) >= 4.95: - print_percent(COLOR_GUI_ACTIVE, 't', perc, '%3d%%' % perc, self.topic, '(%.1f)' % disp_value) - self.last_printed = perc + print_percent(COLOR_GUI_ACTIVE, 't', perc, '%3d%%' % perc, self.topic, '(%.1f)' % disp_value) elif key.startswith(self.KEY_LED_X[:-2]): print_light(COLOR_GUI_ACTIVE, value, self.topic, '(%s)' % self.led_names.get(key, key), True) @@ -704,7 +703,7 @@ class remote(base): print(COLOR_REMOTE + 10 * ' ' + icon + 6 * ' ' + devicename + colored.attr("reset")) -class brennenstuhl_radiator_valve(base): +class brennenstuhl_heating_valve(base): TEMP_RANGE = [10, 30] # KEY_TEMPERATURE_SETPOINT = "current_heating_setpoint" @@ -744,10 +743,10 @@ class brennenstuhl_radiator_valve(base): perc = 100 * (value - self.TEMP_RANGE[0]) / (self.TEMP_RANGE[1] - self.TEMP_RANGE[0]) perc = 100 if perc > 100 else perc perc = 0 if perc < 0 else perc - print_percent(COLOR_RADIATOR_VALVE, '\u03d1', perc, "%4.1f°C" % value, self.topic, "") + print_percent(COLOR_HEATING_VALVE, '\u03d1', perc, "%4.1f°C" % value, self.topic, "") -class gui_radiator_valve(base): +class gui_heating_valve(base): AUTOSEND = False # TEMP_RANGE = [10, 30] @@ -824,6 +823,7 @@ class gui_radiator_valve(base): def print_formatted(self, device, key, value): devicename = ' - '.join(self.topic.split('/')[1:]) if key == self.KEY_TIMER: + value /= 60 try: perc = 100 * value / 60 except TypeError: diff --git a/__simulation__/rooms.py b/__simulation__/rooms.py index b731725..a0bc66a 100644 --- a/__simulation__/rooms.py +++ b/__simulation__/rooms.py @@ -1,6 +1,6 @@ import config -from __simulation__.devices import shelly, silvercrest_powerplug, tradfri_light, tradfri_button, silvercrest_motion_sensor, my_powerplug, remote, brennenstuhl_radiator_valve -from __simulation__.devices import gui_light, gui_led_array, gui_radiator_valve +from __simulation__.devices import shelly, silvercrest_powerplug, tradfri_light, tradfri_button, silvercrest_motion_sensor, my_powerplug, remote, brennenstuhl_heating_valve +from __simulation__.devices import gui_light, gui_led_array, gui_heating_valve import inspect @@ -43,10 +43,10 @@ class gfw_floor(base): self.gui_main_light = gui_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_GUI, True, True, True) self.main_light = shelly(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_SHELLY, input_0_func=shelly.INPUT_FUNC_OUT1_TRIGGER) self.main_light.add_channel_name(shelly.KEY_OUTPUT_0, "Main Light") - self.main_light_zigbee_1 = tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_1_ZIGBEE, True, True, True, False) + self.main_light_zigbee_1 = tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 1, True, True, True, False) self.main_light.add_callback(shelly.KEY_OUTPUT_0, self.main_light_zigbee_1.power_on, "on") self.main_light.add_callback(shelly.KEY_OUTPUT_0, self.main_light_zigbee_1.power_off, "off") - self.main_light_zigbee_2 = tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_2_ZIGBEE, True, True, True, False) + self.main_light_zigbee_2 = tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 2, True, True, True, False) self.main_light.add_callback(shelly.KEY_OUTPUT_0, self.main_light_zigbee_2.power_on, "on") self.main_light.add_callback(shelly.KEY_OUTPUT_0, self.main_light_zigbee_1.power_off, "off") @@ -56,8 +56,8 @@ class gfw_marion(base): self.gui_main_light = gui_light(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_GUI, True, False, False) self.main_light = shelly(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_SHELLY, input_0_func=shelly.INPUT_FUNC_OUT1_TRIGGER) self.main_light.add_channel_name(shelly.KEY_OUTPUT_0, "Main Light") - self.radiator_valve = brennenstuhl_radiator_valve(mqtt_client, config.TOPIC_GFW_MARION_RADIATOR_VALVE_ZIGBEE) - self.gui_radiator_valve = gui_radiator_valve(mqtt_client, config.TOPIC_GFW_MARION_RADIATOR_VALVE_GUI) + self.heating_valve = brennenstuhl_heating_valve(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_ZIGBEE) + self.gui_heating_valve = gui_heating_valve(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_GUI) class gfw_dirk(base): @@ -90,8 +90,8 @@ class gfw_dirk(base): self.led_array.add_channel_name(gui_led_array.KEY_LED_1, "Desk Light") self.led_array.add_channel_name(gui_led_array.KEY_LED_2, "Amplifier") # - self.radiator_valve = brennenstuhl_radiator_valve(mqtt_client, config.TOPIC_GFW_DIRK_RADIATOR_VALVE_ZIGBEE) - self.gui_radiator_valve = gui_radiator_valve(mqtt_client, config.TOPIC_GFW_DIRK_RADIATOR_VALVE_GUI) + self.heating_valve = brennenstuhl_heating_valve(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_ZIGBEE) + self.gui_heating_valve = gui_heating_valve(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_GUI) class gfw(base): @@ -127,8 +127,8 @@ class ffw_sleep(base): class ffw_bath(base): def __init__(self, mqtt_client): - self.radiator_valve = brennenstuhl_radiator_valve(mqtt_client, config.TOPIC_FFW_BATH_RADIATOR_VALVE_ZIGBEE) - self.gui_radiator_valve = gui_radiator_valve(mqtt_client, config.TOPIC_FFW_BATH_RADIATOR_VALVE_GUI) + self.heating_valve = brennenstuhl_heating_valve(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_ZIGBEE) + self.gui_heating_valve = gui_heating_valve(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_GUI) class ffw(base): @@ -190,8 +190,8 @@ class ffe_sleep(base): self.led_array.add_channel_name(gui_led_array.KEY_LED_0, "Main Light") self.led_array.add_channel_name(gui_led_array.KEY_LED_1, "Bed Light Dirk") # - self.radiator_valve = brennenstuhl_radiator_valve(mqtt_client, config.TOPIC_FFE_SLEEP_RADIATOR_VALVE_ZIGBEE) - self.gui_radiator_valve = gui_radiator_valve(mqtt_client, config.TOPIC_FFE_SLEEP_RADIATOR_VALVE_GUI) + self.heating_valve = brennenstuhl_heating_valve(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_ZIGBEE) + self.gui_heating_valve = gui_heating_valve(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_GUI) class ffe_livingroom(base): diff --git a/base.py b/base.py index 1798489..4339750 100644 --- a/base.py +++ b/base.py @@ -9,14 +9,14 @@ except ImportError: class common_base(dict): DEFAULT_VALUES = {} - def __init__(self): - super().__init__(self.DEFAULT_VALUES) + def __init__(self, default_values=None): + super().__init__(default_values or self.DEFAULT_VALUES) self['__type__'] = self.__class__.__name__ # self.__callback_list__ = [] self.logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) - def add_callback(self, key, data, callback, on_change_only=True): + def add_callback(self, key, data, callback, on_change_only=False): """ key: key or None for all keys data: data or None for all data @@ -25,19 +25,20 @@ class common_base(dict): if cb_tup not in self.__callback_list__: self.__callback_list__.append(cb_tup) - def set(self, key, data): + def set(self, key, data, block_callback=[]): 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 - callback(self, key, self[key]) + if not callback in block_callback: # block given callbacks + callback(self, key, self[key]) class mqtt_base(common_base): - def __init__(self, mqtt_client, topic): - super().__init__() + def __init__(self, mqtt_client, topic, default_values=None): + super().__init__(default_values) # self.mqtt_client = mqtt_client self.topic = topic diff --git a/devices/__init__.py b/devices/__init__.py index b505d43..b46ebe0 100644 --- a/devices/__init__.py +++ b/devices/__init__.py @@ -27,6 +27,9 @@ devices (DEVICES) """ # TODO: Usage of mqtt_base for all devices +# +# TODO: brennenstuhl_heatingvalve: always send "{"preset": "manual", "system_mode": "heat"}" with any information +# TODO: usage of base.mqtt as parent for class base __DEPENDENCIES__ = [] @@ -50,6 +53,52 @@ def is_json(data): return True +class group(object): + def __init__(self, *args): + super().__init__() + self._members = args + self._iter_counter = 0 + # + self.methods = [] + for method in [m for m in args[0].__class__.__dict__.keys()]: + if not method.startswith('_') and callable(getattr(args[0], method)): # add all public callable attributes to the list + self.methods.append(method) + # + for member in self: + methods = [m for m in member.__class__.__dict__.keys() if not m.startswith( + '_') if not m.startswith('_') and callable(getattr(args[0], m))] + if self.methods != methods: + raise ValueError("All given instances needs to have same attributes:", self.methods, methods) + + def __iter__(self): + return self + + def __next__(self): + if self._iter_counter < len(self): + self._iter_counter += 1 + return self._members[self._iter_counter - 1] + self._iter_counter = 0 + raise StopIteration + + def __getitem__(self, i): + return self._members[i] + + def __len__(self): + return len(self._members) + + def __getattribute__(self, name): + def group_execution(*args, **kwargs): + for member in self[:]: + m = getattr(member, name) + m(*args, **kwargs) + try: + rv = super().__getattribute__(name) + except AttributeError: + return group_execution + else: + return rv + + class base(dict): TX_TOPIC = "set" TX_VALUE = 0 @@ -508,9 +557,6 @@ class tradfri_light(base): else: return super().pack_filter(key, data) - def request_data(self): - self.mqtt_client.send(self.topic + "/get", '{%s: ""}' % self.KEY_OUTPUT_0) - # # RX # @@ -537,6 +583,9 @@ class tradfri_light(base): # # TX # + def request_data(self, device=None, key=None, data=None): + self.mqtt_client.send(self.topic + "/get", '{"%s": ""}' % self.KEY_OUTPUT_0) + def set_output_0(self, state): """state: [True, False, 'toggle']""" self.pack(self.KEY_OUTPUT_0, state) diff --git a/function/__init__.py b/function/__init__.py index c3b0bd0..c994791 100644 --- a/function/__init__.py +++ b/function/__init__.py @@ -109,8 +109,6 @@ class all_functions(object): # 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_kitchen.circulation_pump.main_light_shelly.add_callback( - devices.shelly.KEY_OUTPUT_0, True, self.ffw_bath.radiator_function.boost, True) def gfw_dirk_input_1(self, device, key, data): if self.last_gfw_dirk_input_1 is not None: diff --git a/function/db.py b/function/db.py index c9e7bcc..2802f01 100644 --- a/function/db.py +++ b/function/db.py @@ -4,12 +4,12 @@ import sqlite3 db_file = os.path.join(os.path.dirname(__file__), '..', 'database.db') -def get_gui_radiator_data(topic): - return __storage__().get_gui_radiator_data(topic) +def get_radiator_data(topic): + return __storage__().get_radiator_data(topic) -def set_gui_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint): - return __storage__().store_gui_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint) +def set_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint): + return __storage__().store_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint) class __storage__(object): @@ -17,30 +17,31 @@ class __storage__(object): self.conn = sqlite3.connect(db_file) self.c = self.conn.cursor() with self.conn: - self.c.execute("""CREATE TABLE IF NOT EXISTS gui_radiator ( + self.c.execute("""CREATE TABLE IF NOT EXISTS radiator ( topic text PRIMARY KEY, away_mode integer, summer_mode integer, - user_temperatur_setpoint real + user_temperatur_setpoint real, + temperatur_setpoint real )""") - def store_gui_radiator_data(self, topic, away_mode, summer_mode, user_temperatur_setpoint): - data = [topic, away_mode, summer_mode, user_temperatur_setpoint] + def store_radiator_data(self, topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint): + data = [topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint] try: with self.conn: self.c.execute( - 'INSERT INTO gui_radiator VALUES (?, ?, ?, ?)', data) + 'INSERT INTO radiator VALUES (?, ?, ?, ?, ?)', data) except sqlite3.IntegrityError: - data = [away_mode, summer_mode, user_temperatur_setpoint] - db_data = self.get_gui_radiator_data(topic) + data = [away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint] + db_data = self.get_radiator_data(topic) if db_data != data: with self.conn: self.c.execute( - 'UPDATE gui_radiator SET away_mode = ?, summer_mode = ?, user_temperatur_setpoint = ? WHERE topic = ?', data + [topic]) + 'UPDATE radiator SET away_mode = ?, summer_mode = ?, user_temperatur_setpoint = ?, temperatur_setpoint = ? WHERE topic = ?', data + [topic]) - def get_gui_radiator_data(self, topic): - """ returns a list [away_mode, summer_mode, user_temperatur_setpoint] or [None, None, None]""" - self.c.execute("SELECT * FROM gui_radiator WHERE topic=?", (topic, )) + def get_radiator_data(self, topic): + """ returns a list [away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint] or [None, None, None, None]""" + self.c.execute("SELECT * FROM radiator WHERE topic=?", (topic, )) data = self.c.fetchone() if data is not None: data = list(data) @@ -48,7 +49,7 @@ class __storage__(object): data[2] = data[2] == 1 return data[1:] else: - return [None, None, None] + return [None, None, None, None] def __del__(self): self.conn.close() diff --git a/function/first_floor_east.py b/function/first_floor_east.py index 09f4aa6..de7df16 100644 --- a/function/first_floor_east.py +++ b/function/first_floor_east.py @@ -4,10 +4,10 @@ import config import devices -from function.modules import brightness_choose_n_action, circulation_pump, radiator_function +from function.modules import brightness_choose_n_action, timer_on_activation, heating_function import logging -from function.rooms import room_shelly, room_shelly_motion_sensor, room_shelly_tradfri_light -from function.videv import videv_switching, videv_switch_brightness, videv_switch_brightness_color_temp +from function.rooms import room +from function.videv import videv_switching, videv_switch_brightness, videv_switching_timer, videv_switch_brightness_color_temp, videv_heating, videv_multistate try: from config import APP_NAME as ROOT_LOGGER_NAME except ImportError: @@ -15,10 +15,23 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) -class first_floor_east_floor(room_shelly): +class first_floor_east_floor(room): def __init__(self, mqtt_client): + # + # Device initialisation + # # http://shelly1l-3C6105E4E629 - super().__init__(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_SHELLY, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_GUI) + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_SHELLY) + super().__init__(mqtt_client) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light = videv_switching( mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_VIDEV, @@ -26,43 +39,82 @@ class first_floor_east_floor(room_shelly): ) -class first_floor_east_kitchen(room_shelly): +class first_floor_east_kitchen(room): def __init__(self, mqtt_client): - # http://shelly1l-8CAAB5616C01 - super().__init__(mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_SHELLY, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_GUI) # - self.circulation_pump = circulation_pump(mqtt_client) - self.circulation_pump.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.flash_main_light) + # Device initialisation + # + # http://shelly1l-8CAAB5616C01 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_SHELLY) + # http://shelly1-e89f6d85a466/ + self.circulation_pump_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_SHELLY) + + super().__init__(mqtt_client) + + # + # Functionality initialisation + # + self.circulation_pump = timer_on_activation(self.circulation_pump_shelly, devices.shelly.KEY_OUTPUT_0, 10*60) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + + self.gui_circulation_pump = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_GUI) + self.gui_circulation_pump.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.circulation_pump_shelly.set_output_0_mcb) + self.circulation_pump_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_circulation_pump.set_state_mcb) + self.circulation_pump.add_callback(timer_on_activation.KEY_TIMER, None, self.gui_circulation_pump.set_timer_mcb, True) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switching( mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_VIDEV, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0 ) - self.circulation_pump_videv = videv_switching( + self.circulation_pump_videv = videv_switching_timer( mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_VIDEV, - self.circulation_pump.main_light_shelly, devices.shelly.KEY_OUTPUT_0 + self.circulation_pump_shelly, devices.shelly.KEY_OUTPUT_0, + self.circulation_pump, timer_on_activation.KEY_TIMER ) def all_off(self, device=None, key=None, data=None): - self.circulation_pump.all_off(device, key, data) + self.circulation_pump_shelly.set_output_0(False) + self.circulation_pump_shelly.set_output_1(False) return super().all_off(device, key, data) -class first_floor_east_dining(room_shelly): +class first_floor_east_dining(room): def __init__(self, mqtt_client): + # + # Device initialisation + # # http://shelly1l-84CCA8ADD055 - super().__init__(mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_SHELLY, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_GUI) + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_SHELLY) self.floorlamp_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_POWERPLUG) if config.CHRISTMAS: self.garland_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_POWERPLUG) - # + super().__init__(mqtt_client) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + self.gui_floorlamp = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_GUI) - # - # Callback initialisation - # - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_powerplug.set_output_0_mcb, True) self.gui_floorlamp.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.floorlamp_powerplug.set_output_0_mcb) self.floorlamp_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_floorlamp.set_state_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Functionality initialisation + # + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_powerplug.set_output_0_mcb, True) + + # + # Virtual Device Interface # self.main_light_videv = videv_switching( mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_VIDEV, @@ -72,6 +124,11 @@ class first_floor_east_dining(room_shelly): mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_VIDEV, self.floorlamp_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0 ) + if config.CHRISTMAS: + self.garland_videv = videv_switching( + mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_VIDEV, + self.garland_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0 + ) def all_off(self, device=None, key=None, data=None): super().all_off(device, key, data) @@ -80,46 +137,105 @@ class first_floor_east_dining(room_shelly): self.garland_powerplug.set_output_0(False) -class first_floor_east_sleep(room_shelly_tradfri_light): +class first_floor_east_sleep(room): def __init__(self, mqtt_client): - # http://shelly1l-E8DB84A254C7 - super().__init__(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_SHELLY, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_GUI, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_ZIGBEE) - # bed light - self.bed_light_di_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_ZIGBEE) - self.gui_bed_light_di = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_GUI) - self.bed_light_ma_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_POWERPLUG) - self.gui_bed_light_ma = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_GUI) # + # Device initialisation + # + # http://shelly1l-E8DB84A254C7 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_SHELLY) + self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_ZIGBEE) + self.bed_light_di_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_ZIGBEE) + self.bed_light_ma_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_POWERPLUG) + self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_ZIGBEE) self.button_tradfri = devices.tradfri_button(mqtt_client, config.TOPIC_FFE_SLEEP_INPUT_DEVICE) + + super().__init__(mqtt_client) + + # + # Functionality initialisation + # # button / brightness function - self.brightness_functions = brightness_choose_n_action(mqtt_client, self.button_tradfri, config.TOPIC_FFE_SLEEP_DEVICE_CHOOSER_LED) + self.brightness_functions = brightness_choose_n_action(self.button_tradfri) self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0) self.brightness_functions.add(self.bed_light_di_tradfri, self.bed_light_di_tradfri, devices.tradfri_light.KEY_OUTPUT_0) - # radiator valve - self.radiator_function = radiator_function(mqtt_client, config.TOPIC_FFE_SLEEP_RADIATOR_VALVE_ZIGBEE, - config.TOPIC_FFE_SLEEP_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_FFE_SLEEP) - # - # Callback initialisation - # - # on/off with button + + # button / main light self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_TOGGLE, self.main_light_shelly.toggle_output_0_mcb) + # button / bed light self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT, self.bed_light_di_tradfri.toggle_output_0_mcb) self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT_LONG, self.bed_light_ma_powerplug.toggle_output_0_mcb) + # heating function + self.heating_function = heating_function(self.heating_valve, config.DEFAULT_TEMPERATURE_FFE_SLEEP) + + ##### TEMPORARY ################################################################################################################### + # main light + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb) # bed light - # switch + self.gui_bed_light_di = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_GUI) self.gui_bed_light_di.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.bed_light_di_tradfri.set_output_0_mcb) self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_bed_light_di.set_state_mcb) - self.gui_bed_light_ma.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.bed_light_ma_powerplug.set_output_0_mcb) - self.bed_light_ma_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_bed_light_ma.set_state_mcb) - # brightness and color temperature self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_bed_light_di.set_enable_mcb) self.gui_bed_light_di.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.bed_light_di_tradfri.set_brightness_mcb) self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_bed_light_di.set_enable_mcb) self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_bed_light_di.set_brightness_mcb) + + self.gui_bed_light_ma = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_GUI) + self.gui_bed_light_ma.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.bed_light_ma_powerplug.set_output_0_mcb) + self.bed_light_ma_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_bed_light_ma.set_state_mcb) + + # heating + self.gui_heating = devices.nodered_gui_radiator(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_GUI) + self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_temperature_mcb, True) + + def set_heating_setpoint(device, key, data): + self.heating_function.set(heating_function.KEY_USER_TEMPERATURE_SETPOINT, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, set_heating_setpoint) + self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_setpoint_temperature_mcb, True) + + def boost(device, key, data): + self.heating_function.set(heating_function.KEY_START_BOOST, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, boost) + + def setpoint_to_default(device, key, data): + self.heating_function.set(heating_function.KEY_SET_DEFAULT_TEMPERATURE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, setpoint_to_default) + + def away_mode(device, key, data): + self.heating_function.set(heating_function.KEY_AWAY_MODE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, away_mode) + self.heating_function.add_callback(heating_function.KEY_AWAY_MODE, None, self.gui_heating.set_away_mcb) + + def summer_mode(device, key, data): + self.heating_function.set(heating_function.KEY_SUMMER_MODE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, summer_mode) + self.heating_function.add_callback(heating_function.KEY_SUMMER_MODE, None, self.gui_heating.set_summer_mcb) + + self.heating_function.add_callback(heating_function.KEY_BOOST_TIMER, None, self.gui_heating.set_timer_mcb) + + # active device led + self.gui_led_active_device = devices.nodered_gui_leds(mqtt_client, config.TOPIC_FFE_SLEEP_DEVICE_CHOOSER_LED) + + def update_active_device_led(device, key, data): + for i in range(0, len(self.brightness_functions.brightness_device_list)): + self.gui_led_active_device.set_led(devices.nodered_gui_leds.KEY_LED_LIST[i], data == i) + self.brightness_functions.add_callback(brightness_choose_n_action.KEY_ACTIVE_DEVICE, None, update_active_device_led, True) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switch_brightness_color_temp( mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_VIDEV, @@ -136,6 +252,14 @@ class first_floor_east_sleep(room_shelly_tradfri_light): mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_VIDEV, self.bed_light_ma_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0 ) + self.heating_function_videv = videv_heating( + mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_VIDEV, + self.heating_function + ) + self.brightness_functions_device_videv = videv_multistate( + mqtt_client, config.TOPIC_FFE_SLEEP_ACTIVE_BRIGHTNESS_DEVICE_VIDEV, + brightness_choose_n_action.KEY_ACTIVE_DEVICE, self.brightness_functions, 2 + ) def all_off(self, device=None, key=None, data=None): super().all_off(device, key, data) @@ -143,42 +267,57 @@ class first_floor_east_sleep(room_shelly_tradfri_light): self.bed_light_ma_powerplug.set_output_0(False) -class first_floor_east_living(room_shelly_tradfri_light): +class first_floor_east_living(room): def __init__(self, mqtt_client): - # http://shelly1l-3C6105E3F910 - super().__init__(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_SHELLY, - config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_GUI, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_ZIGBEE) - for i in range(1, 7): - setattr(self, 'floorlamp_tradfri_%d' % i, devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_ZIGBEE % i)) # + # Device initialisation + # + # http://shelly1l-3C6105E3F910 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_SHELLY) + self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_ZIGBEE) + self.floorlamp_tradfri = devices.group( + *[devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_ZIGBEE % i) for i in range(1, 7)]) if config.CHRISTMAS: self.powerplug_xmas_tree = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_POWERPLUG) self.powerplug_xmas_star = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_STAR_POWERPLUG) + + super().__init__(mqtt_client) + # + # Functionality initialisation + # + # floor lamp synchronisation with main_light + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_tradfri.set_output_0_mcb, True) + + ##### TEMPORARY ################################################################################################################### + # main light + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb) + self.gui_floorlamp = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_GUI) - # + self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.floorlamp_tradfri.set_output_0_mcb) + self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.floorlamp_tradfri.set_brightness_mcb) + self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.floorlamp_tradfri.set_color_temp_mcb) + self.floorlamp_tradfri[0].add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_floorlamp.set_state_mcb) + self.floorlamp_tradfri[0].add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_floorlamp.set_enable_mcb) + self.floorlamp_tradfri[0].add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_floorlamp.set_brightness_mcb) + self.floorlamp_tradfri[0].add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_floorlamp.set_color_temp_mcb) + if config.CHRISTMAS: self.gui_xmas_tree = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_GUI) - # - # Callback initialisation - # - - # floor lamp - for device in self.__floorlamp_devices__(): - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, device.set_output_0_mcb, True) - self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_STATE, None, device.set_output_0_mcb) - self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, device.set_brightness_mcb) - self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, device.set_color_temp_mcb) - self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_floorlamp.set_state_mcb) - self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_floorlamp.set_enable_mcb) - self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_floorlamp.set_brightness_mcb) - self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_floorlamp.set_color_temp_mcb) - # - if config.CHRISTMAS: + self.powerplug_xmas_tree.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.powerplug_xmas_star.set_output_0_mcb) self.powerplug_xmas_tree.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_xmas_tree.set_state_mcb) self.gui_xmas_tree.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_xmas_tree.set_output_0_mcb) - # - self.powerplug_xmas_tree.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.powerplug_xmas_star.set_output_0_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switch_brightness_color_temp( mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_VIDEV, @@ -188,9 +327,9 @@ class first_floor_east_living(room_shelly_tradfri_light): ) self.floorlamp_videv = videv_switch_brightness_color_temp( mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_VIDEV, - self.floorlamp_tradfri_1, devices.tradfri_light.KEY_OUTPUT_0, - self.floorlamp_tradfri_1, devices.tradfri_light.KEY_BRIGHTNESS, - self.floorlamp_tradfri_1, devices.tradfri_light.KEY_COLOR_TEMP + self.floorlamp_tradfri, devices.tradfri_light.KEY_OUTPUT_0, + self.floorlamp_tradfri, devices.tradfri_light.KEY_BRIGHTNESS, + self.floorlamp_tradfri, devices.tradfri_light.KEY_COLOR_TEMP ) if config.CHRISTMAS: self.xmas_tree_videv = videv_switching( @@ -200,14 +339,7 @@ class first_floor_east_living(room_shelly_tradfri_light): def all_off(self, device=None, key=None, data=None): super().all_off(device, key, data) - for floorlamp in self.__floorlamp_devices__(): - floorlamp.set_output_0(False) + self.floorlamp_tradfri.set_output_0(False) if config.CHRISTMAS: self.powerplug_xmas_tree.set_output_0(False) self.powerplug_xmas_star.set_output_0(False) - - def __floorlamp_devices__(self): - rv = [] - for i in range(1, 7): - rv.append(getattr(self, 'floorlamp_tradfri_%d' % i)) - return rv diff --git a/function/first_floor_west.py b/function/first_floor_west.py index 221363f..2798a52 100644 --- a/function/first_floor_west.py +++ b/function/first_floor_west.py @@ -5,9 +5,9 @@ import config import devices import logging -from function.modules import radiator_function -from function.rooms import room_shelly, room_shelly_tradfri_light -from function.videv import videv_switch_brightness, videv_switch_brightness_color_temp +from function.modules import heating_function +from function.rooms import room +from function.videv import videv_switch_brightness, videv_switch_brightness_color_temp, videv_heating try: @@ -17,10 +17,30 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) -class first_floor_west_julian(room_shelly_tradfri_light): - # http://shelly1l-3C6105E43452 +class first_floor_west_julian(room): def __init__(self, mqtt_client): - super().__init__(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_SHELLY, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_GUI, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_ZIGBEE) + # + # Device initialisation + # + # http://shelly1l-3C6105E43452 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_SHELLY) + self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_ZIGBEE) + super().__init__(mqtt_client) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switch_brightness_color_temp( mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_VIDEV, @@ -30,21 +50,85 @@ class first_floor_west_julian(room_shelly_tradfri_light): ) -class first_floor_west_bath(object): +class first_floor_west_bath(room): def __init__(self, mqtt_client): - # radiator valve - self.radiator_function = radiator_function(mqtt_client, config.TOPIC_FFW_BATH_RADIATOR_VALVE_ZIGBEE, - config.TOPIC_FFW_BATH_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_FFW_BATH) + # + # Device initialisation + # + self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_ZIGBEE) + super().__init__(mqtt_client) + # + # Functionality initialisation + # + # heating function + self.heating_function = heating_function(self.heating_valve, config.DEFAULT_TEMPERATURE_FFW_BATH) + + ##### TEMPORARY ################################################################################################################### + self.gui_heating = devices.nodered_gui_radiator(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_GUI) + self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_temperature_mcb, True) + + def set_heating_setpoint(device, key, data): + self.heating_function.set(heating_function.KEY_USER_TEMPERATURE_SETPOINT, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, set_heating_setpoint) + self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_setpoint_temperature_mcb, True) + + def boost(device, key, data): + self.heating_function.set(heating_function.KEY_START_BOOST, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, boost) + + def setpoint_to_default(device, key, data): + self.heating_function.set(heating_function.KEY_SET_DEFAULT_TEMPERATURE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, setpoint_to_default) + + def away_mode(device, key, data): + self.heating_function.set(heating_function.KEY_AWAY_MODE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, away_mode) + self.heating_function.add_callback(heating_function.KEY_AWAY_MODE, None, self.gui_heating.set_away_mcb) + + def summer_mode(device, key, data): + self.heating_function.set(heating_function.KEY_SUMMER_MODE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, summer_mode) + self.heating_function.add_callback(heating_function.KEY_SUMMER_MODE, None, self.gui_heating.set_summer_mcb) + + self.heating_function.add_callback(heating_function.KEY_BOOST_TIMER, None, self.gui_heating.set_timer_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface + # + self.heating_function_videv = videv_heating( + mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_VIDEV, + self.heating_function + ) def all_off(self): pass -class first_floor_west_living(room_shelly_tradfri_light): - # http://shelly1l-84CCA8ACE6A1 +class first_floor_west_living(room): def __init__(self, mqtt_client): - super().__init__(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_SHELLY, - config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_GUI, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_ZIGBEE) + # + # Device initialisation + # + # http://shelly1l-84CCA8ACE6A1 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_SHELLY) + self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_ZIGBEE) + super().__init__(mqtt_client) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switch_brightness_color_temp( mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_VIDEV, @@ -54,10 +138,28 @@ class first_floor_west_living(room_shelly_tradfri_light): ) -class first_floor_west_sleep(room_shelly_tradfri_light): - # http://shelly1-3494546A51F2 +class first_floor_west_sleep(room): def __init__(self, mqtt_client): - super().__init__(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_SHELLY, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_GUI, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_ZIGBEE) + # + # Device initialisation + # + # http://shelly1-3494546A51F2 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_SHELLY) + self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_ZIGBEE) + super().__init__(mqtt_client) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switch_brightness( mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_VIDEV, diff --git a/function/ground_floor_west.py b/function/ground_floor_west.py index f203f6b..62ef37b 100644 --- a/function/ground_floor_west.py +++ b/function/ground_floor_west.py @@ -4,10 +4,10 @@ import config import devices -from function.modules import brightness_choose_n_action, radiator_function +from function.modules import brightness_choose_n_action, heating_function import logging -from function.rooms import room_shelly, room_shelly_tradfri_light, room_shelly_silvercrest_light -from function.videv import videv_switching, videv_switch_brightness_color_temp +from function.rooms import room +from function.videv import videv_switching, videv_switch_brightness_color_temp, videv_heating, videv_multistate import task try: @@ -17,16 +17,39 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) -class ground_floor_west_floor(room_shelly_silvercrest_light): - # http://shelly1l-84CCA8AD1148 +class ground_floor_west_floor(room): def __init__(self, mqtt_client): - super().__init__(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_SHELLY, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_GUI, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_1_ZIGBEE) # - # Callback initialisation + # Device initialisation # - self.main_light_tradfri_2 = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_2_ZIGBEE) - self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.main_light_tradfri_2.set_brightness_mcb) - self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.main_light_tradfri_2.set_color_temp_mcb) + # http://shelly1l-84CCA8AD1148 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_SHELLY) + self.main_light_tradfri = devices.group( + devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 1), + devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 2) + ) + super().__init__(mqtt_client) + + # + # Functionality initialisation + # + # Request silvercrest data of lead light after power on + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.main_light_tradfri[0].request_data, True) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb) + self.main_light_tradfri[0].add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb) + self.main_light_tradfri[0].add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switch_brightness_color_temp( mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_VIDEV, @@ -35,26 +58,71 @@ class ground_floor_west_floor(room_shelly_silvercrest_light): self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP ) - def send_init_message_main_light(self): - super().send_init_message_main_light() - self.main_light_tradfri_2.request_data() - -class ground_floor_west_marion(room_shelly): - # http://shelly1l-E8DB84A1E067 +class ground_floor_west_marion(room): def __init__(self, mqtt_client): - super().__init__(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_SHELLY, config.TOPIC_GFW_MARION_MAIN_LIGHT_GUI) - # radiator valve - self.radiator_function = radiator_function(mqtt_client, config.TOPIC_GFW_MARION_RADIATOR_VALVE_ZIGBEE, - config.TOPIC_GFW_MARION_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_GFW_MARION) + # + # Device initialisation + # + # http://shelly1l-E8DB84A1E067 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_SHELLY) + self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_ZIGBEE) + super().__init__(mqtt_client) + + # + # Functionality initialisation + # + # heating function + self.heating_function = heating_function(self.heating_valve, config.DEFAULT_TEMPERATURE_GFW_MARION) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + + self.gui_heating = devices.nodered_gui_radiator(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_GUI) + self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_temperature_mcb, True) + + def set_heating_setpoint(device, key, data): + self.heating_function.set(heating_function.KEY_USER_TEMPERATURE_SETPOINT, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, set_heating_setpoint) + self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_setpoint_temperature_mcb, True) + + def boost(device, key, data): + self.heating_function.set(heating_function.KEY_START_BOOST, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, boost) + + def setpoint_to_default(device, key, data): + self.heating_function.set(heating_function.KEY_SET_DEFAULT_TEMPERATURE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, setpoint_to_default) + + def away_mode(device, key, data): + self.heating_function.set(heating_function.KEY_AWAY_MODE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, away_mode) + self.heating_function.add_callback(heating_function.KEY_AWAY_MODE, None, self.gui_heating.set_away_mcb) + + def summer_mode(device, key, data): + self.heating_function.set(heating_function.KEY_SUMMER_MODE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, summer_mode) + self.heating_function.add_callback(heating_function.KEY_SUMMER_MODE, None, self.gui_heating.set_summer_mcb) + + self.heating_function.add_callback(heating_function.KEY_BOOST_TIMER, None, self.gui_heating.set_timer_mcb) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switching( mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_VIDEV, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0 ) + self.heating_function_videv = videv_heating( + mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_VIDEV, + self.heating_function + ) -class ground_floor_west_dirk(room_shelly_tradfri_light): +class ground_floor_west_dirk(room): STATE_ACTIVE_DEVICE_MAIN_LIGHT = 0 STATE_ACTIVE_DEVICE_DESK_LIGHT = 1 STATE_ACTIVE_DEVICE_AMPLIFIER = 2 @@ -73,82 +141,126 @@ class ground_floor_west_dirk(room_shelly_tradfri_light): AUDIO_SOURCE_CD = 1 AUDIO_SOURCE_RASPI = 2 - # http://shelly1l-3C6105E44F27 def __init__(self, mqtt_client): - super().__init__(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_SHELLY, config.TOPIC_GFW_DIRK_MAIN_LIGHT_GUI, config.TOPIC_GFW_DIRK_MAIN_LIGHT_ZIGBEE) # + # Device initialisation + # + # http://shelly1l-3C6105E44F27 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_SHELLY) + self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_ZIGBEE) self.powerplug_common = devices.my_powerplug(mqtt_client, config.TOPIC_GFW_DIRK_POWERPLUG) self.desk_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_ZIGBEE) self.button_tradfri = devices.tradfri_button(mqtt_client, config.TOPIC_GFW_DIRK_INPUT_DEVICE) - # - self.gui_desk_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_GUI) - # - self.gui_amplifier = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_GUI) - self.gui_cd_player = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_CD_PLAYER_GUI) - self.gui_pc_dock = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_GUI) - # self.remote_amplifier = devices.remote(mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_REMOTE) + self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_ZIGBEE) + super().__init__(mqtt_client) # - self.brightness_functions = brightness_choose_n_action(mqtt_client, self.button_tradfri, config.TOPIC_GFW_DIRK_DEVICE_CHOOSER_LED) + # Functionality initialisation + # + # Button - Brightness functionality + self.brightness_functions = brightness_choose_n_action(self.button_tradfri) self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0) self.brightness_functions.add(self.desk_light_tradfri, self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT) self.brightness_functions.add(self.remote_amplifier, self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER) - # - self.spotify_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_SPOTIFY) - self.mpd_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_MPD) - # radiator valve - self.radiator_function = radiator_function(mqtt_client, config.TOPIC_GFW_DIRK_RADIATOR_VALVE_ZIGBEE, - config.TOPIC_GFW_DIRK_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_GFW_DIRK) - # - self.delayed_task = task.delayed(1.0, self.send_audio_source) - # - # Callback initialisation - # - - # main light + # Button - Main light self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_TOGGLE, self.main_light_shelly.toggle_output_0_mcb) - - # desk light - # switch + # Button - Desk light self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT, self.powerplug_common.toggle_output_1_mcb) - self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.powerplug_common.set_output_1_mcb) - self.powerplug_common.add_callback(self.KEY_POWERPLUG_DESK_LIGHT, None, self.gui_desk_light.set_state_mcb) - # brightness and color temp - self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.desk_light_tradfri.set_brightness_mcb) - self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.desk_light_tradfri.set_color_temp_mcb) - self.powerplug_common.add_callback(self.KEY_POWERPLUG_DESK_LIGHT, None, self.gui_desk_light.set_enable_mcb) - self.desk_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_desk_light.set_brightness_mcb) - self.desk_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_desk_light.set_color_temp_mcb) - - # amplifier + # Button - Amplifier self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT_LONG, self.powerplug_common.toggle_output_0_mcb) - self.gui_amplifier.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_common.set_output_0_mcb) - self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, None, self.gui_amplifier.set_state_mcb) - # amplifier auto on - self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.cd_amplifier_synchronisation, True) - self.spotify_state.add_callback(devices.status.KEY_STATE, None, self.raspi_amplifier_synchronisation, True) - self.mpd_state.add_callback(devices.status.KEY_STATE, None, self.raspi_amplifier_synchronisation, True) - # audio source + # Button - CD player + self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG, + self.powerplug_common.toggle_output_2_mcb) + # Button - PC dock + self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT, + self.powerplug_common.toggle_output_3_mcb) + + # Mediaplayer - Spotify / MPD state + self.spotify_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_SPOTIFY) + self.mpd_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_MPD) + # Mediaplayer - Amplifier auto on + self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.powerplug_common.set_output_0_mcb, True) + self.spotify_state.add_callback(devices.status.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True) + self.mpd_state.add_callback(devices.status.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True) + # Mediaplayer - Audio source selection self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, True, self.audio_source_selector, True) self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, True, self.audio_source_selector, True) self.spotify_state.add_callback(devices.status.KEY_STATE, True, self.audio_source_selector, True) self.mpd_state.add_callback(devices.status.KEY_STATE, True, self.audio_source_selector, True) self.audio_source = self.AUDIO_SOURCE_PC - # cd player - self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG, - self.powerplug_common.toggle_output_2_mcb) + # heating function + self.heating_function = heating_function(self.heating_valve, config.DEFAULT_TEMPERATURE_GFW_DIRK) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_GUI) + self.gui_desk_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_GUI) + self.gui_amplifier = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_GUI) + self.gui_cd_player = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_CD_PLAYER_GUI) + self.gui_pc_dock = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_GUI) + # Callback initialisation + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb) + self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb) + self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.powerplug_common.set_output_1_mcb) + self.powerplug_common.add_callback(self.KEY_POWERPLUG_DESK_LIGHT, None, self.gui_desk_light.set_state_mcb) + self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.desk_light_tradfri.set_brightness_mcb) + self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.desk_light_tradfri.set_color_temp_mcb) + self.powerplug_common.add_callback(self.KEY_POWERPLUG_DESK_LIGHT, None, self.gui_desk_light.set_enable_mcb) + self.desk_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_desk_light.set_brightness_mcb) + self.desk_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_desk_light.set_color_temp_mcb) + self.gui_amplifier.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_common.set_output_0_mcb) + self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, None, self.gui_amplifier.set_state_mcb) self.gui_cd_player.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_common.set_output_2_mcb) self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.gui_cd_player.set_state_mcb) - - # pc dock - self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT, - self.powerplug_common.toggle_output_3_mcb) self.gui_pc_dock.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_common.set_output_3_mcb) self.powerplug_common.add_callback(self.KEY_POWERPLUG_PC_DOCK, None, self.gui_pc_dock.set_state_mcb) + + self.gui_heating = devices.nodered_gui_radiator(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_GUI) + + def set_heating_setpoint(device, key, data): + self.heating_function.set(heating_function.KEY_USER_TEMPERATURE_SETPOINT, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, set_heating_setpoint) + self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_setpoint_temperature_mcb, True) + + def boost(device, key, data): + self.heating_function.set(heating_function.KEY_START_BOOST, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, boost) + + def setpoint_to_default(device, key, data): + self.heating_function.set(heating_function.KEY_SET_DEFAULT_TEMPERATURE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, setpoint_to_default) + + def away_mode(device, key, data): + self.heating_function.set(heating_function.KEY_AWAY_MODE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, away_mode) + self.heating_function.add_callback(heating_function.KEY_AWAY_MODE, None, self.gui_heating.set_away_mcb) + + def summer_mode(device, key, data): + self.heating_function.set(heating_function.KEY_SUMMER_MODE, data) + self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, summer_mode) + self.heating_function.add_callback(heating_function.KEY_SUMMER_MODE, None, self.gui_heating.set_summer_mcb) + + self.heating_function.add_callback(heating_function.KEY_BOOST_TIMER, None, self.gui_heating.set_timer_mcb) + + self.gui_led_active_device = devices.nodered_gui_leds(mqtt_client, config.TOPIC_GFW_DIRK_DEVICE_CHOOSER_LED) + + def update_active_device_led(device, key, data): + for i in range(0, len(self.brightness_functions.brightness_device_list)): + self.gui_led_active_device.set_led(devices.nodered_gui_leds.KEY_LED_LIST[i], data == i) + self.brightness_functions.add_callback(brightness_choose_n_action.KEY_ACTIVE_DEVICE, None, update_active_device_led, True) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface # self.main_light_videv = videv_switch_brightness_color_temp( mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_VIDEV, @@ -174,19 +286,24 @@ class ground_floor_west_dirk(room_shelly_tradfri_light): mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_VIDEV, self.powerplug_common, self.KEY_POWERPLUG_PC_DOCK ) + self.heating_function_videv = videv_heating( + mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_VIDEV, + self.heating_function + ) + self.brightness_functions_device_videv = videv_multistate( + mqtt_client, config.TOPIC_GFW_DIRK_ACTIVE_BRIGHTNESS_DEVICE_VIDEV, + brightness_choose_n_action.KEY_ACTIVE_DEVICE, self.brightness_functions, 3 + ) + + # + # Other stuff + # + self.delayed_task_remote = task.delayed(1.0, self.send_audio_source) def all_off(self, device=None, key=None, data=None): super().all_off(device, key, data) self.powerplug_common.set_output_all(False) - def cd_amplifier_synchronisation(self, device, key, data): - logger.info("Syncing \"%s\" amplifier with cd player: %s", type(self).__name__, data) - self.powerplug_common.set_output(self.KEY_POWERPLUG_AMPLIFIER, data) - - def raspi_amplifier_synchronisation(self, device, key, data): - logger.info("Syncing \"%s\" amplifier with raspi player: %s", type(self).__name__, data) - self.powerplug_common.set_output(self.KEY_POWERPLUG_AMPLIFIER, data) - def audio_source_selector(self, device, key, data): if device == self.powerplug_common and key == self.KEY_POWERPLUG_CD_PLAYER: # switch on of cd player @@ -196,7 +313,7 @@ class ground_floor_west_dirk(room_shelly_tradfri_light): self.audio_source = self.AUDIO_SOURCE_RASPI elif device == self.powerplug_common and key == self.KEY_POWERPLUG_AMPLIFIER: # switch on of amplifier -> select source and reset stored source value - self.delayed_task.run() + self.delayed_task_remote.run() def send_audio_source(self): if self.audio_source == self.AUDIO_SOURCE_PC: diff --git a/function/modules.py b/function/modules.py index 5981c38..5c32f2c 100644 --- a/function/modules.py +++ b/function/modules.py @@ -11,10 +11,10 @@ Targets: - Method .add_calback(key, data, callback, on_change_only=False) to register videv actualisation on changes """ -import config +from base import common_base import devices -from function.db import get_gui_radiator_data, set_gui_radiator_data -from function.rooms import room_shelly +from function.db import get_radiator_data, set_radiator_data +from function.helpers import now, sunset_time, sunrise_time import logging import task @@ -25,34 +25,25 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) -class base(object): - def set(key, data): - pass +class brightness_choose_n_action(common_base): + KEY_ACTIVE_DEVICE = 'active_device' + # + DEFAULT_VALUES = {KEY_ACTIVE_DEVICE: None} - def add_calback(self, key, data, callback, on_change_only=False): - pass - - -class brightness_choose_n_action(object): - def __init__(self, mqtt_client, button_tradfri, topic_led): - self.gui_led_active_device = devices.nodered_gui_leds(mqtt_client, topic_led) + def __init__(self, button_tradfri): + super().__init__() # brightness change - button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, - devices.tradfri_button.ACTION_BRIGHTNESS_DOWN_LONG, self.brightness_action) - button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_BRIGHTNESS_UP_LONG, self.brightness_action) - button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, - devices.tradfri_button.ACTION_BRIGHTNESS_DOWN_RELEASE, self.brightness_action) - button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, - devices.tradfri_button.ACTION_BRIGHTNESS_UP_RELEASE, self.brightness_action) + button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_DOWN_LONG, self.brightness_action) + button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_UP_LONG, self.brightness_action) + button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_DOWN_RELEASE, self.brightness_action) + button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_UP_RELEASE, self.brightness_action) # device change - button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_BRIGHTNESS_UP, self.choose_next_device) - button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_BRIGHTNESS_DOWN, self.choose_prev_device) + button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_UP, self.choose_next_device) + button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_DOWN, self.choose_prev_device) # self.brightness_device_list = [] self.callback_device_list = [] self.device_states = [] - self.active_device_state = None - self.update_active_device_led() def add(self, brightness_device, callback_device, callback_key): """ @@ -74,42 +65,33 @@ class brightness_choose_n_action(object): def device_state_action(self, device, key, data): self.device_states[self.callback_device_list.index((device, key))] = data if data is True: - self.active_device_state = self.callback_device_list.index((device, key)) - self.update_active_device_led() + self.set(self.KEY_ACTIVE_DEVICE, self.callback_device_list.index((device, key))) else: self.choose_next_device() - def update_active_device_led(self): - for i in range(0, len(self.brightness_device_list)): - self.gui_led_active_device.set_led(devices.nodered_gui_leds.KEY_LED_LIST[i], self.active_device_state == i) - def choose_prev_device(self, device=None, key=None, data=None): - if self.active_device_state is not None: - start_value = self.active_device_state + if self[self.KEY_ACTIVE_DEVICE] is not None: + start_value = self[self.KEY_ACTIVE_DEVICE] for i in range(0, len(self.brightness_device_list)): target_state = (start_value - i - 1) % (len(self.brightness_device_list)) if self.device_states[target_state]: - self.active_device_state = target_state - self.update_active_device_led() + self.set(self.KEY_ACTIVE_DEVICE, target_state) return - self.active_device_state = None - self.update_active_device_led() + self.set(self.KEY_ACTIVE_DEVICE, None) def choose_next_device(self, device=None, key=None, data=None): - if self.active_device_state is not None: - start_value = self.active_device_state + if self[self.KEY_ACTIVE_DEVICE] is not None: + start_value = self[self.KEY_ACTIVE_DEVICE] for i in range(0, len(self.brightness_device_list)): target_state = (start_value + i + 1) % (len(self.brightness_device_list)) if self.device_states[target_state]: - self.active_device_state = target_state - self.update_active_device_led() + self.set(self.KEY_ACTIVE_DEVICE, target_state) return - self.active_device_state = None - self.update_active_device_led() + self.set(self.KEY_ACTIVE_DEVICE, None) def brightness_action(self, device, key, data): - if self.active_device_state is not None: - target = self.brightness_device_list[self.active_device_state] + if self[self.KEY_ACTIVE_DEVICE] is not None: + target = self.brightness_device_list[self[self.KEY_ACTIVE_DEVICE]] if data == devices.tradfri_button.ACTION_BRIGHTNESS_UP_LONG: logger.info("Increasing \"%s\" - %s", type(self).__name__, target.topic) target.default_inc() @@ -120,140 +102,213 @@ class brightness_choose_n_action(object): target.default_stop() -class circulation_pump(room_shelly): - def __init__(self, mqtt_client): - super().__init__(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_SHELLY, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_GUI) +class timer_on_activation(common_base): + KEY_TIMER = 'timer' + # + DEFAULT_VALUES = { + KEY_TIMER: 0 + } + + def __init__(self, sw_device, sw_key, timer_reload_value): + super().__init__() # - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.circ_pump_actions, True) + self.timer_reload_value = timer_reload_value # - self.gui_main_light.set_timer('-') + sw_device.add_callback(sw_key, None, self.circ_pump_actions, True) # self.ct = task.periodic(6, self.cyclic_task) - self.pump_timer = None - # self.ct.run() def circ_pump_actions(self, device, key, data): if data is True: - self.pump_timer = 10 - self.gui_main_light.set_timer(self.pump_timer) + self.set(self.KEY_TIMER, self.timer_reload_value) else: - self.pump_timer = None - self.gui_main_light.set_timer('-') + self.set(self.KEY_TIMER, 0) def cyclic_task(self, rt): - if self.pump_timer is not None: - if self.pump_timer <= 0: - self.pump_timer = None - self.gui_main_light.set_timer('-') - else: - self.gui_main_light.set_timer(self.pump_timer) - self.pump_timer -= self.ct.cycle_time / 60 + timer_value = self[self.KEY_TIMER] - self.ct.cycle_time + if timer_value <= 0: + self.set(self.KEY_TIMER, 0) + else: + self.set(self.KEY_TIMER, timer_value) -class radiator_function(object): +class heating_function(common_base): + KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint' + KEY_TEMPERATURE_SETPOINT = 'temperature_setpoint' + KEY_TEMPERATURE_CURRENT = 'temperature_current' + 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' + # BOOST_TEMPERATURE = 30 AWAY_REDUCTION = 5 SUMMER_TEMPERATURE = 5 - def __init__(self, mqtt_client, topic_valve, topic_gui, default_temperature): - self.default_temperature = default_temperature - self.boost_timer = None - # device initialisation - self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, topic_valve) - self.gui_heating = devices.nodered_gui_radiator(mqtt_client, topic_gui) - # db-stored data initialisation - db_data = get_gui_radiator_data(topic_gui) - self.__away_mode__ = db_data[0] or False - self.__summer_mode__ = db_data[1] or False - self.__user_temperature_setpoint__ = db_data[2] or default_temperature - if self.__away_mode__: - self.away_mode(None, None, True) - elif self.__summer_mode__: - self.summer_mode(None, None, True) - else: - self.set_heating_setpoint(None, None, self.__user_temperature_setpoint__) - # callback initialisation - self.heating_valve.add_callback(devices.brennenstuhl_heatingvalve.KEY_TEMPERATURE, None, self.gui_heating.set_temperature_mcb) - self.heating_valve.add_callback(devices.brennenstuhl_heatingvalve.KEY_HEATING_SETPOINT, None, self.get_radiator_setpoint) + def __init__(self, heating_valve, default_temperature): + db_data = get_radiator_data(heating_valve.topic) + super().__init__({ + self.KEY_USER_TEMPERATURE_SETPOINT: db_data[2] or default_temperature, + self.KEY_TEMPERATURE_SETPOINT: db_data[3] or default_temperature, + self.KEY_TEMPERATURE_CURRENT: None, + self.KEY_AWAY_MODE: db_data[0] or False, + self.KEY_SUMMER_MODE: db_data[1] or False, + self.KEY_START_BOOST: True, + self.KEY_SET_DEFAULT_TEMPERATURE: False, + self.KEY_BOOST_TIMER: 0 + }) # - self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, self.set_heating_setpoint) - self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, self.boost) - self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, self.setpoint_to_default) - self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, self.away_mode) - self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, self.summer_mode) + self.default_temperature = default_temperature + self.heating_valve = heating_valve + self.heating_valve.set_heating_setpoint(self[self.KEY_TEMPERATURE_SETPOINT]) + # + self.heating_valve.add_callback(self.heating_valve.KEY_HEATING_SETPOINT, None, self.get_radiator_setpoint) + self.heating_valve.add_callback(self.heating_valve.KEY_TEMPERATURE, None, self.get_radiator_temperature) + # + self.add_callback(self.KEY_USER_TEMPERATURE_SETPOINT, None, self.user_temperature_setpoint, False) + self.add_callback(self.KEY_TEMPERATURE_SETPOINT, None, self.set_heating_setpoint, True) + self.add_callback(self.KEY_AWAY_MODE, None, self.away_mode, True) + self.add_callback(self.KEY_SUMMER_MODE, None, self.summer_mode, True) + self.add_callback(self.KEY_SET_DEFAULT_TEMPERATURE, None, self.setpoint_to_default) + self.add_callback(self.KEY_START_BOOST, True, self.boost, False) + self.add_callback(self.KEY_BOOST_TIMER, 0, self.timer_expired, True) # cyclic task initialisation self.ct = task.periodic(1, self.cyclic_task) self.ct.run() + def timer_expired(self, device, data, key): + self.set(self.KEY_TEMPERATURE_SETPOINT, self[self.KEY_USER_TEMPERATURE_SETPOINT]) + self.heating_valve.logger.info('Timer expired. returning to regular temperature setpoint %.1f°C.', + self[self.KEY_TEMPERATURE_SETPOINT]) + def cyclic_task(self, rt): - if self.boost_timer is not None: - self.gui_heating.set_timer(round(self.boost_timer / 60, 1)) - # - self.boost_timer -= self.ct.cycle_time - if self.boost_timer <= 0: - self.cancel_boost() - self.heating_valve.set_heating_setpoint(self.__user_temperature_setpoint__) - self.heating_valve.logger.info('Timer expired. returning to regular temperature setpoint %.1f°C.', self.__user_temperature_setpoint__) + timer_value = self[self.KEY_BOOST_TIMER] - self.ct.cycle_time + if self[self.KEY_BOOST_TIMER] <= 0: + self.set(self.KEY_BOOST_TIMER, 0) + else: + self.set(self.KEY_BOOST_TIMER, timer_value) - def cancel_boost(self, device=None, key=None, data=None): - if self.boost_timer is not None: - self.boost_timer = None - self.gui_heating.set_timer('-') + def cancel_boost(self): + self.set(self.KEY_BOOST_TIMER, 0, block_callback=[self.timer_expired]) - def update_states(self, away_mode=None, summer_mode=None, user_temperature_setpoint=None): - if away_mode is not None: - self.__away_mode__ = away_mode - if summer_mode is not None: - self.__summer_mode__ = summer_mode - if user_temperature_setpoint is not None: - self.__user_temperature_setpoint__ = user_temperature_setpoint - set_gui_radiator_data(self.gui_heating.topic, self.__away_mode__, self.__summer_mode__, self.__user_temperature_setpoint__) - # - self.gui_heating.set_away(self.__away_mode__) - self.gui_heating.set_summer(self.__summer_mode__) - self.gui_heating.set_enable(not self.__away_mode__ and not self.__summer_mode__) + def set(self, key, data, block_callback=[]): + rv = super().set(key, data, block_callback) + set_radiator_data(self.heating_valve.topic, self[self.KEY_AWAY_MODE], self[self.KEY_SUMMER_MODE], + self[self.KEY_USER_TEMPERATURE_SETPOINT], self[self.KEY_TEMPERATURE_SETPOINT]) + return rv def away_mode(self, device, key, value): if value is True: self.cancel_boost() - self.update_states(away_mode=value, summer_mode=False) - self.heating_valve.set_heating_setpoint(self.__user_temperature_setpoint__ - self.AWAY_REDUCTION) + self.set(self.KEY_SUMMER_MODE, False, [self.summer_mode]) + self.set(self.KEY_TEMPERATURE_SETPOINT, self[self.KEY_USER_TEMPERATURE_SETPOINT] - self.AWAY_REDUCTION) else: - self.update_states(away_mode=value) - self.heating_valve.set_heating_setpoint(self.__user_temperature_setpoint__) + self.set(self.KEY_TEMPERATURE_SETPOINT, self[self.KEY_USER_TEMPERATURE_SETPOINT]) def summer_mode(self, device, key, value): if value is True: self.cancel_boost() - self.update_states(away_mode=False, summer_mode=value) - self.heating_valve.set_heating_setpoint(self.SUMMER_TEMPERATURE) + self.set(self.KEY_AWAY_MODE, False, [self.away_mode]) + self.set(self.KEY_TEMPERATURE_SETPOINT, self.SUMMER_TEMPERATURE) else: - self.update_states(summer_mode=value) - self.heating_valve.set_heating_setpoint(self.__user_temperature_setpoint__) + self.set(self.KEY_TEMPERATURE_SETPOINT, self[self.KEY_USER_TEMPERATURE_SETPOINT]) def boost(self, device, key, data): - if self.boost_timer is None: + if self[self.KEY_BOOST_TIMER] == 0: self.heating_valve.logger.info('Starting boost mode with setpoint %.1f°C.', self.BOOST_TEMPERATURE) - self.boost_timer = 15*60 - self.heating_valve.set_heating_setpoint(self.BOOST_TEMPERATURE) + self.set(self.KEY_BOOST_TIMER, 15*60) + self.set(self.KEY_TEMPERATURE_SETPOINT, self.BOOST_TEMPERATURE) else: - self.boost_timer += 15 * 60 - if self.boost_timer > 60 * 60: - self.boost_timer = 60 * 60 - self.update_states(away_mode=False, summer_mode=False) + self.set(self.KEY_BOOST_TIMER, min(self[self.KEY_BOOST_TIMER] + 15 * 60, 60 * 60)) + self.set(self.KEY_AWAY_MODE, False, [self.away_mode]) + self.set(self.KEY_SUMMER_MODE, False, [self.summer_mode]) def setpoint_to_default(self, device, key, data): - self.set_heating_setpoint(device, key, self.default_temperature) + self.cancel_boost() + self.set(self.KEY_AWAY_MODE, False, [self.away_mode]) + self.set(self.KEY_SUMMER_MODE, False, [self.summer_mode]) + self.set(self.KEY_USER_TEMPERATURE_SETPOINT, self.default_temperature, [self.user_temperature_setpoint]) + self.set(self.KEY_TEMPERATURE_SETPOINT, self.default_temperature) + + def user_temperature_setpoint(self, device, key, data): + self.cancel_boost() + self.set(self.KEY_AWAY_MODE, False, [self.away_mode]) + self.set(self.KEY_SUMMER_MODE, False, [self.summer_mode]) + self.set(self.KEY_TEMPERATURE_SETPOINT, data) def set_heating_setpoint(self, device, key, data): - self.cancel_boost() - self.update_states(away_mode=False, summer_mode=False, user_temperature_setpoint=data) self.heating_valve.set_heating_setpoint(data) def get_radiator_setpoint(self, device, key, data): - if self.boost_timer is None and not self.__away_mode__ and not self.__summer_mode__: - self.update_states(user_temperature_setpoint=data) - else: - self.update_states() - self.gui_heating.set_setpoint_temperature(data) + if self[self.KEY_BOOST_TIMER] == 0 and not self[self.KEY_AWAY_MODE] and not self[self.KEY_SUMMER_MODE]: + self.set(self.KEY_USER_TEMPERATURE_SETPOINT, data, block_callback=[self.set_heating_setpoint]) + + def get_radiator_temperature(self, device, key, data): + self.set(self.KEY_TEMPERATURE_CURRENT, data) + + +class motion_sensor_light(common_base): + KEY_TIMER = 'timer' + KEY_MOTION_SENSOR = 'motion_%d' + KEY_MOTION_SENSOR_0 = 'motion_%d' % 0 + KEY_MOTION_SENSOR_1 = 'motion_%d' % 1 + KEY_MOTION_SENSOR_2 = 'motion_%d' % 2 + KEY_MOTION_SENSOR_3 = 'motion_%d' % 3 + KEY_MOTION_SENSOR_4 = 'motion_%d' % 4 + + def __init__(self, sw_device, sw_method, *args, timer_value=30): + """ + sw_device is the device switching the light, args are 0-n motion sensors + """ + dv = dict.fromkeys([self.KEY_MOTION_SENSOR % i for i in range(0, len(args))]) + for key in dv: + dv[key] = False + dv[self.KEY_TIMER] = 0 + super().__init__(default_values=dv) + # + self.sw_device = sw_device + self.sw_method = sw_method + self.args = args + self.timer_reload_value = timer_value + # + sw_device.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.reload_timer, True) + sw_device.add_callback(devices.shelly.KEY_OUTPUT_0, False, self.reset_timer, True) + for motion_sensor in args: + motion_sensor.add_callback(motion_sensor.KEY_OCCUPANCY, None, self.set_motion_detected, True) + # + self.add_callback(self.KEY_TIMER, 0, self.timer_expired, True) + # + cyclic_task = task.periodic(1, self.cyclic_task) + cyclic_task.run() + + def reload_timer(self, device, key, data): + self.set(self.KEY_TIMER, self.timer_reload_value) + + def reset_timer(self, device=None, key=None, data=None): + self.set(self.KEY_TIMER, 0) + + def set_motion_detected(self, device, key, data): + for sensor_index, arg_device in enumerate(self.args): + if arg_device.topic == device.topic: + break + self.set(self.KEY_MOTION_SENSOR % sensor_index, data) + if now() < sunrise_time(60) or now() > sunset_time(-60): + if data is True: + logger.info("%s: Motion detected - Switching on main light %s", device.topic, self.sw_device.topic) + self.sw_method(True) + + def motion_detected(self): + for i in range(0, len(self.args)): + if self[self.KEY_MOTION_SENSOR % i]: + return True + return False + + def timer_expired(self, device, key, data): + logger.info("No motion and time ran out - Switching off main light %s", self.sw_device.topic) + self.sw_method(False) + + def cyclic_task(self, cyclic_task): + min_value = 10 if self.motion_detected() else 0 + self.set(self.KEY_TIMER, max(min_value, self[self.KEY_TIMER] - cyclic_task.cycle_time)) diff --git a/function/rooms.py b/function/rooms.py index a8acfe2..9309afe 100644 --- a/function/rooms.py +++ b/function/rooms.py @@ -2,9 +2,6 @@ # -*- coding: utf-8 -*- # -import config -import devices -from function.helpers import now, sunset_time, sunrise_time import logging import task @@ -14,135 +11,53 @@ except ImportError: ROOT_LOGGER_NAME = 'root' logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) +# TODO: all_off: getattr and identify switchable devices switch those off (default method of device) +# - all devices are as attributes in the room class +# - implement a all_off blacklist to be initialised while __init__ +# TODO: implement all off and user feedback method (all off save) + class room(object): def __init__(self, mqtt_client): self.mqtt_client = mqtt_client - - -class room_shelly(room): - def __init__(self, mqtt_client, topic_shelly, topic_gui): - super().__init__(mqtt_client) - self.main_light_shelly = devices.shelly(mqtt_client, topic_shelly) - # - self.gui_main_light = devices.nodered_gui_light(mqtt_client, topic_gui) - # - # Callback initialisation - # - self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) # self.block_all_off = False self.last_flash_data = None - self.delayed_task = task.delayed(.25, self.main_light_shelly.toggle_output_0_mcb, None, None, None) + try: + self.delayed_task = task.delayed(.25, self.main_light_shelly.toggle_output_0_mcb, None, None, None) + except AttributeError: + logger.exception("Device self.main_light does not exist!") + self.delayed_task = task.delayed(.25, self.__delayed_task_dummy__, None, None, None) + + def __delayed_task_dummy__(self, device, key, data): + logger.exception("Device self.main_light does not exist!") def all_off(self, device=None, key=None, data=None): 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) + try: + self.main_light_shelly.set_output_0(False) + self.main_light_shelly.set_output_1(False) + except AttributeError: + logger.exception("Device self.main_light does not exist!") 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) + try: + self.main_light_shelly.set_output_0(True) + except AttributeError: + logger.exception("Device self.main_light does not exist!") self.block_all_off = True self.delayed_task.run() def flash_main_light(self, device, key, data): if self.last_flash_data != data and data is True: logger.info("Flashing \"%s\" main light", type(self).__name__) - self.main_light_shelly.toggle_output_0_mcb(device, key, data) + try: + self.main_light_shelly.toggle_output_0_mcb(device, key, data) + except AttributeError: + logger.exception("Device self.main_light does not exist!") self.delayed_task.run() self.last_flash_data = data - - -class room_shelly_motion_sensor(room_shelly): - def __init__(self, mqtt_client, topic_shelly, topic_gui, topic_motion_sensor_1, topic_motion_sensor_2=None, timer_value=30): - super().__init__(mqtt_client, topic_shelly, topic_gui) - self.timer_value = timer_value - self.motion_detected_1 = False - self.motion_detected_2 = False - # - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.reload_timer, True) - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, False, self.reset_timer, True) - # - self.motion_sensor_silvercrest_1 = devices.silvercrest_motion_sensor(mqtt_client, topic_motion_sensor_1) - self.motion_sensor_silvercrest_1.add_callback(devices.silvercrest_motion_sensor.KEY_OCCUPANCY, None, self.set_motion_detected) - # - if topic_motion_sensor_2 is not None: - self.motion_sensor_silvercrest_2 = devices.silvercrest_motion_sensor(mqtt_client, topic_motion_sensor_2) - self.motion_sensor_silvercrest_2.add_callback(devices.silvercrest_motion_sensor.KEY_OCCUPANCY, None, self.set_motion_detected) - # - self.reset_timer() - # - cyclic_task = task.periodic(1, self.cyclic_task) - cyclic_task.run() - - def set_motion_detected(self, device, key, data): - if device == self.motion_sensor_silvercrest_1: - self.motion_detected_1 = data - elif device == self.motion_sensor_silvercrest_2: - self.motion_detected_2 = data - self.gui_main_light.set_led(devices.nodered_gui_light.KEY_LED_0, self.motion_detected_1) - self.gui_main_light.set_led(devices.nodered_gui_light.KEY_LED_1, self.motion_detected_2) - if now() < sunrise_time(60) or now() > sunset_time(-60): - if data is True: - logger.info("%s: Motion detected - Switching on main light %s", device.topic, self.main_light_shelly.topic) - self.main_light_shelly.set_output_0(True) - - def reload_timer(self, device, key, data): - self.main_light_timer = self.timer_value - - def reset_timer(self, device=None, key=None, data=None): - self.main_light_timer = None - self.gui_main_light.set_timer('-') - - def cyclic_task(self, cyclic_task): - if self.main_light_timer is not None: - if self.main_light_timer <= 0: - logger.info("No motion and time ran out - Switching off main light %s", self.main_light_shelly.topic) - self.main_light_shelly.set_output_0(False) - self.main_light_timer = None - self.gui_main_light.set_timer('-') - else: - self.gui_main_light.set_timer(self.main_light_timer) - if (self.motion_detected_1 or self.motion_detected_2) and self.main_light_timer <= self.timer_value / 10: - self.main_light_timer = self.timer_value / 10 - else: - self.main_light_timer -= cyclic_task.cycle_time - - -class room_shelly_tradfri_light(room_shelly): - def __init__(self, mqtt_client, topic_shelly, topic_gui, topic_tradfri_light): - super().__init__(mqtt_client, topic_shelly, topic_gui) - self.main_light_tradfri = devices.tradfri_light(mqtt_client, topic_tradfri_light) - # - # Callback initialisation - # - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb) - self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb) - self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb) - self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb) - self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb) - - -class room_shelly_silvercrest_light(room_shelly_tradfri_light): - def __init__(self, mqtt_client, topic_shelly, topic_gui, topic_tradfri_light): - super().__init__(mqtt_client, topic_shelly, topic_gui, topic_tradfri_light) - # - # Callback initialisation - # - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.get_initial_main_light_data) - # - self.main_light_shelly_last = None - - def get_initial_main_light_data(self, device, key, data): - if data is True and self.main_light_shelly_last != data: - self.send_init_message_main_light() - self.main_light_shelly_last = data - - def send_init_message_main_light(self): - self.main_light_tradfri.request_data() diff --git a/function/stairway.py b/function/stairway.py index adf2e4d..7445da4 100644 --- a/function/stairway.py +++ b/function/stairway.py @@ -4,10 +4,10 @@ import config import devices -from function.modules import brightness_choose_n_action import logging -from function.rooms import room_shelly_motion_sensor -from function.videv import videv_switching +from function.modules import motion_sensor_light +from function.rooms import room +from function.videv import videv_switching_motion try: from config import APP_NAME as ROOT_LOGGER_NAME @@ -16,14 +16,46 @@ except ImportError: logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) -class stairway(room_shelly_motion_sensor): +class stairway(room): def __init__(self, mqtt_client): - # http://shelly1-3494546A9364 - super().__init__(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_SHELLY, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_GUI, - config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_GF, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_FF, - timer_value=config.USER_ON_TIME_STAIRWAYS) # - self.main_light_videv = videv_switching( - mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_VIDEV, - self.main_light_shelly, devices.shelly.KEY_OUTPUT_0 + # Device initialisation + # + # http://shelly1-3494546A9364 + self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_SHELLY) + self.motion_sensor_gf = devices.silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_GF) + self.motion_sensor_ff = devices.silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_FF) + super().__init__(mqtt_client) + + # + # Functionality initialisation + # + self.motion_sensor_light = motion_sensor_light( + self.main_light_shelly, self.main_light_shelly.set_output_0, + self.motion_sensor_gf, self.motion_sensor_ff, + timer_value=config.USER_ON_TIME_STAIRWAYS + ) + + ##### TEMPORARY ################################################################################################################### + self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_GUI) + self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb) + self.motion_sensor_light.add_callback(motion_sensor_light.KEY_TIMER, None, self.gui_main_light.set_timer_mcb) + + def set_led(device, key, data): + if key == motion_sensor_light.KEY_MOTION_SENSOR_0: + self.gui_main_light.set_led(devices.nodered_gui_light.KEY_LED_0, data) + if key == motion_sensor_light.KEY_MOTION_SENSOR_1: + self.gui_main_light.set_led(devices.nodered_gui_light.KEY_LED_1, data) + self.motion_sensor_light.add_callback(motion_sensor_light.KEY_MOTION_SENSOR_0, None, set_led) + self.motion_sensor_light.add_callback(motion_sensor_light.KEY_MOTION_SENSOR_1, None, set_led) + ##### TEMPORARY ################################################################################################################### + + # + # Virtual Device Interface + # + self.main_light_videv = videv_switching_motion( + mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_VIDEV, + self.main_light_shelly, devices.shelly.KEY_OUTPUT_0, + self.motion_sensor_light ) diff --git a/function/videv.py b/function/videv.py index d131ffc..e9e45ce 100644 --- a/function/videv.py +++ b/function/videv.py @@ -10,8 +10,13 @@ Targets: * 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 inspect import json import logging @@ -25,10 +30,10 @@ except ImportError: class base(mqtt_base): - DEFAULT_VALUES = {} + EXEC_RX_FUNC_ALWAYS = [] - def __init__(self, mqtt_client, topic, *args): - super().__init__(mqtt_client, topic) + 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 @@ -53,7 +58,7 @@ class base(mqtt_base): data = json.loads(message.payload) except json.decoder.JSONDecodeError: data = message.payload - if data != self[key]: + if data != self[key] or key in self.EXEC_RX_FUNC_ALWAYS: self.__rx_functionality__(key, data) self.set(key, data) else: @@ -67,8 +72,8 @@ class base(mqtt_base): class base_routing(base): - def __init__(self, mqtt_client, topic, *args): - super().__init__(mqtt_client, topic, *args) + 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 @@ -79,7 +84,10 @@ class base_routing(base): index += 1 # add callbacks for key in self.__device_list__: - self.__device_list__[key].add_callback(self.__device_key__[key], None, self.__device_data__, True) + 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: @@ -88,7 +96,14 @@ class base_routing(base): self.logger.warning("RX passthrough not possible for key %s", key) def __device_data__(self, device, key, data): - l1 = [k for k, v in self.__device_list__.items() if v == device] + l1 = [] + for k, v in self.__device_list__.items(): + if v.__class__.__name__ == "group": + if device in v: + l1.append(k) + else: + if v == 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] @@ -107,10 +122,45 @@ class videv_switching(base_routing): } 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' @@ -144,3 +194,55 @@ class videv_switch_brightness_color_temp(base_routing): (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, 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, device), default_values=dv) + # + device.add_callback(key, 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])