Jailbroken Kindle Paperwhite e-ink Home Assistant control panel (ES5/old-WebKit)
Find a file
Xena Rathon 675ea52ab3 Add GPL-3 LICENSE file (GPL-3.0 derivative) and link it from README
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 22:31:54 -04:00
homeassistant Initial commit: Kindle SmartHome Dashboard 2026-05-29 19:51:02 -04:00
smarthomedisplay Chores: group by day-of-week + team-split Mark Done 2026-06-15 17:42:52 -04:00
websocket-proxy Kindle: dynamic chore discovery (no redeploy on chore changes) 2026-06-10 19:14:13 -04:00
.gitignore Update: chore completion display, lights dimming, battery indicator, home-screen launcher 2026-05-30 11:48:31 -04:00
LICENSE Add GPL-3 LICENSE file (GPL-3.0 derivative) and link it from README 2026-06-15 22:31:54 -04:00
README.md Add GPL-3 LICENSE file (GPL-3.0 derivative) and link it from README 2026-06-15 22:31:54 -04:00

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:

  1. a small web app that lives on the Kindle,
  2. a proxy (a little Node program) that sits on your server and talks to Home Assistant for the Kindle, and
  3. 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 real config.json is 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.

  1. Open homeassistant/kindle-weather-sensors.yaml and add it under the top-level template: key in your HA configuration.yaml.
  2. 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_button to refresh the screen, an input_button to reload the page, and an input_number for 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):

  1. Copy the whole smarthomedisplay/ folder into the Kindle's extensions/ folder, at /mnt/us/extensions/smarthomedisplay. Keep the folder name exactly.
  2. Inside it, copy mesquite/config.sample.js to mesquite/config.js and edit your values (see Personalize it to your setup).
  3. 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 TokensCreate 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 when DEBUG_MODE is 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.