import appdaemon.plugins.hass.hassapi as hass import appdaemon.exceptions as ad_exception import ad_toolbox.smartcondition as SmartCondition import re import uuid from expressionparser import ParsingException class Notification: ENTITY_PLACEHOLDER_PATTERN = re.compile(r"\{([^{}]+)\}") def __init__(self,ad_api,config,name): self.recipients = dict() self.config = config self.ad_api = ad_api try: self.auto_clear = config['message']['auto_clear'] except KeyError: self.auto_clear = False self.uuid = str(uuid.uuid4()) for recipient in config["recipients"]: try: self.recipients[recipient] = SmartCondition.Evaluator(self.ad_api,self.config["recipients"][recipient]['conditions'],condition_name = name,on_succeed_cb = self.on_condition_succeed, on_fail_cb = self.on_conditon_fail,pass_condition_name_to_cb = recipient, trigger_callback_on_activation = False) except ParsingException as e: self.log_error(str(e)) continue def on_conditon_fail(self, recipient): if self.auto_clear: try: tag = self.config['message']['data']['tag'] except KeyError: tag = self.uuid self.clear_notification(recipient,tag) def on_condition_succeed(self,recipient): self.send_notification(recipient) def on_timeout_expired(self,kwargs): tag = kwargs['tag'] recipient = kwargs['recipient'] self.clear_notification(recipient,tag) def clear_notification(self,recipient,tag): message = 'clear_notification' data = { 'tag' : tag } def clear_notification(service,tag): self.ad_api.log(f"Clear notification {data} from {service}") try: self.ad_api.call_service(service,message = message, data = data) except ad_exception.ServiceException: self.log_error(f"Invalid service: {service}") recipient_yaml = self.config["recipients"][recipient] if isinstance(recipient_yaml['services'], str): clear_notification(recipient_yaml['services'],tag) else: for service in recipient_yaml['services']: clear_notification(service,tag) def render_message_content(self, content): if not isinstance(content, str): return content def replace_entity_state(match): entity_id = match.group(1).strip() if entity_id == "": return match.group(0) try: state = self.ad_api.get_state(entity_id) except Exception as e: self.log_error(f"Failed to resolve '{entity_id}' in notification content: {e}") return match.group(0) if state is None: self.log_error(f"Unable to resolve '{entity_id}' in notification content") return match.group(0) return str(state) return self.ENTITY_PLACEHOLDER_PATTERN.sub(replace_entity_state, content) def send_notification(self,recipient): try: title_template = self.config['message']['title'] except KeyError: title_template = "" try: message_content = self.config['message']['content'] except KeyError: message_content = "" try: data = self.config['message']['data'] except KeyError: data = dict() title = self.render_message_content(title_template) message = self.render_message_content(message_content) #TODO : maybe it's better to not send tag when it's not needed if not 'tag' in data: data['tag'] = self.uuid if 'timeout' in self.config['message']: timeout = self.config['message']['timeout'] self.ad_api.run_in(self.on_timeout_expired,timeout,tag = data['tag'],recipient = recipient) def send_notification(service): self.ad_api.log(f"Sending notification '{message}' to {service}{f' with data {data}' if data else ''}") try: self.ad_api.call_service(service,title = title,message = message, data = data) except ad_exception.ServiceException: self.log_error(f"Invalid service: {service}") recipient_yaml = self.config["recipients"][recipient] if isinstance(recipient_yaml['services'], str): send_notification(recipient_yaml['services']) else: for service in recipient_yaml['services']: send_notification(service) def log_error(self,message,stop_app = False): self.ad_api.log(message,level = "ERROR", log = "main_log") self.ad_api.log(message,level = "ERROR", log = "error_log") if stop_app: self.ad_api.stop_app(self.ad_api.name) class NotificationsManager(hass.Hass): def initialize(self): self.notifications = dict() if "notifications" in self.args: for notification in self.args["notifications"]: self.notifications[notification] = Notification(self,self.args["notifications"][notification],notification)