A battery analytics platform for 100k+ IoT devices

As the sole developer I built a battery analytics platform for 100,000+ IoT thermostats: a tested capacity and runtime estimation library on an internal PyPI, Dagster ingestion, FastAPI services, dashboards, and monitoring.

The platform estimates the remaining battery capacity and runtime of more than 100,000 IoT thermostats. I am its sole developer and work on all parts of it, from the estimation algorithm to the integration into the company's Vue dashboard.

The original task was the estimate itself: when the battery of a given thermostat will run out. Running that estimate in production required more components. The estimate needs field data, and the field data needs a scheduled pipeline. Other systems read the estimate through a service, people inspect it in dashboards, and monitoring reports failures. Every component has to be deployable and revertible.

Components

field data  -> ingestion (Dagster -> Parquet/MinIO)
            -> estimation library (capacity + runtime, typed, tested, internal PyPI)
            -> services (FastAPI: per-device estimates, fleet queries)
            -> dashboards (Streamlit for analysts, company Vue dashboard for the product)
            -> monitoring (health checks, alerting)

The estimation logic is packaged as a separate library, and the other components depend on it.

Estimation library

The capacity and runtime estimation is implemented in one typed Python library based on Polars and published to an internal PyPI. It is tested and strictly type-checked.

Four components use the library: the ingestion pipeline, the per-device diagnostic tool, the fleet reporting, and the evaluation pipeline. An unnoticed change in estimation behavior would affect all four at once. Because the library is a versioned package, each consumer pins a known version, and a change to the algorithm reaches a consumer only through a new release and an explicit version update.

Ingestion

Dagster jobs pull device telemetry, build runtime and capacity datasets, and store them in MinIO as Parquet, partitioned by time.

The fleet produces telemetry continuously. With time partitions, reprocessing one month touches only that month's data, and re-running a partition is cheap and idempotent.

Services and dashboards

The estimates are available in three places. FastAPI services return estimates for a device, a fleet, or a customer to other systems. A per-device diagnostic view shows why the system produced a given estimate for a thermostat. The fleet-level estimates are integrated into the company's existing Vue dashboard, which the product team and customers already use.

The Vue dashboard is maintained by other engineers. I built the battery analytics shown there and integrated them into that frontend.

Monitoring

Health checks and alerting detect ingestion failures, stale datasets, and data-quality problems before the numbers reach the people who use them.

Working as the sole developer

One person builds and operates all components, so each component has to be simple enough for that person to understand and run. This is the reason for the tested core library, the replayable ingestion, the thin services, and monitoring that raises an alert when a step fails.