A Case Study with TimescaleDB and Grafana
In many organizations, hundreds of assets are constantly and quietly publishing operational data (temperatures, pressures, flow rates, statuses, energy readings) into a system that was never designed to answer questions. The data arrives, gets stored, and sits there. Millions, if not billions of readings, accumulating year after year, locked inside a monolithic platform built for collecting rather than reacting or revealing.
This pattern repeats across industries: district heating, building automation, water treatment, manufacturing, fleet management. Anywhere physical assets generate telemetry faster than organizations can make sense of it. The data is there, waiting to be used.
In this post, I will show you how I took one such dataset and turned it into a scrubbable, geospatially-aware, user-friendly monitoring platform using TimescaleDB and Grafana. Two popular tools, both well-maintained, well-documented, and exceptionally good at their respective jobs. The domain here is district heating, but the approach applies anywhere you have assets, sensors, and a growing suspicion that your data could be working harder.
The dataset
The PreDist dataset, published by enercity AG, is one of the few publicly available operational datasets from a real district heating network. It contains time-series data from 93 substations across two manufacturers, along with labeled fault reports, maintenance logs, and normal-behavior reference windows.
Each substation logs between 14 and 19 metrics at 1- to 10-minute intervals: supply and return temperatures on both the primary and secondary side, flow rates, heat power, valve positions, pump statuses, control modes, and cumulative energy readings. Across 49 distinct metric types, the dataset totals roughly 12 million timestamp-rows across all substations.
In a production setting, this data would arrive as a continuous stream using AMQP or MQTT from controllers, OPC UA from SCADA, or a message bus sitting between edge devices and the database. The ingestion pipeline matters, but it's not the topic of this post. The interesting part is what happens once the data lands.
TimescaleDB's Continuous Aggregates: pre-computed answers
Raw data is great for forensics, but querying raw data directly for routine monitoring is often inefficient and time-consuming. Building a UI that depends solely on raw table scans leads to slow page loads and a poor user experience.
TimescaleDB's continuous aggregates solve this. At scheduled intervals, the raw data is continuously aggregated and stored in materialized views. For this case, I've chosen to aggregate the raw data (1- to 10-minute resolution) into hourly buckets. Those hourly buckets are then in turn aggregated into daily buckets. This makes the aggregates hierarchical, which speeds up the aggregation process since the raw data only needs to be read once, by the hourly aggregate.
Grafana: where data becomes legible
TimescaleDB as a Grafana datasource is a natural fit. Grafana speaks PostgreSQL natively, and TimescaleDB adds macros like $__timeFilter() and time_bucket() that make time-series queries straightforward to write. Both datasources and dashboards are provisioned as version-controlled JSON: reproducible, diffable, with no manual clicking required.
A read-only PostgreSQL role ensures the visualization layer can never accidentally modify the data, enforcing least-privilege access even for internal tools.
Grid Map Dashboard: the geographical overview
The Grid Map dashboard is designed for macro-level monitoring, giving a network operator an immediate visual check of system status.
A geomap panel plots all 93 substations as colored markers on a dark basemap. The color represents the selected metric's value: averaged, min'd, max'd, or first/last, over the current time window. Blue is cold, red is hot, and a continuous-BlYlRd scale handles everything in between.

Using Grafana's shortcut buttons to travel forward and backward in time turns a static map into a timelapse of the network's thermal state.


The queries hit the continuous aggregates exclusively. A query that aggregates across all 93 substations over a month of daily data touches roughly 120,000 rows instead of tens of millions. The panel responds in milliseconds.
Below the map: a distribution histogram, a ranked bar chart, and a time-series overlay of all substations. Four complementary views of the same data, same filters, same time range. Change the metric dropdown from p_net_supply_temperature to p_net_meter_heat_power and every panel updates in concert.

(Methodological note on map markers: the original dataset contains no geographic information. The positions were synthesized by computing pairwise correlations on outdoor temperature, supply temperature, and return temperature. Substations with similar thermal signatures cluster together. The resulting coordinates were then snapped to real OpenStreetMap building footprints in Östersund so the markers sit directly on real structures on the map.)
Substation Explorer Dashboard: the deep dive
The Explorer dashboard is for troubleshooting. When something on the Grid Map looks wrong and you need to understand why.

Select a manufacturer, pick a substation, and you get a full diagnostic layout:
- Network Supply & Return: primary side temperatures and flow on dual Y-axes.

- Heating Circuit Temperatures: HC1 supply, return, and outdoor temperatures with setpoints rendered as dashed lines.

- Domestic Hot Water Temperatures: supply, return, storage upper/lower, and setpoints. Useful for spotting stuck actuators or failing storage tank stratification.

- Energy & Volume: cumulative meter readings over time. Identify meter resets easily.

- Fault History: a table of all recorded faults for the selected substation, with dates, problem categories, fault labels, and monitoring potential scores.

The important detail: fault and disturbance annotations overlay on every time-series panel. Red regions mark fault periods, from report date through to anomaly end. Orange markers flag maintenance events. You can visually correlate a temperature anomaly with a known incident. Or, more interestingly, spot anomalies that don't have a corresponding incident report. Those are the expensive ones.

What this proves
Many industry organizations that operate physical assets face this problem: enormous volumes of operational data, accumulating faster than anyone can analyze, trapped in systems that were built for collection rather than insight. The instinct is often to match the scale of the data with equally scaled infrastructure: data lakes, stream processing frameworks, dedicated analytics clusters, or six-figure platform contracts. Sometimes that's warranted.
But for storage, aggregation, and visualization (the foundation that every other analytics effort depends on) the answer is frequently simpler than expected.
TimescaleDB turns PostgreSQL into a time-series database without abandoning everything that makes PostgreSQL useful: SQL, joins, indexes, transactions, roles, and an ecosystem of tools that already know how to talk to it. Continuous aggregates handle performance declaratively. The database does the work so you don't have to build a pipeline to do it. Whether the data comes from heating substations, production lines, HVAC controllers, or water pumps, the pattern is the same.
Grafana turns SQL queries into interactive dashboards with almost no impedance mismatch. If you can write a SELECT statement, you can build a panel. Variables, annotations, provisioned datasources, and dashboard-as-code mean the visualization layer is as reproducible as the schema it queries.
Together, they took 12 million rows of raw sensor data and made them browsable. Not after a six-month platform initiative, nor after procuring three new SaaS contracts: just two dedicated tools, a schema that respects the data's shape, and a few well-placed materialized views.
The data was always there. It just needed somewhere better to go.