Cómo analizar datos de Home Assistant
Photo by Dan Meyers on Unsplash

Cómo analizar datos de consumo eléctrico con Home Assistant.

Análisis de datos con Home Assistant.
Hoy vamos a sacar conclusiones usando los datos de nuestro sistema Home Assistant, veremos en detalle cómo realizar el análisis de nuestro consumo eléctrico y nos adentraremos en el mundo de la visualización con Seaborn… con Paciencia

Hoy veremos una pincelada de varios temas: Por un lado veremos que esa base de datos que no hace más que crecer es una fuente muy interesante para extraer información que nos puede servir para crear nuevas automatizaciones o incluso para ahorrar dinero usando nuestro Home Assistant.

Por un lado empezaremos con lo básico, veremos brevemente como usar la herramienta de Google Colab en la que nos basaremos, con toda la potencia de python y sus paquetes, entre ellos Seaborn para representación, y sin tener que instalar nada en nuestro ordenador, conectando con nuestra base de datos y mirando algunas temperaturas a modo de ejemplo.

Luego veremos algo más complejo, analizando el volumen de una TV y agrupando dichos datos en función de la hora del día.

Finalmente tomaremos los datos de nuestro consumo eléctrico e iremos paso a paso transformando dichos valores en algo con sentido que podremos graficar y extraer conclusiones de este análisis de consumo de energía.

Os dejo algunos enlaces del tipo de sensores y dispositivos que veremos y con los que trabajaremos:

El primero de ellos es el conocido Shelly EM, siendo el medidor de consumo más vendido en domótica, por sus prestaciones. Incluye una pinza de 50A más que suficiente para la mayoría de hogares. Es un dispositivo de calidad a un precio más que asequible:

Otro de los sensores que usaremos son los medidores de temperatura y humedad de Xiaomi, uno de los sensores más baratos del mercado usando bluetooth de baja energía (BLE) por lo que la batería durará años. Aquí tenéis una oferta de pack:

Usaremos a modo de ejemplo una Smart TV Samsung conectada a nuestra red, por lo que además de poder controlarla, podremos obtener los datos de la fuente seleccionada y el nivel de volumen:

Si necesitas empezar a montar desde cero tu sistema de domótica, te recomiendo nuestro post sobre instalación de Home Assistant en Raspberry PI, y la integración de dispositivos zigbee.

Vamos a hacer honor al dicho «Torturar los datos hasta que confiesen»

LA BASE DE DATOS EN HOME ASSISTANT

Ya vimos en este otro post, cómo copiar nuestra base de datos por defecto desde Home Assistant a nuestro ordenador, y cómo opimizarla para que guarde solo aquello que nos interese y poder almacenar muchos más días sin temor a quedarnos sin espacio libre.

Nosotos usaremos la opción de base de datos que viene por defecto (sqlite) pero es muy fácil adaptar el análisis a otras bases de datos, pues las consultas que realizaremos quedarán igual y solo tendréis que cambiar la conexión a la base de datos en cada caso.

A modo de resumen, en aquel post nosotros usamos el addon File Editor, navegando a la carpeta config y descargando el fichero «home-assistant_v2.db«

análisis de consumo de energía

Depende del tamaño tardará más o menos, pero ya tendremos dicho fichero en nuestro ordenador.

Usando Google Colab

Hoy en día existen muchas y muy buenas herramientas profesionales para análisis de datos, tanto en Python como en R. La elección dependerá de muchos factores (conocimiento previo, fin del análisis, etc …). Al fin y al cabo una simple hoja de cálculo también nos puede ayudar en muchas ocasiones.

Mi recomendación hoy por hoy es aprender a usar herramientas ampliamente establecidas, basadas en Python con librerías bien documentadas y con multitud de ejemplos (Pandas, NumPy, …). Si no os suena todo esto, no os preocupéis, pues los pasos son muy simples y no hace falta ser un experto para empezar a obtener resultados como veremos.

La herramienta que usaremos: Google Colab, posee muchas ventajas y nos va a permitir usar Jupyter Notebooks en línea desde el navegador, sin tener que instalar nada en nuestro ordenador y con toda la potencia de Python. Solo necesitaremos una cuenta en Google.

Aquí tenéis cómo empezar, pero mejor dejadlo para el final y meteros de lleno, pues os mostraré nuestro ejemplo paso a paso.

Jupyter Notebook, es una interface gráfica donde podremos insertar texto y código en un mismo entorno, a la vez que podemos ejecutarlo. De esta manera es una herramienta ideal para mostraros cómo analizar cualquier tipo de datos, en este caso extraídos con nuestro sistema Home Assistant.

Una vez tengamos acceso a Google Colab, veréis que os crea una carpeta en vuestro Google Drive para guardar los notebooks:

Crearemos un nuevo notebook para nuestro análisis. Al abrir dicho notebook, en el menú de la parte izquierda, subiremos el fichero de nuestra base de datos:

análisis de consumo de energía

También existe la opción de subirlo previamente a GoogleDrive y mapearlo. La segunda opción tiene la ventaja de no perderlo entre sesiones, por lo que no será necesario subirlo cada vez que queramos trabajar con él. Es la opción que hemos elegido en estos ejemplos.

Preparando librerías y conexión

Lo primero que necesitamos es importar las librerías necesarias:

# Paquetes necesarios
import pandas as pd
import seaborn as sns
import sqlite3

En cada celda de código, tendremos la opción de ejecutarla:

análisis de consumo de energía

Y en cualquier momento, si veis algún problema siempre podemos resetear la sesión y ejecutar todas las celdas desde el menú Runtime:

análisis de consumo de energía

Al final del post os dejo un enlace con el notebook jupyter completo que hemos usado por si lo necesitáis adaptar.

La conexión con nuestra base de datos es muy simple desde la librería sqlite3. En nuestro caso la hemos subido previamente a Google Drive, por lo que debemos especificar la ruta completa:

# Creamos la conexión con la base de datos de Home Assistant
# Cambiar a vuestro propio path y adaptar código a vuestros attributes
con = sqlite3.connect('/content/drive/MyDrive/Colab Notebooks/Explore HA DB sqlite/home-assistant_v2.db') 

JUGANDO CON LAS TEMPERATURAS

Una vez que tenemos conexión con nuestra base de datos, veamos un ejemplo muy sencillo para acceder a los registros de sensores de temperatura que nos dará una idea de las consultas que podemos utilizar.

Sensores de temperatura. Datos en bruto

En un único comando pasaremos en forma de cadena de texto la consulta SQL que queremos, en este caso todos los registros entre dos fechas, de sensores cuyo nombre contenga «ble_temperature»:

# Lectura de datos a un dataframe usando una consulta SQL contra la base de datos de Home Assistant
# Filtrando para una semana en concreto y sensores que contengan "ble_temperature"
temperature_df=pd.read_sql_query('SELECT last_changed,entity_id,state FROM states WHERE last_changed BETWEEN "2021-05-01" AND "2021-05-07" and entity_id LIKE "%ble_temperature%"',con)

Toda la query queda encerrada entre comillas simples (‘) y dentro de ésta, cuando lo hemos necesitado, comillas dobles(«)

Representando el número de lecturas

Podemos ver algunos registros de este dataframe creado usando la función head():

# Vemos las primeras filas del dataframe
temperature_df.head()

Vemos la fecha/hora, el nombre del sensor en cada fila y por supuesto el dato de la temperatura.

Y por fin podemos representar algún gráfico con Seaborn. En este caso un gráfico de categorías:

# Nuestro primer gráfico, contando el número de observaciones de cada sensor
g1=sns.catplot(data=temperature_df,x='entity_id', kind="count", aspect=3)
g1.set_xticklabels(rotation=90)
análisis de sensores de temperatura

Como véis con muy pocas líneas de código tenemos acceso a todos los datos que nuestro sistema Home Assistant ha grabado y que pensábamos que no tenían mucha utilidad.

Empieza el juego…

JUGANDO CON EL VOLUMEN DE NUESTRA TV

Nuestra smart TV es una integración muy común en los sistemas domóticos de hoy en día, bien sea para encender o apagar automáticamente basados en presencia, o para mostrar mensajes.

Nosotros analizaremos la intensidad de volumen que ponemos en nuestra TV en función de la fuente seleccionada para sacar algunas conclusiones.

Volumen de la TV. Datos en bruto

Como siempre empezamos realizando una consulta a nuestra base de datos sqlite cuya conexión hemos establecido en un paso anterior. En nuestro caso concreto, buscamos directamente la entidad de Home Assistant llamada «media_player.tv_samsung«:

# Creamos el dataframe con los datos del sensor específico
# En este caso nos interesa la fecha/hora, y los atributos
samsung_df=pd.read_sql_query('SELECT last_changed,entity_id,attributes FROM states WHERE entity_id="media_player.tv_samsung"',con)

Serán necesarios los campos «last_changed» y «attributes» para conocer la fecha/hora así como todos los datos relativos a esta entidad.

Vemos un ejemplo de contenido del campo attribute con los valores de volumen y fuente que veremos cómo extraer en el siguiente punto:

{"source_list": ["TV", "HDMI", "Prime Video", "Netflix", "Universal Guide", "Rakuten TV", "YouTube", "e-Manual", "Internet", "Gallery", "Apple TV", "PrivacyChoices", "Samsung Promotion", "DAZN", "Apple Music", "Calm", "alacarta RTVE", "Disney+"], "volume_level": 0.11, "is_volume_muted": false, "media_content_type": "video", "media_title": "TV/HDMI", "app_id": "TV/HDMI", "source": "TV/HDMI", "ip_address": "192.168.X.X", "device_model": "UE43RU7105KXXC", "device_name": "[TV] Samsung 7 Series (43)", "friendly_name": "TV Samsung", "icon": "mdi:television", "supported_features": 24509, "device_class": "tv"

Obtenemos la fuente seleccionada y el horario de uso

Queremos ahora extraer el dato del volumen dentro del campo attributes. Para ello usaremos la función split, que buscará un delimitador en todo el campo para dividir. Normalmente el delimitador es un único carácter (una coma, un tabulador, un retorno de carro, …).

El truco aquí será utilizar una cadena como primer delimitador. Esa cadena será «volume level» con sus comillas incluidas. Esto nos dividirá el campo en 2 columnas, estando el dato de volumen en la segunda parte.

Luego usaremos la coma (,) como segundo delimitador para dejar únicamente el dato numérico tipo float:

# Dividiremos en 2 columnas usando como delimitador el texto "volume_level:", con lo que el dato numérico nos quedará en la segunda columna [1].
# Volvemos a dividir ahora usando como delimitador la coma ',' y esta vez escogemos la primera columna generada [0].
# Por último convertimos a tipo float.
# Ahora nuestro dataframe contiene los niveles de volumen

vol=samsung_df['attributes'].str.split('"volume_level":', expand=True)
vol=vol[1].str.split(',', expand=True)
vol=vol[0].astype(float)

vol.describe()

Al usar la función describe() de un dataframe con datos numéricos, se nos presenta un breve resumen de los cuartiles, mínimos y máximos, así como la media y la desviación típica.

Apunte: Como ya aprendimos en este otro post, si asumimos una distribución normal (no lo he comprobado), veríamos que queda definida como N(0.090967,0.065467).

Seguimos el mismo procedimiento para extraer la fuente seleccionada en nuestra smart-TV:

source=samsung_df['attributes'].str.split('"source":', expand=True)
source=source[1].str.split(',', expand=True)
source=source[0]
source

Con la función value_counts() vemos cuántos datos tenemos de cada fuente, lo que ya nos dará una primera idea de cuál es la fuente más usada:

Ahora nos queda extraer el dato de la hora. Por ahora, para realizar el análisis nos centramos en la hora, descartando el día, año, minuto y segundo:

# Usamos la fecha/hora convirtiendo desde string y por ahora solo nos interesa la hora (%H)
hora=samsung_df['last_changed'].astype('datetime64').dt.strftime('%H').astype(int)

Y unimos todos los datos que tenemos por separado (la hora, el volumen y la fuente) en un único dataframe:

test=pd.concat([hora,vol, source],axis=1)
test.columns = ['hora','volumen','fuente']
test

Representado los datos de uso de nuestra TV

Ahora que ya tenemos nuestro dataframe completo vamos con las representaciones usando Seaborn.

Primero representamos volumen vs fuente:

# Graficamos volumen vs fuente
g2=sns.catplot(x='fuente', y='volumen', data=test, aspect=3)
g2.set_xticklabels(rotation=90)
análisis de consumo de volumen smart-tv

También interesante volumen vs hora:

# Graficamos volumen vs hora
g3=sns.catplot(x='hora', y='volumen', data=test, aspect=2)
g3.set_xticklabels(rotation=90)
análisis de consumo de volumen smart-tv

Y por último hora vs fuente:

# Graficamos hora vs fuente
g4=sns.catplot(x='fuente', y='hora', data=test, aspect=3)
g4.set_xticklabels(rotation=90)
análisis de consumo de volumen smart-tv

Podemos incluso añadir una leyenda para distinguir en alguna de estas gráficas de 2 dimensiones la tercera dimensión. Por ejemplo en gráficas del tipo volumen vs hora podemos colorear según la fuente de TV seleccionada:

# Categorizamos según la fuente
g5=sns.relplot(x="hora", y="volumen", hue="fuente", data=test, aspect=3)
análisis de consumo de volumen smart-tv

Otra de las posibilidades, que seguro usaremos más de una vez, es poder filtrar. Vamos a ver primero cuáles son las fuentes más usadas:

# Vamos a filtrar a aquellas fuentes de las que tengamos al menos 10 datos, aplicando un filtro lambda
test2=test.groupby('fuente').filter(lambda x: len(x) >= 10)

# Vemos el nuevo conteo de categorías de fuentes
test2['fuente'].value_counts()

Está claro: Vemos básicamente la TV (antena en este caso, no HDMI), Netflix y Prime.

Vamos por tanto a graficar solo las 3 fuentes principales:

# Graficamos con las 3 fuentes principales
g6=sns.relplot(x="hora", y="volumen", hue="fuente", data=test2, aspect=3)
análisis de consumo de volumen smart-tv

Y ahora llegamos a la interpretación de estos resultados y sus posibles usos.

Análisis de uso de TV y conclusiones

Hemos visto en el paso anterior cómo podemos fácilmente obtener gráficas de nuestros datos de uso de nuestra smart-tv, y cómo filtrar dichos datos.

Obviamente dichas técnicas pueden usarse para analizar cualquier patrón o comportamiento que nuestro sistema domótico sea capaz de almacenar.

Ningún análisis de datos está completo sin sacar algunas conclusiones y utilidades. No olvidemos que nuestros datos son solo nuestros y valen mucho. Solo debemos sacarle partido.

En el caso concreto del análisis de uso efectuado de este electrodoméstico, vemos claramente que

  • El uso principal se centra en 3 fuentes: TV, Netflix y Prime
  • El volumen se establece más alto cuando se selecciona TV que al seleccionar Netflix
  • Las horas de mayor volumen son alrededor de las 12:00 y alrededor de las 17:00

Estos datos, junto con los valores numéricos nos pueden servir para establecer el volumen de manera automática en función de la hora y la fuente seleccionada con un grado muy alto de acierto, por ejemplo usando un volumen medio según los datos históricos.

ANALIZANDO EL CONSUMO ELÉCTRICO

Llegamos ya a la guinda del pastel, pues una de las utilidades del análisis de datos es ser conscientes de nuestro propio comportamiento, y hay pocos sensores que den más información de dicho comportamiento que un medidor de consumo como el Shelly EM.

Nuestros datos de consumo en bruto

Como siempre empezamos consultando nuestra base de datos para tener los datos sin procesar a la vez que eliminamos datos no numéricos por si acaso:

# Consultamos nuestra base de datos de Home Assistant usando SQL
consumo_df=pd.read_sql_query('SELECT last_changed,entity_id,state FROM states WHERE entity_id="sensor.shelly_shem_5e1a16_2_total_consumption"',con)

# Filtramos quitando posibles valores no numéricos del campo state
consumo_df = consumo_df[pd.to_numeric(consumo_df['state'],errors='coerce').notna()]

consumo_df.head()

Así se ven los datos extraídos de la tabla:

análisis de consumo de energía

Como vemos, en cada lectura el contador va aumentando el valor (state) medido en kwh.

Montando nuestro dataframe de trabajo

Por ahora, para analizar el comportamiento nos centramos en los siguientes datos: fecha, día de la semana (este lo calculamos a partir de la fecha), la hora y por supuesto el valor del contador:

# Solo nos interesa:
# La fecha 
# El día de la semana, weekday --> 0=lunes .. 6=domingo
# La hora
# El valor del contador de energía

fecha_consumo = consumo_df['last_changed'].astype('datetime64').dt.strftime('%Y.%m.%d')
diasemana_consumo = consumo_df['last_changed'].astype('datetime64').dt.weekday
hora_consumo = consumo_df['last_changed'].astype('datetime64').dt.strftime('%H').astype(int)
contador_consumo = consumo_df['state'].astype(float)

Ahora nos toca unir estos dataframes individuales para formar uno único:

# Unimos todo para formar un nuevo dataframe
test1=pd.concat([fecha_consumo,diasemana_consumo,hora_consumo, contador_consumo],axis=1)
test1.columns = ['fecha','diasemana','hora','contador']

test1.head()

Siendo éste el resultado mostrando las 5 primeras filas:

análisis de consumo de energía

Cálculo de la energía consumida

Como vemos, el contador en kwh es un valor siempre en aumento, como es lógico

El cálculo que haremos para averiguar la energía consumida en cada hora requiere que agrupemos, y escojamos el mínimo valor del contador y el máximo en cada hora concreta (llamadas funciones de agregación por dar un valor único basado en otros valores):

# Agrupamos para cada fecha, día de la semana y hora, y agregamos el valor máximo (valor final de contador) y el mínimo (valor inicial en esa hora del contador)
# Creamos de esta manera una pivot table

test2 = test1.groupby(['fecha','diasemana','hora'])['contador'].agg(cnt_ini="min", cnt_fin="max")

test2.head()

Veamos el resultado:

análisis de consumo de energía

Al agrupar, hemos creado lo que se conoce como una pivot table, donde ahora los índices son etiquetas de nuestra tabla.

El consumo de enería en cada hora será el máximo valor del contador en esa hora menos el mínimo valor. Una vez calculada la energía en cada hora, nos deshacemos de los valores de mínimos y máximos pues ya no los necesitamos:

# Añadimos la columna energia como diferencia entre el valor final y el valor inicial del contador
test2['energia']= test2['cnt_fin'] - test2['cnt_ini']

# Una vez calculada la energía ya no necesitamos los valores puntuales del contador
test2.drop(['cnt_ini', 'cnt_fin'], axis=1, inplace=True)

test2.head()

Ahora ya nos vamos acercando a lo que necesitamos:

análisis de consumo de energía

Todavía nos queda una agrupación y agregación más, pues según el nuevo marco tarifario desde junio de 2021, el precio de la electricidad se divide en tramos en función del día de la semana y de la hora, por tanto volveremos a agrupar en función de esos dos campos y agregaremos los datos de energía de cada hora calculando la media para que sea más representativo:

# Por cada día de la semana y hora, obtenemos el consumo medio correspondiente
test3=test2.groupby(['diasemana','hora'])['energia'].agg(consumo="mean")

test3.head()
análisis de consumo de energía

Bueno, esto ahora sí parece que puede estar casi listo para representar… vamos manos a la obra

Representación del consumo eléctrico

Para realizar el análisis de consumo de energía, lo primero que haremos es utilizar la función unstack para volver a tener un dataframe en vez de una tabla pivot, y resetear los índices para que sean más representativos (días de la semana y horas):

# Finalmente convertimos de nuevo a un dataframe normal desde la tabla pivot
test3=test3.unstack(level=0)

# Ponemos todo un poco más amigable renombrando los índices y las columnas
test4=test3.reset_index(drop=True)
test4.columns = ['lun','mar','mie','jue','vie','sáb','dom']
test4.index=['00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23']

test4.head()

Y este es el último dataframe que nos queda para usarlo directamente en las representaciones:

análisis de consumo de energía

Esto ya es otra cosa… vamos a dibujarlo con seaborn:

# Ya tenemos todo lo necesario para representar el mapa de nuestro consumo

# Establecemos un buen tamaño
sns.set(rc={'figure.figsize':(12,12)})

# Nuestra paleta favorita de colores
sns.color_palette("magma", as_cmap=True)

# Y representamos, junto con los valores medios de consumo y ocultando la barra de color
g7=sns.heatmap(test4, annot=True, fmt="0.2f",cbar=False)

Hemos usado un gráfico heatmap, anotando los valores con 2 decimales encima de cada casilla de día de la semana y hora. Lo representamos y luego sacamos conclusiones:

análisis de consumo de energía

Análisis de consumo de energía

Nos ha quedado un gráfico muy chulo sobre el análisis de consumo de energía que representa el consumo medio en un par de semanas de datos en cada tramo de cada día de la semana. La gama de colores va desde más oscuro (valores bajos de consumo) hasta los más claros (valores altos).

  • Queda calro que entre semana nuestros consumos comienzan a las 10 de la mañana hasta la 23:00 aproximadamente, con una excepción de la tarde del viernes donde no tenemos tanto consumo.
  • Por las mañanas, antes de las 10, el mayor consumo se debe a preparación de desayunos entre las 5 y 6 AM

Teniendo en cuenta que el tramo punta de la nueva tarifa eléctica PVPC con discriminación es entre las 10:00 y las 14:00 y entre las 18:00 y 22:00, vemos que hay mucho margen de mejora, estando los tramos baratos (de 00:00 a 08:00) muy desaprovechados.

A partir de aquí, cada análisis que hagáis será muy particular y las conclusiones variarán, pero las herramientas usadas son muy potentes, y la visualización de datos es básica para obtener dichas conclusiones.

En otro tipo de instalaciones con Home Assistant (docker por ejemplo) puede existir la opción de conexión directa de jupyter a la base de datos, de forma concurrente con el sistema.

Lo mejor de todo es que una vez hayáis construido vuestro notebook de jupyter, solo tendréis que volver a cargar vuestro fichero de base de datos y dar a ejecutar de nuevo. En cuestión de segundos aparecerá un nuevo gráfico de análisis de consumo de energía con los datos actualizados.

Os dejo enlace al arhivo Explore HA DB.ipynb y las ofertas de los dispositivos que hemos usado en esta ocasión:

Un comentario

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *