Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Ltek/0c9cecf632b9c32915130680d834bcf7 to your computer and use it in GitHub Desktop.

Select an option

Save Ltek/0c9cecf632b9c32915130680d834bcf7 to your computer and use it in GitHub Desktop.
Home Assistant Blueprint: Offline Devices Report
blueprint:
name: "⛔ Offline Devices Report with Notification Actions"
description: |
⛔ Offline Devices Report with Notification Actions
🚀 Version 2026.01.21.39c
- Recent Fixes: Debug triggers, Template syntax errors, To-Do list output, Empty notifications
***Features***
📱 Device Notifications
- Send alerts to mobile devices via mobile_app integration
- iOS and Android specific notification options
- Configurable interruption levels and sounds
📢 Persistent Web UI Notifications
- Dashboard notifications for offline devices
- Optional "no issues" confirmation messages
⚡ Execution methods
- Schedule Time and Day(s)
- Manual trigger via button
- Debug mode for immediate offline/unknown state changes
🚫️ Entity exclusion options
- Directly select specific entities
- Pattern-based exclusions using text strings
- Case-sensitive matching for precise control
- Include or Exclude entities with Visability disabled in HA (hidden entities)
- Some Template Entities may cause problems or errors due to how HA processes them. Exclude those entities if you have problems or disable Visability for them in HA's settings.
📈 Flexible display formats
- Friendly names only
- Entity IDs only
- Combined format (Friendly Name + Entity ID)
🎬 Custom actions after report
- Add offline devices to To-Do list automatically
- Action buttons in notifications for manual control
- Custom automation actions integration
- {{ offline_devices }} variable returns a formatted list of offline devices
- {{ offline_devices_count }} variable returns the count of offline devices
📡 Smart device detection
- Automatic battery sensor identification
- Switch monitoring for offline states
- Combined or separate reporting options
📖 Logging
- Debug to Logbook
- Easy troubleshooting configuration
🐛 Real-Time Debug Mode (Short-term troubleshooting only)
- Trigger on entity state changes to offline/unknown
- Configurable delay to confirm persistent offline state
- Uses same exclusions as scheduled reports
domain: automation
input:
trigger_settings:
name: ⚙️ Trigger Settings
collapsed: true
input:
include_button:
name: Use Button Trigger?
default: disable_button_trigger
selector:
select:
options:
- label: Enabled
value: enable_button_trigger
- label: Disabled
value: disable_button_trigger
button_entity:
name: Set Button Trigger
default: []
selector:
entity:
domain: [input_button]
multiple: false
include_time:
name: Use an Automatic Reoccuring Report?
default: time_disabled
selector:
select:
options:
- label: Enabled
value: time_enabled
- label: Disabled
value: time_disabled
time:
name: Set Reoccuring Time
default: "05:00:00"
selector:
time: {}
weekday_options:
name: Set Reoccuring Days
default: [mon, tue, wed, thu, fri, sat, sun]
selector:
select:
multiple: true
mode: list
options:
- {label: Monday, value: mon}
- {label: Tuesday, value: tue}
- {label: Wednesday, value: wed}
- {label: Thursday, value: thu}
- {label: Friday, value: fri}
- {label: Saturday, value: sat}
- {label: Sunday, value: sun}
device_settings:
name: "🚫 Exclusion Settings"
collapsed: true
input:
include_hidden_entities:
name: Include Hidden Entities
default: exclude_hidden
selector:
select:
options:
- {label: Exclude Hidden Entities, value: exclude_hidden}
- {label: Include Hidden Entities, value: include_hidden}
exclude_entities:
name: Individual Entities
default: []
selector:
entity:
multiple: true
exclude_strings:
name: Text Pattern Exclusions
default: ''
selector:
text:
multiline: true
notification_settings:
name: "📢 Notification Settings"
collapsed: true
input:
include_easy_notify:
name: Device Notifications
default: disabled_easy_notify
selector:
select:
options:
- {label: Disabled, value: disabled_easy_notify}
- {label: Enabled - notify only if offline devices found, value: enable_easy_notify}
- {label: Enabled - notify even if no offline devices found, value: enable_easy_okay_notify}
notify_device:
name: Devices to Notify
default: []
selector:
device:
integration: mobile_app
multiple: true
notify_title:
name: Notification Title
default: Offline Devices Report
selector:
text: {}
notify_message:
name: Message Format
default: all_sensors
selector:
select:
options:
- {label: All Offline Devices, value: all_sensors}
- {label: Only Battery Sensors, value: sensors}
- {label: Only Switches, value: switches}
notify_okay_message:
name: Message when No Offline Devices found
default: No offline devices detected
selector:
text: {}
notify_interruption_level:
name: iOS Interruption Level
default: active
selector:
select:
options:
- {label: Default, value: active}
- {label: Critical Notifications, value: critical}
- {label: Time Sensitive, value: time-sensitive}
- {label: Quiet, value: passive}
notify_sound:
name: iOS Notification Sound
default: ''
selector:
text: {}
notify_data:
name: Android Options
default: []
selector:
select:
multiple: true
options:
- {label: High Priority, value: high_priority}
- {label: Sticky Notification, value: sticky}
- {label: Notification Channel, value: channel}
notify_channel:
name: Android Notification Channel
default: ''
selector:
text: {}
include_persistent_notification:
name: Persistent UI Notifications
default: disabled_persistent_notification
selector:
select:
options:
- {label: Disabled, value: disabled_persistent_notification}
- {label: Notify only when offline devices are found, value: enable_persistent_notification}
- {label: Notify always - even if no offline devices found, value: enable_persistent_okay_notification}
display_options:
name: Display Options
default: friendly_names
selector:
select:
options:
- {label: Entity IDs, value: entity_ids}
- {label: Friendly Names, value: friendly_names}
- {label: Both, value: both}
action_buttons_settings:
name: "🚀 Action Buttons"
collapsed: true
input:
auto_add_to_do:
name: Automatically Add to To-Do List
default: disable_auto_add_to_do
selector:
select:
options:
- {label: Disabled, value: disable_auto_add_to_do}
- {label: Enabled, value: enable_auto_add_to_do}
notify_tag:
name: Notification Action Tag
default: offline_devices_report_action
selector:
text: {}
notify_action_buttons:
name: Action Buttons
default: disabled_notify_action_buttons
selector:
select:
options:
- {label: Disabled, value: disabled_notify_action_buttons}
- {label: Add To-Do Button, value: enable_to_do_list_button}
- {label: Add To-Do with Confirmation, value: enable_to_do_list_button_and_confirmation}
to_do_list:
name: To-Do List
default: []
selector:
entity:
domain: [todo]
multiple: false
to_do_task_title:
name: Task Title
default: Check Offline Devices
selector:
text: {}
action_button_to_do:
name: Action Button Text
default: Add to To-Do List
selector:
text: {}
action_button_cancel:
name: Cancel Button Text
default: Cancel
selector:
text: {}
action_button_confirmation_title:
name: Confirmation Title
default: Action Confirmation
selector:
text: {}
action_button_confirmation_message:
name: Confirmation Message
default: Task added to To-Do list
selector:
text: {}
cancel_action_button_confirmation_message:
name: Cancel Confirmation Message
default: Action cancelled
selector:
text: {}
custom_actions_settings:
name: ⚡ Custom Actions
collapsed: true
input:
include_custom_actions:
name: Enable Custom Actions
default: disabled_custom_actions
selector:
select:
options:
- {label: Enabled, value: enable_custom_actions}
- {label: Disabled, value: disable_custom_actions}
custom_actions:
name: Custom Actions
default: []
selector:
action: {}
global_conditions_settings:
name: "🌍 Global Conditions"
collapsed: true
input:
global_conditions:
name: Conditions
default: []
selector:
condition: {}
debug_settings:
name: "🐛 Debug Settings"
collapsed: true
input:
include_debug:
name: Real-Time Debug Mode?
default: debug_disabled
selector:
select:
options:
- {label: Enabled, value: debug_enabled}
- {label: Disabled, value: debug_disabled}
debug_offline_delay:
name: Wait Time Confirmation
default: "00:03:00"
selector:
duration: {}
include_logbook_debug:
name: Logbook Debug Messages
default: logbook_realtime_only
selector:
select:
options:
- {label: Enabled (All Runs), value: logbook_enabled_all}
- {label: Real-Time Debug Only, value: logbook_realtime_only}
- {label: Disabled, value: logbook_disabled}
include_system_log_debug:
name: System Log Debug Messages
default: systemlog_realtime_only
selector:
select:
options:
- {label: Enabled (All Runs), value: systemlog_enabled_all}
- {label: Real-Time Debug Only, value: systemlog_realtime_only}
- {label: Disabled, value: systemlog_disabled}
include_debug_persistent_notification:
name: Persistent UI Notifications
default: persistent_realtime_only
selector:
select:
options:
- {label: Enabled, value: persistent_realtime_only}
- {label: Disabled, value: persistent_disabled}
variables:
include_button: !input include_button
include_time: !input include_time
include_debug: !input include_debug
debug_offline_delay: !input debug_offline_delay
include_logbook_debug: !input include_logbook_debug
include_system_log_debug: !input include_system_log_debug
include_debug_persistent_notification: !input include_debug_persistent_notification
debug_offline_delay_seconds: >
{% set delay = debug_offline_delay %}
{% if delay is string %}
{% set parts = delay.split(':') %}
{% set hours = parts[0] | int %}
{% set minutes = parts[1] | int if parts|length > 1 else 0 %}
{% set seconds = parts[2] | int if parts|length > 2 else 0 %}
{{ (hours * 3600 + minutes * 60 + seconds) | int }}
{% elif delay is mapping and 'hours' in delay %}
{{ (delay.hours * 3600 + delay.minutes * 60 + delay.seconds) | int }}
{% else %}
180
{% endif %}
weekday_options: !input weekday_options
include_hidden_entities: !input include_hidden_entities
exclude_entities: !input exclude_entities
exclude_strings: !input exclude_strings
display_options: !input display_options
include_easy_notify: !input include_easy_notify
notify_device: !input notify_device
notify_title: !input notify_title
notify_message: !input notify_message
notify_okay_message: !input notify_okay_message
notify_interruption_level: !input notify_interruption_level
notify_sound: !input notify_sound
notify_data: !input notify_data
notify_channel: !input notify_channel
include_persistent_notification: !input include_persistent_notification
notify_action_buttons: !input notify_action_buttons
auto_add_to_do: !input auto_add_to_do
notify_tag: !input notify_tag
to_do_list: !input to_do_list
to_do_task_title: !input to_do_task_title
action_button_to_do: !input action_button_to_do
action_button_cancel: !input action_button_cancel
action_button_confirmation_title: !input action_button_confirmation_title
action_button_confirmation_message: !input action_button_confirmation_message
cancel_action_button_confirmation_message: !input cancel_action_button_confirmation_message
include_custom_actions: !input include_custom_actions
exclude_regex: >
{% if exclude_strings is defined %}
{% set patterns = exclude_strings.split('\n') | map('trim') | reject('eq', '') | list %}
{{ patterns | join('|') if patterns else 'a^' }}
{% else %}
a^
{% endif %}
all_exclude_entities: >
{{ exclude_entities if exclude_entities is defined else [] }}
_offline_devices: >
{% set exclude_entities_list = all_exclude_entities %}
{% set entities = states | map(attribute='entity_id') | reject('search', 'cloud') | reject('in', exclude_entities_list) | reject('search', exclude_regex) | list %}
{% if include_hidden_entities == 'exclude_hidden' %}
{% set entities = entities | reject('is_hidden_entity') | list %}
{% endif %}
{% set sensors = entities | select('search', '^(sensor|binary_sensor)') | list %}
{% set battery_entities = sensors | select('is_state_attr', 'device_class', 'battery') | reject('has_value') | list %}
{% set switches = entities | select('search', '^switch') | reject('has_value') | list %}
{% set offline_entities = (battery_entities + switches) | unique %}
{% if display_options == 'entity_ids' %}
{{ offline_entities | list }}
{% elif display_options == 'friendly_names' %}
{{ offline_entities | map('state_attr', 'friendly_name') | list }}
{% elif display_options == 'both' %}
{% set result = namespace(items=[]) %}
{% for e in offline_entities %}
{% set friendly_name = state_attr(e, 'friendly_name') %}
{% set item = friendly_name ~ ' (' ~ e ~ ')' if friendly_name else e %}
{% set result.items = result.items + [item] %}
{% endfor %}
{{ result.items }}
{% else %}
[]
{% endif %}
offline_devices: >
{% if _offline_devices | length > 0 %}
{{ '- ' ~ (_offline_devices | sort | join('\n- ')) }}
{% else %}
No offline devices detected.
{% endif %}
offline_devices_count: >
{{ _offline_devices | length }}
_battery_sensors: >
{% set exclude_entities_list = all_exclude_entities %}
{% set entities = states | map(attribute='entity_id') | reject('search', 'cloud') | reject('in', exclude_entities_list) | reject('search', exclude_regex) | list %}
{% if include_hidden_entities == 'exclude_hidden' %}
{% set entities = entities | reject('is_hidden_entity') | list %}
{% endif %}
{% set sensors = entities | select('search', '^(sensor|binary_sensor)') | list %}
{% set battery_entities = sensors | select('is_state_attr', 'device_class', 'battery') | reject('has_value') | list %}
{{ battery_entities }}
_switches: >
{% set exclude_entities_list = all_exclude_entities %}
{% set entities = states | map(attribute='entity_id') | reject('search', 'cloud') | reject('in', exclude_entities_list) | reject('search', exclude_regex) | list %}
{% if include_hidden_entities == 'exclude_hidden' %}
{% set entities = entities | reject('is_hidden_entity') | list %}
{% endif %}
{% set switches = entities | select('search', '^switch') | reject('has_value') | list %}
{{ switches }}
all_sensors: >
{% if _offline_devices | length > 0 %}
{{ offline_devices_count ~ ' Offline Devices\n\n' ~ ('- ' ~ (_offline_devices | sort | join('\n- '))) }}
{% else %}
''
{% endif %}
sensors: >
{% if _battery_sensors | length > 0 %}
{{ '🔋 ' ~ _battery_sensors | length ~ ' Offline Battery Sensor(s):\n\n' ~ ('- ' ~ (_battery_sensors | sort | join('\n- '))) }}
{% else %}
''
{% endif %}
switches: >
{% if _switches | length > 0 %}
{{ '🔌 ' ~ _switches | length ~ ' Offline Switch(es):\n\n' ~ ('- ' ~ (_switches | sort | join('\n- '))) }}
{% else %}
''
{% endif %}
easy_notify_message: >
{% if notify_message == 'all_sensors' %}
{{ all_sensors }}
{% elif notify_message == 'sensors' %}
{{ sensors }}
{% elif notify_message == 'switches' %}
{{ switches }}
{% else %}
''
{% endif %}
to_do_description: >
{% set device_list = _offline_devices | join(', ') %}
{% set cleaned = device_list | regex_replace('\\s*\\([^)]+\\)', '') %}
{% if cleaned | length > 250 %}
{{ cleaned[:250] ~ '...' }}
{% else %}
{{ cleaned }}
{% endif %}
sensors_names: >
{% if _battery_sensors | length > 0 %}
{{ (_battery_sensors | list | sort | join('\n')) }}
{% else %}
''
{% endif %}
switches_names: >
{% if _switches | length > 0 %}
{{ (_switches | list | sort | join('\n')) }}
{% else %}
''
{% endif %}
device_message_data: >
{% set message = namespace(data={}) %}
{% set push = namespace(data={}) %}
{% if notify_interruption_level in ['active', 'critical', 'time-sensitive', 'passive'] %}
{% set push.data = dict(push.data, **{ 'interruption-level': notify_interruption_level }) %}
{% endif %}
{% if notify_sound != '' %}
{% set push.data = dict(push.data, **{ 'sound': notify_sound }) %}
{% endif %}
{% if push.data %}
{% set message.data = dict(message.data, **{ 'push': push.data }) %}
{% endif %}
{% if 'high_priority' in notify_data %}
{% set message.data = dict(message.data, **{ 'ttl': 0, 'priority': 'high' }) %}
{% endif %}
{% if 'channel' in notify_data %}
{% set message.data = dict(message.data, **{ 'channel': notify_channel }) %}
{% endif %}
{% if 'sticky' in notify_data %}
{% set message.data = dict(message.data, **{ 'sticky': "true" }) %}
{% endif %}
{% set message.data = dict(message.data, **{ 'tag': notify_tag }) %}
{{ message.data }}
device_message_data_action_button: >
{% set message = namespace(data={}) %}
{% set push = namespace(data={}) %}
{% if notify_interruption_level in ['active', 'critical', 'time-sensitive', 'passive'] %}
{% set push.data = dict(push.data, **{ 'interruption-level': notify_interruption_level }) %}
{% endif %}
{% if notify_sound != '' %}
{% set push.data = dict(push.data, **{ 'sound': notify_sound }) %}
{% endif %}
{% if push.data %}
{% set message.data = dict(message.data, **{ 'push': push.data }) %}
{% endif %}
{% if 'high_priority' in notify_data %}
{% set message.data = dict(message.data, **{ 'ttl': 0, 'priority': 'high' }) %}
{% endif %}
{% if 'channel' in notify_data %}
{% set message.data = dict(message.data, **{ 'channel': notify_channel }) %}
{% endif %}
{% if 'sticky' in notify_data %}
{% set message.data = dict(message.data, **{ 'sticky': "true" }) %}
{% endif %}
{% set actions = [{'action': action_button_to_do, 'title': action_button_to_do}, {'action': action_button_cancel, 'title': action_button_cancel}] %}
{% set message.data = dict(message.data, **{ 'actions': actions, 'tag': notify_tag }) %}
{{ message.data }}
device_confirmation_message_data: >
{% set message = namespace(data={}) %}
{% set push = namespace(data={}) %}
{% if notify_interruption_level in ['active', 'critical', 'time-sensitive', 'passive'] %}
{% set push.data = dict(push.data, **{ 'interruption-level': notify_interruption_level }) %}
{% endif %}
{% if notify_sound != '' %}
{% set push.data = dict(push.data, **{ 'sound': notify_sound }) %}
{% endif %}
{% if push.data %}
{% set message.data = dict(message.data, **{ 'push': push.data }) %}
{% endif %}
{% if 'high_priority' in notify_data %}
{% set message.data = dict(message.data, **{ 'ttl': 0, 'priority': 'high' }) %}
{% endif %}
{% if 'channel' in notify_data %}
{% set message.data = dict(message.data, **{ 'channel': notify_channel }) %}
{% endif %}
{% if 'sticky' in notify_data %}
{% set message.data = dict(message.data, **{ 'sticky': "true" }) %}
{% endif %}
{% set message.data = dict(message.data, **{ 'tag': notify_tag }) %}
{{ message.data }}
trigger:
- platform: time
at: !input time
id: time_trigger
- platform: event
event_type: state_changed
event_data: {}
id: debug_trigger
- platform: template
value_template: >
{% if include_button == "enable_button_trigger" and button_entity %}
{{ states(button_entity) }}
{% else %}
false
{% endif %}
id: button_trigger
condition:
- condition: or
conditions:
- condition: and
conditions:
- condition: trigger
id: button_trigger
- condition: template
value_template: '{{ include_button == "enable_button_trigger" and button_entity }}'
- condition: and
conditions:
- condition: trigger
id: time_trigger
- condition: template
value_template: '{{ include_time == "time_enabled" }}'
- condition: template
value_template: '{{ now().strftime("%a").lower() in weekday_options }}'
- condition: and
conditions:
- condition: trigger
id: debug_trigger
- condition: template
value_template: '{{ include_debug == "debug_enabled" }}'
- condition: template
value_template: >
{{ trigger.event.data.new_state is not none
and trigger.event.data.new_state.state in ["unavailable", "unknown"]
and (trigger.event.data.old_state is none
or trigger.event.data.old_state.state not in ["unavailable", "unknown"]) }}
- condition: and
conditions: !input global_conditions
action:
- choose:
- conditions:
- condition: trigger
id: debug_trigger
- condition: template
value_template: '{{ include_debug == "debug_enabled" }}'
sequence:
- if:
- condition: template
value_template: '{{ include_system_log_debug in ["systemlog_enabled_all", "systemlog_realtime_only"] }}'
then:
- service: system_log.write
data:
message: "Debug mode triggered by state_changed event"
level: warning
- variables:
debug_entity: "{{ trigger.event.data.entity_id }}"
debug_entity_state: "{{ trigger.event.data.new_state.state }}"
debug_entity_name: '{{ trigger.event.data.new_state.attributes.friendly_name or trigger.event.data.entity_id }}'
old_state: "{{ trigger.event.data.old_state.state if trigger.event.data.old_state else '' }}"
- if:
- condition: template
value_template: '{{ include_system_log_debug in ["systemlog_enabled_all", "systemlog_realtime_only"] }}'
then:
- service: system_log.write
data:
message: "Debug checking entity: {{ debug_entity }}, new_state: {{ debug_entity_state }}, old_state: {{ old_state }}"
level: warning
- if:
- condition: template
value_template: '{{ debug_entity_state not in ["unknown", "unavailable"] or old_state in ["unknown", "unavailable"] }}'
then:
- if:
- condition: template
value_template: '{{ include_system_log_debug in ["systemlog_enabled_all", "systemlog_realtime_only"] }}'
then:
- service: system_log.write
data:
message: "Debug stopped: Not a change TO unknown/unavailable for {{ debug_entity }}"
level: info
- stop: 'Not a change TO unknown/unavailable state'
- variables:
entity_excluded: >
{% if debug_entity in all_exclude_entities %}
true
{% elif exclude_regex != 'a^' and debug_entity is search(exclude_regex) %}
true
{% elif include_hidden_entities == 'exclude_hidden' and is_hidden_entity(debug_entity) %}
true
{% elif debug_entity is search('cloud') %}
true
{% else %}
false
{% endif %}
- if:
- condition: template
value_template: '{{ include_system_log_debug in ["systemlog_enabled_all", "systemlog_realtime_only"] }}'
then:
- service: system_log.write
data:
message: "Debug exclusion check for {{ debug_entity }}: excluded={{ entity_excluded }}"
level: info
- if:
- condition: template
value_template: '{{ entity_excluded == true }}'
then:
- if:
- condition: template
value_template: '{{ include_logbook_debug in ["logbook_enabled_all", "logbook_realtime_only"] }}'
then:
- service: logbook.log
data:
name: Offline Devices Debug Mode
message: 'Debug ignored: {{ debug_entity_name }} is excluded'
- if:
- condition: template
value_template: '{{ include_system_log_debug in ["systemlog_enabled_all", "systemlog_realtime_only"] }}'
then:
- service: system_log.write
data:
message: "Debug ignored: {{ debug_entity_name }} ({{ debug_entity }}) is excluded from monitoring"
level: info
- if:
- condition: template
value_template: '{{ include_debug_persistent_notification == "persistent_realtime_only" }}'
then:
- service: persistent_notification.create
data:
title: "🐛 Debug: Entity Excluded"
message: 'Debug ignored: {{ debug_entity_name }} is excluded from monitoring'
notification_id: 'debug_excluded_{{ debug_entity | replace(".", "_") }}'
- stop: 'Entity excluded from debug'
- if:
- condition: template
value_template: '{{ include_logbook_debug in ["logbook_enabled_all", "logbook_realtime_only"] }}'
then:
- service: logbook.log
data:
name: Offline Devices Debug Mode
message: 'Debug triggered: {{ debug_entity_name }} ({{ debug_entity }}) changed from {{ old_state }} to {{ debug_entity_state }}'
- if:
- condition: template
value_template: '{{ include_system_log_debug in ["systemlog_enabled_all", "systemlog_realtime_only"] }}'
then:
- service: system_log.write
data:
message: "🐛 DEBUG ALERT: {{ debug_entity_name }} ({{ debug_entity }}) went {{ debug_entity_state }}"
level: warning
- if:
- condition: template
value_template: '{{ include_debug_persistent_notification in ["persistent_enabled_all", "persistent_realtime_only"] }}'
then:
- service: persistent_notification.create
data:
title: "🐛 Debug: Entity Offline Detected"
message: '{{ debug_entity_name }} ({{ debug_entity }}) changed from {{ old_state }} to {{ debug_entity_state }}'
notification_id: 'debug_trigger_{{ debug_entity | replace(".", "_") }}'
- wait_template: '{{ states(debug_entity) in ["unknown","unavailable"] }}'
timeout: '{{ debug_offline_delay_seconds }}'
continue_on_timeout: false
- choose:
- conditions:
- condition: template
value_template: '{{ wait is defined and wait.timed_out == true }}'
sequence:
- if:
- condition: template
value_template: '{{ include_logbook_debug in ["logbook_enabled_all", "logbook_realtime_only"] }}'
then:
- service: logbook.log
data:
name: Offline Devices Debug Mode
message: '{{ debug_entity_name }} ({{ debug_entity }}) stayed {{ debug_entity_state }} for {{ debug_offline_delay }}'
- if:
- condition: template
value_template: '{{ include_debug_persistent_notification in ["persistent_enabled_all", "persistent_realtime_only"] }}'
then:
- service: persistent_notification.create
data:
title: "🐛 Debug: Entity Confirmed Offline"
message: '{{ debug_entity_name }} ({{ debug_entity }}) stayed {{ debug_entity_state }} for {{ debug_offline_delay }}'
notification_id: 'debug_confirmed_{{ debug_entity | replace(".", "_") }}'
- stop: 'Debug complete - entity stayed offline'
- conditions:
- condition: template
value_template: '{{ wait is defined and wait.timed_out == false }}'
sequence:
- if:
- condition: template
value_template: '{{ include_logbook_debug in ["logbook_enabled_all", "logbook_realtime_only"] }}'
then:
- service: logbook.log
data:
name: Offline Devices Debug Mode
message: '{{ debug_entity_name }} ({{ debug_entity }}) came back online during wait period'
- if:
- condition: template
value_template: '{{ include_debug_persistent_notification in ["persistent_enabled_all", "persistent_realtime_only"] }}'
then:
- service: persistent_notification.create
data:
title: "🐛 Debug: Entity Back Online"
message: '{{ debug_entity_name }} ({{ debug_entity }}) came back online during wait period'
notification_id: 'debug_online_{{ debug_entity | replace(".", "_") }}'
- stop: 'Debug cancelled - entity came back online'
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" or (include_logbook_debug == "logbook_realtime_only" and trigger.id == "debug_trigger") }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: |
Initial: include_easy_notify: {{ include_easy_notify | default('undefined') }},
include_persistent_notification: {{ include_persistent_notification | default('undefined') }},
notify_device: {{ notify_device | default('undefined') }},
offline_devices_count: {{ offline_devices_count | default('0') }},
_battery_sensors_count: {{ _battery_sensors | length | default('0') }},
_switches_count: {{ _switches | length | default('0') }},
easy_notify_message length: {{ easy_notify_message | default('') | length }},
to_do_description length: {{ to_do_description | default('') | length }},
to_do_list: {{ to_do_list | default('undefined') }},
include_hidden_entities: {{ include_hidden_entities | default('undefined') }}
- choose:
- alias: Use the easy notify options
conditions:
- condition: template
value_template: '{{ include_easy_notify == "enable_easy_notify" or include_easy_notify == "enable_easy_okay_notify" }}'
- condition: template
value_template: '{{ notify_device | length > 0 }}'
- condition: template
value_template: '{{ trigger.id != "debug_trigger" }}'
sequence:
- alias: Send a notification to each device
repeat:
for_each: '{{ notify_device | default([]) }}'
sequence:
- choose:
- alias: Offline devices have been found
conditions:
- condition: template
value_template: '{{ offline_devices_count > 0 }}' # FIXED: Changed from easy_notify_message check
sequence:
- choose:
- alias: Easy notify with no action button
conditions:
- condition: template
value_template: '{{ notify_action_buttons == "disabled_notify_action_buttons" or auto_add_to_do == "enable_auto_add_to_do" }}'
sequence:
- service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}
data:
title: !input notify_title
message: '{{ easy_notify_message }}'
data: '{{ device_message_data }}'
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Sent notification without action buttons to device: {{ device_attr(repeat.item, "name") | slugify }}, service: notify.mobile_app_{{ device_attr(repeat.item, "name") | slugify }}, tag: {{ notify_tag }}, message length: {{ easy_notify_message | length }}'
- alias: Easy notify with action button
conditions:
- condition: template
value_template: '{{ notify_action_buttons != "disabled_notify_action_buttons" and auto_add_to_do != "enable_auto_add_to_do" }}'
sequence:
- service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}
data:
title: !input notify_title
message: '{{ easy_notify_message }}'
data: '{{ device_message_data_action_button }}'
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Sent notification with action buttons to device: {{ device_attr(repeat.item, "name") | slugify }}, service: notify.mobile_app_{{ device_attr(repeat.item, "name") | slugify }}, tag: {{ notify_tag }}, actions: {{ device_message_data_action_button }}'
- alias: No offline devices have been found
conditions:
- condition: template
value_template: '{{ offline_devices_count == 0 }}' # FIXED: Changed from easy_notify_message check
- condition: template
value_template: '{{ include_easy_notify == "enable_easy_okay_notify" }}'
sequence:
- service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}
data:
title: !input notify_title
message: '{{ notify_okay_message }}' # FIXED: Changed from !input to template variable
data: '{{ device_message_data }}'
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Sent no-issues notification to device: {{ device_attr(repeat.item, "name") | slugify }}, service: notify.mobile_app_{{ device_attr(repeat.item, "name") | slugify }}, tag: {{ notify_tag }}, message: {{ notify_okay_message }}'
- choose:
- alias: Use the easy notify persistent notification options
conditions:
- condition: template
value_template: '{{ include_persistent_notification == "enable_persistent_notification" or include_persistent_notification == "enable_persistent_okay_notification" }}'
- condition: template
value_template: '{{ trigger.id != "debug_trigger" }}'
sequence:
- choose:
- alias: Offline devices have been found
conditions:
- condition: template
value_template: '{{ offline_devices_count > 0 }}' # FIXED: Changed from easy_notify_message check
sequence:
- service: persistent_notification.create
data:
title: "⛔ {{ notify_title }}"
message: '{{ easy_notify_message }}'
notification_id: offline_devices_report
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Created persistent notification, id: offline_devices_report, message length: {{ easy_notify_message | length }}'
- alias: No offline devices have been found
conditions:
- condition: template
value_template: '{{ offline_devices_count == 0 }}' # FIXED: Changed from easy_notify_message check
- condition: template
value_template: '{{ include_persistent_notification == "enable_persistent_okay_notification" }}'
sequence:
- service: persistent_notification.create
data:
title: "⛔ {{ notify_title }}"
message: '{{ notify_okay_message }}' # FIXED: Changed from !input to template variable
notification_id: offline_devices_report_ok
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Created persistent no-issues notification, id: offline_devices_report_ok, message: {{ notify_okay_message }}'
- choose:
- alias: Auto-add to To-Do list when enabled
conditions:
- condition: template
value_template: '{{ auto_add_to_do == "enable_auto_add_to_do" and to_do_description != "" }}'
- condition: template
value_template: '{{ to_do_list | length > 0 and is_state(to_do_list, "unknown") == false and is_state(to_do_list, "unavailable") == false }}'
- condition: template
value_template: '{{ trigger.id != "debug_trigger" }}'
sequence:
- service: todo.add_item
data:
entity_id: '{{ to_do_list }}'
item: !input to_do_task_title
description: '{{ to_do_description }}'
continue_on_error: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Auto-added To-Do item to {{ to_do_list }}: title={{ to_do_task_title }}, description length={{ to_do_description | length }}'
- choose:
- alias: Check if the To-Do action button is enabled
conditions:
- condition: template
value_template: '{{ include_easy_notify == "enable_easy_notify" or include_easy_notify == "enable_easy_okay_notify" }}'
- condition: template
value_template: '{{ notify_action_buttons == "enable_to_do_list_button" or notify_action_buttons == "enable_to_do_list_button_and_confirmation" }}'
- condition: template
value_template: '{{ auto_add_to_do != "enable_auto_add_to_do" }}'
- condition: template
value_template: '{{ notify_device | length > 0 }}'
- condition: template
value_template: '{{ to_do_list | length > 0 and is_state(to_do_list, "unknown") == false and is_state(to_do_list, "unavailable") == false }}'
- condition: template
value_template: '{{ trigger.id != "debug_trigger" }}'
sequence:
- alias: Wait for a response from the action buttons
wait_for_trigger:
- platform: event
event_type: mobile_app_notification_action
event_data:
action: '{{ action_button_to_do }}'
- platform: event
event_type: mobile_app_notification_action
event_data:
action: '{{ action_button_cancel }}'
timeout: 00:15:00
continue_on_timeout: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Wait for trigger result: {{ wait.trigger | default("no trigger received", true) }}, action: {{ wait.trigger.event.data.action if wait.trigger else "none" }}, tag: {{ notify_tag if wait.trigger else "none" }}, event_data: {{ wait.trigger.event.data if wait.trigger else "none" }}'
- choose:
- alias: Check if confirmation message is disabled
conditions:
- condition: template
value_template: '{{ notify_action_buttons == "enable_to_do_list_button" }}'
sequence:
- if:
- condition: template
value_template: '{{ wait.trigger is not none and wait.trigger.event.data.action == action_button_to_do }}'
then:
- service: todo.add_item
data:
entity_id: '{{ to_do_list }}'
item: !input to_do_task_title
description: '{{ to_do_description }}'
continue_on_error: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Added To-Do item to {{ to_do_list }}: title={{ to_do_task_title }}, description length={{ to_do_description | length }}'
- if:
- condition: template
value_template: '{{ wait.trigger is not none and wait.trigger.event.data.action == action_button_cancel }}'
then:
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Action cancelled for tag: {{ notify_tag }}'
- stop: 'Action cancelled'
- alias: Check if confirmation message is enabled
conditions:
- condition: template
value_template: '{{ notify_action_buttons == "enable_to_do_list_button_and_confirmation" }}'
sequence:
- if:
- condition: template
value_template: '{{ wait.trigger is not none and wait.trigger.event.data.action == action_button_to_do }}'
then:
- service: todo.add_item
data:
entity_id: '{{ to_do_list }}'
item: !input to_do_task_title
description: '{{ to_do_description }}'
continue_on_error: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Added To-Do item to {{ to_do_list }}: title={{ to_do_task_title }}, description length={{ to_do_description | length }}'
- repeat:
for_each: '{{ notify_device | default([]) }}'
sequence:
- service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}
data:
title: !input action_button_confirmation_title
message: !input action_button_confirmation_message
data: '{{ device_confirmation_message_data }}'
continue_on_error: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Sent confirmation notification to device: {{ device_attr(repeat.item, "name") | slugify }}, service: notify.mobile_app_{{ device_attr(repeat.item, "name") | slugify }}, tag: {{ notify_tag }}, message: {{ action_button_confirmation_message }}'
- if:
- condition: template
value_template: '{{ wait.trigger is not none and wait.trigger.event.data.action == action_button_cancel }}'
then:
- repeat:
for_each: '{{ notify_device | default([]) }}'
sequence:
- service: notify.mobile_app_{{ device_attr(repeat.item, 'name') | slugify }}
data:
title: !input action_button_confirmation_title
message: !input cancel_action_button_confirmation_message
data: '{{ device_message_data }}'
continue_on_error: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == "logbook_enabled_all" }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Sent cancel confirmation notification to device: {{ device_attr(repeat.item, "name") | slugify }}, service: notify.mobile_app_{{ device_attr(repeat.item, "name") | slugify }}, tag: {{ notify_tag }}, message: {{ cancel_action_button_confirmation_message }}'
- choose:
- alias: Perform the custom actions
conditions:
- condition: template
value_template: '{{ include_custom_actions == "enable_custom_actions" }}'
- condition: template
value_template: '{{ trigger.id != "debug_trigger" }}'
sequence: !input custom_actions
mode: single
@xbmcnut
Copy link

xbmcnut commented Mar 14, 2025

Awesome, well done. Does exclude with labels work?

@Ltek
Copy link
Author

Ltek commented Mar 14, 2025

Awesome, well done. Does exclude with labels work?

I fixed what existed: specifying individual entities. I have not been able to get labels to work.

@xbmcnut
Copy link

xbmcnut commented Mar 17, 2025

FYI, I tried using Copilot and ChatGPT one Sunday afternoon for hours and although both platforms reckoned they found out why labels weren't working, 12 iterations later, nothing worked. Now I'm a hardware guy and know diddly about code but this is working to reject labels in one of my mushroom cards. Is that remotely relevant?

secondary: |-
  {{ states.light 
  | rejectattr('entity_id', 'in', label_entities('Group'))
  | rejectattr('entity_id', 'in', label_entities('Dummylight'))
  | selectattr('state', 'eq', 'on')
  | list
  | count }}

@Ltek
Copy link
Author

Ltek commented Mar 17, 2025 via email

@Sanbecr
Copy link

Sanbecr commented Sep 27, 2025

I found that the Combined version "Friendly Names (Entity IDs)" wasn't rendering the list properly in the notification, resulting in a list of single characters.

Fix:

    {% elif format_option == 'combined' %}
      {% set ns = namespace(combined_list=[]) %}
      {%- for e in offline_entities %}
        {% set friendly_name = state_attr(e, "friendly_name") | default(e) %}
        {% set ns.combined_list = ns.combined_list + [friendly_name ~ ' (' ~ e ~ ')'] %}
      {%- endfor %}
      {{ ns.combined_list }}
    {% else %}

Fork: https://gist.github.com/Sanbecr/f9f4dd608fcf9a29413ecb2b66d6f784

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment