|
|
||
|---|---|---|
| homeassistant | ||
| smarthomedisplay | ||
| websocket-proxy | ||
| .gitignore | ||
| LICENSE | ||
| README.md | ||
Kindle SmartHome Dashboard
Turn an old jailbroken Kindle Paperwhite 3 (7th-gen e-ink) into a wall-mounted Home Assistant control panel. It shows a tabbed Chores / Lights / Events screen with a clock + weather header, and you can tap to toggle lights, mark chores done, and add calendar events — all over your local network.
Everything is built to run on the Kindle's ancient (~2015) browser, so the on-device app is plain old JavaScript and simple black-and-white CSS. There are three pieces:
- a small web app that lives on the Kindle,
- a proxy (a little Node program) that sits on your server and talks to Home Assistant for the Kindle, and
- a couple of Home Assistant helper sensors for the weather forecast.
New to all this? No problem. The Setup below walks through all three pieces in order, and every step has a click-to-expand explainer telling you what it does and why. This one's more involved than a single script — but you only do it once.
What you'll need first
- A jailbroken Kindle with KUAL installed (the jailbreak's app launcher). Jailbreaking is out of scope for this README — follow a current guide for your exact model/firmware first.
- A running Home Assistant instance on your local network.
- A small always-on machine next to Home Assistant (the HA box itself, a NAS, a Pi…) that can run Node.js 22+ to host the proxy.
Why is there a "proxy" in the middle — can't the Kindle talk to Home Assistant directly?
The Kindle's browser is from ~2015 and speaks an outdated WebSocket dialect that modern Home Assistant rejects. The proxy is a tiny translator: the Kindle connects to the proxy using the old dialect it understands, and the proxy re-speaks everything to Home Assistant in the modern one (and relays calendar lookups, the weather forecast, light toggles, chore claims, etc. back). It also lets the Kindle authenticate with a simple token instead of juggling HA's full auth.
All of this traffic stays on your local network — nothing goes through the cloud.
Setup
There are three pieces to set up, in this order: the proxy (on your server), Home Assistant, then the Kindle.
1. The proxy (on your server)
Copy the websocket-proxy/ folder onto the always-on machine next to Home Assistant, then:
cp config.sample.json config.json
# now open config.json in a text editor and fill in your values (see below)
npm install
node main.js
It will start listening on port 4365.
What do those commands do, and what goes in config.json?
cp config.sample.json config.json— makes your own copy of the example config to edit (the realconfig.jsonis gitignored because it holds a secret token).npm install— downloads the two small libraries the proxy needs.node main.js— starts the proxy. Leave it running (in production, run it as a service so it restarts on reboot — see the next box).
config.json has just two things to fill in:
{
"homeassistant": {
"wsUrl": "http://homeassistant.local:8123",
"accessToken": "<your Home Assistant long-lived token>"
},
"kindle-display": {
"accessToken": "<any password you make up for the Kindle>"
}
}
homeassistant.wsUrl— the address you use to reach Home Assistant on your LAN.homeassistant.accessToken— a Home Assistant long-lived access token (how to make one is in Personalize it to your setup).kindle-display.accessToken— a password you invent. The Kindle has to send this same value to be allowed to connect, so it's not wide open on your network. You'll paste the same string into the Kindle's config in step 3.
How do I keep it running after reboots? (How it's deployed here)
node main.js stops the moment you close the terminal. For a permanent install, run it as a
background service. In this household it runs as a small Docker service using the
node:22-alpine image, which regenerates config.json from environment variables on each
start so the secrets live in one place. A plain systemd service, pm2, or a Docker container
all work equally well — anything that keeps node main.js alive and restarts it on boot.
Node 22+ is required — the proxy uses Node's built-in WebSocket client to reach Home Assistant.
2. Home Assistant
The weather entity in Home Assistant doesn't normally expose today's hi/lo or an hourly forecast, so the dashboard needs two small helper sensors.
- Open
homeassistant/kindle-weather-sensors.yamland add it under the top-leveltemplate:key in your HAconfiguration.yaml. - Restart Home Assistant (not just reload — a new
template:block only loads on a full restart).
What exactly am I adding, and what if I already have a "template:" section?
The file defines two trigger-template sensors that refresh every 30 minutes:
sensor.kindle_weather_today— today's high/low temperature.sensor.kindle_weather_hourly— the next 12 hours of forecast (shown when you tap the weather in the header).
If your configuration.yaml already has a template: section, don't add a second one —
just paste the single - trigger: list item from the file into your existing template: list.
Note the file is written for a weather entity called weather.forecast_home. If yours is named
differently, change that entity id in the file before adding it.
What else does Home Assistant need to have set up?
The dashboard reads from things that should already exist in your HA (or that you'll create):
- Lights you want on the panel (ideally Light Group helpers so a multi-bulb fixture shows as one tile).
- The ChoreOps HACS integration for the Chores tab — it provides per-chore status sensors and a points/leaderboard sensor per player.
- The Google Calendar integration (or any HA calendar) for the Events tab.
- Three optional display-control helpers — an
input_buttonto refresh the screen, aninput_buttonto reload the page, and aninput_numberfor brightness.
All of these are named in the Kindle's config.js in step 3, where you point them at your own
entity ids.
3. The Kindle
With the Kindle plugged in via USB (so it shows up as a drive):
- Copy the whole
smarthomedisplay/folder into the Kindle'sextensions/folder, at/mnt/us/extensions/smarthomedisplay. Keep the folder name exactly. - Inside it, copy
mesquite/config.sample.jstomesquite/config.jsand edit your values (see Personalize it to your setup). - Eject the Kindle and launch it from KUAL → "Launch SmartHome Display".
The Power button exits the dashboard back to your books.
What's in config.js, and why is it a separate file?
config.js holds everything specific to your home — your server's address, the Kindle
token, and all your Home Assistant entity ids (lights, chores, calendars, weather). It's kept
separate (and gitignored) so the rest of the code stays generic and your private addresses/ids
never get committed. The most important line is:
var WS_URL = 'ws://<SERVER_IP>:4365?accessToken=<KINDLE_DISPLAY_TOKEN>';
<SERVER_IP> is the machine running the proxy from step 1, and <KINDLE_DISPLAY_TOKEN> must
exactly match the kindle-display.accessToken you set in the proxy's config.json.
(Optional) Launch it straight from the Kindle home screen, like a book
Copy smarthomedisplay/documents/dashboard.sh into the Kindle's book library
(/mnt/us/documents/). On a jailbroken Kindle the launcher reads the # Name: and # Icon:
headers in that file and shows it on the home screen as if it were a book you can just open.
For a custom cover, drop a 600×800 PNG at the path given in the file's # Icon: line.
Heads up on battery: a live dashboard keeps the screen awake and drains the battery — keep the Kindle on a charger, or do the diode/cap power mod from the upstream project.
Personalize it to your setup
Everything you must change is in two files: the proxy's config.json (step 1) and the
Kindle's config.js (step 3). Here's each value and how to find your own.
Your Home Assistant address (wsUrl in config.json) — whatever you type in a browser to
reach HA on your LAN, e.g. http://homeassistant.local:8123 or http://192.168.1.x:8123.
Your server's IP (<SERVER_IP> in config.js) — the LAN address of the machine running
the proxy from step 1.
How do I create a Home Assistant long-lived access token?
In Home Assistant, click your user name (bottom-left) → scroll to the bottom → Long-Lived
Access Tokens → Create Token. Copy the token it shows you immediately (you can't see
it again) and paste it into config.json as homeassistant.accessToken.
How do I find my entity IDs (lights, chores, calendars, weather)?
In Home Assistant go to Developer Tools → States. That page lists every entity by its id
(like light.living_room_lamp, calendar.family, weather.forecast_home). Use the filter box
to find each one, and copy the exact id into the matching line in config.js.
For lights, it's worth creating Light Group helpers first (Settings → Devices & Services → Helpers) so a fixture with several bulbs becomes one entity you can put on a single tile.
The Kindle's config.js is fully commented; here's what each block controls:
Setting in config.js |
What it does |
|---|---|
WS_URL |
Address + token to reach the proxy. The token must match the proxy's kindle-display token. |
WEATHER_ENTITY |
Your HA weather entity (drives the header icon/temp). |
WEATHER_TODAY_ENTITY / WEATHER_HOURLY_ENTITY |
The two helper sensors from step 2 (today's hi/lo, and the 12-hour forecast popup). |
CHOREOPS_PLAYERS |
The people in your household, as their ChoreOps user names — used for the leaderboard and for choosing who claims a chore. |
CURRENCY_LABEL / SPLIT_BONUS |
What the reward is called (e.g. "Points") and the bonus each person gets when 2+ people split one chore. |
CALENDARS |
Which HA calendars appear in the agenda. |
CALENDAR_FETCH_DAYS / CALENDAR_SHOW_DAYS |
How far ahead to look, and how many upcoming days that have events to show. |
NEW_EVENT_CALENDAR / NEW_EVENT_OPTIONS |
Which calendar new events get added to (and a tap-pick list if you have several). |
EVENT_WHO / EVENT_PRESETS |
Tap-pickable "who is it for" prefixes and preset event titles (the Kindle keyboard doesn't work, so titles are tap-picked). |
LIGHT_ROOMS |
Your lights, grouped by room — each is [entity_id, label]. |
REFRESH_ENTITY / RELOAD_ENTITY / BRIGHTNESS_ENTITY |
The optional e-ink control helpers (refresh screen, reload page, set brightness). |
AUTO_NIGHT_MODE, SCREEN_BRIGHTNESS_DEFAULT, SCREEN_BRIGHTNESS_NIGHT |
Auto-dim at night, and the day/night front-light levels (0 = off; e-ink is readable in daylight). |
DEBUG_MODE |
true shows an on-screen error log + viewport readout while you're getting it working. |
How do chores work — do I have to list each one?
No. Chores are discovered automatically: on startup (and nightly) the app asks the proxy
for the current ChoreOps chore-status sensors, so adding, removing, or renaming chores in Home
Assistant needs no redeploy. You only list the people (CHOREOPS_PLAYERS).
Chores are shared (first to complete wins). When you mark one done you pick who did it; pick
two or more people and the reward splits evenly, each getting the SPLIT_BONUS on top (this
maps to a choreops_split_chore script on the HA side).
What's in the box
| Path | What it is | Where it runs |
|---|---|---|
smarthomedisplay/ |
The KUAL extension: the on-Kindle web app (mesquite/) plus launch scripts. |
The Kindle, at /mnt/us/extensions/smarthomedisplay. |
websocket-proxy/ |
The Node bridge between the Kindle and Home Assistant. | Your server, next to Home Assistant. |
homeassistant/kindle-weather-sensors.yaml |
The two weather helper sensors. | Pasted into HA's configuration.yaml. |
How it all fits together (the architecture)
Kindle (KUAL app, old-WebKit browser)
│ WebSocket ws://<server>:4365
▼
websocket-proxy (Node 22, on the server next to HA)
│ HA WebSocket + REST API (local, token auth)
▼
Home Assistant ──► Govee/MQTT lights, ChoreOps chores, Google Calendar, weather
Inside the Kindle app (smarthomedisplay/mesquite/):
index.html— the header + three tabs + popups (chore detail, hourly weather, new event, event detail) + a page up/down pager.config.js— all your site-specific wiring (covered above).js/index.js— the app logic: WebSocket client (subscribe + auto-reconnect), tab switching, and rendering for lights / chores / weather / agenda, plus event creation.js/qrcode.js— a bundled QR generator (for "create this event on your phone").css/index.css— pure black/white e-ink styling with big tap targets, sized for 600×800.js/error-logger.js— an on-screen error overlay whenDEBUG_MODEis on.
The proxy (websocket-proxy/) loads small modules: one keeps the live connection to Home
Assistant, the other (99-kindle-display) serves the Kindle — handling subscriptions, service
calls, calendar fetches, history, and a one-shot full-state pull used for the automatic chore
discovery above.
What can the dashboard actually do? (Feature tour)
- Header (all tabs): clock + date and current weather (icon, temp, today's hi/lo). Tap the weather for a 12-hour forecast popup.
- Chores: ChoreOps chores grouped Overdue → Today → Coming-up. Tap a chore for its notes and a multi-select Mark Done (one person claims it; two or more split the reward).
- Lights: fixtures grouped by room; tap a tile to toggle.
- Events: a multi-day agenda showing the next few days that have events (empty days are skipped). Tap an event for details. + New Event lets you tap-pick a "for whom" prefix and a preset title, set the date/time with ± steppers, and post it to your calendar — or tap Scan code… for a QR that opens Google Calendar pre-filled on your phone (handy because the Kindle keyboard doesn't work).
- E-ink niceties: page up/down, a periodic full-screen ghost-clear, auto night-dim, and a battery readout. Time is computed in JS (US Eastern from UTC) so it's right even though the Kindle's own clock is DST-unaware.
Notes & gotchas (learned building this)
- The Kindle viewport is 600×800 logical pixels (not the 1072×1448 physical panel) — size all CSS to 600×800.
- The on-screen keyboard doesn't work for
<input>fields in mesquite — that's why titles are tap-picked from presets or entered on a phone via the QR. - Don't trust the Kindle's clock/timezone — it's stuck on EST with no DST, so Eastern time
is derived from UTC in
getLocalDate(). - Native
<select>and<input type=date|time>are unreliable too → the app uses tap-chips and ± steppers instead.
Credit & license
Based on the architecture of
1RandomDev/kindle-smarthome-dashboard
(GPL-3.0) — the WebSocket proxy and KUAL scaffolding come from that project (the proxy is
lightly extended here with a full-state-pull handler for dynamic chore discovery), and the
on-Kindle web app (mesquite/) is a custom rewrite for this household. QR generation uses
qrcode-generator.
GNU GPL v3 (or later) — see LICENSE. As a derivative of the GPL-3.0 upstream, it stays copyleft, so derivatives stay free software.