Home automation field notes All concepts

data capture

Two sources, and what their disagreement means

Two sources for everything that matters, and, more importantly, a plan for the day they disagree, because they will, usually at three in the morning.

Anything that ingests events from the outside world (messages, notifications, webhooks) will stop working eventually, and it will do so without an error. The sender changes a wording. A permission is revoked by an OS update. A forwarding rule silently expires. The outside world, it turns out, does not file a change request first.

Having two sources for everything sounds like caution. In practice it is an invitation to a very specific kind of argument, held at three in the morning, between a cloud service that thinks it is raining and a sensor on the balcony that is dry. The interesting engineering is not in having both. It is in deciding who wins.

The data simply stops, and nothing anywhere says so. Your dashboard shows a smaller number and you assume it was a quiet month.

Capture the same event more than once

Redundant paths are the easy half. For a stream of transactional notifications, for example, you might have an instant path (a webhook fired directly), a store-and-forward path (the same content mailed to an inbox and ingested on a schedule), and an opportunistic path that catches events the others structurally cannot see.

They have different failure modes on purpose: the instant path needs your system up right now; the mail path survives downtime but is late; the third exists because some events never produce a message at all.

Deduplicate on a natural key, timestamp plus a couple of stable fields, so overlapping paths produce one record. Then record which paths saw each event. That by-product is what makes the next section possible, and it is worth surfacing in the interface: an event seen by two paths is not a duplicate, it is a healthy one. The concrete build of exactly this, on a stream of card payments, is on the money page.

The hard half: noticing a path went quiet

A relative test is the natural first idea: a path is stale if the other path saw the last few events and it did not. This works well and catches single-path failures.

But it cannot see the failure that matters most. If both parsers share the same pattern and the sender changes their wording, both paths stop at the same instant. No sightings are recorded at all, the comparison has nothing to compare, and the health sensor reports "fine" straight through a total outage.

So add an absolute arm: if nothing at all has been captured for longer than a plausible quiet period, say so, regardless of the comparison. Pick that threshold from your own history rather than a feeling: look at the real distribution of gaps between events, and choose something past the tail you are willing to be woken for.

Adding a path can break the monitor

Worth knowing before it happens: if a new path captures events the older ones structurally cannot see, every one of those events looks like a miss for the older paths, and the relative test starts reporting them as stale. Count only events that a judged path could plausibly have caught. Adding redundancy should not break the thing watching your redundancy.

Disagreement is information, do not average it

Where two independent sources measure the same physical quantity, the temptation is to take the mean and move on. Resist it.

Outdoor air quality is a good example. A modelled source built from official monitoring stations and a median of nearby citizen sensors will not agree, they can differ by a factor of two. Neither is ground truth: the model interpolates and may not know about the busy road outside your window, while low-cost sensors drift, especially with humidity.

Averaging destroys the most useful signal in the system: a sustained divergence means one of them is broken. Prefer one source, keep the other as a fallback, and display both. Then you can see the disagreement instead of laundering it into a single confident number.

Build the fallback chain explicitly

value = primary
        else secondary
        else tertiary
        else <unavailable, do not invent one>

And make the dependent logic degrade rather than vanish. A ventilation recommendation that needs outdoor particulates should keep working on temperature and humidity when that feed dies, and say the particulate figure is missing, instead of going unavailable and leaving you with nothing at exactly the moment a sensor failed.

A cheap trick for single-device sources

If your only outdoor reference is one hobbyist sensor, you have a single point of failure and no way to know whether it is telling the truth. Public citizen-science networks usually expose an area query. Taking the median of every device within a few kilometres survives any one going offline and rejects the badly-calibrated unit that reads five times high. In one such area sample there were a dozen devices, and at least one was obviously broken. A mean would have carried it; a median did not notice it.

The disagreement, it turns out, is often the most valuable reading in the house. Two sources that agree tell you the weather. Two that disagree tell you which one is broken.

Design rule: two sources that agree tell you the value. Two sources that disagree tell you something more useful, but only if you kept them both.