Stillness - bringing Google Photos to an e-paper screen

Ever since I saw the Weatherman Dashboard I’ve felt inspired to do something with e-ink on my own… and now I’ve built my own e-ink photo frame with albums from Google Photos and I want to share it back with the community!

PXL_20260329_0948049201-ezgif.com-crop

I call it Stillness - and here are a few reasons why I think it’s different:

  • Battery-powered, no soldering and cost efficient
  • It relies on a self-hosted server for more privacy and control
  • It will try to fetch all photo URLs of an album (works with albums with 800+ images - to do this, it uses scraping FYI)
  • It will resize and crop the images in a way to fill the 7.5" frame while preserving humans and facial details as much as possible, and dither the images so they still look crisp in black and white

The “tech stack” basically looks something like this:

  • A server for fetching and transforming photos from Google Photos
  • An ESPHome config that fetches from the server and deep sleeps in between
  • The TRMNL 7.5 DIY Kit for the ESP32, battery and display
  • A 3D printable insert to hold it all together and make it fit inside a 18x13 cm photo frame

I’ve written a bit more how it all comes together in this blog post.

And for all of you Home Assistant fans, I’ve set up some nice monitoring!

Home Assistant helpers

These two template sensors are set up as helpers in HA, the others come directly from ESPHome.

Last refresh

{{ states.sensor.stillness_signal_strength.last_changed }}

Next refresh

{% set last = states('sensor.stillness_last_refresh') | as_datetime %}
{% set delta = states('sensor.stillness_sleep_duration') %}

{# Regex to find numbers followed by h, min, or s #}
{% set hours = delta | regex_findall('(\d+)h', ignorecase=True) | first | default(0) | int  %}
{% set minutes = delta | regex_findall('(\d+)min', ignorecase=True) | first | default(0) | int  %}
{% set seconds = delta | regex_findall('(\d+)s', ignorecase=True) | first | default(0) | int %}

{# Convert everything to a single total of seconds #}
{% set total_seconds = (hours * 3600) + (minutes * 60) + seconds %}

{% if total_seconds > 0 %}
  {{ last + timedelta(seconds = total_seconds) }}
{% else %}
  {{ last }}
{% endif %}

And here’s a look behind the scenes…

I don’t think I’ve enjoyed a hobby project like this in a long time - just seeing photos of my loved ones during development has really put a smile on my face! Only downside it still requires one to set up and maintain some Google Photos albums…

…but I have some ideas around that for a future version.

Enjoy the stillness and stay tuned! :framed_picture:

2 Likes

Neat project.

How long do you expect the battery to last between charges?

Any change it could use a local Immich server instead of Goggle?

Thanks!

Hard to say, right now I’m refreshing only once every 6 hours and it deep sleeps in between. From what I’ve seen on similar devices it could very well be around 3 to 6 months. Let me get back to you once the battery is depleted :smile:

Great idea! Been thinking about it, but I’m not using Immich myself (yet). I’ll probably look into it more once they release smart albums - that could be a real game changer together with the frame!

1 Like