ソースを参照

rework of videv

tags/v1.0.0
Dirk Alders 1年前
コミット
891e3bd111
4個のファイルの変更162行の追加185行の削除
  1. 0
    4
      devices/__init__.py
  2. 3
    3
      function/modules.py
  3. 151
    175
      function/videv.py
  4. 8
    3
      smart_brain.py

+ 0
- 4
devices/__init__.py ファイルの表示

@@ -26,10 +26,6 @@ devices (DEVICES)
26 26
 
27 27
 """
28 28
 
29
-# TODO: usage of base.mqtt as parent for class base
30
-
31
-__DEPENDENCIES__ = []
32
-
33 29
 import json
34 30
 import logging
35 31
 import task

+ 3
- 3
function/modules.py ファイルの表示

@@ -272,7 +272,7 @@ class motion_sensor_light(common_base):
272 272
         #
273 273
         self.sw_device = sw_device
274 274
         self.sw_method = sw_method
275
-        self.args = args
275
+        self.motion_sensors = args
276 276
         self.timer_reload_value = timer_value
277 277
         #
278 278
         sw_device.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.reload_timer, True)
@@ -292,7 +292,7 @@ class motion_sensor_light(common_base):
292 292
         self.set(self.KEY_TIMER, 0)
293 293
 
294 294
     def set_motion_detected(self, device, key, data):
295
-        for sensor_index, arg_device in enumerate(self.args):
295
+        for sensor_index, arg_device in enumerate(self.motion_sensors):
296 296
             if arg_device.topic == device.topic:
297 297
                 break
298 298
         self.set(self.KEY_MOTION_SENSOR % sensor_index, data)
@@ -302,7 +302,7 @@ class motion_sensor_light(common_base):
302 302
                 self.sw_method(True)
303 303
 
304 304
     def motion_detected(self):
305
-        for i in range(0, len(self.args)):
305
+        for i in range(0, len(self.motion_sensors)):
306 306
             if self[self.KEY_MOTION_SENSOR % i]:
307 307
                 return True
308 308
         return False

+ 151
- 175
function/videv.py ファイルの表示

@@ -10,17 +10,10 @@ 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
-
17 13
 from base import mqtt_base
18 14
 import devices
19 15
 import json
20 16
 
21
-BASETOPIC = "videv"
22
-
23
-
24 17
 try:
25 18
     from config import APP_NAME as ROOT_LOGGER_NAME
26 19
 except ImportError:
@@ -28,174 +21,160 @@ except ImportError:
28 21
 
29 22
 
30 23
 class base(mqtt_base):
31
-    EXEC_RX_FUNC_ALWAYS = []
24
+    KEY_INFO = '__info__'
32 25
 
33
-    def __init__(self, mqtt_client, topic, *args, default_values=None):
26
+    def __init__(self, mqtt_client, topic, default_values=None):
34 27
         super().__init__(mqtt_client, topic, default_values=default_values)
35
-        self.__device_list__ = {}
36
-        for videv_key, device in [reduced[:2] for reduced in args]:
37
-            self.__device_list__[videv_key] = device
38
-        # send initial state
39
-        for key in self.keys():
40
-            self.__tx__(key, self[key])
41
-        # add receive topics
42
-        mqtt_client.add_callback(self.topic + "/#", self.__rx__)
28
+        self.__display_dict__ = {}
29
+        self.__control_dict__ = {}
30
+        self.__capabilities__ = None
31
+
32
+    def add_display(self, my_key, ext_device, ext_key, on_change_only=True):
33
+        """
34
+        listen to data changes of ext_device and update videv information
35
+        """
36
+        if ext_device.__class__.__name__ == "group":
37
+            # store information to identify callback from ext_device
38
+            self.__display_dict__[(id(ext_device[0]), ext_key)] = my_key
39
+            # register a callback to listen for data from external device
40
+            ext_device[0].add_callback(ext_key, None, self.__rx_ext_device_data__, on_change_only)
41
+        else:
42
+            # store information to identify callback from ext_device
43
+            self.__display_dict__[(id(ext_device), ext_key)] = my_key
44
+            # register a callback to listen for data from external device
45
+            ext_device.add_callback(ext_key, None, self.__rx_ext_device_data__, on_change_only)
46
+        # send default data to videv interface
47
+
48
+    def __rx_ext_device_data__(self, ext_device, ext_key, data):
49
+        self.__tx__(self.__display_dict__[(id(ext_device), ext_key)], data)
43 50
 
44 51
     def __tx__(self, key, data):
45 52
         if type(data) not in (str, ):
46 53
             data = json.dumps(data)
47
-        if key in self.keys():
48
-            self.mqtt_client.send(self.topic + '/' + key, data)
49
-        else:
50
-            self.logger.warning("Ignoring send request for key %s (not available for this class)", key)
51
-
52
-    def __rx__(self, client, userdata, message):
53
-        key = message.topic.split('/')[-1]
54
-        if key in self.keys():
54
+        self.mqtt_client.send(self.topic + '/' + key, data)
55
+        self.__tx_capabilities__()
56
+
57
+    def __tx_capabilities__(self):
58
+        self.mqtt_client.send(self.topic + '/' + self.KEY_INFO, json.dumps(self.capabilities))
59
+
60
+    def add_control(self, my_key, ext_device, ext_key, on_change_only=True):
61
+        """
62
+        listen to videv information and pass data to ext_device
63
+        """
64
+        self[my_key] = None
65
+        # store information to identify callback from videv
66
+        self.__control_dict__[my_key] = (ext_device, ext_key, on_change_only)
67
+        # add callback for videv changes
68
+        self.mqtt_client.add_callback(self.topic + '/' + my_key, self.__rx_videv_data__)
69
+
70
+    def __rx_videv_data__(self, client, userdata, message):
71
+        my_key = message.topic.split('/')[-1]
72
+        ext_device, ext_key, on_change_only = self.__control_dict__[my_key]
73
+        if my_key in self.keys():
55 74
             try:
56 75
                 data = json.loads(message.payload)
57 76
             except json.decoder.JSONDecodeError:
58 77
                 data = message.payload
59
-            if data != self[key] or key in self.EXEC_RX_FUNC_ALWAYS:
60
-                self.__rx_functionality__(key, data)
61
-            self.set(key, data)
78
+            if data != self[my_key] or not on_change_only:
79
+                ext_device.set(ext_key, data)
80
+            self.set(my_key, data)
62 81
         else:
63 82
             self.logger.info("Ignoring rx message with topic %s", message.topic)
64 83
 
65
-    def __rx_functionality__(self, key, data):
66
-        raise NotImplemented("Method __rx_functionality__ needs to be implemented in child class")
67
-
68
-    def __device_data__(self, device, key, data):
69
-        raise NotImplemented("Method __device_data__ needs to be implemented in child class")
70
-
71
-
72
-class base_routing(base):
73
-    def __init__(self, mqtt_client, topic, *args, default_values=None):
74
-        super().__init__(mqtt_client, topic, *args, default_values=default_values)
75
-        #
76
-        self.__device_key__ = {}
77
-        index = 0
78
-        for videv_key, device, device_key in args:
79
-            if self.__device_list__[videv_key] != device:
80
-                raise ReferenceError("Parent class generated a deviating device list")
81
-            self.__device_key__[videv_key] = device_key
82
-            index += 1
83
-        # add callbacks
84
-        for key in self.__device_list__:
85
-            if self.__device_list__[key].__class__.__name__ == "group":
86
-                self.__device_list__[key][0].add_callback(self.__device_key__[key], None, self.__device_data__, True)
87
-            else:
88
-                self.__device_list__[key].add_callback(self.__device_key__[key], None, self.__device_data__, True)
89
-
90
-    def __rx_functionality__(self, key, data):
91
-        try:
92
-            self.__device_list__[key].set(self.__device_key__[key], data)
93
-        except KeyError:
94
-            self.logger.warning("RX passthrough not possible for key %s", key)
95
-
96
-    def __device_data__(self, device, key, data):
97
-        l1 = []
98
-        for k, v in self.__device_list__.items():
99
-            if v.__class__.__name__ == "group":
100
-                if id(device) in [id(d) for d in v]:
101
-                    l1.append(k)
102
-            else:
103
-                if id(v) == id(device):
104
-                    l1.append(k)
105
-        l2 = [k for k, v in self.__device_key__.items() if v == key]
106
-        try:
107
-            videv_key = [k for k in l1 if k in l2][0]
108
-        except IndexError:
109
-            self.logger.warning("videv_key not available for %s::%s", device.__class__.__name__, device.topic)
110
-        else:
111
-            self.set(videv_key, data)
112
-            self.__tx__(videv_key, data)
113
-
114
-
115
-class videv_switching(base_routing):
84
+    def add_routing(self, my_key, ext_device, ext_key, on_change_only_disp=True, on_change_only_videv=True):
85
+        """
86
+        listen to data changes of ext_device and update videv information
87
+        and
88
+        listen to videv information and pass data to ext_device
89
+        """
90
+        # add display
91
+        self.add_display(my_key, ext_device, ext_key, on_change_only_disp)
92
+        self.add_control(my_key, ext_device, ext_key, on_change_only_videv)
93
+
94
+    @property
95
+    def capabilities(self):
96
+        if self.__capabilities__ is None:
97
+            self.__capabilities__ = {}
98
+            self.__capabilities__['__type__'] = self.__class__.__name__
99
+            for key in self.__control_dict__:
100
+                if not key in self.__capabilities__:
101
+                    self.__capabilities__[key] = {}
102
+                self.__capabilities__[key]['control'] = True
103
+            for key in self.__display_dict__.values():
104
+                if not key in self.__capabilities__:
105
+                    self.__capabilities__[key] = {}
106
+                self.__capabilities__[key]['display'] = True
107
+        return self.__capabilities__
108
+
109
+
110
+class videv_switching(base):
116 111
     KEY_STATE = 'state'
117
-    #
118
-    DEFAULT_VALUES = {
119
-        KEY_STATE: False,
120
-    }
121 112
 
122 113
     def __init__(self, mqtt_client, topic, sw_device, sw_key):
123
-        super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key))
114
+        super().__init__(mqtt_client, topic)
115
+        self.add_routing(self.KEY_STATE, sw_device, sw_key)
116
+        #
117
+        self.__tx_capabilities__()
124 118
 
125 119
 
126
-class videv_switching_timer(base_routing):
120
+class videv_switching_timer(base):
127 121
     KEY_STATE = 'state'
128 122
     KEY_TIMER = 'timer'
129
-    #
130
-    DEFAULT_VALUES = {
131
-        KEY_STATE: False,
132
-        KEY_TIMER: 0
133
-    }
134 123
 
135 124
     def __init__(self, mqtt_client, topic, sw_device, sw_key, tm_device, tm_key):
136
-        super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_TIMER, tm_device, tm_key))
125
+        super().__init__(mqtt_client, topic)
126
+        self.add_routing(self.KEY_STATE, sw_device, sw_key)
127
+        self.add_display(self.KEY_TIMER, tm_device, tm_key)
128
+        #
129
+        self.__tx_capabilities__()
137 130
 
138 131
 
139
-class videv_switching_motion(base_routing):
132
+class videv_switching_motion(base):
140 133
     KEY_STATE = 'state'
134
+    #
141 135
     KEY_TIMER = 'timer'
142 136
     KEY_MOTION_SENSOR = 'motion_%d'
143
-    #
144
-    DEFAULT_VALUES = {
145
-        KEY_STATE: False,
146
-        KEY_TIMER: 0
147
-    }
148 137
 
149 138
     def __init__(self, mqtt_client, topic, sw_device, sw_key, motion_function):
150
-        dv = {self.KEY_STATE: False, self.KEY_TIMER: 0}
151
-        for i in range(0, len(motion_function.args)):
152
-            dv[motion_function.KEY_MOTION_SENSOR % i] = False
153
-        super().__init__(
154
-            mqtt_client, topic,
155
-            (self.KEY_STATE, sw_device, sw_key),
156
-            (self.KEY_TIMER, motion_function, motion_function.KEY_TIMER),
157
-            *[[self.KEY_MOTION_SENSOR % i, motion_function, motion_function.KEY_MOTION_SENSOR % i] for i in range(0, len(motion_function.args))],
158
-            default_values=dv
159
-        )
160
-
161
-
162
-class videv_switch_brightness(base_routing):
139
+        self.motion_sensors = motion_function.motion_sensors
140
+        #
141
+        super().__init__(mqtt_client, topic)
142
+        self.add_routing(self.KEY_STATE, sw_device, sw_key)
143
+        self.add_display(self.KEY_TIMER, motion_function, motion_function.KEY_TIMER)
144
+        # motion sensor state
145
+        for index, motion_sensor in enumerate(self.motion_sensors):
146
+            self.add_display(self.KEY_MOTION_SENSOR % index, motion_sensor, motion_sensor.KEY_OCCUPANCY)
147
+        #
148
+        self.__tx_capabilities__()
149
+
150
+
151
+class videv_switch_brightness(base):
163 152
     KEY_STATE = 'state'
164 153
     KEY_BRIGHTNESS = 'brightness'
165
-    #
166
-    DEFAULT_VALUES = {
167
-        KEY_STATE: False,
168
-        KEY_BRIGHTNESS: 0
169
-    }
170 154
 
171 155
     def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key):
156
+        super().__init__(mqtt_client, topic)
157
+        self.add_routing(self.KEY_STATE, sw_device, sw_key)
158
+        self.add_routing(self.KEY_BRIGHTNESS, br_device, br_key)
172 159
         #
173
-        super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_BRIGHTNESS, br_device, br_key))
160
+        self.__tx_capabilities__()
174 161
 
175 162
 
176
-class videv_switch_brightness_color_temp(base_routing):
163
+class videv_switch_brightness_color_temp(base):
177 164
     KEY_STATE = 'state'
178 165
     KEY_BRIGHTNESS = 'brightness'
179 166
     KEY_COLOR_TEMP = 'color_temp'
180
-    #
181
-    DEFAULT_VALUES = {
182
-        KEY_STATE: False,
183
-        KEY_BRIGHTNESS: 0,
184
-        KEY_COLOR_TEMP: 0,
185
-    }
186 167
 
187 168
     def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key, ct_device, ct_key):
169
+        super().__init__(mqtt_client, topic)
170
+        self.add_routing(self.KEY_STATE, sw_device, sw_key)
171
+        self.add_routing(self.KEY_BRIGHTNESS, br_device, br_key)
172
+        self.add_routing(self.KEY_COLOR_TEMP, ct_device, ct_key)
188 173
         #
189
-        super().__init__(
190
-            mqtt_client, topic,
191
-            (self.KEY_STATE, sw_device, sw_key),
192
-            (self.KEY_BRIGHTNESS, br_device, br_key),
193
-            (self.KEY_COLOR_TEMP, ct_device, ct_key)
194
-        )
174
+        self.__tx_capabilities__()
195 175
 
196 176
 
197
-class videv_heating(base_routing):
198
-    KEY_TEMPERATURE = 'temperature'
177
+class videv_heating(base):
199 178
     KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint'
200 179
     KEY_VALVE_TEMPERATURE_SETPOINT = 'valve_temperature_setpoint'
201 180
     KEY_AWAY_MODE = 'away_mode'
@@ -204,69 +183,66 @@ class videv_heating(base_routing):
204 183
     KEY_SET_DEFAULT_TEMPERATURE = 'set_default_temperature'
205 184
     KEY_BOOST_TIMER = 'boost_timer'
206 185
     #
207
-    EXEC_RX_FUNC_ALWAYS = [KEY_START_BOOST, KEY_SET_DEFAULT_TEMPERATURE, KEY_USER_TEMPERATURE_SETPOINT]
186
+    KEY_TEMPERATURE = 'temperature'
208 187
 
209 188
     def __init__(self, mqtt_client, topic, heating_function):
189
+        super().__init__(mqtt_client, topic)
190
+        #
191
+        self.add_routing(self.KEY_USER_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_USER_TEMPERATURE_SETPOINT)
192
+        self.add_routing(self.KEY_AWAY_MODE, heating_function, heating_function.KEY_AWAY_MODE)
193
+        self.add_routing(self.KEY_SUMMER_MODE, heating_function, heating_function.KEY_SUMMER_MODE)
210 194
         #
211
-        super().__init__(
212
-            mqtt_client, topic,
213
-            (self.KEY_TEMPERATURE, heating_function, heating_function.KEY_TEMPERATURE_CURRENT),
214
-            (self.KEY_USER_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_USER_TEMPERATURE_SETPOINT),
215
-            (self.KEY_VALVE_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_TEMPERATURE_SETPOINT),
216
-            (self.KEY_AWAY_MODE, heating_function, heating_function.KEY_AWAY_MODE),
217
-            (self.KEY_SUMMER_MODE, heating_function, heating_function.KEY_SUMMER_MODE),
218
-            (self.KEY_START_BOOST, heating_function, heating_function.KEY_START_BOOST),
219
-            (self.KEY_SET_DEFAULT_TEMPERATURE, heating_function, heating_function.KEY_SET_DEFAULT_TEMPERATURE),
220
-            (self.KEY_BOOST_TIMER, heating_function, heating_function.KEY_BOOST_TIMER),
221
-            default_values={
222
-                self.KEY_TEMPERATURE: heating_function[heating_function.KEY_TEMPERATURE_CURRENT],
223
-                self.KEY_VALVE_TEMPERATURE_SETPOINT: heating_function[heating_function.KEY_TEMPERATURE_SETPOINT],
224
-                self.KEY_USER_TEMPERATURE_SETPOINT: heating_function[heating_function.KEY_USER_TEMPERATURE_SETPOINT],
225
-                self.KEY_AWAY_MODE: heating_function[heating_function.KEY_AWAY_MODE],
226
-                self.KEY_SUMMER_MODE: heating_function[heating_function.KEY_SUMMER_MODE],
227
-                self.KEY_BOOST_TIMER: heating_function[heating_function.KEY_BOOST_TIMER],
228
-                self.KEY_START_BOOST: True,
229
-                self.KEY_SET_DEFAULT_TEMPERATURE: True,
230
-            }
231
-        )
195
+        self.add_control(self.KEY_START_BOOST, heating_function, heating_function.KEY_START_BOOST, False)
196
+        self.add_control(self.KEY_SET_DEFAULT_TEMPERATURE, heating_function, heating_function.KEY_SET_DEFAULT_TEMPERATURE, False)
197
+        #
198
+        self.add_display(self.KEY_VALVE_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_TEMPERATURE_SETPOINT)
199
+        self.add_display(self.KEY_BOOST_TIMER, heating_function, heating_function.KEY_BOOST_TIMER)
200
+        self.add_display(self.KEY_TEMPERATURE, heating_function, heating_function.KEY_TEMPERATURE_CURRENT)
201
+        #
202
+        self.__tx_capabilities__()
232 203
 
233 204
 
234 205
 class videv_multistate(base):
206
+    KEY_STATE = 'state_%d'
207
+
235 208
     def __init__(self, mqtt_client, topic, key_for_device, device, num_states, default_values=None):
236
-        dv = dict.fromkeys(["state_%d" % i for i in range(0, num_states)])
237
-        for key in dv:
238
-            dv[key] = False
239
-        super().__init__(mqtt_client, topic, (key_for_device, device), default_values=dv)
209
+        super().__init__(mqtt_client, topic)
210
+        self.num_states = num_states
211
+        # send default values
212
+        for i in range(0, num_states):
213
+            self.__tx__(self.KEY_STATE % i, False)
240 214
         #
241 215
         device.add_callback(key_for_device, None, self.__index_rx__, True)
216
+        #
217
+        self.__tx_capabilities__()
242 218
 
243 219
     def __index_rx__(self, device, key, data):
244
-        for index, key in enumerate(self):
245
-            self.set(key, index == data)
246
-            self.__tx__(key, self[key])
247
-
248
-    def __rx_functionality__(self, key, data):
249
-        pass    # read only device
220
+        for i in range(0, self.num_states):
221
+            self.__tx__(self.KEY_STATE % i, i == data)
222
+        #
223
+        self.__tx_capabilities__()
250 224
 
251 225
 
252
-class videv_audio_player(base_routing):
226
+class videv_audio_player(base):
253 227
     KEY_ACTIVE_PLAYER = 'player_%d'
254 228
     KEY_TITLE = 'title'
255 229
     NO_TITLE = '---'
256 230
 
257 231
     def __init__(self, mqtt_client, topic, *args):
258
-        dv = dict.fromkeys([self.KEY_ACTIVE_PLAYER % i for i in range(0, len(args))])
259
-        for key in dv:
260
-            dv[key] = False
261
-        dv[self.KEY_TITLE] = self.NO_TITLE
262
-        super().__init__(
263
-            mqtt_client, topic,
264
-            *[[self.KEY_ACTIVE_PLAYER % i, device, devices.audio_status.KEY_STATE] for i, device in enumerate(args)],
265
-            default_values=dv
266
-        )
232
+        super().__init__(mqtt_client, topic)
233
+        for i, device in enumerate(args):
234
+            self.add_display(self.KEY_ACTIVE_PLAYER % i, device, device.KEY_STATE)
235
+        #
267 236
         for audio_device in args:
268 237
             audio_device.add_callback(audio_device.KEY_TITLE, None, self.__title_rx__, True)
238
+        #
239
+        self.__tx_capabilities__()
269 240
 
270 241
     def __title_rx__(self, device, key, data):
271
-        self.set(self.KEY_TITLE, data or self.NO_TITLE)
272
-        self.__tx__(self.KEY_TITLE, self[self.KEY_TITLE])
242
+        self.__tx__(self.KEY_TITLE, data or self.NO_TITLE)
243
+
244
+    @property
245
+    def capabilities(self):
246
+        super().capabilities
247
+        self.__capabilities__[self.KEY_TITLE] = {'display': True}
248
+        return self.__capabilities__

+ 8
- 3
smart_brain.py ファイルの表示

@@ -7,6 +7,14 @@ import time
7 7
 
8 8
 logger = logging.getLogger(config.APP_NAME)
9 9
 
10
+# TODO: Change Nodered topics to videv
11
+# TODO: Extend virtual devices
12
+#         * All Off
13
+#         * ...
14
+# TODO: Remove gui from rooms and devices
15
+# TODO: Rework devices to base.mqtt (pack -> set, ...)
16
+# TODO: Implement handling of warnings (videv element to show in webapp?)
17
+
10 18
 
11 19
 if __name__ == "__main__":
12 20
     if config.DEBUG:
@@ -19,8 +27,5 @@ if __name__ == "__main__":
19 27
 
20 28
     func = function.all_functions(mc)
21 29
 
22
-    # for device in func.devicelist():
23
-    #     device.add_warning_callback(None)
24
-
25 30
     while (True):
26 31
         time.sleep(1)

読み込み中…
キャンセル
保存