Green-terminal — a tiny ESP32 CRT for your Home Assistant notifications

I built a fake green-phosphor terminal that types out anything you throw at it. It's an ESP32-S3 with a 2" IPS LCD running custom ESPHome firmware. Send a message via HA service, MQTT, or a CLI and it taps it out one character at a time, lingers, fades to a soft afterimage, then moves on. When the queue is empty it falls back to a Matrix-style hex rain.

basic demo

The whole thing is intentionally soft and slow — closer to an early VT100 than a sharp modern OLED. There's a phosphor wash behind the text, a stippled scanline pattern, an optional Bayer-dithered corner vignette, a per-glyph bloom halo, and a brief afterimage when lines scroll off or the screen clears. The cursor and *emphasis* segments blink with a fade trail at the same rate.

How it talks to HA

Three ingress paths:

  • Native API actiongreen_terminal.add_message, add_alert, add_sticky_alert, skip, clear_queue, clear_alerts, etc.
  • MQTT topicsgreen-terminal/enqueue, green-terminal/alert, green-terminal/sticky-alert
  • Adopted device — every visual/timing knob (typing speed, scanline density, vignette strength, alert RGB, ghost duration, idle-rain speed/density, …) is exposed as a number entity under the device's Configuration panel. All values persist in NVS across reboots — so does the message queue itself.

Three message flavours:

  • Normal (configurable green by default)
  • Alert (configurable amber)
  • Sticky alert — pins the cycle on that message and re-types itself until cleared. The footer renders as - 1/1 - with surrounding dashes so it's obvious at a glance.

sticky demo

There's also a 3-pixel WS2812B strip on GPIO16 that's auto-driven by the alert dispatcher — amber pulse by default, but :warning:, :check:, :x:, :bolt:, :heart: glyphs trigger specific colour + effect combos (scan, flicker, etc.). It also surfaces as a "Status LEDs" light entity, so HA can override it as ambient lighting when no alert is active.

Idle state

A "Sleep when idle" switch turns this into a strict power-saver: empty queue + switch on → screen blanks, backlight fades off, SPI flush effectively stops.

When switch is off, a "Matrix" style rain is displayed.

rain demo

Hardware

  • Waveshare
    ESP32-S3-Touch-LCD-2
    (2.0"
    240×320 IPS, ST7789T3 + CST816D capacitive touch) — about US$15
  • 3 × WS2812B addressable LEDs on GPIO16 — pennies
  • That's it. USB-C for power and flashing.

Touch is wired and works (tap anywhere = skip to next message) but otherwise unused — plenty of headroom there.

My Enclosure


I have installed my setup into an old mini slide viewer. The screen sits where the slide would when inside, and the LED strip sits above it so that the lights shine thru the slot where the slide would be loaded.

You can see a short video of it

Source + docs

Everything's on GitHub including the YAML, a Python sender script with bundled --help-api / --help-controls / --help-leds references, and a Python simulator that renders the demo GIFs above off-board (mirrors the firmware's display pipeline exactly, so you can preview a settings change before flashing):

GitHub - jcdietrich/green-terminal: ESPHome firmware for a Waveshare ESP32-S3 2" LCD typewriter-style green-CRT message terminal, with a Python sender for the native API. · GitHub

Happy to answer questions. Open to ideas for new alert glyph mappings or LED effects.

5 Likes