
La calidad del aire en casa influye directamente en nuestra salud, concentración y bienestar diario. Gracias a Home Assistant, es posible no solo medir diferentes parámetros ambientales, sino también representarlos de una forma clara, intuitiva y visual. Hoy te traigo un ejemplo de panel de calidad del aire que integra múltiples sensores y presenta los datos en un tablero muy fácil de entender.
📊 Un panel claro y directo
El panel está diseñado con tarjetas personalizadas (custom:button-card) que muestran el estado de cada variable ambiental con colores y animaciones. De un vistazo puedes saber si el aire es seguro o si hay algún peligro.
En la parte superior se muestra un indicador general:
- ✅ SEGURO → Todos los valores están dentro de los umbrales saludables.
- ⚠️ PELIGRO → Algún parámetro está fuera de lo recomendado.
🌡️ Parámetros monitorizados
Cada tarjeta representa un sensor, con colores que cambian según los valores:
- 🌡️ Temperatura
- 💧 Humedad relativa
- 🌫️ CO₂ (ppm)
- 🔬 Partículas (PM1.0, PM2.5 y PM10)
- ☣️ Formaldehído (HCHO)
- 🧪 Compuestos Orgánicos Volátiles (COVs)
- Estado de batería y pantalla del detector
🧭 Código completo del panel


Para que el código funcione tenéis que instalar https://github.com/custom-cards/button-card mediante HACS
Código de la parte izquierda donde aparecen los iconos:
type: grid
cards:
- type: custom:button-card
name: " "
show_state: false
show_icon: false
state:
- value: "off"
operator: "=="
name: Habitacion Grande <br> ⚠️ PELIGRO
styles:
card:
- background-color: red
- color: white
- value: "on"
operator: "=="
name: Habitacion Grande <br> ✅ SEGURO
styles:
card:
- background-color: green
- color: white
tap_action:
action: none
hold_action:
action: none
entity: binary_sensor.calidad_del_aire_habitacion_grande
show_entity_picture: false
styles:
card:
- height: 80px
- font-size: 24px
- font-weight: bold
- text-transform: none
name:
- font-weight: bold
- font-size: 20px
- text-align: center
- width: 100%
- type: grid
columns: 2
square: false
cards:
- type: custom:button-card
entity: sensor.air_detector_temperatura
icon: mdi:thermometer
name: 🌡️ Temperatura
show_state: true
show_units: true
state:
- operator: <
value: 21
color: blue
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(0, 0, 255, 0.1)
- operator: <=
value: 26
color: green
styles:
card:
- background-color: rgba(0, 128, 0, 0.1)
- operator: ">"
value: 26
color: red
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 0, 0, 0.1)
styles:
state:
- font-size: 24px
- font-weight: bold
card:
- "--blink-animation": |
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
- type: custom:button-card
entity: sensor.air_detector_humedad
icon: mdi:water-percent
name: 💧 Humedad
show_state: true
show_units: true
state:
- operator: <
value: 40
color: blue
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(0, 0, 255, 0.1)
- operator: <=
value: 60
color: green
styles:
card:
- background-color: rgba(0, 128, 0, 0.1)
- operator: ">"
value: 60
color: red
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 0, 0, 0.1)
styles:
state:
- font-size: 24px
- font-weight: bold
card:
- "--blink-animation": |
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
- type: custom:button-card
entity: sensor.air_detector_dioxido_de_carbono
icon: mdi:molecule-co2
name: 🌫️ CO₂
show_state: true
show_units: true
state:
- operator: <
value: 800
color: green
styles:
card:
- background-color: rgba(0, 128, 0, 0.1)
- operator: <=
value: 1200
color: orange
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 165, 0, 0.1)
- operator: ">"
value: 1200
color: red
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 0, 0, 0.1)
styles:
state:
- font-size: 24px
- font-weight: bold
card:
- "--blink-animation": |
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
- type: custom:button-card
entity: sensor.air_detector_pm25
icon: mdi:blur
name: 🔬 PM1.0
show_state: true
show_units: true
state:
- operator: <
value: 10
color: green
styles:
card:
- background-color: rgba(0, 128, 0, 0.1)
- operator: <=
value: 30
color: orange
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 165, 0, 0.1)
- operator: ">"
value: 30
color: red
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 0, 0, 0.1)
styles:
state:
- font-size: 24px
- font-weight: bold
card:
- "--blink-animation": |
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
- type: custom:button-card
entity: sensor.air_detector_pm2_5
icon: mdi:blur
name: 🔬 PM2.5
show_state: true
show_units: true
state:
- operator: <
value: 10
color: green
styles:
card:
- background-color: rgba(0, 128, 0, 0.1)
- operator: <=
value: 30
color: orange
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 165, 0, 0.1)
- operator: ">"
value: 30
color: red
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 0, 0, 0.1)
styles:
state:
- font-size: 24px
- font-weight: bold
card:
- "--blink-animation": |
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
- type: custom:button-card
entity: sensor.air_detector_pm10
icon: mdi:blur
name: 🔬 PM10
show_state: true
show_units: true
state:
- operator: <
value: 10
color: green
styles:
card:
- background-color: rgba(0, 128, 0, 0.1)
- operator: <=
value: 30
color: orange
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 165, 0, 0.1)
- operator: ">"
value: 30
color: red
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 0, 0, 0.1)
styles:
state:
- font-size: 24px
- font-weight: bold
card:
- "--blink-animation": |
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
- type: custom:button-card
entity: sensor.air_detector_formaldehido
icon: mdi:chemical-weapon
name: ☣️ Formaldehído
show_state: true
show_units: true
state:
- operator: <
value: 0.05
color: green
styles:
card:
- background-color: rgba(0, 128, 0, 0.1)
- operator: <=
value: 0.1
color: orange
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 165, 0, 0.1)
- operator: ">"
value: 0.1
color: red
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 0, 0, 0.1)
styles:
state:
- font-size: 24px
- font-weight: bold
card:
- "--blink-animation": |
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
- type: custom:button-card
entity: sensor.air_detector_compuestos_organicos_volatiles
icon: mdi:beaker
name: 🧪 COVs
show_state: true
show_units: true
state:
- operator: <
value: 200
color: green
styles:
card:
- background-color: rgba(0, 128, 0, 0.1)
- operator: <=
value: 500
color: orange
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 165, 0, 0.1)
- operator: ">"
value: 500
color: red
styles:
icon:
- animation: blink 2s infinite
card:
- background-color: rgba(255, 0, 0, 0.1)
styles:
state:
- font-size: 24px
- font-weight: bold
card:
- "--blink-animation": |
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
- type: custom:button-card
entity: switch.air_detector_screen_sleep
icon: mdi:monitor
name: 🖥️ Screen Sleep
show_name: true
show_state: true
styles:
card:
- height: 73px
- background-color: rgba(128, 128, 128, 0.1)
name:
- font-size: 12px
- font-weight: bold
state:
- font-size: 14px
- font-weight: bold
- type: custom:button-card
entity: sensor.air_detector_bateria
icon: mdi:battery
name: 🔋 Batería
show_name: true
show_state: true
show_units: true
styles:
card:
- height: 73px
- background-color: rgba(128, 128, 128, 0.1)
name:
- font-size: 12px
- font-weight: bold
state:
- font-size: 14px
- font-weight: bold
Código de la parte izquierda donde aparece la leyenda:
type: grid
cards:
- type: markdown
content: |-
- **✅ SEGURO**: TODOS los valores están en verde
- **⚠️ PELIGRO**: ALGUNO de los valores no está en verde
### 🌡️ Temperatura
- **AZUL**: < 21°C
- **VERDE**: 21°C - 26°C
- **ROJO**: > 26°C
### 💧 Humedad
- **AZUL**: < 40%
- **VERDE**: 40% - 60%
- **ROJO**: > 60%
### 🌫️ CO₂
- **VERDE**: < 800 ppm
- **NARANJA**: 800 - 1200 ppm
- **ROJO**: > 1200 ppm
### 🔬 Partículas (PM1.0, PM2.5, PM10)
- **VERDE**: < 10 µg/m³
- **NARANJA**: 10 - 30 µg/m³
- **ROJO**: > 30 µg/m³
### ☣️ Formaldehído
- **VERDE**: < 0.05 mg/m³
- **NARANJA**: 0.05 - 0.1 mg/m³
- **ROJO**: > 0.1 mg/m³
### 🧪 COVs
- **VERDE**: < 200 ppb
- **NARANJA**: 200 - 500 ppb
- **ROJO**: > 500 ppb
title: Leyenda de Colores - Umbrales