LifeHub is a personal analytics system: it pulls daily health, nutrition, workout, and spending data into one model, turns it into consistency metrics, and shows the result on a phone dashboard, a Home/Lock Screen widget, and an always-on E-Ink display. The design goal is a "calm technology" view of habits: there when you need it, updated on your terms, never demanding attention.
The analytics: data model, metrics, and consistency scoring
Data sources
- HealthKit: steps, active energy, sleep, weight, dietary calories and macros, and workout sessions.
- Food-delivery spending: a Google Sheets CSV of takeout orders.
- Market prices: VOO daily closes from Stooq's free CSV endpoint (no API key), used for a spending counterfactual.
Metrics
- Streaks and consistency ratios across every source, for example an Active Day Ratio: the share of days over 8,000 steps.
- Clean Streak: days since the last takeout order.
- Weight with forward-filling, so a missed daily weigh-in does not break the series.
- DoorDash-vs-VOO counterfactual: what the same delivery spend would be worth today if it had been invested in the S&P 500 instead.
Consistency over headline numbers
The dashboard is scored around rhythm and streak protection rather than any single number. Weight, for example, is one input among many, not the headline. The main view is a 30-day timeline plus compact cards for nutrition, workouts, movement, sleep, weight, and delivery spend. The widget shows 7-day Move, Eat, and Sleep habit sparklines and a lock-screen "habits hit" gauge.
Refresh model
Data refreshes are intentional rather than running in the background:
- Incremental refresh: recomputes the last 7 days.
- Comprehensive refresh: rebuilds the full history.
Either one compiles a dashboard_data.json payload of calculated summaries plus raw chart data. The E-Ink display fetches that payload and the widget reads a small summary the app writes after each refresh, so all the computation stays on the iPhone.
The Architecture: A Tale of Two Systems
The repo is split into two real components, LifeHub_app/ and LifeHub_dash/, bridged by a thin cloud layer:
1. LifeHub_app (iOS + WidgetKit): A SwiftUI application and its WidgetKit extension. The app is the central data aggregator; the widget is a read-only glance surface sharing the app's SwiftData store via an App Group.
2. The Bridge (GitHub Gist): A private JSON file serves as the dead drop between phone and Pi.
3. LifeHub_dash (Raspberry Pi & E-Ink): A hardware-optimized Python application that wakes on a schedule, fetches the Gist JSON, renders the dashboard, and sleeps.
Part 1: LifeHub_app (iOS)
The main app target renders a dark frosted-glass "Consistency Bento" dashboard, a redesign away from an earlier lighter layout, built around the 30-day timeline and metric cards described above. StockPriceFetcher supplies the VOO closes for the delivery-spend counterfactual.
Home Screen & Lock Screen Widget
LifeHubWidgetExtension renders 7-day Move/Eat/Sleep habit sparklines (.systemSmall, .systemMedium) and a lock-screen habits-hit gauge (.accessoryCircular, .accessoryInline). The widget never touches HealthKit, the network, or refresh logic directly — it only reads a small cached WidgetSummary JSON blob that the main app writes after each refresh, keeping the widget process lightweight and battery-safe.
Publishing the payload
Unlike apps that drain battery in the background, LifeHub only refreshes when asked. Each refresh uploads dashboard_data.json to a GitHub Gist via a Personal Access Token stored in the iOS Keychain.
Part 2: LifeHub_dash (Raspberry Pi)
A Raspberry Pi Zero 2 W with a Waveshare 7.5" E-Ink display and a PiSugar 3 battery HAT, currently deployed and running Raspberry Pi OS Lite (Debian 13 "Trixie"), waking daily at 7:00 AM local time via the PiSugar RTC.
The "Fat Python, Skinny HTML" Approach
Rendering complex layouts on E-Ink is notoriously hard with standard drawing libraries, so LifeHub uses the web stack instead: a local Flask server renders a Tailwind-styled HTML page, Selenium + headless Chromium captures a pixel-perfect 800×480 screenshot, and that screenshot is converted to a 1-bit bitmap and pushed to the E-Ink panel.
The Run-and-Sleep Cycle
run_and_sleep.sh drives the lifecycle: wake at the scheduled RTC alarm, git reset --hard origin/main to pull the latest deploy (any change made only on the Pi is wiped on the next wake — everything must be pushed to GitHub first), fetch the Gist JSON, render and push the new screen, check for a STOP file (for safe SSH debugging), then schedule the next alarm and shut down completely.
Conclusion
LifeHub turns a Raspberry Pi from a tinkering toy into a polished lifestyle appliance, and a WidgetKit extension into a zero-friction status check. By keeping the heavy lifting on the iPhone and the Pi strictly a retrieval-and-render surface, it balances high-fidelity data visualization against battery durability — a personalized, always-on reflection of health, finance, and habits.
