From 4c03bc849e69b00be363dc7a08a736db76a1d036 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Sat, 28 Feb 2026 22:10:11 +0300 Subject: [PATCH] Several minor updates to the existing blueprints --- Common/Dreame Vacuum/blueprint.yaml | 9 +- Common/Immich Album Watcher/README.md | 5 +- Common/Immich Album Watcher/blueprint.yaml | 54 ++++++---- Common/Motion Light/blueprint.yaml | 119 +++++++++++++++++++++ manifest.json | 2 +- 5 files changed, 162 insertions(+), 27 deletions(-) diff --git a/Common/Dreame Vacuum/blueprint.yaml b/Common/Dreame Vacuum/blueprint.yaml index 6cced42..d00ca1d 100644 --- a/Common/Dreame Vacuum/blueprint.yaml +++ b/Common/Dreame Vacuum/blueprint.yaml @@ -275,9 +275,14 @@ variables: # CONDITIONS # ============================================================================= condition: - # Only process events from the configured vacuum entity + # Only process events from the configured vacuum entity. + # The Dreame Vacuum integration uses generate_entity_id() for the entity_id + # in event data, which may append a numeric suffix (e.g., _2) since the + # actual vacuum entity already occupies the base entity_id. - condition: template - value_template: "{{ event_entity_id == vacuum_entity }}" + value_template: > + {{ event_entity_id == vacuum_entity + or event_entity_id.startswith(vacuum_entity ~ '_') }} # ============================================================================= # ACTIONS diff --git a/Common/Immich Album Watcher/README.md b/Common/Immich Album Watcher/README.md index 2d9882d..cf6fa4f 100644 --- a/Common/Immich Album Watcher/README.md +++ b/Common/Immich Album Watcher/README.md @@ -4,8 +4,7 @@ This blueprint monitors Immich album changes and sends notifications when assets ## Features -- Filter by hub/instance name (for multi-hub setups) -- Monitor specific albums by name (whitelist) +- Monitor specific albums by ID (whitelist) - Filter by asset type (track images only, videos only, or both) - Filter by favorites only (only notify about favorite assets) - Sort assets by date, rating, name, or keep original order @@ -159,6 +158,8 @@ Select input_text entities containing Telegram chat IDs. Can be user IDs (positi Sends a summary notification of tracked albums at configured times. Album names and share URLs are automatically read from the Album ID Entity's `album_name` and `share_url` attributes (if available). You can configure multiple notification times (e.g., "12:00, 18:00") using comma-separated 24-hour format with leading zeros. +Use **Summary Interval** to control how often summaries are sent (default: every day). Set it to `7` for weekly, `14` for bi-weekly, etc. The **Summary Start Date** anchors the interval — summaries are sent on that date and every N days after. For example, setting the start date to a Monday with an interval of 7 sends summaries every Monday. + When Telegram media is enabled, an optional image can be attached to the summary message. By default, the official Immich logo is used. Set the **Summary Image URL** to empty to send text-only notifications. ### Summary Message Template Variables diff --git a/Common/Immich Album Watcher/blueprint.yaml b/Common/Immich Album Watcher/blueprint.yaml index 0969537..55205c0 100644 --- a/Common/Immich Album Watcher/blueprint.yaml +++ b/Common/Immich Album Watcher/blueprint.yaml @@ -19,20 +19,9 @@ blueprint: # Hub & Album Configuration # ------------------------------------------------------------------------- albums_group: - name: "Hub & Albums" + name: "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: > @@ -489,6 +478,28 @@ blueprint: selector: boolean: + periodic_summary_interval: + name: Summary Interval (Days) + description: > + Number of days between periodic summaries. + Set to 1 for daily, 7 for weekly, etc. + default: 1 + selector: + number: + min: 1 + max: 365 + step: 1 + mode: box + + periodic_summary_start_date: + name: Summary Start Date + description: > + The date from which to start counting the summary interval. + Summaries are sent on this date and every N days after. + default: "2025-01-01" + selector: + date: + periodic_notification_times: name: Summary Notification Times description: > @@ -886,7 +897,7 @@ variables: # --------------------------------------------------------------------------- # Input Variables # --------------------------------------------------------------------------- - hub_names: !input hub_names + album_id_entities: !input album_id_entities automation_id: !input automation_id @@ -929,6 +940,8 @@ variables: # Periodic Summary Settings enable_periodic_summary: !input enable_periodic_summary + periodic_summary_interval: !input periodic_summary_interval + periodic_summary_start_date: !input periodic_summary_start_date periodic_summary_message_template: !input periodic_summary_message periodic_album_template: !input periodic_album_template periodic_summary_image_url: !input periodic_summary_image_url @@ -1033,10 +1046,6 @@ variables: # --------------------------------------------------------------------------- # 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 }} @@ -1249,13 +1258,15 @@ variables: # --------------------------------------------------------------------------- # Periodic Summary Variables # --------------------------------------------------------------------------- - # Check if periodic summary should run (at configured times) - # Manual test event (immich_album_watcher_test_periodic_summary) bypasses time check + # Check if periodic summary should run (at configured times and interval) + # Manual test event (immich_album_watcher_test_periodic_summary) bypasses interval check # If event data contains automation_id, only matching automations respond should_send_periodic_summary: > {% if enable_periodic_summary %} {% if trigger.id == 'periodic_summary_timer' %} - {{ true }} + {% set start = periodic_summary_start_date | as_datetime %} + {% set days_elapsed = (now().date() - start.date()).days %} + {{ days_elapsed >= 0 and days_elapsed % periodic_summary_interval == 0 }} {% elif trigger.platform == 'event' and trigger.event.event_type == 'immich_album_watcher_test_periodic_summary' %} {% set event_automation_id = trigger.event.data.automation_id | default('') %} {% if event_automation_id | length > 0 %} @@ -1384,7 +1395,7 @@ condition: {% elif trigger.platform == 'event' and trigger.event.event_type in ['immich_album_watcher_test_periodic_summary', 'immich_album_watcher_test_scheduled_assets', 'immich_album_watcher_test_memory_mode'] %} {{ should_send_periodic_summary or should_send_scheduled_assets or should_send_memory_mode }} {% else %} - {{ is_hub_tracked and is_album_tracked and should_notify }} + {{ is_album_tracked and should_notify }} {% endif %} # ============================================================================= @@ -2512,7 +2523,6 @@ action: - {{ 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 }} diff --git a/Common/Motion Light/blueprint.yaml b/Common/Motion Light/blueprint.yaml index 082f801..be7705a 100644 --- a/Common/Motion Light/blueprint.yaml +++ b/Common/Motion Light/blueprint.yaml @@ -951,6 +951,125 @@ action: {% set new_automation_state = (automation_state | combine({ state_motion_light_state: automation_state_none })) %} {{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }} + # Re-evaluate: if an external source turned off the light while + # the automation was actively controlling it, wait briefly and + # re-enable if conditions are still met. + # Safety: state_is_enabled/state_is_enabling reflect the state at + # run start (before reset). When CASE 3 turns off the light, it + # sets state to NONE first, so on restart state_is_none=true and + # this block is skipped — preventing unwanted re-enable. + - choose: + - conditions: + - condition: template + value_template: "{{ state_is_enabled or state_is_enabling }}" + sequence: + # Wait for the off-transition to finish + - delay: + seconds: "{{ transition_duration }}" + + # Fresh condition check (evaluates current entity states) + - condition: template + value_template: > + {% set e = sensors if sensors is iterable else [sensors] %} + {% set motion_active = e | select('is_state', 'on') | list | length > 0 %} + {% set cond_ok = true %} + {% set cs = condition_switches if condition_switches is iterable else [condition_switches] %} + {% if cs | length > 0 %} + {% set cond_ok = (cs | select('is_state', 'on') | list | length) == (cs | length) %} + {% endif %} + {{ motion_active and cond_ok }} + + # Re-read state from input_text (may have changed during delay) + - variables: + re_eval_state_global: > + {% set text = states(automation_state_entity) | string %} + {% if text in ['unknown','unavailable','none',''] %} + {{ dict() }} + {% else %} + {{ text | from_json }} + {% endif %} + re_eval_state: "{{ re_eval_state_global.get(automation_state_key, dict()) }}" + re_eval_motion_light_state: "{{ re_eval_state.get(state_motion_light_state, automation_state_none) }}" + + # Only proceed if state is still NONE (no other run claimed it) + - condition: template + value_template: "{{ (re_eval_motion_light_state | string) == automation_state_none }}" + + # --- Re-enable path (mirrors CASE 2) --- + + # Set state to ENABLING before turning on + - service: input_text.set_value + target: + entity_id: "{{ automation_state_entity }}" + data: + value: > + {% set new_automation_state = (re_eval_state | combine({ + state_motion_light_state: automation_state_enabling, + state_motion_light_last_action_timestamp: now(), + state_motion_light_last_brightness: 0 + })) %} + {{ re_eval_state_global | combine({ automation_state_key: new_automation_state }) | tojson }} + + # Scene or light activation + - choose: + - conditions: + - condition: template + value_template: "{{ use_scene_instead and effective_scene is not none }}" + sequence: + - service: scene.turn_on + target: + entity_id: "{{ effective_scene }}" + data: + transition: "{{ transition_duration }}" + + default: + # Turn ON lights + - choose: + - conditions: + - condition: template + value_template: "{{ resolved_all_lights | length > 0 }}" + sequence: + - service: light.turn_on + target: + entity_id: "{{ resolved_all_lights }}" + data: > + {% set d = effective_light_data if effective_light_data else {} %} + {% if transition_duration > 0 %} + {% set d = d | combine({'transition': transition_duration}) %} + {% endif %} + {{ d }} + + # Turn ON switches + - choose: + - conditions: + - condition: template + value_template: "{{ resolved_all_switches | length > 0 }}" + sequence: + - service: switch.turn_on + target: + entity_id: "{{ resolved_all_switches }}" + + # Execute enable callback + - choose: + - conditions: + - condition: template + value_template: "{{ enable_action != [] }}" + sequence: !input enable_action + + # Debug notification + - choose: + - conditions: "{{ enable_debug_notifications }}" + sequence: + - service: persistent_notification.create + data: + title: "Motion Light Debug" + message: > + Action: RE-ENABLE (external turn-off recovery) + Time: {{ now().strftime('%H:%M:%S') }} + Lights: {{ resolved_all_lights }} + Switches: {{ resolved_all_switches }} + Scene: {{ effective_scene if use_scene_instead else 'N/A' }} + # ----- Sub-case: Automation just turned on the light ----- # Transition from ENABLING to ENABLED, or disable immediately # if motion already cleared during the ENABLING phase diff --git a/manifest.json b/manifest.json index d9f6f21..1651884 100644 --- a/manifest.json +++ b/manifest.json @@ -1,3 +1,3 @@ { - "version": "2.3.0" + "version": "2.4.0" }