# Condition and Rule Reference
This document describes the condition and rule system used by open.WARE transformations, particularly the `MapTransformer` and the alarm system. The system is based on the open.WARE alarm/monitoring condition framework.
## Table of Contents
- [Overview](#overview)
- [Condition Structure](#condition-structure)
- [Rule Types](#rule-types)
- [Numeric Rules](#numeric-rules)
- [String Rules](#string-rules)
- [Boolean Rules](#boolean-rules)
- [Timestamp Rules](#timestamp-rules)
- [Reference Value Rules](#reference-value-rules)
- [Compound Conditions](#compound-conditions)
- [Complete Examples](#complete-examples)
## Overview
The condition system evaluates sensor data values against configurable rules. Each condition references:
- A **data source** and **sensor ID** to evaluate
- A **dimension** within that sensor's multi-dimensional data
- A **rule** that defines the match criteria
Conditions can be simple (single rule) or compound (combining multiple conditions with AND/OR logic).
## Condition Structure
### Simple Condition (Rule Type)
A simple condition evaluates a single rule against a specific sensor dimension:
```json
{
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {
"type": "number_gt",
"value": 25
}
}
```
**Alternative field names** (for backward compatibility):
- `source` can also be `item_source`
- `id` can also be `item_id`
- `dimension` can also be `item_dimension`
- `trigger` can also be `rule`
### Compound Condition (AND/OR Type)
Compound conditions combine multiple child conditions using logical operators:
```json
{
"type": "and",
"children": [
{
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {"type": "number_gt", "value": 25}
},
{
"type": "rule",
"source": "humidity",
"id": "sensor_002",
"dimension": 0,
"trigger": {"type": "number_lt", "value": 60}
}
]
}
```
**Valid compound types**:
- `"and"`: All child conditions must be true
- `"or"`: At least one child condition must be true
## Rule Types
Rules define the actual matching criteria. The `type` field determines which rule applies and which additional parameters are required.
### Numeric Rules
These rules operate on Number-type dimensions.
#### Always Trigger
- **Type**: `"always"`, `"always_*"` (any type ending with `_always`)
- **Parameters**: None
- **Behavior**: Always returns true (useful for testing or unconditional mappings)
```json
{"type": "always"}
```
#### Value Change
- **Type**: `"number_change"`
- **Parameters**: None
- **Behavior**: Triggers when value differs from the previous value
```json
{"type": "number_change"}
```
#### Value Increased
- **Type**: `"number_increased"`
- **Parameters**: None
- **Behavior**: Triggers when value is greater than the previous value
```json
{"type": "number_increased"}
```
#### Value Increased By Amount
- **Type**: `"number_increased_by"`
- **Parameters**:
- `value` (number): Minimum increase amount
- **Behavior**: Triggers when value increased by at least the specified amount
```json
{
"type": "number_increased_by",
"value": 5
}
```
#### Value Decreased
- **Type**: `"number_decreased"`
- **Parameters**: None
- **Behavior**: Triggers when value is less than the previous value
```json
{"type": "number_decreased"}
```
#### Value Decreased By Amount
- **Type**: `"number_decreased_by"`
- **Parameters**:
- `value` (number): Minimum decrease amount
- **Behavior**: Triggers when value decreased by at least the specified amount
```json
{
"type": "number_decreased_by",
"value": 5
}
```
#### Equals Value
- **Type**: `"number_equals"`
- **Parameters**:
- `value` (number): Target value
- **Behavior**: Triggers when value exactly equals the specified value
```json
{
"type": "number_equals",
"value": 100
}
```
#### Not Equals Value
- **Type**: `"number_equals_not"`
- **Parameters**:
- `value` (number): Target value
- **Behavior**: Triggers when value does not equal the specified value
```json
{
"type": "number_equals_not",
"value": 0
}
```
#### In Range
- **Type**: `"number_in_range"`
- **Parameters**:
- `min` (number): Minimum value (exclusive)
- `max` (number): Maximum value (exclusive)
- **Behavior**: Triggers when `min < value < max`
```json
{
"type": "number_in_range",
"min": 10,
"max": 25
}
```
#### Out of Range
- **Type**: `"number_out_of_range"`, `"min-max"`
- **Parameters**:
- `min` (number): Minimum threshold
- `max` (number): Maximum threshold
- **Behavior**: Triggers when `value < min` OR `value > max`
```json
{
"type": "number_out_of_range",
"min": 10,
"max": 90
}
```
#### Less Than
- **Type**: `"number_lt"`, `"min"`
- **Parameters**:
- `value` (number): Threshold
- **Behavior**: Triggers when `value < threshold`
```json
{
"type": "number_lt",
"value": 10
}
```
#### Greater Than
- **Type**: `"number_gt"`, `"max"`
- **Parameters**:
- `value` (number): Threshold
- **Behavior**: Triggers when `value > threshold`
```json
{
"type": "number_gt",
"value": 50
}
```
#### Less Than or Equal
- **Type**: `"number_lte"`
- **Parameters**:
- `value` (number): Threshold
- **Behavior**: Triggers when `value ≤ threshold`
```json
{
"type": "number_lte",
"value": 100
}
```
#### Greater Than or Equal
- **Type**: `"number_gte"`
- **Parameters**:
- `value` (number): Threshold
- **Behavior**: Triggers when `value ≥ threshold`
```json
{
"type": "number_gte",
"value": 0
}
```
### String Rules
These rules operate on String-type dimensions. Most rules support both underscore and hyphen variants (e.g., `string_equals` and `string-equals`).
#### String Change
- **Type**: `"string_change"`, `"string-change"`
- **Parameters**: None
- **Behavior**: Triggers when value differs from the previous value
```json
{"type": "string_change"}
```
#### String Equals
- **Type**: `"string_equals"`, `"string-equals"`
- **Parameters**:
- `string` (string): Target string
- **Behavior**: Triggers when value exactly matches the target string
```json
{
"type": "string_equals",
"string": "active"
}
```
#### String Not Equals
- **Type**: `"string_equals_not"`, `"string-equals-not"`
- **Parameters**:
- `string` (string): Target string
- **Behavior**: Triggers when value does not match the target string
```json
{
"type": "string_equals_not",
"string": "error"
}
```
#### String Includes
- **Type**: `"string_includes"`, `"string-includes"`
- **Parameters**:
- `string` (string): Substring to search for
- **Behavior**: Triggers when value contains the specified substring
```json
{
"type": "string_includes",
"string": "warning"
}
```
#### String Not Includes
- **Type**: `"string_includes_not"`, `"string-includes-not"`
- **Parameters**:
- `string` (string): Substring to search for
- **Behavior**: Triggers when value does not contain the specified substring
```json
{
"type": "string_includes_not",
"string": "error"
}
```
#### String Starts With
- **Type**: `"string_starts_with"`, `"string-starts-with"`
- **Parameters**:
- `string` (string): Prefix to match
- **Behavior**: Triggers when value starts with the specified prefix
```json
{
"type": "string_starts_with",
"string": "HTTP_"
}
```
#### String Not Starts With
- **Type**: `"string_starts_with_not"`, `"string-starts-with-not"`
- **Parameters**:
- `string` (string): Prefix to check
- **Behavior**: Triggers when value does not start with the specified prefix
```json
{
"type": "string_starts_with_not",
"string": "ERR_"
}
```
#### String Ends With
- **Type**: `"string_ends_with"`, `"string-ends-with"`
- **Parameters**:
- `string` (string): Suffix to match
- **Behavior**: Triggers when value ends with the specified suffix
```json
{
"type": "string_ends_with",
"string": ".txt"
}
```
#### String Not Ends With
- **Type**: `"string_ends_with_not"`, `"string-ends-with-not"`
- **Parameters**:
- `string` (string): Suffix to check
- **Behavior**: Triggers when value does not end with the specified suffix
```json
{
"type": "string_ends_with_not",
"string": ".tmp"
}
```
### Boolean Rules
These rules operate on Boolean-type dimensions.
#### Boolean True
- **Type**: `"boolean_true"`
- **Parameters**: None
- **Behavior**: Triggers when value is `true`
```json
{"type": "boolean_true"}
```
#### Boolean False
- **Type**: `"boolean_false"`
- **Parameters**: None
- **Behavior**: Triggers when value is `false`
```json
{"type": "boolean_false"}
```
#### Boolean Rising Edge
- **Type**: `"boolean_rising_edge"`, `"boolean-rising-edge"`
- **Parameters**: None
- **Behavior**: Triggers on transition from `false` to `true`
```json
{"type": "boolean_rising_edge"}
```
#### Boolean Falling Edge
- **Type**: `"boolean_falling_edge"`, `"boolean-falling-edge"`
- **Parameters**: None
- **Behavior**: Triggers on transition from `true` to `false`
```json
{"type": "boolean_falling_edge"}
```
#### Boolean Change (Any Edge)
- **Type**: `"boolean_change"`, `"boolean-rising-falling-edge"`
- **Parameters**: None
- **Behavior**: Triggers on any transition (`true` ↔ `false`)
```json
{"type": "boolean_change"}
```
### Timestamp Rules
These rules evaluate based on the timestamp of the data point rather than its value.
#### Time Since Last Value Changed
- **Type**: `"ts_last_value_changed"`
- **Parameters**:
- `time.value` (number): Time threshold in milliseconds
- **Behavior**: Triggers when more than the specified time has elapsed since the dimension value last changed
```json
{
"type": "ts_last_value_changed",
"time": {
"value": 300000
}
}
```
*Triggers if value hasn't changed in 5 minutes (300,000 ms)*
#### Time Since Last Value Received
- **Type**: `"ts_last_value_received"`
- **Parameters**:
- `time.value` (number): Time threshold in milliseconds
- **Behavior**: Triggers when more than the specified time has elapsed since any value was received for the sensor
```json
{
"type": "ts_last_value_received",
"time": {
"value": 600000
}
}
```
*Triggers if no data received in 10 minutes*
#### Minute of Day
- **Type**: `"ts_minute_of_day"`
- **Parameters**:
- `time.minuteMin` (integer): Start minute of day (0-1439)
- `time.minuteMax` (integer): End minute of day (0-1439)
- `time.tz` (string, optional): Timezone (default: system timezone)
- **Behavior**: Triggers when the data point's timestamp falls within the specified minute range
```json
{
"type": "ts_minute_of_day",
"time": {
"minuteMin": 540,
"minuteMax": 1020,
"tz": "Europe/Berlin"
}
}
```
*Triggers between 9:00 AM (540 minutes) and 5:00 PM (1020 minutes)*
#### Day of Week
- **Type**: `"ts_day_of_week"`
- **Parameters**:
- `time.dateMin` (integer): Start day (1=Monday, 7=Sunday)
- `time.dateMax` (integer): End day (1=Monday, 7=Sunday)
- `time.tz` (string, optional): Timezone
- **Behavior**: Triggers when the data point's day of week is within the range
```json
{
"type": "ts_day_of_week",
"time": {
"dateMin": 1,
"dateMax": 5,
"tz": "Europe/Berlin"
}
}
```
*Triggers Monday through Friday*
#### Day of Month
- **Type**: `"ts_day_of_month"`
- **Parameters**:
- `time.dateMin` (integer): Start day (1-31)
- `time.dateMax` (integer): End day (1-31)
- `time.tz` (string, optional): Timezone
- **Behavior**: Triggers when the data point's day of month is within the range
```json
{
"type": "ts_day_of_month",
"time": {
"dateMin": 1,
"dateMax": 10,
"tz": "Europe/Berlin"
}
}
```
*Triggers on the first 10 days of each month*
#### Day of Year
- **Type**: `"ts_day_of_year"`
- **Parameters**:
- `time.dateMin` (integer): Start day (1-366)
- `time.dateMax` (integer): End day (1-366)
- `time.tz` (string, optional): Timezone
- **Behavior**: Triggers when the data point's day of year is within the range
```json
{
"type": "ts_day_of_year",
"time": {
"dateMin": 172,
"dateMax": 264,
"tz": "Europe/Berlin"
}
}
```
*Triggers during summer months (roughly June 21 to September 21)*
#### Combined Time and Day Filters
The following rule types combine minute-of-day with day filters:
- **`"ts_minute_day_of_week"`**: Minute range AND day of week range
- **`"ts_minute_day_of_month"`**: Minute range AND day of month range
- **`"ts_minute_day_of_year"`**: Minute range AND day of year range
```json
{
"type": "ts_minute_day_of_week",
"time": {
"minuteMin": 540,
"minuteMax": 1020,
"dateMin": 1,
"dateMax": 5,
"tz": "Europe/Berlin"
}
}
```
*Triggers during business hours (9 AM - 5 PM) on weekdays*
## Reference Value Rules
Some rule types support comparison against a **reference value** instead of a static value. The reference value is passed from the context (e.g., in MapTransformer, it's the current value being mapped).
### Numeric Reference Rules
- **`number_gt_ref`**: Value greater than reference
- **`number_lt_ref`**: Value less than reference
- **`number_equals_ref`**: Value equals reference
- **`number_equals_not_ref`**: Value not equals reference
```json
{
"type": "number_gt_ref"
}
```
### Boolean Reference Rules
- **`boolean_equals_ref`**: Boolean value equals reference
- **`boolean_equals_not_ref`**: Boolean value not equals reference
```json
{
"type": "boolean_equals_ref"
}
```
### String Reference Rules
All string rules can have `_ref` appended to compare against the reference value instead of using the `string` parameter:
- `string_equals_ref`
- `string_equals_not_ref`
- `string_includes_ref`
- etc.
## Compound Conditions
You can build complex logic by combining conditions with AND/OR operators:
### AND Condition
All child conditions must be true:
```json
{
"type": "and",
"children": [
{
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {"type": "number_gt", "value": 20}
},
{
"type": "rule",
"source": "humidity",
"id": "sensor_002",
"dimension": 0,
"trigger": {"type": "number_lt", "value": 70}
}
]
}
```
### OR Condition
At least one child condition must be true:
```json
{
"type": "or",
"children": [
{
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {"type": "number_gt", "value": 30}
},
{
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {"type": "number_lt", "value": 10}
}
]
}
```
### Nested Compound Conditions
You can nest AND/OR conditions for complex logic:
```json
{
"type": "and",
"children": [
{
"type": "rule",
"source": "power",
"id": "device_001",
"dimension": 0,
"trigger": {"type": "boolean_true"}
},
{
"type": "or",
"children": [
{
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {"type": "number_gt", "value": 30}
},
{
"type": "rule",
"source": "load",
"id": "meter_001",
"dimension": 0,
"trigger": {"type": "number_gt", "value": 1000}
}
]
}
]
}
```
*Device is powered on AND (temperature > 30 OR load > 1000)*
## Complete Examples
### Example 1: Temperature Category Mapping
Map temperature values to category strings:
```json
{
"dimension": 0,
"mappings": [
{
"condition": {
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {
"type": "number_lt",
"value": 10
}
},
"mapping": "cold"
},
{
"condition": {
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {
"type": "number_in_range",
"min": 10,
"max": 25
}
},
"mapping": "comfortable"
},
{
"condition": {
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {
"type": "number_gte",
"value": 25
}
},
"mapping": "hot"
}
],
"defaultMapping": "unknown",
"valueType": {
"name": "temperature_category",
"unit": "",
"type": "String"
}
}
```
### Example 2: Operating Mode Based on Multiple Sensors
Map to operating mode based on temperature AND humidity:
```json
{
"dimension": 0,
"mappings": [
{
"condition": {
"type": "and",
"children": [
{
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {"type": "number_gt", "value": 25}
},
{
"type": "rule",
"source": "humidity",
"id": "sensor_002",
"dimension": 0,
"trigger": {"type": "number_gt", "value": 60}
}
]
},
"mapping": "cooling"
},
{
"condition": {
"type": "and",
"children": [
{
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {"type": "number_lt", "value": 18}
},
{
"type": "rule",
"source": "humidity",
"id": "sensor_002",
"dimension": 0,
"trigger": {"type": "number_lt", "value": 40}
}
]
},
"mapping": "heating"
}
],
"defaultMapping": "idle",
"valueType": {
"name": "hvac_mode",
"unit": "",
"type": "String"
}
}
```
### Example 3: Business Hours Detection
Map boolean values based on time of day and day of week:
```json
{
"dimension": 0,
"mappings": [
{
"condition": {
"type": "rule",
"source": "clock",
"id": "system_time",
"dimension": 0,
"trigger": {
"type": "ts_minute_day_of_week",
"time": {
"minuteMin": 540,
"minuteMax": 1020,
"dateMin": 1,
"dateMax": 5,
"tz": "Europe/Berlin"
}
}
},
"mapping": true
}
],
"defaultMapping": false,
"valueType": {
"name": "business_hours",
"unit": "",
"type": "Boolean"
}
}
```
### Example 4: Alert Level Based on Value Changes
Map to alert levels based on rate of change:
```json
{
"dimension": 0,
"mappings": [
{
"condition": {
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {
"type": "number_increased_by",
"value": 10
}
},
"mapping": "critical"
},
{
"condition": {
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {
"type": "number_increased_by",
"value": 5
}
},
"mapping": "warning"
},
{
"condition": {
"type": "rule",
"source": "temperature",
"id": "sensor_001",
"dimension": 0,
"trigger": {
"type": "number_change"}
},
"mapping": "normal"
}
],
"defaultMapping": "stable",
"valueType": {
"name": "alert_level",
"unit": "",
"type": "String"
}
}
```
## Usage in MapTransformer
When using conditions in the MapTransformer:
1. **The condition always evaluates against the data item being transformed**
2. Each mapping's condition is evaluated in order
3. The first matching condition determines the mapping value
4. If no conditions match, the `defaultMapping` is used
5. The `source` and `id` in the condition should typically match the data being transformed, but can reference other sensors for cross-sensor logic
**Important**: The user executing the transformation must have read access to all sensors referenced in the conditions, otherwise an `IllegalAccessError` will be thrown.