fluepdot hangs a real, mechanical flip-dot display — the
kind of clattering dot-matrix panel that used to sit above bus doors — directly off Home
Assistant. Not "one switch that sends a string": a device, entities, live preview, a priority
queue, quiet hours, and thirteen actions to draw text, bars, icons, marquees and raw frames on it.
This integration was written almost entirely by an AI (Claude, via Claude Code) for my own flip-dot display at home. I described what I wanted, reviewed the result, tested it against the real hardware, and pushed it. I'm not a Home Assistant integration developer, and I won't pretend otherwise.
It works well for me — the whole thing has been exercised live against a real fluepboard, including the priority queue, quiet hours, font extraction and the render pipeline. If it's useful to you too, great, help yourself. If you find a bug or it doesn't work with your setup, please open an issue — I'll look at it when I can, but there's no support contract behind this, and pull requests are very welcome if you'd rather fix it yourself.
- A fluepdot flip-dot display: the fluepboard controller (ESP32-based) driving one or more flip-dot panels, running the stock fluepdot firmware. This integration talks to the firmware's built-in HTTP API — it does not replace or modify the firmware.
- The board reachable over HTTP from your Home Assistant instance (same LAN/VLAN). The firmware has no authentication, so keep it on a network segment you trust.
- Home Assistant 2024.6 or newer. Developed and tested against 2026.8.
This integration isn't in the default HACS catalog, so add it as a custom repository:
- HACS → the ⋮ menu (top right) → Custom repositories.
- Repository:
https://github.com/Subnet-Zero/fluepdot-ha, category: Integration. - Find Flipdot (fluepdot) in HACS and install it.
- Restart Home Assistant.
Copy custom_components/fluepdot from this repository into your Home Assistant config/custom_components/ folder, then restart Home Assistant.
Settings → Devices & Services → Add Integration → "Flipdot", then enter the board's host or
IP address (e.g. 10.0.0.164). Home Assistant confirms the connection by reading the current
framebuffer, which also tells it the display's actual size — nothing to configure by hand.
One device, 24 entities:
| Entity | What it does |
|---|---|
image.flipdot_preview |
Live PNG snapshot of whatever is currently on the board |
text.flipdot_message |
Free-text field — whatever you type gets shown |
select.flipdot_mode |
rotation · date · manual · off |
select.flipdot_font / select.flipdot_alignment |
Default font and text alignment |
switch.flipdot_display |
Display on/off (off = blank, and stops writing anything) |
switch.flipdot_quiet_hours |
Enable/disable the quiet-hours gate |
time.flipdot_quiet_hours_start / time.flipdot_quiet_hours_end |
Quiet hours as entities — editable from the dashboard or an automation |
switch.flipdot_differential_rendering |
Toggle the firmware's differential draw mode |
number.flipdot_rotation_interval / number.flipdot_set_delay |
Seconds per rotation page · per-column drive timing |
sensor.flipdot_content |
What's showing right now, with source, priority, expiry and counters as attributes |
sensor.flipdot_pixels_on / sensor.flipdot_page |
Diagnostics |
binary_sensor.flipdot_online / binary_sensor.flipdot_foreign_content / binary_sensor.flipdot_quiet_hours |
Connectivity · someone else wrote to the board · quiet hours active |
button.flipdot_clear / button.flipdot_date / button.flipdot_test_pattern / button.flipdot_invert / button.flipdot_exercise_pixels |
One-shot actions, including a pixel-exercise routine against stuck dots |
notify.flipdot_notification |
The board as a regular notification target (notify.send_message) |
fluepdot.send_text · send_lines · marquee · draw · draw_bar · set_pixel ·
clear_pixel · clear · effect (wipe / dissolve / matrix / blink / snow / invert) ·
show_page · reload_pages · extract_fonts · set_rendering_timings — all with UI selectors,
so they're usable straight from the Developer Tools → Actions tab without writing YAML.
Two render paths, picked automatically or explicitly via mode:
device— the board renders its own built-in fonts, exactly like calling its HTTP API directly. Fast, matches the firmware's native look.compose— Home Assistant builds the full 115×16 (or whatever your panel size is) frame in Python and sends it in one shot. This is what makes centering, multi-line text, icons, bars and word wrap possible — the firmware's own renderer can't do any of that.
Anything with align other than left automatically upgrades to compose, since the firmware
has no concept of alignment on its own.
On first start the integration writes fluepdot_pages.yaml into your Home Assistant config
directory, with one active page (today's date) and a handful of disabled examples (clock,
next trash pickup, weather, PV power with a bar, "laundry done"). Each page is a small block of
Jinja templates:
pages:
- id: date
name: Date
enabled: true
duration: 60
mode: device
font: DejaVuSans12bw_bwfont
text: >
{{ now().strftime('%a, %d %B') }}
- id: weather
name: Weather
enabled: false
duration: 30
mode: compose
align: center
lines:
- "{{ state_attr('weather.home','temperature') | round(0) }}°"
- "{{ states('weather.home') }}"Edit the file, then call fluepdot.reload_pages — no restart needed. A page can carry a Jinja
condition (only shown when true), an icon, and a bar block for a progress bar.
Four levels — background (rotation) < normal < high < alarm. A message at a given
priority can only be replaced by one of equal or higher priority; once its duration runs out,
the display falls back to rotation on its own.
Quiet hours are hard: between time.flipdot_quiet_hours_start and time.flipdot_quiet_hours_end, nothing gets written
at all — not even alarm priority — and suppressed messages are dropped rather than queued up
for when quiet hours end. A flip-dot display is audible, and 6:30 AM is not the time to replay
everything that happened overnight.
POST /pixeldoesn't actually flip a dot, even though the firmware answers with HTTP 200 andGET /pixelreports the new value.fluepdot.set_pixel/clear_pixelwork around this by rewriting the whole framebuffer instead of poking a single coordinate.- There's no authentication and no endpoint to clear the whole display in one call — this
integration is the only thing that should be writing to the board if you want predictable
behavior.
binary_sensor.flipdot_foreign_contentflags it when something else writes anyway. fluepdot.extract_fontsmeasures the firmware's built-in fonts by rendering every character once and reading the frame back. It clatters for several minutes, so it never runs on its own — only when you call it.
Issues and pull requests are welcome — for a bug report, the board's firmware version and
whatever's in sensor.flipdot_content's attributes at the time usually helps. This project doesn't
come with a roadmap; it does what my own display needed.
MIT — do what you like with it.

