Inital check_z_actor added + refactoring

This commit is contained in:
Dirk Alders 2024-07-14 18:17:50 +02:00
parent 51c0b9eaa9
commit 5a9b7a1847
6 changed files with 116 additions and 88 deletions

1
check_z_actor Symbolic link
View File

@ -0,0 +1 @@
check_z_heartbeat

View File

@ -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)

View File

@ -1 +1 @@
check_z_follow
check_z_heartbeat

View File

@ -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):

@ -1 +1 @@
Subproject commit 328d3471a748472695a61193becdda76c7eefe69
Subproject commit 14e56ccdbf6594f699b4afcfb4acafe9b899e914

View File

@ -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: