Blog

GeekMagic Ultra – La MINI Pantalla para tu Autocaravana

La GeekMagic Ultra es mucho más que una simple estación meteorológica de escritorio. Con un procesador ESP8266 y una pantalla de casi 1,5 pulgadas, se ha convertido en el aliado perfecto para quienes buscamos monitorizar nuestra autocaravana sin ocupar espacio ni realizar instalaciones complejas.

En este artículo te explicamos cómo pasar de una configuración básica a una integración total con Home Assistant y ESPHome.


1. Configuración Inicial (Modo Estación Meteorológica)

Nada más sacarla de la caja, el proceso es sencillo:

  • Conéctala mediante un cable USB-C.
  • Escanea el código QR o conéctate a la red Wi-Fi que genera la propia pantalla.
  • Introduce tu red Wi-Fi y tu ciudad.
  • Resultado: Ya tienes hora, tiempo y pronóstico con un gasto energético mínimo.

2. Integración con ESPHome (Método Avanzado)

Si quieres control total sobre lo que se muestra (voltaje de batería, niveles de agua, temperatura interior), este es el camino.

Pasos para el flasheo vía OTA:

  1. Entra en ESPHome Web Tools o tu instancia de ESPHome.
  2. Crea un nuevo dispositivo con una configuración vacía.
  3. Copia el código de preparación (el que os dejo abajo), compila y descarga el archivo .bin.
  4. En la interfaz web de la GeekMagic, ve a Firmware Update y sube el archivo descargado.

Código inicial de preparación:

esphome:
  name: geekmagic
  friendly_name: Mini Display TV

esp8266:
  board: esp01_1m

logger:
api:
web_server:
  port: 80

ota:
  - platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "Mini-Display-Tv"
    password: "12345678"

captive_portal:


3. Integración con HACS (El método más rápido)

Si no quieres tocar código, existe un repositorio en GitHub que permite configurar layouts visuales desde la interfaz de Home Assistant.

Instalación:

  1. En HACS, añade este repositorio personalizado: https://github.com/adrienbrault/geekmagic-hacs
  2. Busca e instala Geekmagic Display.
  3. Reinicia Home Assistant.
  4. En Dispositivos y Servicios, añade la integración e introduce la IP de tu pantalla.
  5. Usa el nuevo panel lateral de Geekmagic para arrastrar y soltar los sensores que quieras visualizar.

4. Participa en el Sorteo

Estamos sorteando una unidad de la GeekMagic Ultra entre nuestra comunidad.

Requisitos para participar:

  • Comenta el video de YouTube correspondiente a este artículo.
  • Estate atento al sorteo el próximo jueves 9 de abril.
  • Válido para envíos a toda España.

Nota Legal: YouTube no patrocina ni administra este sorteo. Al participar, eximes a YouTube de cualquier responsabilidad. Consulta las Normas de la Comunidad de YouTube para asegurar tu participación.


5. Código YAML Completo para ESPHome (GeekMagic Ultra)

Este código configura 4 páginas dinámicas: Energía en Ruta, Energía Hogar, Nivelación/Clima y Seguridad. Incluye el sistema de alertas prioritarias que bloquea la pantalla en rojo si detecta una claraboya o puerta abierta.

esphome:
  name: geekmagic
  friendly_name: Mini Display TV

esp8266:
  board: esp01_1m

logger:

web_server:
  port: 80

ota:
  - platform: esphome

api:
  encryption:
    key: "dQnghfgfprueba93Kphfhfghfhfhf77hKs="

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "Mini-Display-Tv"
    password: "12345678"

globals:
  - id: current_page
    type: int
    restore_value: no
    initial_value: '0'

interval:
  - interval: 5s 
    then:
      - lambda: |-
          id(current_page) = (id(current_page) + 1) % 4;
          id(disp).update();

captive_portal:

spi:
  clk_pin: GPIO14
  mosi_pin: GPIO13
  interface: hardware
  id: spihwd

output:
  - platform: esp8266_pwm
    pin: GPIO05
    frequency: 200 Hz
    inverted: true
    id: backlight_pwm

light:
  - platform: monochromatic
    output: backlight_pwm
    name: "Backlight"
    id: backlight
    restore_mode: RESTORE_AND_ON

font:
  - file: "gfonts://Roboto"
    id: font_main
    size: 20
    glyphs: " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~°¡¿ÁÉÍÓÚáéíóúÑñ"

  - file: "gfonts://Roboto"
    id: font_small
    size: 14
    glyphs: " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~°¡¿ÁÉÍÓÚáéíóúÑñ"

  - file: "https://github.com/Templarian/MaterialDesign-Webfont/raw/master/fonts/materialdesignicons-webfont.ttf"
    id: icon_font
    size: 24
    glyphs: [
      "\U000F140B", # Consumo (Power)
      "\U000F1A74", # Producción (Solar)
      "\U000F0599", # Sol (Weather)
      "\U000F0591", # Nubes (Weather)
      "\U000F0596", # Lluvia (Weather)
      "\U000F059C", # Nieve (Weather)
      "\U000F081C", # Puerta Abierta
      "\U000F081B", # Puerta Cerrada
      "\U000F01AC", # Humedad (Water percent)
      "\U000F05AD", # Viento (Wind)
      "\U000F02A2", # Presión (Gauge)
      "\U000F0042", # Balance (Balance-scale)
      "\U000F0590", # Cobertura nubes (Cloudiness)
      "\U000F0635", # Gas (Gas-cylinder)
      "\U000F05A9", # WiFi
      "\U000F09F0", # Nivel (Spirit-level)
      "\U000F0079", # Batería
      "\U000F0637"  # Claraboya abierta (Window-open)
    ]

color:
  - id: c_bg
    hex: "000000"
  - id: c_white
    hex: "FFFFFF"
  - id: c_dark_bg
    hex: "121212"
  - id: c_gray
    hex: "444444"
  - id: c_accent_cyan
    hex: "00E5FF"
  - id: c_accent_orange
    hex: "FF6D00"
  - id: c_alert
    hex: "D50000"
  - id: c_success
    hex: "00C853"

sensor:
  # SENSORES RUTA
  - platform: homeassistant
    id: victron_v
    entity_id: sensor.victron_ble_battery_voltage
  - platform: homeassistant
    id: victron_solar
    entity_id: sensor.victron_ble_pv_power
  - platform: homeassistant
    id: victron_yield
    entity_id: sensor.victron_ble_yield_today
  - platform: homeassistant
    id: victron_bat_est
    entity_id: sensor.victron_buelles_bateria_estimada
  - platform: homeassistant
    id: gas_pct
    entity_id: sensor.bombona_porcentaje

  # SENSORES HOGAR
  - platform: homeassistant
    id: huawei_solar
    entity_id: sensor.huawei_produccion
  - platform: homeassistant
    id: consumo_hogar
    entity_id: sensor.huawei_consumo
  - platform: homeassistant
    id: consumo_importada
    entity_id: sensor.huawei_consumo_importada
  - platform: homeassistant
    id: inverter_yield
    entity_id: sensor.inverter_daily_yield

  # CLIMA Y NIVELACIÓN
  - platform: homeassistant
    id: temp_ext_casa
    entity_id: sensor.ws2900_v2_01_18_outdoor_temperature
  - platform: homeassistant
    id: temp_ext_route
    entity_id: sensor.openweathermap_temperature
  - platform: homeassistant
    id: nivel_lat
    entity_id: sensor.nivel_lateral
  - platform: homeassistant
    id: nivel_long
    entity_id: sensor.nivel_longitudinal

binary_sensor:
  - platform: homeassistant
    id: puerta_buhardilla
    entity_id: binary_sensor.puerta_buhardilla_contact
  - platform: homeassistant
    id: claraboya_cama
    entity_id: binary_sensor.apertura_claraboya_cama_contact
  - platform: homeassistant
    id: claraboya_bano
    entity_id: binary_sensor.apertura_claraboya_bano_contact
  - platform: homeassistant
    id: claraboya_salon
    entity_id: binary_sensor.claraboya_salon_contact

time:
  - platform: homeassistant
    id: current_time

display:
  - platform: mipi_spi
    model: st7789v
    spi_id: spihwd
    dimensions:
      height: 240
      width: 240
    invert_colors: true
    dc_pin: GPIO00
    reset_pin: GPIO02
    color_depth: 16
    update_interval: 2s 
    id: disp
    spi_mode: mode3
    lambda: |-
      // SISTEMA DE ALERTAS CRÍTICAS
      bool claraboya_abierta = id(claraboya_cama).state || id(claraboya_bano).state || id(claraboya_salon).state;
      bool puerta_abierta = id(puerta_buhardilla).state;
      
      if (claraboya_abierta || puerta_abierta) {
        it.fill(id(c_alert));
        it.printf(120, 60, id(icon_font), id(c_white), TextAlign::TOP_CENTER, puerta_abierta ? "\U000F081C" : "\U000F0637");
        it.print(120, 110, id(font_main), id(c_white), TextAlign::TOP_CENTER, "¡ALERTA!");
        it.print(120, 145, id(font_main), id(c_white), TextAlign::TOP_CENTER, puerta_abierta ? "PUERTA" : "CLARABOYA");
        it.print(120, 180, id(font_main), id(c_white), TextAlign::TOP_CENTER, "ABIERTA");
        return;
      }

      it.fill(id(c_bg));

      // RELOJ Y FECHA
      auto time = id(current_time).now();
      it.printf(120, 3, id(font_small), id(c_white), TextAlign::TOP_CENTER, "%02d:%02d", time.hour, time.minute);
      it.printf(120, 18, id(font_small), id(c_gray), TextAlign::TOP_CENTER, "%02d/%02d", time.day_of_month, time.month);

      // INDICADOR DE PÁGINAS
      for(int i=0; i<4; i++) {
        if (id(current_page) == i) {
          it.filled_circle(102 + i*12, 35, 3, id(c_accent_cyan));
        } else {
          it.circle(102 + i*12, 35, 2, id(c_gray));
        }
      }

      // PÁGINA 0: ENERGÍA RUTA
      if (id(current_page) == 0) {
        it.print(120, 45, id(font_small), id(c_accent_cyan), TextAlign::TOP_CENTER, "ENERGÍA RUTA");
        it.line(40, 62, 200, 62, id(c_gray));

        it.print(45, 85, id(icon_font), id(c_success), TextAlign::CENTER, "\U000F0079");
        if (id(victron_v).has_state()) {
          it.printf(75, 75, id(font_main), id(c_white), TextAlign::TOP_LEFT, "%.1f V", id(victron_v).state);
          if (id(victron_bat_est).has_state())
            it.printf(75, 95, id(font_small), id(c_gray), TextAlign::TOP_LEFT, "Carga: %.0f%%", id(victron_bat_est).state);
        }

        it.print(45, 130, id(icon_font), id(c_accent_orange), TextAlign::CENTER, "\U000F1A74");
        if (id(victron_solar).has_state()) {
          it.printf(75, 120, id(font_main), id(c_white), TextAlign::TOP_LEFT, "%.0f W", id(victron_solar).state);
          if (id(victron_yield).has_state())
            it.printf(75, 140, id(font_small), id(c_gray), TextAlign::TOP_LEFT, "Hoy: %.2f kWh", id(victron_yield).state);
        }

        it.print(45, 180, id(icon_font), id(c_alert), TextAlign::CENTER, "\U000F0635");
        if (id(gas_pct).has_state()) {
          it.printf(75, 170, id(font_main), id(c_white), TextAlign::TOP_LEFT, "%.1f %%", id(gas_pct).state);
          it.filled_rectangle(75, 192, 100, 6, id(c_dark_bg));
          float g_fill = (id(gas_pct).state / 100.0) * 100.0;
          it.filled_rectangle(75, 192, (int)g_fill, 6, id(c_alert));
        }

      // PÁGINA 1: ENERGÍA HOGAR
      } else if (id(current_page) == 1) {
        it.print(120, 45, id(font_small), id(c_white), TextAlign::TOP_CENTER, "ENERGÍA HOGAR");
        it.line(40, 62, 200, 62, id(c_gray));

        it.print(50, 90, id(icon_font), id(c_success), TextAlign::CENTER, "\U000F1A74");
        if (id(huawei_solar).has_state()) {
          it.printf(80, 80, id(font_main), id(c_white), TextAlign::TOP_LEFT, "%.0f W", id(huawei_solar).state);
        }

        it.print(50, 140, id(icon_font), id(c_accent_orange), TextAlign::CENTER, "\U000F140B");
        if (id(consumo_hogar).has_state()) {
          it.printf(80, 130, id(font_main), id(c_white), TextAlign::TOP_LEFT, "%.0f W", id(consumo_hogar).state);
        }

        it.print(50, 190, id(icon_font), id(c_accent_cyan), TextAlign::CENTER, "\U000F02A2");
        if (id(consumo_importada).has_state()) {
          it.printf(80, 180, id(font_main), id(c_white), TextAlign::TOP_LEFT, "%.0f W", id(consumo_importada).state);
        }

      // PÁGINA 2: NIVELACIÓN Y CLIMA
      } else if (id(current_page) == 2) {
        it.print(120, 45, id(font_small), id(c_white), TextAlign::TOP_CENTER, "SISTEMA Y CLIMA");
        
        if (id(nivel_lat).has_state()) {
          it.printf(120, 75, id(font_small), id(c_success), TextAlign::TOP_CENTER, "NIVEL LATERAL");
          it.printf(120, 90, id(font_main), id(c_white), TextAlign::TOP_CENTER, "%.1f°", id(nivel_lat).state);
        }

        it.print(50, 160, id(icon_font), id(c_accent_orange), TextAlign::CENTER, "\U000F0599");
        if (id(temp_ext_casa).has_state()) {
          it.printf(80, 150, id(font_main), id(c_white), TextAlign::TOP_LEFT, "EXT: %.1f°C", id(temp_ext_casa).state);
        }
        
        it.print(50, 200, id(icon_font), id(c_accent_cyan), TextAlign::CENTER, "\U000F0591");
        if (id(temp_ext_route).has_state()) {
          it.printf(80, 190, id(font_main), id(c_white), TextAlign::TOP_LEFT, "INT: %.1f°C", id(temp_ext_route).state);
        }

      // PÁGINA 3: SEGURIDAD
      } else if (id(current_page) == 3) {
        it.print(120, 45, id(font_small), id(c_white), TextAlign::TOP_CENTER, "MONITORIZACIÓN");
        
        it.print(50, 85, id(icon_font), id(c_success), TextAlign::CENTER, "\U000F0042");
        it.print(80, 75, id(font_small), id(c_gray), TextAlign::TOP_LEFT, "SISTEMA:");
        it.print(80, 90, id(font_main), id(c_success), TextAlign::TOP_LEFT, "ONLINE");

        it.print(50, 145, id(icon_font), id(c_success), TextAlign::CENTER, "\U000F081B");
        it.print(80, 140, id(font_small), id(c_white), TextAlign::TOP_LEFT, "CIERRES OK");
        
        it.print(120, 220, id(font_small), id(c_gray), TextAlign::TOP_CENTER, "VIAJANDO EN AUTOCARAVANA");
      }