Compare commits

..

8 Commits

Author SHA1 Message Date
claus 5eae8e1d6a nu virker det 2026-03-22 07:50:18 +01:00
claus 1dc0866fe3 Nu virker det 2026-03-22 07:49:16 +01:00
claus 5e368938bc tilpasset 2026-03-22 07:48:54 +01:00
claus 18c6b5e4cb feat(tesla): automate charge deadline to next morning
- Add daily automation to set tesla_charge_deadline at 07:00 next day (13:05 trigger)
- Add automation to update deadline when car is plugged in
- Ensures smart charging always has valid future window
- Eliminates manual deadline management
2026-03-22 07:48:31 +01:00
claus 5cfb27c3ad Refactor Tesla charging templates to modern template syntax
- Converted all Tesla sensors and binary_sensors to the modern
  `template:` integration format compatible with Home Assistant 2026+
- Combined kWh needed, charge hours, and smart charging decision
  into a single template file for inclusion via !include_dir_merge_named
- Preserves existing behavior while preparing for future HA versions
- Legacy template warnings remain for binary_sensor but functionality works
2026-03-20 20:19:50 +01:00
claus 70b78b6275 Improve garage closing logic using vehicle activity
- Only close garage when vehicle tracker is active (speed available)
- Prevents closing when leaving on foot or bike
- Add delay to ensure safe closing after departure
- Align closing logic with car-based opening conditions
2026-03-20 16:31:58 +01:00
claus 3d988314aa Use vehicle speed attribute to control garage opening
- Only open garage if SnowyWhite tracker has valid speed attribute
- Prevents false triggers when car is asleep (speed = unknown)
- Ensures garage opens only when arriving by car, not bike or foot
2026-03-20 07:27:35 +01:00
claus 1e71bc5372 feat(roborock): add mobile notifications for cleaning start, stop, and failure
- Send push notification on successful auto-start of cleaning
- Include elapsed empty-house time in start notification
- Add failure notification if robot fails to start after retries
- Notify when cleaning is stopped due to someone arriving home
- Ensure notifications only trigger on actual state outcomes (not attempts)
2026-03-19 19:02:05 +01:00
5 changed files with 211 additions and 75 deletions
@@ -17,22 +17,22 @@
after: "05:00:00"
before: "23:00:00"
- condition: not
conditions:
- condition: state
entity_id: device_tracker.snowywhite_location_tracker
state: "not_home"
action:
- choose:
# ÅBN (Arriving)
# ÅBN (kun hvis bil er aktiv)
- conditions:
- condition: trigger
id: arriving
- condition: state
entity_id: cover.anne
state: "closed"
- condition: template
value_template: >
{{ state_attr('device_tracker.snowywhite_location_tracker', 'speed') not in [None, 'unknown'] }}
sequence:
- service: cover.open_cover
target:
@@ -40,23 +40,29 @@
- service: notify.mobile_app_clausiphone15
data:
message: "Garage opened (arrival detected)"
message: "Garage opened (arrival by car)"
# LUK (Leaving)
# LUK (kun hvis bil er aktiv)
- conditions:
- condition: trigger
id: leaving
- condition: state
entity_id: cover.anne
state: "open"
- condition: template
value_template: >
{{ state_attr('device_tracker.snowywhite_location_tracker', 'speed') not in [None, 'unknown'] }}
sequence:
- delay: "00:00:20" # giver tid til at du faktisk er kørt ud
- service: cover.close_cover
target:
entity_id: cover.anne
- service: notify.mobile_app_clausiphone15
data:
message: "Garage closed (departure detected)"
message: "Garage closed (departure by car)"
mode: single
+42 -16
View File
@@ -7,10 +7,6 @@
entity_id: binary_sensor.family_presence
to: "off"
condition:
- condition: template
value_template: "{{ trigger.from_state.state != 'unavailable' }}"
action:
- service: input_datetime.set_datetime
target:
@@ -36,41 +32,37 @@
mode: single
trigger:
# Tjek løbende mens huset er tomt
- platform: time_pattern
minutes: "/5"
condition:
# Huset er tomt
- condition: state
entity_id: binary_sensor.family_presence
state: "off"
# Har været tomt i 30 min (robust)
- condition: template
value_template: >
{{ states('input_datetime.house_became_empty') not in ['unknown','unavailable',''] and
(as_timestamp(now()) - as_timestamp(states('input_datetime.house_became_empty'))) > 1800 }}
# Max 2 gange dagligt
- condition: numeric_state
entity_id: input_number.roborock_auto_runs_today
below: 2
# Støvsuger klar (robust state check)
- condition: template
value_template: >
{{ states('vacuum.roborock_s8_pro_ultra') in ['docked','idle','charging'] }}
# Batteri OK
- condition: numeric_state
entity_id: vacuum.roborock_s8_pro_ultra
attribute: battery_level
above: 20
- condition: template
value_template: >
{% set b = state_attr('vacuum.roborock_s8_pro_ultra','battery_level') %}
{{ b is number and b > 20 }}
action:
- alias: "Start cleaning (retry op til 3 gange)"
repeat:
- variables:
started: false
- repeat:
count: 3
sequence:
- service: button.press
@@ -79,14 +71,43 @@
- delay: "00:00:20"
- if:
- condition: state
entity_id: vacuum.roborock_s8_pro_ultra
state: "cleaning"
then:
- variables:
started: true
- stop: "Cleaning started"
- choose:
# ✅ SUCCESS → send notifikation
- conditions:
- condition: template
value_template: "{{ started }}"
sequence:
- service: input_number.increment
target:
entity_id: input_number.roborock_auto_runs_today
- service: notify.mobile_app_clausiphone15
data:
title: "🧹 Roborock startet"
message: >
Rengøring startet automatisk.
Hus har været tomt i {{ ((as_timestamp(now()) - as_timestamp(states('input_datetime.house_became_empty'))) / 60) | int }} min.
# ❌ FAIL → send fejl-notifikation
- conditions:
- condition: template
value_template: "{{ not started }}"
sequence:
- service: notify.mobile_app_clausiphone15
data:
title: "⚠️ Roborock start fejlede"
message: "Automation forsøgte 3 gange uden succes."
- id: roborock_stop_when_home
alias: Roborock Stop When Someone Comes Home
mode: restart
@@ -105,3 +126,8 @@
- service: vacuum.return_to_base
target:
entity_id: vacuum.roborock_s8_pro_ultra
- service: notify.mobile_app_clausiphone15
data:
title: "🏠 Roborock stoppet"
message: "Rengøring stoppet fordi nogen er kommet hjem."
+35
View File
@@ -0,0 +1,35 @@
- alias: Tesla set charge deadline at 13:00
id: tesla_set_charge_deadline_daily
trigger:
- platform: time
at: "13:05:00"
action:
- service: input_datetime.set_datetime
target:
entity_id: input_datetime.tesla_charge_deadline
data:
datetime: >
{{ (now() + timedelta(days=1))
.replace(hour=7, minute=0, second=0, microsecond=0) }}
mode: single
- alias: Tesla set charge deadline when plugged in
id: tesla_set_charge_deadline_on_plug
trigger:
- platform: state
entity_id: binary_sensor.snowywhite_charger
to: "on"
action:
- service: input_datetime.set_datetime
target:
entity_id: input_datetime.tesla_charge_deadline
data:
datetime: >
{{ (now() + timedelta(days=1))
.replace(hour=7, minute=0, second=0, microsecond=0) }}
mode: single
+36 -26
View File
@@ -1,5 +1,5 @@
- alias: Tesla smart charging v3
id: tesla_smart_charging_v3
- alias: Tesla smart charging v4
id: tesla_smart_charging_v4
mode: restart
trigger:
@@ -7,55 +7,65 @@
minutes: "/5"
condition:
# Ensure deadline is valid and in the future
# Valid future deadline
- condition: template
value_template: >
{% set deadline = states('input_datetime.tesla_charge_deadline') %}
{{ deadline not in ['unknown','unavailable',''] and as_timestamp(deadline) > as_timestamp(now()) }}
{{ deadline not in ['unknown','unavailable','']
and as_timestamp(deadline) > as_timestamp(now()) }}
# Only run when car is connected
- condition: template
value_template: >
{{ states('binary_sensor.tesla_connected') == 'on' }}
# Car must be connected
- condition: state
entity_id: binary_sensor.snowywhite_charger
state: "on"
action:
- variables:
charge_now: "{{ is_state('binary_sensor.tesla_charge_now', 'on') }}"
charger_on: "{{ is_state('switch.home_charging', 'on') }}"
- choose:
# -------------------------
# Charge if cheap
# START charging (cheap hour)
# -------------------------
- conditions:
- condition: template
value_template: >
{{ states('binary_sensor.tesla_charge_now') == 'on' }}
value_template: "{{ charge_now and not charger_on }}"
sequence:
# Reset charger if stuck in finished state
- choose:
- conditions:
- condition: template
value_template: >
{{ states('sensor.home_charger_mode') == 'connected_finished' }}
sequence:
- service: switch.turn_off
target:
entity_id: switch.home_charging
- delay: "00:00:05"
- service: system_log.write
data:
message: "Tesla: START charging (cheap hour)"
level: info
# Turn charger on
- service: switch.turn_on
target:
entity_id: switch.home_charging
# -------------------------
# Stop charging if not cheap
# STOP charging (not cheap)
# -------------------------
- conditions:
- condition: template
value_template: >
{{ states('binary_sensor.tesla_charge_now') != 'on' }}
value_template: "{{ not charge_now and charger_on }}"
sequence:
- service: system_log.write
data:
message: "Tesla: STOP charging (not cheap)"
level: info
- service: switch.turn_off
target:
entity_id: switch.home_charging
# -------------------------
# Default: do nothing
# -------------------------
- service: system_log.write
data:
message: >
Tesla: No action (charge_now={{ charge_now }},
charger_on={{ charger_on }})
level: debug
+65 -6
View File
@@ -1,15 +1,74 @@
- sensor:
- name: tesla_kwh_needed
sensors:
tesla_kwh_needed:
friendly_name: "Tesla kWh Needed"
unit_of_measurement: "kWh"
state: >
value_template: >
{% set capacity = 75 %}
{% set soc = states('sensor.snowywhite_battery') | float(0) %}
{% set limit = states('number.snowywhite_charge_limit') | float(100) %}
{{ ((limit - soc)/100 * capacity)|round(2) }}
{{ [0, ((limit - soc)/100 * capacity)] | max | round(2) }}
- name: tesla_charge_hours_needed
tesla_charge_hours_needed:
friendly_name: "Tesla Charge Hours Needed"
unit_of_measurement: "h"
state: >
value_template: >
{% set power = 11 %}
{% set kwh = states('sensor.tesla_kwh_needed') | float(0) %}
{{ (kwh / power)|round(2) }}
tesla_charging_plan:
friendly_name: "Tesla Charging Plan"
value_template: >
{% set deadline_raw = states('input_datetime.tesla_charge_deadline') %}
{% set deadline_ts = as_timestamp(deadline_raw)
if deadline_raw not in ['unknown','unavailable',''] else none %}
{% set hours_needed = [1,
states('sensor.tesla_charge_hours_needed') | float(0) | round(0, 'ceil')
] | max %}
{% set prices = (state_attr('sensor.energidataservice','today') or [])
+ (state_attr('sensor.energidataservice','tomorrow') or []) %}
{% set valid = [] %}
{% for p in prices %}
{% if p.hour is defined and p.hour %}
{% set ts = as_timestamp(p.hour) %}
{% if not deadline_ts or ts <= deadline_ts %}
{% set valid = valid + [p] %}
{% endif %}
{% endif %}
{% endfor %}
{% set cheapest = (valid | sort(attribute='price'))[:hours_needed] %}
{% set times = cheapest
| map(attribute='hour')
| map('as_timestamp')
| map('timestamp_local')
| list %}
{{ times | join(', ') if times | count > 0 else 'none' }}
binary_sensors:
tesla_charge_now:
friendly_name: "Tesla Charge Now"
device_class: power
value_template: >
{% set hours_needed = [1, states('sensor.tesla_charge_hours_needed') | float(0) | round(0, 'ceil')] | max %}
{% set deadline_raw = states('input_datetime.tesla_charge_deadline') %}
{% set deadline_ts = as_timestamp(deadline_raw) if deadline_raw not in ['unknown','unavailable',''] else none %}
{% set prices = (state_attr('sensor.energidataservice','today') or []) + (state_attr('sensor.energidataservice','tomorrow') or []) %}
{% set valid_prices = [] %}
{% for p in prices %}
{% if p.hour is defined and p.hour %}
{% set ts = as_timestamp(p.hour) %}
{% if not deadline_ts or ts <= deadline_ts %}
{% set valid_prices = valid_prices + [p] %}
{% endif %}
{% endif %}
{% endfor %}
{% set cheapest = (valid_prices | sort(attribute='price'))[:hours_needed] %}
{% set now_ts = now().replace(minute=0, second=0, microsecond=0).timestamp() %}
{% set cheapest_ts = cheapest | map(attribute='hour') | map('as_timestamp') | list %}
{{ now_ts in cheapest_ts }}