Most of us have a drawer (or a box) stuffed with old gadgets—a sort of tech graveyard we can’t quite bring ourselves to throw out. One day, while rummaging through my stash of dusty chargers and questionable cables, I found my trusty Kindle Paperwhite 7. It had faithfully delivered countless stories but eventually got sidelined for an iPad.

As I grow older, my love for simple devices only grows. Maybe it’s not my age but the informational overload we’re exposed to throughout the day that makes us want to simplify our lives. Rather than letting it fade into oblivion, I decided to transform it into something fresh and genuinely useful: a minimalist, long-running weather dashboard.

(Amazing how something designed for a single purpose can age so gracefully. Take notes, “smart” devices.)

Finding the Right Solution

After deciding to repurpose the Kindle, I started researching potential projects. That’s when I discovered Pascal Widdershoven’s kindle-dash project, which demonstrated how to turn Kindles into simple e-ink dashboards (something like trml that costs around $250 bucks). Pascal’s approach was robust, but I wanted to narrow its purpose to just one thing I check every day: the weather. Pairing the Kindle’s crisp e-ink screen with a weather feed sounded perfect—especially given e-ink’s stellar readability and power efficiency. There’s a certain elegance to seeing weather data in neat black-and-white e-ink form, especially in bright sunlight.

The Jailbreaking Adventure

To pull off this new lease on life, I first had to jailbreak the Kindle. If you’re game to try, you can find step-by-step guides at kindlemodding.org. The process can be a bit nerve-wracking—nobody wants to brick their Kindle—but it’s not too scary if you carefully follow the instructions.

One huge reason to jailbreak is that it grants root access. This is keys-to-the-kingdom stuff, letting you modify power management settings, fiddle with the native Linux environment, and run your own scripts. Without rooting the device, you’re at the mercy of Amazon’s locked-down ecosystem. Once my Kindle was jailbroken, the real fun began.

KUAL and USBNetwork: The Extra Mile

After the basic jailbreak, you’ll want to install KUAL (Kindle Unified Application Launcher). Then, for network access via USB or WiFi (a must for SSH), you’ll need the USBNetwork hack. The setup instructions can be found at this helpful wiki page. You can snag the required files from this thread.

Next, you’ll need to copy your SSH public key into /mnt/us/usbnet/etc/authorized_keys. It absolutely must be in the ssh-rsa format. If you’re thinking “But I love ssh-ed25519 more,” guess what? The Kindle doesn’t share your enthusiasm. After two hours of furious Googling and cursing at the screen, I discovered— the hard way— that ssh-ed25519 doesn’t play nice with the Kindle’s built-in SSH daemon (Dropbear or whichever it was using at the time). Save yourself the headache and go with ssh-rsa.

Jailbroken Kindle

The Gritty Setup

This entire project boils down to two main parts:

  1. A Node.js server that collects weather data and generates an image formatted for the Kindle
  2. A client script on the Kindle that periodically reports the battery percentage and pulls the latest dashboard image from the server.

Bun / Node.js to the Rescue (Server Side)

I built the server piece with Bun (yes, that shiny new JavaScript environment) and Playwright for some scraping wizardry. Containerized with Docker, it uses Playwright to capture a screenshot of Environment Canada’s weather page (because who doesn’t love scraping government websites?), processes the image with Sharp, and serves it through a lightweight Express.js server.

Here’s a snippet from the Dockerfile, just to show the environment:

FROM oven/bun:1.0.35

The image processing pipeline looks something like this:

// Launch browser and capture weather data
const browser = await playwright.chromium.launch();
const page = await browser.newPage();
await page.goto(WEATHER_URL);
const screenshot = await page.screenshot();
await browser.close();

// Process the screenshot with Sharp
await sharp(screenshot)
    .rotate(90)           // Match Kindle's orientation
    .png()
    .toFile('public/dash.png');

The trickiest part was getting the image right for the Kindle Paperwhite 7. It turns out that not all Kindles handle the same image formats in the same way, and e-ink screens can be a bit particular. While Pascal’s kindle-dash was a great starting point, I found that the default image format didn’t render properly on this specific Kindle.

So I did some detective work, poking around the device’s framebuffer:

[root@kindle dashboard]# eips -i

Fixed framebuffer info
    id:              mxc_epdc_fb    smem_start:       0x81000000
    smem_len:            6782976    type:          PACKED_PIXELS
    type_aux:                  0    visual:   STATIC_PSEUDOCOLOR
    // ... existing code ...

Variable framebuffer info
    xres:                   1072    yres:                   1448
    xres_virtual:           1088    yres_virtual:           6144
    xoffset:                   0    yoffset:                   0
    bits_per_pixel:            8    grayscale:                 1
    // ... existing code ...
    rotate:                    3
DPI: 299.217582 301.468852 300

This data was gold. The Kindle expects 8-bit grayscale images at exactly 1072×1448 pixels. Once I wrapped my head around that, a custom image pipeline using Sharp in Node.js did the trick:

await sharp(processedImage)
    .toColorspace('b-w')  // Convert to black and white
    .removeAlpha()        // Remove alpha channel that gets added when I merge the image with the battery percentage
    .rotate(90)           // Match Kindle's orientation
    .png()
    .toFile('public/dash.png');

I also defined some constants to ensure the final output matches Kindle dimensions:

const DASHBOARD_WIDTH = 1448;
const DASHBOARD_HEIGHT = 1072;

Another neat tweak is the built-in battery monitoring. When the Kindle reports its battery level, the server bakes that info right into the image:

app.get('/battery/:percentage', (req, res) => {
    const newBattery = parseInt(req.params.percentage);
    BATTERY_PERCENTAGE = newBattery;
    // Update the dashboard with new battery information and generate the new dashboard image
});

Taming the Kindle OS (Client Side)

On the Kindle, a script called dash.sh handles everything from stopping the default Kindle interface to controlling power usage and updating the display. It looks something like this:

init() {
  if [ -z "$TIMEZONE" ] || [ -z "$REFRESH_SCHEDULE" ]; then
    log "Missing required configuration."
    log "Timezone: ${TIMEZONE:-(not set)}."
    log "Schedule: ${REFRESH_SCHEDULE:-(not set)}."
    exit 1
  }

  #stop framework
  if [ "$ISKINDLE4NT" = true ]; then
      /etc/init.d/framework stop #kindle NT4 code
  else
      stop framework
      stop lab126_gui #code for kindle paperwhite3
  fi

  initctl stop webreader
  echo powersave >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
  lipc-set-prop com.lab126.powerd preventScreenSaver 1
}

During initialization, it kills the default Kindle processes, flips the CPU to a power-saving profile, and stops the screensaver from kicking in. This sets the stage for your e-ink dashboard to properly run.

Here’s a snapshot of the logs you might see on the Kindle, showing what happens during a refresh cycle:

[Sat Feb  8 07:49:49 UTC 2025] Woke up, refreshing dashboard
[Sat Feb  8 07:49:49 UTC 2025] Battery level: 99%, 1443 mAh
[Sat Feb  8 07:49:49 UTC 2025] Refreshing dashboard
[Sat Feb  8 07:49:49 UTC 2025] Wi-Fi connected
Battery updated to 100%
[Sat Feb  8 07:49:56 UTC 2025] Partial screen refresh
update_to_display: update_mode=PARTIAL, wave_mode=2 inverted=0
swipe feature is not supported in this platfom G1
[Sat Feb  8 07:50:07 UTC 2025] Going to sleep, next wakeup in 7810s
wakeup from "no" at Sat Feb  8 10:00:16 2025
rtcwake: write error: Invalid argument

First success with the dashboard - unbelievably cold indeed!

First success … Holy F*ck, it’s -22°C outside! Welcome to Canada, where venturing out to touch the grass might just freeze your toes off.

The Speed Bumps

Here are some of the technical and hardware challenges I had to wrangle.

E-Ink Ghosting (No, It’s Not a Dating Thing)

E-ink displays are notorious for “ghosting,” where remnants of the previous screen remain faintly visible. To handle that, I do a mix of full and partial refreshes:

By the way, eips is the built-in Kindle command that allows you to update or clear the e-ink display. Think of it as the indispensable little elf behind the scenes that orchestrates screen refreshes.

if [ "$num_refresh" -eq "$FULL_DISPLAY_REFRESH_RATE" ]; then
    num_refresh=0
    # Full refresh to keep the screen clean
    /usr/sbin/eips -f -g "$DASH_PNG"
else
    # Partial refresh for regular updates
    /usr/sbin/eips -g "$DASH_PNG"
fi

The idea is to run a full refresh only occasionally, keeping power usage low and the screen free of artifacts most of the time.

Sleep or No Sleep? That Is the Question

Power management was a puzzle. I initially tried the Linux rtcwake utility:

In a typical Linux distro, rtcwake is the handy tool that schedules wake-up times after your system goes to sleep. Unfortunately, the Kindle’s custom kernel doesn’t always play nice with it, which leads to the fun errors you’ll see below.

rtc_sleep() {
  duration=$1
  rtcwake -d /dev/rtc1 -m no -s "$duration"
  echo "mem" >/sys/power/state
}

But the Kindle doesn’t play nicely with rtcwake. I kept getting errors like:

[root@kindle root]# rtcwake -d /dev/rtc0 -m mem -s 10
wakeup from "mem" at Sat Feb  8 06:35:20 2025
rtcwake: write error: Device or resource busy

Digging deeper, I found that Amazon’s custom kernel patches basically override this standard functionality. The next best approach was to schedule a wake-up time using the Kindle’s native system, then manually push the device to sleep:

# First, set our next wakeup time
next_wakeup_secs=$("$DIR/next-wakeup" --schedule="$REFRESH_SCHEDULE" --timezone="$TIMEZONE")

# Then actually put the Kindle to sleep
echo "mem" > /sys/power/state

This turned out to be far more reliable than forcing rtcwake.

Dealing with WiFi Gremlins

Crucially, I discovered that the Kindle Paperwhite 7’s WiFi can get stuck after network hiccups—like if your ISP goes down. To battle that, I added an auto-recovery system:

restart_wifi() {
  # Turn WiFi off and on
  lipc-set-prop com.lab126.cmd wirelessEnable 0
  sleep 5
  log "WiFi turned off"
  lipc-set-prop com.lab126.cmd wirelessEnable 1
  sleep 5  # Give it time to reconnect
  log "WiFi turned on"
}

It basically toggles the Kindle’s wireless if too many pings fail. This ensures the Kindle doesn’t get stranded in a failed network state. A few seconds of downtime in exchange for a stable connection is totally worth it.

The Payoff

After all this tinkering, I ended up with a dashboard that:

  • Updates every three hours, balancing freshness with battery life
  • Runs for weeks on a single charge
  • Displays weather data in crisp, eye-friendly e-ink
  • Self-monitors battery levels and network connectivity
  • Handles power management like a champ, sleeping between updates

Field Notes: What I Learned Along the Way

  1. I love old hardware: There’s a lot you can do with devices once considered obsolete. I have an IPhone that runs an analytics dashboard and is always plugged in.
  2. Power Management Matters: I am lazy AF, so the longer the device can run on a charge, the better.
  3. Document, Document, Document: Especially when tinkering with a device that uses a modified OS or scripts. I am pretty sure I will forget how I did things 2 weeks from now so having some kind of record of what I did will be invaluable.

Next Steps (Maybe?)

  • Integrating my work and personal calendars to display upcoming meetings and events
  • Creating a Hacker News dashboard that shows the top 5 trending articles
  • Building a rotation system to cycle between weather, calendar, and HN views with each refresh
  • Keep monitoring and make it more reliable and useful

Final Thoughts

I have always loved this Kindle, and making it a dashboard just made me love it even more. I am sure someone else will find this useful and bring theirs back to life.

Each morning, I glance at my custom Kindle for the day’s weather. No bright screens in the dark, no hourly charge cycles, and no fuss. Just crisp black-and-white forecasts on a device that used to gather dust. It’s a testament to the notion that with some hacking know-how, you can resurrect the gadgets of yesteryear and make them practical and even delightful again.


My Kindle’s final resting place, overshadowed by my glorious RGB PC

And here it is in its final resting place—perched atop my over-the-top RGB gaming rig. It’s kinda like a serene e-ink monk sitting under the flashy neon lights of a techno cathedral, calmly reporting the weather while everything else tries to look like a cyberpunk disco ball.

Acknowledgment: This project was heavily inspired by Pascal Widdershoven’s kindle-dash. Although I’ve reworked it substantially for my Paperwhite 7, his code was the starting point that sparked this whole journey.