JLindvig

Live SoC

Mar 1st, 2026 (edited)
385
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
YAML 5.73 KB | None | 0 0
  1. template:
  2.   - triggers:
  3.       - trigger: state
  4.         entity_id:
  5.          # A helper saving the SoC% the precise moment the cable is plugged in.
  6.           - input_number.yeti_ev_battery_level_on_connection
  7.           # Your EV charger's current charging power (measured in Watts).
  8.           - sensor.peblar_ev_charger_effekt
  9.           # Energy added during the current session (kWh)
  10.           - sensor.peblar_ev_charger_session_energy
  11.           # Entity showing 'on' when the cable is physically connected to the car.
  12.           - switch.ev_smart_charging_ev_connected
  13.           # Entity showing 'on' when actively pumping energy into the car.
  14.           - binary_sensor.ev_charging_status
  15.           # Your car's official cloud API SoC sensor (e.g., Bluelink, Tesla, VW).
  16.           - sensor.yeti_ev_battery_level
  17.       - trigger: homeassistant
  18.         event: start
  19.       - trigger: event
  20.         event_type: event_template_reloaded
  21.     sensor:
  22.       name: Yeti Resterende Ladetid
  23.       unique_id: 0d9abfbb80bf4d6e95ade0a215818074
  24.       icon: mdi:timer-sand
  25.       unit_of_measurement: "min"
  26.       state_class: measurement
  27.       variables:
  28.        # --- 1. SENSOR DATA ---
  29.         # Energy added in current session (kWh)
  30.         session_energy_kwh: "{{ states('sensor.peblar_ev_charger_session_energy') | float(0) }}"
  31.         # SoC% frozen at the time of plug-in
  32.         soc_start: "{{ states('input_number.yeti_ev_battery_level_on_connection') | float(0) }}"
  33.         # Actual SoC% from the car's cloud API
  34.         soc_api: "{{ states('sensor.yeti_ev_battery_level') | float(0) }}"
  35.         # Current charging power converted from Watts to kW
  36.         charge_power_kw: "{{ states('sensor.peblar_ev_charger_effekt') | float(0) / 1000 }}"
  37.         # Expected charging speed (in % per hour)
  38.         soc_rate_hr: "{{ states('number.ev_smart_charging_charging_speed') | float(0) }}"
  39.         # Boolean flag: True if the EV is physically plugged into the charger
  40.         is_connected: "{{ is_state('switch.ev_smart_charging_ev_connected', 'on') }}"
  41.         # Boolean flag: True if the charger is actively delivering power right now
  42.         is_charging: "{{ is_state('binary_sensor.ev_charging_status', 'on') }}"
  43.         # Fallback value: The car API's own estimated charge time, used when unplugged
  44.         fallback_charge_duration: "{{ states('sensor.yeti_estimated_station_charge_duration') | int(0) }}"
  45.  
  46.         # --- 2. GLOBAL CONSTANTS ---
  47.         # The desired end-goal for the battery level (typically 100% for PHEVs).
  48.         # Adjust this if you routinely limit charging to e.g., 80% or 90% for battery health.
  49.         TARGET_SOC_PCT: 100
  50.         # Net battery size (usable capacity in kWh)
  51.         # BATTERY_SIZE_KWH. 13.8
  52.         BATTERY_SIZE_KWH: "{{ state_attr('sensor.variables', 'variables').YETI_BATTERY_SIZE_KWH | float(0) }}"
  53.         # Average kW your charger delivers during an active session
  54.         # CHARGER_ACTIVE_POWER_AVERAGE: 3.39
  55.         CHARGER_ACTIVE_POWER_AVERAGE: "{{ state_attr('sensor.variables', 'variables').PEBLAR_ACTIVE_POWER_AVERAGE | float(0) }}"
  56.         # Charging efficiency to account for heat/inverter loss (e.g., 90% = 0.90)
  57.         # CHARGER_EFFICIENCY: 0.775
  58.         CHARGER_EFFICIENCY: "{{ state_attr('sensor.variables', 'variables').PEBLAR_CHARGING_EFFICIENCY | float(0) / 100 }}"
  59.       state: >-
  60.         {# Calculate actual energy added considering efficiency loss #}
  61.         {% set real_energy_added = session_energy_kwh * CHARGER_EFFICIENCY %}
  62.         {% set added_soc_pct = ((real_energy_added / BATTERY_SIZE_KWH) * 100) if is_charging else 0 %}
  63.  
  64.         {# Check if target is already reached (using the highest known truth) #}
  65.         {% if [soc_start + added_soc_pct, soc_api] | max >= TARGET_SOC_PCT %}
  66.           0
  67.         {% elif is_connected %}
  68.           {% if is_charging %}
  69.             {# Active charging: Calculate remaining time dynamically based on current power #}
  70.             {% set soc_needed_pct = TARGET_SOC_PCT - (soc_start + added_soc_pct) %}
  71.             {% set time_at_ref_power = (soc_needed_pct / soc_rate_hr) %}
  72.             {% set power_scaling_factor = CHARGER_ACTIVE_POWER_AVERAGE / (charge_power_kw if charge_power_kw > 0.1 else CHARGER_ACTIVE_POWER_AVERAGE) %}
  73.             {% set remaining_hours = time_at_ref_power * power_scaling_factor %}
  74.           {% else %}
  75.             {# Suspended state: Calculate time based on theoretical reference speed #}
  76.             {% set remaining_hours = ((TARGET_SOC_PCT - soc_start) / soc_rate_hr) %}
  77.           {% endif %}
  78.           {{ [(remaining_hours * 60), 0] | max | int(0) }}
  79.         {% else %}
  80.           {# Fallback when disconnected #}
  81.           {{ fallback_charge_duration }}
  82.         {% endif %}
  83.       attributes:
  84.         soc_start: "{{ soc_start }}"
  85.         soc_api: "{{ soc_api }}"
  86.         simulated_live_soc: >-
  87.           {# Fetch previous state to use as our ratchet #}
  88.           {% set previous_soc = this.attributes.get('simulated_live_soc', soc_api) | float(soc_api) if this is defined else soc_api %}
  89.           {% if is_connected %}
  90.             {% set calculated_soc = 0 %}
  91.  
  92.             {# Only calculate added energy if actually charging #}
  93.             {% if is_charging %}
  94.               {% set real_energy_added = session_energy_kwh * CHARGER_EFFICIENCY %}
  95.               {% set calculated_soc = soc_start + ((real_energy_added / BATTERY_SIZE_KWH) * TARGET_SOC_PCT) %}
  96.             {% endif %}
  97.  
  98.             {# The Ratchet: Pick the highest truth while plugged in #}
  99.             {% set best_soc = [soc_api, previous_soc, calculated_soc] | max %}
  100.             {{ [best_soc, TARGET_SOC_PCT] | min | int(0) }}
  101.           {% else %}
  102.             {# Cable unplugged: Disable ratchet and mirror API #}
  103.             {{ soc_api | int(0) }}
  104.           {% endif %}
Advertisement
Add Comment
Please, Sign In to add comment