<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[EnergyMeterHub]]></title><description><![CDATA[EnergyMeterHub]]></description><link>https://energymeterhub.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>EnergyMeterHub</title><link>https://energymeterhub.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 21 Sep 2026 11:30:34 GMT</lastBuildDate><atom:link href="https://energymeterhub.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Smart Meter Data: Avoid Double Counting, Resets, and Daily Total Errors]]></title><description><![CDATA[A meter can report a perfectly reasonable cumulative reading while the dashboard built from it shows an impossible daily total. Before changing the hardware, check how the application identifies readi]]></description><link>https://energymeterhub.hashnode.dev/smart-meter-data-double-counting-daily-totals</link><guid isPermaLink="true">https://energymeterhub.hashnode.dev/smart-meter-data-double-counting-daily-totals</guid><category><![CDATA[iot]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[guanvee]]></dc:creator><pubDate>Fri, 11 Sep 2026 01:56:07 GMT</pubDate><content:encoded><![CDATA[<p>A meter can report a perfectly reasonable cumulative reading while the dashboard built from it shows an impossible daily total. Before changing the hardware, check how the application identifies readings, handles resets, and decides where a day begins.</p>
<p>This walkthrough uses synthetic data for one cumulative grid-import register. It is a design example, not a report of a field-tested installation.</p>
<h2>First, define what the number means</h2>
<p>These three measurements need different calculations:</p>
<ul>
<li><strong>Power in W:</strong> a rate at a particular moment. Estimating energy requires integration over time.</li>
<li><strong>Interval energy in Wh:</strong> energy attributed to a defined interval. Sum non-overlapping, deduplicated intervals.</li>
<li><strong>Cumulative energy in Wh:</strong> a running counter. Compute differences between accepted readings from the same register and counter epoch.</li>
</ul>
<p>For example, readings of 100,000 Wh and 100,250 Wh represent an increase of 250 Wh, or 0.25 kWh. Adding the two readings would produce 200.25 kWh, which is not the interval's consumption.</p>
<p>Do not combine grid import and export into one increasing counter. Keep register identity, direction, unit, and scaling explicit. Convert to your chosen unit once at the ingestion boundary, preserving the meter's supported precision.</p>
<h2>Keep measurement time separate from arrival time</h2>
<p>A useful record includes a device identifier, register identifier, source timestamp or sequence number, received-at timestamp, normalized value, quality flag, and counter epoch.</p>
<p>The source time says when the measurement applies. The arrival time says when your server received it. After a network outage, old readings may arrive together; assigning all of them to the arrival minute creates a misleading load spike.</p>
<p>If the meter supplies no trustworthy measurement timestamp, record that limitation. A timestamp assigned by your poller is not proof of the device's measurement time.</p>
<p>For cumulative readings, a repeated value with a newer timestamp can be valid: no measurable energy was added. A repeated event with the same identity must not create another accounting entry.</p>
<h2>Make retries safe before calculating totals</h2>
<p>Choose an event key using the source's actual guarantees. Device, register, counter epoch, and source sequence can work when that sequence is unique within an epoch. A timestamp is suitable only if its resolution reliably distinguishes samples.</p>
<p>Receiving an identical retry should leave the result unchanged. If the same event key arrives with a different value, retain the conflict for investigation instead of silently counting both.</p>
<p>The following JavaScript example is deliberately narrow. It processes a strictly time-ordered stream for one register, rejects ambiguous readings, and uses integer Wh because the synthetic meter has 1 Wh resolution.</p>
<pre><code class="language-javascript">function acceptReading(previous, current) {
  const valid = r =&gt; r &amp;&amp;
    Number.isSafeInteger(r.timeMs) &amp;&amp; r.timeMs &gt;= 0 &amp;&amp;
    Number.isSafeInteger(r.totalWh) &amp;&amp; r.totalWh &gt;= 0 &amp;&amp;
    typeof r.epoch === "string" &amp;&amp; r.epoch.length &gt; 0;

  if (!valid(current) || (previous &amp;&amp; !valid(previous))) {
    return { status: "invalid" };
  }
  if (!previous) return { status: "baseline", next: current };
  if (current.timeMs &lt; previous.timeMs) return { status: "late" };
  if (current.timeMs === previous.timeMs) {
    const same = current.totalWh === previous.totalWh &amp;&amp;
      current.epoch === previous.epoch;
    return { status: same ? "duplicate" : "conflict" };
  }
  if (current.epoch !== previous.epoch) {
    return { status: "new-baseline", next: current };
  }
  if (current.totalWh &lt; previous.totalWh) {
    return { status: "counter-drop" };
  }
  return {
    status: "accepted",
    deltaWh: current.totalWh - previous.totalWh,
    next: current
  };
}
</code></pre>
<p>Update the baseline only when the result contains <code>next</code>. Store a delta only for <code>accepted</code> results. In production, event deduplication, delta storage, and baseline advancement must form one durable transaction or equivalent atomic operation. An in-memory check alone does not protect against concurrent workers or process crashes.</p>
<p>This example does not implement event storage, rollover recovery, clock correction, or late-data reconciliation. Route rejected records to a review or replay path; do not simply discard them. Devices with finer resolution need an appropriate scaled unit or decimal representation.</p>
<h2>Treat a counter drop as a question, not an answer</h2>
<p>A lower reading can mean a confirmed reset, a replaced meter, a register rollover, an out-of-order response, or a parsing error. These are not interchangeable.</p>
<p>Do not automatically take the absolute value of a negative difference. Do not automatically count the new reading as fresh usage either. Confirm the device's reset or rollover behavior before bridging the boundary.</p>
<p>A confirmed new epoch starts a new baseline in the example above. Energy across that boundary remains unaccounted for unless additional evidence supports recovery. Record that gap rather than presenting the day as complete.</p>
<p>Home Assistant also distinguishes different counter semantics. Its <a href="https://developers.home-assistant.io/docs/core/entity/sensor/">sensor developer documentation</a> explains when <code>total</code> and <code>total_increasing</code> are appropriate. Choose based on the source's behavior, not simply because its unit is kWh.</p>
<h2>Calculate daily totals using explicit boundaries</h2>
<p>A daily total is not just the last reading minus the first reading found in a folder. You need boundary coverage, a declared site timezone, and consistent treatment of gaps and resets.</p>
<p>For a continuous, trustworthy counter with no reset, missing intermediate samples may still leave a reliable total between two valid endpoints. They do not reveal how consumption was distributed inside that gap.</p>
<p>If a delta spans local midnight, assigning all of it to the later date shifts energy between days. Prefer readings at the boundary, or label any interpolation as an estimate. The same issue matters at tariff changes. Local days can also be 23 or 25 hours where daylight-saving time applies.</p>
<p>Keep import and export totals separate throughout this process. Display completeness alongside the daily figure when missing coverage could change its meaning.</p>
<h2>Avoid aggregating counters that reset independently</h2>
<p>Two resetting counters can briefly produce a false combined value if one resets before the other. Home Assistant's <a href="https://www.home-assistant.io/docs/energy/faq/">energy FAQ</a> specifically warns about inflated totals from this pattern and recommends configuring the individual cumulative sensors as separate Energy dashboard sources.</p>
<p>In a custom pipeline, validate each independent counter first, then aggregate compatible energy intervals. Also check the physical measurement boundaries: adding a whole-home meter and an EV submeter would count the EV twice if the whole-home reading already includes it.</p>
<h2>Test the failure cases before trusting the chart</h2>
<p>Use these cases as a small acceptance checklist:</p>
<ul>
<li>A 100,000 Wh baseline followed by 100,250 Wh produces exactly 250 Wh.</li>
<li>An identical replay produces no new accounting entry.</li>
<li>A newer timestamp with the same value produces a zero delta.</li>
<li>The same timestamp with a different value is a conflict.</li>
<li>An older timestamp goes to late-data handling.</li>
<li>A lower counter in the same epoch raises a counter-drop flag.</li>
<li>A confirmed new epoch creates a baseline, not an invented interval total.</li>
<li>A gap crossing midnight cannot silently become a complete daily allocation.</li>
</ul>
<p>Keep raw readings available so a correction can be reproduced. A dashboard should show the result of an auditable calculation, not be the only surviving record of it.</p>
<p>For readers building a local collection layer, the <a href="https://www.energymeterhub.com/our-projects/energy-device-gateway?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=external-content-pilot">EnergyMeterHub energy-device-gateway project page</a> is a related starting point. The checklist and sample above are general ingestion guidance, not a claim that the gateway implements every safeguard described here.</p>
<p>Disclosure: This article is published by EnergyMeterHub, which maintains the linked project page.</p>
]]></content:encoded></item></channel></rss>