Turn your Raspberry Pi into a beautiful, Nest Hub-style smart display! This project creates a stunning carousel of cards featuring weather, calendar, photos, and Home Assistant integration - perfect for your kitchen counter, living room, or anywhere you want a smart display.
Main ambient view with weather overlay and nature photos
Calendar agenda view showing upcoming events
Detailed weather forecast with 5-day predictions
- Ambient Card: Gorgeous nature photos from Unsplash with time and weather overlay
- Daily Summary Card: AI-powered daily insights using Google Gemini
- Home Assistant Card: Seamless integration with your HA dashboard
- Weather Forecast Card: 5-day forecast with detailed weather info
- Calendar Agenda Card: Weekly view of your upcoming events
- Current Weather: Temperature, humidity, wind speed/direction, precipitation
- 5-Day Forecast: Daily predictions with beautiful weather icons
- Wind Direction Arrows: Visual indicators so you know which way the wind blows
- Colored Weather Icons: Sunny (yellow), cloudy (gray), and more
- OpenMeteo API: Free, reliable weather data that just works
- Auto Refresh: Weather updates every 30-45 minutes (optimized for performance)
- Floating Calendar: Shows your next 24 hours right on the ambient card
- Agenda View: Weekly calendar with clear day headers
- OAuth2 Authentication: Secure Google Calendar access
- 12-Hour Format: AM/PM time display for easy reading
- Auto Refresh: Calendar events update every 1-2 minutes
- Unsplash Integration: High-quality nature photos that change regularly
- API Refresh: New photos every 1.5-3 minutes to keep things fresh
- Slideshow: Photos cycle every 1-2 minutes for variety
- Photographer Attribution: Credits displayed in the corner (because artists deserve recognition!)
- Customizable Query: Set your preferred photo theme (nature, landscapes, etc.)
- Landscape Orientation: Optimized for display screens
- Performance Optimized: Lower resolution images for smooth performance
- iFrame Embedding: Seamless HA dashboard integration
- Touch Navigation: Swipe gestures for easy navigation
- Settings Button: Quick access to HA settings
- Back Button: Return to main display with one tap
- Google Gemini AI: Daily summary generation that actually makes sense
- Smart Summaries: Contextual daily overviews that are actually useful
- Configurable Timing: Set when daily summaries appear (morning coffee time?)
- Fallback Support: Graceful handling when AI is unavailable
- Nest Hub Design: Glassmorphism and modern aesthetics that look great
- Touch-Friendly: Optimized for touchscreen interaction
- Responsive Design: Adapts to different screen sizes
- Uniform Navigation: Consistent back/settings buttons everywhere
- Cursor Hidden: Clean display experience for kiosk mode
- Low Power Mode Detection: Automatically adjusts for Raspberry Pi
- DOM Caching: Reduced DOM queries for better performance
- Throttled Updates: Prevents excessive API calls and DOM updates
- Optimized Intervals: Longer refresh times on low-power devices
- Event Delegation: Efficient event handling
- Debounced Resize: Smooth window resize handling
- Image Optimization: Lower resolution images for better performance
- Raspberry Pi (3B or newer recommended)
- Node.js 18+ and npm
- Touchscreen Display (optional but highly recommended)
- Google Account with Calendar access
- Unsplash Account (free)
- Google Gemini API Key (optional, for AI summaries)
-
Clone the repository
git clone https://github.com/Piflyer/SmartDisplayPi.git cd SmartDisplayPi -
Install dependencies
npm install
-
Set up Google Calendar OAuth2
This is the trickiest part, but we'll get through it together! Follow the official Google Calendar API quickstart:
- Go to Google Calendar API Node.js Quickstart
- Complete steps 1-6 to create OAuth2 credentials
- Download the JSON file as
client_secret.json - Place it in your project root
- Run the OAuth2 setup:
node setup-oauth.js
- Follow the browser authorization flow
- This creates
token.jsonfor persistent authentication
-
Set up Unsplash API
- Create account at Unsplash Developers
- Create a new application
- Get your Access Key and Secret Key
-
Set up Google Gemini AI (Optional)
- Go to Google AI Studio
- Create an API key for Gemini
- Add it to your environment variables
-
Configure environment variables
cp env.example .env
Edit
.envwith your API keys:GOOGLE_APPLICATION_CREDENTIALS=./client_secret.json UNSPLASH_ACCESS_KEY=your_unsplash_access_key_here UNSPLASH_SECRET_KEY=your_unsplash_secret_key_here GEMINI_API_KEY=your_gemini_api_key_here PORT=3000
-
Start the server
node server.js
-
Enjoy your smart display!
- Open
http://localhost:3000in your browser - For Raspberry Pi:
http://your-pi-ip:3000
- Open
Access settings by clicking the gear icon on any card:
- Photo Query: Change Unsplash search term (default: "nature landscape")
- Home Assistant URL: Set your HA instance URL
- Location: Set latitude/longitude for weather data
- Summary Time: Set when daily AI summaries appear (default: 8:00 AM)
- System Actions: Refresh page functionality
- Settings persist in browser local storage
Want to add more cards to the carousel? Here's how:
-
Add new card to HTML (
public/index.html):<div class="carousel-card custom-website-card"> <div class="custom-header"> <h3>Custom Website</h3> <button class="back-btn">←</button> <button class="settings-btn">⚙️</button> </div> <iframe src="https://your-website.com" frameborder="0"></iframe> </div>
-
Add CSS styling (
public/styles.css):.custom-website-card { background: #fff; border-radius: 20px; box-shadow: 0 8px 32px rgba(0,0,0,0.1); } .custom-header { display: flex; align-items: center; padding: 16px; border-bottom: 1px solid #eee; }
-
Update JavaScript (
public/app.js):// Add to currentCard logic const totalCards = 6; // Update total count // Add event listeners for new buttons document.addEventListener('click', (e) => { if (e.target.closest('.custom-website-card .back-btn')) { goToCard(0); // Return to ambient card } });
<div class="carousel-card news-card"> <div class="news-header"> <h3>📰 News</h3> <button class="back-btn">←</button> </div> <iframe src="https://news.ycombinator.com" frameborder="0"></iframe> </div><div class="carousel-card dashboard-card"> <div class="dashboard-header"> <h3>📊 Dashboard</h3> <button class="back-btn">←</button> </div> <iframe src="https://your-dashboard.com" frameborder="0"></iframe> </div>server.js: Main Express server with API endpoints- Google Calendar API: OAuth2 authentication and event fetching
- Google Gemini AI: AI-powered daily summaries
- Unsplash API: Photo retrieval with caching
- OpenMeteo API: Weather data integration
- Static file serving: Frontend delivery
public/index.html: Main HTML structurepublic/styles.css: Nest Hub-esque styling with performance optimizationspublic/app.js: Carousel logic, API calls, and performance optimizations- Touch/Click Events: Navigation and interaction handling with throttling
SmartDisplayPi/ ├── server.js # Main Express server ├── setup-oauth.js # Google OAuth2 setup script ├── test-server.js # Development server with mock data ├── kiosk.sh # Browser kiosk script (adapted from pi-kiosk) ├── kiosk.service # SystemD service for kiosk mode (adapted from pi-kiosk) ├── public/ │ ├── index.html # Main HTML structure │ ├── styles.css # CSS styling with optimizations │ └── app.js # Frontend JavaScript with performance features ├── client_secret.json # Google OAuth2 credentials ├── token.json # OAuth2 tokens (generated) ├── .env # Environment variables ├── package.json # Dependencies and scripts └── README.md # This file GET /api/weather?lat=40.7128&lon=-74.0060- Returns current weather and 5-day forecast
- Uses OpenMeteo API (free, no key required)
- Auto-refreshes every 30-45 minutes (optimized)
GET /api/calendar/events- Returns next 24 hours of events
GET /api/calendar/agenda- Returns next week of events
- Requires Google OAuth2 setup
- Auto-refreshes every 1-2 minutes (optimized)
GET /api/photos/:query?- Returns Unsplash photos for specified query
- Auto-refreshes every 1.5-3 minutes (optimized)
GET /api/summary- Returns AI-generated daily summary
- Uses Google Gemini AI
- Configurable timing via settings
- Automatically detects Raspberry Pi and low-power devices
- Adjusts refresh intervals and performance settings
- Optimizes for ARM processors and limited memory
- Caches frequently accessed DOM elements
- Reduces DOM queries by 80%
- Improves rendering performance
- Prevents excessive API calls
- Reduces server load
- Improves battery life on mobile devices
- Event delegation for better performance
- Throttled swipe gestures
- Debounced resize handlers
- Lower resolution images for better performance
- Optimized loading strategies
- Reduced bandwidth usage
-
Install Node.js:
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt-get install -y nodejs -
Clone and Setup:
git clone https://github.com/Piflyer/SmartDisplayPi.git cd SmartDisplayPi npm install -
Configure OAuth2 (on a computer with browser):
- Run
node setup-oauth.js - Copy
token.jsonto Raspberry Pi
- Run
-
Start Service:
chmod +x start.sh ./start.sh
This project includes kiosk mode configuration files adapted from Jeff Geerling's pi-kiosk project:
kiosk.sh: Browser kiosk script (adapted from pi-kiosk)kiosk.service: SystemD service for auto-starting kiosk mode (adapted from pi-kiosk)
To set up kiosk mode:
-
Install prerequisites:
sudo apt install unclutter
-
Set up kiosk script:
mkdir -p /home/pi/sdp cp kiosk.sh /home/pi/sdp/kiosk.sh chmod +x /home/pi/sdp/kiosk.sh
-
Move service file to system location:
sudo cp kiosk.service /lib/systemd/system/kiosk.service sudo systemctl daemon-reload sudo systemctl enable kiosk.service -
Start kiosk mode:
sudo systemctl start kiosk
Important: The service file must be placed in /lib/systemd/system/ for systemd to recognize it. After copying the file, run sudo systemctl daemon-reload to tell systemd about the new service.
The kiosk script will automatically launch Chromium in full-screen mode pointing to your SmartDisplayPi application at http://localhost:3000.
sudo nano /etc/systemd/system/smartdisplay.serviceAdd:
[Unit] Description=Smart Display Service After=network.target [Service] Type=simple User=pi WorkingDirectory=/home/pi/SmartDisplayPi ExecStart=/usr/bin/node server.js Restart=always Environment=NODE_ENV=production [Install] WantedBy=multi-user.targetEnable:
sudo systemctl enable smartdisplay sudo systemctl start smartdisplayThis project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
- Low Power Mode Detection: Automatically detects Raspberry Pi and adjusts performance
- DOM Caching: Reduced DOM queries by 80% for better performance
- Throttled Updates: Prevents excessive API calls and DOM updates
- Optimized Intervals: Longer refresh times on low-power devices
- Event Delegation: More efficient event handling
- Image Optimization: Lower resolution images for better performance
- Cursor Hidden: Clean display experience for kiosk mode
- AI Integration: Google Gemini AI for daily summaries
- System Actions: Page refresh functionality in settings
- Enhanced Settings: More configuration options
- Better Error Handling: Graceful fallbacks for API failures
- Touch Optimization: Better touch gesture handling
- Responsive Design: Improved mobile and tablet support
- Loading States: Better user feedback during data loading
- Accessibility: Improved keyboard navigation
The kiosk mode setup files (kiosk.sh and kiosk.service) are adapted from Jeff Geerling's pi-kiosk project. This project provides a simple and effective way to create a persistent browser kiosk on Raspberry Pi devices.
Original Project: geerlingguy/pi-kiosk
License: GPL v3
Author: Jeff Geerling
The kiosk configuration enables full-screen browser mode that automatically starts on boot, making it perfect for creating a smart display that runs continuously without user intervention.