Преглед на файлове

Restructured: Splitting function from gui + videv implementation

tags/v1.0.0
Dirk Alders преди 1 година
родител
ревизия
3169b01270
променени са 12 файла, в които са добавени 989 реда и са изтрити 483 реда
  1. 12
    12
      __simulation__/devices.py
  2. 12
    12
      __simulation__/rooms.py
  3. 8
    7
      base.py
  4. 52
    3
      devices/__init__.py
  5. 17
    16
      function/db.py
  6. 204
    72
      function/first_floor_east.py
  7. 119
    17
      function/first_floor_west.py
  8. 196
    79
      function/ground_floor_west.py
  9. 191
    136
      function/modules.py
  10. 26
    111
      function/rooms.py
  11. 41
    9
      function/stairway.py
  12. 111
    9
      function/videv.py

+ 12
- 12
__simulation__/devices.py Целия файл

@@ -11,7 +11,7 @@ COLOR_POWERPLUG = colored.fg("light_cyan")
11 11
 COLOR_LIGHT_ACTIVE = colored.fg("yellow")
12 12
 COLOR_LIGHT_PASSIVE = COLOR_LIGHT_ACTIVE + colored.attr("dim")
13 13
 COLOR_MOTION_SENSOR = colored.fg("dark_orange_3b")
14
-COLOR_RADIATOR_VALVE = colored.fg("red")
14
+COLOR_HEATING_VALVE = colored.fg("red")
15 15
 COLOR_REMOTE = colored.fg("green")
16 16
 
17 17
 
@@ -518,7 +518,6 @@ class gui_light(tradfri_light):
518 518
         self.led_names = {}
519 519
         #
520 520
         self.maxvalue = None
521
-        self.last_printed = None
522 521
 
523 522
     def __init_data__(self, enable_state, enable_brightness, enable_color_temp):
524 523
         data = {}
@@ -595,19 +594,19 @@ class gui_light(tradfri_light):
595 594
                     ""
596 595
                 )
597 596
             elif key == self.KEY_TIMER:
598
-                if isinstance(value, (int, float, complex)) and not isinstance(value, bool):
599
-                    if self.maxvalue is None:
597
+                if value > 0:
598
+                    if self.maxvalue is None and value != 0:
600 599
                         self.maxvalue = value
601 600
                     disp_value = value
602
-                    perc = disp_value / self.maxvalue * 100
601
+                    try:
602
+                        perc = disp_value / self.maxvalue * 100
603
+                    except ZeroDivisionError:
604
+                        perc = 0
603 605
                 else:
604 606
                     disp_value = 0
605 607
                     perc = 0
606 608
                     self.maxvalue = None
607
-                    self.last_printed = None
608
-                if self.last_printed is None or abs(self.last_printed - perc) >= 4.95:
609
-                    print_percent(COLOR_GUI_ACTIVE, 't', perc, '%3d%%' % perc, self.topic, '(%.1f)' % disp_value)
610
-                    self.last_printed = perc
609
+                print_percent(COLOR_GUI_ACTIVE, 't', perc, '%3d%%' % perc, self.topic, '(%.1f)' % disp_value)
611 610
             elif key.startswith(self.KEY_LED_X[:-2]):
612 611
                 print_light(COLOR_GUI_ACTIVE, value, self.topic, '(%s)' % self.led_names.get(key, key), True)
613 612
 
@@ -704,7 +703,7 @@ class remote(base):
704 703
         print(COLOR_REMOTE + 10 * ' ' + icon + 6 * ' ' + devicename + colored.attr("reset"))
705 704
 
706 705
 
707
-class brennenstuhl_radiator_valve(base):
706
+class brennenstuhl_heating_valve(base):
708 707
     TEMP_RANGE = [10, 30]
709 708
     #
710 709
     KEY_TEMPERATURE_SETPOINT = "current_heating_setpoint"
@@ -744,10 +743,10 @@ class brennenstuhl_radiator_valve(base):
744 743
             perc = 100 * (value - self.TEMP_RANGE[0]) / (self.TEMP_RANGE[1] - self.TEMP_RANGE[0])
745 744
             perc = 100 if perc > 100 else perc
746 745
             perc = 0 if perc < 0 else perc
747
-            print_percent(COLOR_RADIATOR_VALVE, '\u03d1', perc, "%4.1f°C" % value, self.topic, "")
746
+            print_percent(COLOR_HEATING_VALVE, '\u03d1', perc, "%4.1f°C" % value, self.topic, "")
748 747
 
749 748
 
750
-class gui_radiator_valve(base):
749
+class gui_heating_valve(base):
751 750
     AUTOSEND = False
752 751
     #
753 752
     TEMP_RANGE = [10, 30]
@@ -824,6 +823,7 @@ class gui_radiator_valve(base):
824 823
     def print_formatted(self, device, key, value):
825 824
         devicename = ' - '.join(self.topic.split('/')[1:])
826 825
         if key == self.KEY_TIMER:
826
+            value /= 60
827 827
             try:
828 828
                 perc = 100 * value / 60
829 829
             except TypeError:

+ 12
- 12
__simulation__/rooms.py Целия файл

@@ -1,6 +1,6 @@
1 1
 import config
2
-from __simulation__.devices import shelly, silvercrest_powerplug, tradfri_light, tradfri_button, silvercrest_motion_sensor, my_powerplug, remote, brennenstuhl_radiator_valve
3
-from __simulation__.devices import gui_light, gui_led_array, gui_radiator_valve
2
+from __simulation__.devices import shelly, silvercrest_powerplug, tradfri_light, tradfri_button, silvercrest_motion_sensor, my_powerplug, remote, brennenstuhl_heating_valve
3
+from __simulation__.devices import gui_light, gui_led_array, gui_heating_valve
4 4
 import inspect
5 5
 
6 6
 
@@ -43,10 +43,10 @@ class gfw_floor(base):
43 43
         self.gui_main_light = gui_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_GUI, True, True, True)
44 44
         self.main_light = shelly(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_SHELLY, input_0_func=shelly.INPUT_FUNC_OUT1_TRIGGER)
45 45
         self.main_light.add_channel_name(shelly.KEY_OUTPUT_0, "Main Light")
46
-        self.main_light_zigbee_1 = tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_1_ZIGBEE, True, True, True, False)
46
+        self.main_light_zigbee_1 = tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 1, True, True, True, False)
47 47
         self.main_light.add_callback(shelly.KEY_OUTPUT_0, self.main_light_zigbee_1.power_on, "on")
48 48
         self.main_light.add_callback(shelly.KEY_OUTPUT_0, self.main_light_zigbee_1.power_off, "off")
49
-        self.main_light_zigbee_2 = tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_2_ZIGBEE, True, True, True, False)
49
+        self.main_light_zigbee_2 = tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 2, True, True, True, False)
50 50
         self.main_light.add_callback(shelly.KEY_OUTPUT_0, self.main_light_zigbee_2.power_on, "on")
51 51
         self.main_light.add_callback(shelly.KEY_OUTPUT_0, self.main_light_zigbee_1.power_off, "off")
52 52
 
@@ -56,8 +56,8 @@ class gfw_marion(base):
56 56
         self.gui_main_light = gui_light(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_GUI, True, False, False)
57 57
         self.main_light = shelly(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_SHELLY, input_0_func=shelly.INPUT_FUNC_OUT1_TRIGGER)
58 58
         self.main_light.add_channel_name(shelly.KEY_OUTPUT_0, "Main Light")
59
-        self.radiator_valve = brennenstuhl_radiator_valve(mqtt_client, config.TOPIC_GFW_MARION_RADIATOR_VALVE_ZIGBEE)
60
-        self.gui_radiator_valve = gui_radiator_valve(mqtt_client, config.TOPIC_GFW_MARION_RADIATOR_VALVE_GUI)
59
+        self.heating_valve = brennenstuhl_heating_valve(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_ZIGBEE)
60
+        self.gui_heating_valve = gui_heating_valve(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_GUI)
61 61
 
62 62
 
63 63
 class gfw_dirk(base):
@@ -90,8 +90,8 @@ class gfw_dirk(base):
90 90
         self.led_array.add_channel_name(gui_led_array.KEY_LED_1, "Desk Light")
91 91
         self.led_array.add_channel_name(gui_led_array.KEY_LED_2, "Amplifier")
92 92
         #
93
-        self.radiator_valve = brennenstuhl_radiator_valve(mqtt_client, config.TOPIC_GFW_DIRK_RADIATOR_VALVE_ZIGBEE)
94
-        self.gui_radiator_valve = gui_radiator_valve(mqtt_client, config.TOPIC_GFW_DIRK_RADIATOR_VALVE_GUI)
93
+        self.heating_valve = brennenstuhl_heating_valve(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_ZIGBEE)
94
+        self.gui_heating_valve = gui_heating_valve(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_GUI)
95 95
 
96 96
 
97 97
 class gfw(base):
@@ -127,8 +127,8 @@ class ffw_sleep(base):
127 127
 
128 128
 class ffw_bath(base):
129 129
     def __init__(self, mqtt_client):
130
-        self.radiator_valve = brennenstuhl_radiator_valve(mqtt_client, config.TOPIC_FFW_BATH_RADIATOR_VALVE_ZIGBEE)
131
-        self.gui_radiator_valve = gui_radiator_valve(mqtt_client, config.TOPIC_FFW_BATH_RADIATOR_VALVE_GUI)
130
+        self.heating_valve = brennenstuhl_heating_valve(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_ZIGBEE)
131
+        self.gui_heating_valve = gui_heating_valve(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_GUI)
132 132
 
133 133
 
134 134
 class ffw(base):
@@ -190,8 +190,8 @@ class ffe_sleep(base):
190 190
         self.led_array.add_channel_name(gui_led_array.KEY_LED_0, "Main Light")
191 191
         self.led_array.add_channel_name(gui_led_array.KEY_LED_1, "Bed Light Dirk")
192 192
         #
193
-        self.radiator_valve = brennenstuhl_radiator_valve(mqtt_client, config.TOPIC_FFE_SLEEP_RADIATOR_VALVE_ZIGBEE)
194
-        self.gui_radiator_valve = gui_radiator_valve(mqtt_client, config.TOPIC_FFE_SLEEP_RADIATOR_VALVE_GUI)
193
+        self.heating_valve = brennenstuhl_heating_valve(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_ZIGBEE)
194
+        self.gui_heating_valve = gui_heating_valve(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_GUI)
195 195
 
196 196
 
197 197
 class ffe_livingroom(base):

+ 8
- 7
base.py Целия файл

@@ -9,14 +9,14 @@ except ImportError:
9 9
 class common_base(dict):
10 10
     DEFAULT_VALUES = {}
11 11
 
12
-    def __init__(self):
13
-        super().__init__(self.DEFAULT_VALUES)
12
+    def __init__(self, default_values=None):
13
+        super().__init__(default_values or self.DEFAULT_VALUES)
14 14
         self['__type__'] = self.__class__.__name__
15 15
         #
16 16
         self.__callback_list__ = []
17 17
         self.logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
18 18
 
19
-    def add_callback(self, key, data, callback, on_change_only=True):
19
+    def add_callback(self, key, data, callback, on_change_only=False):
20 20
         """
21 21
         key: key or None for all keys
22 22
         data: data or None for all data
@@ -25,19 +25,20 @@ class common_base(dict):
25 25
         if cb_tup not in self.__callback_list__:
26 26
             self.__callback_list__.append(cb_tup)
27 27
 
28
-    def set(self, key, data):
28
+    def set(self, key, data, block_callback=[]):
29 29
         value_changed = self[key] != data
30 30
         self[key] = data
31 31
         for cb_key, cb_data, callback, on_change_only in self.__callback_list__:
32 32
             if cb_key is None or key == cb_key:                 # key fits to callback definition
33 33
                 if cb_data is None or cb_data == self[key]:     # data fits to callback definition
34 34
                     if value_changed or not on_change_only:     # change status fits to callback definition
35
-                        callback(self, key, self[key])
35
+                        if not callback in block_callback:      # block given callbacks
36
+                            callback(self, key, self[key])
36 37
 
37 38
 
38 39
 class mqtt_base(common_base):
39
-    def __init__(self, mqtt_client, topic):
40
-        super().__init__()
40
+    def __init__(self, mqtt_client, topic, default_values=None):
41
+        super().__init__(default_values)
41 42
         #
42 43
         self.mqtt_client = mqtt_client
43 44
         self.topic = topic

+ 52
- 3
devices/__init__.py Целия файл

@@ -27,6 +27,9 @@ devices (DEVICES)
27 27
 """
28 28
 
29 29
 # TODO: Usage of mqtt_base for all devices
30
+#
31
+# TODO: brennenstuhl_heatingvalve: always send "{"preset": "manual", "system_mode": "heat"}" with any information
32
+# TODO: usage of base.mqtt as parent for class base
30 33
 
31 34
 __DEPENDENCIES__ = []
32 35
 
@@ -50,6 +53,52 @@ def is_json(data):
50 53
         return True
51 54
 
52 55
 
56
+class group(object):
57
+    def __init__(self, *args):
58
+        super().__init__()
59
+        self._members = args
60
+        self._iter_counter = 0
61
+        #
62
+        self.methods = []
63
+        for method in [m for m in args[0].__class__.__dict__.keys()]:
64
+            if not method.startswith('_') and callable(getattr(args[0], method)):   # add all public callable attributes to the list
65
+                self.methods.append(method)
66
+        #
67
+        for member in self:
68
+            methods = [m for m in member.__class__.__dict__.keys() if not m.startswith(
69
+                '_') if not m.startswith('_') and callable(getattr(args[0], m))]
70
+            if self.methods != methods:
71
+                raise ValueError("All given instances needs to have same attributes:", self.methods, methods)
72
+
73
+    def __iter__(self):
74
+        return self
75
+
76
+    def __next__(self):
77
+        if self._iter_counter < len(self):
78
+            self._iter_counter += 1
79
+            return self._members[self._iter_counter - 1]
80
+        self._iter_counter = 0
81
+        raise StopIteration
82
+
83
+    def __getitem__(self, i):
84
+        return self._members[i]
85
+
86
+    def __len__(self):
87
+        return len(self._members)
88
+
89
+    def __getattribute__(self, name):
90
+        def group_execution(*args, **kwargs):
91
+            for member in self[:]:
92
+                m = getattr(member, name)
93
+                m(*args, **kwargs)
94
+        try:
95
+            rv = super().__getattribute__(name)
96
+        except AttributeError:
97
+            return group_execution
98
+        else:
99
+            return rv
100
+
101
+
53 102
 class base(dict):
54 103
     TX_TOPIC = "set"
55 104
     TX_VALUE = 0
@@ -508,9 +557,6 @@ class tradfri_light(base):
508 557
         else:
509 558
             return super().pack_filter(key, data)
510 559
 
511
-    def request_data(self):
512
-        self.mqtt_client.send(self.topic + "/get", '{%s: ""}' % self.KEY_OUTPUT_0)
513
-
514 560
     #
515 561
     # RX
516 562
     #
@@ -537,6 +583,9 @@ class tradfri_light(base):
537 583
     #
538 584
     # TX
539 585
     #
586
+    def request_data(self, device=None, key=None, data=None):
587
+        self.mqtt_client.send(self.topic + "/get", '{"%s": ""}' % self.KEY_OUTPUT_0)
588
+
540 589
     def set_output_0(self, state):
541 590
         """state: [True, False, 'toggle']"""
542 591
         self.pack(self.KEY_OUTPUT_0, state)

+ 17
- 16
function/db.py Целия файл

@@ -4,12 +4,12 @@ import sqlite3
4 4
 db_file = os.path.join(os.path.dirname(__file__), '..', 'database.db')
5 5
 
6 6
 
7
-def get_gui_radiator_data(topic):
8
-    return __storage__().get_gui_radiator_data(topic)
7
+def get_radiator_data(topic):
8
+    return __storage__().get_radiator_data(topic)
9 9
 
10 10
 
11
-def set_gui_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint):
12
-    return __storage__().store_gui_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint)
11
+def set_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint):
12
+    return __storage__().store_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint)
13 13
 
14 14
 
15 15
 class __storage__(object):
@@ -17,30 +17,31 @@ class __storage__(object):
17 17
         self.conn = sqlite3.connect(db_file)
18 18
         self.c = self.conn.cursor()
19 19
         with self.conn:
20
-            self.c.execute("""CREATE TABLE IF NOT EXISTS gui_radiator (
20
+            self.c.execute("""CREATE TABLE IF NOT EXISTS radiator (
21 21
                     topic text PRIMARY KEY,
22 22
                     away_mode integer,
23 23
                     summer_mode integer,
24
-                    user_temperatur_setpoint real
24
+                    user_temperatur_setpoint real,
25
+                    temperatur_setpoint real
25 26
                 )""")
26 27
 
27
-    def store_gui_radiator_data(self, topic, away_mode, summer_mode, user_temperatur_setpoint):
28
-        data = [topic, away_mode, summer_mode, user_temperatur_setpoint]
28
+    def store_radiator_data(self, topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint):
29
+        data = [topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint]
29 30
         try:
30 31
             with self.conn:
31 32
                 self.c.execute(
32
-                    'INSERT INTO gui_radiator VALUES (?, ?, ?, ?)', data)
33
+                    'INSERT INTO radiator VALUES (?, ?, ?, ?, ?)', data)
33 34
         except sqlite3.IntegrityError:
34
-            data = [away_mode, summer_mode, user_temperatur_setpoint]
35
-            db_data = self.get_gui_radiator_data(topic)
35
+            data = [away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint]
36
+            db_data = self.get_radiator_data(topic)
36 37
             if db_data != data:
37 38
                 with self.conn:
38 39
                     self.c.execute(
39
-                        'UPDATE gui_radiator SET away_mode = ?, summer_mode = ?, user_temperatur_setpoint = ? WHERE topic = ?', data + [topic])
40
+                        'UPDATE radiator SET away_mode = ?, summer_mode = ?, user_temperatur_setpoint = ?, temperatur_setpoint = ? WHERE topic = ?', data + [topic])
40 41
 
41
-    def get_gui_radiator_data(self, topic):
42
-        """ returns a list [away_mode, summer_mode, user_temperatur_setpoint] or [None, None, None]"""
43
-        self.c.execute("SELECT * FROM gui_radiator WHERE topic=?", (topic, ))
42
+    def get_radiator_data(self, topic):
43
+        """ returns a list [away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint] or [None, None, None, None]"""
44
+        self.c.execute("SELECT * FROM radiator WHERE topic=?", (topic, ))
44 45
         data = self.c.fetchone()
45 46
         if data is not None:
46 47
             data = list(data)
@@ -48,7 +49,7 @@ class __storage__(object):
48 49
             data[2] = data[2] == 1
49 50
             return data[1:]
50 51
         else:
51
-            return [None, None, None]
52
+            return [None, None, None, None]
52 53
 
53 54
     def __del__(self):
54 55
         self.conn.close()

+ 204
- 72
function/first_floor_east.py Целия файл

@@ -4,10 +4,10 @@
4 4
 
5 5
 import config
6 6
 import devices
7
-from function.modules import brightness_choose_n_action, circulation_pump, radiator_function
7
+from function.modules import brightness_choose_n_action, timer_on_activation, heating_function
8 8
 import logging
9
-from function.rooms import room_shelly, room_shelly_motion_sensor, room_shelly_tradfri_light
10
-from function.videv import videv_switching, videv_switch_brightness, videv_switch_brightness_color_temp
9
+from function.rooms import room
10
+from function.videv import videv_switching, videv_switch_brightness, videv_switching_timer, videv_switch_brightness_color_temp, videv_heating, videv_multistate
11 11
 try:
12 12
     from config import APP_NAME as ROOT_LOGGER_NAME
13 13
 except ImportError:
@@ -15,10 +15,23 @@ except ImportError:
15 15
 logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
16 16
 
17 17
 
18
-class first_floor_east_floor(room_shelly):
18
+class first_floor_east_floor(room):
19 19
     def __init__(self, mqtt_client):
20
+        #
21
+        # Device initialisation
22
+        #
20 23
         # http://shelly1l-3C6105E4E629
21
-        super().__init__(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_SHELLY, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_GUI)
24
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_SHELLY)
25
+        super().__init__(mqtt_client)
26
+
27
+        ##### TEMPORARY ###################################################################################################################
28
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_GUI)
29
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
30
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
31
+        ##### TEMPORARY ###################################################################################################################
32
+
33
+        #
34
+        # Virtual Device Interface
22 35
         #
23 36
         self.main_light = videv_switching(
24 37
             mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_VIDEV,
@@ -26,43 +39,82 @@ class first_floor_east_floor(room_shelly):
26 39
         )
27 40
 
28 41
 
29
-class first_floor_east_kitchen(room_shelly):
42
+class first_floor_east_kitchen(room):
30 43
     def __init__(self, mqtt_client):
44
+        #
45
+        # Device initialisation
46
+        #
31 47
         # http://shelly1l-8CAAB5616C01
32
-        super().__init__(mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_SHELLY, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_GUI)
48
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_SHELLY)
49
+        # http://shelly1-e89f6d85a466/
50
+        self.circulation_pump_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_SHELLY)
51
+
52
+        super().__init__(mqtt_client)
53
+
33 54
         #
34
-        self.circulation_pump = circulation_pump(mqtt_client)
35
-        self.circulation_pump.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.flash_main_light)
55
+        # Functionality initialisation
56
+        #
57
+        self.circulation_pump = timer_on_activation(self.circulation_pump_shelly, devices.shelly.KEY_OUTPUT_0, 10*60)
58
+
59
+        ##### TEMPORARY ###################################################################################################################
60
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_GUI)
61
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
62
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
63
+
64
+        self.gui_circulation_pump = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_GUI)
65
+        self.gui_circulation_pump.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.circulation_pump_shelly.set_output_0_mcb)
66
+        self.circulation_pump_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_circulation_pump.set_state_mcb)
67
+        self.circulation_pump.add_callback(timer_on_activation.KEY_TIMER, None, self.gui_circulation_pump.set_timer_mcb, True)
68
+        ##### TEMPORARY ###################################################################################################################
69
+
70
+        #
71
+        # Virtual Device Interface
36 72
         #
37 73
         self.main_light_videv = videv_switching(
38 74
             mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_VIDEV,
39 75
             self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
40 76
         )
41
-        self.circulation_pump_videv = videv_switching(
77
+        self.circulation_pump_videv = videv_switching_timer(
42 78
             mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_VIDEV,
43
-            self.circulation_pump.main_light_shelly, devices.shelly.KEY_OUTPUT_0
79
+            self.circulation_pump_shelly, devices.shelly.KEY_OUTPUT_0,
80
+            self.circulation_pump, timer_on_activation.KEY_TIMER
44 81
         )
45 82
 
46 83
     def all_off(self, device=None, key=None, data=None):
47
-        self.circulation_pump.all_off(device, key, data)
84
+        self.circulation_pump_shelly.set_output_0(False)
85
+        self.circulation_pump_shelly.set_output_1(False)
48 86
         return super().all_off(device, key, data)
49 87
 
50 88
 
51
-class first_floor_east_dining(room_shelly):
89
+class first_floor_east_dining(room):
52 90
     def __init__(self, mqtt_client):
91
+        #
92
+        # Device initialisation
93
+        #
53 94
         # http://shelly1l-84CCA8ADD055
54
-        super().__init__(mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_SHELLY, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_GUI)
95
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_SHELLY)
55 96
         self.floorlamp_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_POWERPLUG)
56 97
         if config.CHRISTMAS:
57 98
             self.garland_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_POWERPLUG)
58
-        #
99
+        super().__init__(mqtt_client)
100
+
101
+        ##### TEMPORARY ###################################################################################################################
102
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_GUI)
103
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
104
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
105
+
59 106
         self.gui_floorlamp = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_GUI)
107
+        self.gui_floorlamp.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.floorlamp_powerplug.set_output_0_mcb)
108
+        self.floorlamp_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_floorlamp.set_state_mcb)
109
+        ##### TEMPORARY ###################################################################################################################
110
+
60 111
         #
61
-        # Callback initialisation
112
+        # Functionality initialisation
62 113
         #
63 114
         self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_powerplug.set_output_0_mcb, True)
64
-        self.gui_floorlamp.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.floorlamp_powerplug.set_output_0_mcb)
65
-        self.floorlamp_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_floorlamp.set_state_mcb)
115
+
116
+        #
117
+        # Virtual Device Interface
66 118
         #
67 119
         self.main_light_videv = videv_switching(
68 120
             mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_VIDEV,
@@ -72,6 +124,11 @@ class first_floor_east_dining(room_shelly):
72 124
             mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_VIDEV,
73 125
             self.floorlamp_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
74 126
         )
127
+        if config.CHRISTMAS:
128
+            self.garland_videv = videv_switching(
129
+                mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_VIDEV,
130
+                self.garland_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
131
+            )
75 132
 
76 133
     def all_off(self, device=None, key=None, data=None):
77 134
         super().all_off(device, key, data)
@@ -80,46 +137,105 @@ class first_floor_east_dining(room_shelly):
80 137
             self.garland_powerplug.set_output_0(False)
81 138
 
82 139
 
83
-class first_floor_east_sleep(room_shelly_tradfri_light):
140
+class first_floor_east_sleep(room):
84 141
     def __init__(self, mqtt_client):
142
+        #
143
+        # Device initialisation
144
+        #
85 145
         # http://shelly1l-E8DB84A254C7
86
-        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)
87
-        # bed light
146
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_SHELLY)
147
+        self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_ZIGBEE)
88 148
         self.bed_light_di_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_ZIGBEE)
89
-        self.gui_bed_light_di = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_GUI)
90 149
         self.bed_light_ma_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_POWERPLUG)
91
-        self.gui_bed_light_ma = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_GUI)
92
-        #
150
+        self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_ZIGBEE)
93 151
         self.button_tradfri = devices.tradfri_button(mqtt_client, config.TOPIC_FFE_SLEEP_INPUT_DEVICE)
152
+
153
+        super().__init__(mqtt_client)
154
+
155
+        #
156
+        # Functionality initialisation
157
+        #
94 158
         # button / brightness function
95
-        self.brightness_functions = brightness_choose_n_action(mqtt_client, self.button_tradfri, config.TOPIC_FFE_SLEEP_DEVICE_CHOOSER_LED)
159
+        self.brightness_functions = brightness_choose_n_action(self.button_tradfri)
96 160
         self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0)
97 161
         self.brightness_functions.add(self.bed_light_di_tradfri, self.bed_light_di_tradfri, devices.tradfri_light.KEY_OUTPUT_0)
98
-        # radiator valve
99
-        self.radiator_function = radiator_function(mqtt_client, config.TOPIC_FFE_SLEEP_RADIATOR_VALVE_ZIGBEE,
100
-                                                   config.TOPIC_FFE_SLEEP_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_FFE_SLEEP)
101
-        #
102
-        # Callback initialisation
103
-        #
104
-        # on/off with button
162
+
163
+        # button / main light
105 164
         self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_TOGGLE,
106 165
                                          self.main_light_shelly.toggle_output_0_mcb)
166
+        # button / bed light
107 167
         self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT,
108 168
                                          self.bed_light_di_tradfri.toggle_output_0_mcb)
109 169
         self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT_LONG,
110 170
                                          self.bed_light_ma_powerplug.toggle_output_0_mcb)
111 171
 
172
+        # heating function
173
+        self.heating_function = heating_function(self.heating_valve, config.DEFAULT_TEMPERATURE_FFE_SLEEP)
174
+
175
+        ##### TEMPORARY ###################################################################################################################
176
+        # main light
177
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_GUI)
178
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
179
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
180
+
181
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb)
182
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb)
183
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb)
184
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb)
185
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb)
112 186
         # bed light
113
-        # switch
187
+        self.gui_bed_light_di = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_GUI)
114 188
         self.gui_bed_light_di.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.bed_light_di_tradfri.set_output_0_mcb)
115 189
         self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_bed_light_di.set_state_mcb)
116
-        self.gui_bed_light_ma.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.bed_light_ma_powerplug.set_output_0_mcb)
117
-        self.bed_light_ma_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_bed_light_ma.set_state_mcb)
118
-        # brightness and color temperature
119 190
         self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_bed_light_di.set_enable_mcb)
120 191
         self.gui_bed_light_di.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.bed_light_di_tradfri.set_brightness_mcb)
121 192
         self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_bed_light_di.set_enable_mcb)
122 193
         self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_bed_light_di.set_brightness_mcb)
194
+
195
+        self.gui_bed_light_ma = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_GUI)
196
+        self.gui_bed_light_ma.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.bed_light_ma_powerplug.set_output_0_mcb)
197
+        self.bed_light_ma_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_bed_light_ma.set_state_mcb)
198
+
199
+        # heating
200
+        self.gui_heating = devices.nodered_gui_radiator(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_GUI)
201
+        self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_temperature_mcb, True)
202
+
203
+        def set_heating_setpoint(device, key, data):
204
+            self.heating_function.set(heating_function.KEY_USER_TEMPERATURE_SETPOINT, data)
205
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, set_heating_setpoint)
206
+        self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_setpoint_temperature_mcb, True)
207
+
208
+        def boost(device, key, data):
209
+            self.heating_function.set(heating_function.KEY_START_BOOST, data)
210
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, boost)
211
+
212
+        def setpoint_to_default(device, key, data):
213
+            self.heating_function.set(heating_function.KEY_SET_DEFAULT_TEMPERATURE, data)
214
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, setpoint_to_default)
215
+
216
+        def away_mode(device, key, data):
217
+            self.heating_function.set(heating_function.KEY_AWAY_MODE, data)
218
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, away_mode)
219
+        self.heating_function.add_callback(heating_function.KEY_AWAY_MODE, None, self.gui_heating.set_away_mcb)
220
+
221
+        def summer_mode(device, key, data):
222
+            self.heating_function.set(heating_function.KEY_SUMMER_MODE, data)
223
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, summer_mode)
224
+        self.heating_function.add_callback(heating_function.KEY_SUMMER_MODE, None, self.gui_heating.set_summer_mcb)
225
+
226
+        self.heating_function.add_callback(heating_function.KEY_BOOST_TIMER, None, self.gui_heating.set_timer_mcb)
227
+
228
+        # active device led
229
+        self.gui_led_active_device = devices.nodered_gui_leds(mqtt_client, config.TOPIC_FFE_SLEEP_DEVICE_CHOOSER_LED)
230
+
231
+        def update_active_device_led(device, key, data):
232
+            for i in range(0, len(self.brightness_functions.brightness_device_list)):
233
+                self.gui_led_active_device.set_led(devices.nodered_gui_leds.KEY_LED_LIST[i], data == i)
234
+        self.brightness_functions.add_callback(brightness_choose_n_action.KEY_ACTIVE_DEVICE, None, update_active_device_led, True)
235
+        ##### TEMPORARY ###################################################################################################################
236
+
237
+        #
238
+        # Virtual Device Interface
123 239
         #
124 240
         self.main_light_videv = videv_switch_brightness_color_temp(
125 241
             mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_VIDEV,
@@ -136,6 +252,14 @@ class first_floor_east_sleep(room_shelly_tradfri_light):
136 252
             mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_VIDEV,
137 253
             self.bed_light_ma_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
138 254
         )
255
+        self.heating_function_videv = videv_heating(
256
+            mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_VIDEV,
257
+            self.heating_function
258
+        )
259
+        self.brightness_functions_device_videv = videv_multistate(
260
+            mqtt_client, config.TOPIC_FFE_SLEEP_ACTIVE_BRIGHTNESS_DEVICE_VIDEV,
261
+            brightness_choose_n_action.KEY_ACTIVE_DEVICE, self.brightness_functions, 2
262
+        )
139 263
 
140 264
     def all_off(self, device=None, key=None, data=None):
141 265
         super().all_off(device, key, data)
@@ -143,42 +267,57 @@ class first_floor_east_sleep(room_shelly_tradfri_light):
143 267
         self.bed_light_ma_powerplug.set_output_0(False)
144 268
 
145 269
 
146
-class first_floor_east_living(room_shelly_tradfri_light):
270
+class first_floor_east_living(room):
147 271
     def __init__(self, mqtt_client):
148
-        # http://shelly1l-3C6105E3F910
149
-        super().__init__(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_SHELLY,
150
-                         config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_GUI, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
151
-        for i in range(1, 7):
152
-            setattr(self, 'floorlamp_tradfri_%d' % i, devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_ZIGBEE % i))
153 272
         #
273
+        # Device initialisation
274
+        #
275
+        # http://shelly1l-3C6105E3F910
276
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_SHELLY)
277
+        self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
278
+        self.floorlamp_tradfri = devices.group(
279
+            *[devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_ZIGBEE % i) for i in range(1, 7)])
154 280
         if config.CHRISTMAS:
155 281
             self.powerplug_xmas_tree = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_POWERPLUG)
156 282
             self.powerplug_xmas_star = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_STAR_POWERPLUG)
283
+
284
+        super().__init__(mqtt_client)
285
+
157 286
         #
158
-        self.gui_floorlamp = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_GUI)
159
-        #
160
-        if config.CHRISTMAS:
161
-            self.gui_xmas_tree = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_GUI)
162
-        #
163
-        # Callback initialisation
287
+        # Functionality initialisation
164 288
         #
289
+        # floor lamp synchronisation with main_light
290
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_tradfri.set_output_0_mcb, True)
291
+
292
+        ##### TEMPORARY ###################################################################################################################
293
+        # main light
294
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_GUI)
295
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
296
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
297
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb)
298
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb)
299
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb)
300
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb)
301
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb)
302
+
303
+        self.gui_floorlamp = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_GUI)
304
+        self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.floorlamp_tradfri.set_output_0_mcb)
305
+        self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.floorlamp_tradfri.set_brightness_mcb)
306
+        self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.floorlamp_tradfri.set_color_temp_mcb)
307
+        self.floorlamp_tradfri[0].add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_floorlamp.set_state_mcb)
308
+        self.floorlamp_tradfri[0].add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_floorlamp.set_enable_mcb)
309
+        self.floorlamp_tradfri[0].add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_floorlamp.set_brightness_mcb)
310
+        self.floorlamp_tradfri[0].add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_floorlamp.set_color_temp_mcb)
165 311
 
166
-        # floor lamp
167
-        for device in self.__floorlamp_devices__():
168
-            self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, device.set_output_0_mcb, True)
169
-            self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_STATE, None, device.set_output_0_mcb)
170
-            self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, device.set_brightness_mcb)
171
-            self.gui_floorlamp.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, device.set_color_temp_mcb)
172
-        self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_floorlamp.set_state_mcb)
173
-        self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_floorlamp.set_enable_mcb)
174
-        self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_floorlamp.set_brightness_mcb)
175
-        self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_floorlamp.set_color_temp_mcb)
176
-        #
177 312
         if config.CHRISTMAS:
313
+            self.gui_xmas_tree = devices.nodered_gui_switch(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_GUI)
314
+            self.powerplug_xmas_tree.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.powerplug_xmas_star.set_output_0_mcb)
178 315
             self.powerplug_xmas_tree.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_xmas_tree.set_state_mcb)
179 316
             self.gui_xmas_tree.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_xmas_tree.set_output_0_mcb)
180
-            #
181
-            self.powerplug_xmas_tree.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.powerplug_xmas_star.set_output_0_mcb)
317
+        ##### TEMPORARY ###################################################################################################################
318
+
319
+        #
320
+        # Virtual Device Interface
182 321
         #
183 322
         self.main_light_videv = videv_switch_brightness_color_temp(
184 323
             mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_VIDEV,
@@ -188,9 +327,9 @@ class first_floor_east_living(room_shelly_tradfri_light):
188 327
         )
189 328
         self.floorlamp_videv = videv_switch_brightness_color_temp(
190 329
             mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_VIDEV,
191
-            self.floorlamp_tradfri_1, devices.tradfri_light.KEY_OUTPUT_0,
192
-            self.floorlamp_tradfri_1, devices.tradfri_light.KEY_BRIGHTNESS,
193
-            self.floorlamp_tradfri_1, devices.tradfri_light.KEY_COLOR_TEMP
330
+            self.floorlamp_tradfri, devices.tradfri_light.KEY_OUTPUT_0,
331
+            self.floorlamp_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
332
+            self.floorlamp_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
194 333
         )
195 334
         if config.CHRISTMAS:
196 335
             self.xmas_tree_videv = videv_switching(
@@ -200,14 +339,7 @@ class first_floor_east_living(room_shelly_tradfri_light):
200 339
 
201 340
     def all_off(self, device=None, key=None, data=None):
202 341
         super().all_off(device, key, data)
203
-        for floorlamp in self.__floorlamp_devices__():
204
-            floorlamp.set_output_0(False)
342
+        self.floorlamp_tradfri.set_output_0(False)
205 343
         if config.CHRISTMAS:
206 344
             self.powerplug_xmas_tree.set_output_0(False)
207 345
             self.powerplug_xmas_star.set_output_0(False)
208
-
209
-    def __floorlamp_devices__(self):
210
-        rv = []
211
-        for i in range(1, 7):
212
-            rv.append(getattr(self, 'floorlamp_tradfri_%d' % i))
213
-        return rv

+ 119
- 17
function/first_floor_west.py Целия файл

@@ -5,9 +5,9 @@
5 5
 import config
6 6
 import devices
7 7
 import logging
8
-from function.modules import radiator_function
9
-from function.rooms import room_shelly, room_shelly_tradfri_light
10
-from function.videv import videv_switch_brightness, videv_switch_brightness_color_temp
8
+from function.modules import heating_function
9
+from function.rooms import room
10
+from function.videv import videv_switch_brightness, videv_switch_brightness_color_temp, videv_heating
11 11
 
12 12
 
13 13
 try:
@@ -17,10 +17,30 @@ except ImportError:
17 17
 logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
18 18
 
19 19
 
20
-class first_floor_west_julian(room_shelly_tradfri_light):
21
-    # http://shelly1l-3C6105E43452
20
+class first_floor_west_julian(room):
22 21
     def __init__(self, mqtt_client):
23
-        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)
22
+        #
23
+        # Device initialisation
24
+        #
25
+        # http://shelly1l-3C6105E43452
26
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_SHELLY)
27
+        self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_ZIGBEE)
28
+        super().__init__(mqtt_client)
29
+
30
+        ##### TEMPORARY ###################################################################################################################
31
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_GUI)
32
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
33
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
34
+
35
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb)
36
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb)
37
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb)
38
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb)
39
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb)
40
+        ##### TEMPORARY ###################################################################################################################
41
+
42
+        #
43
+        # Virtual Device Interface
24 44
         #
25 45
         self.main_light_videv = videv_switch_brightness_color_temp(
26 46
             mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_VIDEV,
@@ -30,21 +50,85 @@ class first_floor_west_julian(room_shelly_tradfri_light):
30 50
         )
31 51
 
32 52
 
33
-class first_floor_west_bath(object):
53
+class first_floor_west_bath(room):
34 54
     def __init__(self, mqtt_client):
35
-        # radiator valve
36
-        self.radiator_function = radiator_function(mqtt_client, config.TOPIC_FFW_BATH_RADIATOR_VALVE_ZIGBEE,
37
-                                                   config.TOPIC_FFW_BATH_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_FFW_BATH)
55
+        #
56
+        # Device initialisation
57
+        #
58
+        self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_ZIGBEE)
59
+        super().__init__(mqtt_client)
60
+        #
61
+        # Functionality initialisation
62
+        #
63
+        # heating function
64
+        self.heating_function = heating_function(self.heating_valve, config.DEFAULT_TEMPERATURE_FFW_BATH)
65
+
66
+        ##### TEMPORARY ###################################################################################################################
67
+        self.gui_heating = devices.nodered_gui_radiator(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_GUI)
68
+        self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_temperature_mcb, True)
69
+
70
+        def set_heating_setpoint(device, key, data):
71
+            self.heating_function.set(heating_function.KEY_USER_TEMPERATURE_SETPOINT, data)
72
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, set_heating_setpoint)
73
+        self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_setpoint_temperature_mcb, True)
74
+
75
+        def boost(device, key, data):
76
+            self.heating_function.set(heating_function.KEY_START_BOOST, data)
77
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, boost)
78
+
79
+        def setpoint_to_default(device, key, data):
80
+            self.heating_function.set(heating_function.KEY_SET_DEFAULT_TEMPERATURE, data)
81
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, setpoint_to_default)
82
+
83
+        def away_mode(device, key, data):
84
+            self.heating_function.set(heating_function.KEY_AWAY_MODE, data)
85
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, away_mode)
86
+        self.heating_function.add_callback(heating_function.KEY_AWAY_MODE, None, self.gui_heating.set_away_mcb)
87
+
88
+        def summer_mode(device, key, data):
89
+            self.heating_function.set(heating_function.KEY_SUMMER_MODE, data)
90
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, summer_mode)
91
+        self.heating_function.add_callback(heating_function.KEY_SUMMER_MODE, None, self.gui_heating.set_summer_mcb)
92
+
93
+        self.heating_function.add_callback(heating_function.KEY_BOOST_TIMER, None, self.gui_heating.set_timer_mcb)
94
+        ##### TEMPORARY ###################################################################################################################
95
+
96
+        #
97
+        # Virtual Device Interface
98
+        #
99
+        self.heating_function_videv = videv_heating(
100
+            mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_VIDEV,
101
+            self.heating_function
102
+        )
38 103
 
39 104
     def all_off(self):
40 105
         pass
41 106
 
42 107
 
43
-class first_floor_west_living(room_shelly_tradfri_light):
44
-    # http://shelly1l-84CCA8ACE6A1
108
+class first_floor_west_living(room):
45 109
     def __init__(self, mqtt_client):
46
-        super().__init__(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_SHELLY,
47
-                         config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_GUI, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
110
+        #
111
+        # Device initialisation
112
+        #
113
+        # http://shelly1l-84CCA8ACE6A1
114
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_SHELLY)
115
+        self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
116
+        super().__init__(mqtt_client)
117
+
118
+        ##### TEMPORARY ###################################################################################################################
119
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_GUI)
120
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
121
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
122
+
123
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb)
124
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb)
125
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb)
126
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb)
127
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb)
128
+        ##### TEMPORARY ###################################################################################################################
129
+
130
+        #
131
+        # Virtual Device Interface
48 132
         #
49 133
         self.main_light_videv = videv_switch_brightness_color_temp(
50 134
             mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_VIDEV,
@@ -54,10 +138,28 @@ class first_floor_west_living(room_shelly_tradfri_light):
54 138
         )
55 139
 
56 140
 
57
-class first_floor_west_sleep(room_shelly_tradfri_light):
58
-    # http://shelly1-3494546A51F2
141
+class first_floor_west_sleep(room):
59 142
     def __init__(self, mqtt_client):
60
-        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)
143
+        #
144
+        # Device initialisation
145
+        #
146
+        # http://shelly1-3494546A51F2
147
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_SHELLY)
148
+        self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_ZIGBEE)
149
+        super().__init__(mqtt_client)
150
+
151
+        ##### TEMPORARY ###################################################################################################################
152
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_GUI)
153
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
154
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
155
+
156
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb)
157
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb)
158
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb)
159
+        ##### TEMPORARY ###################################################################################################################
160
+
161
+        #
162
+        # Virtual Device Interface
61 163
         #
62 164
         self.main_light_videv = videv_switch_brightness(
63 165
             mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_VIDEV,

+ 196
- 79
function/ground_floor_west.py Целия файл

@@ -4,10 +4,10 @@
4 4
 
5 5
 import config
6 6
 import devices
7
-from function.modules import brightness_choose_n_action, radiator_function
7
+from function.modules import brightness_choose_n_action, heating_function
8 8
 import logging
9
-from function.rooms import room_shelly, room_shelly_tradfri_light, room_shelly_silvercrest_light
10
-from function.videv import videv_switching, videv_switch_brightness_color_temp
9
+from function.rooms import room
10
+from function.videv import videv_switching, videv_switch_brightness_color_temp, videv_heating, videv_multistate
11 11
 import task
12 12
 
13 13
 try:
@@ -17,16 +17,39 @@ except ImportError:
17 17
 logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
18 18
 
19 19
 
20
-class ground_floor_west_floor(room_shelly_silvercrest_light):
21
-    # http://shelly1l-84CCA8AD1148
20
+class ground_floor_west_floor(room):
22 21
     def __init__(self, mqtt_client):
23
-        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)
24 22
         #
25
-        # Callback initialisation
23
+        # Device initialisation
24
+        #
25
+        # http://shelly1l-84CCA8AD1148
26
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_SHELLY)
27
+        self.main_light_tradfri = devices.group(
28
+            devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 1),
29
+            devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 2)
30
+        )
31
+        super().__init__(mqtt_client)
32
+
26 33
         #
27
-        self.main_light_tradfri_2 = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_2_ZIGBEE)
28
-        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.main_light_tradfri_2.set_brightness_mcb)
29
-        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.main_light_tradfri_2.set_color_temp_mcb)
34
+        # Functionality initialisation
35
+        #
36
+        # Request silvercrest data of lead light after power on
37
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.main_light_tradfri[0].request_data, True)
38
+
39
+        ##### TEMPORARY ###################################################################################################################
40
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_GUI)
41
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
42
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
43
+
44
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb)
45
+        self.main_light_tradfri[0].add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb)
46
+        self.main_light_tradfri[0].add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb)
47
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb)
48
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb)
49
+        ##### TEMPORARY ###################################################################################################################
50
+
51
+        #
52
+        # Virtual Device Interface
30 53
         #
31 54
         self.main_light_videv = videv_switch_brightness_color_temp(
32 55
             mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_VIDEV,
@@ -35,26 +58,71 @@ class ground_floor_west_floor(room_shelly_silvercrest_light):
35 58
             self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
36 59
         )
37 60
 
38
-    def send_init_message_main_light(self):
39
-        super().send_init_message_main_light()
40
-        self.main_light_tradfri_2.request_data()
41
-
42 61
 
43
-class ground_floor_west_marion(room_shelly):
44
-    # http://shelly1l-E8DB84A1E067
62
+class ground_floor_west_marion(room):
45 63
     def __init__(self, mqtt_client):
46
-        super().__init__(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_SHELLY, config.TOPIC_GFW_MARION_MAIN_LIGHT_GUI)
47
-        # radiator valve
48
-        self.radiator_function = radiator_function(mqtt_client, config.TOPIC_GFW_MARION_RADIATOR_VALVE_ZIGBEE,
49
-                                                   config.TOPIC_GFW_MARION_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_GFW_MARION)
64
+        #
65
+        # Device initialisation
66
+        #
67
+        # http://shelly1l-E8DB84A1E067
68
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_SHELLY)
69
+        self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_ZIGBEE)
70
+        super().__init__(mqtt_client)
71
+
72
+        #
73
+        # Functionality initialisation
74
+        #
75
+        # heating function
76
+        self.heating_function = heating_function(self.heating_valve, config.DEFAULT_TEMPERATURE_GFW_MARION)
77
+
78
+        ##### TEMPORARY ###################################################################################################################
79
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_GUI)
80
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
81
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
82
+
83
+        self.gui_heating = devices.nodered_gui_radiator(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_GUI)
84
+        self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_temperature_mcb, True)
85
+
86
+        def set_heating_setpoint(device, key, data):
87
+            self.heating_function.set(heating_function.KEY_USER_TEMPERATURE_SETPOINT, data)
88
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, set_heating_setpoint)
89
+        self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_setpoint_temperature_mcb, True)
90
+
91
+        def boost(device, key, data):
92
+            self.heating_function.set(heating_function.KEY_START_BOOST, data)
93
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, boost)
94
+
95
+        def setpoint_to_default(device, key, data):
96
+            self.heating_function.set(heating_function.KEY_SET_DEFAULT_TEMPERATURE, data)
97
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, setpoint_to_default)
98
+
99
+        def away_mode(device, key, data):
100
+            self.heating_function.set(heating_function.KEY_AWAY_MODE, data)
101
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, away_mode)
102
+        self.heating_function.add_callback(heating_function.KEY_AWAY_MODE, None, self.gui_heating.set_away_mcb)
103
+
104
+        def summer_mode(device, key, data):
105
+            self.heating_function.set(heating_function.KEY_SUMMER_MODE, data)
106
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, summer_mode)
107
+        self.heating_function.add_callback(heating_function.KEY_SUMMER_MODE, None, self.gui_heating.set_summer_mcb)
108
+
109
+        self.heating_function.add_callback(heating_function.KEY_BOOST_TIMER, None, self.gui_heating.set_timer_mcb)
110
+        ##### TEMPORARY ###################################################################################################################
111
+
112
+        #
113
+        # Virtual Device Interface
50 114
         #
51 115
         self.main_light_videv = videv_switching(
52 116
             mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_VIDEV,
53 117
             self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
54 118
         )
119
+        self.heating_function_videv = videv_heating(
120
+            mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_VIDEV,
121
+            self.heating_function
122
+        )
55 123
 
56 124
 
57
-class ground_floor_west_dirk(room_shelly_tradfri_light):
125
+class ground_floor_west_dirk(room):
58 126
     STATE_ACTIVE_DEVICE_MAIN_LIGHT = 0
59 127
     STATE_ACTIVE_DEVICE_DESK_LIGHT = 1
60 128
     STATE_ACTIVE_DEVICE_AMPLIFIER = 2
@@ -73,82 +141,126 @@ class ground_floor_west_dirk(room_shelly_tradfri_light):
73 141
     AUDIO_SOURCE_CD = 1
74 142
     AUDIO_SOURCE_RASPI = 2
75 143
 
76
-    # http://shelly1l-3C6105E44F27
77 144
     def __init__(self, mqtt_client):
78
-        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)
79 145
         #
146
+        # Device initialisation
147
+        #
148
+        # http://shelly1l-3C6105E44F27
149
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_SHELLY)
150
+        self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_ZIGBEE)
80 151
         self.powerplug_common = devices.my_powerplug(mqtt_client, config.TOPIC_GFW_DIRK_POWERPLUG)
81 152
         self.desk_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_ZIGBEE)
82 153
         self.button_tradfri = devices.tradfri_button(mqtt_client, config.TOPIC_GFW_DIRK_INPUT_DEVICE)
83
-        #
84
-        self.gui_desk_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_GUI)
85
-        #
86
-        self.gui_amplifier = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_GUI)
87
-        self.gui_cd_player = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_CD_PLAYER_GUI)
88
-        self.gui_pc_dock = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_GUI)
89
-        #
90 154
         self.remote_amplifier = devices.remote(mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_REMOTE)
155
+        self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_ZIGBEE)
156
+        super().__init__(mqtt_client)
91 157
         #
92
-        self.brightness_functions = brightness_choose_n_action(mqtt_client, self.button_tradfri, config.TOPIC_GFW_DIRK_DEVICE_CHOOSER_LED)
158
+        # Functionality initialisation
159
+        #
160
+        # Button - Brightness functionality
161
+        self.brightness_functions = brightness_choose_n_action(self.button_tradfri)
93 162
         self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0)
94 163
         self.brightness_functions.add(self.desk_light_tradfri, self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT)
95 164
         self.brightness_functions.add(self.remote_amplifier, self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER)
96
-        #
97
-        self.spotify_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_SPOTIFY)
98
-        self.mpd_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_MPD)
99
-        # radiator valve
100
-        self.radiator_function = radiator_function(mqtt_client, config.TOPIC_GFW_DIRK_RADIATOR_VALVE_ZIGBEE,
101
-                                                   config.TOPIC_GFW_DIRK_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_GFW_DIRK)
102
-        #
103
-        self.delayed_task = task.delayed(1.0, self.send_audio_source)
104
-        #
105
-        # Callback initialisation
106
-        #
107
-
108
-        # main light
165
+        # Button - Main light
109 166
         self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_TOGGLE,
110 167
                                          self.main_light_shelly.toggle_output_0_mcb)
111
-
112
-        # desk light
113
-        # switch
168
+        # Button - Desk light
114 169
         self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT,
115 170
                                          self.powerplug_common.toggle_output_1_mcb)
171
+        # Button - Amplifier
172
+        self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT_LONG,
173
+                                         self.powerplug_common.toggle_output_0_mcb)
174
+        # Button - CD player
175
+        self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG,
176
+                                         self.powerplug_common.toggle_output_2_mcb)
177
+        # Button - PC dock
178
+        self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT,
179
+                                         self.powerplug_common.toggle_output_3_mcb)
180
+
181
+        # Mediaplayer - Spotify / MPD state
182
+        self.spotify_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_SPOTIFY)
183
+        self.mpd_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_MPD)
184
+        # Mediaplayer - Amplifier auto on
185
+        self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.powerplug_common.set_output_0_mcb, True)
186
+        self.spotify_state.add_callback(devices.status.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
187
+        self.mpd_state.add_callback(devices.status.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
188
+        # Mediaplayer - Audio source selection
189
+        self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, True, self.audio_source_selector, True)
190
+        self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, True, self.audio_source_selector, True)
191
+        self.spotify_state.add_callback(devices.status.KEY_STATE, True, self.audio_source_selector, True)
192
+        self.mpd_state.add_callback(devices.status.KEY_STATE, True, self.audio_source_selector, True)
193
+        self.audio_source = self.AUDIO_SOURCE_PC
194
+
195
+        # heating function
196
+        self.heating_function = heating_function(self.heating_valve, config.DEFAULT_TEMPERATURE_GFW_DIRK)
197
+
198
+        ##### TEMPORARY ###################################################################################################################
199
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_GUI)
200
+        self.gui_desk_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_GUI)
201
+        self.gui_amplifier = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_GUI)
202
+        self.gui_cd_player = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_CD_PLAYER_GUI)
203
+        self.gui_pc_dock = devices.nodered_gui_switch(mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_GUI)
204
+        # Callback initialisation
205
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
206
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
207
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
208
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb)
209
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb)
210
+        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb)
211
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb)
212
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb)
116 213
         self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.powerplug_common.set_output_1_mcb)
117 214
         self.powerplug_common.add_callback(self.KEY_POWERPLUG_DESK_LIGHT, None, self.gui_desk_light.set_state_mcb)
118
-        # brightness and color temp
119 215
         self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.desk_light_tradfri.set_brightness_mcb)
120 216
         self.gui_desk_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.desk_light_tradfri.set_color_temp_mcb)
121 217
         self.powerplug_common.add_callback(self.KEY_POWERPLUG_DESK_LIGHT, None, self.gui_desk_light.set_enable_mcb)
122 218
         self.desk_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_desk_light.set_brightness_mcb)
123 219
         self.desk_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_desk_light.set_color_temp_mcb)
124
-
125
-        # amplifier
126
-        self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT_LONG,
127
-                                         self.powerplug_common.toggle_output_0_mcb)
128 220
         self.gui_amplifier.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_common.set_output_0_mcb)
129 221
         self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, None, self.gui_amplifier.set_state_mcb)
130
-        # amplifier auto on
131
-        self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.cd_amplifier_synchronisation, True)
132
-        self.spotify_state.add_callback(devices.status.KEY_STATE, None, self.raspi_amplifier_synchronisation, True)
133
-        self.mpd_state.add_callback(devices.status.KEY_STATE, None, self.raspi_amplifier_synchronisation, True)
134
-        # audio source
135
-        self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, True, self.audio_source_selector, True)
136
-        self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, True, self.audio_source_selector, True)
137
-        self.spotify_state.add_callback(devices.status.KEY_STATE, True, self.audio_source_selector, True)
138
-        self.mpd_state.add_callback(devices.status.KEY_STATE, True, self.audio_source_selector, True)
139
-        self.audio_source = self.AUDIO_SOURCE_PC
140
-
141
-        # cd player
142
-        self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG,
143
-                                         self.powerplug_common.toggle_output_2_mcb)
144 222
         self.gui_cd_player.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_common.set_output_2_mcb)
145 223
         self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.gui_cd_player.set_state_mcb)
146
-
147
-        # pc dock
148
-        self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT,
149
-                                         self.powerplug_common.toggle_output_3_mcb)
150 224
         self.gui_pc_dock.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_common.set_output_3_mcb)
151 225
         self.powerplug_common.add_callback(self.KEY_POWERPLUG_PC_DOCK, None, self.gui_pc_dock.set_state_mcb)
226
+
227
+        self.gui_heating = devices.nodered_gui_radiator(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_GUI)
228
+
229
+        def set_heating_setpoint(device, key, data):
230
+            self.heating_function.set(heating_function.KEY_USER_TEMPERATURE_SETPOINT, data)
231
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, set_heating_setpoint)
232
+        self.heating_function.add_callback(heating_function.KEY_TEMPERATURE_SETPOINT, None, self.gui_heating.set_setpoint_temperature_mcb, True)
233
+
234
+        def boost(device, key, data):
235
+            self.heating_function.set(heating_function.KEY_START_BOOST, data)
236
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, boost)
237
+
238
+        def setpoint_to_default(device, key, data):
239
+            self.heating_function.set(heating_function.KEY_SET_DEFAULT_TEMPERATURE, data)
240
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, setpoint_to_default)
241
+
242
+        def away_mode(device, key, data):
243
+            self.heating_function.set(heating_function.KEY_AWAY_MODE, data)
244
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, away_mode)
245
+        self.heating_function.add_callback(heating_function.KEY_AWAY_MODE, None, self.gui_heating.set_away_mcb)
246
+
247
+        def summer_mode(device, key, data):
248
+            self.heating_function.set(heating_function.KEY_SUMMER_MODE, data)
249
+        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, summer_mode)
250
+        self.heating_function.add_callback(heating_function.KEY_SUMMER_MODE, None, self.gui_heating.set_summer_mcb)
251
+
252
+        self.heating_function.add_callback(heating_function.KEY_BOOST_TIMER, None, self.gui_heating.set_timer_mcb)
253
+
254
+        self.gui_led_active_device = devices.nodered_gui_leds(mqtt_client, config.TOPIC_GFW_DIRK_DEVICE_CHOOSER_LED)
255
+
256
+        def update_active_device_led(device, key, data):
257
+            for i in range(0, len(self.brightness_functions.brightness_device_list)):
258
+                self.gui_led_active_device.set_led(devices.nodered_gui_leds.KEY_LED_LIST[i], data == i)
259
+        self.brightness_functions.add_callback(brightness_choose_n_action.KEY_ACTIVE_DEVICE, None, update_active_device_led, True)
260
+        ##### TEMPORARY ###################################################################################################################
261
+
262
+        #
263
+        # Virtual Device Interface
152 264
         #
153 265
         self.main_light_videv = videv_switch_brightness_color_temp(
154 266
             mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_VIDEV,
@@ -174,19 +286,24 @@ class ground_floor_west_dirk(room_shelly_tradfri_light):
174 286
             mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_VIDEV,
175 287
             self.powerplug_common, self.KEY_POWERPLUG_PC_DOCK
176 288
         )
289
+        self.heating_function_videv = videv_heating(
290
+            mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_VIDEV,
291
+            self.heating_function
292
+        )
293
+        self.brightness_functions_device_videv = videv_multistate(
294
+            mqtt_client, config.TOPIC_GFW_DIRK_ACTIVE_BRIGHTNESS_DEVICE_VIDEV,
295
+            brightness_choose_n_action.KEY_ACTIVE_DEVICE, self.brightness_functions, 3
296
+        )
297
+
298
+        #
299
+        # Other stuff
300
+        #
301
+        self.delayed_task_remote = task.delayed(1.0, self.send_audio_source)
177 302
 
178 303
     def all_off(self, device=None, key=None, data=None):
179 304
         super().all_off(device, key, data)
180 305
         self.powerplug_common.set_output_all(False)
181 306
 
182
-    def cd_amplifier_synchronisation(self, device, key, data):
183
-        logger.info("Syncing \"%s\" amplifier with cd player: %s", type(self).__name__, data)
184
-        self.powerplug_common.set_output(self.KEY_POWERPLUG_AMPLIFIER, data)
185
-
186
-    def raspi_amplifier_synchronisation(self, device, key, data):
187
-        logger.info("Syncing \"%s\" amplifier with raspi player: %s", type(self).__name__, data)
188
-        self.powerplug_common.set_output(self.KEY_POWERPLUG_AMPLIFIER, data)
189
-
190 307
     def audio_source_selector(self, device, key, data):
191 308
         if device == self.powerplug_common and key == self.KEY_POWERPLUG_CD_PLAYER:
192 309
             # switch on of cd player
@@ -196,7 +313,7 @@ class ground_floor_west_dirk(room_shelly_tradfri_light):
196 313
             self.audio_source = self.AUDIO_SOURCE_RASPI
197 314
         elif device == self.powerplug_common and key == self.KEY_POWERPLUG_AMPLIFIER:
198 315
             # switch on of amplifier -> select source and reset stored source value
199
-            self.delayed_task.run()
316
+            self.delayed_task_remote.run()
200 317
 
201 318
     def send_audio_source(self):
202 319
         if self.audio_source == self.AUDIO_SOURCE_PC:

+ 191
- 136
function/modules.py Целия файл

@@ -11,10 +11,10 @@ Targets:
11 11
     - Method .add_calback(key, data, callback, on_change_only=False) to register videv actualisation on changes
12 12
 """
13 13
 
14
-import config
14
+from base import common_base
15 15
 import devices
16
-from function.db import get_gui_radiator_data, set_gui_radiator_data
17
-from function.rooms import room_shelly
16
+from function.db import get_radiator_data, set_radiator_data
17
+from function.helpers import now, sunset_time, sunrise_time
18 18
 import logging
19 19
 import task
20 20
 
@@ -25,34 +25,25 @@ except ImportError:
25 25
 logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
26 26
 
27 27
 
28
-class base(object):
29
-    def set(key, data):
30
-        pass
28
+class brightness_choose_n_action(common_base):
29
+    KEY_ACTIVE_DEVICE = 'active_device'
30
+    #
31
+    DEFAULT_VALUES = {KEY_ACTIVE_DEVICE: None}
31 32
 
32
-    def add_calback(self, key, data, callback, on_change_only=False):
33
-        pass
34
-
35
-
36
-class brightness_choose_n_action(object):
37
-    def __init__(self, mqtt_client, button_tradfri, topic_led):
38
-        self.gui_led_active_device = devices.nodered_gui_leds(mqtt_client, topic_led)
33
+    def __init__(self, button_tradfri):
34
+        super().__init__()
39 35
         # brightness change
40
-        button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION,
41
-                                    devices.tradfri_button.ACTION_BRIGHTNESS_DOWN_LONG, self.brightness_action)
42
-        button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_BRIGHTNESS_UP_LONG, self.brightness_action)
43
-        button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION,
44
-                                    devices.tradfri_button.ACTION_BRIGHTNESS_DOWN_RELEASE, self.brightness_action)
45
-        button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION,
46
-                                    devices.tradfri_button.ACTION_BRIGHTNESS_UP_RELEASE, self.brightness_action)
36
+        button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_DOWN_LONG, self.brightness_action)
37
+        button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_UP_LONG, self.brightness_action)
38
+        button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_DOWN_RELEASE, self.brightness_action)
39
+        button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_UP_RELEASE, self.brightness_action)
47 40
         # device change
48
-        button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_BRIGHTNESS_UP, self.choose_next_device)
49
-        button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_BRIGHTNESS_DOWN, self.choose_prev_device)
41
+        button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_UP, self.choose_next_device)
42
+        button_tradfri.add_callback(button_tradfri.KEY_ACTION, button_tradfri.ACTION_BRIGHTNESS_DOWN, self.choose_prev_device)
50 43
         #
51 44
         self.brightness_device_list = []
52 45
         self.callback_device_list = []
53 46
         self.device_states = []
54
-        self.active_device_state = None
55
-        self.update_active_device_led()
56 47
 
57 48
     def add(self, brightness_device, callback_device, callback_key):
58 49
         """
@@ -74,42 +65,33 @@ class brightness_choose_n_action(object):
74 65
     def device_state_action(self, device, key, data):
75 66
         self.device_states[self.callback_device_list.index((device, key))] = data
76 67
         if data is True:
77
-            self.active_device_state = self.callback_device_list.index((device, key))
78
-            self.update_active_device_led()
68
+            self.set(self.KEY_ACTIVE_DEVICE, self.callback_device_list.index((device, key)))
79 69
         else:
80 70
             self.choose_next_device()
81 71
 
82
-    def update_active_device_led(self):
83
-        for i in range(0, len(self.brightness_device_list)):
84
-            self.gui_led_active_device.set_led(devices.nodered_gui_leds.KEY_LED_LIST[i], self.active_device_state == i)
85
-
86 72
     def choose_prev_device(self, device=None, key=None, data=None):
87
-        if self.active_device_state is not None:
88
-            start_value = self.active_device_state
73
+        if self[self.KEY_ACTIVE_DEVICE] is not None:
74
+            start_value = self[self.KEY_ACTIVE_DEVICE]
89 75
             for i in range(0, len(self.brightness_device_list)):
90 76
                 target_state = (start_value - i - 1) % (len(self.brightness_device_list))
91 77
                 if self.device_states[target_state]:
92
-                    self.active_device_state = target_state
93
-                    self.update_active_device_led()
78
+                    self.set(self.KEY_ACTIVE_DEVICE, target_state)
94 79
                     return
95
-        self.active_device_state = None
96
-        self.update_active_device_led()
80
+        self.set(self.KEY_ACTIVE_DEVICE, None)
97 81
 
98 82
     def choose_next_device(self, device=None, key=None, data=None):
99
-        if self.active_device_state is not None:
100
-            start_value = self.active_device_state
83
+        if self[self.KEY_ACTIVE_DEVICE] is not None:
84
+            start_value = self[self.KEY_ACTIVE_DEVICE]
101 85
             for i in range(0, len(self.brightness_device_list)):
102 86
                 target_state = (start_value + i + 1) % (len(self.brightness_device_list))
103 87
                 if self.device_states[target_state]:
104
-                    self.active_device_state = target_state
105
-                    self.update_active_device_led()
88
+                    self.set(self.KEY_ACTIVE_DEVICE, target_state)
106 89
                     return
107
-        self.active_device_state = None
108
-        self.update_active_device_led()
90
+        self.set(self.KEY_ACTIVE_DEVICE, None)
109 91
 
110 92
     def brightness_action(self, device, key, data):
111
-        if self.active_device_state is not None:
112
-            target = self.brightness_device_list[self.active_device_state]
93
+        if self[self.KEY_ACTIVE_DEVICE] is not None:
94
+            target = self.brightness_device_list[self[self.KEY_ACTIVE_DEVICE]]
113 95
             if data == devices.tradfri_button.ACTION_BRIGHTNESS_UP_LONG:
114 96
                 logger.info("Increasing \"%s\" - %s", type(self).__name__, target.topic)
115 97
                 target.default_inc()
@@ -120,140 +102,213 @@ class brightness_choose_n_action(object):
120 102
                 target.default_stop()
121 103
 
122 104
 
123
-class circulation_pump(room_shelly):
124
-    def __init__(self, mqtt_client):
125
-        super().__init__(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_SHELLY, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_GUI)
105
+class timer_on_activation(common_base):
106
+    KEY_TIMER = 'timer'
107
+    #
108
+    DEFAULT_VALUES = {
109
+        KEY_TIMER: 0
110
+    }
111
+
112
+    def __init__(self, sw_device, sw_key, timer_reload_value):
113
+        super().__init__()
126 114
         #
127
-        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.circ_pump_actions, True)
115
+        self.timer_reload_value = timer_reload_value
128 116
         #
129
-        self.gui_main_light.set_timer('-')
117
+        sw_device.add_callback(sw_key, None, self.circ_pump_actions, True)
130 118
         #
131 119
         self.ct = task.periodic(6, self.cyclic_task)
132
-        self.pump_timer = None
133
-        #
134 120
         self.ct.run()
135 121
 
136 122
     def circ_pump_actions(self, device, key, data):
137 123
         if data is True:
138
-            self.pump_timer = 10
139
-            self.gui_main_light.set_timer(self.pump_timer)
124
+            self.set(self.KEY_TIMER, self.timer_reload_value)
140 125
         else:
141
-            self.pump_timer = None
142
-            self.gui_main_light.set_timer('-')
126
+            self.set(self.KEY_TIMER, 0)
143 127
 
144 128
     def cyclic_task(self, rt):
145
-        if self.pump_timer is not None:
146
-            if self.pump_timer <= 0:
147
-                self.pump_timer = None
148
-                self.gui_main_light.set_timer('-')
149
-            else:
150
-                self.gui_main_light.set_timer(self.pump_timer)
151
-                self.pump_timer -= self.ct.cycle_time / 60
129
+        timer_value = self[self.KEY_TIMER] - self.ct.cycle_time
130
+        if timer_value <= 0:
131
+            self.set(self.KEY_TIMER, 0)
132
+        else:
133
+            self.set(self.KEY_TIMER, timer_value)
152 134
 
153 135
 
154
-class radiator_function(object):
136
+class heating_function(common_base):
137
+    KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint'
138
+    KEY_TEMPERATURE_SETPOINT = 'temperature_setpoint'
139
+    KEY_TEMPERATURE_CURRENT = 'temperature_current'
140
+    KEY_AWAY_MODE = 'away_mode'
141
+    KEY_SUMMER_MODE = 'summer_mode'
142
+    KEY_START_BOOST = 'start_boost'
143
+    KEY_SET_DEFAULT_TEMPERATURE = 'set_default_temperature'
144
+    KEY_BOOST_TIMER = 'boost_timer'
145
+    #
155 146
     BOOST_TEMPERATURE = 30
156 147
     AWAY_REDUCTION = 5
157 148
     SUMMER_TEMPERATURE = 5
158 149
 
159
-    def __init__(self, mqtt_client, topic_valve, topic_gui, default_temperature):
150
+    def __init__(self, heating_valve, default_temperature):
151
+        db_data = get_radiator_data(heating_valve.topic)
152
+        super().__init__({
153
+            self.KEY_USER_TEMPERATURE_SETPOINT: db_data[2] or default_temperature,
154
+            self.KEY_TEMPERATURE_SETPOINT: db_data[3] or default_temperature,
155
+            self.KEY_TEMPERATURE_CURRENT: None,
156
+            self.KEY_AWAY_MODE: db_data[0] or False,
157
+            self.KEY_SUMMER_MODE: db_data[1] or False,
158
+            self.KEY_START_BOOST: True,
159
+            self.KEY_SET_DEFAULT_TEMPERATURE: False,
160
+            self.KEY_BOOST_TIMER: 0
161
+        })
162
+        #
160 163
         self.default_temperature = default_temperature
161
-        self.boost_timer = None
162
-        # device initialisation
163
-        self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, topic_valve)
164
-        self.gui_heating = devices.nodered_gui_radiator(mqtt_client, topic_gui)
165
-        # db-stored data initialisation
166
-        db_data = get_gui_radiator_data(topic_gui)
167
-        self.__away_mode__ = db_data[0] or False
168
-        self.__summer_mode__ = db_data[1] or False
169
-        self.__user_temperature_setpoint__ = db_data[2] or default_temperature
170
-        if self.__away_mode__:
171
-            self.away_mode(None, None, True)
172
-        elif self.__summer_mode__:
173
-            self.summer_mode(None, None, True)
174
-        else:
175
-            self.set_heating_setpoint(None, None, self.__user_temperature_setpoint__)
176
-        # callback initialisation
177
-        self.heating_valve.add_callback(devices.brennenstuhl_heatingvalve.KEY_TEMPERATURE, None, self.gui_heating.set_temperature_mcb)
178
-        self.heating_valve.add_callback(devices.brennenstuhl_heatingvalve.KEY_HEATING_SETPOINT, None, self.get_radiator_setpoint)
164
+        self.heating_valve = heating_valve
165
+        self.heating_valve.set_heating_setpoint(self[self.KEY_TEMPERATURE_SETPOINT])
166
+        #
167
+        self.heating_valve.add_callback(self.heating_valve.KEY_HEATING_SETPOINT, None, self.get_radiator_setpoint)
168
+        self.heating_valve.add_callback(self.heating_valve.KEY_TEMPERATURE, None, self.get_radiator_temperature)
179 169
         #
180
-        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TEMP, None, self.set_heating_setpoint)
181
-        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_BOOST, None, self.boost)
182
-        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SETPOINT_TO_DEFAULT, None, self.setpoint_to_default)
183
-        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_AWAY, None, self.away_mode)
184
-        self.gui_heating.add_callback(devices.nodered_gui_radiator.KEY_SUMMER, None, self.summer_mode)
170
+        self.add_callback(self.KEY_USER_TEMPERATURE_SETPOINT, None, self.user_temperature_setpoint, False)
171
+        self.add_callback(self.KEY_TEMPERATURE_SETPOINT, None, self.set_heating_setpoint, True)
172
+        self.add_callback(self.KEY_AWAY_MODE, None, self.away_mode, True)
173
+        self.add_callback(self.KEY_SUMMER_MODE, None, self.summer_mode, True)
174
+        self.add_callback(self.KEY_SET_DEFAULT_TEMPERATURE, None, self.setpoint_to_default)
175
+        self.add_callback(self.KEY_START_BOOST, True, self.boost, False)
176
+        self.add_callback(self.KEY_BOOST_TIMER, 0, self.timer_expired, True)
185 177
         # cyclic task initialisation
186 178
         self.ct = task.periodic(1, self.cyclic_task)
187 179
         self.ct.run()
188 180
 
181
+    def timer_expired(self, device, data, key):
182
+        self.set(self.KEY_TEMPERATURE_SETPOINT, self[self.KEY_USER_TEMPERATURE_SETPOINT])
183
+        self.heating_valve.logger.info('Timer expired. returning to regular temperature setpoint %.1f°C.',
184
+                                       self[self.KEY_TEMPERATURE_SETPOINT])
185
+
189 186
     def cyclic_task(self, rt):
190
-        if self.boost_timer is not None:
191
-            self.gui_heating.set_timer(round(self.boost_timer / 60, 1))
192
-            #
193
-            self.boost_timer -= self.ct.cycle_time
194
-            if self.boost_timer <= 0:
195
-                self.cancel_boost()
196
-                self.heating_valve.set_heating_setpoint(self.__user_temperature_setpoint__)
197
-                self.heating_valve.logger.info('Timer expired. returning to regular temperature setpoint %.1f°C.', self.__user_temperature_setpoint__)
198
-
199
-    def cancel_boost(self, device=None, key=None, data=None):
200
-        if self.boost_timer is not None:
201
-            self.boost_timer = None
202
-            self.gui_heating.set_timer('-')
203
-
204
-    def update_states(self, away_mode=None, summer_mode=None, user_temperature_setpoint=None):
205
-        if away_mode is not None:
206
-            self.__away_mode__ = away_mode
207
-        if summer_mode is not None:
208
-            self.__summer_mode__ = summer_mode
209
-        if user_temperature_setpoint is not None:
210
-            self.__user_temperature_setpoint__ = user_temperature_setpoint
211
-        set_gui_radiator_data(self.gui_heating.topic, self.__away_mode__, self.__summer_mode__, self.__user_temperature_setpoint__)
212
-        #
213
-        self.gui_heating.set_away(self.__away_mode__)
214
-        self.gui_heating.set_summer(self.__summer_mode__)
215
-        self.gui_heating.set_enable(not self.__away_mode__ and not self.__summer_mode__)
187
+        timer_value = self[self.KEY_BOOST_TIMER] - self.ct.cycle_time
188
+        if self[self.KEY_BOOST_TIMER] <= 0:
189
+            self.set(self.KEY_BOOST_TIMER, 0)
190
+        else:
191
+            self.set(self.KEY_BOOST_TIMER, timer_value)
192
+
193
+    def cancel_boost(self):
194
+        self.set(self.KEY_BOOST_TIMER, 0, block_callback=[self.timer_expired])
195
+
196
+    def set(self, key, data, block_callback=[]):
197
+        rv = super().set(key, data, block_callback)
198
+        set_radiator_data(self.heating_valve.topic, self[self.KEY_AWAY_MODE], self[self.KEY_SUMMER_MODE],
199
+                          self[self.KEY_USER_TEMPERATURE_SETPOINT], self[self.KEY_TEMPERATURE_SETPOINT])
200
+        return rv
216 201
 
217 202
     def away_mode(self, device, key, value):
218 203
         if value is True:
219 204
             self.cancel_boost()
220
-            self.update_states(away_mode=value, summer_mode=False)
221
-            self.heating_valve.set_heating_setpoint(self.__user_temperature_setpoint__ - self.AWAY_REDUCTION)
205
+            self.set(self.KEY_SUMMER_MODE, False, [self.summer_mode])
206
+            self.set(self.KEY_TEMPERATURE_SETPOINT, self[self.KEY_USER_TEMPERATURE_SETPOINT] - self.AWAY_REDUCTION)
222 207
         else:
223
-            self.update_states(away_mode=value)
224
-            self.heating_valve.set_heating_setpoint(self.__user_temperature_setpoint__)
208
+            self.set(self.KEY_TEMPERATURE_SETPOINT, self[self.KEY_USER_TEMPERATURE_SETPOINT])
225 209
 
226 210
     def summer_mode(self, device, key, value):
227 211
         if value is True:
228 212
             self.cancel_boost()
229
-            self.update_states(away_mode=False, summer_mode=value)
230
-            self.heating_valve.set_heating_setpoint(self.SUMMER_TEMPERATURE)
213
+            self.set(self.KEY_AWAY_MODE, False, [self.away_mode])
214
+            self.set(self.KEY_TEMPERATURE_SETPOINT, self.SUMMER_TEMPERATURE)
231 215
         else:
232
-            self.update_states(summer_mode=value)
233
-            self.heating_valve.set_heating_setpoint(self.__user_temperature_setpoint__)
216
+            self.set(self.KEY_TEMPERATURE_SETPOINT, self[self.KEY_USER_TEMPERATURE_SETPOINT])
234 217
 
235 218
     def boost(self, device, key, data):
236
-        if self.boost_timer is None:
219
+        if self[self.KEY_BOOST_TIMER] == 0:
237 220
             self.heating_valve.logger.info('Starting boost mode with setpoint %.1f°C.', self.BOOST_TEMPERATURE)
238
-            self.boost_timer = 15*60
239
-            self.heating_valve.set_heating_setpoint(self.BOOST_TEMPERATURE)
221
+            self.set(self.KEY_BOOST_TIMER, 15*60)
222
+            self.set(self.KEY_TEMPERATURE_SETPOINT, self.BOOST_TEMPERATURE)
240 223
         else:
241
-            self.boost_timer += 15 * 60
242
-        if self.boost_timer > 60 * 60:
243
-            self.boost_timer = 60 * 60
244
-        self.update_states(away_mode=False, summer_mode=False)
224
+            self.set(self.KEY_BOOST_TIMER, min(self[self.KEY_BOOST_TIMER] + 15 * 60, 60 * 60))
225
+        self.set(self.KEY_AWAY_MODE, False, [self.away_mode])
226
+        self.set(self.KEY_SUMMER_MODE, False, [self.summer_mode])
245 227
 
246 228
     def setpoint_to_default(self, device, key, data):
247
-        self.set_heating_setpoint(device, key, self.default_temperature)
229
+        self.cancel_boost()
230
+        self.set(self.KEY_AWAY_MODE, False, [self.away_mode])
231
+        self.set(self.KEY_SUMMER_MODE, False, [self.summer_mode])
232
+        self.set(self.KEY_USER_TEMPERATURE_SETPOINT, self.default_temperature, [self.user_temperature_setpoint])
233
+        self.set(self.KEY_TEMPERATURE_SETPOINT, self.default_temperature)
248 234
 
249
-    def set_heating_setpoint(self, device, key, data):
235
+    def user_temperature_setpoint(self, device, key, data):
250 236
         self.cancel_boost()
251
-        self.update_states(away_mode=False, summer_mode=False, user_temperature_setpoint=data)
237
+        self.set(self.KEY_AWAY_MODE, False, [self.away_mode])
238
+        self.set(self.KEY_SUMMER_MODE, False, [self.summer_mode])
239
+        self.set(self.KEY_TEMPERATURE_SETPOINT, data)
240
+
241
+    def set_heating_setpoint(self, device, key, data):
252 242
         self.heating_valve.set_heating_setpoint(data)
253 243
 
254 244
     def get_radiator_setpoint(self, device, key, data):
255
-        if self.boost_timer is None and not self.__away_mode__ and not self.__summer_mode__:
256
-            self.update_states(user_temperature_setpoint=data)
257
-        else:
258
-            self.update_states()
259
-        self.gui_heating.set_setpoint_temperature(data)
245
+        if self[self.KEY_BOOST_TIMER] == 0 and not self[self.KEY_AWAY_MODE] and not self[self.KEY_SUMMER_MODE]:
246
+            self.set(self.KEY_USER_TEMPERATURE_SETPOINT, data, block_callback=[self.set_heating_setpoint])
247
+
248
+    def get_radiator_temperature(self, device, key, data):
249
+        self.set(self.KEY_TEMPERATURE_CURRENT, data)
250
+
251
+
252
+class motion_sensor_light(common_base):
253
+    KEY_TIMER = 'timer'
254
+    KEY_MOTION_SENSOR = 'motion_%d'
255
+    KEY_MOTION_SENSOR_0 = 'motion_%d' % 0
256
+    KEY_MOTION_SENSOR_1 = 'motion_%d' % 1
257
+    KEY_MOTION_SENSOR_2 = 'motion_%d' % 2
258
+    KEY_MOTION_SENSOR_3 = 'motion_%d' % 3
259
+    KEY_MOTION_SENSOR_4 = 'motion_%d' % 4
260
+
261
+    def __init__(self, sw_device, sw_method, *args, timer_value=30):
262
+        """
263
+        sw_device is the device switching the light, args are 0-n motion sensors
264
+        """
265
+        dv = dict.fromkeys([self.KEY_MOTION_SENSOR % i for i in range(0, len(args))])
266
+        for key in dv:
267
+            dv[key] = False
268
+        dv[self.KEY_TIMER] = 0
269
+        super().__init__(default_values=dv)
270
+        #
271
+        self.sw_device = sw_device
272
+        self.sw_method = sw_method
273
+        self.args = args
274
+        self.timer_reload_value = timer_value
275
+        #
276
+        sw_device.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.reload_timer, True)
277
+        sw_device.add_callback(devices.shelly.KEY_OUTPUT_0, False, self.reset_timer, True)
278
+        for motion_sensor in args:
279
+            motion_sensor.add_callback(motion_sensor.KEY_OCCUPANCY, None, self.set_motion_detected, True)
280
+        #
281
+        self.add_callback(self.KEY_TIMER, 0, self.timer_expired, True)
282
+        #
283
+        cyclic_task = task.periodic(1, self.cyclic_task)
284
+        cyclic_task.run()
285
+
286
+    def reload_timer(self, device, key, data):
287
+        self.set(self.KEY_TIMER, self.timer_reload_value)
288
+
289
+    def reset_timer(self, device=None, key=None, data=None):
290
+        self.set(self.KEY_TIMER, 0)
291
+
292
+    def set_motion_detected(self, device, key, data):
293
+        for sensor_index, arg_device in enumerate(self.args):
294
+            if arg_device.topic == device.topic:
295
+                break
296
+        self.set(self.KEY_MOTION_SENSOR % sensor_index, data)
297
+        if now() < sunrise_time(60) or now() > sunset_time(-60):
298
+            if data is True:
299
+                logger.info("%s: Motion detected - Switching on main light %s", device.topic, self.sw_device.topic)
300
+                self.sw_method(True)
301
+
302
+    def motion_detected(self):
303
+        for i in range(0, len(self.args)):
304
+            if self[self.KEY_MOTION_SENSOR % i]:
305
+                return True
306
+        return False
307
+
308
+    def timer_expired(self, device, key, data):
309
+        logger.info("No motion and time ran out - Switching off main light %s", self.sw_device.topic)
310
+        self.sw_method(False)
311
+
312
+    def cyclic_task(self, cyclic_task):
313
+        min_value = 10 if self.motion_detected() else 0
314
+        self.set(self.KEY_TIMER, max(min_value, self[self.KEY_TIMER] - cyclic_task.cycle_time))

+ 26
- 111
function/rooms.py Целия файл

@@ -2,9 +2,6 @@
2 2
 # -*- coding: utf-8 -*-
3 3
 #
4 4
 
5
-import config
6
-import devices
7
-from function.helpers import now, sunset_time, sunrise_time
8 5
 import logging
9 6
 import task
10 7
 
@@ -14,135 +11,53 @@ except ImportError:
14 11
     ROOT_LOGGER_NAME = 'root'
15 12
 logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
16 13
 
14
+# TODO: all_off: getattr and identify switchable devices switch those off (default method of device)
15
+#        - all devices are as attributes in the room class
16
+#        - implement a all_off blacklist to be initialised while __init__
17
+# TODO: implement all off and user feedback method (all off save)
18
+
17 19
 
18 20
 class room(object):
19 21
     def __init__(self, mqtt_client):
20 22
         self.mqtt_client = mqtt_client
21
-
22
-
23
-class room_shelly(room):
24
-    def __init__(self, mqtt_client, topic_shelly, topic_gui):
25
-        super().__init__(mqtt_client)
26
-        self.main_light_shelly = devices.shelly(mqtt_client, topic_shelly)
27
-        #
28
-        self.gui_main_light = devices.nodered_gui_light(mqtt_client, topic_gui)
29
-        #
30
-        # Callback initialisation
31
-        #
32
-        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
33
-        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
34 23
         #
35 24
         self.block_all_off = False
36 25
         self.last_flash_data = None
37
-        self.delayed_task = task.delayed(.25, self.main_light_shelly.toggle_output_0_mcb, None, None, None)
26
+        try:
27
+            self.delayed_task = task.delayed(.25, self.main_light_shelly.toggle_output_0_mcb, None, None, None)
28
+        except AttributeError:
29
+            logger.exception("Device self.main_light does not exist!")
30
+            self.delayed_task = task.delayed(.25, self.__delayed_task_dummy__, None, None, None)
31
+
32
+    def __delayed_task_dummy__(self, device, key, data):
33
+        logger.exception("Device self.main_light does not exist!")
38 34
 
39 35
     def all_off(self, device=None, key=None, data=None):
40 36
         if not self.block_all_off:
41 37
             logger.info("Switching all off \"%s\"", type(self).__name__)
42
-            self.main_light_shelly.set_output_0(False)
43
-            self.main_light_shelly.set_output_1(False)
38
+            try:
39
+                self.main_light_shelly.set_output_0(False)
40
+                self.main_light_shelly.set_output_1(False)
41
+            except AttributeError:
42
+                logger.exception("Device self.main_light does not exist!")
44 43
         self.block_all_off = False
45 44
 
46 45
     def all_off_feedback(self, device=None, key=None, data=None):
47 46
         logger.info("Flashing \"%s\" main light", type(self).__name__)
48 47
         if self.main_light_shelly.output_0 is False:
49
-            self.main_light_shelly.set_output_0(True)
48
+            try:
49
+                self.main_light_shelly.set_output_0(True)
50
+            except AttributeError:
51
+                logger.exception("Device self.main_light does not exist!")
50 52
             self.block_all_off = True
51 53
             self.delayed_task.run()
52 54
 
53 55
     def flash_main_light(self, device, key, data):
54 56
         if self.last_flash_data != data and data is True:
55 57
             logger.info("Flashing \"%s\" main light", type(self).__name__)
56
-            self.main_light_shelly.toggle_output_0_mcb(device, key, data)
58
+            try:
59
+                self.main_light_shelly.toggle_output_0_mcb(device, key, data)
60
+            except AttributeError:
61
+                logger.exception("Device self.main_light does not exist!")
57 62
             self.delayed_task.run()
58 63
         self.last_flash_data = data
59
-
60
-
61
-class room_shelly_motion_sensor(room_shelly):
62
-    def __init__(self, mqtt_client, topic_shelly, topic_gui, topic_motion_sensor_1, topic_motion_sensor_2=None, timer_value=30):
63
-        super().__init__(mqtt_client, topic_shelly, topic_gui)
64
-        self.timer_value = timer_value
65
-        self.motion_detected_1 = False
66
-        self.motion_detected_2 = False
67
-        #
68
-        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.reload_timer, True)
69
-        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, False, self.reset_timer, True)
70
-        #
71
-        self.motion_sensor_silvercrest_1 = devices.silvercrest_motion_sensor(mqtt_client, topic_motion_sensor_1)
72
-        self.motion_sensor_silvercrest_1.add_callback(devices.silvercrest_motion_sensor.KEY_OCCUPANCY, None, self.set_motion_detected)
73
-        #
74
-        if topic_motion_sensor_2 is not None:
75
-            self.motion_sensor_silvercrest_2 = devices.silvercrest_motion_sensor(mqtt_client, topic_motion_sensor_2)
76
-            self.motion_sensor_silvercrest_2.add_callback(devices.silvercrest_motion_sensor.KEY_OCCUPANCY, None, self.set_motion_detected)
77
-        #
78
-        self.reset_timer()
79
-        #
80
-        cyclic_task = task.periodic(1, self.cyclic_task)
81
-        cyclic_task.run()
82
-
83
-    def set_motion_detected(self, device, key, data):
84
-        if device == self.motion_sensor_silvercrest_1:
85
-            self.motion_detected_1 = data
86
-        elif device == self.motion_sensor_silvercrest_2:
87
-            self.motion_detected_2 = data
88
-        self.gui_main_light.set_led(devices.nodered_gui_light.KEY_LED_0, self.motion_detected_1)
89
-        self.gui_main_light.set_led(devices.nodered_gui_light.KEY_LED_1, self.motion_detected_2)
90
-        if now() < sunrise_time(60) or now() > sunset_time(-60):
91
-            if data is True:
92
-                logger.info("%s: Motion detected - Switching on main light %s", device.topic, self.main_light_shelly.topic)
93
-                self.main_light_shelly.set_output_0(True)
94
-
95
-    def reload_timer(self, device, key, data):
96
-        self.main_light_timer = self.timer_value
97
-
98
-    def reset_timer(self, device=None, key=None, data=None):
99
-        self.main_light_timer = None
100
-        self.gui_main_light.set_timer('-')
101
-
102
-    def cyclic_task(self, cyclic_task):
103
-        if self.main_light_timer is not None:
104
-            if self.main_light_timer <= 0:
105
-                logger.info("No motion and time ran out - Switching off main light %s", self.main_light_shelly.topic)
106
-                self.main_light_shelly.set_output_0(False)
107
-                self.main_light_timer = None
108
-                self.gui_main_light.set_timer('-')
109
-            else:
110
-                self.gui_main_light.set_timer(self.main_light_timer)
111
-                if (self.motion_detected_1 or self.motion_detected_2) and self.main_light_timer <= self.timer_value / 10:
112
-                    self.main_light_timer = self.timer_value / 10
113
-                else:
114
-                    self.main_light_timer -= cyclic_task.cycle_time
115
-
116
-
117
-class room_shelly_tradfri_light(room_shelly):
118
-    def __init__(self, mqtt_client, topic_shelly, topic_gui, topic_tradfri_light):
119
-        super().__init__(mqtt_client, topic_shelly, topic_gui)
120
-        self.main_light_tradfri = devices.tradfri_light(mqtt_client, topic_tradfri_light)
121
-        #
122
-        # Callback initialisation
123
-        #
124
-        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
125
-        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_enable_mcb)
126
-        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_main_light.set_brightness_mcb)
127
-        self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.gui_main_light.set_color_temp_mcb)
128
-        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.main_light_tradfri.set_brightness_mcb)
129
-        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_COLOR_TEMP, None, self.main_light_tradfri.set_color_temp_mcb)
130
-
131
-
132
-class room_shelly_silvercrest_light(room_shelly_tradfri_light):
133
-    def __init__(self, mqtt_client, topic_shelly, topic_gui, topic_tradfri_light):
134
-        super().__init__(mqtt_client, topic_shelly, topic_gui, topic_tradfri_light)
135
-        #
136
-        # Callback initialisation
137
-        #
138
-        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.get_initial_main_light_data)
139
-        #
140
-        self.main_light_shelly_last = None
141
-
142
-    def get_initial_main_light_data(self, device, key, data):
143
-        if data is True and self.main_light_shelly_last != data:
144
-            self.send_init_message_main_light()
145
-        self.main_light_shelly_last = data
146
-
147
-    def send_init_message_main_light(self):
148
-        self.main_light_tradfri.request_data()

+ 41
- 9
function/stairway.py Целия файл

@@ -4,10 +4,10 @@
4 4
 
5 5
 import config
6 6
 import devices
7
-from function.modules import brightness_choose_n_action
8 7
 import logging
9
-from function.rooms import room_shelly_motion_sensor
10
-from function.videv import videv_switching
8
+from function.modules import motion_sensor_light
9
+from function.rooms import room
10
+from function.videv import videv_switching_motion
11 11
 
12 12
 try:
13 13
     from config import APP_NAME as ROOT_LOGGER_NAME
@@ -16,14 +16,46 @@ except ImportError:
16 16
 logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
17 17
 
18 18
 
19
-class stairway(room_shelly_motion_sensor):
19
+class stairway(room):
20 20
     def __init__(self, mqtt_client):
21
+        #
22
+        # Device initialisation
23
+        #
21 24
         # http://shelly1-3494546A9364
22
-        super().__init__(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_SHELLY, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_GUI,
23
-                         config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_GF, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_FF,
24
-                         timer_value=config.USER_ON_TIME_STAIRWAYS)
25
+        self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_SHELLY)
26
+        self.motion_sensor_gf = devices.silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_GF)
27
+        self.motion_sensor_ff = devices.silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_FF)
28
+        super().__init__(mqtt_client)
29
+
30
+        #
31
+        # Functionality initialisation
32
+        #
33
+        self.motion_sensor_light = motion_sensor_light(
34
+            self.main_light_shelly, self.main_light_shelly.set_output_0,
35
+            self.motion_sensor_gf, self.motion_sensor_ff,
36
+            timer_value=config.USER_ON_TIME_STAIRWAYS
37
+        )
38
+
39
+        ##### TEMPORARY ###################################################################################################################
40
+        self.gui_main_light = devices.nodered_gui_light(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_GUI)
41
+        self.gui_main_light.add_callback(devices.nodered_gui_light.KEY_STATE, None, self.main_light_shelly.set_output_0_mcb)
42
+        self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_main_light.set_state_mcb)
43
+        self.motion_sensor_light.add_callback(motion_sensor_light.KEY_TIMER, None, self.gui_main_light.set_timer_mcb)
44
+
45
+        def set_led(device, key, data):
46
+            if key == motion_sensor_light.KEY_MOTION_SENSOR_0:
47
+                self.gui_main_light.set_led(devices.nodered_gui_light.KEY_LED_0, data)
48
+            if key == motion_sensor_light.KEY_MOTION_SENSOR_1:
49
+                self.gui_main_light.set_led(devices.nodered_gui_light.KEY_LED_1, data)
50
+        self.motion_sensor_light.add_callback(motion_sensor_light.KEY_MOTION_SENSOR_0, None, set_led)
51
+        self.motion_sensor_light.add_callback(motion_sensor_light.KEY_MOTION_SENSOR_1, None, set_led)
52
+        ##### TEMPORARY ###################################################################################################################
53
+
54
+        #
55
+        # Virtual Device Interface
25 56
         #
26
-        self.main_light_videv = videv_switching(
57
+        self.main_light_videv = videv_switching_motion(
27 58
             mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_VIDEV,
28
-            self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
59
+            self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
60
+            self.motion_sensor_light
29 61
         )

+ 111
- 9
function/videv.py Целия файл

@@ -10,8 +10,13 @@ Targets:
10 10
   * No functionality should be implemented here
11 11
 """
12 12
 
13
+# TODO: Extend virtual devices
14
+#         * Digital-Audio-Sources (Spotify, MPD, Currently playing) oder direkt von my_apps?!
15
+#         *
16
+
13 17
 from base import mqtt_base
14 18
 import devices
19
+import inspect
15 20
 import json
16 21
 import logging
17 22
 
@@ -25,10 +30,10 @@ except ImportError:
25 30
 
26 31
 
27 32
 class base(mqtt_base):
28
-    DEFAULT_VALUES = {}
33
+    EXEC_RX_FUNC_ALWAYS = []
29 34
 
30
-    def __init__(self, mqtt_client, topic, *args):
31
-        super().__init__(mqtt_client, topic)
35
+    def __init__(self, mqtt_client, topic, *args, default_values=None):
36
+        super().__init__(mqtt_client, topic, default_values=default_values)
32 37
         self.__device_list__ = {}
33 38
         for videv_key, device in [reduced[:2] for reduced in args]:
34 39
             self.__device_list__[videv_key] = device
@@ -53,7 +58,7 @@ class base(mqtt_base):
53 58
                 data = json.loads(message.payload)
54 59
             except json.decoder.JSONDecodeError:
55 60
                 data = message.payload
56
-            if data != self[key]:
61
+            if data != self[key] or key in self.EXEC_RX_FUNC_ALWAYS:
57 62
                 self.__rx_functionality__(key, data)
58 63
             self.set(key, data)
59 64
         else:
@@ -67,8 +72,8 @@ class base(mqtt_base):
67 72
 
68 73
 
69 74
 class base_routing(base):
70
-    def __init__(self, mqtt_client, topic, *args):
71
-        super().__init__(mqtt_client, topic, *args)
75
+    def __init__(self, mqtt_client, topic, *args, default_values=None):
76
+        super().__init__(mqtt_client, topic, *args, default_values=default_values)
72 77
         #
73 78
         self.__device_key__ = {}
74 79
         index = 0
@@ -79,7 +84,10 @@ class base_routing(base):
79 84
             index += 1
80 85
         # add callbacks
81 86
         for key in self.__device_list__:
82
-            self.__device_list__[key].add_callback(self.__device_key__[key], None, self.__device_data__, True)
87
+            if self.__device_list__[key].__class__.__name__ == "group":
88
+                self.__device_list__[key][0].add_callback(self.__device_key__[key], None, self.__device_data__, True)
89
+            else:
90
+                self.__device_list__[key].add_callback(self.__device_key__[key], None, self.__device_data__, True)
83 91
 
84 92
     def __rx_functionality__(self, key, data):
85 93
         try:
@@ -88,7 +96,14 @@ class base_routing(base):
88 96
             self.logger.warning("RX passthrough not possible for key %s", key)
89 97
 
90 98
     def __device_data__(self, device, key, data):
91
-        l1 = [k for k, v in self.__device_list__.items() if v == device]
99
+        l1 = []
100
+        for k, v in self.__device_list__.items():
101
+            if v.__class__.__name__ == "group":
102
+                if device in v:
103
+                    l1.append(k)
104
+            else:
105
+                if v == device:
106
+                    l1.append(k)
92 107
         l2 = [k for k, v in self.__device_key__.items() if v == key]
93 108
         try:
94 109
             videv_key = [k for k in l1 if k in l2][0]
@@ -107,10 +122,45 @@ class videv_switching(base_routing):
107 122
     }
108 123
 
109 124
     def __init__(self, mqtt_client, topic, sw_device, sw_key):
110
-        #
111 125
         super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key))
112 126
 
113 127
 
128
+class videv_switching_timer(base_routing):
129
+    KEY_STATE = 'state'
130
+    KEY_TIMER = 'timer'
131
+    #
132
+    DEFAULT_VALUES = {
133
+        KEY_STATE: False,
134
+        KEY_TIMER: 0
135
+    }
136
+
137
+    def __init__(self, mqtt_client, topic, sw_device, sw_key, tm_device, tm_key):
138
+        super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_TIMER, tm_device, tm_key))
139
+
140
+
141
+class videv_switching_motion(base_routing):
142
+    KEY_STATE = 'state'
143
+    KEY_TIMER = 'timer'
144
+    KEY_MOTION_SENSOR = 'motion_%d'
145
+    #
146
+    DEFAULT_VALUES = {
147
+        KEY_STATE: False,
148
+        KEY_TIMER: 0
149
+    }
150
+
151
+    def __init__(self, mqtt_client, topic, sw_device, sw_key, motion_function):
152
+        dv = {self.KEY_STATE: False, self.KEY_TIMER: 0}
153
+        for i in range(0, len(motion_function.args)):
154
+            dv[motion_function.KEY_MOTION_SENSOR % i] = False
155
+        super().__init__(
156
+            mqtt_client, topic,
157
+            (self.KEY_STATE, sw_device, sw_key),
158
+            (self.KEY_TIMER, motion_function, motion_function.KEY_TIMER),
159
+            *[[self.KEY_MOTION_SENSOR % i, motion_function, motion_function.KEY_MOTION_SENSOR % i] for i in range(0, len(motion_function.args))],
160
+            default_values=dv
161
+        )
162
+
163
+
114 164
 class videv_switch_brightness(base_routing):
115 165
     KEY_STATE = 'state'
116 166
     KEY_BRIGHTNESS = 'brightness'
@@ -144,3 +194,55 @@ class videv_switch_brightness_color_temp(base_routing):
144 194
             (self.KEY_BRIGHTNESS, br_device, br_key),
145 195
             (self.KEY_COLOR_TEMP, ct_device, ct_key)
146 196
         )
197
+
198
+
199
+class videv_heating(base_routing):
200
+    KEY_TEMPERATURE = 'temperature'
201
+    KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint'
202
+    KEY_VALVE_TEMPERATURE_SETPOINT = 'valve_temperature_setpoint'
203
+    KEY_AWAY_MODE = 'away_mode'
204
+    KEY_SUMMER_MODE = 'summer_mode'
205
+    KEY_START_BOOST = 'start_boost'
206
+    KEY_SET_DEFAULT_TEMPERATURE = 'set_default_temperature'
207
+    KEY_BOOST_TIMER = 'boost_timer'
208
+    #
209
+    EXEC_RX_FUNC_ALWAYS = [KEY_START_BOOST, KEY_SET_DEFAULT_TEMPERATURE, KEY_USER_TEMPERATURE_SETPOINT]
210
+
211
+    def __init__(self, mqtt_client, topic, heating_function):
212
+        #
213
+        super().__init__(
214
+            mqtt_client, topic,
215
+            (self.KEY_TEMPERATURE, heating_function, heating_function.KEY_TEMPERATURE_CURRENT),
216
+            (self.KEY_USER_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_USER_TEMPERATURE_SETPOINT),
217
+            (self.KEY_VALVE_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_TEMPERATURE_SETPOINT),
218
+            (self.KEY_AWAY_MODE, heating_function, heating_function.KEY_AWAY_MODE),
219
+            (self.KEY_SUMMER_MODE, heating_function, heating_function.KEY_SUMMER_MODE),
220
+            (self.KEY_START_BOOST, heating_function, heating_function.KEY_START_BOOST),
221
+            (self.KEY_SET_DEFAULT_TEMPERATURE, heating_function, heating_function.KEY_SET_DEFAULT_TEMPERATURE),
222
+            (self.KEY_BOOST_TIMER, heating_function, heating_function.KEY_BOOST_TIMER),
223
+            default_values={
224
+                self.KEY_TEMPERATURE: heating_function[heating_function.KEY_TEMPERATURE_CURRENT],
225
+                self.KEY_VALVE_TEMPERATURE_SETPOINT: heating_function[heating_function.KEY_TEMPERATURE_SETPOINT],
226
+                self.KEY_USER_TEMPERATURE_SETPOINT: heating_function[heating_function.KEY_USER_TEMPERATURE_SETPOINT],
227
+                self.KEY_AWAY_MODE: heating_function[heating_function.KEY_AWAY_MODE],
228
+                self.KEY_SUMMER_MODE: heating_function[heating_function.KEY_SUMMER_MODE],
229
+                self.KEY_BOOST_TIMER: heating_function[heating_function.KEY_BOOST_TIMER],
230
+                self.KEY_START_BOOST: True,
231
+                self.KEY_SET_DEFAULT_TEMPERATURE: True,
232
+            }
233
+        )
234
+
235
+
236
+class videv_multistate(base):
237
+    def __init__(self, mqtt_client, topic, key, device, num_states, default_values=None):
238
+        dv = dict.fromkeys(["state_%d" % i for i in range(0, num_states)])
239
+        for key in dv:
240
+            dv[key] = False
241
+        super().__init__(mqtt_client, topic, (key, device), default_values=dv)
242
+        #
243
+        device.add_callback(key, None, self.__index_rx__, True)
244
+
245
+    def __index_rx__(self, device, key, data):
246
+        for index, key in enumerate(self):
247
+            self.set(key, index == data)
248
+            self.__tx__(key, self[key])

Loading…
Отказ
Запис