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, Synology Photos, Telegram), handling image processing, resizing, dithering, and overlay generation.
- Multiple Data Sources:
- Google Photos: Uses the Picker API to securely select albums and photos.
- Synology Photos: Connect directly to your Synology NAS (supports DSM 7 Personal and Shared spaces).
- Telegram Bot: Send photos directly to your frame via a Telegram bot.
- URL Proxy: Display images from any URL.
- AI Generation: Generate unique images using OpenAI (GPT Image, DALL-E) or Google Gemini.
- Smart Image Processing:
- Automatic cropping to device aspect ratio (800x480 or 480x800).
- Smart Collage: Automatically combines two landscape photos in portrait mode (or vice versa) to maximize screen usage.
- Dithering: Applies Floyd-Steinberg dithering optimized for Spectra 6 color e-paper.
- 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.
The easiest way to run the server is as a Home Assistant add-on.
-
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
/datafor persistent storage (automatically backed up) - Port 9607 is exposed for direct device access
- Ingress is enabled - access via Home Assistant sidebar
- The add-on uses
-
Start:
- Click Start
- Enable Start on boot if desired
- Access via the sidebar or
http://homeassistant.local:9607
If upgrading from a previous version that used /config/esp32-photoframe-server/:
- Data is automatically migrated to
/dataon first startup - Check logs to verify migration completed successfully
- Old data in
/configcan be manually removed after verification
For non-Home Assistant deployments:
docker run -d \
-p 9607:9607 \
-v /path/to/data:/data \
--name photoframe-server \
aitjcize/esp32-photoframe-server:latestAccess the dashboard at http://localhost:9607 (or your server IP, or via Home Assistant ingress).
-
Create Account:
- On first launch, you'll be prompted to create an admin account
- Enter a username and password
-
Generate Device Token:
- Go to Settings → Account
- Click Generate New Token
- Give it a name (e.g., "Living Room Frame")
- Copy the token - you'll need this for your ESP32 device
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 root@homeassistant.local -p 22222 - Public Domain: Use a domain name with Cloudflare Tunnel or similar
-
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:
ssh -L 9607:localhost:9607 root@homeassistant.local -p 22222
- 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
- If running on Home Assistant, set up port forwarding first:
-
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
- 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.
- Create a new bot via @BotFather on Telegram.
- Get the Bot Token.
- Go to Settings → Data Sources in the dashboard.
- Select Source: Telegram Bot.
- Enter your Bot Token and save.
- Send a photo to your bot on Telegram. The frame will update to show this photo immediately.
- Go to Settings → Data Sources.
- Select Source: URL Proxy.
- Add URLs to images you want to display.
- Assign URLs to specific devices.
Generate unique AI artwork for your photo frame using OpenAI or Google Gemini.
-
Get an API Key:
- OpenAI: Get your key at platform.openai.com/api-keys
- Google Gemini: Get your key at aistudio.google.com/app/apikey
-
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
Once you've configured a photo source, the correct URL will be displayed in the settings:
http(s)://<hostname/IP address>:9607/image/<source>
- Copy the URL from the settings page.
- Copy your device token from Settings → Account.
- Log into the photo frame web app.
- Go to the Auto-Rotate tab and paste the URL and Token in the appropriate boxes.
- Click the 'Save Settings' button.
GET /image/google_photos: Returns a random image from Google Photos.GET /image/synology_photos: Returns a random image from Synology Photos.GET /image/telegram: Returns the last photo sent via Telegram Bot.GET /image/url_proxy: Returns a random image from configured URLs.GET /image/ai_generation: Returns a newly generated AI image based on device prompt.
All image endpoints require authentication via Bearer token:
Authorization: Bearer <your-device-token>
# Build Docker image
docker build -t esp32-photoframe-server .
# Or use make
make build
make runUse the included deploy-dev.sh script for rapid local testing:
./deploy-dev.sh [ssh-host]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
If you find this project useful, consider buying me a coffee! ☕
MIT License - see LICENSE file for details.