diff --git a/check_z_actor b/check_z_actor new file mode 120000 index 0000000..e32a613 --- /dev/null +++ b/check_z_actor @@ -0,0 +1 @@ +check_z_heartbeat \ No newline at end of file diff --git a/check_z_heartbeat b/check_z_heartbeat index 8112b16..e7aa6f6 100755 --- a/check_z_heartbeat +++ b/check_z_heartbeat @@ -3,11 +3,12 @@ import argparse import nagios +import os 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_SETPOINT, DID_BATTERY_LEVEL, DID_HEARTBEAT, DID_LINKQUALITY +from z_server.z_protocol import DIDS from z_server import socket_protocol import sys @@ -32,19 +33,14 @@ if __name__ == '__main__': "fun": args.fun } # - if sys.argv[0].endswith('check_z_heartbeat'): - sp.send(socket_protocol.SID_READ_REQUEST, DID_HEARTBEAT, data) - sp_data = sp.receive(socket_protocol.SID_READ_RESPONSE, DID_HEARTBEAT).get_data() - elif sys.argv[0].endswith('check_z_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() - elif sys.argv[0].endswith('check_z_linkquality'): - sp.send(socket_protocol.SID_READ_REQUEST, DID_LINKQUALITY, data) - sp_data = sp.receive(socket_protocol.SID_READ_RESPONSE, DID_LINKQUALITY).get_data() - elif sys.argv[0].endswith('check_z_follow'): - sp.send(socket_protocol.SID_READ_REQUEST, DID_FOLLOWS_SETPOINT, data) - sp_data = sp.receive(socket_protocol.SID_READ_RESPONSE, DID_FOLLOWS_SETPOINT).get_data() - else: - sys.stderr.write('No action for command called "%s"\n' % sys.argv[0]) + + did = os.path.basename(sys.argv[0])[8:] + # + if did not in DIDS: + print("Unknown data id %s. You might add it in z_server.z_protocol." % did) sys.exit(100) - nagios.Nagios().exit(**sp_data) + else: + sp.send(socket_protocol.SID_READ_REQUEST, did, data) + sp_data = sp.receive(socket_protocol.SID_READ_RESPONSE, did).get_data() or { + "status": 101, "msg": "No answer from device for \"%s\". You might want to add it in z_server.devices" % did} + nagios.Nagios().exit(**sp_data) diff --git a/check_z_linkquality b/check_z_linkquality index 9d4fee9..e32a613 120000 --- a/check_z_linkquality +++ b/check_z_linkquality @@ -1 +1 @@ -check_z_follow \ No newline at end of file +check_z_heartbeat \ No newline at end of file diff --git a/z_server/devices/__init__.py b/z_server/devices/__init__.py index 1709336..a4483c4 100644 --- a/z_server/devices/__init__.py +++ b/z_server/devices/__init__.py @@ -3,6 +3,7 @@ import logging import mqtt import nagios import time +from z_protocol import DID_ACTOR, DID_BATTERY_LEVEL, DID_FOLLOWS_SETPOINT, DID_HEARTBEAT, DID_LINKQUALITY try: from config import APP_NAME as ROOT_LOGGER_NAME @@ -12,14 +13,13 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) class base(object): - MONITORING_HEARTBEAT = "heartbeat" - MONITORING_BATTERY = "battery" - MONITORING_FOLLOW_SETPOINT = "follow_setpoint" - MONITORING_LINKQUALITY = "linkquality" + KEY_BATTERY = 'battery' + KEY_CURRENT_VALUE = None + KEY_LINKQUALITY = 'linkquality' + KEY_SETPOINT = None # 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_KEY = None # BATTERY_LVL_WARNING = 15 BATTERY_LVL_ERROR = 5 @@ -42,9 +42,6 @@ class base(object): # self.__target_storage__ = {} self.__state_storage__ = {} - # - self.battery = None - self.linkquality = None def __rx__(self, client, userdata, message): try: @@ -54,28 +51,22 @@ class base(object): else: if type(payload) is dict: # + # Device values + # + if message.topic == self.topic: + for key in [self.KEY_BATTERY, self.KEY_CURRENT_VALUE, self.KEY_LINKQUALITY, self.KEY_SETPOINT]: + if key in payload: + self.state(key, payload[key]) + # + # Device setpoint + # + if message.topic == self.topic + '/set' and self.KEY_SETPOINT in payload: + self.target(self.KEY_SETPOINT, payload[self.KEY_SETPOINT]) + # # heartbeat # if message.topic == self.topic: self.last_device_msg = time.time() - # - # follow setpoint - # - if self.FOLLOW_KEY is not None and self.FOLLOW_KEY in payload: - if message.topic == self.topic + '/set': - self.target(self.FOLLOW_KEY, payload[self.FOLLOW_KEY]) - if message.topic == self.topic: - self.state(self.FOLLOW_KEY, payload[self.FOLLOW_KEY]) - # - # battery level - # - if self.MONITORING_BATTERY in payload and message.topic == self.topic: - self.battery = payload[self.MONITORING_BATTERY] - # - # linkquality - # - if self.MONITORING_LINKQUALITY in payload and message.topic == self.topic: - self.linkquality = payload[self.MONITORING_LINKQUALITY] def target(self, key, value): tm_t, value_t = self.__target_storage__.get(key, (0, None)) @@ -89,74 +80,81 @@ class base(object): def status(self, key): # + # ACTOR + # + if key == DID_ACTOR: + return self.__nagios_return__(DID_ACTOR, nagios.Nagios.WARNING, "Not available for this device") + # # HEARTBEAT # - if key == self.MONITORING_HEARTBEAT: + elif key == DID_HEARTBEAT: if self.last_device_msg is None: - return self.__nagios_return__(self.MONITORING_HEARTBEAT, nagios.Nagios.UNKNOWN, "Device exists, but no data received") + return self.__nagios_return__(DID_HEARTBEAT, nagios.Nagios.UNKNOWN, "Device exists, but no data received") else: dt = time.time() - self.last_device_msg dt_disp = dt / 60 / 60 if dt > self.LAST_MSG_ERROR: - return self.__nagios_return__(self.MONITORING_HEARTBEAT, nagios.Nagios.ERROR, "Last message %.1fh ago" % dt_disp) + return self.__nagios_return__(DID_HEARTBEAT, nagios.Nagios.ERROR, "Last message %.1fh ago" % dt_disp) elif dt > self.LAST_MSG_WARNING: - return self.__nagios_return__(self.MONITORING_HEARTBEAT, nagios.Nagios.WARNING, "Last message %.1fh ago" % dt_disp) + return self.__nagios_return__(DID_HEARTBEAT, nagios.Nagios.WARNING, "Last message %.1fh ago" % dt_disp) else: - return self.__nagios_return__(self.MONITORING_HEARTBEAT, nagios.Nagios.OK, "Last message %.1fh ago" % dt_disp) + return self.__nagios_return__(DID_HEARTBEAT, nagios.Nagios.OK, "Last message %.1fh ago" % dt_disp) # # FOLLOW SETPOINT # - elif key == self.MONITORING_FOLLOW_SETPOINT: - if self.FOLLOW_KEY is None: - return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.UNKNOWN, "Device exist, but does not follow any setpoint.", force=True) - tm_s, value_s = self.__state_storage__.get(self.FOLLOW_KEY, (0, None)) + elif key == DID_FOLLOWS_SETPOINT: + if self.KEY_SETPOINT is None: + return self.__nagios_return__(DID_FOLLOWS_SETPOINT, nagios.Nagios.UNKNOWN, "Device exist, but does not follow any setpoint.", force=True) + tm_s, value_s = self.__state_storage__.get(self.KEY_SETPOINT, (0, None)) try: - tm_t, value_t = self.__target_storage__[self.FOLLOW_KEY] + tm_t, value_t = self.__target_storage__[self.KEY_SETPOINT] except KeyError: if value_s is not None: - return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.OK, "Current temperature setpoint %.1f°C, but never received a setpoint. That might be okay." % value_s) - return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.UNKNOWN, "Device exists, but no data received") + return self.__nagios_return__(DID_FOLLOWS_SETPOINT, nagios.Nagios.OK, "Current temperature setpoint %.1f°C, but never received a setpoint. That might be okay." % value_s) + return self.__nagios_return__(DID_FOLLOWS_SETPOINT, nagios.Nagios.UNKNOWN, "Device exists, but no data received") else: tm = time.time() dt = tm - tm_t if value_t != value_s and dt > self.FOLLOW_REQUEST_ERROR: - return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.ERROR, "Requested setpoint %.1f°C unequal valve setpoint %.1f°C since %.1fmin" % (value_t, value_s, (time.time()-tm_s)/60)) + return self.__nagios_return__(DID_FOLLOWS_SETPOINT, nagios.Nagios.ERROR, "Requested setpoint %.1f°C unequal valve setpoint %.1f°C since %.1fmin" % (value_t, value_s, (time.time()-tm_s)/60)) elif value_t != value_s and dt > self.FOLLOW_REQUEST_WARNING: - return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.WARNING, "Requested setpoint %.1f°C unequal valve setpoint %.1f°C since %.1fmin" % (value_t, value_s, (time.time()-tm_s))) - return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.OK, "Requested setpoint equal valve setpoint %.1f°C" % value_s) + return self.__nagios_return__(DID_FOLLOWS_SETPOINT, nagios.Nagios.WARNING, "Requested setpoint %.1f°C unequal valve setpoint %.1f°C since %.1fmin" % (value_t, value_s, (time.time()-tm_s))) + return self.__nagios_return__(DID_FOLLOWS_SETPOINT, nagios.Nagios.OK, "Requested setpoint equal valve setpoint %.1f°C" % value_s) # # BATTERY # - elif key == self.MONITORING_BATTERY: - if self.battery is None: - return self.__nagios_return__(self.MONITORING_BATTERY, nagios.Nagios.UNKNOWN, "Device exists, but no data received or unknown monitoring") - elif self.battery <= self.BATTERY_LVL_ERROR: - return self.__nagios_return__(self.MONITORING_BATTERY, nagios.Nagios.ERROR, "Battery level critical low (%.1f%%)" % self.battery) - elif self.battery <= self.BATTERY_LVL_WARNING: - return self.__nagios_return__(self.MONITORING_BATTERY, nagios.Nagios.WARNING, "Battery level low (%.1f%%)" % self.battery) + elif key == DID_BATTERY_LEVEL: + battery_lvl = self.__state_storage__.get(self.KEY_BATTERY, (0, None))[1] + if battery_lvl is None: + return self.__nagios_return__(DID_BATTERY_LEVEL, nagios.Nagios.UNKNOWN, "Device exists, but no data received or unknown monitoring") + elif battery_lvl <= self.BATTERY_LVL_ERROR: + return self.__nagios_return__(DID_BATTERY_LEVEL, nagios.Nagios.ERROR, "Battery level critical low (%.1f%%)" % battery_lvl) + elif battery_lvl <= self.BATTERY_LVL_WARNING: + return self.__nagios_return__(DID_BATTERY_LEVEL, nagios.Nagios.WARNING, "Battery level low (%.1f%%)" % battery_lvl) else: - return self.__nagios_return__(self.MONITORING_BATTERY, nagios.Nagios.OK, "Battery okay (%.1f%%)" % self.battery) + return self.__nagios_return__(DID_BATTERY_LEVEL, nagios.Nagios.OK, "Battery okay (%.1f%%)" % battery_lvl) # # LINKQUALITY # - elif key == self.MONITORING_LINKQUALITY: - if self.linkquality is None: - return self.__nagios_return__(self.MONITORING_LINKQUALITY, nagios.Nagios.UNKNOWN, "Device exists, but no data received or unknown monitoring") - elif self.linkquality <= self.LINKQUALITY_ERROR: - return self.__nagios_return__(self.MONITORING_LINKQUALITY, nagios.Nagios.ERROR, "Linkquality critical low (%d)" % self.linkquality) - elif self.linkquality <= self.LINKQUALITY_WARNING: - return self.__nagios_return__(self.MONITORING_LINKQUALITY, nagios.Nagios.WARNING, "Linkquality level low (%d)" % self.linkquality) + elif key == DID_LINKQUALITY: + linkquality = self.__state_storage__.get(self.KEY_LINKQUALITY, (0, None))[1] + if linkquality is None: + return self.__nagios_return__(DID_LINKQUALITY, nagios.Nagios.UNKNOWN, "Device exists, but no data received or unknown monitoring") + elif linkquality <= self.LINKQUALITY_ERROR: + return self.__nagios_return__(DID_LINKQUALITY, nagios.Nagios.ERROR, "Linkquality critical low (%d)" % linkquality) + elif linkquality <= self.LINKQUALITY_WARNING: + return self.__nagios_return__(DID_LINKQUALITY, nagios.Nagios.WARNING, "Linkquality level low (%d)" % linkquality) else: - return self.__nagios_return__(self.MONITORING_LINKQUALITY, nagios.Nagios.OK, "Linkquality okay (%d)" % self.linkquality) + return self.__nagios_return__(DID_LINKQUALITY, nagios.Nagios.OK, "Linkquality okay (%d)" % linkquality) - def __nagios_return__(self, monitoring_name, status, msg, force=False): + def __nagios_return__(self, did, status, msg, force=False): tm = time.time() - if monitoring_name not in self.__unknown_tm__: - self.__unknown_tm__[monitoring_name] = None + if did not in self.__unknown_tm__: + self.__unknown_tm__[did] = None if status == nagios.Nagios.UNKNOWN and not force: - if self.__unknown_tm__[monitoring_name] is None: - self.__unknown_tm__[monitoring_name] = tm - dt = tm - self.__unknown_tm__[monitoring_name] + if self.__unknown_tm__[did] is None: + self.__unknown_tm__[did] = tm + dt = tm - self.__unknown_tm__[did] if dt >= self.LAST_MSG_ERROR: status = nagios.Nagios.UNKNOWN elif dt >= self.LAST_MSG_WARNING: @@ -165,7 +163,7 @@ class base(object): status = nagios.Nagios.OK msg += " - since %.1fh" % (dt / 3600) else: - self.__unknown_tm__[monitoring_name] = None + self.__unknown_tm__[did] = None return {"status": status, "msg": msg} @@ -202,7 +200,33 @@ class livarno_sw_br_ct(base): class brennenstuhl_heatingvalve(base): BATTERY_LVL_WARNING = 4 BATTERY_LVL_ERROR = 3 - FOLLOW_KEY = "current_heating_setpoint" + # + ACTOR_WARN_OFFSET = 1.5 + ACTOR_ERR_OFFSET = 2.5 + # + KEY_SETPOINT = "current_heating_setpoint" + KEY_CURRENT_VALUE = "local_temperature" + + def status(self, key): + # + # ACTOR + # + if key == DID_ACTOR: + tm_s, value_s = self.__state_storage__.get(self.KEY_SETPOINT, (0, None)) + tm_c, value_c = self.__state_storage__.get(self.KEY_CURRENT_VALUE, (0, None)) + # + if value_s is None or value_c is None: + return self.__nagios_return__(DID_ACTOR, nagios.Nagios.UNKNOWN, "Device exists, but no data received") + elif value_s <= 5: + return self.__nagios_return__(DID_ACTOR, nagios.Nagios.OK, "No monitoring in Summer Mode") + elif value_c > value_s + self.ACTOR_ERR_OFFSET: + return self.__nagios_return__(DID_ACTOR, nagios.Nagios.ERROR, "Current Temperature much to high %.1f°C > %.1f°C" % (value_c, value_s)) + elif value_c > value_s + self.ACTOR_WARN_OFFSET: + return self.__nagios_return__(DID_ACTOR, nagios.Nagios.WARNING, "Current Temperature to high %.1f°C > %.1f°C" % (value_c, value_s)) + else: + return self.__nagios_return__(DID_ACTOR, nagios.Nagios.OK, "Current Temperature okay %.1f°C > %.1f°C" % (value_c, value_s)) + else: + return super().status(key) class silvercrest_powerplug(base): diff --git a/z_server/mqtt b/z_server/mqtt index 328d347..14e56cc 160000 --- a/z_server/mqtt +++ b/z_server/mqtt @@ -1 +1 @@ -Subproject commit 328d3471a748472695a61193becdda76c7eefe69 +Subproject commit 14e56ccdbf6594f699b4afcfb4acafe9b899e914 diff --git a/z_server/z_protocol.py b/z_server/z_protocol.py index 90708b8..ee5aec0 100644 --- a/z_server/z_protocol.py +++ b/z_server/z_protocol.py @@ -2,10 +2,19 @@ import nagios import socket_protocol # from devdi.topic import topic_by_props -DID_FOLLOWS_SETPOINT = 'follow_setpoint' +DID_ACTOR = 'actor' DID_BATTERY_LEVEL = 'battery' +DID_FOLLOWS_SETPOINT = 'follow' DID_HEARTBEAT = 'heartbeat' DID_LINKQUALITY = 'linkquality' +# +DIDS = ( + DID_ACTOR, + DID_BATTERY_LEVEL, + DID_FOLLOWS_SETPOINT, + DID_HEARTBEAT, + DID_LINKQUALITY +) class server(socket_protocol.pure_json_protocol): @@ -15,10 +24,8 @@ class server(socket_protocol.pure_json_protocol): socket_protocol.pure_json_protocol.__init__(self, *args, **kwargs) # if not self.__comm_inst__.IS_CLIENT: - self.register_callback(socket_protocol.SID_READ_REQUEST, DID_FOLLOWS_SETPOINT, self.device_status) - self.register_callback(socket_protocol.SID_READ_REQUEST, DID_BATTERY_LEVEL, self.device_status) - self.register_callback(socket_protocol.SID_READ_REQUEST, DID_LINKQUALITY, self.device_status) - self.register_callback(socket_protocol.SID_READ_REQUEST, DID_HEARTBEAT, self.device_status) + for did in DIDS: + self.register_callback(socket_protocol.SID_READ_REQUEST, did, self.device_status) def device_status(self, msg): if msg.get_status() == socket_protocol.STATUS_OKAY: