"""Independent AppDaemon worker monitoring the boiler climate heartbeat.""" import time import appdaemon.plugins.hass.hassapi as hass from logger_interface import LoggerInterface from boilerregulator import finite class BoilerWatchdog(hass.Hass, LoggerInterface): def initialize(self): self.initialize_logger_interface(self.get_ad_api(), ascii_encode=False) self.base = self.args.get('mqtt_topic', 'appdaemon/radiator_heating').rstrip('/') self.mqtt_namespace = self.args.get('mqtt_namespace', 'mqtt') self.timeout = finite(self.args.get('heartbeat_timeout', 180)) if self.timeout < 60: raise ValueError('heartbeat_timeout must be at least 60 seconds') if self.AD.plugins.get_plugin_object(self.mqtt_namespace) is None: raise RuntimeError('MQTT plugin missing; configure it and restart AppDaemon') self.last_heartbeat = None self.fault = False self.listen_event(self._mqtt_event, 'MQTT_MESSAGE', namespace=self.mqtt_namespace) self.run_every(self._check, 'now', 10) self._check() def _mqtt_event(self, event, data, kwargs): if data.get('topic') == self.base + '/heartbeat': self.last_heartbeat = time.monotonic() if self.fault: self.log_info('Boiler climate heartbeat recovered') self.fault = False elif data.get('topic') is None and data.get('state') == 'Connected': self.last_heartbeat = None self.fault = False self._check() def _check(self, kwargs=None): if self.last_heartbeat is None or time.monotonic() - self.last_heartbeat >= self.timeout: if not self.fault: self.call_service('mqtt/publish', namespace=self.mqtt_namespace, topic=self.base + '/availability', payload='offline', qos=0, retain=True) self.log_warning('Boiler climate unavailable: waiting for a fresh heartbeat') self.fault = True