###################################################################### # @CCOSTAN - Follow Me on X # For more info visit https://www.vcloudinfo.com/click-here # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- # Vacation Mode - house-sitter arrival, analytics, and security checks. # Detects long absences, tracks sitter visits, and delivers vacation-specific reminders. # ------------------------------------------------------------------- # Related Issue: 793 # Related Issue: 1791 # Related Issue: 1792 # Notes: Vacation mode auto-enables after 24 hours of family absence or 24 hours without bed use while the family is away. # Notes: General vacation speech uses Chromecast only; the garage Alexa welcome is the one local-device exception. # Notes: Visit analytics use recorder-backed visit counts plus arrival/departure helpers for accurate durations. # Notes: Dorm zones are reported as away locations, not Bear Stone home. # Notes: Recent-departure lockout and a 3-hour stale-visit timeout prevent missed close edges from creating inflated visit durations. # Notes: Missed sitter alerts include 2-day and 3-day snooze actions that clear after a visit. # Notes: 10 AM and 10 PM house digests send lock-screen pushes and dispatch a richer snapshot to Joanna for vacation-mode review. # Notes: Powerwall/grid status is always included in vacation digests because power events are high signal. # Video: https://youtu.be/15kRcFaVV2Y # Blog: https://www.vcloudinfo.com/2026/05/home-assistant-vacation-mode-house-sitter-automation.html # Snooze video: https://youtu.be/y47KSflS1aw # Snooze blog: https://www.vcloudinfo.com/2026/06/home-assistant-notification-snooze-buttons.html ###################################################################### input_boolean: vacation_mode: name: Vacation Mode icon: mdi:palm-tree initial: off house_sitter_present: name: House Sitter Present icon: mdi:account-key initial: off input_datetime: vacation_house_sitter_last_arrival: name: Vacation House Sitter Last Arrival has_date: true has_time: true vacation_house_sitter_last_departure: name: Vacation House Sitter Last Departure has_date: true has_time: true vacation_house_sitter_missed_visit_snooze_until: name: Vacation House Sitter Missed Visit Snooze Until has_date: true has_time: true vacation_house_status_digest_last_sent: name: Vacation House Status Digest Last Sent has_date: true has_time: true input_number: vacation_house_sitter_last_visit_minutes: name: Vacation House Sitter Last Visit Minutes min: 0 max: 1440 step: 0.1 mode: box unit_of_measurement: min script: vacation_house_sitter_clear_presence: alias: Vacation House Sitter Clear Presence mode: queued fields: visit_minutes: description: Visit duration in minutes. departure_timestamp: description: Epoch timestamp to store as the sitter departure time. clear_reason: description: Reason this clear action ran. sequence: - service: input_number.set_value target: entity_id: input_number.vacation_house_sitter_last_visit_minutes data: value: "{{ visit_minutes }}" - service: input_datetime.set_datetime target: entity_id: input_datetime.vacation_house_sitter_last_departure data: timestamp: "{{ departure_timestamp }}" - service: homeassistant.turn_off target: entity_id: input_boolean.house_sitter_present vacation_house_status_digest: alias: Vacation House Status Digest mode: queued fields: digest_mode: description: Digest window label, usually morning or night. example: morning sequence: - variables: digest_mode_value: "{{ digest_mode | default('morning', true) | lower }}" - variables: digest_label: "{{ 'Night' if digest_mode_value == 'night' else 'Morning' }}" last_digest_ts: "{{ as_timestamp(states('input_datetime.vacation_house_status_digest_last_sent'), 0) }}" vacation_started_ts: "{{ as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) }}" vacation_duration: >- {% set started = as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) %} {% if is_state('input_boolean.vacation_mode', 'on') and started > 0 %} {% set hours = ((as_timestamp(now()) - started) / 3600) | int(0) %} {% set days = (hours // 24) | int(0) %} {% set remaining_hours = (hours % 24) | int(0) %} {{ days ~ 'd ' ~ remaining_hours ~ 'h' if days > 0 else remaining_hours ~ 'h' }} {% else %} inactive {% endif %} sitter_summary: >- {% set visit_count = states('sensor.vacation_house_sitter_visit_count') | int(0) %} {% set hours_since = state_attr('sensor.vacation_house_sitter_analytics', 'hours_since_last_visit') | float(0) %} {% if is_state('input_boolean.house_sitter_present', 'on') %} Sitter is currently at the house. {% elif visit_count > 0 %} Sitter visits: {{ visit_count }}; last visit {{ hours_since | round(1) }}h ago. {% else %} No sitter visit recorded yet this vacation. {% endif %} powerwall_grid_label: >- {% set grid = states('binary_sensor.powerwall_grid_status') %} {% if grid == 'on' %} grid up {% elif grid == 'off' %} GRID DOWN {% else %} grid {{ grid }} {% endif %} powerwall_charge_label: >- {% set charge = states('sensor.powerwall_charge') %} {% if charge in ['unknown', 'unavailable', 'none', ''] %} battery unavailable {% else %} battery {{ charge | float(0) | round(0) | int(0) }}% {% endif %} powerwall_grid_changed_ts: >- {% set entity = states.binary_sensor.powerwall_grid_status if states.binary_sensor.powerwall_grid_status is defined else none %} {{ as_timestamp(entity.last_changed, 0) if entity is not none else 0 }} powerwall_recent_event: >- {% set changed = powerwall_grid_changed_ts | float(0) %} {{ last_digest_ts | float(0) > 0 and changed > last_digest_ts | float(0) }} powerwall_summary: >- {% set summary = 'Powerwall: ' ~ powerwall_grid_label | trim ~ ', ' ~ powerwall_charge_label | trim ~ '.' %} {% if powerwall_recent_event | bool(false) %} {{ summary }} Grid status changed since the last digest at {{ (powerwall_grid_changed_ts | float(0)) | timestamp_custom('%H:%M', true) }}. {% else %} {{ summary }} {% endif %} powerwall_issues: >- {% set ns = namespace(items=[]) %} {% set charge = states('sensor.powerwall_charge') %} {% if is_state('binary_sensor.powerwall_grid_status', 'off') %} {% set ns.items = ns.items + ['grid is down'] %} {% endif %} {% if charge in ['unknown', 'unavailable', 'none', ''] %} {% set ns.items = ns.items + ['Powerwall charge unavailable'] %} {% elif charge | float(100) < 60 %} {% set ns.items = ns.items + ['Powerwall charge ' ~ (charge | float(0) | round(0) | int(0)) ~ '%'] %} {% endif %} {{ ns.items | join('; ') if ns.items | count > 0 else 'none' }} security_issues: >- {% set ns = namespace(items=[]) %} {% if states('lock.front_door') != 'locked' %} {% set ns.items = ns.items + ['front door ' ~ states('lock.front_door')] %} {% endif %} {% if states('group.locks') != 'locked' %} {% set ns.items = ns.items + ['locks ' ~ states('group.locks')] %} {% endif %} {% if states('group.garage_doors') not in ['closed', 'off'] %} {% set ns.items = ns.items + ['garage ' ~ states('group.garage_doors')] %} {% endif %} {% if states('group.entry_points') not in ['off', 'closed'] %} {% set ns.items = ns.items + ['entry points ' ~ states('group.entry_points')] %} {% endif %} {{ ns.items | join('; ') if ns.items | count > 0 else 'none' }} security_summary: >- {% if security_issues == 'none' %} Secure: locks locked, garage closed, entry points closed. {% else %} Needs security attention: {{ security_issues }}. {% endif %} climate_summary: >- {% set down_temp = state_attr('climate.downstairs', 'current_temperature') %} {% set up_temp = state_attr('climate.upstairs', 'current_temperature') %} Climate: downstairs {{ down_temp if down_temp is not none else states('climate.downstairs') }}, upstairs {{ up_temp if up_temp is not none else states('climate.upstairs') }}. safety_issues: >- {% set ns = namespace(items=[]) %} {% set fridge = states('sensor.refrigerator_fridge_temp') %} {% set freezer = states('sensor.refrigerator_freezer_temp') %} {% set flow = states('sensor.phyn_water_flowing') %} {% if states('valve.phyn_shutoff_valve') != 'open' %} {% set ns.items = ns.items + ['Phyn valve ' ~ states('valve.phyn_shutoff_valve')] %} {% endif %} {% if flow in ['unknown', 'unavailable', 'none', ''] %} {% set ns.items = ns.items + ['water telemetry ' ~ flow] %} {% elif flow in ['on', 'true'] or flow | float(0) > 0 %} {% set ns.items = ns.items + ['water flowing ' ~ flow] %} {% endif %} {% if is_state('binary_sensor.refrigerator_door_open', 'on') %} {% set ns.items = ns.items + ['refrigerator door open'] %} {% endif %} {% if fridge in ['unknown', 'unavailable', 'none', ''] and freezer in ['unknown', 'unavailable', 'none', ''] %} {% set ns.items = ns.items + ['refrigerator telemetry unavailable'] %} {% endif %} {% if fridge not in ['unknown', 'unavailable', 'none', ''] and fridge | float(0) > 45 %} {% set ns.items = ns.items + ['fridge ' ~ (fridge | float(0) | round(1)) ~ 'F'] %} {% endif %} {% if freezer not in ['unknown', 'unavailable', 'none', ''] and freezer | float(0) > 15 %} {% set ns.items = ns.items + ['freezer ' ~ (freezer | float(0) | round(1)) ~ 'F'] %} {% endif %} {% if is_state('binary_sensor.front_door_packages_present', 'on') %} {% set ns.items = ns.items + ['package at front door'] %} {% endif %} {{ ns.items | join('; ') if ns.items | count > 0 else 'none' }} infra_issues: >- {% set ns = namespace(items=[]) %} {% if is_state('binary_sensor.mqtt_broker_problem', 'on') %} {% set ns.items = ns.items + ['MQTT broker problem'] %} {% endif %} {% if is_state('binary_sensor.infra_nebula_sync_degraded', 'on') %} {% set ns.items = ns.items + ['Nebula Sync DNS degraded'] %} {% endif %} {% if is_state('binary_sensor.infra_pihole_iot_dns_degraded', 'on') %} {% set ns.items = ns.items + ['IoT Pi-hole DNS degraded'] %} {% endif %} {% if is_state('input_boolean.infra_duplicati_backup_repair_active', 'on') %} {% set ns.items = ns.items + ['Duplicati backup repair active'] %} {% endif %} {{ ns.items | join('; ') if ns.items | count > 0 else 'none' }} - variables: action_items: >- {% set ns = namespace(items=[]) %} {% if powerwall_issues != 'none' %} {% set ns.items = ns.items + ['Powerwall: ' ~ powerwall_issues] %} {% endif %} {% if security_issues != 'none' %} {% set ns.items = ns.items + ['Security: ' ~ security_issues] %} {% endif %} {% if safety_issues != 'none' %} {% set ns.items = ns.items + ['Safety: ' ~ safety_issues] %} {% endif %} {% if infra_issues != 'none' %} {% set ns.items = ns.items + ['Infra: ' ~ infra_issues] %} {% endif %} {{ ns.items | join(' | ') if ns.items | count > 0 else 'No material issues found.' }} fallback_message_one: >- {% set msg = 'Vacation ' ~ digest_label | lower ~ ' (' ~ vacation_duration | trim ~ '): ' ~ powerwall_summary | trim ~ ' ' ~ security_summary | trim ~ ' ' ~ sitter_summary | trim %} {{ msg[:447] ~ '...' if msg | length > 450 else msg }} fallback_message_two: >- {% set msg = 'Actions: ' ~ action_items | trim ~ ' ' ~ climate_summary | trim %} {{ msg[:447] ~ '...' if msg | length > 450 else msg }} joanna_diagnostics: >- vacation_mode={{ states('input_boolean.vacation_mode') }}; digest={{ digest_mode_value }}; vacation_duration={{ vacation_duration | trim }}; last_digest_ts={{ last_digest_ts }}; powerwall_grid={{ states('binary_sensor.powerwall_grid_status') }}; powerwall_charge={{ states('sensor.powerwall_charge') }}; powerwall_recent_event={{ powerwall_recent_event | trim }}; security={{ security_issues }}; safety={{ safety_issues }}; infra={{ infra_issues }}; sitter={{ sitter_summary | trim }}; climate={{ climate_summary | trim }}; entities=binary_sensor.powerwall_grid_status,sensor.powerwall_charge,group.locks,lock.front_door,group.garage_doors,group.entry_points,input_boolean.house_sitter_present,sensor.vacation_house_sitter_analytics,sensor.phyn_water_flowing,valve.phyn_shutoff_valve,binary_sensor.refrigerator_door_open,sensor.refrigerator_fridge_temp,sensor.refrigerator_freezer_temp,binary_sensor.front_door_packages_present,binary_sensor.mqtt_broker_problem,binary_sensor.infra_nebula_sync_degraded,binary_sensor.infra_pihole_iot_dns_degraded,input_boolean.infra_duplicati_backup_repair_active - service: script.notify_engine data: title: "Vacation {{ digest_label }} House Digest" value1: "{{ fallback_message_one }}" who: carlo group: vacation level: active - service: script.notify_engine data: title: "Vacation {{ digest_label }} Power/Issues" value1: "{{ fallback_message_two }}" who: carlo group: vacation level: active - service: rest_command.bearclaw_command data: source: home_assistant_automation.vacation_house_status_digest user: carlo context: "Vacation Mode {{ digest_label }} House Status Digest" domain_hint: ops lane_hint: joanna.ops priority: high async_only: true metadata: vacationMode: true digestMode: "{{ digest_mode_value }}" houseDigest: true powerwallImportant: true text: >- Vacation {{ digest_label | lower }} house digest snapshot. Fallback push 1: {{ fallback_message_one }} Fallback push 2: {{ fallback_message_two }} Diagnostics: {{ joanna_diagnostics }} Request: Use the vacation-mode heavier reasoning profile. Return one concise Home Assistant push paragraph under 420 characters. Powerwall/grid status is high signal and must always be mentioned. Mention only material issues or actions; avoid Docker/AP recovery noise unless unresolved. Start with "Vacation {{ digest_label }}:". - service: script.send_to_logbook data: topic: VACATION DIGEST message: "{{ digest_label }} vacation digest sent. {{ action_items }}" - service: input_datetime.set_datetime target: entity_id: input_datetime.vacation_house_status_digest_last_sent data: timestamp: "{{ now().timestamp() }}" sensor: - platform: history_stats name: Vacation House Sitter Visit Count entity_id: input_boolean.house_sitter_present state: 'on' type: count start: >- {% if is_state('input_boolean.vacation_mode', 'on') %} {{ states.input_boolean.vacation_mode.last_changed }} {% else %} {{ now() }} {% endif %} end: "{{ now() }}" template: - sensor: - name: "Vacation House Sitter Analytics" unique_id: vacation_house_sitter_analytics icon: mdi:clipboard-text-clock-outline state: "{{ states('sensor.vacation_house_sitter_visit_count') | int(0) }}" attributes: vacation_started_at: >- {% if is_state('input_boolean.vacation_mode', 'on') %} {{ as_local(states.input_boolean.vacation_mode.last_changed).isoformat() }} {% else %} none {% endif %} last_arrival_at: >- {% set vacation_start = as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) %} {% set raw = states('input_datetime.vacation_house_sitter_last_arrival') %} {% set ts = as_timestamp(raw, 0) %} {% if is_state('input_boolean.vacation_mode', 'on') and ts >= vacation_start and ts > 0 %} {{ as_local(as_datetime(raw)).isoformat() }} {% else %} none {% endif %} last_departure_at: >- {% set vacation_start = as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) %} {% set raw = states('input_datetime.vacation_house_sitter_last_departure') %} {% set ts = as_timestamp(raw, 0) %} {% if is_state('input_boolean.vacation_mode', 'on') and ts >= vacation_start and ts > 0 %} {{ as_local(as_datetime(raw)).isoformat() }} {% else %} none {% endif %} last_visit_minutes: >- {% set vacation_start = as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) %} {% set departure_ts = as_timestamp(states('input_datetime.vacation_house_sitter_last_departure'), 0) %} {% if is_state('input_boolean.vacation_mode', 'on') and departure_ts >= vacation_start %} {{ states('input_number.vacation_house_sitter_last_visit_minutes') | float(0) }} {% else %} 0 {% endif %} hours_since_last_visit: >- {% set vacation_start = as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) %} {% set departure = states('input_datetime.vacation_house_sitter_last_departure') %} {% set departure_ts = as_timestamp(departure, 0) %} {% if is_state('input_boolean.vacation_mode', 'on') and departure_ts >= vacation_start %} {{ ((as_timestamp(now()) - departure_ts) / 3600) | round(1) }} {% else %} 0 {% endif %} active_visit_minutes: >- {% if is_state('input_boolean.house_sitter_present', 'on') %} {{ ((as_timestamp(now()) - as_timestamp(states.input_boolean.house_sitter_present.last_changed, 0)) / 60) | round(1) }} {% else %} 0 {% endif %} automation: - alias: 'Vacation Mode Enable' id: 163f4434-2a2f-4528-a53c-8fc6e1fd8e14 mode: single trigger: - platform: state entity_id: group.family to: 'not_home' for: "24:00:00" - platform: state entity_id: group.bed to: 'off' for: "24:00:00" - platform: homeassistant event: start - platform: time_pattern hours: "/1" condition: - condition: state entity_id: input_boolean.vacation_mode state: 'off' - condition: template value_template: >- {% set family_away = is_state('group.family', 'not_home') %} {% set family_away_seconds = as_timestamp(now()) - as_timestamp(states.group.family.last_changed, 0) %} {% set bed_unused_seconds = as_timestamp(now()) - as_timestamp(states.group.bed.last_changed, 0) %} {{ (family_away and family_away_seconds >= 86400) or (family_away and is_state('group.bed', 'off') and bed_unused_seconds >= 86400) }} action: - variables: vacation_reason: >- {% set family_away_seconds = as_timestamp(now()) - as_timestamp(states.group.family.last_changed, 0) %} {% set bed_unused_seconds = as_timestamp(now()) - as_timestamp(states.group.bed.last_changed, 0) %} {% if is_state('group.bed', 'off') and bed_unused_seconds >= 86400 and family_away_seconds < 86400 %} Bed inactivity while the family remained away {% elif is_state('group.bed', 'off') and bed_unused_seconds >= 86400 %} Family absence and no bed use {% else %} Family absence {% endif %} - service: input_boolean.turn_on entity_id: - input_boolean.vacation_mode - service: input_boolean.turn_off entity_id: - input_boolean.house_sitter_present - service: script.send_to_logbook data: topic: VACATION MODE message: >- Vacation mode enabled after 24 hours based on {{ vacation_reason | lower }}. - alias: 'Vacation Mode Reset' id: d7d33f43-22f6-4db4-8a7d-c38f3c6d0c65 mode: single trigger: - platform: state entity_id: group.family to: 'home' for: "00:02:00" condition: - condition: template value_template: >- {{ is_state('input_boolean.vacation_mode', 'on') and trigger.from_state is not none and trigger.from_state.state == 'not_home' }} action: - service: input_boolean.turn_off entity_id: - input_boolean.vacation_mode - input_boolean.house_sitter_present - service: script.speech_engine data: value1: >- Vacation Mode has been disabled and normal automations will continue. call_no_announcement: 1 - alias: 'Vacation Mode Clear Missed Visit Snooze' id: 88b7ce4b-b7f9-4770-8b1c-2c42b7a98e32 mode: queued trigger: - platform: state entity_id: input_boolean.vacation_mode to: - 'on' - 'off' - platform: state entity_id: input_boolean.house_sitter_present to: 'on' action: - service: input_datetime.set_datetime target: entity_id: input_datetime.vacation_house_sitter_missed_visit_snooze_until data: datetime: "{{ (now() - timedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') }}" - alias: 'Vacation Mode House Digest Schedule' id: ed48b582-107f-4424-ab47-bb54cbd8ad38 mode: single trigger: - platform: time id: morning at: "10:00:00" - platform: time id: night at: "22:00:00" condition: - condition: template value_template: "{{ is_state('input_boolean.vacation_mode', 'on') }}" action: - service: script.vacation_house_status_digest data: digest_mode: "{{ trigger.id }}" - alias: 'Vacation Mode Garage Welcome' id: 63ac9268-c895-4a1b-b0df-92b5bb8e9d01 mode: single max_exceeded: silent trigger: - platform: state entity_id: group.garage_doors to: 'open' for: "00:00:15" condition: - condition: template value_template: >- {{ is_state('group.family', 'not_home') and is_state('input_boolean.vacation_mode', 'on') and is_state('input_boolean.house_sitter_present', 'off') and trigger.to_state is not none }} action: - delay: "00:00:30" - condition: state entity_id: group.family state: 'not_home' - condition: state entity_id: input_boolean.vacation_mode state: 'on' - condition: state entity_id: input_boolean.house_sitter_present state: 'off' - service: notify.alexa_media_garage data: message: >- Welcome back to Bear Stone. Please check whether any packages are waiting at the front door. data: type: announce - alias: 'Vacation Mode House Sitter Arrival' id: 8058a1e1-8517-4a07-839b-ef8f30ec24e4 mode: single max_exceeded: silent trigger: - platform: state entity_id: group.garage_doors to: 'open' for: "00:00:15" id: garage - platform: state entity_id: binary_sensor.mcu1_gpio5 to: 'on' for: "00:00:02" id: front_door_sensor - platform: state entity_id: lock.front_door to: 'unlocked' for: "00:02:00" id: front_door_unlock condition: - condition: template value_template: >- {{ is_state('group.family', 'not_home') and is_state('input_boolean.vacation_mode', 'on') and is_state('input_boolean.house_sitter_present', 'off') }} action: - variables: entry_label: >- {{ 'garage door' if trigger.id == 'garage' else 'front door' }} entry_delay: >- {{ '00:05:00' if trigger.id == 'garage' else '00:02:00' }} vacation_start_ts: "{{ as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) }}" last_departure_raw: "{{ states('input_datetime.vacation_house_sitter_last_departure') }}" last_departure_ts: "{{ as_timestamp(last_departure_raw, 0) }}" visit_number: "{{ (states('sensor.vacation_house_sitter_visit_count') | int(0)) + 1 }}" away_duration: >- {% set seconds = (as_timestamp(now()) - as_timestamp(states.group.family.last_changed, 0)) | int(0) %} {% set days = (seconds // 86400) | int(0) %} {% set hours = ((seconds % 86400) // 3600) | int(0) %} {% set minutes = ((seconds % 3600) // 60) | int(0) %} {% if days > 0 %} {{ days }} day{{ 's' if days != 1 else '' }}{% if hours > 0 %} and {{ hours }} hour{{ 's' if hours != 1 else '' }}{% endif %} {% elif hours > 0 %} {{ hours }} hour{{ 's' if hours != 1 else '' }}{% if minutes > 0 %} and {{ minutes }} minute{{ 's' if minutes != 1 else '' }}{% endif %} {% else %} {{ minutes }} minute{{ 's' if minutes != 1 else '' }} {% endif %} last_visit_gap: >- {% set gap_seconds = (as_timestamp(now()) - (last_departure_ts | float(0))) | int(0) %} {% set hours = (gap_seconds // 3600) | int(0) %} {% set minutes = ((gap_seconds % 3600) // 60) | int(0) %} {% if hours > 0 %} {{ hours }} hour{{ 's' if hours != 1 else '' }}{% if minutes > 0 %} and {{ minutes }} minute{{ 's' if minutes != 1 else '' }}{% endif %} {% else %} {{ minutes }} minute{{ 's' if minutes != 1 else '' }} {% endif %} visit_context: >- {% if (last_departure_ts | float(0)) >= (vacation_start_ts | float(0)) %} This is visit number {{ visit_number }} for this vacation. The last visit ended about {{ last_visit_gap }} ago. {% else %} This is the first house-sitter visit for this vacation. {% endif %} carlo_location: >- {% set person_state = states('person.carlo') %} {% set place = states('sensor.carlo_place') %} {% set label = place if place not in ['unknown', 'unavailable', 'none', ''] else person_state %} {% if person_state == 'driving' or label.startswith('Driving') %} driving near {{ label | replace('Driving near ', '') | replace('Driving', '') | trim }} {% elif person_state == 'home' %} at Bear Stone home {% elif 'Dorm' in label %} away at {{ label }} {% else %} at {{ label }} {% endif %} stacey_location: >- {% set person_state = states('person.stacey') %} {% set place = states('sensor.stacey_place') %} {% set label = place if place not in ['unknown', 'unavailable', 'none', ''] else person_state %} {% if person_state == 'driving' or label.startswith('Driving') %} driving near {{ label | replace('Driving near ', '') | replace('Driving', '') | trim }} {% elif person_state == 'home' %} at Bear Stone home {% elif 'Dorm' in label %} away at {{ label }} {% else %} at {{ label }} {% endif %} justin_location: >- {% set person_state = states('person.justin') %} {% set place = states('sensor.justin_place') %} {% set label = place if place not in ['unknown', 'unavailable', 'none', ''] else person_state %} {% if person_state == 'driving' or label.startswith('Driving') %} driving near {{ label | replace('Driving near ', '') | replace('Driving', '') | trim }} {% elif person_state == 'home' %} at Bear Stone home {% elif 'Dorm' in label %} away at {{ label }} {% else %} at {{ label }} {% endif %} paige_location: >- {% set person_state = states('person.paige') %} {% set place = states('sensor.paige_place') %} {% set label = place if place not in ['unknown', 'unavailable', 'none', ''] else person_state %} {% if person_state == 'driving' or label.startswith('Driving') %} driving near {{ label | replace('Driving near ', '') | replace('Driving', '') | trim }} {% elif person_state == 'home' %} at Bear Stone home {% elif 'Dorm' in label %} away at {{ label }} {% else %} at {{ label }} {% endif %} garbage_message: >- {% set day = now().strftime('%a') %} {% if day in ['Tue', 'Sat'] %} If you have a chance, please take the garbage cans out tonight. {% elif day in ['Wed', 'Sun'] %} If the garbage cans are still outside today, please bring them back in. {% else %} {% endif %} package_message: >- {% if trigger.id == 'garage' and is_state('binary_sensor.front_door_packages_present', 'on') %} Please also check whether any packages are waiting at the front door. {% else %} {% endif %} arrival_message: >- House sitter arrival detected through the {{ entry_label }}. The family was last home {{ away_duration }} ago. {{ visit_context }} Carlo is {{ carlo_location }}. Stacey is {{ stacey_location }}. Justin is {{ justin_location }}. Paige is {{ paige_location }}. Please check Molly's food, water, and litter box. {{ package_message }} {{ garbage_message }} push_message: >- House sitter arrival detected via the {{ entry_label }} after {{ away_duration }} away. {{ visit_context }} Carlo={{ carlo_location }}, Stacey={{ stacey_location }}, Justin={{ justin_location }}, Paige={{ paige_location }}. Molly reminder is included. {{ package_message }} {{ garbage_message }} - delay: "{{ entry_delay }}" - condition: template value_template: >- {{ is_state('group.family', 'not_home') and is_state('input_boolean.vacation_mode', 'on') and is_state('input_boolean.house_sitter_present', 'off') }} - condition: template value_template: >- {% set vacation_start = as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) %} {% set departure_ts = as_timestamp(states('input_datetime.vacation_house_sitter_last_departure'), 0) %} {{ departure_ts < vacation_start or (as_timestamp(now()) - departure_ts) >= 900 }} - service: input_boolean.turn_on entity_id: - input_boolean.house_sitter_present - service: input_datetime.set_datetime target: entity_id: input_datetime.vacation_house_sitter_last_arrival data: timestamp: "{{ now().timestamp() }}" - service: automation.trigger target: entity_id: automation.late_night_helper data: skip_condition: false - choose: - conditions: - condition: template value_template: "{{ trigger.id == 'front_door_unlock' }}" sequence: - service: automation.trigger target: entity_id: automation.late_night_helper_outside data: skip_condition: false - service: script.send_to_logbook data: topic: HOUSE SITTER message: >- House sitter arrival inferred from the {{ entry_label }} after {{ away_duration }} of family absence. {{ visit_context }} - service: script.notify_engine data: title: House sitter arrival value1: "{{ push_message }}" who: carlo group: information level: active - service: script.speech_processing data: media_player: media_player.livingroomcc speech_direct: true speech_message: "{{ arrival_message }}" - alias: 'Vacation Mode Sitter Checklist Follow-Up' id: 4bd37a05-e6b6-493f-9ddb-fc8d9781c4df mode: single max_exceeded: silent trigger: - platform: state entity_id: input_boolean.house_sitter_present to: 'on' for: "00:10:00" condition: - condition: template value_template: >- {{ is_state('input_boolean.vacation_mode', 'on') and is_state('input_boolean.house_sitter_present', 'on') }} action: - variables: garage_status: >- {% if is_state('group.garage_doors', 'open') %} If the garage door is still open, please close it before you leave. {% else %} The garage doors already look closed. {% endif %} checklist_message: >- Quick reminder. Please check Molly's food, water, and litter box. {{ garage_status }} The front door should auto-lock when you leave. - service: script.speech_processing data: media_player: media_player.livingroomcc speech_direct: true speech_message: "{{ checklist_message }}" - alias: 'Vacation Mode House Sitter Visit Timeout' id: b7c03da2-9017-4524-8202-e93f5a6dd742 mode: single max_exceeded: silent trigger: - platform: state entity_id: input_boolean.house_sitter_present to: 'on' for: "03:00:00" condition: - condition: template value_template: >- {{ is_state('group.family', 'not_home') and is_state('input_boolean.vacation_mode', 'on') and is_state('input_boolean.house_sitter_present', 'on') }} action: - variables: visit_count: "{{ states('sensor.vacation_house_sitter_visit_count') | int(0) }}" visit_minutes_numeric: >- {% set seconds = (as_timestamp(now()) - as_timestamp(states.input_boolean.house_sitter_present.last_changed, 0)) | int(0) %} {{ (seconds / 60) | round(1) }} - service: script.vacation_house_sitter_clear_presence data: visit_minutes: "{{ visit_minutes_numeric }}" departure_timestamp: "{{ now().timestamp() }}" clear_reason: timeout - service: script.send_to_logbook data: topic: HOUSE SITTER message: >- House sitter presence timed out after {{ visit_minutes_numeric }} minutes because no departure door or garage close was detected. Visit count this vacation is now {{ visit_count }}. - service: script.notify_engine data: title: House sitter visit timeout value1: >- House sitter presence was still active after {{ visit_minutes_numeric }} minutes, so it was cleared as a stale visit. who: carlo group: information level: active - alias: 'Vacation Mode House Sitter Departure' id: 04f5ab9e-65a8-4cd5-a8e9-0f40c82a2f62 mode: single max_exceeded: silent trigger: - platform: state entity_id: group.garage_doors to: 'closed' for: "00:00:15" id: garage - platform: state entity_id: binary_sensor.mcu1_gpio5 to: 'off' for: "00:00:02" id: front_door condition: - condition: template value_template: >- {% set seconds = as_timestamp(now()) - as_timestamp(states.input_boolean.house_sitter_present.last_changed, 0) %} {{ is_state('input_boolean.house_sitter_present', 'on') and seconds >= 60 }} action: - variables: exit_label: >- {{ 'garage door' if trigger.id == 'garage' else 'front door' }} visit_count: "{{ states('sensor.vacation_house_sitter_visit_count') | int(0) }}" visit_minutes_numeric: >- {% set seconds = (as_timestamp(now()) - as_timestamp(states.input_boolean.house_sitter_present.last_changed, 0)) | int(0) %} {{ (seconds / 60) | round(1) }} visit_duration: >- {% set seconds = (as_timestamp(now()) - as_timestamp(states.input_boolean.house_sitter_present.last_changed, 0)) | int(0) %} {% set minutes = (seconds // 60) | int(0) %} {% set remainder = (seconds % 60) | int(0) %} {{ minutes }} minute{{ 's' if minutes != 1 else '' }}{% if remainder > 0 %} and {{ remainder }} second{{ 's' if remainder != 1 else '' }}{% endif %} - service: script.vacation_house_sitter_clear_presence data: visit_minutes: "{{ visit_minutes_numeric }}" departure_timestamp: "{{ now().timestamp() }}" clear_reason: "{{ trigger.id }}" - service: script.send_to_logbook data: topic: HOUSE SITTER message: >- House sitter departure inferred when the {{ exit_label }} closed after {{ visit_duration }}. Visit count this vacation is now {{ visit_count }}. - service: script.notify_engine data: title: House sitter left value1: >- House sitter presence was cleared after the {{ exit_label }} closed about {{ visit_duration }} into visit number {{ visit_count }}. who: carlo group: information level: active - alias: 'Vacation Mode Secure House Check' id: 5c4f6a2d-d976-4f9f-b204-ab444b24d76f mode: restart max_exceeded: silent trigger: - platform: state entity_id: input_boolean.house_sitter_present from: 'on' to: 'off' for: "00:25:00" condition: - condition: template value_template: >- {{ is_state('input_boolean.vacation_mode', 'on') and is_state('input_boolean.house_sitter_present', 'off') }} action: - variables: visit_count: "{{ states('sensor.vacation_house_sitter_visit_count') | int(0) }}" visit_minutes: "{{ state_attr('sensor.vacation_house_sitter_analytics', 'last_visit_minutes') | float(0) }}" front_door_issue: "{{ '' if is_state('lock.front_door', 'locked') else 'front door is ' ~ states('lock.front_door') }}" garage_issue: "{{ 'a garage door is open' if is_state('group.garage_doors', 'open') else '' }}" entry_issue: "{{ 'an entry point is still open' if is_state('group.entry_points', 'on') else '' }}" issue_count: >- {{ [front_door_issue, garage_issue, entry_issue] | reject('equalto', '') | list | count }} unsecured_message: >- {% set items = [front_door_issue, garage_issue, entry_issue] | reject('equalto', '') | list %} {% if items | count > 0 %} {{ items | join(', ') }} {% else %} all entry points are secure {% endif %} joanna_message: >- Vacation house check after a sitter visit found: {{ unsecured_message }}. Visit count this vacation: {{ visit_count }}. Last visit duration: {{ visit_minutes | round(1) }} minutes. Front door state: {{ states('lock.front_door') }}. Garage state: {{ states('group.garage_doors') }}. Entry points state: {{ states('group.entry_points') }}. - choose: - conditions: - condition: template value_template: "{{ issue_count | int(0) > 0 }}" sequence: - service: script.notify_engine data: title: Vacation secure-house alert value1: >- After the sitter left, {{ unsecured_message }}. Last visit was {{ visit_minutes | round(1) }} minutes. who: carlo group: information level: critical - service: script.joanna_send_telegram data: message: "{{ joanna_message }}" - service: script.send_to_logbook data: topic: VACATION MODE message: >- Secure-house check failed after sitter departure because {{ unsecured_message }}. default: - service: script.send_to_logbook data: topic: VACATION MODE message: >- Secure-house check passed 25 minutes after the sitter left. - alias: 'Vacation Mode Missed Visit Alert' id: 9f83b8cf-57ef-45b3-b72b-ea6f6c8446ec mode: single max_exceeded: silent trigger: - platform: time_pattern minutes: "30" condition: - condition: template value_template: >- {% set vacation_start = as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) %} {% set last_departure = as_timestamp(states('input_datetime.vacation_house_sitter_last_departure'), 0) %} {% set baseline = last_departure if last_departure >= vacation_start else vacation_start %} {{ is_state('input_boolean.vacation_mode', 'on') and is_state('input_boolean.house_sitter_present', 'off') and (as_timestamp(now()) - baseline) >= 172800 }} - condition: template value_template: >- {% set last_alert = as_timestamp(state_attr('automation.vacation_mode_missed_visit_alert', 'last_triggered'), 0) %} {{ last_alert == 0 or (as_timestamp(now()) - last_alert) >= 86400 }} - condition: template value_template: >- {% set snooze_until = as_timestamp(states('input_datetime.vacation_house_sitter_missed_visit_snooze_until'), 0) %} {{ snooze_until <= as_timestamp(now()) }} action: - variables: visit_count: "{{ states('sensor.vacation_house_sitter_visit_count') | int(0) }}" vacation_start: "{{ as_timestamp(states.input_boolean.vacation_mode.last_changed, 0) }}" last_departure: "{{ as_timestamp(states('input_datetime.vacation_house_sitter_last_departure'), 0) }}" baseline: >- {{ last_departure if (last_departure | float(0)) >= (vacation_start | float(0)) else vacation_start }} hours_since: "{{ ((as_timestamp(now()) - (baseline | float(0))) / 3600) | round(1) }}" baseline_label: >- {% if (last_departure | float(0)) >= (vacation_start | float(0)) %} the last sitter departure {% else %} vacation mode started {% endif %} alert_message: >- No sitter visit has been detected for {{ hours_since }} hours since {{ baseline_label }}. Visit count this vacation is {{ visit_count }}. - service: script.notify_engine_two_button data: title: Vacation missed visit alert value1: "{{ alert_message }}" who: carlo group: information level: active title1: "Snooze 2d" action1: "SNOOZE_SITTER_MISSED_VISIT_2D" icon1: "sfsymbols:clock" title2: "Snooze 3d" action2: "SNOOZE_SITTER_MISSED_VISIT_3D" icon2: "sfsymbols:calendar" - service: script.joanna_send_telegram data: message: "{{ alert_message }}" - service: script.send_to_logbook data: topic: VACATION MODE message: "{{ alert_message }}" - alias: 'Vacation Mode Missed Visit Snooze' id: b5a2e44a-1a42-4a2f-992c-18d8dd8d71d9 mode: queued trigger: - platform: event event_type: mobile_app_notification_action event_data: action: SNOOZE_SITTER_MISSED_VISIT_2D id: two_days - platform: event event_type: mobile_app_notification_action event_data: action: SNOOZE_SITTER_MISSED_VISIT_3D id: three_days variables: snooze_until: >- {% if trigger.id == 'two_days' %} {{ (now() + timedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S') }} {% else %} {{ (now() + timedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S') }} {% endif %} snooze_label: "{{ '2 days' if trigger.id == 'two_days' else '3 days' }}" action: - service: input_datetime.set_datetime target: entity_id: input_datetime.vacation_house_sitter_missed_visit_snooze_until data: datetime: "{{ snooze_until }}" - service: script.send_to_logbook data: topic: VACATION MODE message: "Vacation missed sitter visit alert snoozed for {{ snooze_label }} (until {{ snooze_until }})." - alias: 'Vacation Mode Room Hints' id: e0d4e35c-2bf4-435d-adff-52d836812999 mode: single max_exceeded: silent trigger: - platform: state entity_id: light.kitchen_lights from: 'off' to: 'on' for: "00:00:05" id: kitchen - platform: state entity_id: light.living_room_lights from: 'off' to: 'on' for: "00:00:05" id: living_room - platform: state entity_id: light.living_room_accents from: 'off' to: 'on' for: "00:00:05" id: living_room - platform: state entity_id: light.office_lights from: 'off' to: 'on' for: "00:00:05" id: office - platform: state entity_id: light.garage_lights from: 'off' to: 'on' for: "00:00:05" id: garage - platform: state entity_id: light.upstairs_lights from: 'off' to: 'on' for: "00:00:05" id: upstairs condition: - condition: state entity_id: input_boolean.vacation_mode state: 'on' - condition: state entity_id: input_boolean.house_sitter_present state: 'on' - condition: or conditions: - condition: and conditions: - condition: state entity_id: group.bed state: 'off' - condition: time after: '08:00:00' before: '22:00:00' - condition: state entity_id: input_boolean.alert_mode state: 'on' action: - variables: hint_message: >- {% set hints = { 'kitchen': 'House tip. The kitchen lights are on. You can also say, Alexa, turn on the sink light or turn off the kitchen accents.', 'living_room': 'House tip. The living room lights are on. You can also say, Alexa, turn on the TV stand light or turn on the living room accents.', 'office': 'House tip. The office lights are on. You can also say, Alexa, turn on the office accent or turn off the office lamp switch.', 'garage': 'House tip. The garage lights are on. You can also say, Alexa, turn on the garage hallway or open the large garage door.', 'upstairs': 'House tip. The upstairs lights are on. You can also say, Alexa, turn on the upstairs lamp or turn off the upstairs lights.' } %} {{ hints.get(trigger.id, 'House tip. Alexa can help with the lights in this room.') }} - service: script.speech_processing data: media_player: media_player.livingroomcc speech_direct: true speech_message: "{{ hint_message }}" - delay: "00:20:00"