Cómo usar AppDaemon en Home Assistant

Con AppDaemon vamos a avanzar un paso más en el tema de las automatizaciones con Home Assistant. Con el permiso de sus desarrolladores, diremos que Home Assistant no para de mejorar versión tras versión, y ha conseguido que sus Automations sean realmente potentes y útiles. Sobre todo a la hora de depurar con la opción «Show Trace» que nos indica perfectamente cada paso que se ejecuta. Todo de manera visual o bien configurando con yaml.

Pero todavía nos encontramos con ideas de automatización que sería muy complicado, si no imposible, ejecutar directamente en Home Assistant.

Podemos decir que este proyecto viene a cubrir esas carencias, aportando la potencia de un lenguaje moderno como Python y la posibilidad de usar paquetes externos. El listón en ese aspecto lo pone muy alto.

¿Qué es AppDaemon?

Podemos definir AppDaemon 4 como un espacio de ejecución de scripts de Python con el añadido de ciertas funciones que nos permitirán interactuar con el sistema Home Assistant. La ejecución es bastante independiente del sistema Home Assistant accediendo solo a través de API, por lo que no correremos riesgos de «dañar» otras partes del sistema.

Para aquellos que estén familiarizados con Python, es una manera muy natural y flexible de realizar nuestras automatizaciones.

Paa aquellos que no hayan tocado nada de programación, decirles que se animen, que no hace falta conocer todos los pormenores para buscar algún buen ejemplo (como el post que ahora mismo lees) e intentar modificar algo para crear tu propio programa.

Ventajas de usar AppDaemon

Para el usuario que se inicia con Home Assistant, recomendaría primero familiarizarse con Automations, pues cada vez irá mejorando más, y para tareas simples es suficiente.

Pero una vez pasada esa fase, imaginad lo que se puede conseguir usando Python: Desde implementar vuestro propio algoritmo super-completo para quitar/poner la calefacción, explorar técnicas de machine learning para predecir el consumo eléctrico, programar un control PID (para los que entiendan de control), agrupar varias automatizaciones que actúen sobre el mismo elemento con un simple if-then-else… etc.

En mi opinión junto a las automatizaciones de Home Assistant serán técnicas complementarias y en la práctica tendremos coexistiendo ambas sin problemas.

Cómo instalar AppDaemon en Home Assistant

Aunque AppDaemon puede correr en muchas configuraciones (no solo con Home Assistant) , vamos a centrarnos en la instalación como Add-on por su simplicidad, pero su uso con docker también es perfectamente posible.

En nuestro sistema Home Assistant navegaremos hasta Supervisor> Add-on Store y buscaremos AppDaemon 4 para instalarlo y arrancarlo:

AppDaemon Add-on

Por ahora eso es todo lo necesario para empezar. Dejaremos todas las opciones por defecto, pero desde luego se podrá personalizar el arranque cargando algún paquete adicional que necesitemos, o con comandos de inicio específicos.

Si el arranque es correcto, en la pestaña Log del propio Addon veremos lo siguiente al final:

...
2021-11-08 19:37:18.592701 INFO hello_world: Hello from AppDaemon
2021-11-08 19:37:18.595452 INFO hello_world: You are now ready to run Apps!
2021-11-08 19:37:18.598831 INFO AppDaemon: App initialization complete

Creando la primera aplicación con AppDaemon

Vamos a comenzar creando desde cero una aplicación bastante sosa, pues solo nos mostrará un texto. El sentido es explicar cómo y dónde se deja el código de una aplicación y cómo se le indica a Home Assistant que la ejecute.

Al instalar el Addon, se ha creado una nueva estructura de directorios. Vamos a trabajar en la carpeta config/appdaemon/apps/ . Esto lo podemos hacer bien con el Addon «File editor» o configurando Samba:

File editor Addon
Samba Addon

Para ficheros no muy extensos, os puede valer perfectamente File editor.

Sin embargo, para un texto más extenso tendréis más comodidad usando otro tipo de editores (Notepad++ por ejemplo para windows o su gemelo Notepadqq en Linux), o mejor aún un IDE como Visual Studio. Para esto lo mejor es exponer el directorio Config de Home Assistant y acceder desde nuestro ordenador habitual usando Samba.

En cualquier caso, vamos a crear en esta carpeta config/appdaemon/apps/ un fichero con extensión .py (en nuestro caso lo llamaremos prueba.py) y posteriormente modificaremos un fichero de configuración que está en esa misma carpeta (apps.yaml).

Vamos a crear primero el fichero prueba.py con el siguiente código:

import appdaemon.plugins.hass.hassapi as hass

#
# Aplicación 1
#

class ClasePrueba (hass.Hass):

  def initialize(self):
     self.log("Escritura en log por prueba.py")

Como estamos viendo, otra de las ventajas es poder incluir comentarios en el código (usando el carácter #)

Aquí va una breve explicación:

  • import appdaemon.plugins.hass.hassapi as hass –> Esta línea siempre será la primera, e indica la importación de la clase (hassapi, con el nombre hass) que se usará como base para crear nuestra nueva clase (ClasePrueba). Heredará todos los métodos (funciones) que luego podremos usar.
  • class ClasePrueba (hass.Hass): –> Con esta línea comienza la definición de nuestra clase que hemos llamado ClasePrueba.
  • def initialize(self) –> Toda clase debe tener una función initialize que se ejecutará una única vez al inicio.
  • self.log(«Escritura en logpor prueba.py») –> Este comando escribe en la pestaña Log del propio addon un texto.

No os procupéis si los conceptos de Clase, Herencia, Métodos o Atributos no os suenan para nada. Aunque os invito a profundizar en la programación orientada a objetos (OOP), las partes principales de cada aplicación serán muy similares.

Ahora modificaremos el fichero existente apps.yaml añadiendo las siguientes líneas al final para dar de alta nuestra aplicación:

prueba:
  module: prueba
  class: ClasePrueba

La primera línea será el nombre de nuestra aplicación.

El parámetro module coincidirá con el nombre del fichero .py creado donde hemos definido la clase, y class será el nombre de la clase que hemos definido en ese fichero.

Por ahora lo dejamos así de simple, pero tened en cuenta que varias aplicaciones pueden usar la misma clase creando distintas instancias, o omcluso un mismo módulo puede contener varias clases.

Podremos (lo veremos en el siguiente apartado) definir variables dentro de la sección de nuestra aplicación en el fichero apps.yaml. Dichas variables las podrá usar internamente nuestra aplicación, y será la manera que tengamos de programar aplicaciones genéricas que nos valgan para más de un caso concreto.

Para ver que realmente esta aplicación funciona (aunque sea muy simple), iremos a la pestaña Log del Addon AppDaemon instalado, daremos a REFRESH si es necesario, y en alguna línea debe aparecer lo siguiente:

2021-11-09 10:17:31.556496 INFO prueba: Escritura en log por prueba.py

Esta será la manera más sencilla de ver lo que van haciendo nuestras aplicaciones.

Como véis, NO hemos tenido que reiniciar Home Assistant, ni siquiera el propio addon, por lo que es normal que aparezcan otras líneas indicando que los ficheros se han creado o modificado. Esto es debido a que está constantemente monitorizando la creación y definición de sus aplicaciones.

Hemos aprendido el proceso básico para crear nuestras propias automatizaciones con AppDaemon:

  • Crearemos el fichero .py con el código python que queramos (ya veréis que no es demasiado complicado)
  • Modificaremos el fichero apps.yaml para incluir la nueva aplicación.
  • Comprobamos que funciona correctamente

Programando una automatización simple con AppDaemon

Vamos a continuar subiendo un peldaño en el grado de complejidad, reproduciendo una automatización que podemos realizar también sin usar AppDaemon, pero como veremos con ciertas ventajas en su uso.

La función de esta aplicación será apagar un enchufe (enchufe_c) si vemos que su consumo está por debajo de 15W durante 5 minutos, para ahorrar energía.

Hemos usado un tipo de enchufe con protocolo Zigbee y con indicación de consumo. Perfectamente válidos son también los que se usen por WiFi. Os dejo una selección con precios muy bajos, junto a una oferta del famoso ConBee II para poder controlarlos:

Solución usando automatizaciones de Home Assistant

Usando las automatizaciones incluidas es una tarea muy simple:

  • El iniciador se activará cuando la potencia medida por ese enchufe sea menor que 15 W durante 5 minutos.
  • Como condición pondremos que el enchufe esté encendido.
  • La acción lógicamente será apagar el enchufe.

Lo vemos en la versión yaml, pero se puede realizar todo con la interface visual:

alias: Corte enchufe
description: 'Corta enchufe si potencia<15W durante 5 minutos'
trigger:
  - platform: numeric_state
    entity_id: sensor.enchufe_c_electrical_measurement
    below: '15'
    for:
      hours: 0
      minutes: 5
      seconds: 0
      milliseconds: 0
condition:
  - condition: device
    type: is_on
    device_id: 09686a7609c0b3252d97d7dfa400808c
    entity_id: switch.enchufe_c_on_off
    domain: switch
action:
  - type: turn_off
    device_id: 09686a7609c0b3252d997d7dfa400808c
    entity_id: switch.enchufe_c_on_off
    domain: switch
mode: single

Solución usando AppDaemon

Como hemos visto, lo primero será crear nuestro fichero de aplicación (lo llamaremos corteEnchufe.py) con el siguiente código que luego explicaremos:

 
import appdaemon.plugins.hass.hassapi as hass
import datetime


# Aplicación para apagar enchufe por bajo consumo.
#
# Version log:
# 10NOV21:  Versión inicial



class ClaseCorteEnchufe(hass.Hass):


  def initialize(self):

    # Leemos variables
    self.nombre_enchufe=self.args["variables"]["nombre_enchufe"]
    self.estado_enchufe=self.args["variables"]["estado_enchufe"]
    self.consumo_enchufe=self.args["variables"]["consumo_enchufe"]
    self.limite_minutos=self.args["variables"]["limite_minutos"]
    self.limite_potencia=self.args["variables"]["limite_potencia"]
    self.baja_potencia=False
    self.tiempo_inicial=datetime.datetime.now()

    # Monitorizamos el cambio de valor en el sensor que mide consumo
    self.listen_state(self.monitorizar, self.consumo_enchufe)

    self.log('Inicializado')



  def monitorizar(self, entity, attribute, old, new, kwargs):
   
    # Variables locales de esta función
    potencia = float(self.get_state(self.consumo_enchufe))
    estado=self.get_state(self.estado_enchufe)
    ahora=datetime.datetime.now()

    if potencia>=self.limite_potencia:
      self.baja_potencia=False

    if potencia<self.limite_potencia and not self.baja_potencia:
      self.tiempo_inicial=datetime.datetime.now()
      self.baja_potencia=True
    
    if self.baja_potencia and estado=="on" and ((ahora - self.tiempo_inicial).minutes)>self.limite_minutos:
      self.log(f'Baja potencia de {self.nombre_enchufe} durante más de {self.limite_minutos} minutos.')
      self.accion()
    
  

  def accion(self, kwargs):
    self.log(f'Cortando {self.nombre_enchufe}')
    self.turn_off(self.estado_enchufe)




Parece muy complejo, pero realmente todo se reduce a 3 funciones:

  • initialize –> Como vimos esta función solo se ejecuta una vez, por lo que es el sitio ideal para leer las variables definidas en apps.yaml y definir qué sensor estaremos «escuchando» cuando cambie su estado para ejecutar la función «monitorizar»
  • monitorizar –> A esta función llegamos cuando hay un cambio en el estado del sensor, por tanto pondremos código para ver si se cumple el criterio de potencia<15 durante más de 5 minutos. Todo con sus parámetros, no usando números fijos, claro.
  • accion –> Función que realizará la acción sobre el enchufe (en este caso apagarlo)

Unas notas sobre convención de nombres: Python no dará error si no usas estas reglas, pero es muy recomendable seguirlas:

  • Los nombres de las clases suelen empezar por mayúsulas
  • Los nombres de las variables y las instancias de clases suelen empezar por minúsculas.
  • Las variables, por favor, que tengan un nombre que indique su significado: Mejor usar self.limite_minutos que self.t
  • Concatenar palabras para uso como variables se suele realizar mayoritariamente de dos formas (lo ideal es no mezclar):
    • limiteMinutos –> Como curiosidad llamada camel case porque las mayúsculas van quedando como jorobas de un camello entre las minúsculas.
    • limite_minutos –> Mi preferida

Todas las funciones que podemos usar en nuestras propias aplicaciones las podemos consultar con ejemplos de uso en la referencia de API de AppDaemon y en la API de Hass. Aparte, podemos usar todo lo que trae Python incluido y sus posibles paquetes.

Luego, como vimos, debemos modificar el fichero apps.yaml para añadir lo siguiente:

corteEnchufe_C:
  module: CorteEnchufe
  class: ClaseCorteEnchufe
  variables:
    nombre_enchufe: "Enchufe C"
    estado_enchufe: "switch.tz3000_g5xawfcq_ts0121_24c369fe_on_off"
    consumo_enchufe: "sensor.tz3000_g5xawfcq_ts0121_24c369fe_electrical_measurement"
    limite_minutos: 5
    limite_potencia: 15

Esta parte de declarar las variables de una aplicación se puede hacer de varias formas, pero a mí me gusta dejarlo así muy claro: Tenemos un nivel llamado «variables» y dentro un subnivel con el valor de cada una. Para hacer referencia en la aplicación hemos usado ambos nombres juntos y entre corchetes. Por ejemplo [variables][limite_minutos]

Ahora sí que nos podemos aprovechar de la parametrización que hemos realizado, para que se vea todo el sentido de hacer las cosas como las hemos hecho: Si por ejemplo tenemos 3 enchufes y queremos hacer esta automatización en todos ellos, lo único que tenemos que hacer es modificar nuestro fichero apps.yaml de esta manera:

corteEnchufe_A:
  module: CorteEnchufe
  class: ClaseCorteEnchufe
  variables:
    nombre_enchufe: "Enchufe A"
    estado_enchufe: "switch.enchufe_a_on_off"
    consumo_enchufe: "sensor.enchufe_a_electrical_measurement"
    limite_minutos: 5
    limite_potencia: 15

corteEnchufe_B:
  module: CorteEnchufe
  class: ClaseCorteEnchufe
  variables:
    nombre_enchufe: "Enchufe B"
    estado_enchufe: "switch.enchufe_b_on_off"
    consumo_enchufe: "sensor.enchufe_b_electrical_measurement"
    limite_minutos: 10
    limite_potencia: 15

corteEnchufe_C:
  module: CorteEnchufe
  class: ClaseCorteEnchufe
  variables:
    nombre_enchufe: "Enchufe C"
    estado_enchufe: "switch.enchufe_c_on_off"
    consumo_enchufe: "sensor.enchufe_c_electrical_measurement"
    limite_minutos: 5
    limite_potencia: 15

Elegante, ¿verdad?

Incluso hemos podido particularizar y decidir que el enchufe_b espere 10 minutos en vez de 5 minutos.

Si mañana mejoramos la aplicación en el fichero CorteEnchufe.py , de forma inmediata las 3 aplicaciones se verán modificadas sin realizar nada más, poque cada una de ellas crea una instancia de la misma clase.

Preguntas frecuentes sobre AppDaemon

¿Puede una aplicación de AppDaemon dejar sin funcionamiento mi sistema Home Assistant?

Por la forma en la que está desarrollado este addon, no es probable, pues su ejecución se desarrolla en un sandbox que no es más que una zona de memoria reservada e independiente del resto del sistema.

¿Dónde puedo encontrar más ejemplos de aplicaciones AppDaemon?

La primera opción debería ser siempre la propia documentación. En este caso tenéis varios ejemplos y algunos tutoriales.
En GitHub tambén podéis ver estos otros ejemplos.

¿Se pueden crear paneles de mando con AppDaemon?

Basado en AppDaemon existe otro proyecto llamado HADashboard, con el que se puden crear tableros de mando enfocados específicamente a tablets, con visualizaciones grandes y visualmente minimalistas:

Si os ha gustado el post y queréis aprender de otros temas, aquí os dejo como siempre una selección para principiantes y avanzados:

Deja un comentario