Each severity of a threshold rule has one condition. A condition is either one comparison (a leaf) or a group of conditions. This page describes how Hermetry evaluates them, so you can predict when an alarm opens.
Shape
Condition = { "key": …, "op": …, "value": …, "topic"?: … }
| { "all": [ Condition, … ] }
| { "any": [ Condition, … ] }
The form edits one comparison per severity. Groups are written in JSON mode.
Operators
op | True when |
|---|---|
> | the value is greater than the threshold |
>= | greater than or equal |
< | less than |
<= | less than or equal |
== | equal |
!= | not equal |
There is no “between”: use all with >= and <=. There is no arithmetic: a condition cannot say “setpoint + 10” or “more than the other sensor”. For that, compute a derived key in a flow (see Derived values) or use a Rules-card formula.
How values compare
| Value and threshold | Comparison |
|---|---|
Both are numbers, or text that reads as a number ("23.5", "1e3") | As numbers. "80" and 80 are equal. |
Anything else with == or != | As text: true equals "true". |
Anything else with >, >=, <, <= | Always false. |
The device sent null | == is false and != is true. |
So a status reported as text works with ==: { "key": "state", "op": "==", "value": "fault" }. Booleans work too: { "key": "door_open", "op": "==", "value": true } also matches a device that sends "true".
Per-device thresholds from attributes
Instead of a number, the value can name an attribute of the device:
{ "key": "temperature", "op": ">", "value": { "attribute": "max_temp" } }
In the form, type attribute:max_temp in Value.
- Hermetry reads the device’s server attribute of that name, then its shared attribute. Client attributes are not used.
- A device that has no such attribute is not checked for that comparison: the leaf is false. Give every device the attribute, or add a second severity with a fixed number.
- A changed attribute is used within about 30 seconds.
This lets one rule serve devices with different limits. Set the attributes on the devices’ Attributes tab, by import or through the API. See Attributes.
Groups: all and any
{
"all": [
{ "key": "door_open", "op": "==", "value": true },
{ "key": "pressure", "op": "<", "value": 1.2 }
]
}
allis true when every part is true;anywhen at least one is.- Groups can be nested up to 4 levels.
- One severity’s condition may contain at most 16 comparisons in total.
Rules over several keys
Devices often send keys in separate messages. A rule is evaluated on the device’s latest known values, not only on the message that just arrived:
- A rule is evaluated only on a message that carries at least one of its keys (from a matching topic). A message without any of its keys neither opens nor clears it, and does not count as a sample for debounce.
- For a key the message does not carry, the comparison uses the last value of that key the rule saw on this device.
- A comparison whose key the rule has never seen on the device is false.
Example: the door state arrives every minute and the pressure every ten seconds. The all rule above opens as soon as both are true, whichever message comes last.
The remembered values are kept with the rule’s state. A device silent for 30 days loses that state and starts afresh.
Topic filters
For devices that publish on their own MQTT topics, a leaf can be limited to one topic:
topic | The leaf applies to |
|---|---|
| absent | messages from any topic |
"factory/line1/temp" | messages from exactly this topic |
null | messages from the default topics only |
The same key on two topics counts as two different values. In the form, set it under Advanced > Only from topic. See MQTT topics and payloads.
Keys
A key is a telemetry key: no spaces, not a bare number, at most 128 characters. Keys are case-sensitive: Temperature and temperature are different keys. A key the profile does not declare is accepted with a warning; a number-like key only when the profile declares it.
Severities
With several severities, all conditions are evaluated on each message and the highest true severity wins:
"severities": [
{ "severity": "CRITICAL", "condition": { "key": "temperature", "op": ">", "value": 90 } },
{ "severity": "MAJOR", "condition": { "key": "temperature", "op": ">", "value": 80 } },
{ "severity": "WARNING", "condition": { "key": "temperature", "op": ">", "value": 70 } }
]
At 85 the alarm is Major; at 95 it escalates to Critical; back at 85 it returns to Major (after the clear debounce); below 70 (minus the hysteresis) it clears.
What a condition cannot do
| Not possible | Alternative |
|---|---|
| Compare two keys or two devices | A Rules-card formula, or a flow that writes a derived key |
| Use time of day or a calendar | A Rules-card rule with a time window |
| “Has not changed for 10 minutes” | A flow with a watchdog or delta block |
| Arithmetic in the threshold | A derived key, or a per-device attribute |
| Read a client attribute as threshold | Store the limit as a server or shared attribute |

