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.json
for 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
.env
with 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:3000
in 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.json
to 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.service
Add:
[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.target
Enable:
sudo systemctl enable smartdisplay
sudo systemctl start smartdisplay
This 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.