DEV Community

Willson Guan
Willson Guan

Posted on

Building a Local Energy Monitoring Pipeline Without Locking Yourself Into One Vendor

Energy monitoring projects often start with one simple request: show the current power reading in a dashboard.

The first version is usually straightforward. Poll a meter, parse its response, and draw a chart. The trouble starts later, when the meter changes, a second inverter is added, or the same data needs to reach both a local dashboard and a cloud service.

At that point, device-specific code tends to spread everywhere.

A more durable design is to separate the system into four small responsibilities:

Meter or inverter
       |
       v
Device adapter
       |
       v
Normalized energy model
       |
       +--> Local web dashboard
       +--> Home automation
       +--> Time-series storage
       +--> Optional cloud forwarding
Enter fullscreen mode Exit fullscreen mode

This article explains why that middle normalization layer matters and how to build the pipeline without turning a home-energy project into a large integration platform.

Start with the question your gateway must answer

A gateway does not need to understand every feature exposed by every device. It needs a stable answer to a narrower question:

What is the current electrical state, and is the source healthy enough for me to trust it?

For a three-phase source, a useful normalized record might include:

{
  "timestamp": "2026-09-01T08:30:00Z",
  "source": {
    "type": "meter",
    "model": "example-three-phase-meter",
    "online": true
  },
  "phases": [
    { "voltageV": 230.4, "currentA": 1.8, "activePowerW": 402 },
    { "voltageV": 231.1, "currentA": 0.7, "activePowerW": 148 },
    { "voltageV": 229.8, "currentA": 2.2, "activePowerW": 487 }
  ],
  "totals": {
    "activePowerW": 1037,
    "importEnergyKwh": 8421.7,
    "exportEnergyKwh": 1926.3
  }
}
Enter fullscreen mode Exit fullscreen mode

The exact schema is less important than the boundary. Code outside the adapter should not need to know whether the original source was Modbus TCP, a vendor HTTP API, or a solar inverter endpoint.

Put device quirks inside adapters

Every device family has details that should not leak into the rest of the system:

  • register addresses and byte order;
  • signed versus separate import/export values;
  • missing phase fields;
  • vendor-specific HTTP response shapes;
  • timeout and retry behavior;
  • unit conversions;
  • firmware-dependent differences.

An adapter owns those quirks and returns one normalized record. The dashboard should not contain branches such as if source is Shelly or if source is Fronius. Neither should the uploader.

That separation makes testing much easier. You can feed stored device responses into an adapter and verify the normalized result without starting the web UI or contacting a real meter.

Treat direction as data, not presentation

Import and export are a common source of errors.

One device may report import and export as separate cumulative counters. Another may use signed active power. An inverter may report production but not the full household load. These are not interchangeable measurements.

Normalize the direction explicitly. Do not make the chart decide what a negative number means.

For example:

  • activePowerW > 0 can be documented as grid import;
  • activePowerW < 0 can be documented as grid export;
  • cumulative importEnergyKwh and exportEnergyKwh remain separate fields;
  • solar production stays separate from grid flow.

The rule must be documented and covered by fixtures. Otherwise, an attractive dashboard can quietly reverse import and export.

Validate locally before forwarding anything

Cloud forwarding should be the last step, not the first.

A safer setup sequence is:

  1. Connect to one source device.
  2. Show the raw and normalized readings locally.
  3. Compare phase values with the source device's own interface.
  4. Verify import and export direction using a known household load.
  5. Observe timeouts and reconnect behavior.
  6. Enable an upload destination only after the local data is believable.

This creates a useful debugging boundary. If the local record is wrong, fix the adapter. If the local record is right but the remote platform is wrong, inspect the transformation or upload path.

Keep the first deployment boring

For a server-style gateway, a small Docker deployment is usually enough:

docker build -t energy-device-gateway .
docker run --rm -p 8080:8080 energy-device-gateway
Enter fullscreen mode Exit fullscreen mode

The service can expose a local setup page, poll one supported source, store a small amount of runtime history, and forward normalized records at a conservative interval.

Avoid adding a message broker, database cluster, and rules engine before the basic pipeline is trustworthy. Those components can be useful later, but they should solve an observed requirement.

Security is part of the architecture

Energy readings reveal household behavior, and a configuration screen may contain local network addresses or upload destinations. Even a LAN-only gateway should have clear security defaults.

Useful minimums include:

  • no built-in default password;
  • a one-time password bootstrap flow;
  • one-way password hashing;
  • no public Internet exposure by default;
  • masked identifiers in logs and screenshots;
  • explicit limits on uploaded file types and sizes;
  • conservative request and polling timeouts.

Container deployment does not replace application authentication. A container isolates packaging and runtime dependencies; it does not decide who may use the web console.

Support a small device set honestly

There is a strong temptation to list every protocol and device that might work. A smaller tested matrix is more useful.

For example, our current open-source gateway work focuses on three LAN source families:

  • IAMMETER WEM3080T over Modbus TCP;
  • Fronius SunSpec inverters over Modbus TCP;
  • Shelly Pro 3EM through its local RPC HTTP interface.

That limited scope makes fixtures, validation, and documentation manageable. A fourth adapter should be added when it can be tested, not when its name looks good in a compatibility list.

A practical test strategy

You can test most of the pipeline without electrical hardware on the desk.

Use three layers:

Adapter fixtures

Store representative source responses and verify parsing, units, missing values, and direction conventions.

A deterministic device simulator

Run local Modbus or HTTP profiles so reconnects, malformed responses, and UI behavior can be exercised repeatedly.

Real-hardware validation

Before claiming support, compare the normalized output with a real source under several conditions: low load, a known large load, phase imbalance, and solar export where applicable.

Simulation improves repeatability. It does not replace the final real-device check.

Where the pipeline can grow

Once the source boundary is stable, several additions become much easier:

  • MQTT publishing;
  • Home Assistant discovery or sensors;
  • InfluxDB or another time-series destination;
  • alerts for stale or implausible readings;
  • per-circuit or multi-site models;
  • offline buffering;
  • comparison tools that consume the same normalized schema.

The key is that these features consume the normalized record. They should not become new places where vendor-specific parsing is implemented.

Final takeaway

A useful local energy gateway is not defined by how many protocols it lists. It is defined by whether it makes one source understandable, testable, and reusable.

Keep device quirks inside adapters. Normalize direction and units once. Validate locally before forwarding. Add destinations only after the data boundary is stable.

If you want a concrete implementation to inspect, the EnergyMeterHub local energy gateway project documents the browser workflow and links to the source. The repository is available on GitHub.

Disclosure: I work on EnergyMeterHub and the open-source gateway referenced above. The architecture discussion is intended to stand on its own; the links are included for readers who want to inspect the implementation.

Top comments (0)