Standalone physical status monitor for the Telephone-Booth art installation. It reads the authenticated Telephone-Booth Operator API and renders booth state, today/overall counters, time, optional weather, and system health through BUSY Cloud.
The service is deliberately independent of the Operator deployment. Run one instance on an always-on home server, Portainer host, or cloud container.
Open the interactive BusyBar display demo
to preview the idle carousel, live clock layout, smart weather details, active
states, and warnings. The self-contained GitHub Pages source lives in
demo/.
While the booth is healthy and idle, the front rotates through full-width gradient cards:
CALLS / DAY / nMSGS / DAY / nCALLS / ALL / nMSGS / ALL / n- A 24-hour local clock with a weekday/date card
- Current weather, when Home Assistant weather is configured
Set BUSY_BAR_FRONT_ROTATION_SECONDS from 3 to 600 seconds (10 minutes) to
control how long each idle card remains visible.
Call activity interrupts the carousel immediately with CALLING, PLAYING,
RECORDING, or SENDING. Warnings and faults remain pinned until recovery.
Calls, messages, and active states use Canadian telephone-booth pixel art. Weather uses condition-specific artwork for every Home Assistant weather state. Its detail badge prefers precipitation probability, then a meaningful humidex or wind-chill difference, then the daily high and low.
The rear display keeps the existing booth overview, system, and network pages,
and adds a SMART HOME page. It shows the Start and dial scene mappings,
whether the configured lights currently match Comfy, the target and current
brightness levels, and the last smart-home action. A scene button temporarily
opens this page for four seconds, then returns to the previous page. Press Back
to cycle backward through all four rear pages.
- A BUSY Bar linked to BUSY Cloud
- A BUSY Cloud API token from https://cloud.busy.app/api-tokens
- A monitor-scoped token from the Operator console
- Outbound HTTPS/WSS access to BUSY Cloud and the Operator API
- Optional Home Assistant access for weather
No inbound ports or database access are required.
Create a stack from compose.yaml, then define:
| Variable | Value |
|---|---|
BUSY_BAR_CLOUD_TOKEN |
BUSY Cloud token |
BUSY_BAR_OPERATOR_API_URL |
Operator origin, without /v1 |
BUSY_BAR_OPERATOR_TOKEN |
Monitor-scoped Operator token |
BUSY_BAR_BOOTH_ID |
Usually booth-01 |
BUSY_BAR_LOCAL_URL |
Optional LAN URL for physical input events |
BUSY_BAR_LOCAL_ACCESS_KEY |
Password for the BUSY Bar LAN API |
BUSY_BAR_START_SCENE_ID |
Scene for Start/Pause, such as scene.comfy |
BUSY_BAR_DIAL_SCENE_ID |
Scene for dial press, such as scene.good_night |
BUSY_BAR_WEATHER_ENABLED |
Set true to add Home Assistant weather |
BUSY_BAR_HOME_ASSISTANT_URL |
Home Assistant origin |
BUSY_BAR_HOME_ASSISTANT_TOKEN |
Home Assistant long-lived access token |
BUSY_BAR_START_TOGGLE_LIGHT_IDS |
Optional comma-separated lights turned off by a second Start/Pause press |
Deploy exactly one replica. The container exposes no ports.
Configure the password-protected local URL and access key to receive physical dial events. Turning the dial cycles through Weather, Clock, Weather + Clock, Telephone Booth counters, and the full carousel. Active booth states and health warnings continue to override the selected idle mode.
The Start/Pause button and dial press can activate Home Assistant scenes with
BUSY_BAR_START_SCENE_ID and BUSY_BAR_DIAL_SCENE_ID. Both trigger only on
the initial button press; dial rotation continues to select the idle display
mode. A successful scene activation plays a short color-sweep confirmation on
the front display. When BUSY_BAR_START_TOGGLE_LIGHT_IDS is configured, the
Start/Pause button compares those lights with the on state and brightness saved
in the scene. If every light matches, it turns only those lights off and
displays LIGHTS OFF; otherwise it activates the configured scene. For Comfy, use
light.kitchen_island_lights,light.kitchen_main_lights,light.living_room_main_lights
to make a second press turn off the overhead lighting while leaving the lamps
on.
- Deploy the matching Operator release first so
GET /v1/monitor/summaryis available. - In Portainer, replace any service that uses the Operator API image and
node dist/busy-bar-worker.jswithcompose.yaml. - Keep the existing BUSY Cloud token, then set the Operator URL, a monitor-scoped Operator token, and the booth id.
- Use
75seconds for status freshness,20seconds for system freshness, andAmerica/Torontofor the daily reset unless the booth configuration differs. - Pull
ghcr.io/djensenius/busybar:latestand redeploy the stack. - Verify the idle carousel, active-call overrides, structured logs, and recovery after restarting the container.
After the first image is published, ensure the GHCR package is public before pulling it anonymously from Portainer.
mise install
pnpm install
cp .env.example .env
pnpm devRun the checks with:
pnpm checkSee .env.example for every setting. Notable defaults:
- Status is stale after 75 seconds because the booth heartbeat defaults to 30 seconds.
- System telemetry is stale after 20 seconds because snapshots normally arrive every five seconds.
- Today counters reset in
America/Toronto; total counters cover the active installation. - Counter summaries refresh every 30 seconds.
- The clock uses 24-hour time and can be disabled with
BUSY_BAR_CLOCK_ENABLED=false. - Weather refreshes every 10 minutes and disappears from the carousel after one hour without a successful Home Assistant response.
BUSY_BAR_STALE_AFTER_SECONDS remains a legacy shared fallback. Prefer the
separate status and system thresholds.
Set BUSY_BAR_WEATHER_ENABLED=true, then provide a Home Assistant URL,
long-lived access token, and weather entity. The recommended Environment Canada
configuration is:
BUSY_BAR_WEATHER_ENTITY_ID=weather.patio_environment_canada_forecast
BUSY_BAR_SUN_ENTITY_ID=sun.sun
BUSY_BAR_WEATHER_HUMIDEX_ENTITY_ID=sensor.patio_environment_canada_humidex
BUSY_BAR_WEATHER_WIND_CHILL_ENTITY_ID=sensor.patio_environment_canada_wind_chill
BUSY_BAR_WEATHER_PRECIPITATION_ENTITY_ID=sensor.patio_environment_canada_chance_of_precipitationThe optional companion sensors improve the detail badge. The weather entity
still supplies current conditions, temperature, and hourly/daily forecasts.
The standard Home Assistant sun.sun entity automatically switches idle cards
to black backgrounds with condition colors used as accents after sunset. At
23:00 local time, the monitor sets the hardware brightness to the configured
late-night level and restores automatic brightness at sunrise. Set
BUSY_BAR_LATE_NIGHT_BRIGHTNESS from 0 to 100 percent; the default is 5.
The counter carousel requires Operator API support for
GET /v1/monitor/summary. Deploy the matching Operator release before updating
this worker. Older Operator releases still provide state and health, but summary
polls will log 404 until the endpoint is available.