A self-hosted family dashboard designed for wall-mounted displays and dedicated tablets. Aggregates weather, calendar events, tasks, goals, stocks, and more into a high-contrast, glanceable interface optimized for viewing from a distance.
Built with Next.js 16, deployable via Docker, and configurable through a built-in web admin panel — no code edits required after setup.
Shown with sample data — weather and stock prices are live (public, no-key APIs), everything else (calendar, goals, to-dos) is mock content for the screenshot. Background photo by Massimiliano Morosinotto on Unsplash
| Section | Description | Service |
|---|---|---|
| Clock & Weather | Large clock, current conditions, UV index, 5-day forecast | Open-Meteo (free, no key) |
| Second City | Optional second clock and weather card for a remote location | Open-Meteo |
| Goals | Card-based goals list | Google Sheets |
| To-Dos | Interactive task list with priority colours | Google Tasks |
| Events | Upcoming calendar events (14-day window) | Google Calendar |
| Celebrations | Anniversary notifications (90-day window) | Google Calendar |
| Trip Planner | Countdown cards for upcoming trips | Google Calendar |
| Stock Ticker | Infinite-scrolling ticker pinned to the bottom | Yahoo Finance (no key) |
| Events Carousel | Upcoming live music events | Ticketmaster API |
| Connectivity | Live latency, packet loss, and uptime | pi-monitor (proxies UniFi Site Manager) |
A full-screen black overlay fires every 5–10 minutes to prevent image burn-in on OLED panels.
The minimum setup needs no API keys — just location and a password for the admin panel.
git clone https://github.com/TacoRebell/daylight-dashboard.git
cd daylight-dashboard
npm install
cp .env.example .env.localEdit .env.local and set at minimum:
ADMIN_PASSWORD=choose-a-passwordStart the dev server:
npm run devOpen http://localhost:3000 for the dashboard and http://localhost:3000/admin to configure your location and stocks.
Weather data comes from Open-Meteo — free, no API key required. Stock data comes from Yahoo Finance — also no key required.
Visit /admin to configure the dashboard without touching any code or files.
| Setting | What it does |
|---|---|
| Primary Location | City search → sets weather, clock, and UV index |
| Secondary Location | Toggle on + search for a second city clock/weather card |
| Connectivity Monitor | Toggle on to show network stats proxied from the pi-monitor stack |
| Stock Ticker | Add/remove ticker symbols |
| Events Carousel | Toggle on to show live music events via Ticketmaster |
Changes take effect immediately after saving — no restart required.
Protecting the admin panel: set ADMIN_PASSWORD in your environment. Without it the login page returns a 503 error.
Required for: goals, to-dos, events, celebrations, trip planner, and daily quotes.
- Go to Google Cloud Console and create a project.
- Enable these APIs: Google Calendar API, Google Tasks API, Google Sheets API.
- Create OAuth 2.0 credentials (Web application type). Add
https://developers.google.com/oauthplaygroundas an authorised redirect URI. - Open OAuth Playground. Click the gear icon, check Use your own OAuth credentials, and enter your client ID and secret.
- Authorise these scopes:
https://www.googleapis.com/auth/calendar.readonlyhttps://www.googleapis.com/auth/taskshttps://www.googleapis.com/auth/spreadsheets.readonly
- Click Exchange authorization code for tokens and copy the Refresh token.
- Add to
.env.local:GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... GOOGLE_REFRESH_TOKEN=...
Calendar IDs — go to each calendar's settings page → Integrate calendar → copy the Calendar ID:
GOOGLE_CALENDAR_ID_ANNIVERSARY=... # anniversaries / celebrations
GOOGLE_CALENDAR_ID_TRIPS=... # trips / travelGoogle Sheets — create a spreadsheet and add the ID from its URL:
GOOGLE_SHEET_ID=...Expected sheet structure:
- Sheet1, cell A1 — quote or message of the day (optional)
- Sheet named "Quotes" — col A: quote text, col B: author (rows 2+)
- Sheet named "Goals" — col A: goal text (rows 2–10)
Shows upcoming live music events for your area.
- Register at developer.ticketmaster.com — free tier available.
- Add your key to
.env.local:TICKETMASTER_API_KEY=...
- Enable the carousel in
/admin→ Events Carousel.
Shows live WAN latency, packet loss, and uptime. This isn't a built-in integration — Daylight proxies the metrics from a separate self-hosted service (referred to here as pi-monitor, not included in this repo) rather than calling a router/ISP API directly, so Daylight itself never needs those credentials.
To use this widget, you need your own service reachable on your network that exposes:
GET /api/unifi/isp
returning JSON shaped like:
{
"summary": {
"latency_ms": 14,
"packet_loss_percent": 0,
"download_mbps": 512.3,
"upload_mbps": 41.7,
"uptime_percent": 99.98,
"isp_name": "Example ISP"
}
}(any field can be null; see src/app/api/connectivity/route.ts for exactly how each is consumed). The reference implementation proxies UniFi Site Manager, but the contract above is all Daylight cares about — point it at anything that speaks this shape.
- Deploy your
/api/unifi/isp-compatible service and note its URL. - Add to
.env.local(defaults tohttp://192.168.1.5if omitted):PI_MONITOR_URL=http://<your-service-host>
- Enable the widget in
/admin→ Internet Connectivity.
# Build
docker build -t daylight-dashboard .
# Run
docker run -d \
--name daylight-dashboard \
--restart unless-stopped \
-p 3000:3000 \
--env-file .env.local \
-v $(pwd)/config.json:/app/config.json \
daylight-dashboardThe -v config.json mount persists your admin settings across container restarts and rebuilds.
docker compose up -dThe included docker-compose.yml handles the volume mount and resource limits automatically.
See deploy.md for a complete guide covering Docker installation, auto-start, reverse proxy setup, and Raspberry Pi-specific performance tuning.
All configuration lives in two places:
.env.local— secrets and API keys (never committed)config.json— display preferences, edited via/admin. Gitignored since it can contain real location data; copyconfig.example.jsontoconfig.jsonto start (or just start the app —readConfig()falls back to built-in defaults if the file is missing).
| Variable | Required | Description |
|---|---|---|
ADMIN_PASSWORD |
Yes | Password for the /admin settings panel |
GOOGLE_CLIENT_ID |
For Google features | OAuth2 client ID |
GOOGLE_CLIENT_SECRET |
For Google features | OAuth2 client secret |
GOOGLE_REFRESH_TOKEN |
For Google features | Long-lived OAuth2 refresh token |
GOOGLE_SHEET_ID |
For goals & quotes | Spreadsheet ID from URL |
GOOGLE_CALENDAR_ID_ANNIVERSARY |
For celebrations | Calendar ID |
GOOGLE_CALENDAR_ID_TRIPS |
For trip planner | Calendar ID |
TICKETMASTER_API_KEY |
For events carousel | Ticketmaster Discovery API key |
PI_MONITOR_URL |
For connectivity widget | pi-monitor stack URL (defaults to http://192.168.1.5) |
Copy .env.example to .env.local for a fully documented template.
layout.tsx (revalidates 5 min)
└── getStocks() → yahoo-finance2 → StockTicker
page.tsx (force-dynamic, reads config.json)
└── Promise.all([
getFamilyGoals() → Google Sheets
getAnniversaries() → Google Calendar
getTrips() → Google Calendar
getUpcomingEvents() → Google Calendar
getQuotes() → Google Sheets / ZenQuotes API
])
Client-side polling:
WelcomeHeader — weather every 15 min (Open-Meteo)
WeatherCard — weather every 15 min (Open-Meteo)
SecondaryClockWeather — weather on mount (Open-Meteo)
TodoList — tasks every 60 sec (/api/tasks)
StockTicker — network stats every 30 sec (/api/connectivity)
API routes:
GET /api/config— read dashboard configPOST /api/config— write dashboard config; requires an authenticated/adminsession; callsrevalidatePath('/')to flush ISR cacheGET /api/tasks,POST /api/tasks/complete— Google Tasks proxyGET /api/connectivity— pi-monitor ISP metrics proxyGET /api/events-carousel— Ticketmaster events near your primary location (1h ISR cache)
npm run dev # dev server on :3000
npm run build # production build
npm run lint # ESLintThe project uses Next.js 16 App Router with TypeScript. Server components fetch data directly from lib/; client components receive it as props or poll their own API routes.
MIT — see LICENSE.

