2023-12-12 12:44:34 +01:00
import json
import logging
import mqtt
import nagios
import time
try :
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError :
ROOT_LOGGER_NAME = ' root '
logger = logging . getLogger ( ROOT_LOGGER_NAME ) . getChild ( __name__ )
class base ( object ) :
2023-12-20 09:36:42 +01:00
MONITORING_HEARTBEAT = " heartbeat "
MONITORING_BATTERY = " battery "
MONITORING_FOLLOW_SETPOINT = " follow_setpoint "
#
2023-12-12 12:44:34 +01:00
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
2023-12-20 07:26:39 +01:00
FOLLOW_KEY = None
2023-12-16 07:12:29 +01:00
#
2023-12-19 07:12:56 +01:00
BATTERY_LVL_WARNING = 15
BATTERY_LVL_ERROR = 5
2023-12-16 08:16:45 +01:00
#
2023-12-20 09:36:42 +01:00
LAST_MSG_WARNING = 6 * 60 * 60
LAST_MSG_ERROR = 24 * 60 * 60
2023-12-12 12:44:34 +01:00
def __init__ ( self , mqtt_client : mqtt . mqtt_client , topic ) :
self . topic = topic
#
2023-12-20 09:36:42 +01:00
self . __unknown_tm__ = { }
#
2023-12-12 12:44:34 +01:00
mqtt_client . add_callback ( topic , self . __rx__ )
mqtt_client . add_callback ( topic + ' /# ' , self . __rx__ )
#
2023-12-16 08:16:45 +01:00
self . last_device_msg = None
#
2023-12-12 12:44:34 +01:00
self . __target_storage__ = { }
self . __state_storage__ = { }
2023-12-16 07:12:29 +01:00
#
self . battery = None
2023-12-12 12:44:34 +01:00
def __rx__ ( self , client , userdata , message ) :
2023-12-20 07:26:39 +01:00
try :
payload = json . loads ( message . payload )
except json . decoder . JSONDecodeError :
pass
else :
if type ( payload ) is dict :
#
# 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
#
2023-12-20 09:36:42 +01:00
if self . MONITORING_BATTERY in payload and message . topic == self . topic :
self . battery = payload [ self . MONITORING_BATTERY ]
2023-12-12 12:44:34 +01:00
def target ( self , key , value ) :
tm_t , value_t = self . __target_storage__ . get ( key , ( 0 , None ) )
if value != value_t :
self . __target_storage__ [ key ] = time . time ( ) , value
logger . debug ( " Target value for device identified: %s : %s " , key , repr ( value ) )
def state ( self , key , value ) :
2023-12-14 18:16:15 +01:00
self . __state_storage__ [ key ] = time . time ( ) , value
2023-12-12 12:44:34 +01:00
logger . debug ( " Device state identified: %s : %s " , key , repr ( value ) )
def status ( self , key ) :
2023-12-16 07:12:29 +01:00
#
2023-12-16 08:16:45 +01:00
# HEARTBEAT
#
2023-12-20 09:36:42 +01:00
if key == self . MONITORING_HEARTBEAT :
2023-12-16 08:16:45 +01:00
if self . last_device_msg is None :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_HEARTBEAT , nagios . Nagios . UNKNOWN , " Device exists, but no data received " )
2023-12-16 08:16:45 +01:00
else :
dt = time . time ( ) - self . last_device_msg
dt_disp = dt / 60 / 60
if dt > self . LAST_MSG_ERROR :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_HEARTBEAT , nagios . Nagios . ERROR , " Last message %.1f h ago " % dt_disp )
2023-12-16 08:16:45 +01:00
elif dt > self . LAST_MSG_WARNING :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_HEARTBEAT , nagios . Nagios . WARNING , " Last message %.1f h ago " % dt_disp )
2023-12-16 08:16:45 +01:00
else :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_HEARTBEAT , nagios . Nagios . OK , " Last message %.1f h ago " % dt_disp )
2023-12-16 08:16:45 +01:00
#
2023-12-16 07:12:29 +01:00
# FOLLOW SETPOINT
#
2023-12-20 09:36:42 +01:00
elif key == self . MONITORING_FOLLOW_SETPOINT :
2023-12-20 07:26:39 +01:00
if self . FOLLOW_KEY is None :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_FOLLOW_SETPOINT , nagios . Nagios . UNKNOWN , " Device exist, but does not follow any setpoint. " , force = True )
2023-12-20 07:26:39 +01:00
tm_s , value_s = self . __state_storage__ . get ( self . FOLLOW_KEY , ( 0 , None ) )
2023-12-16 07:12:29 +01:00
try :
2023-12-20 07:26:39 +01:00
tm_t , value_t = self . __target_storage__ [ self . FOLLOW_KEY ]
2023-12-16 07:12:29 +01:00
except KeyError :
if value_s is not None :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_FOLLOW_SETPOINT , nagios . Nagios . WARNING , " Current temperature setpoint %.1f °C (age= %.1f min), but never received a setpoint " % ( value_s , ( time . time ( ) - tm_s ) / 60 ) )
return self . __nagios_return__ ( self . MONITORING_FOLLOW_SETPOINT , nagios . Nagios . UNKNOWN , " Device exists, but no data received " )
2023-12-16 07:12:29 +01:00
else :
tm = time . time ( )
dt = tm - tm_t
if value_t != value_s and dt > self . FOLLOW_REQUEST_ERROR :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_FOLLOW_SETPOINT , nagios . Nagios . ERROR , " Requested setpoint unequal valve setpoint %.1f °C since %.1f min " % ( value_s , ( time . time ( ) - tm_s ) / 60 ) )
2023-12-16 07:12:29 +01:00
elif value_t != value_s and dt > self . FOLLOW_REQUEST_WARNING :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_FOLLOW_SETPOINT , nagios . Nagios . WARNING , " Requested setpoint unequal valve setpoint %.1f °C since %.1f min " % ( 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 )
2023-12-16 07:12:29 +01:00
#
# BATTERY
#
2023-12-20 09:36:42 +01:00
elif key == self . MONITORING_BATTERY :
2023-12-16 07:12:29 +01:00
if self . battery is None :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_BATTERY , nagios . Nagios . UNKNOWN , " Device exists, but no data received or unknown monitoring " )
2023-12-19 07:12:56 +01:00
elif self . battery < = self . BATTERY_LVL_ERROR :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_BATTERY , nagios . Nagios . ERROR , " Battery level critical low ( %.1f %% ) " % self . battery )
2023-12-19 07:12:56 +01:00
elif self . battery < = self . BATTERY_LVL_WARNING :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_BATTERY , nagios . Nagios . WARNING , " Battery level low ( %.1f %% ) " % self . battery )
2023-12-16 07:12:29 +01:00
else :
2023-12-20 09:36:42 +01:00
return self . __nagios_return__ ( self . MONITORING_BATTERY , nagios . Nagios . OK , " Battery okay ( %.1f %% ) " % self . battery )
def __nagios_return__ ( self , monitoring_name , status , msg , force = False ) :
tm = time . time ( )
if monitoring_name not in self . __unknown_tm__ :
self . __unknown_tm__ [ monitoring_name ] = 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 dt > = self . LAST_MSG_ERROR :
status = nagios . Nagios . UNKNOWN
elif dt > = self . LAST_MSG_WARNING :
status = nagios . Nagios . WARNING
else :
status = nagios . Nagios . OK
msg + = " - since %.1f h " % ( dt / 3600 )
else :
self . __unknown_tm__ [ monitoring_name ] = None
return { " status " : status , " msg " : msg }
2023-12-12 12:44:34 +01:00
class group ( object ) :
def __init__ ( self , * args , * * kwargs ) :
pass
class shelly_sw1 ( base ) :
pass
class tradfri_sw ( base ) :
pass
class tradfri_sw_br ( base ) :
pass
class tradfri_sw_br_ct ( base ) :
pass
class tradfri_button ( base ) :
2023-12-22 06:46:52 +01:00
LAST_MSG_WARNING = 15 * 60 * 60
LAST_MSG_ERROR = 24 * 60 * 60
2023-12-12 12:44:34 +01:00
class livarno_sw_br_ct ( base ) :
pass
class brennenstuhl_heatingvalve ( base ) :
2023-12-19 07:12:56 +01:00
BATTERY_LVL_WARNING = 4
BATTERY_LVL_ERROR = 3
2023-12-20 07:26:39 +01:00
FOLLOW_KEY = " current_heating_setpoint "
2023-12-12 12:44:34 +01:00
class silvercrest_powerplug ( base ) :
pass
class silvercrest_motion_sensor ( base ) :
2023-12-20 07:26:39 +01:00
pass
2023-12-12 12:44:34 +01:00
class my_powerplug ( base ) :
pass
class audio_status ( base ) :
pass
class remote ( base ) :
pass