# A Home Assistant Lovelace view becomes a Kindle screensaver through a screenshot loop

> hass-lovelace-kindle-screensaver renders a Lovelace view of a Home Assistant instance in headless Chromium, converts it to a grayscale PNG a Kindle can display, and serves it over HTTP so a jailbroken device can poll it as a screensaver. The polling-friendly part is an ETag and Last-Modified scheme that only changes when the picture does, which is what keeps an e-ink screen from refreshing pointlessly.

**sibbl/hass-lovelace-kindle-screensaver** — This tool generates a png from a Home Assistant Lovelace view, which can be displayed on a Kindle device which has the Online Screensaver plugin installed.

- Repository: https://github.com/sibbl/hass-lovelace-kindle-screensaver
- Stars: 657 · Forks: 107
- Language: JavaScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/sibbl-hass-lovelace-kindle-screensaver

## The problem: a Kindle cannot run a dashboard

hass-lovelace-kindle-screensaver is a tool that generates a PNG from a Home Assistant Lovelace view, which can be displayed on a Kindle device that has the Online Screensaver plugin installed. The README states the target plainly: a jailbroken Kindle device, and it regularly takes a screenshot which can be polled and used as a screensaver image.

The use cases it names give the project its shape: a weather station, or a display for next public transport departures. Both are cases where a static picture that refreshes every minute or so is entirely adequate, and where the alternative, a browser on the device, is not available.

The mechanism is a headless browser pointed at a Home Assistant page. The tool regularly takes a screenshot of a specific page of the Home Assistant setup and converts it into the PNG or JPEG grayscale format which Kindles can display. The repository contains a battery_sensor_blueprint.yaml and a lovelace-eink-theme.yml, so the project expects you to also work on the Home Assistant side, with a theme tuned for e-ink and a blueprint for reporting the Kindle's battery state back.

The author is explicit that this is one of two projects, and the distinction matters for anyone evaluating it. If you want to render your own HTML, the README points to a separate project, hass-kindle-screensaver, which renders a React page and can be adapted to your specific needs. This one is specifically for Lovelace views of Home Assistant, and the author recommends the online screensaver extension for any jailbroken Kindle for setting it up.

## The energy saving mechanism: stable ETag and Last-Modified

The feature that makes this usable on e-ink is not the screenshot, it is the conditional HTTP response. The README describes it as energy-efficient image updates.

The tool compares each new screenshot with the previous one and only updates the served image when changes are detected. This keeps the Last-Modified timestamp and the ETag header stable, allowing e-ink clients to skip unnecessary downloads and screen refreshes. On a display that draws its power in full page refreshes, the difference between a screen that redraws every poll and one that redraws only when the weather changed is the difference between a usable dashboard and one that eats the battery.

The client side of that contract is documented too. A lightweight HEAD request checks for changes without downloading the image:

```bash
curl -I http://localhost:5000/
```

And the README tells you to compare the returned ETag or Last-Modified with your last known value, only issuing a GET for the image if it changed. Conditional GET and HEAD requests using If-None-Match or If-Modified-Since return 304 Not Modified when the image has not changed.

This is the pattern the online screensaver plugin on the Kindle side is expected to use, and it is the reason the tool bothers to keep the headers stable rather than simply serving a file with a fresh timestamp every time.

## On-demand rendering, status headers and a bounded queue

The API surface is small and it is designed around the fact that a render is expensive. A scheduled render happens on a cron interval, but you can also trigger one for a single image before downloading it:

```bash
curl -v http://localhost:5000/?refresh=1
curl -v http://localhost:5000/2?refresh=1
```

The second form targets page two, which is how the multi-page feature works: the docker compose file carries a commented set of numbered variables for a second Home Assistant instance rendered in the same Chromium process, whose image is served at /2.

The response includes an X-Render-Status header. If rendering fails, the server keeps serving the previous image when one exists and returns X-Render-Status: failed, which is the behaviour you want on a display that has no user to report a failure to.

Render and cache-clear operations share a bounded queue with one active operation and one waiting. When the queue is full, API calls return HTTP 503 with the error render_queue_full, while image refresh requests serve the last image with X-Render-Status: failed and an X-Render-Error of render_queue_full, and the client should retry later. That split between what an API caller sees and what a display client sees is deliberate: a display that gets a 503 has nothing to render, while a display that gets the last image keeps working.

For API clients there are POST /render to render all pages, POST /render/2 to render one page, and POST /cache/clear to restart Chromium and clear browser-side frontend caches. A Kindle-compatible image request can combine cache clearing and rendering with the clearCache=1 and refresh=1 query parameters.

## Configuration: the Home Assistant credentials and the rendering knobs

The configuration is entirely environment variables, and the README documents them in a table with a required flag, an array marker and a description.

The three required ones are HA_BASE_URL, the base URL of your Home Assistant instance, HA_SCREENSHOT_URL, the relative URL to take a screenshot of, and HA_ACCESS_TOKEN, a long-lived access token from Home Assistant. The example value for the screenshot URL is /lovelace/screensaver?kiosk, and the README explains that the kiosk parameter hides the navigation bar, using the kiosk mode project.

Several are optional. HA_THEME names the Home Assistant theme to use for rendering, and it must be installed in your instance, otherwise the default theme is used. HA_BATTERY_WEBHOOK is a webhook that receives batteryLevel as a number between 0 and 100 and isCharging as a boolean, as JSON, so the Kindle can report its battery state back into Home Assistant. HTTP_AUTH_USER and HTTP_AUTH_PASSWORD enable optional HTTP basic authentication on the image server, and each requires the other to be set.

The compose file shows the rest of the tuning surface: CRON_JOB for the schedule, RENDERING_TIMEOUT, RENDERING_DELAY, RENDERING_SCREEN_HEIGHT and RENDERING_SCREEN_WIDTH for the viewport, GRAYSCALE_DEPTH for the conversion, OUTPUT_PATH for where the image is written, LANGUAGE, ROTATION and SCALING. The sample .env file in the repository shows the minimum set:

```text
HA_BASE_URL=http://homeassistant.local
HA_SCREENSHOT_URL=/lovelace/screensaver?kiosk
HA_ACCESS_TOKEN=ey...
```

Two details are worth calling out. The screenshot URL is relative to the base URL and the instance is expected to be reachable from inside the container, and the screen height and width define the viewport the screenshot is taken at, so they should match the Kindle's panel as closely as the image format allows.

## Running it as a container or as a Home Assistant addon

There are three documented ways to run it, and the author has a preference.

The container route is the simplest. The Docker Hub image is sibbl/hass-lovelace-kindle-screensaver, and the container exposes a single port, 5000 by default. The recommendation is to use the docker-compose.yml file inside the repository, configure everything there, and run docker-compose up -d within the file's directory, which pulls the image, creates the container with all the environment variables from the file, and runs it in detached mode so it keeps running when you exit your shell or ssh connection. Updating later is docker-compose pull followed by docker-compose up -d again.

The Home Assistant addon route adds the repository as an addon repository through the supervisor redirect, reloads, and the Lovelace Kindle Screensaver addon appears among the options.

The Dockerfile is what sits behind the image, and it explains the rendering dependencies. It is based on node:16-alpine3.17, installs chromium along with nss, freetype, font-noto-emoji, font-noto-cjk, freetype-dev, harfbuzz, ca-certificates, ttf-freefont and imagemagick, and copies a local.conf into the fonts directory. The emoji and CJK font packages are there because a Home Assistant dashboard in a non-Latin locale needs them, and ImageMagick is there for the grayscale conversion. The build sets PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true and points PUPPETEER_EXECUTABLE_PATH at the system chromium-browser, and USE_IMAGE_MAGICK=true. The image exposes port 5000 and has a healthcheck that hits the /health endpoint every 60 seconds with a 10 second timeout, and the entrypoint is npm start.

The Node dependencies in package.json are minimal and tell you the shape of the implementation: puppeteer for the browser, the gm library for image manipulation, cron for the schedule, dotenv for the environment and fs-extra for file handling.

## Where this project is the wrong tool

Three limits, and the first one is about the hardware rather than the software.

The first is the jailbreak requirement. The README states the device has to be jailbroken, and points at the mobile read forum threads for the jailbreak and for the online screensaver plugin. A Kindle sold today, or one that has been updated to a recent firmware, is not a supported target. If your device is not jailbroken, this project does nothing for you, and no amount of configuration changes that.

The second is that the output is a still image, not a dashboard. The tool takes a screenshot on a schedule and serves the file. That is the right shape for a weather station and completely wrong for anything interactive, and it also means the resolution is whatever the panel is and the refresh is whatever your cron interval says. The README's own default in the compose file is a cron job of every minute, which for an e-ink display and a Home Assistant instance is a reasonable starting point and a reasonable thing to tune down.

The third is that the render is a full page load of your Home Assistant instance in headless Chromium. That is a heavyweight operation to run on a schedule, which is exactly why the tool has a bounded render queue, a rendering timeout, a rendering delay, and a cache clear operation that restarts Chromium. It also means the Home Assistant instance must be reachable and must render the view quickly enough for the timeout, and that a long-lived access token is required, which is a credential with real reach.

The alternative approach, and the author points at it directly, is the sibling project hass-kindle-screensaver, which renders a React page rather than a Lovelace view. The difference in approach is that a custom React page can be built for the e-ink panel, with exactly the fields you want and none of the dashboard furniture, and it does not need Home Assistant to render a page at all, at the cost of writing and maintaining that page yourself. The Lovelace route is the one for a user who already has a dashboard they want to reuse, and the React route is the one for a user who wants a purpose-built display.

## Conclusion

Adopt this project if you own a jailbroken Kindle and want your Home Assistant dashboard on its e-ink screen, since the combination of kiosk mode, a grayscale conversion and conditional HTTP responses is the whole design and it is already packaged as a Docker image and a Home Assistant addon. Do not adopt it for a modern Kindle without jailbreak, and do not expect a live dashboard, since the device polls a still image on a schedule. Verify first that your Lovelace view renders cleanly in kiosk mode at the configured screen size, since the tool takes a screenshot of a page and a dashboard that depends on the sidebar or on an element that is only visible at a different viewport will produce a broken picture, that the HA_ACCESS_TOKEN is a long-lived token with the permissions the page needs, and that the scheduled interval is one your e-ink screen and your Home Assistant instance can both afford, since the cron entry in the compose file is set to run every minute by default.

## FAQ

### What does hass-lovelace-kindle-screensaver do?

It regularly takes a screenshot of a specific page of your Home Assistant setup, converts it into the PNG or JPEG grayscale format which Kindles can display, and serves it so a jailbroken Kindle with the Online Screensaver plugin can poll it as a screensaver image.

### How does the project avoid wasting e-ink screen refreshes?

It compares each new screenshot with the previous one and only updates the served image when changes are detected, keeping the Last-Modified timestamp and ETag header stable. Clients can use a HEAD request or a conditional GET with If-None-Match or If-Modified-Since to get a 304 Not Modified and skip the download.

### Which environment variables are required?

HA_BASE_URL, the base URL of the Home Assistant instance, HA_SCREENSHOT_URL, the relative URL to screenshot, and HA_ACCESS_TOKEN, a long-lived access token, are all required. HA_THEME, HA_BATTERY_WEBHOOK, HTTP_AUTH_USER and HTTP_AUTH_PASSWORD are optional.

### How do I run hass-lovelace-kindle-screensaver?

The author recommends the docker-compose.yml inside the repository, configured and run with docker-compose up -d, which pulls the sibbl/hass-lovelace-kindle-screensaver image, creates the container with the environment variables and runs it detached. The image is also published as a Home Assistant addon, and the container exposes a single port, 5000 by default.

### What happens if a render fails?

The response includes an X-Render-Status header, and if rendering fails the server keeps serving the previous image when one exists and returns X-Render-Status: failed. Render and cache clear operations share a bounded queue, and when it is full API calls return HTTP 503 with error render_queue_full while image refresh requests serve the last image with an X-Render-Error of render_queue_full.

## Sources

- [Issues](https://github.com/sibbl/hass-lovelace-kindle-screensaver/issues)
- [License: MIT](https://github.com/sibbl/hass-lovelace-kindle-screensaver/blob/main/LICENSE)
- [README](https://github.com/sibbl/hass-lovelace-kindle-screensaver/blob/main/README.md)
- [Releases](https://github.com/sibbl/hass-lovelace-kindle-screensaver/releases)
- [sibbl/hass-lovelace-kindle-screensaver on GitHub](https://github.com/sibbl/hass-lovelace-kindle-screensaver)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sibbl-hass-lovelace-kindle-screensaver
