123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- 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):
- 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 = 15
- BATTERY_LVL_ERROR = 5
- #
- LAST_MSG_WARNING = 6 * 24 * 60 * 60
- LAST_MSG_ERROR = 24 * 24 * 60 * 60
-
- def __init__(self, mqtt_client: mqtt.mqtt_client, topic):
- self.topic = topic
- #
- mqtt_client.add_callback(topic, self.__rx__)
- mqtt_client.add_callback(topic + '/#', self.__rx__)
- #
- self.last_device_msg = None
- #
- self.__target_storage__ = {}
- self.__state_storage__ = {}
- #
- self.battery = None
-
- def __rx__(self, client, userdata, message):
- pass
-
- 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):
- self.__state_storage__[key] = time.time(), value
- logger.debug("Device state identified: %s: %s", key, repr(value))
-
- def status(self, key):
- #
- # HEARTBEAT
- #
- if key == "heartbeat":
- if self.last_device_msg is None:
- return {"status": nagios.Nagios.UNKNOWN, "msg": "Device exists, but no data received or unknown monitoring"}
- else:
- dt = time.time() - self.last_device_msg
- dt_disp = dt / 60 / 60
- if dt > self.LAST_MSG_ERROR:
- return {"status": nagios.Nagios.ERROR, "msg": "Last message %.1fh ago" % dt_disp}
- elif dt > self.LAST_MSG_WARNING:
- return {"status": nagios.Nagios.WARNING, "msg": "Last message %.1fh ago" % dt_disp}
- else:
- return {"status": nagios.Nagios.OK, "msg": "Last message %.1fh ago" % dt_disp}
- #
- # FOLLOW SETPOINT
- #
- elif 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):
- 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):
- def __rx__(self, client, userdata, message):
- try:
- payload = json.loads(message.payload)
- except json.decoder.JSONDecodeError:
- logger.warning("JSON decode error %s", self.topic)
- else:
- #
- # heartbeat
- #
- if message.topic == self.topic:
- self.last_device_msg = time.time()
- #
- # battery level
- #
- if "battery" in payload and message.topic == self.topic:
- self.battery = payload["battery"]
-
-
- class livarno_sw_br_ct(base):
- pass
-
-
- class brennenstuhl_heatingvalve(base):
- BATTERY_LVL_WARNING = 4
- BATTERY_LVL_ERROR = 3
-
- def __rx__(self, client, userdata, message):
- try:
- payload = json.loads(message.payload)
- except json.decoder.JSONDecodeError:
- logger.warning("JSON decode error %s", self.topic)
- else:
- #
- # heartbeat
- #
- if message.topic == self.topic:
- self.last_device_msg = time.time()
- #
- # follow setpoint
- #
- if "current_heating_setpoint" in payload:
- if message.topic == self.topic + '/set':
- self.target("current_heating_setpoint", payload["current_heating_setpoint"])
- if message.topic == self.topic:
- self.state("current_heating_setpoint", payload["current_heating_setpoint"])
- #
- # battery level
- #
- if "battery" in payload and message.topic == self.topic:
- self.battery = payload["battery"]
-
-
- class silvercrest_powerplug(base):
- pass
-
-
- class silvercrest_motion_sensor(base):
- def __rx__(self, client, userdata, message):
- try:
- payload = json.loads(message.payload)
- except json.decoder.JSONDecodeError:
- logger.warning("JSON decode error %s", self.topic)
- else:
- #
- # heartbeat
- #
- if message.topic == self.topic:
- self.last_device_msg = time.time()
- #
- # battery level
- #
- if "battery" in payload and message.topic == self.topic:
- self.battery = payload["battery"]
-
-
- class my_powerplug(base):
- pass
-
-
- class audio_status(base):
- pass
-
-
- class remote(base):
- pass
|