ESP32 PhotoFrame Server
A image server for the ESP32 PhotoFrame project. This server acts as a bridge between the E-paper display and various photo sources (Google Photos, Immich, Synology Photos, Unsplash, Pexels, and more), handling image processing, resizing, dithering, and overlay generation.
Features
Supported Sources
Mix and match photo sources per device:
Source Description Gallery Photos you upload from the dashboard, or send in via a Telegram bot . Google Photos Pick albums & photos securely via the Picker API. Immich Self-hosted photo server — sync selected albums or the All / Favorites / Memories views. Synology Photos Your Synology NAS (DSM 7 Personal & Shared spaces). Unsplash · Pexels Free stock-photo search — add topics (e.g. black and white, landscape) and each becomes a synced album. URL Proxy Display images from any image URL. AI Generation Generate images with OpenAI (GPT Image, DALL·E) or Google Gemini .
Other Features
Smart Image Processing :
Automatic cropping to each device's aspect ratio and resolution.
Smart Collage : Automatically combines two landscape photos in portrait mode (or vice versa) to maximize screen usage.
Dithering : Floyd-Steinberg dithering for both Spectra 6 color and GC16 grayscale e-paper panels, with per-device palette/gamma calibration.
Overlays :
Customizable Date/Time display.
Real-time Weather status (Temperature + Condition) based on location.
"iPhone Lockscreen" style aesthetics with Inter font and drop shadows.
Authentication :
User account system with login/registration.
Revocable API tokens for device access.
Session management.
Deployment
Home Assistant Add-on (Recommended)
The easiest way to run the server is as a Home Assistant add-on.
Installation
Add Repository :
Go to Settings → Add-ons → Add-on Store → ⋮ (three dots) → Repositories
Add: https://github.com/aitjcize/esp32-photoframe-server
Install Add-on :
Find "ESP32 PhotoFrame Server" in the add-on store
Click Install
Wait for the build to complete (5-15 minutes on first install)
Configure :
The add-on uses /data for persistent storage (automatically backed up)
Port 9607 is exposed for direct device access
Ingress is enabled - access via Home Assistant sidebar
Start :
Click Start
Enable Start on boot if desired
Access via the sidebar or http://homeassistant.local:9607
Data Migration
If upgrading from a previous version that used /config/esp32-photoframe-server/:
Data is automatically migrated to /data on first startup
Check logs to verify migration completed successfully
Old data in /config can be manually removed after verification
Docker (Standalone)
For non-Home Assistant deployments:
docker run -d \
-p 9607:9607 \
-v /path/to/data:/data \
--name photoframe-server \
aitjcize/esp32-photoframe-server:latest
Copy
Configuration
Access the dashboard at http://localhost:9607 (or your server IP, or via Home Assistant ingress).
Initial Setup
Create Account :
On first launch, you'll be prompted to create an admin account
Enter a username and password
Configure a Source & Frame :
Set up a photo source under Settings → Data Sources (see Supported Sources below).
Configure your frame under Settings → Devices .
Device access tokens are issued and managed automatically — you no longer generate or copy them by hand.
Google Photos Setup
Important
**Google OAuth Restriction**: Google does not allow `.local` domains or private IP addresses in OAuth redirect URIs. If running on Home Assistant, you must use one of these methods:
- **Port Forwarding** (recommended for one-time setup): `ssh -L 9607:localhost:9607 [email protected] -p 22222`
- **Public Domain**: Use a domain name with Cloudflare Tunnel or similar
Steps:
Create OAuth Credentials :
Go to Google Cloud Console
Create a new project or select an existing one
Enable the Google Photos Picker API
Go to Credentials → Create Credentials → OAuth 2.0 Client ID
Application type: Web application
Authorized JavaScript Origins : http://localhost:9607
Authorized Redirect URIs : http://localhost:9607/api/auth/google/callback
Click Create and save your Client ID and Client Secret
Configure the Server :
If running on Home Assistant, set up port forwarding first:
Access the dashboard at http://localhost:9607
Go to Settings → Data Sources
Select Source: Google Photos
Enter your Client ID and Client Secret
Click Save All Settings
Authenticate and Import Photos :
Go to the Gallery tab
Click Add Photos via Google
You'll be redirected to Google OAuth (sign in if needed)
Select the photos you want to display
Click Add to import them
After Setup :
The OAuth token is saved in the database
You can close the SSH tunnel (if used)
Access the server normally via Home Assistant ingress or http://homeassistant.local:9607
Re-authentication is only needed if you revoke access or want to add more photos
Synology Setup
Go to Settings → Data Sources in the dashboard.
Enable Synology Photos .
Enter your NAS URL (e.g., https://192.168.1.10:5001), Account , and Password .
If using 2FA, enter the OTP Code when testing the connection.
Select the Photo Space (Personal or Shared) and optionally a specific Album .
Click Sync Now to import metadata.
Immich Setup
Go to Settings → Data Sources and open the Immich tab.
Enter your Server URL (e.g., https://immich.example.com) and an API Key (in Immich: Account Settings → API Keys ).
Click Connect to validate, then choose the albums to sync — or the All Photos , Favorites , or Memories views.
Click Sync Now to import.
Telegram Bot (Gallery)
A Telegram bot is an optional way to add photos to the Gallery source — send a photo to the bot and it's uploaded to your gallery.
Create a new bot via @BotFather and copy its Bot Token .
Go to Settings → Data Sources → Gallery and enter the Telegram Bot Token , then save.
Send a photo to your bot. It's added to the Gallery, and frames using the Gallery source will show it.
URL Proxy Setup
Go to Settings → Data Sources .
Select Source: URL Proxy .
Add URLs to images you want to display.
Assign URLs to specific devices.
AI Generation Setup
Generate unique AI artwork for your photo frame using OpenAI or Google Gemini.
Get an API Key :
Configure API Keys :
Go to Settings → Data Sources → AI Generation
Enter your API key(s) and click Save API Keys
Configure Per-Device :
Go to Settings → Devices
Click Edit on the device you want to configure
Under AI Image Generation , select a provider (OpenAI or Google Gemini)
Choose a model and enter a prompt describing the images you want
Click Save
Available Models :
OpenAI : GPT Image 1, DALL-E 3, DALL-E 2
Google Gemini : Gemini 2.5 Flash Image, Gemini 3 Pro Image
Stock Photo Search (Unsplash & Pexels)
Turn any topic into a rotating album of free stock photography — great for a themed or black-and-white art frame.
Get a free API key :
Go to Settings → Data Sources , open the Unsplash or Pexels tab, paste the key, and click Connect .
Add one or more topics (e.g., black and white, mountains, minimalist). Each topic becomes a synced album and saves automatically.
Click Sync Now to import, then assign the source (and specific topics) to a device under Settings → Devices .
Photo Frame Configuration
Frames are configured from the dashboard — the server issues the access token and pushes settings to the device automatically:
Open Settings → Devices and select your frame.
In the Auto Rotate tab, enable "Use this server" and pick a Source (and, for album/topic sources, which albums or topics to rotate through).
Save. The server generates the device's token and pushes the image URL and settings to the frame — no manual token or URL copying required.
API Endpoints (For ESP32)
Image Endpoints
GET /image : The endpoint frames use. The device is identified by its bearer token and served its server-assigned source (cropped and dithered for its panel). Configure the source under Settings → Devices ; a device with no source assigned gets an error.
GET /image/<source> : A legacy URL form still routed for older firmware — the <source> is ignored . A device is only ever served the source assigned to it in the server, never one it isn't assigned.
Authentication
All image endpoints require authentication via Bearer token:
Authorization: Bearer <your-device-token>
Copy
Building
# Build Docker image
docker build -t esp32-photoframe-server .
# Or use make
make build
make run
Copy
Home Assistant Add-on Development
Use the included deploy-dev.sh script for rapid local testing:
./deploy-dev.sh [ssh-host]
Copy
This script:
Syncs code to Home Assistant's local add-on directory
Modifies config for development (port 9608, dev slug)
Triggers Supervisor to rebuild and restart the add-on
Support
If you find this project useful, consider buying me a coffee! ☕
License
MIT License - see LICENSE file for details.