Tinkering · Home energy

Where does my house's power actually go?

My utility draws me a chart. I wanted the data behind it.

So I built a pipeline that collects my home's electricity use every half hour, lines it up against the weather hour by hour, and keeps all of it somewhere I can ask questions of. It runs on its own in AWS, deploys through GitHub Actions, and is analysed with DuckDB.

It's a proof of concept for a bigger question: what architecture turns raw utility data into insight that's actually useful about one specific home? Most of what I learned came from the parts that fought back.

70complete days archived
3,362half-hour readings
1,704hours of weather joined
0gaps in the series
0restated readings

Pipeline figures generated from the live archive on September 25, 2026. Household numbers are deliberately left off this page.

The shape of it

Two independent feeds, one archive, analysed in place. Nothing here needs a server running.

Sources

Utility API

30-minute interval usage. Behind 2FA and a Cloudflare rule that fingerprints TLS handshakes.

Open-Meteo archive

Hourly temperature and humidity, requested in UTC.

Collect

EventBridge Scheduler

Weather at 07:00, usage at 09:00 Eastern, and a session keepalive every 20 minutes.

AWS Lambda · Python

One function, four modes: probe, fetch, keepalive, weather. Browser-TLS session via curl_cffi.

SSM Parameter Store

The session tokens, rotated and written back on every refresh.

Store

S3 raw archive

Every pull kept whole and partitioned by date, so overlapping pulls can reveal a restated history.

Analyse

DuckDB

Reads S3 directly. One SQL model dedupes pulls, strips padding and joins weather. No warehouse.

Standing reports

Pipeline health, always-on load, weather sensitivity, weather-adjusted anomalies.

Watch

CloudWatch → SNS

Alarms for a failed pull, a stale usage feed and a stale weather feed, tracked separately.

Ship

GitHub Actions

Plan on every pull request that touches the pipeline, apply on merge. OIDC into AWS; no stored keys.

Terraform

All of the above as code, with remote state and native S3 locking.

What fought back

Each of these looked fine until it was tested against the real thing.

Built with

Collect
Python 3.12, uv, curl_cffi, AWS Lambda
Schedule
EventBridge Scheduler, SSM Parameter Store
Store
Amazon S3, partitioned by pull date
Analyse
DuckDB, SQL, Open-Meteo
Operate
CloudWatch, SNS
Deploy
Terraform, GitHub Actions, OIDC, ruff
Host
Netlify, Cloudflare DNS

What's next