# 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.