The problem
Industrial energy monitoring in India today still works mostly the way it worked 20 years ago.
A factory or solar plant has expensive electrical equipment generating data: voltage, current, kW, kWh, power factor, frequency, temperature. The meters measure all of it. The meters even know how to send it. But where does that data actually go?
In most plants, the answer is "into a Windows PC in the control room running SCADA software from 2008." The PC has a screen. The screen shows a dashboard. The dashboard was built once, by a contractor, four years ago, and nobody knows how to change it. To see the data, you walk to the control room. To get a report, you ask an engineer to export an Excel file once a day.
If you have multiple sites, you have multiple Windows PCs in multiple control rooms with multiple sets of dashboards that don't talk to each other. If you're an energy auditor or a parent company, you have no live way to see what your sites are actually doing.
The platform's founders had a vision for something better: a real cloud-based energy monitoring platform that any industrial customer could plug into their existing meters and start seeing their data in a browser. Not a desktop tool. Not a vendor-locked solution. A real multi-tenant SaaS where any energy company could sign up, configure their meters, build their own dashboards, and have their team logging in from anywhere.
The brief had several non-negotiables:
- Real multi-tenancy — every customer fully isolated from every other. One customer's data, one customer's users, one customer's dashboards. No "tenant_id column" shortcuts.
- Each customer at their own subdomain — each tenant on its own branded subdomain. Customers want to feel like the platform is theirs, not a shared tool.
- Real-time — the data on screen has to refresh every few seconds. This is the difference between watching the plant operate and looking at yesterday's report.
- Customers build their own dashboards — not a fixed template. Energy customers all have different equipment and different questions. The platform has to be flexible enough that any customer can put together the views they need.
- Formula engine — raw sensor channels are not what people actually want to see. The platform needs to let customers define their own calculations on top of raw data ("Total plant power = Line A + Line B + Line C") and treat the result as a first-class number they can put on any widget.
- Time-window logic that an energy auditor will trust — energy data is tricky because today is incomplete. The platform has to handle "this week", "this month", "this year" the right way (rolling windows excluding partial days), or the numbers will be wrong in ways that destroy trust.
- A real ingestion pipeline — meters and sensors have to push data into the platform reliably, in real time, over the standard industrial IoT messaging protocol.
The approach
We made three big architectural decisions up front that ended up shaping the entire project.
Decision #1 — Schema-per-tenant, not column-per-tenant.
The lazy way to build multi-tenancy is to put a tenant_id column on every table and filter every query by it. It works, sort of, until it doesn't — until one customer's heavy query slows down another customer's dashboards, until a developer forgets a tenant_id filter and a bug leaks data across customers, until you need to delete a customer and you can't because their data is intermingled with everyone else's.
We chose the rigorous version instead: each customer gets their own actual database schema. Their users, their widgets, their variables, their formulas, their meter configurations, their stored data — all in a separate schema that's physically isolated from everyone else's. A customer can never see another customer's data even by accident. One customer's slow query doesn't slow down anyone else. A customer can be deleted cleanly with one command. This is the same kind of separation a bank uses between accounts, applied to a SaaS platform.
This is harder to build than the lazy version, and it pays for itself the first time a customer runs a 10-million-row report and doesn't take down everyone else's dashboards.
Decision #2 — The dashboard is configuration, not code.
A dashboard built in code can only be changed by a developer. That's fine for a single product with a single audience. But the platform serves industrial customers with wildly different equipment — solar inverters, factory line meters, building HVAC systems, agricultural irrigation pumps. Every customer needs different widgets in different positions showing different variables.
So we built the dashboard as a configuration system. Each widget on a customer's dashboard is a database record describing: which type (line chart, gauge, bar chart, etc.), which variable, which time period, which color, which refresh interval, which position on the page. The customer (or their admin) can add, remove, resize, and reconfigure widgets without ever touching code. New widget types get added as the platform evolves and every customer immediately has access to them.
Decision #3 — Time-window logic has to be obviously correct.
Energy auditors are picky about numbers — and they should be, because plant managers make procurement decisions based on those numbers. If the "this month" view includes today's partial data, the consumption number is too low and people think they're saving money when they're not.
We documented the exact behavior of every time period in a doc that lives in the codebase, and built the logic to match. This week = the last 7 complete days, not the calendar week. This month = the last 30 complete days. This year = the last 365 complete days. Energy data (kWh) uses differential math (end reading minus start reading). Power data (kW) uses averaging. Energy widgets get tagged with is_energy_data: true so the system applies the right logic automatically.
Sounds tiny. It's the difference between numbers an auditor trusts and numbers they don't.
What we built
A real multi-tenant SaaS infrastructure
- Each customer registered as a "Client" with their own subdomain, secret token, optional admin password
- The system automatically creates a fresh database schema for each new customer
- All requests are routed to the right schema based on the subdomain in the URL
- Customer data is physically separated, not just filtered
- Customers can have their own users, each with their own login
Subdomain routing
- The solar customer's subdomain lands on their tenant
- The energy customer's subdomain lands on their tenant
- Each tenant sees only their own widgets, variables, formulas, and data
- The same backend serves all of them — no per-customer infrastructure to maintain
Per-customer user management
- Each tenant has their own user accounts
- Logins are token-based (secure, browser-friendly, with cookie protection)
- Password reset flow built in
- An optional admin password for sensitive operations (changing meter config, deleting widgets) with a configurable timeout — routine viewing is normal login, making changes requires the admin password
Real-time MQTT data ingestion
- The platform runs an ingestion service that subscribes to the customer's MQTT broker
- As each message comes in, it's parsed, validated, mapped to the customer's variables, and pushed into time-series storage
- The raw original message is also stored separately as an audit trail — if there's ever a question about a number, the source data is right there
- Multiple ingestion patterns supported: per-tenant brokers, shared brokers with topic-based routing, etc.
Variables and formulas
- Each customer defines variables — labelled data channels (e.g. "Line A current", "Solar inverter output", "Roof temperature")
- Each customer defines formulas that combine variables into derived numbers ("Total power = Line A + Line B + Line C", "Net consumption = Total drawn − Solar produced")
- Formulas are stored as configuration and evaluated at query time
- The result of a formula can be displayed on any widget the same way as a raw variable — the customer doesn't have to think about whether something is "raw" or "derived"
11 widget types
Each one configurable, each one with its own settings:
- Live Value (Label-Value Card) — big number with label and unit, refreshes live
- Bar Chart — compare values across categories or time periods
- Pie Chart — breakdown by source or category
- Line Chart (X-Y) — value over time, multiple series, customizable axes
- Real-Time Line Chart — streaming chart that updates as new data flows in
- Trend Line — with zoom levels for year, month, week, day, hour
- Sankey Chart — energy flow visualization showing where power goes (one of the most useful chart types in energy and one of the hardest to build)
- Scatter Chart — relationships between variables
- Calendar Heatmap — day-by-day color grid showing consumption patterns
- Gauge — speedometer-style live readout
- Photo / iFrame Widgets — equipment photos, drone shots, embedded external dashboards
Each widget can be configured with its own refresh interval (1 second, 2, 5, 10, 30 seconds, or 1 minute) so customers can decide how live they want their dashboard to feel.
Time-period logic that holds up to an energy audit
- Six standard time periods (live, past day, this week, this month, this year, custom range)
- All "rolling window" semantics — "this week" is the last 7 complete days, not the calendar week
- Energy widgets use differential math (end reading minus start reading), summed across days
- Power widgets use averaging math
- Energy widgets explicitly tagged so the system applies the right logic automatically
- Documented in detail so any developer or auditor can verify the behavior
Configurable sidebar / navigation
- Each customer's dashboard layout, page structure, and navigation are configurable
- Admins can group widgets into pages, organize by site or by equipment, build the navigation that fits their plant
- The "no-sidebar" design variant we're working on now is a cleaner full-screen mode that some customers prefer
Production deployment
- Multiple production tenants live in production today
- Nginx configurations for routing subdomains to the right backend
- Database backups (the project includes both local dumps and a Telegram-based backup script that pushes daily snapshots to a private chat for safety)
- Test data scripts for spinning up realistic data when developing new features
Special endpoint for energy data with time periods
A dedicated endpoint that any widget can call to get energy data for any of the standard time periods (live, past day, this week, this month, this year), with all the differential math and time-window logic applied correctly. The widgets just ask for the period they need; the backend handles the complexity.
The hard part — and how we solved it
There were three hard parts on this project, and they all came from trying to build something that feels simple to use but handles enormous complexity behind the scenes.
Hard part #1 — True multi-tenancy that doesn't fall apart
Schema-per-tenant is the right architecture but it's fundamentally harder than column-per-tenant. Migrations have to run across every schema. Foreign keys have to stay within a schema. The routing layer has to figure out which schema to use before any database query happens. Cross-tenant operations (for the platform admin) require special handling.
We solved this by building on a mature multi-tenancy library that handles the schema management correctly, then layering custom routing on top of it. Every incoming request gets matched to a tenant by subdomain, the right schema gets activated, and the rest of the application code just runs normally. New tenants are created with a single command that provisions their schema, runs all migrations, and sets up their default configuration. Adding a new client to the platform takes minutes, not days.
Hard part #2 — Real-time data without melting the database
Live IoT data is brutal on a traditional database. A single meter pushing values every 2 seconds = 43,200 readings per day. Multiply by 50 sensors per plant, 10 plants per customer, and several customers — you're looking at millions of new rows a day. If you naively store everything and query everything, the database falls over within a month.
We solved this in three ways:
- Separate the raw stream from the processed stream. Raw MQTT messages get stored in a different table from the processed time-series data. The raw table is the audit trail; the processed table is what dashboards query.
- Index the time column aggressively so queries for "show me the last 7 days" are fast even with millions of rows.
- Aggregate on read, not on write. Instead of pre-computing every possible time aggregation, the system computes them at query time using efficient SQL. This keeps the write side simple and lets the read side stay flexible (any custom time range works without pre-computation).
The result: dashboards stay fast even with millions of readings stored, and adding a new widget type doesn't require rebuilding any data pipelines.
Hard part #3 — Energy time-window logic
This is the part that makes the difference between a tool that energy auditors trust and one they don't. The naive approach — "this month = January 1 through today" — is wrong because today is incomplete. The right approach is rolling windows that exclude partial days for energy data, but the logic gets complicated quickly:
- For "live" mode showing kW (instantaneous power), we want the latest reading
- For "live" mode showing kWh (cumulative energy used today), we want today's consumption so far — calculated as (current reading − reading at midnight)
- For "past day" mode, we want yesterday's complete 24 hours, not "the last 24 hours" (which includes part of today)
- For "this week" mode, we want the last 7 complete days, excluding today
- And the energy math has to be differential (sum of daily deltas), not averaging — otherwise the kWh number is meaningless
We documented every one of these rules in plain English and built the logic to match. Then we tested it against real meter data and verified the numbers matched what an energy auditor would compute by hand. The result: the platform's "this month" number is the same number an auditor would write on their report — to the watt-hour.
The outcome
- Live in production with multiple real industrial customers running on their own subdomains
- True multi-tenancy — schema-per-tenant isolation, not just column filtering
- Real-time data ingestion from real industrial meters via the standard IoT messaging protocol
- 11 widget types including the hard ones (Sankey for energy flow, real-time streaming line charts, calendar heatmaps)
- Customer-configurable dashboards — every customer builds their own views from a library of widgets without writing code
- Variables and formulas so customers see the numbers they actually care about, not raw sensor channels
- Time-window logic energy auditors trust — rolling windows, differential math for kWh, averaging math for kW, all tagged automatically
- Configurable refresh intervals from 1 second to 1 minute per widget
- Per-customer admin password protection for sensitive operations
- Subdomain routing —
<customer>.<platform-domain>for every customer - Daily database backups pushed to a Telegram channel for safety
- Real production tenants: solar, energy, and industrial operators
The bigger outcome: a real Indian energy intelligence company has a modern multi-tenant cloud platform that any industrial customer can sign up for, configure to their plant, and start using the same day. No SCADA software in the control room. No PCs in the plant. No exporting Excel files at the end of the day. Just live data, in a browser, from anywhere.
This is what industrial software in 2026 should look like.
Finish reading — and take the PDF.
Drop your details to unlock the rest of Industrial Energy-Monitoring SaaS on this page and download the full write-up as a PDF.