AppDaemon
Taking Home Assistant to the next level with Python — part 1
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 ofLightSequence
event
parameter, the name of the event that was calleddata
parameter, a dictionary containing event data, if anykwargs
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
ortransition
.
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.