Skip to content

Commit

Permalink
feat(get-entities): Add device/area/floor/label conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
zachowj committed Aug 13, 2024
1 parent c0a4dca commit b194eb0
Show file tree
Hide file tree
Showing 27 changed files with 1,089 additions and 322 deletions.
58 changes: 47 additions & 11 deletions docs/guide/conditionals.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,71 @@ sidebarDepth: 2

## Rules

### `is` and `is not`
### `==`, `!=`, `is`, and `is not`

### `<` and `<=` and `>` and `>=`
These operators check if a value is equal to or different from the expected value using the [equality](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality) operator. The `is` and `is not` are aliases for `==` and `!=`.

### `<`, `<=`, `>`, and `>=`

These operators compare values to determine if one is less than, less than or equal to, greater than, or greater than or equal to another value.

### `in` and `not in`

The value is expected to be a comma-separated list of items.
These operators check if a value is within a specified list of items. The list should be comma-separated.

#### Example

```yaml
in: "on,off"
```
### `includes` and `not includes`

These operators check if an array includes or does not include a specified value.

### `is null` and `is not null`

These operators check if a value is [`null`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null) or not.

### `JSONata`

When this is selected as a rule it expects a boolean to be returned by the value.
This rule expects a boolean result from evaluating a value using JSONata, a powerful expression language. More information can be found [here](https://jsonata.org/), with examples specific to Home Assistant [here](./jsonata/).

### `starts with`

This operator checks if a value begins with the expected string.

### `in group`

This operator checks if a value is present in the `attributes.entity_id` property of a given group.

## Value Types

### `string` and `number` and `boolean` and `regular expression` and `JSONata expression`
### `string`, `number`, `boolean`, `regular expression`, and `JSONata expression`

Evaluates and returns the value as the selected type.
These types evaluate and return the value as the specified type.

### `msg` and `flow` and `global`
### `msg`, `flow`, and `global`

Reference to the different contexts of Node-RED.
These refer to the different [contexts](https://nodered.org/docs/user-guide/context) within Node-RED.

### `entity` and `prevEntity`

Reference the entity that triggered the node.
These refer to the entities that triggered the node.

- `entity` refers to the current state entity.
- `prevEntity` refers to the previous state entity.

#### Examples

To reference the current state of the entity:

```yaml
entity.: "state"
```

To reference the brightness attribute of the entity:

- `entity` is the current state entity
- `prevEntity` is the previous state entity
```yaml
entity.: "attributes.brightness"
```
140 changes: 85 additions & 55 deletions docs/node/get-entities.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,98 @@
# Get Entities

Get Entities based on search criteria
Retrieve entities based on specific search criteria, such as labels, floors, areas, devices, and entity properties. This node can output the entities in various formats, including an array, a count of the entities, a random entity, or as separate messages for each entity.

## Configuration

### Search Criteria

All search criteria have to be true for an entity to be valid.
All specified criteria must be met for an entity to be considered valid.

### Property

Has autocompleted with all properties currently set on all loaded entities.
Autocomplete is available for all properties currently set on loaded entities.

## Inputs

### payload
### Payload

- Type: `Object`
- **Type**: `Object`

Override config values by passing in a property with a valid value.
Override the configuration values by passing in a property with a valid value.

- rules array
- property string
- logic string
- value string
- valueType string
- outputType string
- outputEmptyResults boolean
- outputLocationType string
- outputLocation string
- outputResultscount number
- `rules` (array)
- `condition`: string (e.g., `stateObject`, `labelRegistry`, `areaRegistry`, `deviceRegistry`, `floorRegistry`)
- `property`: string
- `logic`: string
- `value`: string
- `valueType`: string
- `outputType`: string
- `outputEmptyResults`: boolean
- `outputLocationType`: string
- `outputLocation`: string
- `outputResultsCount`: number

#### Example

To retrieve all entities with the device class `battery` on the `First floor` that are not labeled `not replaceable`, include the following in the `payload` property of the message:

```json
{
"rules": [
{
"condition": "stateObject",
"property": "attributes.device_class",
"logic": "is",
"value": "battery",
"valueType": "str"
},
{
"condition": "floorRegistry",
"property": "name",
"logic": "is",
"value": "First floor",
"valueType": "str"
},
{
"condition": "labelRegistry",
"property": "name",
"logic": "is_not",
"value": "not replaceable",
"valueType": "str"
}
]
}
```

## Outputs

### Array

- Type: `Array`
- **Type**: `Array`

Sends an array of state objects from search criteria to the Output Location.
Returns an array of state objects based on the search criteria and sends them to the specified output location.

### Count

- Type: `number`
- **Type**: `number`

Return the total count of valid entities.
Returns the total count of valid entities.

### Random

- Type: `Object|Array`
- **Type**: `Object | Array`

Return a random object or array from the available state objects to the Output Location. When <code>One Max Results</code> is selected it will return an object and when more than one is selected will always return an array of state objects.
Returns a random object or array from the available state objects. If `One Max Results` is selected, it will return a single object; otherwise, it will return an array of state objects.

### Split

- Type: `msg` part
- **Type**: `msg.part`

Sends a message for each state object. In the same format as if the split node was used.
Sends a separate message for each state object, similar to the output of a split node.

## State Object Format

Sample output when the Output Type is an array:
Sample output when the output type is an array:

```json
[
Expand All @@ -86,45 +119,45 @@ Sample output when the Output Type is an array:

### entity_id

- Type: `string`
- **Type**: `string`

The entity to which this state belongs
The entity to which this state belongs.

### state

- Type: `string`
- **Type**: `string`

Main attribute state value, examples: 'on', 'off', 'home', 'open', 'closed', etc...
The main attribute state value, such as 'on', 'off', 'home', 'open', 'closed', etc.

### attributes

- Type: `Object`
- **Type**: `Object`

Supported attributes of state set by Home Assistant
Supported attributes of the state as set by Home Assistant.

### last_changed

- Type: `string`
- **Type**: `string`

ISO Date string of last time entity state changed
ISO Date string representing the last time the entity state changed.

### timeSinceChangedMs

- Type: `number`
- **Type**: `number`

Milliseconds since last time entity state changed
Milliseconds since the last state change of the entity.

### last_updated

- Type: `string`
- **Type**: `string`

ISO Date string of last time entity state was updated
ISO Date string representing the last time the entity state was updated.

### context

- Type: `Object`
- **Type**: `Object`

Information on who/what changed the state of this object last.
Information about who or what last changed the state of this entity.

## Examples

Expand All @@ -135,40 +168,37 @@ Information on who/what changed the state of this object last.
</InfoPanelOnly>

<DocsOnly>
### Example 1

#### Example 1

You have a presence detection of some sort running in Home Assistant and you want to get a notification when you leave if any doors or windows are left open.

Using the `get entities` node here to get a possible list of entity ids [binary_sensor.front_door, binary_sensor.back_door, binary_sensor.front_window, binary_sensor.back_window] if their state is equal to `open`. The entities are returned with the output `Split`. This means that a message is sent for each valid entity. We then are using a template node to format the payload into the entity friendly name and joining them back into one payload using the `join` node.
Get a notification when you leave if any doors or windows are left open. Use the `get entities` node to get a list of entity IDs (e.g., `binary_sensor.front_door`, `binary_sensor.back_door`, `binary_sensor.front_window`, `binary_sensor.back_window`) where the state is `open`. The entities are returned with the output `Split`, sending a message for each valid entity. A template node can then format the payload into the entity's friendly name, and the messages can be joined back into one payload using the `join` node.

![screenshot](./images/get-entities_03.png)

@[code](@examples/node/get-entities/example_01.json)

#### Example 2

Sort of a Vacation or Away script to randomly turn on some lights around your home.
### Example 2

Using an `inject` node here but you could use your preference of timer node. The `get entities` node is randomly choosing one entity from the criteria where `entity_id starts with light.`.
Create a vacation or away script to randomly turn on lights around your home. Use an `inject` node (or your preferred timer node) and the `get entities` node to randomly choose an entity from the criteria where `entity_id` starts with `light.`.

![screenshot](./images/get-entities_02.png)

@[code](@examples/node/get-entities/example_02.json)

---

#### Example 3

On Reddit the other day a user posted this [How can I join 1 to 4 pre-defined messages together based on 4 separate entity states?](https://www.reddit.com/r/homeassistant/comments/a628cw/nodered_how_can_i_join_1_to_4_predefined_messages/) (Their solution can be found in the post)
### Example 3

Here's my take on it using the `get entities` and a `function` node. Using the `Array` output option here.
Join pre-defined messages based on separate entity states. Use the `get entities` and `function` nodes with the `Array` output option.

![screenshot](./images/get-entities_01.png)

@[code](@examples/node/get-entities/example_03.json)

Disclaimer: All these examples are untested but should give you a general idea of how to use it.
### Example 4

Get all lights in an area named "Kitchen" that are off.

![screenshot](./images/get-entities_04.png)

@[code](@examples/node/get-entities/example_04.json)

</DocsOnly>

Expand Down
Binary file added docs/node/images/get-entities_04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/node/get-entities/example_01.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"id":"dafa6c83.24e07","type":"trigger-state","z":"7f24e6b8.eab548","name":"Left Home","entityid":"device_tracker.jason","entityidfiltertype":"exact","debugenabled":false,"constraints":[{"id":"9i2c9sz7d3e","targetType":"this_entity","targetValue":"","propertyType":"previous_state","propertyValue":"old_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"home"},{"id":"3h3lghs8xsm","targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"not_home"}],"constraintsmustmatch":"all","outputs":2,"customoutputs":[],"outputinitially":false,"state_type":"str","x":230,"y":192,"wires":[["42dd6dce.c48ce4"],[]]},{"id":"42dd6dce.c48ce4","type":"ha-get-entities","z":"7f24e6b8.eab548","name":"","rules":[{"property":"entity_id","logic":"includes","value":"binary_sensor.front_door,binary_sensor.back_door,binary_sensor.front_window,binary_sensor.back_window","valueType":"str"},{"property":"state","logic":"is","value":"open","valueType":"str"}],"output_type":"split","output_empty_results":false,"output_location_type":"msg","output_location":"payload","output_results_count":1,"x":396,"y":192,"wires":[["301b2b51.48b104"]]},{"id":"301b2b51.48b104","type":"template","z":"7f24e6b8.eab548","name":"Format Friendly Name","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{payload.attributes.friendly_name}}","output":"str","x":600,"y":192,"wires":[["8ed935f8.afbad8"]]},{"id":"8ed935f8.afbad8","type":"join","z":"7f24e6b8.eab548","name":"","mode":"custom","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":",","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":770,"y":192,"wires":[["454131f7.c19f"]]},{"id":"454131f7.c19f","type":"api-call-service","z":"7f24e6b8.eab548","name":"Notify","version":1,"debugenabled":false,"service_domain":"notify","service":"push_jason","entityId":"","data":"{\"message\": \"The {{payload}} are open.\",\"title\": \"Left Open\"}","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":898,"y":192,"wires":[[]]}]
[{"id":"4ad3d88d8a2357d5","type":"trigger-state","z":"b9bff4863336dc6b","name":"Left Home","server":"","version":5,"inputs":1,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["device_tracker.jason"],"substring":[],"regex":[]},"debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"previous_state","propertyValue":"old_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"home"},{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"not_home"}],"customOutputs":[],"outputInitially":false,"stateType":"str","enableInput":true,"x":284,"y":1248,"wires":[["d115adfcfa168310"],[]]},{"id":"d115adfcfa168310","type":"ha-get-entities","z":"b9bff4863336dc6b","name":"","server":"","version":1,"rules":[{"condition":"stateObject","property":"entity_id","logic":"includes","value":"binary_sensor.front_door,binary_sensor.back_door,binary_sensor.front_window,binary_sensor.back_window","valueType":"str"},{"condition":"stateObject","property":"state","logic":"is","value":"open","valueType":"str"}],"outputType":"split","outputEmptyResults":false,"outputLocationType":"msg","outputLocation":"payload","outputResultsCount":1,"x":450,"y":1248,"wires":[["f7e4e11c30fe53bd"]]},{"id":"f7e4e11c30fe53bd","type":"template","z":"b9bff4863336dc6b","name":"Format Friendly Name","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{payload.attributes.friendly_name}}","output":"str","x":654,"y":1248,"wires":[["6932846cb5c86e6f"]]},{"id":"6932846cb5c86e6f","type":"join","z":"b9bff4863336dc6b","name":"","mode":"custom","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":",","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":824,"y":1248,"wires":[["601e97da25388235"]]},{"id":"601e97da25388235","type":"api-call-service","z":"b9bff4863336dc6b","name":"Notify","server":"","version":6,"debugenabled":false,"action":"notify.push_jason","floorId":[],"areaId":[],"deviceId":[],"entityId":[],"labelId":[],"data":"{\"message\": \"The {{payload}} are open.\",\"title\": \"Left Open\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"data"}],"queue":"none","domain":"notify","service":"push_jason","output_location":"payload","output_location_type":"msg","x":952,"y":1248,"wires":[[]]}]
2 changes: 1 addition & 1 deletion examples/node/get-entities/example_02.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"id":"39342a95.8db3d6","type":"inject","z":"c86782e1.cf429","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":252,"y":256,"wires":[["e9038030.b79e4"]]},{"id":"e9038030.b79e4","type":"ha-get-entities","z":"c86782e1.cf429","name":"","rules":[{"property":"entity_id","logic":"starts_with","value":"light.","valueType":"str"}],"output_type":"random","output_empty_results":false,"output_location_type":"msg","output_location":"payload","output_results_count":1,"x":402,"y":256,"wires":[["2586f129.6136fe"]]},{"id":"58c67712.8aa4a8","type":"delay","z":"c86782e1.cf429","name":"","pauseType":"random","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"20","randomLast":"30","randomUnits":"minutes","drop":false,"x":732,"y":256,"wires":[["986369ca.af3f48"]]},{"id":"2586f129.6136fe","type":"api-call-service","z":"c86782e1.cf429","name":"Turn on Light","version":1,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"{{payload.entity_id}}","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":562,"y":256,"wires":[["58c67712.8aa4a8"]]},{"id":"986369ca.af3f48","type":"api-call-service","z":"c86782e1.cf429","name":"Turn off Light","version":"1","debugenabled":false,"service_domain":"light","service":"turn_off","entityId":"{{payload.entity_id}}","data":"","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":898,"y":256,"wires":[[]]}]
[{"id":"39342a95.8db3d6","type":"inject","z":"b9bff4863336dc6b","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":300,"y":1360,"wires":[["e9038030.b79e4"]]},{"id":"e9038030.b79e4","type":"ha-get-entities","z":"b9bff4863336dc6b","name":"","server":"","version":1,"rules":[{"condition":"stateObject","property":"entity_id","logic":"starts_with","value":"light.","valueType":"str"}],"outputType":"random","outputEmptyResults":false,"outputLocationType":"msg","outputLocation":"payload","outputResultsCount":1,"x":450,"y":1360,"wires":[["2586f129.6136fe"]]},{"id":"58c67712.8aa4a8","type":"delay","z":"b9bff4863336dc6b","name":"","pauseType":"random","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"20","randomLast":"30","randomUnits":"minutes","drop":false,"outputs":1,"x":780,"y":1360,"wires":[["986369ca.af3f48"]]},{"id":"2586f129.6136fe","type":"api-call-service","z":"b9bff4863336dc6b","name":"Turn on Light","server":"","version":6,"debugenabled":false,"action":"light.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["{{payload.entity_id}}"],"labelId":[],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","domain":"light","service":"turn_on","output_location":"","output_location_type":"none","x":610,"y":1360,"wires":[["58c67712.8aa4a8"]]},{"id":"986369ca.af3f48","type":"api-call-service","z":"b9bff4863336dc6b","name":"Turn off Light","server":"","version":6,"debugenabled":false,"action":"light.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["{{payload.entity_id}}"],"labelId":[],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"data"}],"queue":"none","domain":"light","service":"turn_off","output_location":"payload","output_location_type":"msg","x":946,"y":1360,"wires":[[]]}]
Loading

0 comments on commit b194eb0

Please sign in to comment.