# ============================================================================= # Immich Album Watcher Blueprint for Home Assistant # ============================================================================= # This blueprint monitors Immich album changes and sends notifications when # assets (photos/videos) are added to specified albums. # Designed to be used in pair with `Immich Album Watcher` integration. # # Features: # - Filter by hub/instance name (for multi-hub setups) # - Monitor specific albums by name (whitelist) # - Filter by asset type (track images only, videos only, or both) # - Send notifications to multiple targets simultaneously # - Customizable notification messages with template variables # - Separate templates for image and video assets # - Optional: include people detected in photos # - Optional: include detailed asset list with per-item formatting # - Support for multiple change types (assets added, removed, changed) # - Optional: send photos/videos as Telegram media attachments # - Optional: periodic summary notifications with album list and share URLs # # Event Data from Immich: # Assets Added/Removed/Changed Events: # - `hub_name`: Name of the Immich hub/instance that sent the event # - `album_id`: Album ID # - `album_name`: Album name # - `album_url`: Public URL to view the album (only if album has shared link) # - `change_type`: Type of change (assets_added, assets_removed, changed) # - `added_count`: Number of assets added # - `removed_count`: Number of assets removed # - `added_assets`: List of added assets (see Added Assets Fields below) # - `removed_assets`: List of removed asset IDs # - `people`: List of all people detected in the album # # Album Renamed Event: # - `hub_name`: Name of the Immich hub/instance that sent the event # - `album_id`: Album ID # - `old_name`: Previous album name # - `new_name`: New album name # - `album_url`: Public URL to view the album (only if album has shared link) # # Album Deleted Event: # - `hub_name`: Name of the Immich hub/instance that sent the event # - `album_id`: Album ID # - `album_name`: Album name that was deleted # # Added Assets Fields (each item in `added_assets`): # - `id`: Unique asset ID # - `asset_type`: Type of asset (IMAGE or VIDEO) # - `asset_filename`: Original filename of the asset # - `asset_description`: User-provided description of the asset # - `asset_created`: Date/time when the asset was originally created # - `asset_owner`: Display name of the user who owns the asset # - `asset_owner_id`: Unique ID of the user who owns the asset # - `asset_url`: Public URL to view the asset (only if album has shared link) # - `asset_download_url`: Direct download URL for the asset (preferred for Telegram images) # - `asset_playback_url`: Playback/streaming URL for videos (preferred for Telegram videos) # - `people`: List of people detected in this specific asset # - `asset_is_favorite`: Whether the asset is marked as favorite (true or false) # - `asset_rating`: User rating of the asset (1-5 stars, or null if not rated) # # Message Template Variables: # All message templates support these placeholder variables (use single braces): # - `{album_name}` - Name of the album # - `{album_url}` - Public URL to view the album (empty if no shared link) # - `{added_count}` - Number of assets added # - `{removed_count}` - Number of assets removed # - `{people}` - Comma-separated list of people detected # - `{assets}` - Formatted list of added assets (using asset item template) # - `{video_warning}` - Warning about video size limits (Telegram only, empty otherwise) # - `{common_date}` - Common date formatted with template if all assets share same date, empty otherwise # # Asset Item Template Variables (for image/video templates): # These variables can be used in the image and video asset templates. # Also used for Telegram media captions. # - `{filename}` - Original filename of the asset # - `{description}` - User-provided description of the asset # - `{type}` - Asset type (IMAGE or VIDEO) # - `{created}` - Creation date/time (always shown) # - `{created_if_unique}` - Creation date/time formatted with template if dates differ, empty if all same # - `{owner}` - Owner display name # - `{url}` - Public URL to view the asset (empty if no shared link) # - `{people}` - People detected in this asset # - `{album_name}` - Name of the album # - `{is_favorite}` - Favorite indicator (using template) if asset is favorite, empty otherwise # - `{rating}` - User rating (1-5) or empty if not rated # # Telegram Media Attachments: # When enabled, photos/videos are sent as media attachments to Telegram # using the immich_album_watcher.send_telegram_notification service. # # Supports multiple recipients via two methods: # 1. Notify Entities: Select Telegram notify entities. Chat ID is extracted # from the friendly name. Format: "Name (123456789)" # 2. Input Text Entities: Select input_text entities containing chat IDs for groups/channels. # # Requirements: # - Immich Album Watcher integration must be configured with Telegram bot token # - Immich album must have a shared link (to generate public asset URLs) # # Behavior: # - First sends a text notification message to each Telegram chat # - Then sends photos/videos as media groups (albums) as replies to that message # - Media is downloaded from Immich and uploaded to Telegram (bypasses CORS) # - Large media lists are automatically split into multiple groups (2-10 items per group) # # Limitations: # - Only assets with valid public URLs will be sent # - Telegram has a 50 MB file size limit for media # - Optional video warning can be shown when videos are present # - Media captions use the Image/Video Asset Templates # # Periodic Summary: # Sends a summary notification of tracked albums at regular intervals. # Album names and share URLs are automatically read from the Album ID Entity's # `album_name` and `share_url` attributes (if available). # # Summary Message Template Variables: # - `{albums}` - Formatted list of albums (using album item template) # - `{album_count}` - Number of tracked albums # # Album Item Template Variables: # - `{album_name}` - Name of the album # - `{album_url}` - Share URL for the album # # Testing Periodic Summary: # To test the periodic summary without waiting for the scheduled time, # fire this event from Developer Tools > Events: # Event type: immich_album_watcher_test_periodic_summary # # Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com) # ============================================================================= blueprint: name: "Custom: Immich Album Watcher" description: > Sends notifications to multiple targets when photos/videos are added to specified Immich albums. Monitors Immich album watcher events and filters by album name. domain: automation # =========================================================================== # INPUT CONFIGURATION # =========================================================================== input: # ------------------------------------------------------------------------- # Hub & Album Configuration # ------------------------------------------------------------------------- albums_group: name: "Hub & Albums" collapsed: false input: hub_names: name: Hub Names to Track description: > List of Immich hub/instance names to monitor. Only events from matching hubs will trigger notifications. Leave empty to track all hubs. default: [] selector: text: multiple: true album_id_entities: name: Album ID Entities description: > List of sensor or input_text entities containing album IDs to monitor. Only albums matching these IDs will trigger notifications. Leave empty to track all albums. Album IDs are stable and won't change if albums get renamed. Album names and share URLs are automatically read from the entity's `album_name` and `share_url` attributes for periodic summary notifications. default: [] selector: entity: domain: - sensor - input_text multiple: true track_assets_added: name: Track Assets Added description: "Send notifications when assets are added to albums" default: true selector: boolean: track_assets_removed: name: Track Assets Removed description: "Send notifications when assets are removed from albums" default: false selector: boolean: track_images: name: Track Images description: "Include IMAGE assets in notifications" default: true selector: boolean: track_videos: name: Track Videos description: "Include VIDEO assets in notifications" default: true selector: boolean: track_album_renamed: name: Track Album Renamed description: "Send notifications when albums are renamed" default: true selector: boolean: track_album_deleted: name: Track Album Deleted description: "Send notifications when albums are deleted" default: true selector: boolean: # ------------------------------------------------------------------------- # Notification Configuration # ------------------------------------------------------------------------- notification_group: name: "Notification" collapsed: false input: notify_targets: name: Notification Targets description: "Notification service entities to send messages to (select one or more)" selector: entity: domain: notify multiple: true include_people: name: Include People in Notification description: "Include list of detected people in the notification message" default: true selector: boolean: include_asset_details: name: Include Asset Details description: "Include formatted list of added assets in the notification" default: false selector: boolean: max_assets_to_show: name: Maximum Assets to Show in Text description: > Maximum number of assets to list in the text notification message (the `{assets}` placeholder). Only applies when "Include Asset Details" is enabled. Set to 0 for unlimited. Excess assets show as "...and X more". Note: This is separate from "Maximum Media to Send" which controls the actual photo/video attachments sent to Telegram. default: 5 selector: number: min: 0 max: 50 mode: slider # ------------------------------------------------------------------------- # Message Templates # ------------------------------------------------------------------------- messages_group: name: "Messages" collapsed: true input: message_assets_added: name: "Assets Added Message" description: > Message sent when assets are added to an album. Variables: `{album_name}`, `{album_url}`, `{added_count}`, `{people}`, `{assets}`, `{video_warning}`, `{common_date}` default: "šŸ“· {added_count} new photo(s) added to album \"{album_name}\".{people}{assets}{video_warning}" selector: text: multiline: true message_assets_removed: name: "Assets Removed Message" description: > Message sent when assets are removed from an album. Variables: `{album_name}`, `{album_url}`, `{removed_count}` default: "šŸ—‘ļø {removed_count} photo(s) removed from album \"{album_name}\"." selector: text: multiline: true message_album_renamed: name: "Album Renamed Message" description: > Message sent when an album is renamed. Variables: `{old_name}`, `{new_name}`, `{album_url}` default: "āœļø Album \"{old_name}\" renamed to \"{new_name}\"." selector: text: multiline: true message_album_deleted: name: "Album Deleted Message" description: > Message sent when an album is deleted. Variables: `{album_name}` default: "šŸ—‘ļø Album \"{album_name}\" was deleted." selector: text: multiline: true message_people_format: name: "People Format" description: > Format for the people list in notifications. Use `{people}` as the placeholder for the comma-separated list. Leave empty to just show people names without prefix. default: " People: {people}." selector: text: multiline: true message_asset_image: name: "Image Asset Template" description: > Template for IMAGE assets in the list. Also used for Telegram media captions. Variables: `{filename}`, `{description}`, `{type}`, `{created}`, `{created_if_unique}`, `{owner}`, `{url}`, `{people}`, `{album_name}`, `{is_favorite}`, `{rating}` default: "\n • šŸ–¼ļø {filename}" selector: text: multiline: true message_asset_video: name: "Video Asset Template" description: > Template for VIDEO assets in the list. Also used for Telegram media captions. Variables: `{filename}`, `{description}`, `{type}`, `{created}`, `{created_if_unique}`, `{owner}`, `{url}`, `{people}`, `{album_name}`, `{is_favorite}`, `{rating}` default: "\n • šŸŽ¬ {filename}" selector: text: multiline: true message_assets_format: name: "Assets List Format" description: > Format wrapper for the assets list. Use `{assets}` as placeholder for the formatted asset items. Use `{more_count}` for number of additional assets not shown. default: "\nAssets:{assets}" selector: text: multiline: true message_assets_more: name: "More Assets Message" description: > Message appended when there are more assets than the maximum shown. Use `{more_count}` for the number of additional assets. default: "\n • ...and {more_count} more" selector: text: multiline: true date_format: name: "Date Format" description: > Format for displaying asset creation dates. Uses Python strftime format codes (e.g., %Y-%m-%d %H:%M). Note: Month names (%b, %B) use system locale (typically English). Use numeric format (%m) for locale-independent dates. default: "%d.%m.%Y, %H:%M UTC" selector: text: common_date_template: name: "Common Date Template" description: > Template for showing the common date in the header message when all assets share the same date. Use `{date}` as the placeholder for the formatted date. Leave empty to just show the raw date without wrapper. default: " from {date}" selector: text: date_if_unique_template: name: "Date If Unique Template" description: > Template for showing dates in asset items when assets have different dates. Use `{date}` as the placeholder for the formatted date. Leave empty to just show the raw date without wrapper. default: " ({date})" selector: text: favorite_indicator_template: name: "Favorite Indicator Template" description: > Template for showing favorite indicator in asset items. This will be shown when an asset is marked as favorite. Leave empty to not show favorite indicators. default: "ā¤ļø" selector: text: # ------------------------------------------------------------------------- # Telegram Media Attachments # ------------------------------------------------------------------------- telegram_group: name: "Telegram Media" description: "Send photos/videos as Telegram attachments (requires public asset URLs)" collapsed: true input: send_telegram_media: name: Send Media to Telegram description: > Send photos/videos as media attachments to Telegram using the immich_album_watcher.send_telegram_notification service. Media is downloaded from Immich and uploaded to Telegram, bypassing CORS restrictions. Large media lists are automatically split into multiple groups. default: false selector: boolean: telegram_notify_targets: name: Telegram Notify Targets description: > Select Telegram notify entities to send media to. The chat ID will be extracted from the entity's friendly name. Expected format: "Name (123456789)" where the number in parentheses is the chat ID. default: [] selector: entity: domain: notify multiple: true telegram_chat_id_entities: name: Telegram Chat ID Entities description: > List of input_text entities containing Telegram chat IDs to send media to. Use this for chat IDs not associated with notify entities, or as a fallback if friendly name parsing doesn't work. Can be user IDs (positive) or group IDs (negative). Chat IDs are read from the entity states. default: [] selector: entity: domain: - input_text multiple: true max_media_to_send: name: Maximum Media to Send description: > Maximum number of photos/videos to send as attachments. Set to 0 for unlimited. default: 50 selector: number: min: 0 max: 50 mode: slider max_media_per_group: name: Maximum Media per Group description: > Maximum number of items in a single media group (album). Telegram allows 2-10 items per group. Large media lists are automatically split into multiple groups. default: 10 selector: number: min: 2 max: 10 mode: slider telegram_media_delay: name: Delay Between Media Groups (ms) description: > Delay in milliseconds between sending multiple media groups. Helps avoid rate limiting when large media lists are split. Set to 0 for no delay. default: 500 selector: number: min: 0 max: 60000 step: 100 unit_of_measurement: ms mode: slider telegram_video_warning: name: Video Warning Message description: > Warning message appended to notifications when videos are present. Telegram has a 50 MB file size limit for media sent via URL, so large videos may fail to send. Leave empty to disable warning. default: "\n\nāš ļø Note: Videos may not be sent due to Telegram's 50 MB file size limit." selector: text: multiline: true telegram_disable_url_preview: name: Disable URL Preview description: > Disable link previews in Telegram text messages. When enabled, URLs in the message won't show a preview card. default: false selector: boolean: telegram_send_large_photos_as_documents: name: Send Large Photos as Documents description: > Send large photos as documents instead of compressed images. This preserves the original quality but may result in larger file sizes. Only applies to photos sent as Telegram media attachments. default: false selector: boolean: # ------------------------------------------------------------------------- # Periodic Summary # ------------------------------------------------------------------------- periodic_group: name: "Periodic Summary" description: "Send periodic notifications with a summary of tracked albums" collapsed: true input: enable_periodic_summary: name: Enable Periodic Summary description: > Send a summary notification of tracked albums at regular intervals. Requires Album Names and Album Share URLs to be configured. default: false selector: boolean: periodic_interval_hours: name: Summary Interval (Hours) description: "How often to send the summary notification (in hours)" default: 24 selector: number: min: 1 max: 168 unit_of_measurement: hours mode: slider periodic_start_hour: name: Summary Start Hour description: > Hour of day (0-23) when the first summary should be sent. Subsequent summaries are sent at this hour plus the interval. Example: Start hour 12 with 24h interval = daily at 12:00. default: 12 selector: number: min: 0 max: 23 unit_of_measurement: hour mode: slider periodic_summary_message: name: Summary Message Template description: > Message template for the periodic summary. Variables: `{albums}` (formatted list of albums), `{album_count}` (number of albums) default: "šŸ“‹ Tracked Albums Summary ({album_count} albums):{albums}" selector: text: multiline: true periodic_album_template: name: Album Item Template description: > Template for each album in the summary list. Variables: `{album_name}`, `{album_id}`, `{album_url}` default: "\n • {album_name}: {album_url}" selector: text: multiline: true # ------------------------------------------------------------------------- # Debug # ------------------------------------------------------------------------- debug: name: "Debug" collapsed: true input: enable_debug_notifications: name: Enable Debug Notifications description: > Send persistent notifications for debugging automation behavior. Shows event data and filtering decisions. default: false selector: boolean: # ============================================================================= # AUTOMATION MODE # ============================================================================= # Single mode to process one event at a time mode: single # ============================================================================= # TRIGGERS # ============================================================================= trigger: # Monitor Immich album watcher events - platform: event event_type: immich_album_watcher_assets_added id: "assets_added" - platform: event event_type: immich_album_watcher_assets_removed id: "assets_removed" - platform: event event_type: immich_album_watcher_changed id: "changed" - platform: event event_type: immich_album_watcher_album_renamed id: "album_renamed" - platform: event event_type: immich_album_watcher_album_deleted id: "album_deleted" # Periodic summary trigger (time pattern) # Note: Uses template to dynamically set interval, but HA requires static value # so we trigger every hour and check interval in conditions - platform: time_pattern hours: "/1" id: "periodic_summary" # Manual trigger for testing periodic summary # Fire this event from Developer Tools > Events to test periodic summary immediately # Event type: immich_album_watcher_test_periodic_summary - platform: event event_type: immich_album_watcher_test_periodic_summary id: "periodic_summary" # ============================================================================= # VARIABLES # ============================================================================= variables: # --------------------------------------------------------------------------- # Input Variables # --------------------------------------------------------------------------- hub_names: !input hub_names album_id_entities: !input album_id_entities # Read album IDs from entity states album_ids: > {% set ns = namespace(ids = []) %} {% for entity_id in album_id_entities %} {% set value = states(entity_id) | default('') | trim %} {% if value | length > 0 and value not in ['unknown', 'unavailable'] %} {% set ns.ids = ns.ids + [value] %} {% endif %} {% endfor %} {{ ns.ids }} notify_targets: !input notify_targets include_people: !input include_people include_asset_details: !input include_asset_details max_assets_to_show: !input max_assets_to_show track_assets_added: !input track_assets_added track_assets_removed: !input track_assets_removed track_images: !input track_images track_videos: !input track_videos track_album_renamed: !input track_album_renamed track_album_deleted: !input track_album_deleted enable_debug_notifications: !input enable_debug_notifications # Telegram Media Settings send_telegram_media: !input send_telegram_media telegram_notify_targets: !input telegram_notify_targets telegram_chat_id_entities: !input telegram_chat_id_entities max_media_to_send: !input max_media_to_send max_media_per_group: !input max_media_per_group telegram_media_delay: !input telegram_media_delay telegram_video_warning_template: !input telegram_video_warning telegram_disable_url_preview: !input telegram_disable_url_preview telegram_send_large_photos_as_documents: !input telegram_send_large_photos_as_documents # Periodic Summary Settings enable_periodic_summary: !input enable_periodic_summary periodic_interval_hours: !input periodic_interval_hours periodic_start_hour: !input periodic_start_hour periodic_summary_message_template: !input periodic_summary_message periodic_album_template: !input periodic_album_template # Parse chat IDs from notify entity friendly names (format: "Name (123456789)") # and combine with chat IDs from input_text entities telegram_chat_ids: > {% set ns = namespace(ids = []) %} {# Extract chat IDs from notify entity friendly names #} {% for entity_id in telegram_notify_targets %} {% set friendly_name = state_attr(entity_id, 'friendly_name') | default('') %} {# Match number in parentheses at the end: "Diana (350705409)" -> "350705409" #} {% set match = friendly_name | regex_findall('\\((-?\\d+)\\)\\s*$') %} {% if match | length > 0 %} {% set ns.ids = ns.ids + [match[0]] %} {% endif %} {% endfor %} {# Read chat IDs from input_text entity states #} {% for entity_id in telegram_chat_id_entities %} {% set value = states(entity_id) | default('') | trim %} {% if value | length > 0 and value not in ['unknown', 'unavailable'] and value not in ns.ids %} {% set ns.ids = ns.ids + [value] %} {% endif %} {% endfor %} {{ ns.ids }} # --------------------------------------------------------------------------- # Message Templates # --------------------------------------------------------------------------- message_assets_added_template: !input message_assets_added message_assets_removed_template: !input message_assets_removed message_album_renamed_template: !input message_album_renamed message_album_deleted_template: !input message_album_deleted message_people_format_template: !input message_people_format message_asset_image_template: !input message_asset_image message_asset_video_template: !input message_asset_video message_assets_format_template: !input message_assets_format message_assets_more_template: !input message_assets_more date_format: !input date_format common_date_template: !input common_date_template date_if_unique_template: !input date_if_unique_template favorite_indicator_template: !input favorite_indicator_template # --------------------------------------------------------------------------- # Event Data # --------------------------------------------------------------------------- event_hub_name: "{{ trigger.event.data.hub_name | default('') }}" event_album_name: "{{ trigger.event.data.album_name | default('Unknown Album') }}" event_album_id: "{{ trigger.event.data.album_id | default('') }}" event_album_url: "{{ trigger.event.data.album_url | default('') }}" event_change_type: "{{ trigger.event.data.change_type | default(trigger.id) }}" event_added_count: "{{ trigger.event.data.added_count | default(0) | int }}" event_removed_count: "{{ trigger.event.data.removed_count | default(0) | int }}" event_people: "{{ trigger.event.data.people | default([]) }}" event_added_assets: "{{ trigger.event.data.added_assets | default([]) }}" # Album renamed event data event_old_name: "{{ trigger.event.data.old_name | default('') }}" event_new_name: "{{ trigger.event.data.new_name | default('') }}" # --------------------------------------------------------------------------- # Computed Values # --------------------------------------------------------------------------- # Check if this hub should be tracked (empty list = track all) is_hub_tracked: > {{ hub_names | length == 0 or event_hub_name in hub_names }} # Check if this album should be tracked (empty list = track all) is_album_tracked: > {{ album_ids | length == 0 or event_album_id in album_ids }} # Format people list for notification people_list: > {% if include_people and event_people | length > 0 %} {% set people_str = event_people | join(', ') %} {{ message_people_format_template | replace('{people}', people_str) }} {% else %} {{ '' }} {% endif %} # Filter assets based on track_images/track_videos settings filtered_assets: > {% set ns = namespace(assets = []) %} {% for asset in event_added_assets %} {% if (asset.asset_type == 'IMAGE' and track_images) or (asset.asset_type == 'VIDEO' and track_videos) %} {% set ns.assets = ns.assets + [asset] %} {% endif %} {% endfor %} {{ ns.assets }} # Count of filtered assets (for notifications) filtered_added_count: "{{ filtered_assets | length }}" # Compute unique dates from filtered assets unique_dates: > {% set ns = namespace(dates = []) %} {% for asset in filtered_assets %} {% set raw_date = asset.asset_created | default('') %} {% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %} {% set formatted_date = dt.strftime(date_format) if dt else '' %} {% if formatted_date | length > 0 and formatted_date not in ns.dates %} {% set ns.dates = ns.dates + [formatted_date] %} {% endif %} {% endfor %} {{ ns.dates }} # Check if all assets share the same date all_dates_same: "{{ unique_dates | length == 1 }}" # Common date for header (formatted with template if all same, empty otherwise) common_date: > {% if unique_dates | length == 1 %} {{ common_date_template | replace('{date}', unique_dates[0]) }} {% else %} {{ '' }} {% endif %} # Format assets list for notification assets_list: > {% if include_asset_details and filtered_assets | length > 0 %} {% set ns = namespace(items = '') %} {% set max_items = max_assets_to_show if max_assets_to_show > 0 else filtered_assets | length %} {% set assets_to_show = filtered_assets[:max_items] %} {% for asset in assets_to_show %} {% set asset_template = message_asset_video_template if asset.asset_type == 'VIDEO' else message_asset_image_template %} {% set raw_date = asset.asset_created | default('') %} {% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %} {% set formatted_date = dt.strftime(date_format) if dt else '' %} {% set created_if_unique = '' if unique_dates | length == 1 else (date_if_unique_template | replace('{date}', formatted_date)) %} {% set is_favorite = favorite_indicator_template if asset.asset_is_favorite | default(false) else '' %} {% set rating = asset.asset_rating | default('') | string if asset.asset_rating not in [none, ''] else '' %} {% set item = asset_template | replace('{filename}', asset.asset_filename | default('Unknown')) | replace('{description}', asset.asset_description | default('')) | replace('{type}', asset.asset_type | default('Unknown')) | replace('{created}', formatted_date) | replace('{created_if_unique}', created_if_unique) | replace('{owner}', asset.asset_owner | default('Unknown')) | replace('{url}', asset.asset_url | default('')) | replace('{people}', (asset.people | default([])) | join(', ')) | replace('{album_name}', event_album_name) | replace('{is_favorite}', is_favorite) | replace('{rating}', rating) %} {% set ns.items = ns.items ~ item %} {% endfor %} {% set more_count = filtered_assets | length - max_items %} {% if more_count > 0 %} {% set ns.items = ns.items ~ message_assets_more_template | replace('{more_count}', more_count | string) %} {% endif %} {{ message_assets_format_template | replace('{assets}', ns.items) }} {% else %} {{ '' }} {% endif %} # Check if filtered assets contain any videos (for Telegram warning) has_videos_in_assets: > {{ filtered_assets | selectattr('asset_type', 'equalto', 'VIDEO') | list | length > 0 }} # Video warning text (only populated when Telegram media is enabled and videos are present) video_warning_text: >- {% if send_telegram_media and has_videos_in_assets and telegram_video_warning_template | length > 0 %}{{ telegram_video_warning_template }}{% else %}{{ '' }}{% endif %} # Filter assets that have valid URLs (for Telegram media) # URL preference: playback_url (videos) > download_url > asset_url (viewer) assets_with_urls: > {% set ns = namespace(assets = []) %} {% for asset in filtered_assets %} {% set playback_url = asset.asset_playback_url | default('') %} {% set download_url = asset.asset_download_url | default('') %} {% set view_url = asset.asset_url | default('') %} {% if playback_url | length > 0 or download_url | length > 0 or view_url | length > 0 %} {% set ns.assets = ns.assets + [asset] %} {% endif %} {% endfor %} {{ ns.assets }} # Determine if this event type should trigger a notification # For added assets, check filtered count (respects track_images/track_videos) should_notify: > {% if trigger.id == 'assets_added' and track_assets_added and filtered_added_count | int > 0 %} {{ true }} {% elif trigger.id == 'assets_removed' and track_assets_removed and event_removed_count > 0 %} {{ true }} {% elif trigger.id == 'changed' and (track_assets_added or track_assets_removed) %} {% if track_assets_added and filtered_added_count | int > 0 %} {{ true }} {% elif track_assets_removed and event_removed_count > 0 %} {{ true }} {% else %} {{ false }} {% endif %} {% elif trigger.id == 'album_renamed' and track_album_renamed %} {{ true }} {% elif trigger.id == 'album_deleted' and track_album_deleted %} {{ true }} {% else %} {{ false }} {% endif %} # --------------------------------------------------------------------------- # Periodic Summary Variables # --------------------------------------------------------------------------- # Check if periodic summary should run (every N hours starting from start_hour) # Formula: (current_hour - start_hour) % interval == 0 # Example: start_hour=12, interval=24 → sends at 12:00 daily # Example: start_hour=9, interval=12 → sends at 09:00 and 21:00 # Manual test event (immich_album_watcher_test_periodic_summary) bypasses time check should_send_periodic_summary: > {% if trigger.id == 'periodic_summary' and enable_periodic_summary %} {% if trigger.platform == 'event' and trigger.event.event_type == 'immich_album_watcher_test_periodic_summary' %} {{ true }} {% else %} {% set current_hour = now().hour %} {% set start = periodic_start_hour | int %} {% set interval = periodic_interval_hours | int %} {{ (current_hour - start) % interval == 0 }} {% endif %} {% else %} {{ false }} {% endif %} # Format the albums list for periodic summary # Reads album name and share URL from album ID entity's attributes periodic_albums_list: > {% set ns = namespace(items = '') %} {% for i in range(album_id_entities | length) %} {% set entity_id = album_id_entities[i] %} {% set id = states(entity_id) | default('') %} {% if id | length > 0 and id not in ['unknown', 'unavailable'] %} {% set name_attr = state_attr(entity_id, 'album_name') %} {% set name = name_attr if name_attr not in [none, '', 'unknown', 'unavailable'] else id %} {% set url_attr = state_attr(entity_id, 'share_url') %} {% set url = url_attr if url_attr not in [none, 'unknown', 'unavailable'] else '' %} {% set item = periodic_album_template | replace('{album_name}', name) | replace('{album_id}', id) | replace('{album_url}', url) %} {% set ns.items = ns.items ~ item %} {% endif %} {% endfor %} {{ ns.items }} # Complete periodic summary message periodic_summary_formatted: > {{ periodic_summary_message_template | replace('{albums}', periodic_albums_list) | replace('{album_count}', album_ids | length | string) }} # ============================================================================= # CONDITIONS # ============================================================================= condition: # Allow through if: # 1. Periodic summary trigger and should send periodic summary # 2. Event trigger and passes all event-based checks - condition: template value_template: > {% if trigger.id == 'periodic_summary' %} {{ should_send_periodic_summary }} {% else %} {{ is_hub_tracked and is_album_tracked and should_notify }} {% endif %} # ============================================================================= # ACTIONS # ============================================================================= action: # --------------------------------------------------------------------------- # PERIODIC SUMMARY: Send summary of tracked albums (time-based trigger) # --------------------------------------------------------------------------- - choose: - conditions: - condition: template value_template: "{{ trigger.id == 'periodic_summary' }}" sequence: # Send periodic summary to notification targets - service: notify.send_message target: entity_id: "{{ notify_targets }}" data: message: "{{ periodic_summary_formatted }}" # Also send to Telegram if configured - choose: - conditions: - condition: template value_template: "{{ send_telegram_media and telegram_chat_ids | length > 0 and album_id_entities | length > 0 }}" sequence: - repeat: for_each: "{{ telegram_chat_ids }}" sequence: - service: immich_album_watcher.send_telegram_notification response_variable: telegram_periodic_response target: entity_id: "{{ album_id_entities[0] }}" data: chat_id: "{{ repeat.item }}" caption: "{{ periodic_summary_formatted }}" disable_web_page_preview: "{{ telegram_disable_url_preview }}" # Stop here for periodic summary - don't continue to event-based actions - stop: "Periodic summary sent" # --------------------------------------------------------------------------- # DEBUG: Log event data (enabled via Debug input section) # --------------------------------------------------------------------------- - choose: - conditions: - condition: template value_template: "{{ enable_debug_notifications }}" sequence: - service: persistent_notification.create data: title: "Immich Album Watcher Debug" message: > **Event Data:** - Trigger ID: {{ trigger.id }} - Hub: {{ event_hub_name }} - Album: {{ event_album_name }} - Album ID: {{ event_album_id }} - Album URL: {{ event_album_url }} - Change Type: {{ event_change_type }} **Asset Counts:** - Added Count (raw): {{ event_added_count }} - Added Count (filtered): {{ filtered_added_count }} - Removed Count: {{ event_removed_count }} - Added Assets: {{ event_added_assets | length }} **People:** - {{ event_people | join(', ') if event_people | length > 0 else '(none)' }} **Filtering:** - Is Hub Tracked: {{ is_hub_tracked }} - Is Album Tracked: {{ is_album_tracked }} - Should Notify: {{ should_notify }} - Track Images: {{ track_images }} - Track Videos: {{ track_videos }} - Include Asset Details: {{ include_asset_details }} **Telegram:** - Enabled: {{ send_telegram_media }} - Chat IDs: {{ telegram_chat_ids | join(', ') if telegram_chat_ids | length > 0 else '(none)' }} - Album Shared: {{ 'Yes' if event_album_url | length > 0 else 'No (media cannot be sent)' }} - Assets with URLs: {{ assets_with_urls | length }} # --------------------------------------------------------------------------- # Send Notification Based on Event Type # --------------------------------------------------------------------------- - choose: # --------------------------------------------------------------------- # CASE 1: Assets Added # --------------------------------------------------------------------- - conditions: - condition: template value_template: "{{ trigger.id == 'assets_added' and track_assets_added and filtered_added_count | int > 0 }}" sequence: - variables: message: > {% set tpl = message_assets_added_template %} {{ tpl | replace('{album_name}', event_album_name) | replace('{album_url}', event_album_url) | replace('{added_count}', filtered_added_count | string) | replace('{people}', people_list) | replace('{assets}', assets_list) | replace('{video_warning}', video_warning_text) | replace('{common_date}', common_date) }} - service: notify.send_message target: entity_id: "{{ notify_targets }}" data: message: "{{ message }}" # --------------------------------------------------------------------- # CASE 2: Assets Removed # --------------------------------------------------------------------- - conditions: - condition: template value_template: "{{ trigger.id == 'assets_removed' and track_assets_removed and event_removed_count > 0 }}" sequence: - variables: message: > {% set tpl = message_assets_removed_template %} {{ tpl | replace('{album_name}', event_album_name) | replace('{album_url}', event_album_url) | replace('{removed_count}', event_removed_count | string) }} - service: notify.send_message target: entity_id: "{{ notify_targets }}" data: message: "{{ message }}" # --------------------------------------------------------------------- # CASE 3: Changed (handles both added and removed in one event) # --------------------------------------------------------------------- - conditions: - condition: template value_template: "{{ trigger.id == 'changed' }}" sequence: # Send added notification if applicable - choose: - conditions: - condition: template value_template: "{{ track_assets_added and filtered_added_count | int > 0 }}" sequence: - variables: message: > {% set tpl = message_assets_added_template %} {{ tpl | replace('{album_name}', event_album_name) | replace('{album_url}', event_album_url) | replace('{added_count}', filtered_added_count | string) | replace('{people}', people_list) | replace('{assets}', assets_list) | replace('{video_warning}', video_warning_text) }} - service: notify.send_message target: entity_id: "{{ notify_targets }}" data: message: "{{ message }}" # Send removed notification if applicable - choose: - conditions: - condition: template value_template: "{{ track_assets_removed and event_removed_count > 0 }}" sequence: - variables: message: > {% set tpl = message_assets_removed_template %} {{ tpl | replace('{album_name}', event_album_name) | replace('{album_url}', event_album_url) | replace('{removed_count}', event_removed_count | string) }} - service: notify.send_message target: entity_id: "{{ notify_targets }}" data: message: "{{ message }}" # --------------------------------------------------------------------- # CASE 4: Album Renamed # --------------------------------------------------------------------- - conditions: - condition: template value_template: "{{ trigger.id == 'album_renamed' and track_album_renamed }}" sequence: - variables: message: > {% set tpl = message_album_renamed_template %} {{ tpl | replace('{old_name}', event_old_name) | replace('{new_name}', event_new_name) | replace('{album_url}', event_album_url) }} - service: notify.send_message target: entity_id: "{{ notify_targets }}" data: message: "{{ message }}" # Also send to Telegram if configured - choose: - conditions: - condition: template value_template: "{{ send_telegram_media and telegram_chat_ids | length > 0 and album_id_entities | length > 0 }}" sequence: - repeat: for_each: "{{ telegram_chat_ids }}" sequence: - service: immich_album_watcher.send_telegram_notification response_variable: telegram_renamed_response target: entity_id: "{{ album_id_entities[0] }}" data: chat_id: "{{ repeat.item }}" caption: "{{ message }}" disable_web_page_preview: "{{ telegram_disable_url_preview }}" # --------------------------------------------------------------------- # CASE 5: Album Deleted # --------------------------------------------------------------------- - conditions: - condition: template value_template: "{{ trigger.id == 'album_deleted' and track_album_deleted }}" sequence: - variables: message: > {% set tpl = message_album_deleted_template %} {{ tpl | replace('{album_name}', event_album_name) }} - service: notify.send_message target: entity_id: "{{ notify_targets }}" data: message: "{{ message }}" # Also send to Telegram if configured - choose: - conditions: - condition: template value_template: "{{ send_telegram_media and telegram_chat_ids | length > 0 and album_id_entities | length > 0 }}" sequence: - repeat: for_each: "{{ telegram_chat_ids }}" sequence: - service: immich_album_watcher.send_telegram_notification response_variable: telegram_deleted_response target: entity_id: "{{ album_id_entities[0] }}" data: chat_id: "{{ repeat.item }}" caption: "{{ message }}" disable_web_page_preview: "{{ telegram_disable_url_preview }}" # --------------------------------------------------------------------------- # Send Media to Telegram (if enabled) # --------------------------------------------------------------------------- # Sends photos/videos to Telegram using telegram_bot integration. # Two modes available: # 1. Individual messages (default) - uses telegram_bot.send_photo/send_video # 2. Media group (album) - uses rest_command to call Telegram API directly - choose: - conditions: - condition: template value_template: > {{ send_telegram_media and telegram_chat_ids | length > 0 and assets_with_urls | length > 0 and (trigger.id == 'assets_added' or (trigger.id == 'changed' and filtered_added_count | int > 0)) }} sequence: - variables: # Calculate how many media items to send media_limit: > {% if max_media_to_send > 0 %} {{ max_media_to_send }} {% else %} {{ assets_with_urls | length }} {% endif %} media_to_send: "{{ assets_with_urls[:media_limit | int] }}" # Debug logging for Telegram media - choose: - conditions: - condition: template value_template: "{{ enable_debug_notifications }}" sequence: - service: persistent_notification.create data: title: "Immich Album Watcher - Telegram Media Debug" message: > **Telegram Configuration:** - Chat IDs: {{ telegram_chat_ids | join(', ') }} - Max items per group: {{ max_media_per_group }} **Media to Send ({{ media_to_send | length }} items):** {% for asset in media_to_send %} {% set playback_url = asset.asset_playback_url | default('') %} {% set download_url = asset.asset_download_url | default('') %} {% set view_url = asset.asset_url | default('') %} {% if asset.asset_type == 'VIDEO' %} {% set used_url = playback_url if playback_url | length > 0 else (download_url if download_url | length > 0 else view_url) %} {% else %} {% set used_url = download_url if download_url | length > 0 else view_url %} {% endif %} **{{ loop.index }}. {{ asset.asset_type }}: {{ asset.asset_filename | default('unknown') }}** - Playback URL: {{ playback_url if playback_url | length > 0 else '(not available)' }} - Download URL: {{ download_url if download_url | length > 0 else '(not available)' }} - View URL: {{ view_url if view_url | length > 0 else '(not available)' }} - **Using: {{ used_url if used_url | length > 0 else '(no URL!)' }}** {% endfor %} --- **Note:** Media is sent via immich_album_watcher.send_telegram_notification service. Large lists are automatically split into multiple groups. # Build the notification message for Telegram # Uses {video_warning} placeholder which is populated when videos are present - variables: telegram_message: > {% set tpl = message_assets_added_template %} {{ tpl | replace('{album_name}', event_album_name) | replace('{album_url}', event_album_url) | replace('{added_count}', filtered_added_count | string) | replace('{people}', people_list) | replace('{assets}', assets_list) | replace('{video_warning}', video_warning_text) | replace('{common_date}', common_date) }} # Build URLs list for media # URL preference: playback_url (videos) > download_url > asset_url media_urls: > {% set ns = namespace(items = []) %} {% for asset in media_to_send %} {% set asset_type = asset.asset_type | default('IMAGE') %} {% set playback_url = asset.asset_playback_url | default('') %} {% set download_url = asset.asset_download_url | default('') %} {% set view_url = asset.asset_url | default('') %} {% if asset_type == 'VIDEO' and playback_url | length > 0 %} {% set media_url = playback_url %} {% elif download_url | length > 0 %} {% set media_url = download_url %} {% else %} {% set media_url = view_url %} {% endif %} {% set media_type = 'video' if asset_type == 'VIDEO' else 'photo' %} {% set item = {'url': media_url, 'type': media_type} %} {% set ns.items = ns.items + [item] %} {% endfor %} {{ ns.items }} # Loop through each chat ID and send notifications - repeat: for_each: "{{ telegram_chat_ids }}" sequence: - variables: current_chat_id: "{{ repeat.item }}" # First send the text notification message to this chat - service: immich_album_watcher.send_telegram_notification response_variable: telegram_text_response target: entity_id: "{{ album_id_entities[0] if album_id_entities | length > 0 else '' }}" data: chat_id: "{{ current_chat_id }}" caption: "{{ telegram_message }}" disable_web_page_preview: "{{ telegram_disable_url_preview }}" # Extract message ID for replies - variables: reply_to_message_id: "{{ telegram_text_response[album_id_entities[0]].message_id | default(0) | int }}" # Send media if we have URLs - choose: - conditions: - condition: template value_template: "{{ media_urls | length > 0 }}" sequence: # Debug: Log reply_to_message_id value - choose: - conditions: - condition: template value_template: "{{ enable_debug_notifications }}" sequence: - service: persistent_notification.create data: title: "Immich Album Watcher - Reply To Debug" message: > **Reply To Message Debug:** - Value: {{ telegram_text_response }} - service: immich_album_watcher.send_telegram_notification response_variable: telegram_media_response continue_on_error: true target: entity_id: "{{ album_id_entities[0] if album_id_entities | length > 0 else '' }}" data: chat_id: "{{ current_chat_id }}" urls: "{{ media_urls }}" reply_to_message_id: "{{ reply_to_message_id }}" max_group_size: "{{ max_media_per_group }}" chunk_delay: "{{ telegram_media_delay }}" send_large_photos_as_documents: "{{ telegram_send_large_photos_as_documents }}" wait_for_response: false