Integrar el SenseCAP Indicator en HA

Integrar el SenseCAP Indicator en HA

Hoy te enseño a integrar el SenseCAP Indicator en HA, y sacarle todo el provecho a sus componentes para explotarlo en tu instancia.

SenseCAP Indicator D1

El SenseCAP Indicator D1 es un producto desarrollado por Seeed Studio, fabricante que ya conocemos por otros magníficos productos como el ReSpeaker Lite, y distribuidor oficial de productos de Home Assistant. En concreto se trata de una pantalla táctil de 4 pulgadas (480 x 480 píxeles) que funciona impulsada por un sistema de doble microcontrolador (ESP32-S3 y RP2040), y admite comunicación Wi-Fi/BLE/LoRa.

En principio, se trata de un dispositivo pensado para medir la calidad del aire, ya que tiene sensores tVOC (SGP40) y CO2 (SCD41) integrados, y un sensor externo de temperatura y humedad (AHT20) que se conecta a través de su puerto Grove. También incorpora un ‘buzzer’ o zumbador (MLT-8530) y un lector de tarjetas Micro SD (no incluida). No obstante, también he visto otros usos para este dispositivo (como interactuar con ChatGPT).

💡 Por supuesto, puedes aprovechar su puerto Grove para conectar otro tipo de sensores.

Ten en cuenta que te acabo de describir las especificaciones de la versión más completa, pero puedes elegir aquella que más te convenga (y adaptar el precio). La diferencia entre las distintas versiones se detalla en esta tabla:

SenseCAP Indicator

Requisitos previos

Para integrar el SenseCAP Indicator en HA previamente necesitas:

  • Un SenseCAP Indicator, en alguna de sus versiones.
  • Haber instalado MQTT en Home Assistant, si sigues el método «oficial».
  • Haber instalado ESPHome en Home Assistant, si sigues nuestro método.
💡 Puedes encontrar todas las versiones disponibles en su página web.
Indicador SenseCAP D1, plataforma de desarrollo IoT con pantalla táctil de 4 pulgadas alimentada por ESP32S3 y RP2040
Aliexpress
75,29€
Indicador SenseCAP D1, plataforma de desarrollo IoT con pantalla táctil de 4 pulgadas alimentada por ESP32S3 y RP2040
Indicador SenseCAP D1, plataforma de desarrollo IoT con pantalla táctil de 4 pulgadas alimentada por ESP32S3 y RP2040
Aliexpress
44,61€
Indicador SenseCAP D1, plataforma de desarrollo IoT con pantalla táctil de 4 pulgadas alimentada por ESP32S3 y RP2040
*Algún precio puede haber cambiado desde la última revisión

Primeros pasos con el SenseCAP Indicator

Antes de integrar el SenseCAP Indicator en HA, vamos a ver qué podemos hacer con él con su configuración de serie:

  1. Saca la pantalla de la caja y conecta el sensor de temperatura y humedad en el puerto Grove derecho de la parte posterior. Utiliza el cable USB-C que viene en la caja para alimentarlo y este se encenderá automáticamente.
  2. Lo primero que verás en pantalla es que te solicita que selecciones tu red WiFi para conectarse. Como siempre, te recomiendo que asignes una IP fija en tu router para evitar fallos en el futuro si esta cambia.
  3. Hecho esto ya puedes volver a la pantalla inicial que muestra la fecha, hora y ubicación. Si te desplazas hacia los lados aparecerá el detalle de la calidad del aire, opciones de ajustes del dispositivo y para configurar tu cuenta de OpenAI.
  4. Si pulsas sobre cada uno de los bloques de información de calidad del aire encontrarás una gráfica de la evolución de los valores en las últimas 24 horas o la última semana.

Integración con Home Assistant

Como ves, el dispositivo es muy sencillo de utilizar. No obstante, me parece que el dispositivo es demasiado potente como para utilizarlo únicamente como un sensor de calidad del aire. Afortunadamente, podemos integrar el SenseCAP Indicator en HA, no sólo para visualizar sus valores en nuestro panel de control y utilizarlo en nuestras automatizaciones, también para controlar nuestros dispositivos desde su pantalla.

Para ello puedes seguir el proceso de la documentación oficial de Seeed Studio, flasheando el dispositivo con un firmware alternativo y utilizando MQTT. No osbtante, puesto que el dispositivo funciona sobre una placa ESP32-S3, en nuestro caso vamos a integrarlo usando ESPHome (así podrás usarlo para otras funciones, como detección de presencia con Bermuda).

💡 Si en algún momento quieres flashear el firmware original, sigue estas instrucciones de la documentación oficial.

Si quieres integrar el SenseCAP Indicator en HA a «nuestra manera», sigue estos pasos:

  1. En Home Assistant, ve a tu complemento de ESPHome, pulsa en “New device” y “Continue”.
  2. Dale un nombre a tu dispositivo (por ejemplo, “SenseCAP Indicator”) y pulsa en “Next”.
  3. Como tipo de dispositivo selecciona “ESP32-S3”. Observarás de fondo que se ha creado un nuevo bloque para tu dispositivo.
  4. Pulsa en “Skip” y haz clic en “Edit”, sobre el bloque de tu dispositivo. Copia el código que aparece y consérvalo, ya que vas a necesitar alguna parte del mismo.
  5. Copia el siguiente código y úsalo para reemplazar el código anterior en ESPHome.
substitutions:
# Device customization
# Personalización del dispositivo
  name: sensecap-indicator
  friendly_name: SenseCAP Indicator
# Example of Sensors
# Ejemplo de Sensores
  sensor_temperature: sensor.openweathermap_temperature
# Example of Devices
# Ejemplo de Dispositivos
  device1: light.tira_led_escritorio
  device2: climate.salon
  device3: switch.ventilador
  device4: switch.impresora_3d
# Other settings
# Otros ajustes
  allowed_characters: " ¿?¡!#%'()+,-./:°0123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyzáéíóú"
################################################################################################################
esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2024.6.0
  name_add_mac_suffix: false
  platformio_options:
    board_build.flash_mode: dio
    build_unflags: -Werror=all
  project:
    name: esphome.web
    version: dev
esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: esp-idf
    # Required to achieve sufficient PSRAM bandwidth for screen graphics
    sdkconfig_options:
      CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: y
      CONFIG_ESP32S3_DATA_CACHE_64KB: y
      CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
      CONFIG_SPIRAM_RODATA: y
external_components:
  - source:
      type: git
      url: https://github.com/tobi1449/esphome-sensecap-indicator-sensors.git
      ref: main
    components: [sensecap_indicator_sensors]
    refresh: 1s
# Enable Home Assistant API
api:
  encryption:
    key: "E21VY8asjsa9823a4D2FGXod6fasdasd12"
# Allow Over-The-Air updates
ota:
- platform: esphome
  password: "a0d023dfggdfb5b67b3f4bf17234df35"
# Allow provisioning Wi-Fi via serial
improv_serial:
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Sensecap-Indicator"
    password: "cC2sada45tS"
psram:
  mode: octal
  speed: 80MHz
logger:
  level: DEBUG
  hardware_uart: UART0
captive_portal:
binary_sensor:
  - platform: touchscreen
    name: "Previous Page"
    internal: true
    x_min: 0
    x_max: 30
    y_min: 0
    y_max: 480
    on_press:
      then:
        lvgl.page.previous:
  - platform: touchscreen
    name: "Next Page"
    internal: true
    x_min: 450
    x_max: 480
    y_min: 0
    y_max: 480
    on_press:
      then:
        lvgl.page.next:
font:
  - file: "gfonts://Rubik"
    id: font_clock
    size: 75
    glyphs: ${allowed_characters}
  - file: "gfonts://Rubik"
    id: font_date
    size: 20
    glyphs: ${allowed_characters}
  - file: "gfonts://Material+Symbols+Outlined"
    id: font_icons
    size: 40
    glyphs: [
      "\U0000e846", # thermometer
      "\U0000f87e", # humidity
      "\U0000e7b0", # co2
      "\U0000f55b", # pollution
      "\U0000ea24", # bulb
      "\U0000ea43", # fireplace
      "\U0000f168", # fan
      "\U0000e8ad", # printer
      ] 
  - file: "gfonts://Roboto"
    id: font_button_value
    size: 22
    glyphs: ${allowed_characters}
  - file: "gfonts://Roboto"
    id: font_button
    size: 18
    glyphs: ${allowed_characters}
i2c:
  sda: 39
  scl: 40
  scan: false
  id: bus_a
image:
  - file: https://img.icons8.com/?size=100&id=qA3w9Yp2vY7r&format=png&color=000000
    id: icon_weather
    resize: 70x70
    type: RGB565
    transparency: alpha_channel
light:
  - platform: monochromatic
    output: ledc_gpio4
    name: Display Backlight
    id: display_backlight
    restore_mode: ALWAYS_ON
output:
  - platform: ledc
    pin: 
      number: GPIO45
      ignore_strapping_warning: true
    id: ledc_gpio4
    frequency: 100Hz
pca9554:
  - id: p_c_a
    pin_count: 16
    address: 0x20
sensor:
  - platform: homeassistant
    id: sensor_temperature
    entity_id: ${sensor_temperature}
    internal: true
    on_value:
      then:
        - lvgl.label.update:
            id: lbl_weather
            text:
              format: "%.0f°C"
              args:
                - id(sensor_temperature).state
        - lvgl.label.update:
            id: lbl_weather2
            text:
              format: "%.0f°C"
              args:
                - id(sensor_temperature).state
  - platform: sensecap_indicator_sensors
    temperature:
      name: "Temperature"
      id: sensor_temp
      on_value:
        then:
          - lvgl.label.update:
              id: lbl_button1_value
              text:
                format: "%.0f°C"
                args:
                  - id(sensor_temp).state
    humidity:
      name: "Humidity"
      id: sensor_humi
      on_value:
        then:
          - lvgl.label.update:
              id: lbl_button2_value
              text:
                format: "%.0f%%"
                args:
                  - id(sensor_humi).state
    co2:
      name: "CO2"
      id: sensor_co2
      on_value:
        then:
          - lvgl.label.update:
              id: lbl_button3_value
              text:
                format: "%.0f ppm"
                args:
                  - id(sensor_co2).state
    altitude: 405
    tvoc:
      name: "TVOC"
      id: sensor_tvoc
      on_value:
        then:
          - lvgl.label.update:
              id: lbl_button4_value
              text:
                format: "%.0f ppb"
                args:
                  - id(sensor_tvoc).state
spi:
  - id: lcd_spi
    clk_pin: 41
    mosi_pin: 48
text_sensor:
  - platform: homeassistant
    id: device_1
    entity_id: ${device1}
    internal: true
    on_value:
      then:
        - lvgl.label.update:
            id: lbl_device1_state
            text: !lambda |-
              if (x == "on") {
                return "Encendida";
              } else {
                return "Apagada";
              }
  - platform: homeassistant
    id: device_2
    entity_id: ${device2}
    internal: true
    on_value:
      then:
        - lvgl.label.update:
            id: lbl_device2_state
            text: !lambda |-
              if (x == "on") {
                return "Encendida";
              } else {
                return "Apagada";
              }
  - platform: homeassistant
    id: device_3
    entity_id: ${device3}
    internal: true
    on_value:
      then:
        - lvgl.label.update:
            id: lbl_device3_state
            text: !lambda |-
              if (x == "on") {
                return "Encendido";
              } else {
                return "Apagado";
              }
  - platform: homeassistant
    id: device_4
    entity_id: ${device4}
    internal: true
    on_value:
      then:
        - lvgl.label.update:
            id: lbl_device4_state
            text: !lambda |-
              if (x == "on") {
                return "Encendida";
              } else {
                return "Apagada";
              }
time:
  - platform: homeassistant
    id: esptime
touchscreen:
  - platform: ft5x06
    id: ft5x06_touchscreen
    transform:
      mirror_x: true
      mirror_y: true
    on_release:
      then:
        - if:
            condition: lvgl.is_paused
            then:
              - light.turn_on: display_backlight
              - lvgl.resume:
              - lvgl.widget.redraw:
uart:
  tx_pin: 19
  rx_pin: 20
  baud_rate: 115200
display:
  - platform: st7701s
    auto_clear_enabled: false
    update_interval: never
    spi_mode: MODE3
    color_order: RGB
    dimensions:
      width: 480
      height: 480
    invert_colors: true
    transform:
      mirror_x: true
      mirror_y: true
    cs_pin:
      pca9554: p_c_a
      number: 4
    reset_pin:
      pca9554: p_c_a
      number: 5
    de_pin: 18
    hsync_pin: 16
    vsync_pin: 17
    pclk_pin: 21
# Init sequence can be one of the defaults and/or custom sequences.
    init_sequence:
      - 1 # select canned init sequence number 1
# Custom sequences are an array, first byte is command, the rest are data.
      - [ 0xE0, 0x1F ]  # Set sunlight readable enhancement
    data_pins:
      red:
        - 4         #r1
        - 3         #r2
        - 2         #r3
        - 1         #r4
        - 0         #r5
      green:
        - 10        #g0
        - 9         #g1
        - 8         #g2
        - 7         #g3
        - 6         #g4
        - 5         #g5
      blue:
        - 15        #b1
        - 14        #b2
        - 13        #b3
        - 12        #b4
        - 11        #b5
lvgl:
  touchscreens: ft5x06_touchscreen
  buffer_size: 100%
  bg_color: 0x062e20
  on_idle:
    - timeout: 10s
      then:
        - logger.log: idle timeout
        - if:
            condition:
              lvgl.is_idle:
                timeout: 5s
            then:
              - logger.log: LVGL is idle
    - timeout: 60s
      then:
        - logger.log: idle 60s timeout
        - lvgl.pause:
        - light.turn_off:
            id: display_backlight
            transition_length: 5s
  # debugging info will write all the touchscreen taps and their x/y to console
  log_level: INFO
  color_depth: 16
  style_definitions:
    - id: style_button
      width: 208
      height: 145
      bg_color: 0x042318
      border_color: 0x042318
      radius: 20
    - id: style_button_icon
      align: TOP_MID
      text_font: font_icons
    - id: style_button_value
      align: CENTER
      text_color: 0xffffff
      text_font: font_button_value
      pad_top: 25
    - id: style_button_text
      align: BOTTOM_MID
      text_color: 0x57766b
      text_font: font_button
      pad_bottom: 10
  pages:
    - id: page_home
      widgets:
        - label:
            align: TOP_LEFT
            id: lbl_date
            text: "00/00/0000"
            text_color: 0x21c48e
            text_font: font_date
            pad_top: 20
            pad_left: 30
        - label:
            align: TOP_LEFT
            id: lbl_clock
            text: "00:00"
            text_color: 0xd3ebe3
            text_font: font_clock
            pad_top: 35
            pad_left: 20
        - label:
            align: TOP_RIGHT
            id: lbl_weather
            text: "25°C"
            text_color: 0x21c48e
            text_font: font_date
            pad_top: 20
            pad_right: 30
        - image:
            align: TOP_RIGHT
            src: icon_weather
            pad_top: 45
            pad_right: 20
        - obj:
            styles: style_button
            x: 28
            y: 148
            widgets:
              - label:
                  styles: style_button_icon
                  text: "\U0000e846"
                  text_color: 0xaee55f
              - label:
                  styles: style_button_value
                  id: lbl_button1_value
                  text: "25°C"
              - label:
                  styles: style_button_text
                  id: lbl_button1
                  text: "Temperatura"
        - obj:
            styles: style_button
            x: 245
            y: 148
            widgets:
              - label:
                  styles: style_button_icon
                  text: "\U0000f87e"
                  text_color: 0x5fb4e5
              - label:
                  styles: style_button_value
                  id: lbl_button2_value
                  text: "45%"
              - label:
                  styles: style_button_text
                  id: lbl_button2
                  text: "Humedad"
        - obj:
            styles: style_button
            x: 28
            y: 305
            widgets:
              - label:
                  styles: style_button_icon
                  text: "\U0000e7b0"
                  text_color: 0xa7a7a7
              - label:
                  styles: style_button_value
                  id: lbl_button3_value
                  text: "1000 ppm"
              - label:
                  styles: style_button_text
                  id: lbl_button3
                  text: "CO2"
        - obj:
            styles: style_button
            x: 245
            y: 305
            widgets:
              - label:
                  styles: style_button_icon
                  text: "\U0000f55b"
                  text_color: 0xe96fb4
              - label:
                  styles: style_button_value
                  id: lbl_button4_value
                  text: "20 ppb"
              - label:
                  styles: style_button_text
                  id: lbl_button4
                  text: "tVOC"
    - id: page_devices
      widgets:
        - label:
            align: TOP_LEFT
            id: lbl_date2
            text: "00/00/0000"
            text_color: 0x21c48e
            text_font: font_date
            pad_top: 20
            pad_left: 30
        - label:
            align: TOP_LEFT
            id: lbl_clock2
            text: "00:00"
            text_color: 0xd3ebe3
            text_font: font_clock
            pad_top: 35
            pad_left: 20
        - label:
            align: TOP_RIGHT
            id: lbl_weather2
            text: "25°C"
            text_color: 0x21c48e
            text_font: font_date
            pad_top: 20
            pad_right: 30
        - image:
            align: TOP_RIGHT
            src: icon_weather
            pad_top: 45
            pad_right: 20
        - obj:
            styles: style_button
            x: 28
            y: 148
            on_press:
              then:
                - homeassistant.action:
                    service: light.toggle
                    data:
                      entity_id: ${device1}
            widgets:
              - label:
                  styles: style_button_icon
                  text: "\U0000ea24"
                  text_color: 0xe9d96f
              - label:
                  styles: style_button_value
                  id: lbl_button1_value2
                  text: "Escritorio"
              - label:
                  styles: style_button_text
                  id: lbl_device1_state
                  text: "Apagado"
        - obj:
            styles: style_button
            x: 245
            y: 148
            on_press:
              then:
                - homeassistant.action:
                    service: climate.toggle
                    data:
                      entity_id: ${device2}
            widgets:
              - label:
                  styles: style_button_icon
                  text: "\U0000ea43"
                  text_color: 0xe99d6f
              - label:
                  styles: style_button_value
                  id: lbl_button2_value2
                  text: "Calefacción"
              - label:
                  styles: style_button_text
                  id: lbl_device2_state
                  text: "Apagado"
        - obj:
            styles: style_button
            bg_color: 0x042318
            border_color: 0x042318
            x: 28
            y: 305
            on_press:
              then:
                - homeassistant.action:
                    service: switch.toggle
                    data:
                      entity_id: ${device3}
            widgets:
              - label:
                  styles: style_button_icon
                  text: "\U0000f168"
                  text_color: 0xcde4f2
              - label:
                  styles: style_button_value
                  id: lbl_button3_value2
                  text: "Ventilador"
              - label:
                  styles: style_button_text
                  id: lbl_device3_state
                  text: "Apagado"
        - obj:
            styles: style_button
            x: 245
            y: 305
            on_press:
              then:
                - homeassistant.action:
                    service: switch.toggle
                    data:
                      entity_id: ${device4}
            widgets:
              - label:
                  styles: style_button_icon
                  text: "\U0000e8ad"
                  text_color: 0xeeeeee
              - label:
                  styles: style_button_value
                  id: lbl_button4_value2
                  text: "Impresora"
              - label:
                  styles: style_button_text
                  id: lbl_device4_state
                  text: "Apagado"
interval:
  - interval: 60s
    then:
      - lvgl.label.update:
          id: lbl_date
          text: !lambda 'return id(esptime).now().strftime("%d/%m/%y");'
      - lvgl.label.update:
          id: lbl_date2
          text: !lambda 'return id(esptime).now().strftime("%d/%m/%y");'
  - interval: 60s
    then:
      - lvgl.label.update:
          id: lbl_clock
          text: !lambda 'return id(esptime).now().strftime("%H:%M");'
      - lvgl.label.update:
          id: lbl_clock2
          text: !lambda 'return id(esptime).now().strftime("%H:%M");'
  1. Parte importante. 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"
  1. Lo que tienes que hacer es, encontrar las líneas correspondientes en el código (está al principio) y añadir la información correspondiente.
  2. Ahora sí, pulsa en “Save” e “Install”. Selecciona “Manual download” y espera a que se compile el código. 
  3. Cuando termine, selecciona la opción “Modern format” para descargar el fichero ‘.bin’ correspondiente.
  4. Conecta el SenseCAP Indicator con el cable USB-C de datos por el puerto que trae en la parte trasera a tu ordenador.
  5. Ahora ve a la página de ESPHome y pulsa en “Connect”. En la ventana emergente selecciona tu placa y pulsa en “Conectar”.
  6. Ahora pulsa en “Install” y selecciona el fichero ‘.bin’ obtenido en el paso 9. De nuevo, pulsa en “Install”.
  7. 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.
  8. Para terminar ve a Configuración > Dispositivos y servicios > ESPHome. Pulsa sobre el enlace «Configurar» correspondiente tu dispositivo. En la ventana emergente marca la casilla «Permitir que el dispositivo realice acciones de Home Assistant» y pulsa en «Enviar». Esto va a permitir que podamos controlar nuestros dispositivos desde la pantalla.
🎨 Observa que puedes personalizar el dispositivo como quieras (colores, dispositivos, iconos...). Este es sólo un ejemplo para que te sirva de punto de partida!
🛟 ¿Dudas? Si necesitas ayuda entra aquí 👈 🎁 Y si te ha gustado y quieres más... 🥑

¡Copiado!
20%
Descuento en todos los productos que funcionan con Curve
¡Copiado!
12%
Descuento en todos los productos de la tienda de Zemismart
¡Copiado!
10%
Descuento en todos los productos de la tienda de Shelly Spain
¡Copiado!
10%
Descuento en todos los productos probados por Aguacatec
¡Copiado!
10%
Descuento en todos los productos de Home Assistant
¡Copiado!
10%
Descuento en todos los productos de la tienda.
¡Copiado!
5%
Descuento en las pantallas de la serie CrowPanel
¡Copiado!
5%
Descuento en todos los productos de la tienda de Lilygo