Hoy vamos a integrar Ikea VINDRIKTNING 2.0 en HA, una versión mejorada con más funciones de este magnífico hack!
Índice
Ikea VINDRIKTNING 2.0
Ha pasado muy poco tiempo desde que te enseñé cómo había «hackeado» este dispositivo. Siendo sincero, no hice nada diferente a lo que puedas consultar en los últimos años. Y la verdad es que me quedé varios días revisando mi cajón de placas y sensores, pensando en cómo podía incorporar algunas de las funciones o componentes más modernos a este proyecto tan popular.

De esta forma es como he llegado a esta guía para integrar Ikea VINDRIKTNING 2.0 en HA, un dispositivo con las siguientes características:
- Integración del dispositivo en Home Assistant con en ESPHome.
- Expone como sensores la concentración de partículas PM2.5, la temperatura y la humedad.
- Control pleno del LED RGB, que puedes usar como quieras.
- Detecta presencia y movimiento en la estancia.
- Puedes utilizarlo como Bluetooth Proxy con Bermuda para la detección de presencia personalizada por habitaciones.
- El dispositivo utiliza la carcasa original del dispositivo.
Requisitos previos
Para integrar Ikea VINDRIKTNING en HA previamente necesitas:
- Un Ikea VINDRIKTNING.
- Una placa ESP32-S3 Matrix. Se trata de una variante de placa ESP32 que nos va a permitir incorporar algunas funciones más a nuestro dispositivo. Además, gracias a su forma y tamaño encaja como un guante en la carcasa!
- Un sensor AHT10, para incorporar la información de temperatura y humedad a nuestro dispositivo.
- Un sensor LD2410C, para detectar presencia y movimiento en la estancia.
- Un cable USB-C para alimentar la placa de DATOS (con un cable de carga no vas a poder instalar el software).
- Haber instalado ESPHome en Home Assistant.
Conexión de los componentes
Lo primero que vamos a hacer para integrar Ikea VINDRIKTNING en HA es abrir la carcasa del sensor. Para ello tienes que quitar los 4 tornillos que hay en las esquinas y hecho esto ya podrás retirar (con cuidado) la cubierta frontal. Desconecta los conectores de la placa tirando suavemente de ellos para trabajar más cómodamente.

Fíjate en la placa que trae el dispositivo, por la parte opuesta al puerto USB-C, y observarás una serie de pines expuestos. En estos pines vamos a soldar tres cables que irán a nuestra placa ESP.
- El cable de alimentación (rojo) irá al pin identificado con +5V
- El cable de tierra (negro) irá al pin identificado con GND
- El cable de datos (verde) irá al pin identificado con REST

Lo siguiente es observar el ‘pinout’ (o distribución de pines) de la placa. En mi caso voy a utilizar una placa ESP32-S3 Matrix y este es su ‘pinout’.

El esquema de conexiones que vamos a seguir es el siguiente:
- El cable de alimentación (en mi caso, el rojo) irá al pin 5V
- El cable de tierra (en mi caso, el negro) irá al pin GND
- El cable de datos (en mi caso, el verde) irá al pin GP7
El sensor AHT10 seguirá este esquema de conexiones:
- El pin ‘VIN’ irá al pin 3V3 de la placa.
- El pin ‘GND’ irá al pin al pin GND de la placa.
- El pin ‘SCL’ irá al GP6 de la placa.
- El pin ‘SDA’ irá al pin GP5 de la placa.
Por último, el sensor LD2410C se conectará a la placa con el siguiente esquema de conexiones:
- VCC irá conectado al pin de alimentación 5v de la placa.
- GND irá conectado al pin GND (‘ground’ o tierra) de la placa.
- RX irá conectado al GP4 de la placa.
- TX irá conectado al pin GP3 de la placa.
- No realizaremos ninguna conexión al pin OUT.
Por tanto, el esquema completo de conexiones será el siguiente:

Adaptaciones de la carcasa
Como te decía en la introducción, al integrar Ikea VINDRIKTNING 2.0 en HA no vas a necesitar ninguna carcasa o accesorio adicional, ni fabricar ninguna pieza con tu impresora 3D. No obstante, si vamos a realizar dos pequeños ajustes.
El primero de ellos es «anular» los LED que el dispositivo trae por defecto, ya que los que vamos a utilizar son los de la matriz LED que trae nuestra placa. No obstante, lo vamos a hacer de una forma muy sencilla, sin alterar la placa que viene de serie con el dispositivo. Lo único que necesitas es pegar un poco de cinta aislante opaca.

Lo siguiente que vamos a hacer es crear una pequeña endidura en la parte central para interponer nuestra placa entre la carcasa y la placa original de Ikea. Puedes usar un cutter, el soldador o cualquier herramienta que te parezca adecuada. Ten paciencia y cuidado, pero no te preocupes por el acabado ya que no se apreciará desde el exterior.


Configuración de ESPHome
Antes de volver a montar el dispositivo, vamos a flashear la placa y comprobar que todo funciona:
- Conecta la placa a tu ordenador a través del puerto USB-C.
- En Home Assistant, accede al complemento de ESPHome desde el menú lateral y pulsa en “New device”. Pulsa en “Continue” y dale un nombre (por ejemplo, “Ikea Vindriktning»).
- Pulsa en “Next” y a continuación selecciona la opción “ESP32-S3” como tipo de dispositivo. Verás que en el fondo se ha creado un nuevo dispositivo.
- Pulsa en “Skip” y haz clic en el enlace “Edit” del bloque correspondiente al dispositivo que acabas de crear. Copia el código que aparece y consérvalo, ya que vas a necesitar alguna parte del mismo.
- Copia el siguiente código y úsalo para reemplazar el código anterior en ESPHome.
esphome:
name: ikea-vindriktning-v2
friendly_name: Ikea Vindriktning v2
on_boot:
then:
- light.turn_on:
id: led_matrix
brightness: 60%
effect: "Arcoiris"
- delay: 5s
- light.turn_off: led_matrix
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "be915ZxV7sjksd093290ndsXKyQtkEDNaJPI5pKº122"
ota:
- platform: esphome
password: "38484nndc752weqb460f4a0"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Ikea-Vindriktning-V2"
password: "gdxFAoV8mLip"
captive_portal:
# Activación del BT Proxy para utilizar Bermuda#
esp32_ble_tracker:
scan_parameters:
interval: 320ms
window: 300ms
active: true
bluetooth_proxy:
active: true
################################################
light:
- platform: esp32_rmt_led_strip
rgb_order: RGB
pin: GPIO14
num_leds: 64
chipset: ws2812
name: "LED"
id: led_matrix
color_correct: [30%, 30%, 30%]
icon: mdi:led-strip-variant
effects:
- addressable_rainbow:
name: "Arcoiris"
- pulse:
name: "Fade"
- addressable_lambda:
name: "Fuego"
update_interval: 50ms
lambda: |-
static uint8_t heat[8][8];
static int cooling = 60; // Reduce el enfriamiento para mantener más brillo
static int sparking = 130; // Aumenta las chispas para más dinamismo
// Desplazamiento del calor hacia arriba
for (int x = 0; x < 8; x++) {
for (int y = 7; y > 0; y--) {
heat[x][y] = (heat[x][y - 1] * 2 + heat[x][y]) / 3;
}
}
// Generar nuevas chispas en la parte inferior
for (int x = 0; x < 8; x++) {
if (random_uint32() % 255 < sparking) {
heat[x][0] = 255;
}
}
// Aplicar color a la matriz
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
uint8_t color = heat[x][y] - (random_uint32() % cooling);
if (color < 0) color = 0;
Color col;
if (color > 180) {
col = Color(255, color - 50, 0); // Rojo intenso con un poco de naranja
} else if (color > 120) {
col = Color(255, color - 80, 0); // Rojo-naranja
} else if (color > 60) {
col = Color(200, color - 40, 0); // Naranja oscuro
} else {
col = Color(120, 20, 0); // Rojo apagado
}
it[8 * y + x] = col;
}
}
- addressable_lambda:
name: "Lluvia"
update_interval: 100ms
lambda: |-
static uint8_t rain[8][8] = {0}; // Matriz para el estado de la lluvia
// Desplazar gotas hacia abajo con efecto de goteo disperso
for (int x = 0; x < 8; x++) {
for (int y = 7; y > 0; y--) {
// Probabilidad de que la gota desaparezca antes de llegar al final
if (random_uint32() % 8 < 2) {
rain[x][y] = 0;
} else {
rain[x][y] = rain[x][y - 1]; // Mueve la gota una posición abajo
}
}
// Control estricto para menos gotas encendidas al mismo tiempo
if (random_uint32() % 18 == 0) { // Reduce la frecuencia de aparición
rain[x][0] = 200 + (random_uint32() % 55); // Azul claro con variación de brillo
} else {
rain[x][0] = 0;
}
}
// Renderizar la lluvia con tonos de azul claro y más fondo negro
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
uint8_t intensity = rain[x][y]; // Brillo de la gota
Color col;
// Mayor contraste con más LEDs apagados
if (intensity > 220) {
col = Color(120, 200, 255); // Azul celeste brillante
} else if (intensity > 160) {
col = Color(90, 180, 240); // Azul cielo
} else if (intensity > 100) {
col = Color(70, 150, 220); // Azul medio
} else if (intensity > 50) {
col = Color(50, 120, 200); // Azul tenue
} else {
col = Color(0, 0, 0); // Negro total (fondo)
}
it[8 * y + x] = col;
}
}
uart:
- id: pm1006_uart
rx_pin: GPIO7
baud_rate: 9600
- id: ld2410_uart
tx_pin: GPIO4
rx_pin: GPIO3
baud_rate: 256000
parity: NONE
stop_bits: 1
i2c:
- id: bus_a
scl: GPIO6
sda: GPIO5
scan: True
sensor:
- platform: pm1006
uart_id: pm1006_uart
pm_2_5:
name: "Partículas de 2.5µm"
id: particulate_matter_2_5
- platform: aht10
variant: AHT20
i2c_id: bus_a
temperature:
name: "Temperatura"
humidity:
name: "Humedad"
update_interval: 120s
- platform: ld2410
light:
name: light
moving_distance:
name : Moving Distance
still_distance:
name: Still Distance
moving_energy:
name: Move Energy
still_energy:
name: Still Energy
detection_distance:
name: Detection Distance
g0:
move_energy:
name: g0 move energy
still_energy:
name: g0 still energy
g1:
move_energy:
name: g1 move energy
still_energy:
name: g1 still energy
g2:
move_energy:
name: g2 move energy
still_energy:
name: g2 still energy
g3:
move_energy:
name: g3 move energy
still_energy:
name: g3 still energy
g4:
move_energy:
name: g4 move energy
still_energy:
name: g4 still energy
g5:
move_energy:
name: g5 move energy
still_energy:
name: g5 still energy
g6:
move_energy:
name: g6 move energy
still_energy:
name: g6 still energy
g7:
move_energy:
name: g7 move energy
still_energy:
name: g7 still energy
g8:
move_energy:
name: g8 move energy
still_energy:
name: g8 still energy
text_sensor:
- platform: template
name: "Calidad del aire (PM2.5)"
icon: mdi:air-filter
update_interval: 60s
lambda: |-
if (id(particulate_matter_2_5).state <= 12.0) {
return {"Excelente"};
} else if (id(particulate_matter_2_5).state > 12.0 && id(particulate_matter_2_5).state <= 35.0) {
return {"Buena"};
} else if (id(particulate_matter_2_5).state > 35.0 && id(particulate_matter_2_5).state <= 55.0) {
return {"Moderada"};
} else if (id(particulate_matter_2_5).state > 55.0 && id(particulate_matter_2_5).state <= 150.0) {
return {"Insana"};
} else if (id(particulate_matter_2_5).state > 150.0 && id(particulate_matter_2_5).state <= 250.0) {
return {"Dañina"};
} else if (id(particulate_matter_2_5).state > 250.0 && id(particulate_matter_2_5).state <= 350.0) {
return {"Muy dañina"};
} else {
return {"Peligrosa"};
}
ld2410:
uart_id: ld2410_uart
binary_sensor:
- platform: ld2410
has_target:
name: Presence
has_moving_target:
name: Moving Target
has_still_target:
name: Still Target
out_pin_presence_status:
name: out pin presence status
switch:
- platform: ld2410
engineering_mode:
name: "engineering mode"
bluetooth:
name: "control bluetooth"
number:
- platform: ld2410
timeout:
name: timeout
light_threshold:
name: light threshold
max_move_distance_gate:
name: max move distance gate
max_still_distance_gate:
name: max still distance gate
g0:
move_threshold:
name: g0 move threshold
still_threshold:
name: g0 still threshold
g1:
move_threshold:
name: g1 move threshold
still_threshold:
name: g1 still threshold
g2:
move_threshold:
name: g2 move threshold
still_threshold:
name: g2 still threshold
g3:
move_threshold:
name: g3 move threshold
still_threshold:
name: g3 still threshold
g4:
move_threshold:
name: g4 move threshold
still_threshold:
name: g4 still threshold
g5:
move_threshold:
name: g5 move threshold
still_threshold:
name: g5 still threshold
g6:
move_threshold:
name: g6 move threshold
still_threshold:
name: g6 still threshold
g7:
move_threshold:
name: g7 move threshold
still_threshold:
name: g7 still threshold
g8:
move_threshold:
name: g8 move threshold
still_threshold:
name: g8 still threshold
button:
- platform: ld2410
factory_reset:
name: "factory reset"
restart:
name: "restart"
query_params:
name: query params
select:
- platform: ld2410
distance_resolution:
name: "distance resolution"
baud_rate:
name: "baud rate"
light_function:
name: light function
out_pin_level:
name: out pin level
- En este código no se han incluido las credenciales para que el dispositivo se conecte a tu WiFi y tu instancia de Home Assistant y tienes que incluirlas manualmente. En concreto me refiero a las siguientes líneas del código que has copiado en el paso 4.
# Enable Home Assistant API
api:
encryption:
key: "bg6hash6sjdjsdjk02hh0qnQeYVwm123vdfKE8BP5"
ota:
- platform: esphome
password: "asddasda27aab65a48484502b332f"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Assist Fallback Hotspot"
password: "ZsasdasdHGP2234"
- Cuando hayas terminado de editar el código pulsa en “Save” e “Install”. Selecciona la opción “Manual download” y espera a que el código se compile.
- Cuando termine, selecciona la opción “Modern format” para descargar el fichero ‘.bin’ correspondiente.
- Ahora ve a la página de ESPHome y pulsa en “Connect”. En la ventana emergente selecciona tu placa y pulsa en “Conectar”.
- Ahora pulsa en “Install” y selecciona el fichero ‘.bin’ obtenido en el paso 7. De nuevo, pulsa en “Install”.
- Vuelve a Home Assistant y ve a Configuración > Dispositivos y servicios. Lo normal es que tu dispositivo haya sido descubierto y aparezca en la parte superior, esperando únicamente a que pulses el botón de “Configurar”. De lo contrario pulsa en el botón de “Añadir integración”, busca “ESPHome” e introduce la IP de tu placa en el campo ‘Host’. Como siempre, te recomiendo que asignes una IP fija en tu router para evitar fallos en el futuro si esta cambia.
Montaje y puesta a punto
Ya casi lo tenemos, para terminar de integrar Ikea VINDRIKTNING 2.0 en HA haz lo siguiente:
- El primer paso es volver a atornillar la placa original en su sitio (sólo los dos tornillos de la parte inferior). La parte inferior debe quedar ajustada para que puedas seguir usando el puerto USB-C de alimentación, pero no te preocupes de la parte superior ya que hay espacio de sobra.
- Te sugiero que sigas la siguiente distribución de los componentes. Lejos de ser fruto de la casualidad, he pasado un rato pensando la mejor forma de acomodarlos. Una gotita de silicona puede ser útil para fijar el sensor de presencia.


- Ahora, con cuidado de que no se deshaga ninguna conexión, conecta los cables del resto de componentes (el sensor de partículas PM2.5 y el motor) a la placa. Hecho esto, aloja todos los cables en el hueco superior y atornilla la carcasa.
- Observa que he definido una serie de efectos visuales que puedes utilizar en tus automatizaciones. Por ejemplo, para que reproduzca el efecto «fuego» cuando la temperatura es muy alta, o el efecto «lluvia» cuando aumenta la humedad.
- Por cierto, una vez que hayas colocado el dispositivo en su lugar definitivo, no olvides calibrar el sensor de presencia.

