This dashboard gives a real-time view of a home solar, battery, and grid setup — showing live power flows, historical charts, electricity pricing, and running cost/earnings tracking. Here's how something like this can be put together from scratch.
The dashboard is split into three layers: a data source, a secure proxy, and a frontend.
Your inverter, battery, and smart meter all send data to Home Assistant via their respective integrations. HA stores these as sensor entities and exposes them through a local REST API. This is the single source of truth.
A small Python script sits between the dashboard and Home Assistant. It only exposes a whitelisted set of sensors (no smart home controls), polls them every 5 seconds, caches the results, and completely blocks any write operations. Your HA API token never leaves your local network.
A single HTML file with vanilla JavaScript fetches data from the proxy and renders it. No frameworks, no build step. Chart.js handles the 12-hour history graphs. The animated power flow diagram is built with SVG and CSS animations.
The Flask app and a Caddy reverse proxy run together in Docker Compose. Caddy automatically handles HTTPS certificates and adds security headers including HSTS, CSP, and X-Frame-Options. Secrets like API tokens are kept in an environment file that is never committed to version control.
Instead of opening ports in your router, a Cloudflare Tunnel container makes an outbound-only connection to Cloudflare's network. Your home IP is never exposed, there are no inbound ports to attack, and you get DDoS protection and global CDN performance for free. Traffic flows: visitor → Cloudflare → tunnel → Caddy → Flask → Home Assistant.
Once Home Assistant is running on your local network, install the integrations for your specific hardware from the HA integration store. Popular ones:
After setup, each piece of hardware exposes sensor entities in HA (e.g. solar power in watts, battery state of charge, grid import/export). Note down the entity IDs — you'll need them for the proxy whitelist.
Create a long-lived access token in your HA profile settings. This is what the proxy uses to authenticate with the HA API. Keep it private and never expose it in frontend code.
The proxy is the most important security piece. Here's the core concept in pseudocode:
/api/states/<entity_id> endpoint — but only serve a response if the entity is in the whitelist/api/history/period/<start> endpoint for chart history data, again whitelisted/Flask is a good choice for this — it's lightweight, easy to read, and straightforward to containerise. The whole proxy is around 100 lines of Python.
The dashboard is a single HTML file — no build tools, no npm, no frameworks. Everything runs in the browser.
setInterval calls the proxy API every 5 seconds and updates the DOM directly<animateMotion>. The number of visible dots scales with the power levelYour Home Assistant instance should stay on your local network only. The proxy is the only thing that talks to it.
Only expose the specific sensors you need. Don't create a catch-all proxy — your locks, cameras, and alarms should be unreachable.
Reject all HTTP methods except GET and OPTIONS. Even if someone finds the endpoint, they can't control anything.
Caddy handles TLS automatically. HSTS, Content Security Policy, X-Frame-Options DENY, and X-Content-Type-Options headers are all set to harden the public-facing site.
Running a Cloudflare Tunnel means your router has zero inbound ports open. Your home IP is never visible to the public internet, removing a large attack surface entirely.
API tokens and URLs are stored in a .env file that is listed in .gitignore and never committed to version control. Rotate tokens periodically and never hardcode them in source files.
A docker-compose.yaml with three services is all you need:
.env fileCaddyfile that reverse proxies to Flask, handles HTTPS, and sets security headersFor a public URL, register a domain and point its nameservers to Cloudflare. In the Cloudflare Zero Trust dashboard, create a tunnel and add a public hostname route pointing to your local Caddy port. Cloudflare handles all TLS — no Let's Encrypt setup needed on your end.
The dashboard code — including the animated SVG power flow diagram, the Flask proxy, the Docker configuration, and this guide — was developed collaboratively with Claude, Anthropic's AI assistant.
Claude helped design the architecture, write and iterate on the Python proxy code, debug edge cases in the JavaScript, and structure the frontend layout. The approach taken here — a read-only proxy layer, a whitelisted entity set, cookie-based visit tracking, and a single-file no-build frontend — all came out of back-and-forth conversation with Claude.
If you want to build something similar, Claude is a great tool for talking through the architecture, generating boilerplate, and working through problems as they come up. You can try it at claude.ai.
Start by describing your hardware setup and what data you want to see. Then ask Claude to help you design the proxy first — getting the security layer right before building the frontend means you won't accidentally expose anything you shouldn't.