Skip to content

Commit

Permalink
[basicprofiles] Add additional comparisons to State Filter profile (#…
Browse files Browse the repository at this point in the history
…17323)

* Add inequality comparisons to State Filter profile

- Fix bug where undefined `mismatchState` passed `UNDEF` instead of ignoring state updates
- Support multiline conditions
- Support comparing against the input state from handler to filter out
unwanted data

* Support comparing item to item or input to item

Signed-off-by: Jimmy Tanagra <[email protected]>
  • Loading branch information
jimtng authored Aug 28, 2024
1 parent c7a2026 commit e4ce954
Show file tree
Hide file tree
Showing 7 changed files with 806 additions and 152 deletions.
117 changes: 83 additions & 34 deletions bundles/org.openhab.transform.basicprofiles/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ This bundle provides a list of useful Profiles.
This Profile can be used to send a Command towards the Item when one event of a specified event list is triggered.
The given Command value is parsed either to `IncreaseDecreaseType`, `NextPreviousType`, `OnOffType`, `PlayPauseType`, `RewindFastforwardType`, `StopMoveType`, `UpDownType` or a `StringType` is used.

### Configuration
### Generic Command Profile Configuration

| Configuration Parameter | Type | Description |
|-------------------------|------|----------------------------------------------------------------------------------|
| `events` | text | Comma separated list of events to which the profile should listen. **mandatory** |
| `command` | text | Command which should be sent if the event is triggered. **mandatory** |

### Full Example
### Generic Command Profile Example

```java
Switch lightsStatus {
Expand All @@ -27,13 +27,13 @@ Switch lightsStatus {

The Generic Toggle Switch Profile is a specialization of the Generic Command Profile and toggles the State of a Switch Item whenever one of the specified events is triggered.

### Configuration
### Generic Toggle Switch Profile Configuration

| Configuration Parameter | Type | Description |
|-------------------------|------|----------------------------------------------------------------------------------|
| `events` | text | Comma separated list of events to which the profile should listen. **mandatory** |

### Full Example
### Generic Toggle Switch Profile Example

```java
Switch lightsStatus {
Expand All @@ -47,13 +47,13 @@ Switch lightsStatus {
This Profile counts and skips a user-defined number of State changes before it sends an update to the Item.
It can be used to debounce Item States.

### Configuration
### Debounce (Counting) Profile Configuration

| Configuration Parameter | Type | Description |
|-------------------------|---------|-----------------------------------------------|
| `numberOfChanges` | integer | Number of changes before updating Item State. |

### Full Example
### Debounce (Counting) Profile Example

```java
Switch debouncedSwitch { channel="xxx" [profile="basic-profiles:debounce-counting", numberOfChanges=2] }
Expand All @@ -66,15 +66,15 @@ In `FIRST` mode this profile discards values for the configured time after a val

It can be used to debounce Item States/Commands or prevent excessive load on networks.

### Configuration
### Debounce (Time) Profile Configuration

| Configuration Parameter | Type | Description |
|-------------------------|---------|-----------------------------------------------|
| `toItemDelay` | integer | Timespan in ms before a received value is send to the item. |
| `toHandlerDelay` | integer | Timespan in ms before a received command is passed to the handler. |
| `mode` | text | `FIRST` (sends the first value received and discards later values), `LAST` (sends the last value received, discarding earlier values). |

### Full Example
### Debounce (Time) Profile Example

```java
Number:Temperature debouncedSetpoint { channel="xxx" [profile="basic-profiles:debounce-time", toHandlerDelay=1000] }
Expand All @@ -87,7 +87,7 @@ It requires no specific configuration.

The values of `QuantityType`, `PercentType` and `DecimalTypes` are negated (multiplied by `-1`).
Otherwise the following mapping is used:

`IncreaseDecreaseType`: `INCREASE` <-> `DECREASE`
`NextPreviousType`: `NEXT` <-> `PREVIOUS`
`OnOffType`: `ON` <-> `OFF`
Expand All @@ -97,7 +97,7 @@ Otherwise the following mapping is used:
`StopMoveType`: `MOVE` <-> `STOP`
`UpDownType`: `UP` <-> `DOWN`

### Full Example
### Invert / Negate Profile Example

```java
Switch invertedSwitch { channel="xxx" [profile="basic-profiles:invert"] }
Expand All @@ -109,14 +109,14 @@ The Round Profile scales the State to a specific number of decimal places based
Optionally the [Rounding mode](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/RoundingMode.html) can be set.
Source Channels should accept Item Type `Number`.

### Configuration
### Round Profile Configuration

| Configuration Parameter | Type | Description |
|-------------------------|---------|-----------------------------------------------------------------------------------------------------------------|
| `scale` | integer | Scale to indicate the resulting number of decimal places (min: -16, max: 16, STEP: 1) **mandatory**. |
| `mode` | text | Rounding mode to be used (e.g. "UP", "DOWN", "CEILING", "FLOOR", "HALF_UP" or "HALF_DOWN" (default: "HALF_UP"). |

### Full Example
### Round Profile Example

```java
Number roundedNumber { channel="xxx" [profile="basic-profiles:round", scale=0] }
Expand All @@ -133,13 +133,13 @@ Source Channels should accept Item Type `Dimmer` or `Number`.
This profile is a shortcut for the System Hysteresis Profile.
:::

### Configuration
### Threshold Profile Configuration

| Configuration Parameter | Type | Description |
|-------------------------|---------|-----------------------------------------------------------------------------------------------------|
| `threshold` | integer | Triggers `ON` if value is below the given threshold, otherwise OFF (default: 10, min: 0, max: 100). |

### Full Example
### Threshold Profile Example

```java
Switch thresholdItem { channel="xxx" [profile="basic-profiles:threshold", threshold=15] }
Expand All @@ -152,7 +152,7 @@ The value of the percent type can be different between a specific time of the da
A possible use-case is switching lights (using a presence detector) with different intensities at day and at night.
Be aware: a range beyond midnight (e.g. start="23:00", end="01:00") is not yet supported.

### Configuration
### Time Range Profile Configuration

| Configuration Parameter | Type | Description |
|-------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------|
Expand All @@ -169,7 +169,7 @@ Possible values for parameter `restoreValue`:
- `PREVIOUS` - Return to previous value
- `0` - `100` - Set a user-defined percent value

### Full Example
### Time Range Profile Example

```java
Switch motionSensorFirstFloor {
Expand All @@ -180,31 +180,80 @@ Switch motionSensorFirstFloor {

## State Filter Profile

This filter passes on state updates from a (binding) handler to the item if and only if all listed item state conditions
are met (conditions are ANDed together).
Option to instead pass different state update in case the conditions are not met.
State values may be quoted to treat as `StringType`.
This filter passes on state updates from the (binding) handler to the item if and only if all listed conditions are met (conditions are ANDed together).
In case the conditions are not met, a fixed predefined state can be passed to the item instead of ignoring the update.

Use cases:

- Ignore values from the binding unless some other item(s) have a specific state.
- Filter out invalid values from the binding.

### State Filter Configuration

| Configuration Parameter | Type | Description |
| ----------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `conditions` | text | A list of conditions to check before posting an update from the binding to the item. When all the conditions are met, the update from the binding is passed to the item. |
| `mismatchState` | text | What to pass to the item when `conditions` aren't met. Use single quotes to treat as `StringType`. When undefined (the default), updates from the binding are ignored. |
| `separator` | text | Optional separator string to separate multiple expressions. Defaults to `,`. |

#### State Filter Conditions

Use case: Ignore values from a binding unless some other item(s) have a specific state.
The conditions are defined in the format `[ITEM_NAME] OPERATOR VALUE_OR_ITEM_NAME`, e.g. `MyItem EQ OFF`.
Multiple conditions can be entered on separate lines in the UI, or in a single line separated with the `separator` character/string.

### Configuration
The state of one item can be compared against the state of another item by having item names on both sides of the comparison, e.g.: `Item1 > Item2`.
When `ITEM_NAME` is omitted, e.g. `> 10, < 100`, the comparisons are applied against the input data from the binding.
In this case, the value can also be replaced with an item name, which will result in comparing the input state against the state of that item, e.g. `> LowerLimitItem, < UpperLimitItem`.
This can be used to filter out unwanted data, e.g. to ensure that incoming data are within a reasonable range.

| Configuration Parameter | Type | Description |
|-------------------------|------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `conditions` | text | Comma separated list of expressions on the format `ITEM_NAME OPERATOR ITEM_STATE`, ie `MyItem EQ OFF`. Use quotes around `ITEM_STATE` to treat value as string ie `'OFF'` and not `OnOffType.OFF` |
| `mismatchState` | text | Optional state to pass instead if conditions are NOT met. Use single quotes to treat as `StringType`. Defaults to `UNDEF` |
| `separator` | text | Optional separator string to separate expressions when using multiple. Defaults to `,` |
Some tips:

Possible values for token `OPERATOR` in `conditions`:
- When dealing with QuantityType data, the unit must be included in the comparison value, e.g.: `PowerItem > 1 kW`.
- Use single quotes around the `VALUE` to perform a string comparison, e.g. `'UNDEF'` is not equal to `UNDEF` (of type `UnDefType`).
This will distinguish between a string literal and an item name or a constant such as `UNDEF`, `ON`/`OFF`, `OPEN`, etc.
- `VALUE` cannot be on the left hand side of the operator.

- `EQ` - Equals
- `NEQ` - Not equals
##### State Filter Operators

| Name | Symbol | |
| :---: | :----------: | ------------------------- |
| `EQ` | `==` | Equals |
| `NEQ` | `!=` or `<>` | Not equals |
| `GT` | `>` | Greater than |
| `GTE` | `>=` | Greater than or equals to |
| `LT` | `<` | Less than |
| `LTE` | `<=` | Less than or equals to |

### Full Example
Notes:

- The operator names must be surrounded by spaces, i.e.: `Item EQ 10`
- The operator symbols do not need to be surrounded by spaces, e.g.: `Item==10` and `Item == 10` are both fine.

### State Filter Examples

Condition based on the state of other items:

```java
Number:Temperature airconTemperature {
channel="mybinding:mything:mychannel" [ profile="basic-profiles:state-filter", conditions="airconPower_item EQ ON", mismatchState="UNDEF" ]
}
```

Check against the incoming state, to discard incoming data outside a fixed range:

```java
Number:Power PowerUsage {
channel="mybinding:mything:mychannel" [ profile="basic-profiles:state-filter", conditions=">= 0 kW", "< 20 kW" ]
}
```

The incoming state can be compared against other items:

```java
Number:Power MinimumPowerLimit { unit="W" }
Number:Power MaximumPowerLimit { unit="W" }

```Java
Number:Temperature airconTemperature{
channel="mybinding:mything:mychannel"[profile="basic-profiles:state-filter",conditions="airconPower_item EQ ON",mismatchState="UNDEF"]
Number:Power PowerUsage {
channel="mybinding:mything:mychannel" [ profile="basic-profiles:state-filter", conditions=">= MinimumPowerLimit", "< MaximumPowerLimit" ]
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
*/
package org.openhab.transform.basicprofiles.internal.config;

import java.util.List;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.types.UnDefType;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.transform.basicprofiles.internal.profiles.StateFilterProfile;

/**
Expand All @@ -24,9 +26,9 @@
@NonNullByDefault
public class StateFilterProfileConfig {

public String conditions = "";
public List<String> conditions = List.of();

public String mismatchState = UnDefType.UNDEF.toString();
public @Nullable String mismatchState;

public String separator = ",";
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public class BasicProfilesFactory implements ProfileFactory, ProfileTypeProvider
.withSupportedChannelTypeUIDs(DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_TYPE_UID_MOTION) //
.build();
private static final ProfileType PROFILE_STATE_FILTER = ProfileTypeBuilder
.newState(STATE_FILTER_UID, "Filter handler state updates based on any item state").build();
.newState(STATE_FILTER_UID, "State Filter").build();

private static final Set<ProfileTypeUID> SUPPORTED_PROFILE_TYPE_UIDS = Set.of(GENERIC_COMMAND_UID,
GENERIC_TOGGLE_SWITCH_UID, DEBOUNCE_COUNTING_UID, DEBOUNCE_TIME_UID, INVERT_UID, ROUND_UID, THRESHOLD_UID,
Expand Down
Loading

0 comments on commit e4ce954

Please sign in to comment.