A tiny Home Assistant panel server for low-power browsers, old tablets, kiosk displays, and digital photo frames.
Instead of loading the full Home Assistant frontend, HA Light Panel serves a small SVG/HTML interface and pulls state from the Home Assistant REST API. The browser does very little work, which makes it a good fit for slow Android WebViews and cheap wall panels.
If this saves you a little time, buy me a coffee. Add Buy me a coffee in the PayPal note so I know what it was for.
- Lightweight climate dashboard with six room cards
- Desktop visual SVG panel layout builder with native config export
- Compact SVG UI, no frontend framework
- Landscape kiosk layout that reflows for portrait phones
- Home Assistant REST polling
- Config-driven entity mapping
- Temperature, humidity, battery, HVAC mode, comfort band, and status-panel cards
- Optional camera snapshot grid
- Optional settings page for humidity-biased cooling and seasonal mode
- Optional Blink live-view proxy integration hooks, health modal, and restart control
- Service-backed action buttons for comfort tweaks and room balancing
- HACS integration that proxies the panel through Home Assistant, so it works over Nabu Casa and anything else that only tunnels port 8123
- HAOS add-on, Docker, and systemd deployment examples
- No runtime npm dependencies
Choose the deployment that owns the panel service: Home Assistant add-on, Docker Compose, managed systemd, or manual Node.js/custom service. The complete commands, first-run configuration, update path, and optional HACS proxy steps are in the installation guide.
The panel runs on its own port, which anything that only tunnels Home Assistant
core — Nabu Casa Cloud especially — cannot reach. The bundled ha_light_panel
integration reverse-proxies the panel at /api/ha_light_panel/ so it is
reachable wherever Home Assistant is.
- In HACS, click ⋮ → Custom repositories.
- Add
https://github.com/Teethree89/ha-light-panelwith type Integration. - Download HA Light Panel and restart Home Assistant.
- Settings → Devices & Services → Add Integration → HA Light Panel, then
point it at the panel (
http://127.0.0.1:8890for a systemd install on the same host).
This proxies a panel you are already running; it does not run one. See docs/integration.md.
After restarting Home Assistant, the integration also adds an admin-only HA Light Panel item to the Home Assistant sidebar. It keeps the normal HA navigation visible while switching between the panel Overview and the visual Builder. Its Device links page copies the recommended panel and builder URLs for phones, tablets, desktops, and wall displays. If the panel is not connected, Find panel address tests safe local candidates and only offers services that identify as HA Light Panel. The original direct URLs remain available as well.
The easiest install path if you're running Home Assistant OS or Supervised:
- Go to Settings → Add-ons → Add-on Store.
- Click ⋮ → Repositories and add:
https://github.com/Teethree89/ha-light-panel - Find HA Light Panel, click Install, then set your
ha_tokenandha_urlin the add-on options and start it. - To show your own sensors, copy examples/starter.json to
/config/ha-light-panel.json, edit the entity IDs, and restart the add-on. Without it the panel renders its built-in reference layout.
The panel will be available at http://<your-ha-host>:8890/.
See addon/DOCS.md for the full option reference.
To see the panel before wiring anything up:
npm run demoThen open http://127.0.0.1:8890/.
This starts a stub Home Assistant with plausible readings and points the panel
at it — no token, no real HA, nothing touching your own entities. The panel
speaks to Home Assistant over exactly two REST endpoints (GET /api/states and
POST /api/services/...), so the stub is indistinguishable from the real thing
as far as the panel is concerned. Buttons work; they log the service call
instead of firing it.
New here? The getting started guide
walks through the whole thing, including how to find your entity ids and what to
check when a card shows --.
- Copy the starter config:
cp examples/starter.json config.jsonexamples/starter.json is a small, commented config with three room cards.
examples/frameo-climate.json is the everything-switched-on reference — useful
to crib from, but a lot to edit as a first step.
- Create a Home Assistant long-lived access token.
In Home Assistant, open your profile, create a long-lived access token, and put it in .env or your service env file.
cp .env.example .env- Edit
.env:
HA_URL=http://homeassistant.local:8123
HA_TOKEN=your-token-here
CONFIG_PATH=./config.json-
Edit
config.jsonand replace the example entity IDs with your entities. -
Run it:
npm run validate
npm startOpen:
http://localhost:8890/
Work in progress. The builder safely exports a reviewed
config.json, but it is not yet a complete replacement for every bespoke SVG region or a general Lovelace editor. Back up your config and test changes before using them on a primary display. See the builder status and limits.
Open the builder from a desktop browser at one of these URLs:
- Direct Node, Docker, or add-on install:
http://<panel-server>:8890/builder - Through the optional HA Light Panel HACS integration:
https://<your-ha-url>/api/ha_light_panel/builder
The builder loads any existing native SVG composition first. Those imported
regions retain their actions and live values; their geometry and labels are
exported under panel.layout.nativeCards. New cards are intentionally generic:
add a Display card for any entity or attribute, an Action button for a
Home Assistant service or script, or fixed Text. Action buttons can have
confirmation, success, and failure modals. Drag, resize, duplicate, and style
each card with colours and font sizes. Canvas presets cover common landscape
and portrait displays; portrait mode can stack cards automatically or keep a
separately adjusted layout.
When the panel has a working HA_URL and HA_TOKEN, Entity ID fields offer
every Home Assistant entity by ID and friendly name. The draft stays in the
browser. Copy config and Download config export a reviewed
config.json; install that file and restart the panel service to apply it.
The builder never writes to Home Assistant or overwrites a running service
configuration from the browser.
Select Balance rooms in the Layout Builder to configure its Home Assistant
service or script and optional JSON service data. Its action inspector also
offers checkboxes for the HVAC readiness guard, a confirmation modal, and
success/failure modals. The readiness guard is enabled by default and is
enforced by the server as well as the browser. Action and modal settings are
saved under panel.layout.nativeCards.btnBalance.action; the prior
panel.actions.assist remains the fallback for existing configurations.
Generic Action buttons use the same modal controls, but save under their entry
in panel.layout.cards. The panel resolves the card ID and configured service
on the server, so a browser cannot substitute a different service or payload.
For the full Docker/Compose install, configuration, restart, and update steps, see the installation guide.
cp examples/starter.json config.json
cp .env.example .env
docker compose -f docker-compose.example.yml --env-file .env up -d --buildFor standard and custom-service adoption instructions, including the precise updater service name, see the installation guide.
On a Debian-style host with Node.js 20+ use the managed installer:
curl -fsSL https://raw.githubusercontent.com/Teethree89/ha-light-panel/main/scripts/bootstrap.sh | sudo bashIt keeps a tagged source checkout, deploys the released server code, preserves
/etc/ha-light-panel.env and /opt/ha-light-panel/config.json, and installs
an on-demand updater. When adopting a legacy config-less panel, it first saves
the exact built-in Frameo layout as /opt/ha-light-panel/config.json rather
than replacing it with the generic example. Re-run the same command to upgrade. Set
INSTALL_AUTOUPDATE=1 before the command to enable the optional daily check.
The HA sidebar Overview detects this managed systemd installation, reports live panel and HA-data status, and offers Update panel now. The update status bar follows the service lifecycle while it restarts; it is deliberately not presented as a fake download percentage. The Node process cannot run privileged commands: it writes a local request that a root-owned systemd path unit consumes.
The same Overview gives the appropriate instructions for a Home Assistant add-on, Docker/Compose, and manual/custom-service deployment. Only a detected managed systemd updater gets an in-app update button; add-ons, containers, and custom hosts remain owned by their respective deployment tools.
From a local checkout, sudo scripts/install-systemd.sh performs the same
install. Then edit the environment and config only on first setup:
sudo nano /etc/ha-light-panel.env
sudo nano /opt/ha-light-panel/config.json
sudo systemctl restart ha-light-panelIf an older installation already has a differently named service, adopt it by
passing its service name, app directory, environment file, and service user to
the same bootstrap script. This preserves the existing environment and
config.json, installs the updater units beside that service, and restarts it.
If a prior adoption accidentally created the generic example file, export the
legacy built-in layout safely. The command first saves the current file as a
timestamped config.json.before-layout-capture.* backup:
sudo env APP_DIR=/opt/frameo-svg-dashboard \
bash /opt/src/ha-light-panel/scripts/capture-default-config.shFor a panel running on the same Home Assistant host, set the service-only API
address to http://127.0.0.1:8123; use the browser-facing HA URL separately
when needed. Do not put a token in config.json:
sudo nano /etc/frameo-dashboard.env
# HA_URL=http://127.0.0.1:8123
# HA_BROWSER_URL=https://ha-server.home
# HA_TOKEN=your-long-lived-access-token
sudo systemctl restart frameo-svg-dashboardThen open:
http://your-server:8890/
See the configuration guide.
The most important sections are:
homeAssistant: HA URL and optional browser URLpanel.metrics: top-card entity IDspanel.rooms: six room cardspanel.mode: which sensors decide the headline statuspanel.comfort: comfort-band targets and schedule statepanel.actions: service calls for buttonspanel.statusPanel: optional side status widgetpanel.balance: gating for the Balance Rooms buttoncameras: optional camera snapshot/live mappings
Config keys merge over built-in defaults, so a config file only needs the keys it changes, and environment variables win over both. The panel logs which config file it loaded at startup. If the file is missing or fails to parse, it says so and falls back to the defaults rather than failing to start.
HA Light Panel can show Blink snapshots by using normal Home Assistant camera entities. For direct live view, push-to-talk, local clip browsing, and manual snapshot refresh buttons, it expects the separate Blink Liveview Proxy package:
The proxy package contains:
- a Home Assistant custom integration that exposes
camera.blink_live_*entities and local/api/blink_liveview_proxy/...routes - a small Python service that logs in to Blink with BlinkPy and bridges the direct Blink live-view stream
- install, configuration, systemd, and known-limitations docs
The proxy repo ships both a repository.yaml and a hacs.json, so it installs
either way — the two halves are separate mechanisms and you need both:
| Piece | Where it goes | How to install |
|---|---|---|
| Proxy service | Add-on store | ⋮ → Repositories → add the proxy repo URL, install its add-on |
| Proxy integration | HACS | HACS → Integrations → ⋮ → Custom repositories → add the proxy repo URL |
HA Light Panel has two separate pieces: the panel service is installed as an add-on, Docker/Compose deployment, systemd service, or manual Node process; the optional Home Assistant proxy integration is installed through HACS. HACS does not run the panel service by itself. See the installation guide for the exact path for each host.
Without the proxy installed the panel still works: snapshots, climate, rooms, and motion toggles all use plain Home Assistant APIs. Only live view, push-to-talk, clip browsing, and manual snapshot refresh need it — those routes return an error until the proxy is running.
In this panel's camera config, set sourceEntity to the normal HA Blink camera
for snapshots, and set liveEntity to the matching proxy camera for live view.
The optional liveProxyEntity and snapshotRefreshPath settings let the panel
show proxy health and request fresh Blink snapshots from the proxy integration.
See the camera configuration notes in the camera configuration guide, and the proxy setup guide in the Blink proxy install guide.
For a Frameo or similar Android picture frame, see the Frameo Fully Kiosk guide.
For USB microphone, SSH, OTG host mode, and push-to-talk notes on Frameo-style frames, see the Frameo USB microphone guide.
The panel is designed as a 1280x800 canvas and scales to fit, so any landscape display renders it as intended — frames, tablets, and desktop browsers all get the native layout.
Portrait screens (a phone, or the panel embedded in the Home Assistant app) get
a reflowed layout instead of a letterboxed one: the status cards become a 2x2
grid, rooms stack into a single full-width column, action buttons stack, and the
page scrolls vertically. The camera grid reflows to one camera per row. The
switch is driven by @media (max-aspect-ratio: 1 / 1) and reverses exactly when
you rotate back, so landscape behaviour is untouched.
Short version:
- Prefer
http://SERVER_IP:8890/over.localnames on Android WebView. - Use Fully Kiosk Browser as the full-screen browser.
- Use Taskbar or another edge launcher if you want to switch between the photo-frame app and the panel.
- Use ADB for sideloading and setup when the frame exposes it.
Plain HTTP is usually fine for a trusted LAN display. HTTPS becomes important for browser microphone access, push-to-talk, remote access, or anything leaving your LAN.
See the HTTPS guide.
This app is designed for a trusted LAN. It holds a Home Assistant token and exposes controls. Do not publish it directly to the internet. If you need remote access, put it behind HTTPS and authentication.
See Security Notes.
The normal HA frontend is excellent, but it can be heavy for older Android frames and kiosk browsers. HA Light Panel keeps Home Assistant as the backend and turns the display into a dumb, fast, low-power panel.
