From 33042e3f3c86df80859ae7e960a509f2d1c43fe3 Mon Sep 17 00:00:00 2001 From: Dirk Alders Date: Sat, 16 Dec 2023 07:12:29 +0100 Subject: [PATCH] Zigbee heating valve follow check improved, battery check added --- check_z_heat_vlv_battery | 1 + check_z_heat_vlv => check_z_heat_vlv_follow | 13 +++-- z_server/devices/__init__.py | 54 +++++++++++++++------ z_server/z_protocol.py | 6 ++- 4 files changed, 54 insertions(+), 20 deletions(-) create mode 120000 check_z_heat_vlv_battery rename check_z_heat_vlv => check_z_heat_vlv_follow (58%) diff --git a/check_z_heat_vlv_battery b/check_z_heat_vlv_battery new file mode 120000 index 0000000..06aac91 --- /dev/null +++ b/check_z_heat_vlv_battery @@ -0,0 +1 @@ +check_z_heat_vlv_follow \ No newline at end of file diff --git a/check_z_heat_vlv b/check_z_heat_vlv_follow similarity index 58% rename from check_z_heat_vlv rename to check_z_heat_vlv_follow index 3e4ae53..04f0d07 100755 --- a/check_z_heat_vlv +++ b/check_z_heat_vlv_follow @@ -7,7 +7,7 @@ import time from z_server import config from z_server import tcp_socket from z_server.z_protocol import server as client_prot -from z_server.z_protocol import DID_FOLLOWS_HEATING_SETPOINT +from z_server.z_protocol import DID_FOLLOWS_HEATING_SETPOINT, DID_BATTERY_LEVEL from z_server import socket_protocol import sys @@ -30,7 +30,14 @@ if __name__ == '__main__': "roo": args.roo, "fun": "FUN_HEA" # <-- Const, because script is for heat_vlv only } - sp.send(socket_protocol.SID_READ_REQUEST, DID_FOLLOWS_HEATING_SETPOINT, data) # - sp_data = sp.receive(socket_protocol.SID_READ_RESPONSE, DID_FOLLOWS_HEATING_SETPOINT).get_data() + if sys.argv[0].endswith('check_z_heat_vlv_follow'): + sp.send(socket_protocol.SID_READ_REQUEST, DID_FOLLOWS_HEATING_SETPOINT, data) + sp_data = sp.receive(socket_protocol.SID_READ_RESPONSE, DID_FOLLOWS_HEATING_SETPOINT).get_data() + elif sys.argv[0].endswith('check_z_heat_vlv_battery'): + sp.send(socket_protocol.SID_READ_REQUEST, DID_BATTERY_LEVEL, data) + sp_data = sp.receive(socket_protocol.SID_READ_RESPONSE, DID_BATTERY_LEVEL).get_data() + else: + sys.stderr.write('No action for command called "%s"\n' % sys.argv[0]) + sys.exit(100) nagios.Nagios().exit(**sp_data) diff --git a/z_server/devices/__init__.py b/z_server/devices/__init__.py index 22c6179..5c91314 100644 --- a/z_server/devices/__init__.py +++ b/z_server/devices/__init__.py @@ -14,6 +14,10 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) class base(object): FOLLOW_REQUEST_WARNING = 5 # Seconds, till warning comes up, if device does not follow the command FOLLOW_REQUEST_ERROR = 60 # Seconds, till error comes up, if device does not follow the command + FOLLOW_KEYS = ["current_heating_setpoint", ] + # + BATTERY_LVL_WARNING = 10 + BATTERY_LVL_ERROR = 5 def __init__(self, mqtt_client: mqtt.mqtt_client, topic): self.topic = topic @@ -23,6 +27,8 @@ class base(object): # self.__target_storage__ = {} self.__state_storage__ = {} + # + self.battery = None def __rx__(self, client, userdata, message): pass @@ -38,21 +44,37 @@ class base(object): logger.debug("Device state identified: %s: %s", key, repr(value)) def status(self, key): - tm_s, value_s = self.__state_storage__.get(key, (0, None)) - try: - tm_t, value_t = self.__target_storage__[key] - except KeyError: - if value_s is not None: - return {"status": nagios.Nagios.WARNING, "msg": "Current temperature setpoint %.1f°C (age=%.1fs), but never received a setpoint" % (value_s, time.time()-tm_s)} - return {"status": nagios.Nagios.UNKNOWN, "msg": "Device exists, but no data received or unknown monitoring"} - else: - tm = time.time() - dt = tm - tm_t - if value_t != value_s and dt > self.FOLLOW_REQUEST_ERROR: - return {"status": nagios.Nagios.ERROR, "msg": "Requested setpoint unequal valve setpoint since %.1fs" % dt} - elif value_t != value_s and dt > self.FOLLOW_REQUEST_WARNING: - return {"status": nagios.Nagios.WARNING, "msg": "Requested setpoint unequal valve setpoint since %.1fs" % dt} - return {"status": nagios.Nagios.OK, "msg": "Requested setpoint equal valve setpoint"} + # + # FOLLOW SETPOINT + # + if key in self.FOLLOW_KEYS: + tm_s, value_s = self.__state_storage__.get(key, (0, None)) + try: + tm_t, value_t = self.__target_storage__[key] + except KeyError: + if value_s is not None: + return {"status": nagios.Nagios.WARNING, "msg": "Current temperature setpoint %.1f°C (age=%.1fs), but never received a setpoint" % (value_s, time.time()-tm_s)} + return {"status": nagios.Nagios.UNKNOWN, "msg": "Device exists, but no data received or unknown monitoring"} + else: + tm = time.time() + dt = tm - tm_t + if value_t != value_s and dt > self.FOLLOW_REQUEST_ERROR: + return {"status": nagios.Nagios.ERROR, "msg": "Requested setpoint unequal valve setpoint since %.1f°C (age=%.1fs)" % (value_s, time.time()-tm_s)} + elif value_t != value_s and dt > self.FOLLOW_REQUEST_WARNING: + return {"status": nagios.Nagios.WARNING, "msg": "Requested setpoint unequal valve setpoint since %.1f°C (age=%.1fs)" % (value_s, time.time()-tm_s)} + return {"status": nagios.Nagios.OK, "msg": "Requested setpoint equal valve setpoint %.1f°C (age=%.1fs)" % (value_s, time.time()-tm_s)} + # + # BATTERY + # + elif key == "battery": + if self.battery is None: + return {"status": nagios.Nagios.UNKNOWN, "msg": "Device exists, but no data received or unknown monitoring"} + elif self.battery < self.BATTERY_LVL_ERROR: + return {"status": nagios.Nagios.ERROR, "msg": "Battery level critical low (%.1f%%)" % self.battery} + elif self.battery < self.BATTERY_LVL_WARNING: + return {"status": nagios.Nagios.WARNING, "msg": "Battery level low (%.1f%%)" % self.battery} + else: + return {"status": nagios.Nagios.OK, "msg": "Battery okay (%.1f%%)" % self.battery} class group(object): @@ -95,6 +117,8 @@ class brennenstuhl_heatingvalve(base): self.target("current_heating_setpoint", payload["current_heating_setpoint"]) if message.topic == self.topic: self.state("current_heating_setpoint", payload["current_heating_setpoint"]) + if "battery" in payload and message.topic == self.topic: + self.battery = payload["battery"] class silvercrest_powerplug(base): diff --git a/z_server/z_protocol.py b/z_server/z_protocol.py index d4f8195..37b5cd4 100644 --- a/z_server/z_protocol.py +++ b/z_server/z_protocol.py @@ -3,6 +3,7 @@ import socket_protocol # from devdi.topic import topic_by_props DID_FOLLOWS_HEATING_SETPOINT = 'current_heating_setpoint' +DID_BATTERY_LEVEL = 'battery' class server(socket_protocol.pure_json_protocol): @@ -15,9 +16,10 @@ class server(socket_protocol.pure_json_protocol): # DID_FOLLOWS_HEATING_SETPOINT, 'current_heating_setpoint') # if not self.__comm_inst__.IS_CLIENT: - self.register_callback(socket_protocol.SID_READ_REQUEST, DID_FOLLOWS_HEATING_SETPOINT, self.follow_heating_setpoint) + self.register_callback(socket_protocol.SID_READ_REQUEST, DID_FOLLOWS_HEATING_SETPOINT, self.device_status) + self.register_callback(socket_protocol.SID_READ_REQUEST, DID_BATTERY_LEVEL, self.device_status) - def follow_heating_setpoint(self, msg): + def device_status(self, msg): if msg.get_status() == socket_protocol.STATUS_OKAY: try: dev = self.__devices__.get_str(**msg.get_data())