├── .gitignore ├── README.md ├── examples ├── auto_entities_card.yaml └── detailed_persistent_notification.yaml ├── images ├── entities_card_closed_example.png ├── entities_card_open_example.png └── group_more_info.png ├── package_unavailable_entities.yaml └── v1 └── package_unavailable_entities_v1.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .vscode 3 | .git 4 | unavailable-entities-sensor.code-workspace -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://hacs.xyz/) 2 | 3 | # Unavailable Entities Template Sensor 4 | ## What does this package do? 5 | This package creates a group of entities that have no value (a state of unknown or unavailable) and a sensor that provides a count of the entities in this group. 6 | 7 | The sensor can be used to help you discover entities which should to be disabled or deleted and help you identify misbehaving or failed devices. 8 | 9 | The structure for this sensor has changed from the original version. [You can read about the changes here.](https://github.com/jazzyisj/unavailable-entities-sensor/discussions/57) 10 | 11 | ### Requirements 12 | Home Assistant v2024.8 is the minimum version required to use this package. 13 | 14 | The [auto-entities](https://github.com/thomasloven/lovelace-auto-entities) and [fold-entity-row](https://github.com/thomasloven/lovelace-fold-entity-row) plugins is required to use the example lovelace card. 15 | 16 | [![auto-entities](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=thomasloven&repository=lovelace-auto-entities) 17 | 18 | The [fold-entity-row](https://github.com/thomasloven/lovelace-fold-entity-row) plugins is required to use the example lovelace card. 19 | 20 | [![auto-entities](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=thomasloven&repository=lovelace-fold-entity-row) 21 | 22 | ## Installation 23 | ### Install As Package 24 | The easiest way to use this sensor is to install it as a [package](https://www.home-assistant.io/docs/configuration/packages/). 25 | 26 | If you already have packages enabled in your configuration, download [`package_unavailable_entities.yaml`](https://github.com/jazzyisj/unavailable-entities-sensor/blob/main/package_unavailable_entities.yaml) to your packages directory. 27 | 28 | To enable packages in your configuation, create a folder in your config directory named `packages` and add the following line to your `configuration.yaml` file. 29 | 30 | homeassistant: 31 | packages: !include_dir_named packages 32 | 33 | ### Install Without Packages 34 | To create this sensor without using packages simply copy the relevant code to an appropriate place in your configuration.yaml file. The example notification automation is optional. 35 | 36 | **NOTE! You must reload automations, templates, and group entities after adding this package!** 37 | 38 | ## Excluding Entities 39 | You will likely have many devices and/or entities which you do not need to monitor or that often have states of unknown or unavailable. These devices and entities can be excluded from the sensor. Excluding a device excludes all of it's entities. 40 | 41 | Disabled devices and entities and devices and entities labeled `ignored` in the UI are excluded from the sensor by default. To use a different label name, change the `ignore_label` value in the group template to the desired value. The label is not case sensitive. This is the recommended way to exclude entities. 42 | 43 | ### Customizing The Template 44 | In addition to excluding entities using labels in the UI, entities can also be excluded with additional filters in the template, or by adding them to the `group.ignored_entities` definition. 45 | 46 | #### Ignore Domains 47 | 48 | Domains that are "stateless" (button, scene, event etc) or that don't make sense to monitor are excluded by default. To monitor these domains remove them from the ignored domains filter. 49 | 50 | Example - remove the `input_button` domain. 51 | 52 | |rejectattr('domain', 'in', ['button', 'event', 'group', 'input_text', 'scene']) 53 | 54 | If you wish to ignore additional domains you can also add them to the ignored domains filter. 55 | 56 | Example - add the `switch` domain. 57 | 58 | |rejectattr('domain', 'in', ['button', 'event', 'group', 'input_button', 'input_text', 'scene', 'switch']) 59 | 60 | #### Ignore Specific Entities 61 | 62 | To ignore specific entities, add them to the `group.ignored_unavailable_entities` declaration. 63 | 64 | #### Ignore Matching Entities 65 | 66 | **Search Test** 67 | 68 | If you have multiple entities to ignore that share a common uniquly identifiable portion of their entity_id name you can exclude them without having to add each individual sensor to the ingore_entities group by adding filters using a search test to the template. 69 | 70 | |rejectattr('entity_id', 'search', 'wifi_') 71 | 72 | Be as specific as possible in your filters so you don't exclude unintended entities! For example, if you have the following sensors in your configuration and just want to exclude just the wifi signal strengh sensors, rejecting a search of 'wifi_' will also exclude `binary_sensor.wifi_connected`. 73 | 74 | - binary_sensor.wifi_connected 75 | - sensor.wifi_downstairs 76 | - sensor.wifi_upstairs 77 | 78 | You can also use [regex pattern matching](https://regex101.com/) in a seach rejectattr (selectattr) filter. 79 | 80 | |rejectattr('entity_id', 'search', '(_alarm_volume|_next_alarm|_alarms)') 81 | 82 | The filter above effectively combines these three filters. 83 | 84 | |rejectattr('entity_id', 'search', '_alarm_volume') 85 | |rejectattr('entity_id', 'search', '_next_alarm') 86 | |rejectattr('entity_id', 'search', '_alarms') 87 | 88 | **Contains Test** 89 | 90 | The [contains](https://www.home-assistant.io/docs/configuration/templating/#contains) test can also be used if required. Regex matching is not available for the contains test. 91 | 92 | |rejectattr('entity_id', 'contains', 'wifi_') 93 | 94 | #### Excluding Specific Integrations 95 | 96 | You can exclude entities from a specific integration by using an `in` test for the entity_id and the [integration_entities() function](https://www.home-assistant.io/docs/configuration/templating/#integrations). 97 | 98 | |rejectattr('entity_id', 'in',integration_entities('hassio')) 99 | 100 | #### Excluding Specific Devices 101 | 102 | You can exclude entities from a specific integration by using an `in` test for the entity_id and the [device_entities() function](https://www.home-assistant.io/docs/configuration/templating/#devices). 103 | 104 | |rejectattr('entity_id', 'in',device_entities('fffe8e4c87c68ee60e0ae84c295676ce')) 105 | 106 | ### Full Example 107 | 108 | automation: 109 | ~~~ 110 | action: 111 | - action: group.set 112 | data: 113 | object_id: unavailable_entities 114 | entities: > 115 | {% set ignore_seconds = 60 %} 116 | {% set ignore_label = 'ignored' %} 117 | {% set ignored_domains = ['button', 'conversation', 'event', 'group', 'image', 118 | 'input_button', 'input_text', 'remote', 'tts', 'scene', 'stt', 'update'] %} 119 | {% set ignore_ts = (now().timestamp() - ignore_seconds)|as_datetime %} 120 | {% set disabled_device_entities = state_attr('sensor.disabled_device_entities', 'entities') 121 | | regex_replace(find='\[|\]|\{|\}|\'entity_id\':', replace='') %} 122 | {% set ignored_devices = label_devices(ignore_label | lower) %} 123 | {% set ignored_device_entities = namespace(value=[]) %} 124 | {% for device in ignored_devices %} 125 | {% set ignored_device_entities.value = ignored_device_entities.value + device_entities(device) %} 126 | {% endfor %} 127 | {{ states 128 | | rejectattr('domain', 'in', ignored_domains) 129 | | rejectattr('entity_id', 'in', disabled_device_entities) 130 | | rejectattr('entity_id', 'in', state_attr('group.ignored_entities', 'entity_id')) 131 | | rejectattr('entity_id', 'in', ['group.unavailable_entities', 'group.ignored_entities']) 132 | | rejectattr('entity_id', 'in', ignored_device_entities.value) 133 | | rejectattr('entity_id', 'in', label_entities(ignore_label | lower)) 134 | | rejectattr('last_changed', 'ge', ignore_ts) 135 | | rejectattr('entity_id', 'search', 'browser_') 136 | | rejectattr('entity_id', 'search', '_alarm_volume|_next_alarm|_alarms') 137 | | rejectattr('entity_id', 'contains', '_memory_percent') 138 | | rejectattr('entity_id', 'in', integration_entities('hassio')) 139 | | rejectattr('entity_id', 'in', device_entities('fffe8e4c87c68ee60e0ae84c295676ce')) 140 | | selectattr('state', 'in', ['unknown', 'unavailable']) 141 | | map(attribute='entity_id') | list | sort }} 142 | 143 | See [Home Assistant Templating](https://www.home-assistant.io/docs/configuration/templating/) for additional options. 144 | 145 | ### Specifing Entities to Monitor - Custom Sensors 146 | 147 | You can configure the sensor to only monitor entities you specify instead of monitoring all entities and specifing the entities to ignore by using select or selectattr filters instead of reject and rejectattr filters. Remember, filters are cumlative and entities may be already excluded by previous filters. 148 | 149 | This is useful to create sensors that monitor specific domains, integrations etc. You can create as many groups and related sensors as you need. The following example monitors only the `sensor` domain from the Shelly integration that contain the string `_power` in the entity_id. 150 | 151 | ### Example 152 | 153 | template: 154 | - sensor: 155 | - name: "Unavailable Shelly Power Entities" 156 | unique_id: unavailable_shelly_power_entities 157 | icon: "{{ iif(states(this.entity_id)|int(-1) > 0, 'mdi:alert-circle', 'mdi:check-circle') }}" 158 | state_class: measurement 159 | state: > 160 | {% set entities = state_attr('group.unavailable_shelly_power_entities', 'entity_id') %} 161 | {{ entities | count if entities != none else -1 }} 162 | 163 | automation: 164 | ~~~ 165 | action: 166 | - action: group.set 167 | data: 168 | object_id: unavailable_shelly_power_entities 169 | entities: > 170 | {{ states.sensor 171 | | selectattr('entity_id', 'in', integration_entities('shelly')) 172 | | selectattr('entity_id', 'contains', '_power') 173 | | selectattr('state', 'in', ['unknown', 'unavailable']) 174 | | map(attribute='entity_id') | list | sort }} 175 | 176 | ## Using With Automations 177 | There is an example automation provided in the package that will display unavailable entities as a persistent notification. You can change this automation to meet your requirements. This automation is enabled by default. You can comment it out or delete it if not required. See the examples folder for more automations. 178 | 179 | ## Display in the UI 180 | To display a list of unavailable entities open the more-info dialogue of group.unavailable_entities. 181 | 182 | ![Example](https://github.com/jazzyisj/unavailable-entities-sensor/blob/main/images/group_more_info.png) 183 | 184 | #### Using Auto Entities and Fold Entity Row 185 | Using the [auto-entities](https://github.com/thomasloven/lovelace-auto-entities) and [fold-entity-row](https://github.com/thomasloven/lovelace-fold-entity-row) plugins is an excellent way to display your unavailable entities sensor in your UI. 186 | 187 | [Example Entities Card](https://github.com/jazzyisj/unavailable-entities-sensor/blob/master/examples/auto_entities_card.yaml) 188 | 189 | *Closed Fold Entity Row* 190 | 191 | ![Example](https://github.com/jazzyisj/unavailable-entities-sensor/blob/main/images/entities_card_closed_example.png) 192 | 193 | *Open Fold Entity Row* 194 | 195 | ![Example](https://github.com/jazzyisj/unavailable-entities-sensor/blob/main/images/entities_card_open_example.png) 196 | -------------------------------------------------------------------------------- /examples/auto_entities_card.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################### 2 | ## DESCRIPTION: Using unavailable entities sensor with an auto entities card and fold entity row 3 | ## https://github.com/thomasloven/lovelace-auto-entities 4 | ## https://github.com/thomasloven/lovelace-fold-entity-row 5 | ################################################################################################### 6 | type: entities 7 | title: "Unavailable Entities Example" 8 | state_color: true 9 | show_header_toggle: false 10 | entities: 11 | - type: custom:auto-entities 12 | show_empty: true 13 | unique: true 14 | filter: 15 | include: 16 | - group: group.unavailable_entities 17 | sort: 18 | method: state 19 | card: 20 | type: custom:fold-entity-row 21 | padding: 0 22 | head: 23 | entity: sensor.unavailable_entities -------------------------------------------------------------------------------- /examples/detailed_persistent_notification.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################### 2 | ## DESCRIPTION: Detailed persistent notification message - courtesy of @ThomDietrich and @warthog9 3 | ################################################################################################### 4 | automation: 5 | - id: unavailable_entities_notification 6 | alias: "Unavailable Entities Notification" 7 | description: "Create persistent notification if unavailable entities, dismiss if none." 8 | mode: restart 9 | triggers: 10 | - trigger: state 11 | entity_id: group.unavailable_entities 12 | attribute: entity_id 13 | to: ~ 14 | for: 5 # throttle triggers and prevent blank notifications 15 | conditions: 16 | - condition: template 17 | alias: "Sensor state is a valid numerical value" 18 | value_template: "{{ is_number(states('sensor.unavailable_entities')) }}" 19 | actions: 20 | - action: persistent_notification.create 21 | data: 22 | notification_id: unavailable_entities 23 | title: "Unavailable Entities" 24 | message: > 25 | {% set ns = namespace(result=[]) %} 26 | {% for s in expand(state_attr('group.unavailable_entities', 'entity_id')) %} 27 | {% set ns.result = ns.result + [ 28 | device_attr(s.entity_id, "name") ~ "|" ~ device_id(s.entity_id) ~ "|- **" ~ s.name ~ "**\n" 29 | ~ " - *entity_id*: " ~ s.entity_id ~ "\n" 30 | ~ " - *state*: " ~ s.state ~ "\n" 31 | ] 32 | %} 33 | {% endfor %} 34 | {% set ns.result = ns.result | sort %} 35 | {% set lastdev = namespace( id="" ) %} 36 | {% set tarr = ns.result %} 37 | {% set ns.result = [] %} 38 | {% for item in tarr %} 39 | {% set dev = namespace( id="" ) %} 40 | {% set entity = namespace( data="" ) %} 41 | {% set dev.id = item.split("|")[1] %} 42 | {% set entity.data = item.split("|")[2] %} 43 | {% if lastdev.id != dev.id %} 44 | {% if dev.id != 'None' %} 45 | {% set ns.result = ns.result + [ "**" ~ device_attr(dev.id, "name") ~ "**" ] %} 46 | {% else %} 47 | {% set ns.result = ns.result + [ "**Non-Device Entities**" ] %} 48 | {% endif %} 49 | {% set lastdev.id = dev.id %} 50 | {% endif %} 51 | {% set ns.result = ns.result + [ entity.data ] %} 52 | {% endfor %} 53 | {{ ns.result | join('\n') }} 54 | -------------------------------------------------------------------------------- /images/entities_card_closed_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzyisj/unavailable-entities-sensor/089bb2f6bb3244961653154e2d1c2761678e589f/images/entities_card_closed_example.png -------------------------------------------------------------------------------- /images/entities_card_open_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzyisj/unavailable-entities-sensor/089bb2f6bb3244961653154e2d1c2761678e589f/images/entities_card_open_example.png -------------------------------------------------------------------------------- /images/group_more_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzyisj/unavailable-entities-sensor/089bb2f6bb3244961653154e2d1c2761678e589f/images/group_more_info.png -------------------------------------------------------------------------------- /package_unavailable_entities.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################### 2 | ## PACKAGE: Unavailable Entities Sensor v2.4 3 | ## DESCRIPTION: Count and list entities with a state of unknown or unavailable 4 | ## REQUIREMENTS: Home Assistant v2024.8 5 | ## USAGE: https://github.com/jazzyisj/unavailable-entities-sensor/blob/main/README.md 6 | ################################################################################################### 7 | 8 | #REQUIRED - List of disabled device entities 9 | command_line: 10 | sensor: 11 | name: Disabled Device Entities 12 | unique_id: disabled_device_entities 13 | json_attributes: 14 | - entities 15 | value_template: "{{ value_json.entities | length }}" 16 | command: 'jq ''.data.entities |= map(select(.disabled_by? != null) | {entity_id: .entity_id}) | del(.data.deleted_entities) | flatten | .[3]'' < .storage/core.entity_registry' 17 | 18 | #REQUIRED - Count of unavailable entities 19 | template: 20 | - sensor: 21 | - name: "Unavailable Entities" 22 | unique_id: unavailable_entities 23 | icon: "{{ iif(states(this.entity_id)|int(-1) > 0, 'mdi:alert-circle', 'mdi:check-circle') }}" 24 | state_class: measurement 25 | state: > 26 | {% set entities = state_attr('group.unavailable_entities', 'entity_id') %} 27 | {{ entities | count if entities != none else -1 }} 28 | 29 | #REQUIRED - Group of individually ignored entities 30 | group: 31 | ignored_entities: 32 | entities: [] 33 | 34 | #REQUIRED - Create and update the monitored entities group. Updates once per minute. 35 | automation: 36 | - id: update_unavailable_entities_group 37 | alias: "Update Unavailable Entities Group" 38 | description: "Update unavailable entities group." 39 | mode: single 40 | max_exceeded: silent 41 | triggers: 42 | - trigger: event 43 | event_type: call_service 44 | event_data: 45 | domain: group 46 | service: reload 47 | 48 | - trigger: time_pattern 49 | minutes: "/1" 50 | actions: 51 | - action: group.set 52 | data: 53 | object_id: unavailable_entities 54 | entities: > 55 | {% set ignore_seconds = 60 %} 56 | {% set ignore_label = 'ignored' %} 57 | {% set ignored_domains = ['button', 'conversation', 'event', 'group', 'image', 58 | 'input_button', 'input_text', 'remote', 'tts', 'scene', 'stt', 'update'] %} 59 | {% set ignore_ts = (now().timestamp() - ignore_seconds)|as_datetime %} 60 | {% set disabled_device_entities = state_attr('sensor.disabled_device_entities', 'entities') 61 | | regex_replace(find='\[|\]|\{|\}|\'entity_id\':', replace='') %} 62 | {% set ignored_devices = label_devices(ignore_label | lower) %} 63 | {% set ignored_device_entities = namespace(value=[]) %} 64 | {% for device in ignored_devices %} 65 | {% set ignored_device_entities.value = ignored_device_entities.value + device_entities(device) %} 66 | {% endfor %} 67 | {{ states 68 | | rejectattr('domain', 'in', ignored_domains) 69 | | rejectattr('entity_id', 'in', disabled_device_entities) 70 | | rejectattr('entity_id', 'in', state_attr('group.ignored_entities', 'entity_id')) 71 | | rejectattr('entity_id', 'in', ['group.unavailable_entities', 'group.ignored_entities']) 72 | | rejectattr('entity_id', 'in', ignored_device_entities.value) 73 | | rejectattr('entity_id', 'in', label_entities(ignore_label | lower)) 74 | | rejectattr('last_changed', 'ge', ignore_ts) 75 | | selectattr('state', 'in', ['unknown', 'unavailable']) 76 | | map(attribute='entity_id') | list | sort }} 77 | 78 | #OPTIONAL - Example notfication automation to demonstrate how you can utilize this sensor. (See example folder for more.) 79 | - id: unavailable_entities_notification 80 | alias: "Unavailable Entities Notification" 81 | description: "Create persistent notification if unavailable entities, dismiss if none." 82 | mode: restart 83 | triggers: 84 | - trigger: state 85 | entity_id: group.unavailable_entities 86 | attribute: entity_id 87 | to: ~ 88 | for: 5 # throttle triggers and prevent blank notifications 89 | conditions: 90 | - condition: template 91 | alias: "Sensor state is a valid numerical value" 92 | value_template: "{{ is_number(states('sensor.unavailable_entities')) }}" 93 | actions: 94 | - if: 95 | - condition: numeric_state 96 | entity_id: sensor.unavailable_entities 97 | below: 1 98 | then: 99 | - action: persistent_notification.dismiss 100 | data: 101 | notification_id: unavailable_entities 102 | else: 103 | - action: persistent_notification.create 104 | data: 105 | notification_id: unavailable_entities 106 | title: "Unavailable Entities" 107 | message: "{{ state_attr('group.unavailable_entities', 'entity_id') | join('\n') }}" 108 | -------------------------------------------------------------------------------- /v1/package_unavailable_entities_v1.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################### 2 | ## PACKAGE: Unavailable Entities Sensor v1.1 3 | ## DESCRIPTION: Count and list entities with a state of unknown or unavailable 4 | ## REQUIREMENTS: Home Assistant v2022.5 5 | ## USAGE: https://github.com/jazzyisj/unavailable-entities-sensor/blob/main/README.md 6 | ################################################################################################### 7 | 8 | #REQUIRED - This is the template sensor 9 | template: 10 | - sensor: 11 | - name: "Unavailable Entities" 12 | unique_id: unavailable_entities 13 | icon: "{{ iif(states(this.entity_id)|int(-1) > 0, 'mdi:alert-circle', 'mdi:check-circle') }}" 14 | state_class: measurement 15 | unit_of_measurement: entities 16 | state: > 17 | {% set entities = state_attr(this.entity_id, 'entity_id') %} 18 | {{ entities | count if entities != none else none }} 19 | attributes: 20 | entity_id: > 21 | {% set ignore_seconds = 60 %} 22 | {% set ignored = state_attr('group.ignored_unavailable_entities', 'entity_id') %} 23 | {% set ignore_ts = (now().timestamp() - ignore_seconds)|as_datetime %} 24 | {% set entities = states 25 | | rejectattr('domain','in',['button', 'event', 'group', 'image', 'input_button', 'input_text', 'remote', 'tts', 'scene', 'stt']) 26 | | rejectattr('last_changed', 'ge', ignore_ts) %} 27 | {% set entities = entities | rejectattr('entity_id', 'in', ignored) if ignored != none else entities %} 28 | {{ entities | map(attribute='entity_id') | reject('has_value') | list | sort }} 29 | 30 | #OPTIONAL - Add entities you want to ignore to this group. Delete if not using group. 31 | group: 32 | ignored_unavailable_entities: 33 | entities: 34 | - sensor.example_ignored_entity 35 | 36 | #OPTIONAL Example automation to demonstrate how you can utilize this sensor, other examples in examples folder 37 | automation: 38 | - id: unavailable_entities_notification 39 | alias: "Unavailable Entities Notification" 40 | description: "Create persistent notification if unavailable entities, dismiss if none." 41 | mode: restart 42 | trigger: 43 | - platform: state 44 | entity_id: sensor.unavailable_entities 45 | attribute: entity_id 46 | to: ~ 47 | condition: 48 | - condition: template 49 | alias: "Sensor state is a valid numerical value" 50 | value_template: > 51 | {{ is_number(trigger.from_state.state) 52 | and is_number(trigger.to_state.state) }} 53 | action: 54 | - if: 55 | - condition: numeric_state 56 | entity_id: sensor.unavailable_entities 57 | below: 1 58 | then: 59 | - service: persistent_notification.dismiss 60 | data: 61 | notification_id: unavailable_entities 62 | else: 63 | - service: persistent_notification.create 64 | data: 65 | notification_id: unavailable_entities 66 | title: "Unavailable Entities" 67 | message: "{{ state_attr('sensor.unavailable_entities', 'entity_id') | join('\n') }}" --------------------------------------------------------------------------------