diff --git a/__simulation__/devices.py b/__simulation__/devices.py index cefb326..25a6f69 100644 --- a/__simulation__/devices.py +++ b/__simulation__/devices.py @@ -504,10 +504,19 @@ class gui_light(tradfri_light): AUTOSEND = False # KEY_ENABLE = "enable" + KEY_TIMER = "timer" + KEY_LED_X = "led%d" def __init__(self, mqtt_client, topic, enable_state=True, enable_brightness=False, enable_color_temp=False): super().__init__(mqtt_client, topic, enable_state, enable_brightness, enable_color_temp) self.add_callback(self.KEY_ENABLE, self.print_formatted, None) + self.add_callback(self.KEY_TIMER, self.print_formatted, None) + for i in range(0, 10): + self.add_callback(self.KEY_LED_X % i, self.print_formatted, None) + self.led_names = {} + # + self.maxvalue = None + self.last_printed = None def __init_data__(self, enable_state, enable_brightness, enable_color_temp): data = {} @@ -518,6 +527,9 @@ class gui_light(tradfri_light): data[self.KEY_BRIGHTNESS] = 50 if enable_color_temp: data[self.KEY_COLOR_TEMP] = 5 + data[self.KEY_TIMER] = '-' + for i in range(0, 10): + data[self.KEY_LED_X % i] = False self.store_data(**data) def __rx__(self, client, userdata, message): @@ -559,14 +571,18 @@ class gui_light(tradfri_light): else: print("Unknown command!") + def add_led_name(self, key, name): + self.led_names[key] = name + def print_formatted(self, device, key, value): if value is not None: color = COLOR_GUI_ACTIVE + device = " - ".join(self.topic.split('/')[1:]) if key == self.KEY_STATE: if value == True: - print(color + 10 * ' ' + u'\u25a0' + 9 * ' ' + " - ".join(self.topic.split('/')[1:]) + colored.attr("reset")) + print(color + 10 * ' ' + u'\u25a0' + 9 * ' ' + device + colored.attr("reset")) else: - print(color + 10 * ' ' + u'\u25a1' + 9*' ' + " - ".join(self.topic.split('/')[1:]) + colored.attr("reset")) + print(color + 10 * ' ' + u'\u25a1' + 9 * ' ' + device + colored.attr("reset")) elif key == self.KEY_ENABLE: self.print_formatted(device, self.KEY_BRIGHTNESS, self.data.get(self.KEY_BRIGHTNESS)) self.print_formatted(device, self.KEY_COLOR_TEMP, self.data.get(self.KEY_COLOR_TEMP)) @@ -577,8 +593,26 @@ class gui_light(tradfri_light): sys.stdout.write(color) sys.stdout.write('B' if key == self.KEY_BRIGHTNESS else 'C') sys.stdout.write(percent_bar(value)) - sys.stdout.write("%3d%%" % value + 5 * " ") - print(" - ".join(self.topic.split('/')[1:]) + colored.attr("reset")) + print("%3d%%" % value + 5 * " " + device + colored.attr("reset")) + elif key == self.KEY_TIMER: + if isinstance(value, (int, float, complex)) and not isinstance(value, bool): + if self.maxvalue is None: + self.maxvalue = value + disp_value = value + perc = disp_value / self.maxvalue * 100 + 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(color + 't' + percent_bar(perc) + '%3d%%' % perc + 5 * ' ' + device + ' (%.1f)' % disp_value + colored.attr('reset')) + self.last_printed = perc + elif key.startswith(self.KEY_LED_X[:-2]): + led = green_led() if value else grey_led() + ledname = '(%s)' % self.led_names.get(key, key) + devicename = ' - '.join(self.topic.split('/')[1:]) + print(10 * ' ' + led + 9 * ' ' + COLOR_GUI_ACTIVE + devicename + ' ' + ledname + colored.attr("reset")) class tradfri_button(base): @@ -676,34 +710,6 @@ class remote(base): print(COLOR_REMOTE + 10 * ' ' + icon + 6 * ' ' + devicename + colored.attr("reset")) -class gui_timer(base): - AUTOSEND = False - - def __init__(self, mqtt_client, topic): - super().__init__(mqtt_client, topic) - self.maxvalue = None - self.last_printed = None - - def __rx__(self, client, userdata, message): - payload = payload_filter(message.payload) - if message.topic.startswith(self.topic) and message.topic.endswith('/feedback/set'): - if isinstance(payload, (int, float, complex)) and not isinstance(payload, bool): - if self.maxvalue is None: - self.maxvalue = payload - perc = payload / self.maxvalue * 100 - if self.last_printed is None or abs(self.last_printed - perc) >= 4.8: - sys.stdout.write(COLOR_GUI_ACTIVE + 't' + percent_bar(perc)) - print('%3d%%' % perc + 2 * ' ' + ' - '.join(self.topic.split('/')[1:]) + ' (%.1f)' % payload + colored.attr('reset')) - self.last_printed = perc - else: - self.maxvalue = None - self.last_printed = None - sys.stdout.write(COLOR_GUI_ACTIVE + 't' + percent_bar(0)) - print('%3d%%' % 0 + 2 * ' ' + ' - '.join(self.topic.split('/')[1:]) + colored.attr('reset')) - else: - print("Unknown Message") - - class brennenstuhl_radiator_valve(base): TEMP_RANGE = [10, 30] # diff --git a/__simulation__/rooms.py b/__simulation__/rooms.py index 07351f5..6ef1de0 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_timer, gui_radiator_valve +from __simulation__.devices import gui_light, gui_led_array, gui_radiator_valve import inspect @@ -136,7 +136,6 @@ class ffe_kitchen(base): self.circulation_pump = shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_SHELLY, input_0_func=shelly.INPUT_FUNC_OUT1_TRIGGER, output_0_auto_off=10*60) self.circulation_pump.add_channel_name(shelly.KEY_OUTPUT_0, "Circulation Pump") - self.gui_timer_circulation_pump = gui_timer(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_GUI_TIMER) class ffe_diningroom(base): @@ -206,6 +205,8 @@ class ffe(base): class stairway(base): def __init__(self, mqtt_client): self.gui_main_light = gui_light(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_GUI, True, False, False) + self.gui_main_light.add_led_name(self.gui_main_light.KEY_LED_X % 0, "Motion Ground Floor") + self.gui_main_light.add_led_name(self.gui_main_light.KEY_LED_X % 1, "Motion First Floor") self.main_light = shelly(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_SHELLY, input_0_func=shelly.INPUT_FUNC_OUT1_TRIGGER) self.main_light.add_channel_name(shelly.KEY_OUTPUT_0, "Main Light") self.motion_sensor_gf = silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_GF) diff --git a/devices/__init__.py b/devices/__init__.py index 377509e..e3dc5a1 100644 --- a/devices/__init__.py +++ b/devices/__init__.py @@ -186,6 +186,41 @@ class base(dict): return self.__previous__.get(key) +class nodered_gui_leds(base): + KEY_LED_0 = "led0" + KEY_LED_1 = "led1" + KEY_LED_2 = "led2" + KEY_LED_3 = "led3" + KEY_LED_4 = "led4" + KEY_LED_5 = "led5" + KEY_LED_6 = "led6" + KEY_LED_7 = "led7" + KEY_LED_8 = "led8" + KEY_LED_9 = "led9" + KEY_LED_LIST = [KEY_LED_0, KEY_LED_1, KEY_LED_2, KEY_LED_3, KEY_LED_4, KEY_LED_5, KEY_LED_6, KEY_LED_7, KEY_LED_8, KEY_LED_9] + # + TX_TYPE = base.TX_VALUE + + def set_led(self, key, data): + """data: [True, False]""" + self.logger.debug("Sending %s with content %s", key, str(data)) + self.pack(key, data) + + +class nodered_gui_timer(base): + KEY_TIMER = "timer" + # + TX_TYPE = base.TX_VALUE + + def set_timer(self, data): + """data: numeric""" + self.pack(self.KEY_TIMER, data) + + def set_timer_mcb(self, device, key, data): + self.logger.debug("Sending %s with content %s", key, str(data)) + self.set_timer(data) + + class shelly(base): KEY_OUTPUT_0 = "relay/0" KEY_OUTPUT_1 = "relay/1" @@ -700,7 +735,7 @@ class nodered_gui_switch(nodered_gui_button): self.set_state(data) -class nodered_gui_light(nodered_gui_switch): +class nodered_gui_light(nodered_gui_switch, nodered_gui_leds, nodered_gui_timer): KEY_ENABLE = "enable" KEY_BRIGHTNESS = "brightness" KEY_COLOR_TEMP = "color_temp" @@ -755,28 +790,6 @@ class nodered_gui_light(nodered_gui_switch): self.set_color_temp(data) -class nodered_gui_leds(base): - KEY_LED_0 = "led0" - KEY_LED_1 = "led1" - KEY_LED_2 = "led2" - KEY_LED_3 = "led3" - KEY_LED_4 = "led4" - KEY_LED_5 = "led5" - KEY_LED_6 = "led6" - KEY_LED_7 = "led7" - KEY_LED_8 = "led8" - KEY_LED_9 = "led9" - # - TX_TYPE = base.TX_VALUE - # - RX_KEYS = [KEY_LED_0, KEY_LED_1, KEY_LED_2, KEY_LED_3, KEY_LED_4, KEY_LED_5, KEY_LED_6, KEY_LED_7, KEY_LED_8, KEY_LED_9] - - def set_led(self, key, data): - """data: [True, False]""" - self.logger.debug("Sending %s with content %s", key, str(data)) - self.pack(key, data) - - class brennenstuhl_heatingvalve(base): KEY_LINKQUALITY = "linkquality" KEY_BATTERY = "battery" diff --git a/function/__init__.py b/function/__init__.py index 3328dbb..c46bb1f 100644 --- a/function/__init__.py +++ b/function/__init__.py @@ -16,10 +16,8 @@ except ImportError: ROOT_LOGGER_NAME = 'root' logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) -# TODO: implement devices.nodered_gui_timer (for circulation pump) # TODO: implement devices.nodered_gui_heatvalve incl. setpoint, boost, ... + replace led with timer -# TODO: improve heating function -# TODO: implement timer and motion leds for stairwasy +# TODO: improve heating function (away mode (reduced setpoint), return to default button, default values in config) # TODO: improve devices.brennenstuhl_heatvalve # TODO: implement garland (incl. day events like sunset, sunrise, ...) # TODO: implement warning message diff --git a/function/common.py b/function/common.py index faa2097..674de13 100644 --- a/function/common.py +++ b/function/common.py @@ -23,8 +23,7 @@ class common_circulation_pump(room_shelly): # self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.circ_pump_actions, True) # - self.gui_timer_view = devices.nodered_gui_heatvalve(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_GUI_TIMER) - self.gui_timer_view.set_feedback('-') + self.gui_main_light.set_timer('-') # self.ct = task.periodic(6, self.cyclic_task) self.pump_timer = None @@ -34,18 +33,18 @@ class common_circulation_pump(room_shelly): def circ_pump_actions(self, device, key, data): if data is True: self.pump_timer = 10 - self.gui_timer_view.set_feedback(self.pump_timer) + self.gui_main_light.set_timer(self.pump_timer) else: self.pump_timer = None - self.gui_timer_view.set_feedback('-') + self.gui_main_light.set_timer('-') def cyclic_task(self, rt): if self.pump_timer is not None: if self.pump_timer <= 0: self.pump_timer = None - self.gui_timer_view.set_feedback('-') + self.gui_main_light.set_timer('-') else: - self.gui_timer_view.set_feedback(self.pump_timer) + self.gui_main_light.set_timer(self.pump_timer) self.pump_timer -= self.ct.cycle_time / 60 diff --git a/function/modules.py b/function/modules.py index 388ebbb..c2b3f91 100644 --- a/function/modules.py +++ b/function/modules.py @@ -43,7 +43,7 @@ class brightness_choose_n_action(object): callback_device: A device for installing callback which are executed, when the device is switched on or off. It needs the following method: * .add_callback(key, data or None, callback, on_changes_only) """ - if len(self.brightness_device_list) >= len(devices.nodered_gui_leds.RX_KEYS): + if len(self.brightness_device_list) >= len(devices.nodered_gui_leds.KEY_LED_LIST): raise ValueError("Number of devices is limited by number of leds in devices.nodered_gui_leds.") self.brightness_device_list.append(brightness_device) self.callback_device_list.append((callback_device, callback_key)) @@ -61,7 +61,7 @@ class brightness_choose_n_action(object): 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.RX_KEYS[i], self.active_device_state == i) + 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: diff --git a/function/rooms.py b/function/rooms.py index d6e7092..f6dcf32 100644 --- a/function/rooms.py +++ b/function/rooms.py @@ -59,9 +59,11 @@ class room_shelly(room): 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=config.DEFAULT_ON_TIME_LIGHT): + 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) @@ -79,11 +81,13 @@ class room_shelly_motion_sensor(room_shelly): cyclic_task.run() def set_motion_detected(self, device, key, data): - if now() < sunrise_time(60) or now() > sunset_time(-60) or data is False: - if device == self.motion_sensor_silvercrest_1: - self.motion_detected_1 = data - elif device == self.motion_sensor_silvercrest_2: - self.motion_detected_2 = 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) @@ -99,12 +103,17 @@ class room_shelly_motion_sensor(room_shelly): def cyclic_task(self, cyclic_task): if self.main_light_timer is not None: if self.main_light_timer <= 0: - if not self.motion_detected_1 and not self.motion_detected_2: - 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 + 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 else: - self.main_light_timer -= cyclic_task.cycle_time + 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 + else: + self.gui_main_light.set_timer('-') class room_shelly_tradfri_light(room_shelly): diff --git a/function/stairway.py b/function/stairway.py index dbb95e5..8de38db 100644 --- a/function/stairway.py +++ b/function/stairway.py @@ -17,5 +17,5 @@ class stairway(room_shelly_motion_sensor): 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, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MIN_ON_TIME) + 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)