A greenhouse has several sensors on it. Temperature, humidity, CO2. Then moving things get added — vents and valves.
At install time this is not a problem. Three of them, so write three into the code. The problem arrives three years later. The thermometer fails and is replaced with a different product. That product measures in Fahrenheit. Another CO2 sensor goes in. A valve is added.
If the control code has to be edited every time, that greenhouse becomes a thing that cannot be fixed without the person who installed it.
The node says what it is
In this sample each node declares itself in six fields.
printf("%s{\"id\":\"%s\",\"role\":\"%s\",\"kind\":\"%s\","
"\"model\":\"%s\",\"unit\":\"%s\",\"value\":%.1f}",
i ? "," : "", n->id, n->role, n->kind, n->model,
n->unit, n->value);
id · role · kind · model · unit · value.
unit existing as a field is the core of it. A thermometer that measures in Fahrenheit says Fahrenheit. It does not helpfully pre-convert to Celsius. Hardware says honestly only what it knows, and the aligning happens above.
The aligning happens in one place
/// The value moved into the unit the rules use.
///
/// This is the only place in this sample that knows about units, and it
/// decides from the unit a node declared, not from a model number. A sensor
/// nobody has ever heard of still states its unit, so it lands here exactly.
double get canonicalValue {
switch (unit) {
case 'F':
return (value - 32) * 5 / 9;
default:
return value;
}
}
Branch on the model number and this place becomes a list. It grows a line with every new product bought, and a product missing from the list quietly gets used with the wrong value.
Look at unit and a product you've never seen aligns itself.
The rules don't know part numbers
One growing rule is seven lines.
Rule(
name: 'vent above 26C',
whenKind: 'temperature',
above: 26.0,
thenKind: 'vent',
setTo: 80.0,
elseSetTo: 0.0,
),
No sensor is named. No relay is named either. Only the kind of value read and the kind of thing to move.
TH-100 or FX-200 — if its kind is temperature, this rule sees it.
The swap was actually done
Install A — Celsius thermometer, three nodes.
A: discovered 3 nodes — t1:temperature:TH-100(C), h1:humidity:HM-20(pct), v1:vent:VT-9(pct)
A: vent above 26C: 26.1 -> v1=80.0


Now swap the thermometer for a Fahrenheit model and add a CO2 sensor and a valve.
=== pass B: probe swapped for a Fahrenheit model, two nodes added ===
server code: unchanged rules: unchanged rebuild: none
B: discovered 5 nodes — t1:temperature:FX-200(F), h1:humidity:HM-20(pct),
c1:co2:CO-5(ppm), v1:vent:VT-9(pct), w1:valve:WV-3(pct)
B: vent above 26C: 26.1 -> v1=80.0 · water below 60% humidity: 62.8 -> w1=0.0


Server code unchanged. Rules unchanged. No rebuild.
And vent above 26C fired at exactly 26.1 in both installs. Because the value the Fahrenheit sensor sent was moved to Celsius in one place. Without that one place, 79 degrees Fahrenheit would have sailed past a 26-degree threshold and the vent would have stayed open.
This content requires Developer or above
Sign in and upgrade your plan to continue reading.
View Plans