
Después de pasar el asistente de Xiaozhi a ESPHome, me ha saltado este pequeño aparato, el Geekmagic Ultra, un dispositivo con ESP8266 y que permite usarlo de forma nativa como estación meteorológica o bien, ponerlo con ESPHome para poder hacer lo que queramos e integrarlo en nuestro Home Assistant.
Vídeo de la review / tutorial del Geekmagic Ultra con ESPHome
Comandos usados
Para aquellos que queréis tener la lista de comandos listos para copiar y pegar, podéis ver el vídeo y aquí os paso los códigos:
Antes de nada, os muestro el pinout del Geekmagic Ultra:

Ahora, deberíais usar las pinzas y el USB-Uart que muestro en el vídeo.
Para dumpear el esp8266:
Linux:
esptool.py -p /dev/ttyUSBX -b 115200 read_flash 0 0x400000 backup_esp8266.binDonde ttyUSBX es el número del puerto que ha pillado el dispositivo USB-UART
Windows:
esptool.py -p COMX -b 115200 read_flash 0 0x400000 backup_esp8266.binDonde COMX es el COM de vuestro serie
Este es el último código de ESPHome que muestro en el vídeo (recordad adaptarlo a los nombres de vuestros dispositivos/entidades):
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:
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
# ---------------------------------------------------------
# FUENTES CORREGIDAS (Sin duplicados)
# ---------------------------------------------------------
font:
- file: "gfonts://Roboto"
id: font_main
size: 20
glyphs: "0123456789.,: %°CW"
- file: "gfonts://Roboto"
id: font_small
size: 14
# He añadido la 'K' mayúscula que faltaba para el "OK"
glyphs: " !"%()+,-.:0123456789ABCDEFILMOPRSTUWKabcdefghilmnorstuy°Ññ"
- file: "materialdesignicons-webfont.ttf"
id: icon_font
size: 24
glyphs: [
"U000F0339", # Rayo
"U000F050F", # Termometro
"U000F0591", # Lavadora
"U000F042D", # Impresora
"U000F011B" # Gato
]
color:
- id: c_bg
hex: "000000" # Negro Fondo
- id: c_white
hex: "FFFFFF"
- id: c_gray
hex: "333333" # Gris para fondos de barras
- id: c_accent
hex: "00D2FF" # Azul Cyan
- id: c_warn
hex: "FF9800" # Naranja
- id: c_alert
hex: "FF0033" # Rojo
- id: c_good
hex: "4CAF50" # Verde
# ---------------------------------------------------------
# SENSORES
# ---------------------------------------------------------
sensor:
- platform: homeassistant
id: consumo
entity_id: sensor.consumocasa_channel_1_power
- platform: homeassistant
id: temperatura
entity_id: sensor.habitacion_1_temperature
- platform: homeassistant
id: lavadora
entity_id: sensor.lavadora_consumo
- platform: homeassistant
id: p1s_pc
entity_id: sensor.p1s_progreso_de_la_impresion
# Nuevo sensor de nivel de arena (0-100)
- platform: homeassistant
id: nivel_arena
entity_id: sensor.pb01009g25030010664_cat_litter_level
text_sensor:
- platform: homeassistant
id: p1s_state
entity_id: sensor.p1s_print_status
binary_sensor:
# Nuevo sensor binario de basura llena
- platform: homeassistant
id: basura_llena
entity_id: binary_sensor.pb01009g25030010664_garbage_can_full
time:
- platform: homeassistant
id: current_time
# ---------------------------------------------------------
# PANTALLA
# ---------------------------------------------------------
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: 5s
id: disp
spi_mode: mode3
lambda: |-
// Limpiar pantalla
it.fill(id(c_bg));
// 1. HEADER
auto time = id(current_time).now();
it.printf(120, 5, id(font_small), id(c_white), TextAlign::TOP_CENTER, "%02d:%02d", time.hour, time.minute);
it.line(10, 25, 230, 25, id(c_gray));
// 2. DATOS DE CASA
int y_row1 = 40;
// Consumo
it.print(40, y_row1, id(icon_font), id(c_warn), TextAlign::TOP_CENTER, "U000F0339");
if (id(consumo).has_state()) {
it.printf(40, y_row1 + 25, id(font_main), id(c_white), TextAlign::TOP_CENTER, "%.0f", id(consumo).state);
it.printf(40, y_row1 + 45, id(font_small), id(c_gray), TextAlign::TOP_CENTER, "W");
}
// Temperatura
it.print(120, y_row1, id(icon_font), id(c_alert), TextAlign::TOP_CENTER, "U000F050F");
if (id(temperatura).has_state()) {
it.printf(120, y_row1 + 25, id(font_main), id(c_white), TextAlign::TOP_CENTER, "%.1f", id(temperatura).state);
it.printf(120, y_row1 + 45, id(font_small), id(c_gray), TextAlign::TOP_CENTER, "°C");
}
// Lavadora
Color c_lav = id(c_white);
if (id(lavadora).state > 5) c_lav = id(c_accent);
it.print(200, y_row1, id(icon_font), c_lav, TextAlign::TOP_CENTER, "U000F0591");
if (id(lavadora).has_state()) {
it.printf(200, y_row1 + 25, id(font_main), id(c_white), TextAlign::TOP_CENTER, "%.0f", id(lavadora).state);
it.printf(200, y_row1 + 45, id(font_small), id(c_gray), TextAlign::TOP_CENTER, "W");
}
// 3. IMPRESORA P1S (ARRIBA)
int y_p1s = 110;
it.filled_rectangle(5, y_p1s, 230, 55, Color(20, 20, 20));
it.print(15, y_p1s + 8, id(icon_font), id(c_white), TextAlign::TOP_LEFT, "U000F042D");
it.print(45, y_p1s + 10, id(font_small), id(c_white), TextAlign::TOP_LEFT, "Bambu P1S");
std::string info_p1s = "---";
Color st_color_p1s = id(c_gray);
std::string raw_p1s = id(p1s_state).state;
if (raw_p1s == "running" || raw_p1s == "printing") { info_p1s = "Print"; st_color_p1s = id(c_good); }
else if (raw_p1s == "finish") { info_p1s = "Done"; st_color_p1s = id(c_accent); }
else if (raw_p1s == "idle") { info_p1s = "Idle"; st_color_p1s = id(c_gray); }
else { info_p1s = raw_p1s; }
it.printf(225, y_p1s + 10, id(font_small), st_color_p1s, TextAlign::TOP_RIGHT, "%s", info_p1s.c_str());
it.filled_rectangle(15, y_p1s + 38, 210, 8, id(c_gray));
if (id(p1s_pc).has_state()) {
float pct_p1s = id(p1s_pc).state;
int fill_w_p1s = (int)(210 * (pct_p1s / 100.0));
if (fill_w_p1s > 210) fill_w_p1s = 210;
if (fill_w_p1s > 0) it.filled_rectangle(15, y_p1s + 38, fill_w_p1s, 8, st_color_p1s);
}
// 4. SECCIÓN ARENERO + GATO (ABAJO)
int y_cat = 180;
// ZONA IZQUIERDA (BARRA ARENA)
it.print(10, y_cat, id(font_small), id(c_white), TextAlign::TOP_LEFT, "Arena");
if (id(nivel_arena).has_state()) {
it.printf(160, y_cat, id(font_small), id(c_accent), TextAlign::TOP_RIGHT, "%.0f%%", id(nivel_arena).state);
it.filled_rectangle(10, y_cat + 25, 150, 15, id(c_gray)); // Fondo
float pct_arena = id(nivel_arena).state;
if (pct_arena > 100) pct_arena = 100;
if (pct_arena < 0) pct_arena = 0;
int fill_w_arena = (int)(150 * (pct_arena / 100.0));
Color c_bar_arena = id(c_accent);
if (pct_arena < 15) c_bar_arena = id(c_alert); // Rojo si queda poca
if (fill_w_arena > 0) {
it.filled_rectangle(10, y_cat + 25, fill_w_arena, 15, c_bar_arena);
}
}
// ZONA DERECHA (GATO)
Color c_gato = id(c_good); // Verde por defecto
// Si el sensor binario dice que está lleno, poner ROJO FIJO
if (id(basura_llena).state) {
c_gato = id(c_alert);
}
// Dibujar icono
it.print(200, y_cat + 5, id(icon_font), c_gato, TextAlign::TOP_CENTER, "U000F011B");
// Texto debajo
if (id(basura_llena).state) {
it.print(200, y_cat + 35, id(font_small), id(c_alert), TextAlign::TOP_CENTER, "FULL");
} else {
it.print(200, y_cat + 35, id(font_small), id(c_gray), TextAlign::TOP_CENTER, "OK");
}Si queréis los links a los productos que he usado en el vídeo, aquí los tenéis:
USB-Uart: https://s.click.aliexpress.com/e/_EzR9oBW
Pinzas Cleqee: https://s.click.aliexpress.com/e/_EHpEAFQ
Cables dupont (deben ser hembra-hembra F-F): https://s.click.aliexpress.com/e/_EIwrOqk
Destornillador: https://s.click.aliexpress.com/e/_EuabsaM
Cupones Aliexpress activos:
✂️ ESCD02 (-2€ para más de 15€)
✂️ ESCD04 (-4€ para más de 29€)
✂️ ESCD07 (-7€ para más de 49€)
✂️ ESCD09 (-9€ para más de 69€)
✂️ ESCD13 (-13€ para más de 99€)
✂️ ESCD20 (-20€ para más de 159€)
✂️ ESCD25 (-25€ para más de 209€)
✂️ ESCD40 (-40€ para más de 329€)
✂️ ESCD55 (-55€ para más de 459€)
✂️ ESCD60 (-60€ para más de 569€)
✂️ IFPZ1LEE (-2$ para más de 15$)
✂️ IFPXGXTQ (-4$ para más de 29$)
✂️ IFPJKLT2 (-7$ para más de 49$)
✂️ IFP7NQTY (-9$ para más de 69$)
✂️ IFPHW4ZE (-13$ para más de 99$)
✂️ IFPPAHJX (-20$ para más de 159$)
✂️ IFPO38WM (-25$ para más de 209$)
✂️ IFPKSSAS (-40$ para más de 329$)
✂️ IFPMXUJ6 (-55$ para más de 459$)
✂️ IFP8CPVA (-60$ para más de 529$)
✂️ IFPJQUB0 (-3$ para más de 29$)
✂️ IFPCQSUE (-6$ para más de 59$)
✂️ IFPGPKET (-9$ para más de 89$)
✂️ IFPI3EHA (-16$ para más de 149$)
✂️ IFPFOX4R (-23$ para más de 199$)
✂️ IFPXB6DN (-30$ para más de 269$)
✂️ IFPKIJK1 (-40$ para más de 369$)
✂️ IFPN9ONF (-50$ para más de 469$)
✂️ IFPJHBPY (-60$ para más de 599$)
✂️ IFPKFHJJ (-70$ para más de 699$)
Más cupones:
✂️ IFPJ7ZZH (-3$ para más de 29$)
✂️ IFPQ0RSI (-6$ para más de 59$)
✂️ IFPACEWN (-9$ para más de 89$)
✂️ IFPRKZB1 (-16$ para más de 149$)
✂️ IFPZZAN6 (-23$ para más de 199$)
✂️ IFPDHCWF (-30$ para más de 269$)
✂️ IFPSXSV1 (-40$ para más de 369$)
✂️ IFPDF8RA (-50$ para más de 469$)
✂️ IFP4IO2P (-60$ para más de 599$)
✂️ IFPTTFK6 (-70$ para más de 699$)
Otros específicos:
✂️ IFPGMTKJ (-5$ para más de 39$)
✂️ IFPK9AKK (-10$ para más de 89$)
✂️ IFPDSISD (-25$ para más de 149$)
✂️ IFPZWXFH (-45$ para más de 259$)
✂️ IFPVHT9K (-60$ para más de 349$)
✂️ IFPK9FDL (-70$ para más de 459$)
✂️ IFPDLDPJ (-120$ para más de 599$)
Encuentra las ofertas:
🔥CUPONES LIMITADOS,¡ATENTO QUE VOLARÁN!🔥Por si queréis más ofertas podéis verlas aquí.
* Si os salen agotados, podéis avisarme en los comentarios e intento buscar nuevos.
La entrada Geekmagic Ultra: Mini estación meteorológica compatible con ESPHome se publicó primero en Domótica en Casa.