From 7b3c7198be81cbe6b68ff48485c75dbf8ba5a56d Mon Sep 17 00:00:00 2001 From: Dirk Alders Date: Fri, 10 Feb 2023 14:34:49 +0100 Subject: [PATCH] Warning collector implemented --- devices/__init__.py | 49 +++++++++++++++++++++++++++++++++++++------- function/__init__.py | 7 +++++-- function/rooms.py | 15 ++++++++++++++ function/videv.py | 14 +++++++++++++ 4 files changed, 76 insertions(+), 9 deletions(-) diff --git a/devices/__init__.py b/devices/__init__.py index 5c4e62f..741636c 100644 --- a/devices/__init__.py +++ b/devices/__init__.py @@ -30,7 +30,9 @@ from base import mqtt_base from function.videv import base as videv_base import json import logging +import math import task +import time try: from config import APP_NAME as ROOT_LOGGER_NAME @@ -40,6 +42,27 @@ except ImportError: BATTERY_WARN_LEVEL = 5 +class warning(dict): + TYPE_BATTERY_LOW = 1 + TYPE_OVERTEMPERATURE = 2 + # + KEY_ID = 'id' + KEY_TYPE = 'type' + KEY_TEXT = 'text' + KEY_TM = 'tm' + + def __init__(self, identification, type, text, args): + super().__init__({ + self.KEY_ID: identification, + self.KEY_TYPE: type, + self.KEY_TEXT: text % args, + self.KEY_TM: time.localtime(), + }) + + def __str__(self): + return time.asctime(self.get(self.KEY_TM)) + ": " + self[self.KEY_TEXT] + " - " + self[self.KEY_ID] + + def is_json(data): try: json.loads(data) @@ -110,7 +133,7 @@ class base(mqtt_base): KEY_WARNING = '__WARNING__' def __init__(self, mqtt_client, topic): - super().__init__(mqtt_client, topic, default_values=dict.fromkeys(self.RX_KEYS)) + super().__init__(mqtt_client, topic, default_values=dict.fromkeys(self.RX_KEYS + [self.KEY_WARNING])) # data storage # initialisations mqtt_client.add_callback(topic=self.topic, callback=self.receive_callback) @@ -119,7 +142,7 @@ class base(mqtt_base): def set(self, key, data, block_callback=[]): if key in self.RX_IGNORE_KEYS: pass # ignore these keys - elif key in self.RX_KEYS: + elif key in self.RX_KEYS or key == self.KEY_WARNING: return super().set(key, data, block_callback) else: self.logger.warning("Unexpected key %s", key) @@ -230,7 +253,9 @@ class shelly(base): # WARNING CALL # def __warning__(self, client, key, data): - pass # TODO: implement warning feedback (key: KEY_OVERTEMPERATURE - info: KEY_TEMPERATURE) + w = warning(self.topic, warning.TYPE_OVERTEMPERATURE, "Temperature to high (%.1f°C)", self.get(self.KEY_TEMPERATURE, math.nan)) + self.logger.warning(w) + self.set(self.KEY_WARNING, w) # # RX @@ -382,7 +407,9 @@ class silvercrest_motion_sensor(base): # WARNING CALL # def __warning__(self, client, key, data): - pass # TODO: implement warning feedback (key: KEY_BATTERY_LOW - info: KEY_BATTERY) + w = warning(self.topic, warning.TYPE_BATTERY_LOW, "Battery low (%.1f%%)", self.get(self.KEY_BATTERY, math.nan)) + self.logger.warning(w) + self.set(self.KEY_WARNING, w) # # RX @@ -392,6 +419,11 @@ class silvercrest_motion_sensor(base): """rv: numeric value""" return self.get(self.KEY_LINKQUALITY) + @property + def battery(self): + """rv: numeric value""" + return self.get(self.KEY_BATTERY) + class my_powerplug(base): KEY_OUTPUT_0 = "output/1" @@ -637,8 +669,9 @@ class tradfri_button(base): def __warning__(self, client, key, data): if data <= BATTERY_WARN_LEVEL: if not self.__battery_warning__: - self.__battery_warning__ = True - pass # TODO: implement warning feedback (key: KEY_BATTERY_LOW - info: KEY_BATTERY) + w = warning(self.topic, warning.TYPE_BATTERY_LOW, "Battery low (%.1f%%)", data) + self.logger.warning(w) + self.set(self.KEY_WARNING, w) else: self.__battery_warning__ = False @@ -684,7 +717,9 @@ class brennenstuhl_heatingvalve(base): if data <= BATTERY_WARN_LEVEL: if not self.__battery_warning__: self.__battery_warning__ = True - pass # TODO: implement warning feedback (key: KEY_BATTERY_LOW - info: KEY_BATTERY) + w = warning(self.topic, warning.TYPE_BATTERY_LOW, "Battery low (%.1f%%)", data) + self.logger.warning(w) + self.set(self.KEY_WARNING, w) else: self.__battery_warning__ = False diff --git a/function/__init__.py b/function/__init__.py index f224ed6..f0c9cad 100644 --- a/function/__init__.py +++ b/function/__init__.py @@ -8,8 +8,7 @@ from function.ground_floor_west import ground_floor_west from function.first_floor_west import first_floor_west from function.first_floor_east import first_floor_east from function.rooms import room_collection -from function.videv import all_off -import inspect +from function.videv import all_off, videv_warnings import logging try: @@ -40,6 +39,10 @@ class all_functions(room_collection): self.init_cross_room_interactions() # Off Buttons self.init_off_functionality() + # Warnings + videv_warning = videv_warnings(self.mqtt_client, config.TOPIC_WARNINGS) + for device in self.all_devices(): + device.add_callback(devices.base.KEY_WARNING, None, videv_warning.warningcollector) def init_cross_room_interactions(self): # shelly dirk input 1 diff --git a/function/rooms.py b/function/rooms.py index 24affe0..2d2f9c6 100644 --- a/function/rooms.py +++ b/function/rooms.py @@ -40,3 +40,18 @@ class room_collection(object): sub = getattr(self, sub_name) if sub.__class__.__bases__[0].__name__ in self.ALLOWED_CLASSES: sub.all_off() + + def all_devices(self, object_to_analyse=None): + target = object_to_analyse or self + # + devices = [] + for name, obj in inspect.getmembers(target): + if not callable(obj): # sort out methods + try: + if obj.__module__.startswith('function.'): + devices.extend(self.all_devices(obj)) # rekurse in function instances + elif obj.__module__ == "devices": + devices.append(obj) + except AttributeError: + pass # sort out non modules + return devices diff --git a/function/videv.py b/function/videv.py index a67140b..aeecfd7 100644 --- a/function/videv.py +++ b/function/videv.py @@ -260,6 +260,20 @@ class videv_audio_player(base): return self.__capabilities__ +class videv_warnings(base): + MAX_WARNINGS = 10 + KEY_WARNING = 'text' + + def __init__(self, mqtt_client, topic, default_values=None): + super().__init__(mqtt_client, topic, default_values) + self.__warnings__ = [] + + def warningcollector(self, client, key, data): + self.__warnings__.append(data) + self.__warnings__ = self.__warnings__[-self.MAX_WARNINGS:] + self.__tx__(self.KEY_WARNING, '\n'.join([str(w) for w in self.__warnings__])) + + class all_off(base): ALLOWED_CLASSES = (room, room_collection, )