AppDaemon

Taking Home Assistant to the next level with Python — part 1

Marcel Blijleven
6 min readNov 8, 2021
Screenshot of Python code for an Appdaemon app

AppDaemon is a great way to take your smart home automations to the next level. In this series I will guide you through AppDaemon and how to use it to take your home automation to the next level.

  • Part 1: setting up AppDaemon and creating a simple light sequence (this article)
  • Part 2: turning off power to a sit-stand desk if it’s no longer in use after a certain time, and how to test your app
  • Part 3: controlling mechanical ventilation using HTTP requests and reporting its status back to Home Assistant

Getting started

What is AppDaemon?

AppDaemon is a sandboxed Python execution environment which can be used to write automation apps for Home Assistant. These apps run in the background and can subscribe to Home Assistant events, or respond to events like sunrise and sunset. Because it uses Python, you can make it as simple or as fancy as you want. You can even write unit tests for your automations to ensure it still does what you want after applying changes.

How to setup

The easiest way to install AppDaemon for Home Assistant, is to use the official add-on created by Frenck. Go to Supervisor > add-ons and search for ‘AppDaemon 4’. Install it, and when the installation is finished press the start button. Let it start and navigate to the logs of the add-on (same screen, different tab) to check if all went well.

Now go to the configuration tab, on this tab you change the configuration of the AppDaemon add-on. Under python-packages: you can add all third party packages you think you need, like the imaging library Pillow. You don’t have to configure it completely right away, it can always be changed afterwards. Just make sure you restart the add-on after changing the configuration.

After starting the add-on, it should have created a /config/appdaemon directory. Navigate to that directory and open the appdaemon.yaml file and place the following contents.

secrets: /config/secrets.yaml
appdaemon:
latitude: !secret latitude_home
longitude: !secret longitude_home
elevation: 0
time_zone: Europe/Amsterdam
log_thread_actions: 1
plugins:
HASS:
type: hass
http:
url: http://0.0.0.0:5050
hadashboard:
admin:
api:

Make sure you have a secrets.yaml file with the latitude_home and longitude_home secrets. These values will be used for sunset/sunrise triggers. After creating the appdaemon.yaml file, create a directory called apps inside /config/appdaemon if it doesn’t exist yet and place an empty apps.yaml file in that directory.

Creating a simple light sequence

AppDaemon sequences

Before diving into Python, let’s look at the easiest way to create a sequence with AppDaemon sequences. If you followed along with the ‘How to setup’ steps, you should have an apps.yaml file inside your /config/appdaemon/apps directory. Open it and add the following (be sure to change entity_id‘s to match your situation):

It’s a simple sequence which turns off all lights in the living room, then turns them back on, one at a time with a delay of 1 second between each light.

Python

Now that we’ve created a sequence, we need a way to trigger it. The first thing you should do is create a file called light_sequence.py inside the apps directory (see how to setup). Inside that file, add the following lines:

First, we import the Hass class from appdaemon.plugins.hass.hassapi, which we’ll use to create our own AppDaemon app class. We then create a variable with the name and value LIGHT_SEQUENCE. This will be used later on to subscribe to Home Assistant events. Finally, we create a class called LightSequence which inherits from the hass.Hass class, this allows us to use its services inside our own class

Each AppDaemon app class must have an initialize() method. This method allows the app to register callbacks, these callbacks can respond to state changes in Home Assistant. The initialize() method will be called when AppDaemon starts, when Home Asssistant restarts and when you change your app module parameters or class code.

If we would run the light sequence app now, it wouldn’t do anything because we used pass inside the initialize() method, which is Pythons no-op (no operation) statement.

Replace thepass statement with self.listen_event(self.lights_cb, LIGHT_SEQUENCE). listen_event() is a method inherited from the hass.Hass class. The first argument for this method is the callback, a method which should be called whenever a event is triggered. The second argument is the name of the event it should subscribe to, in this case LIGHT_SEQUENCE. If no event name is specified, listen_event() will subscribe to all events.

Now add a new method inside our LightSequence class and call it lights_cb(self, event, data, kwargs). This method has a specific signature which is needed for event callbacks, which consists of:

  • self parameter, a reference to the instance of LightSequence
  • event parameter, the name of the event that was called
  • data parameter, a dictionary containing event data, if any
  • kwargs parameter, a dictionary of additional keyword arguments to be provided to the callback

If the app would run, it would call the lights_cb() callback each time a event with name LIGHT_SEQUENCE was triggered, but since it is empty, it wouldn’t do anything. So let’s add some code to run the sequence we’ve created earlier.

The lights_cb() method now has some code in it. When called, it checks if the class attribute sequence_handle (declared on line #2) is None. If it is, it creates a new handle by running the run_sequence() method which returns a handle. If it is not None, it means the sequence was already called so we cancel it using the cancel_timer. By doing this, you can make changes to your app without keeping the thread busy, which could lead to thread starvation.

It’s also possible to run a sequence with Python code only, because AppDaemon apps have a method called run_in() which takes the following arguments:

  • a callback method
  • a delay in seconds
  • a set of keyword arguments to provide to callback, like brightness or transition.

The same sequence would look like this using run_in():

Let’s talk about the turn_on_lights() method first. It has the required signature for a run_in() callback. This signature prevents us from using the self.turn_on() service directly, so we have to create a custom callback for it. The kwargs parameter allows for adding keyword arguments to be passed to the callback, in this case the name/id of the entity. The only thing the turn_on_lights() method will do is call the Home Assistant service turn_on() to turn on the provided entity.

Inside the lights_cb() method, we first call the turn_off() service for the lights group light.living_room before starting the sequence. The entity light.hue_spot_1 is turned on after a delay of 1 second, light.hue_spot_2 is turned on after a delay of 2 seconds and so on. This creates a nice sequence where all lights are turned back on, one at a time.

Be sure to play around with delays, and try things like adding brightness=128, color=green to the turn_on() service to make it even more fancy.

Registering the app

Final thing to do in AppDaemon is to register the app. You should have an apps.yaml file by now, so open it up and add the following:

light_sequence:
module: light_sequence
class: LightSequence

This is the most basic way of registering an app. The module refers to the light_sequence.py file, the class is our custom class called LightSequence.

Now restart your AppDaemon add-on and inspect the logs, you should see it register the new app.

Triggering the sequence

To trigger the sequence, we will create a script in Home Assistant which triggers the LIGHT_SEQUENCE event. This event will then be picked up by our Python app and the sequence is fired. On your Home Assistant host, navigate to /config and open the scripts.yaml file. Add the following to the file the create a service which can be called from the Home Assistant frontend.

light_sequence:
alias: Light sequence
sequence:
- event: LIGHT_SEQUENCE

Here you can see the event name is called LIGHT_SEQUENCE, which is the event our app listens to. If you want to test the script, go to Developer Tools in Home Assistant and navigate to the ‘Services’ tab. Select the script.light_sequence service and press the ‘Call service’ button. When everything is working correctly, you can add a ‘button’ card to your Home Assistant dashboard and set the action to ‘call service’.

Final thoughts

This is a short introduction to using AppDaemon with Home Assistant, but it should be enough for you to start exploring. In part 2, I focus more on how to test your AppDaemon app to make sure the code does what it needs to do, so be sure to check that out too.

--

--