Changelog: c100x_dashboard/CHANGELOG.md
The integration ships its own icon in
custom_components/c100x_dashboard/brand/(Home Assistant 2026.3+).
Design custom screens for a BTicino Classe 100X video intercom and push them to its display from Home Assistant โ with live sensor values. Compose screens by dragging elements onto an 800ร480 canvas in a web editor; the intercom shows the active screen and refreshes the values every second.
๐ฎ๐น Leggi in italiano
About this project โ authorship. Roughly 99% of this project was written by Claude (Anthropic's AI), working from the goals, testing, and on-device feedback provided by the repository owner. The owner directed the design, ran everything against real hardware, and validated each step; the code, the QML, the add-on and the integration were produced by Claude.
- Add-on (
c100x_dashboard/) โ a Home Assistant add-on that is both the editor (web UI) and the server the intercom polls. It reads entity values from HA, renders Jinja2 templates, and serves the active screen, images and icons over HTTP. - Intercom side (
c100x_dashboard/citofono/) โSchedaPage.qml, the renderer that runs on the intercom, plus the patch scripts. Can be installed automatically from the add-on via SSH. - Integration (
custom_components/c100x_dashboard/) โ an optional Home Assistant integration that adds services (show,hide,set_active) so you can trigger screens by name from automations.
Editor (add-on) โโsaveโโ> screen layouts (JSON)
โ
HA automation / Node-RED โshowโ> add-on โโGET /activeโโ> intercom (polls ~1s)
โ โ
โโ reads HA entity values โโ values refresh live
The intercom polls http://<add-on>:8099/active every second. When you "show" a screen, the
intercom wakes and displays it; values update on screen while it stays up. You can hide it on
command (handy for "show during a blackout, hide when power is back").
- A BTicino Classe 100X with SSH access and the community controller (fquinto/bticinoClasse300x / slyoldfox/c300x-controller).
- Home Assistant with the Supervisor (Home Assistant OS or Supervised), needed to run add-ons.
- The intercom and HA on the same LAN; the intercom must be able to reach HA over HTTP.
- Node.js present on the intercom (it ships with the controller setup) for the QML patch step.
As a local add-on (simplest):
- Copy the
c100x_dashboard/folder into your Home Assistant/addons/directory (via the Samba, SSH & Web Terminal or Studio Code Server add-ons). - Settings โ Add-ons โ Store โ โฎ โ Check for updates. It appears under Local add-ons.
- Open it โ Install (the first build also downloads the MDI icon set), then Start.
- The editor shows up in the HA sidebar (Ingress). It's also at
http://IP_HA:8099/.
As an add-on repository (one click for others): push this repo to GitHub, then in HA add the repo URL under Store โ โฎ โ Repositories. The add-on installs from there.
To ship changes, bump
version:inconfig.yaml: Home Assistant then offers an Update for the add-on (Settings โ Add-ons โ Store โ โฎ โ Check for updates). The add-on's โฎ โ Rebuild is only needed to force a fresh build without a version change.
Elements โ add from the palette: text, sensor value, entity icon, template, image, icon, shapes, line, arrow.
- Sensor value: search the entity (autocomplete) and get a live preview. Optionally show a
specific attribute instead of the state, append the unit of measurement automatically,
and format dates/times (e.g.
DD/MM/YYYY,HH:mm). - Entity icon: icon and colour follow the entity state. You can force a specific icon while keeping the state-driven colour, or drive the colour from a conditional template.
- Template: write Jinja2 with basic markdown (bold, italic, headings, lists, line breaks), rendered like a Lovelace markdown card. A second template can set the colour conditionally (a direct colour, or true/false โ two configurable colours).
Intercom buttons โ click a key on the on-screen intercom to bind a Home Assistant action to it. The front keys (1โ4, โ , lock, eye), the wheel (up/down/OK) and both handsets are supported. Each button can show an on-screen message (which may contain Jinja2 for dynamic text) and optionally light up on press. Action data may also contain Jinja2 templates.
Editing โ drag to move, bottom-right handle to resize, top handle to rotate. Extras:
- Arrow keys move by 1px (Shift = 10px), PgUp/PgDn change stacking order, Del removes.
- Select several elements (Shift-click or rubber-band) to align them; group with Ctrl+G, ungroup with Ctrl+Shift+G.
- Zoom with the wheel, pan with the middle mouse button, double middle-click to reset.
Screens โ name the layout and Save; press Show now (with a duration) to display it on the intercom. From the home screen you can export all screens to a backup file and import them back โ handy so you don't lose your work if you reinstall the add-on.
Press Intercom in the editor, enter the intercom's SSH host/user/password and the
add-on URL as the intercom sees it (e.g. http://192.168.1.10:8099), then
Install / update. This uploads SchedaPage.qml, patches main.qml (with a backup),
patches MainPage.qml โ the native default menu โ to enable wheel navigation there too
(a single added property, backed up first), uploads the live-view component and the
ptrace-inject button-injection tool (see below), replaces the
c300x-controller bundle on the intercom
with a build that adds start/stop support for both (original bundle backed up first, its
:8080 page also got a small visual refresh and is now fully in English), and reboots. A
checkbox lets you store the SSH password in the add-on or be asked each time.
Prefer manual install? See c100x_dashboard/citofono/README.md.
Press Live in the editor for a real, interactive mirror of the intercom's screen: the add-on proxies a WebSocket connection through to a small VNC server on the intercom, and noVNC (a mature, vendored library) handles the actual protocol in the browser โ this replaced an earlier, simpler approach (the add-on polling a still image) that turned out to be too heavy for the intercom's weak CPU and could lock up its VNC server under load.
While it's open you also get a button panel (1-7, wheel up/down/OK, answer/hang up/mute)
that really presses the intercom's own buttons at the system level: a small tool
(ptrace-inject) attaches to the intercom's graphics process and injects the press directly
into its system calls, indistinguishable from a physical press as far as the firmware is
concerned. Unlike an approach based on calling QML functions, this works everywhere โ
inside screens created with this add-on, and in the intercom's native default menu too.
This needs the custom controller bundle mentioned above (it's what starts/stops both the VNC server and the injector on the intercom), so both are part of the standard SSH install now, not optional.
A small backlight indicator (screen on/off) is always visible both in the live view and in the editor's top bar, reflecting the intercom's real state.
A note on risk. System-level injection via
ptraceis inherently more invasive than a plain QML function call โ during development it caused one intercom reboot (not reproduced since, after a fix: it now delivers one input event at a time, matching how the real device driver behaves). For this reason the injector is only started while the Live view is open, and stopped a few seconds after you close it or lose connection โ it is not meant to run as a permanent background service.
The integration is separate from the add-on and is not installed automatically โ the
add-on can't write into HA's custom_components/. You only need it if you want the convenient
show / hide / set_active services in your automations, or the sensor/light entities below;
without it you can still drive the add-on over REST (see below).
- Copy
custom_components/c100x_dashboard/into your HAconfig/custom_components/(or add this repo to HACS as a custom integration). - Restart Home Assistant.
- Settings โ Devices & Services โ Add integration โ C100X Dashboard, and enter the add-on URL
(e.g.
http://192.168.1.10:8099).
See custom_components/c100x_dashboard/CHANGELOG.md
for the integration's own version history (independent of the add-on's).
Besides the show/hide/set_active services, the integration creates a few entities
under the same device:
- Intercom renderer (
update.*) โ tracks whether the QML patch on the intercom matches the one shipped by this add-on version. - Pagina attiva (
sensor.*) โ which screen is actually showing on the physical display right now (idlewhen the screen is free: home, closed via the side wheel, turned off by the intercom's native standby, or interrupted by a real call). Polled every 2s against a value the add-on already keeps in memory โ pushed live by the patched QML and cross-checked against the physical backlight read from sysfs, so it doesn't stay stuck on a screen name once the display is truly off โ so this has no extra impact on the intercom itself. - Citofono occupato (
binary_sensor.*) โ on while the intercom's camera is in use, for any reason: a real doorbell press, the WebRTC bundle, the BTicino app locally, or the BTicino app over LTE. Detected at the OpenWebNet bus level, not the network level, so it covers all of the above with a single mechanism. Requires MQTT โ see below. - Ponte MQTT citofono online (
binary_sensor.*) โ whether the MQTT bridge below is reachable. - Retroilluminazione display (
light.*) โ the intercom display's backlight. Reflects the real state (read every ~2s) and can be turned on/off directly from Home Assistant.
"Citofono occupato" needs two things you set up yourself, both optional:
- Home Assistant's own MQTT integration, configured against the same broker the intercom can reach.
- TcpDump2Mqtt installed and running on the
intercom (
/etc/tcpdump2mqtt), publishing raw OpenWebNet traffic toBticino/txand its own online/offline status toBticino/LastWillT.
If either piece is missing, both entities degrade gracefully instead of breaking the integration:
- No MQTT integration configured in HA โ both entities stay
unavailable, with a one-time warning in the HA log. - MQTT configured but the bridge never publishes โ
citofono_occupatodefaults tooff(assumes free) andponte_mqtt_citofono_onlinestaysunknownuntil the bridge's retained Last Will message arrives.
Known gotcha on the intercom side: TcpDump2Mqtt's own startup script checks for a default
gateway only once; on a slow Wi-Fi reconnect after a reboot it can give up before wlan0 gets
an IP, and nothing restarts it afterwards. If binary_sensor.ponte_mqtt_citofono_online stays
off after a reboot, SSH in and run /etc/tcpdump2mqtt/TcpDump2Mqtt.sh again โ or patch the
gateway check into a retry loop (see c100x_dashboard/citofono/README.md).
With the integration. Once installed (see above), you get these actions:
# Show a named screen, keep it until hidden
action: c100x_dashboard.show
data:
name: consumi
duration: 0
# Hide whatever is on screen
action: c100x_dashboard.hide
# Just set the active screen (without showing)
action: c100x_dashboard.set_active
data:
name: consumiWithout the integration (REST). The add-on is a small REST server, so two calls work from
Node-RED (http request nodes) or HA rest_command:
rest_command:
citofono_mostra:
url: "http://192.168.1.10:8099/api/show"
method: POST
content_type: "application/json"
payload: '{"name":"{{ name }}","duration":{{ duration | default(0) }}}'
citofono_nascondi:
url: "http://192.168.1.10:8099/api/hide"
method: POST| Method | Endpoint | Purpose |
|---|---|---|
| GET | / |
editor (Ingress) |
| GET/PUT/DELETE | /api/layouts[/:name] |
list / save / delete layouts |
| GET | /api/layout-live/:name |
layout with values resolved to the current state (thumbnails) |
| GET/POST | /api/export ยท /api/import |
back up / restore all screens |
| POST | /api/template-preview |
render a Jinja2 template for the editor preview |
| POST | /api/active |
set the active layout |
| POST | /api/show |
show now ({name?, duration?}) |
| POST | /api/hide |
hide |
| GET/POST | /api/scheda-state |
what's actually on screen right now ({name} or {state:"idle"}), reported live by the patched QML |
| GET | /api/entities ยท /api/icons ยท /api/entity-icons |
autocomplete sources |
| GET | /icon/:name ยท /image/:name ยท /ha-image/:name |
icons / images for editor + intercom |
| POST | /api/citofono/install |
upload + patch + reboot via SSH |
| POST | /api/live/start ยท /api/live/stop |
start/stop the live view (VNC + button injector) on the intercom |
| WS | /api/live/ws |
WebSocketโTCP proxy to the intercom's VNC server, consumed by noVNC |
| POST | /api/live/button |
press/release a real intercom button ({button, phase}) โ proxied to the controller's ptrace-inject endpoint |
| GET/POST | /api/backlight-state |
current backlight state, reported live by the patched QML |
| GET/POST | /api/backlight-command |
request the intercom to turn the backlight on/off |
| GET | /active |
for the intercom: active layout with resolved values |
- Port 8099 is exposed on the LAN without auth (the intercom must reach it). Fine on a home LAN.
- The intercom's display has a limited colour gamut: flat graphics, icons and shapes look great; photographic images may shift in colour.
- Only MDI icons are supported (HA's built-in set); custom icon packs aren't served to the intercom.
- Template elements support basic markdown (bold, italic, headings, lists, line breaks): the intercom renders an HTML subset (Qt 5 RichText), so very complex markdown may not render exactly.
- The SSH password, if saved, is stored in the add-on's
/datain clear text and never returned to the browser. - The live view uses real CPU on the intercom (VNC server + button injector): fine for occasional
use, but not meant to be left running continuously โ see the risk note above about
ptrace.
This project stands on the shoulders of prior reverse-engineering work by the BTicino intercom community:
- slyoldfox/c300x-controller โ the on-device controller this project relies on (Node runtime, HTTP endpoints, Home Assistant bridge).
- slyoldfox/c300x-dashboard โ the inspiration: a controller-fed QML dashboard for the C300X (Qt 4.8.7 / QtQuick 1.x). Since it states "Bticino c100x devices are untested" and targets a different Qt/QtQuick generation, the renderer here was written from scratch for the C100X (Qt5 / QtQuick 2.x).
- fquinto/bticinoClasse300x โ the modified firmware that makes root/SSH access possible.
- novnc/noVNC โ the VNC client library (vendored) that powers the live screen view in the browser.
- Roboto (Apache License 2.0) โ the bundled UI font, shared by the editor and the intercom renderer.
MIT โ see LICENSE.
