An e-ink dashboard for the Visionect Joan, fed by Home Assistant. Clock, weather and a multi-day forecast, a merged family calendar, the dinner plan, the next bin day, and radio controls. Landscape, black on white.
It's a plain HTML page, which is the point: you lay it out however you want, and it reads the Joan's own battery, WiFi and temperature straight from the renderer. HADashboard can't do either.
The Joan cannot talk to Home Assistant directly. The Visionect Software Suite renders a URL in a headless browser and pushes the image to the screen. joanboard is that URL: a static page plus a small add-on. The add-on serves the page on the LAN and proxies the Home Assistant API, adding your token on the server side, so the token is never sent to the browser.
Joan -> Visionect Software Suite -> e-ink screen
^
renders this dashboard URL
|
joanboard add-on (nginx): serves the page and
proxies the HA API, injecting the token server-side
|
Home Assistant API + services
Battery, WiFi signal, and inside temperature are read natively from the device via the Visionect renderer.
This is only the dashboard. You need a working Visionect Joan setup before it is useful:
- A Visionect Joan e-ink device (6 or 13).
- The Visionect Software Suite (VSS) running and managing the device. VSS is what renders a URL onto the e-ink screen. Run it either via Adam7411's all-in-one Home Assistant add-on, or self-host the official Visionect Server. Either works.
- The Visionect Joan Home Assistant integration (Adam7411). It provides the service to point the device at the dashboard URL and the Day/Night view switching used by the optional automations.
- Home Assistant with a long-lived access token, plus the entities the dashboard reads: a weather entity, a media player, and one or more calendars.
See Credits for links.
dashboard.html- the dashboard.sleep.html- a sleep screen shown overnight.config.example.js- copy toconfig.jsand fill in. Holds entity IDs, language, and the status-bar message. No token: the add-on injects it.config.jsis gitignored.lang/en.json,lang/is.json- UI translations. Add more languages by dropping in another file and settingLANGUAGEin your config.addon-joan-httpserver/- a small nginx Home Assistant add-on that serves these files on a LAN-only port and proxies the HA API with the token injected server-side. The token and an IP allowlist are set in its Configuration tab.joan-automations.yaml- optional Home Assistant automations: switch the Day/Night view around the device's native sleep window, plus a low-battery reminder. The Visionect Software Suite handles the actual sleep/wake ("Working hours"). Fill in the placeholders before use.
-
Add the add-on repository. Click the button above, or in Home Assistant go to Settings, Add-ons, Add-on Store, the three-dot menu, Repositories, and paste this repository URL. Then install "Joan Dashboard Server".
-
Configure the add-on (its Configuration tab):
- ha_token: a Home Assistant long-lived access token (Profile, Security, Create token).
The add-on injects this into API requests, so you leave
TOKENempty inconfig.js. - allow_cidr: which networks may reach the server and API proxy. Start with
0.0.0.0/0to get going, then tighten it (see Security). Restart the add-on after changing it.
Start the add-on. It serves the dashboard on port 8099 on your LAN.
- ha_token: a Home Assistant long-lived access token (Profile, Security, Create token).
The add-on injects this into API requests, so you leave
-
Put the dashboard files where the add-on serves them, the Home Assistant
share/joanfolder. Copydashboard.html,sleep.html, thelang/folder, and yourconfig.jsintoshare/joan. Use the File editor or Studio Code Server add-on, Samba, or scp. -
Create your config. Copy
config.example.jstoconfig.js, leaveHA_URLandTOKENempty, and set the entities and language (see below). -
Point the Joan at
http://YOUR_HA_IP:8099/dashboard.html(Visionect set URL service, or a Day view in the integration).
The dashboard files live in Home Assistant's share/joan folder. Common ways to get files
there and edit them:
- Samba share add-on: exposes the
sharefolder over the network (SMB), so you can open and edit the files straight from your computer's file explorer. - File editor or Studio Code Server add-on: edit the files in the browser from inside Home Assistant.
- SSH & Web Terminal add-on, or scp: from the command line.
After editing, reload the page on the tablet, or let the next refresh pick it up.
Copy config.example.js to config.js, then edit:
HA_URLandTOKEN: leave both empty. The add-on proxies the API and injects the token, so the dashboard calls the API on the same origin and needs no token of its own. (Set them only if you serve this dashboard yourself, without the add-on's proxy.)LANGUAGE: matches a file inlang/, for exampleenoris. Add your own by copyinglang/en.json.BAR_MSG: free text shown in the center of the status bar, or""to hide it.- Entity IDs (
SONOS_ENTITY,WEATHER_ENTITY,DINNER_CALENDAR): set to your own. Find exact IDs in Developer Tools, States. (Battery, WiFi and inside temperature come natively from the device, so there is nothing to set for them.) FAMILY: a list of[calendar_entity, display_name]pairs. Identical events across calendars are merged and the names joined.SCRIPT_PLAY,SCRIPT_SKIP: object IDs of Home Assistant scripts (without thescript.prefix). The center button callsSCRIPT_PLAY; previous and next callSCRIPT_SKIPwithstepof -1 or +1. Point them at plainmedia_playerservices instead if you prefer.TRASH_ENTITY(optional): a sensor whose state is the next bin-collection date (YYYY-MM-DD), shown under the clock.TRASH_LABEL_ATTRnames an attribute holding the bin type, andTRASH_LABEL_MAPcan rename bin terms (for example{ "Mixed": "General" }). LeaveTRASH_ENTITYempty to hide the line.STATION_TITLE(optional): some radio streams report a junk "now playing" title (a filename). Map a stable substring of the stream URL (media_content_id) to a fixed name, for example{ "somestreamid": "Some FM" }.- Refresh cadence:
RADIO_REFRESH_SEC(default 30),PERIODIC_REFRESH_MINfor weather and the family calendar (default 30), andDAILY_REFRESH_ATfor dinner and bin (default"06:40"). Clock, battery and signal refresh once a minute.
Edit config.js directly in share/joan with the File editor or VS Code add-on, then reload
the page on the tablet. config.js is gitignored so your token never enters version control.
This project avoids serving the dashboard from Home Assistant's /local/ (www) folder. Files
in /local/ are unauthenticated, so if your Home Assistant is reachable from the internet
(Nabu Casa, a reverse proxy, Cloudflare, port forwarding), anyone could open the page. Putting a
token in such a page would leak it publicly.
Instead the add-on keeps the token server-side: it proxies the Home Assistant API and adds the
Authorization header itself, from the ha_token set in its Configuration tab. The token is
never in config.js and is never served to the browser.
Lock down who can reach the server with allow_cidr. The only thing that needs access is the
Visionect renderer:
- VSS in the Home Assistant add-on stack: use the internal Docker/Supervisor network, typically
172.30.32.0/23. Add your own machine (for example192.168.1.50/32) if you also want to open the dashboard in a browser. - VSS self-hosted elsewhere: use the Visionect server's IP.
0.0.0.0/0allows everything and is for first-run debugging only.
Changing allow_cidr only takes effect after you restart the add-on.
Residual risk: anyone allowed by allow_cidr can call the HA API through the proxy (the proxy
adds the token, so the caller needs none), but there is no token in any file to steal and reuse
elsewhere, and it stays bounded to the allowed networks. The token is full access (Home Assistant
tokens cannot be scoped). Never forward or tunnel port 8099 to the internet.
- Play/pause button: mute and unmute (wired to a Home Assistant script). A muted player shows an empty volume bar (0%).
- Previous/next: change radio station (wired to a Home Assistant script).
- Minus/plus: volume.
- When the player is not playing (paused, idle, off) the controls are hidden and a power button
appears in the top-right of the radio card; pressing it runs
SCRIPT_PLAYto start it. - Tap anywhere on the screen to refresh all data on demand.
The script object IDs are set in config.js via SCRIPT_PLAY and SCRIPT_SKIP.
This project would not be possible without the work of Adam7411, who built and maintains the Home Assistant integration and the Visionect Server add-on that make driving a Joan from Home Assistant possible. Huge thanks.
- Home Assistant integration: https://github.com/Adam7411/visionect_joan
- Visionect Server add-on: https://github.com/Adam7411/visionect-v3-allinone
joanboard is just the dashboard that sits on top of that foundation.
