Skip to content

AppDaemon automation examples

Aephir edited this page Jan 10, 2023 · 3 revisions

AppDAaemon Automation Examples

Example 1 - Notify kid(s) about new "ugeplan" and about important things

This will run at set intervals (in this form, every Sunday at 10:00), as well as every time the ugeplan attribute changes. It will notify the kid (or you, if you add your device instead) about checking the schedule for next week, as well as about anything they should take special note of, e.g. if the need to present (fremlægge), remember something (husk), or prepare something (forbered). Additional keywords can easily be added as needed.

This has been only superficially tested, but seems to work well with the parameters I have been able to test.

You can simply copy/paste, and make a search and replace for the few things mentioned below.

import hassapi as hass
import globals
import datetime


class Notify(hass.Hass, globals.Entities):
    """
    Reminder of important things in "ugeplan" of the "aula" integration.
    It runs every Sunday morning, to remind kids to look over their weekly schedule.
    It also runs upon changes to the "ugeplan" attribute, where it specifically notifies about anything noteworthy.
    "Noteworthy" is defined in the "self.keywords" dictionary,

    To do:
        Run daily to check/remind about "noteworthy" for upcoming day.
    """

    def initialize(self) -> None:
        """
        Adding "noteworthy" keywords and corresponding message.
        Setting up schedule and listeners.
        """

        self.keywords = {
            "fremlægge": "i skal fremlægge",
            "husk": "du skal huske noget",
            "syg": "der er sygdom",
            "forbered": "der er noget du skal have forberedt",
        }

        start = datetime.datetime(2023, 1, 8, 10, 0, 0)
        self.run_every(self.run_every_week_wrapper, start, 7 * 24 * 60 * 60)
        self.listen_state(self.main, self.aula_sensors["child"], attributes="ugeplan")

    def main(self, entity, attribute, old, new, kwargs) -> None:
        """
        Main function.
        """

        title, message = self.get_message(entity)
        self.notify(title, message)

    def run_every_week_wrapper(self, kwargs) -> None:
        """
        Wrapper to be able to call "main" with the "run_every", as this does not provide the arguments required.
        """

        entity: str = "run_every_week_wrapper"
        self.main(entity, None, None, None, None)

    def get_message(self, entity) -> tuple[str, str]:
        """
        Create a message about what to do. If updates in ugeplan outside of weekly schedule:
            A message detailing whether they are "noteworthy", and which day they occur on (if only one occurrence).
        If run on weekly schedule:
            A message about the "ugeplan" being available, and a note about what to remember.
        """

        if entity == self.aula_sensors["child"]:
            title, message, days_with_notice = self.parse_notifications()
        elif entity == "run_every_week_wrapper":
            title, message, days_with_notice = self.parse_notifications()
            if title == "Hej child!":
                message: str = "Klik på denne besked for at se ugeplan.\nHer kan du læse hvad du skal, så du husker det hele :)"
            else:
                added_info: str = ""
                for val in days_with_notice.values():
                    if self.keywords[val[1]] not in added_info:
                        added_info += self.keywords[val[1]] + ", "
                added_info: str = added_info[:-2]
                if len(days_with_notice) > 1:
                    added_info: str = self.reverse_replace(added_info)
                message: str = (
                    "Klik på denne besked for at se ugeplan.\nHusk at "
                    + added_info
                    + "."
                )
            title: str = "Hej child, her er din ugeplan!"

        return title, message

    def parse_notifications(self) -> tuple[str, str, dict[str, list[str, str]]]:
        """
        Parser for getting "noteworthy" information out. "Noteworthy" is in the "self.keywords" dictionary.
        Any number of keywords and values can be added here.
        """

        ugeplan_text: str = self.get_entity(self.aula_sensors["child"]).get_state(
            attribute="ugeplan"
        )
        ugeplan_text_by_day: list[str] = ugeplan_text.split("<h3>")[1:]
        ugeplan_text_by_subject: list[list[str]] = [day.split("<b>") for day in ugeplan_text_by_day]
        days_with_notice: dict = dict()

        for day in range(len(ugeplan_text_by_subject)):
            for text in ugeplan_text_by_subject[day]:
                for key in self.keywords.keys():
                    if key in text:
                        date: str = ugeplan_text_by_subject[day][0][:-5]
                        subject: str = text.split("</b>")[0]
                        days_with_notice[date] = [subject, key]

        if len(days_with_notice) > 1:
            title: str = (
                "Der er "
                + str(len(days_with_notice))
                + " ting du skal være opmærksom på!"
            )
            information: str = ""
            for val in days_with_notice.values():
                information += self.keywords[val[1]] + ", "
            information: str = information[:-3]
            information: str = self.reverse_replace(information)
            message: str = (
                "Der er beskeder om at "
                + information
                + ". Klik på beskeden for at se ugeplanen."
            )
        elif len(days_with_notice) == 1:
            title: str = (
                "Hej child, husk at "
                + list(days_with_notice.values())[0][1]
                + " på "
                + list(days_with_notice.keys())[0]
                + "!"
            )
            message: str = (
                "Der er en besked om at du skal huske at "
                + list(days_with_notice.values())[0][1]
                + " på "
                + list(days_with_notice.keys())[0]
                + ". Klik på beskeden for at se ugeplanen."
            )
        elif len(days_with_notice) == 0:
            title: str = "Hej child!"
            message: str = (
                "Der er nye beskeder om ugen. Klik på beskeden for at se ugeplanen."
            )

        return title, message, days_with_notice

    def reverse_replace(self, text: str) -> str:
        """
        Helper function to remove the last ", " and replace with " og ". E.g.
        "fremlægge, huske, forberede" becomes "fremlægge, huske og forberede".
        """

        old: str = ", "
        new: str = " og "
        return (text[::-1].replace(old[::-1], new[::-1], 1))[::-1]

    def notify(self, title: str, message: str) -> None:
        """
        Notifies the defined device/service with the title and message passed.
        "url": "/lovelace-child/school" and "clickAction": "/lovelace-child/school" are the android and iOS (respectively)
        tags to open the home assistant app at the corresponding dashboard/tab where the "ugeplan" is stored.
        """

        data: dict[str, str] = {
            "tag": "child_school_week",
            "url": "/lovelace-child/school",
            "clickAction": "/lovelace-child/school",
        }

        self.call_service(
            self.notify_phones["child"], title=title, message=message, data=data
        )

Notes: This is specific for my entities and Lovelace. You need to make the following changes:

  1. For "/lovelace-child/school" you should navigate in a browser to the Home Assistant dashboard/tab/view you want the message to open, and copy the last part. E.g. if the full URL is https://my.ha.com/lovelace-child/school, it would be "/lovelace-child/school".
  2. Every instance of self.aula_sensors["child"] should be replaced with your aula sensor entity_id.
  3. Every instance of self.notify_phones["child"] should be replaced with the service/device you want notified (e.g. "notify/mobile_app_child_iphone"
  4. You can omit the import globals and passing globals.Entities to the class Notify, I do so because all my entity_ids are stored in a globals.py file so I only have to change it one place if they change.
  5. You might want to keep an eye on which keywords your teachers use, and update them in the self.keywords dictionary.
  6. The self.run_every(self.run_every_week_wrapper, start, 7 * 24 * 60 * 60) means it will run every week (7 days * 24 hours * 60 minutes * 60 seconds between each run) starting on the start = datetime.datetime(2023, 1, 8, 10, 0, 0), which is Sunday, January 8th at 10:00 (am, if you use this). Change as you like.