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.06.07d
- debug time delay fixed (07d)
***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)
- NOTE - 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
description: You must enable at least one.
collapsed: true
input:
include_button:
name: Use Button Trigger?
description: Enable manual triggering via button
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
description: Button that will trigger the automation
default: []
selector:
entity:
domain:
- input_button
multiple: false
include_time:
name: Use an Automatic Reoccuring Report?
description: Day and Time to automatically run the report
default: time_disabled
selector:
select:
options:
- label: Enabled
value: time_enabled
- label: Disabled
value: time_disabled
time:
name: Set Reoccuring Time
description: When to run automatic checks
default: "05:00:00"
selector:
time: {}
weekday_options:
name: Set Reoccuring Days
description: Must select at least one day when enabled
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"
description: Configure device monitoring parameters
collapsed: true
input:
include_hidden_entities:
name: Include Hidden Entities
description: Include entities marked as hidden (Visible disabled in entity settings)
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
description: Manually select entities to ignore
default: []
selector:
entity:
multiple: true
exclude_strings:
name: Text Pattern Exclusions
description: 'Exclude entities containing specific text
type in one string per line ... CaSe SenSative!
Examples:
sensor.phone
_battery
temperature'
default: ''
selector:
text:
multiline: true
notification_settings:
name: "📢 Notification Settings"
description: Configure alert preferences
collapsed: true
input:
include_easy_notify:
name: Device Notifications
default: disabled_easy_notify
selector:
select:
options:
- label: Disabled
value: disabled_easy_notify
- label: Enabled
value: enable_easy_notify
- label: Enabled with No Issues confirmation
value: enable_easy_okay_notify
notify_device:
name: Devices to Notify
description: Select at least one if Device Notifications are enabled
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: No Issues Confirmation Message
default: No offline devices detected
selector:
text: {}
notify_interruption_level:
name: iOS Interruption Level
description: only for iOS
default: active
selector:
select:
options:
- label: Default
value: active
- label: Critical Notifications
value: critical
- label: Time Sensitive Notifications
value: time-sensitive
- label: Quiet Notifications Without Waking Screen
value: passive
notify_sound:
name: iOS Notification Sound
description: only for iOS
default: ''
selector:
text: {}
notify_data:
name: Android Options
description: Android-specific notification 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
description: only for Android
default: ''
selector:
text: {}
include_persistent_notification:
name: Persistent Notification
default: disabled_persistent_notification
selector:
select:
options:
- label: Disabled
value: disabled_persistent_notification
- label: Enabled
value: enable_persistent_notification
- label: Enabled with No Issues confirmation
value: enable_persistent_okay_notification
display_options:
name: Display Options
description: What the notifications will show
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"
description: |
Configure notification actions
⚠️ **To-Do List Limitations:**
Home Assistant To-Do lists have a 255 character limit for descriptions.
This blueprint automatically:
1. Removes domain prefixes (sensor., binary_sensor., switch.)
2. Truncates to 252 characters
3. Adds "++" when truncated to indicate more content exists
collapsed: true
input:
auto_add_to_do:
name: Automatically Add to To-Do List
description: Automatically add offline devices to the To-Do list without requiring action button press
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
description: A unique tag to identify the action buttons for this notification. Helps prevent interference from other notifications.
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
description: Additional actions to run
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"
description: Additional conditions for automation
collapsed: true
input:
global_conditions:
name: Conditions
default: []
selector:
condition: {}
debug_settings:
name: "🐛 Debug Settings"
description: Configure debug options
collapsed: true
input:
include_logbook_debug:
name: Debug to Logbook?
description: Adds extra log entries for troubleshooting. Applies to ALL runs of the automation (button, schedule, AND debug mode)
default: enable_logbook_debug
selector:
select:
options:
- label: Enabled
value: enable_logbook_debug
- label: Disabled
value: disable_logbook_debug
include_debug:
name: Real-Time Debug Mode?
description: "⚠️FOR SHORT-TERM TROUBLESHOOTING ONLY\n\n Triggers on entity state changes to Offline or Unknown. Uses same exclusions as scheduled reports."
default: debug_disabled
selector:
select:
options:
- label: Enabled
value: debug_enabled
- label: Disabled
value: debug_disabled
debug_offline_delay:
name: Wait Time Confirmation - only for Real-Time Debug Mode
description: "Time to wait to ensure entity stays in Offline or Unknown state, not a momentary issue (hours:minutes:seconds)"
default: "00:03:00"
selector:
duration: {}
variables:
include_button: !input include_button
include_time: !input include_time
include_debug: !input include_debug
debug_offline_delay: !input debug_offline_delay
debug_offline_delay_seconds: >
{% set delay = debug_offline_delay %}
{% if delay is string %}
{# Parse HH:MM:SS 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 %}
{# Already a duration dict #}
{{ (delay.hours * 3600 + delay.minutes * 60 + delay.seconds) | int }}
{% else %}
{# Fallback to 3 minutes #}
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
include_logbook_debug: !input include_logbook_debug
exclude_regex: "{% if exclude_strings is defined %}\n {% set patterns = exclude_strings.split('\\n') | map('trim') | reject('eq', '') | list %}\n {{ patterns | join('|') if patterns else 'a^' }}\n{% else %}\n a^\n{% endif %}"
all_exclude_entities: "{{ exclude_entities if exclude_entities is defined else [] }}"
# To-Do description processing
to_do_description: >
{% set full_description = easy_notify_message | default('') %}
{% if full_description %}
{# Remove domain prefixes for cleaner display #}
{% set cleaned = full_description | regex_replace('(sensor\\.|binary_sensor\\.|switch\\.)', '') %}
{# Truncate to 252 chars and add ++ if truncated #}
{% if cleaned | length > 252 %}
{{ cleaned[:252] ~ '++' }}
{% else %}
{{ cleaned }}
{% endif %}
{% else %}
''
{% endif %}
# Core offline devices logic
_offline_devices: >
{% set exclude_entities_list = all_exclude_entities if all_exclude_entities is iterable else [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 }}
# Separate battery sensors and switches for notification options
_battery_sensors: >
{% set exclude_entities_list = all_exclude_entities if all_exclude_entities is iterable else [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 %}
{% if display_options == 'entity_ids' %}
{{ battery_entities | list }}
{% elif display_options == 'friendly_names' %}
{{ battery_entities | map('state_attr', 'friendly_name') | list }}
{% elif display_options == 'both' %}
{% set result = namespace(items=[]) %}
{% for e in battery_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 %}
_switches: >
{% set exclude_entities_list = all_exclude_entities if all_exclude_entities is iterable else [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 %}
{% if display_options == 'entity_ids' %}
{{ switches | list }}
{% elif display_options == 'friendly_names' %}
{{ switches | map('state_attr', 'friendly_name') | list }}
{% elif display_options == 'both' %}
{% set result = namespace(items=[]) %}
{% for e in switches %}
{% 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 %}
# Formatted messages for notifications
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 %}
# Message selection for notifications
easy_notify_message: >
{% if notify_message == 'all_sensors' %}
{{ all_sensors if all_sensors else '' }}
{% elif notify_message == 'sensors' %}
{{ sensors if sensors else '' }}
{% elif notify_message == 'switches' %}
{{ switches if switches else '' }}
{% else %}
''
{% endif %}
# Names only for To-Do list
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 %}
# Notification data templates
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: state
entity_id: !input button_entity
id: button_trigger
- platform: time
at: !input time
id: time_trigger
- platform: event
id: debug_trigger
event_type: state_changed
event_data:
new_state:
state:
- unknown
- unavailable
condition:
- condition: or
conditions:
- condition: and
conditions:
- condition: trigger
id: button_trigger
- condition: template
value_template: '{{ include_button == ''enable_button_trigger'' }}'
- 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: and
conditions: !input global_conditions
action:
- if:
condition: template
value_template: '{{ trigger.id == ''debug_trigger'' and include_debug == ''debug_enabled'' }}'
then:
- variables:
entity_id: '{{ trigger.event.data.entity_id }}'
new_state: '{{ trigger.event.data.new_state.state }}'
old_state: '{{ trigger.event.data.old_state.state if trigger.event.data.old_state else "" }}'
exclude_entities_list: "{{ all_exclude_entities if all_exclude_entities is iterable else [all_exclude_entities] }}"
entity_excluded: >
{% if entity_id in exclude_entities_list %}
true
{% elif exclude_regex != 'a^' and entity_id is search(exclude_regex) %}
true
{% elif include_hidden_entities == 'exclude_hidden' and is_hidden_entity(entity_id) %}
true
{% elif entity_id is search('cloud') %}
true
{% else %}
false
{% endif %}
is_battery_or_switch: >
{% if entity_id is search('^(sensor|binary_sensor)') %}
{% if device_class(entity_id) == 'battery' %}
true
{% else %}
false
{% endif %}
{% elif entity_id is search('^switch') %}
true
{% else %}
false
{% endif %}
valid_state_change: >
{% if new_state in ['unknown', 'unavailable'] and old_state not in ['unknown', 'unavailable'] %}
true
{% else %}
false
{% endif %}
- choose:
- conditions:
- condition: template
value_template: '{{ valid_state_change == true and entity_excluded == false and is_battery_or_switch == true }}'
sequence:
- alias: Wait for offline delay and check if entity is still offline
wait_template: >
{{ states(entity_id) not in ['unknown', 'unavailable'] }}
timeout: '{{ debug_offline_delay_seconds }}'
continue_on_timeout: false
- choose:
- conditions:
- condition: template
value_template: '{{ wait.completed }}'
sequence:
- alias: Entity came back online during delay period
service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Debug mode cancelled for {{ entity_id }} - came back online during delay period'
- stop: 'Debug mode cancelled - entity came back online'
- alias: Entity remained offline/unknown for the full delay period
service: logbook.log
data:
name: Offline Devices Report Debug
message: >
{% set td = as_timedelta(debug_offline_delay) if debug_offline_delay else as_timedelta('00:03:00') %}
Debug mode triggered for {{ entity_id }} - remained offline/unknown for {{ td.total_seconds() // 3600 }}h {{ (td.total_seconds() % 3600) // 60 }}m {{ td.total_seconds() % 60 }}s
- conditions: []
sequence:
- alias: Entity is excluded or not a battery/switch, stop automation
service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Debug mode ignored for {{ entity_id }} - excluded: {{ entity_excluded }}, is_battery_or_switch: {{ is_battery_or_switch }}, valid_state_change: {{ valid_state_change }}'
- stop: 'Debug mode cancelled - entity excluded or not battery/switch'
- if:
- condition: template
value_template: '{{ include_logbook_debug == ''enable_logbook_debug'' }}'
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 }}'
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: '{{ easy_notify_message != '''' }}'
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 == ''enable_logbook_debug'' }}'
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 == ''enable_logbook_debug'' }}'
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: '{{ easy_notify_message == '''' }}'
- 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: !input notify_okay_message
data: '{{ device_message_data }}'
- if:
- condition: template
value_template: '{{ include_logbook_debug == ''enable_logbook_debug'' }}'
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'' }}'
sequence:
- choose:
- alias: Offline devices have been found
conditions:
- condition: template
value_template: '{{ easy_notify_message != '''' }}'
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 == ''enable_logbook_debug'' }}'
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: '{{ easy_notify_message == '''' }}'
- condition: template
value_template: '{{ include_persistent_notification == ''enable_persistent_okay_notification'' }}'
sequence:
- service: persistent_notification.create
data:
title: "⛔ {{ notify_title }}"
message: !input notify_okay_message
notification_id: offline_devices_report_ok
- if:
- condition: template
value_template: '{{ include_logbook_debug == ''enable_logbook_debug'' }}'
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[0], ''unknown'') == false and is_state(to_do_list[0], ''unavailable'') == false }}'
sequence:
- service: todo.add_item
data:
entity_id: '{{ to_do_list[0] }}'
item: !input to_do_task_title
description: '{{ to_do_description }}'
continue_on_error: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == ''enable_logbook_debug'' }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Auto-added To-Do item to {{ to_do_list[0] }}: title={{ to_do_task_title }}, description length={{ to_do_description | length }}, truncated_description={{ to_do_description }}'
- 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[0], ''unknown'') == false and is_state(to_do_list[0], ''unavailable'') == false }}'
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 == ''enable_logbook_debug'' }}'
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[0] }}'
item: !input to_do_task_title
description: '{{ to_do_description }}'
continue_on_error: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == ''enable_logbook_debug'' }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Added To-Do item to {{ to_do_list[0] }}: title={{ to_do_task_title }}, description length={{ to_do_description | length }}, truncated_description={{ to_do_description }}, entity_state={{ states(to_do_list[0]) }}'
- 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 == ''enable_logbook_debug'' }}'
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[0] }}'
item: !input to_do_task_title
description: '{{ to_do_description }}'
continue_on_error: true
- if:
- condition: template
value_template: '{{ include_logbook_debug == ''enable_logbook_debug'' }}'
then:
- service: logbook.log
data:
name: Offline Devices Report Debug
message: 'Added To-Do item to {{ to_do_list[0] }}: title={{ to_do_task_title }}, description length={{ to_do_description | length }}, truncated_description={{ to_do_description }}, entity_state={{ states(to_do_list[0]) }}'
- 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 == ''enable_logbook_debug'' }}'
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 == ''enable_logbook_debug'' }}'
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'' }}'
sequence: !input custom_actions
mode: single
@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