# yaml-language-server: $schema=null ############################################################################### # PACKAGE: 16A EV Charger Control ############################################################################### # # FILE: # packages/16a_ev_charging_controls.yaml # # VERSION: # V1.2 2026-07-20 # - Added structured mode, timing, and restart behavior documentation. # # V1.1 2026-05-15 # - Reworked 1hr topup logic to use a hard deadline and watchdog. # - Removed dependency on timer.finished event for relay shutoff. # - Offpeak until charged now continues past offpeak end until charging stops. # - 1hr topup now rolls into Normal Offpeak if the hour ends during # the offpeak window, without switching the relay off and back on. # # V1.0 2026-05-11 # - Initial package for 16A EV charger offpeak and charge-until-full control. # # PURPOSE: # Controls the Leaf 16A charger relay using scheduled offpeak operation and # user-selectable immediate or charge-until-full modes. # # DEPENDENCIES: # - switch.garage_db_control_16a_wallcharger_operation controls charger power. # - binary_sensor.garage_db_control_16a_wallcharger_charging reports whether # the vehicle is actively charging. # # CHARGE MODES: # - Normal (Offpeak) turns the relay on and off at the configured offpeak # start and end times. Selecting this mode takes no immediate action. # - Offpeak until charged starts at the offpeak time, or immediately if # selected during offpeak, and may continue beyond offpeak end until the # vehicle stops charging. # - 1hr topup now turns the relay on immediately and records a hard deadline # one hour ahead. # - Charge now until full turns the relay on immediately and keeps it on until # the vehicle stops charging. # - Stop charging now turns the relay off immediately and returns the selector # to Normal (Offpeak). # # TIMING AND COMPLETION: # - Charge-until-full modes allow five minutes for charging to start. This # also handles a vehicle that is already full. # - Charging is considered complete after the charging sensor remains off for # five minutes. # - The one-hour topup deadline is stored in input_datetime and checked every # minute and when Home Assistant starts. # - If a topup ends during offpeak, the relay remains on and the mode returns # to Normal (Offpeak), avoiding an off/on relay cycle. # - Equal offpeak start and end times are interpreted as an all-day window. # # OPERATION NOTES: # - Normal offpeak start and end use exact-time triggers. # - There is no startup catch-up if Home Assistant is offline at either time. # # PACKAGE SETTINGS: # Car display name: # - Leaf # # Charger relay output: # - switch.garage_db_control_16a_wallcharger_operation # # Vehicle charging sensor: # - binary_sensor.garage_db_control_16a_wallcharger_charging # # Default offpeak period: # - Start: 21:00:00 # - End: 00:00:00 # ############################################################################### input_datetime: ev_16a_offpeak_start: name: 16A EV Offpeak Start has_date: false has_time: true initial: "21:00:00" ev_16a_offpeak_end: name: 16A EV Offpeak End has_date: false has_time: true initial: "00:00:00" ev_16a_1hr_topup_until: name: 16A EV 1hr topup until has_date: true has_time: true initial: "2000-01-01 00:00:00" input_select: ev_16a_charge_mode: name: Leaf EV charge mode icon: mdi:ev-station options: - "Normal (Offpeak)" - "Offpeak until charged" - "1hr topup now" - "Charge now until full" - "Stop charging now" initial: "Normal (Offpeak)" automation: ############################################################################# # OFFPEAK START ############################################################################# - id: ev_16a_charger_offpeak_start alias: "16A EV Charger - Offpeak Start" mode: single triggers: - trigger: time at: input_datetime.ev_16a_offpeak_start conditions: - condition: template value_template: > {{ states('input_select.ev_16a_charge_mode') in ['Normal (Offpeak)', 'Offpeak until charged'] }} actions: - action: switch.turn_on target: entity_id: switch.garage_db_control_16a_wallcharger_operation - choose: - conditions: - condition: state entity_id: input_select.ev_16a_charge_mode state: "Offpeak until charged" sequence: # If the car never starts charging, or is already full, turn off # after a 5 minute grace period. - delay: "00:05:00" - condition: state entity_id: input_select.ev_16a_charge_mode state: "Offpeak until charged" - condition: state entity_id: binary_sensor.garage_db_control_16a_wallcharger_charging state: "off" - action: switch.turn_off target: entity_id: switch.garage_db_control_16a_wallcharger_operation - action: input_select.select_option target: entity_id: input_select.ev_16a_charge_mode data: option: "Normal (Offpeak)" ############################################################################# # OFFPEAK END ############################################################################# - id: ev_16a_charger_offpeak_end alias: "16A EV Charger - Offpeak End" mode: single triggers: - trigger: time at: input_datetime.ev_16a_offpeak_end conditions: - condition: state entity_id: input_select.ev_16a_charge_mode state: "Normal (Offpeak)" actions: - action: switch.turn_off target: entity_id: switch.garage_db_control_16a_wallcharger_operation ############################################################################# # USER CHARGE MODE SELECTIONS ############################################################################# - id: ev_16a_charger_charge_mode_selected alias: "16A EV Charger - Charge Mode Selected" mode: restart triggers: - trigger: state entity_id: input_select.ev_16a_charge_mode actions: - variables: selected_mode: "{{ trigger.to_state.state }}" is_offpeak_now: > {% set start = states('input_datetime.ev_16a_offpeak_start') %} {% set end = states('input_datetime.ev_16a_offpeak_end') %} {% set now_time = now().strftime('%H:%M:%S') %} {% if start == end %} {{ true }} {% elif start < end %} {{ start <= now_time < end }} {% else %} {{ now_time >= start or now_time < end }} {% endif %} - choose: ##################################################################### # NORMAL OFFPEAK ##################################################################### - conditions: - condition: template value_template: "{{ selected_mode == 'Normal (Offpeak)' }}" sequence: # No immediate action. # Normal mode only acts when the offpeak start/end triggers fire. - stop: "Normal offpeak mode selected." ##################################################################### # OFFPEAK UNTIL CHARGED ##################################################################### - conditions: - condition: template value_template: "{{ selected_mode == 'Offpeak until charged' }}" sequence: - if: - condition: template value_template: "{{ is_offpeak_now }}" then: - action: switch.turn_on target: entity_id: switch.garage_db_control_16a_wallcharger_operation # If the car never starts charging, or is already full, turn # off after a 5 minute grace period. - delay: "00:05:00" - condition: state entity_id: input_select.ev_16a_charge_mode state: "Offpeak until charged" - condition: state entity_id: binary_sensor.garage_db_control_16a_wallcharger_charging state: "off" - action: switch.turn_off target: entity_id: switch.garage_db_control_16a_wallcharger_operation - action: input_select.select_option target: entity_id: input_select.ev_16a_charge_mode data: option: "Normal (Offpeak)" ##################################################################### # 1 HOUR TOPUP NOW ##################################################################### - conditions: - condition: template value_template: "{{ selected_mode == '1hr topup now' }}" sequence: - action: input_datetime.set_datetime target: entity_id: input_datetime.ev_16a_1hr_topup_until data: datetime: > {{ (as_timestamp(now()) + 3600) | timestamp_custom('%Y-%m-%d %H:%M:%S', true) }} - action: switch.turn_on target: entity_id: switch.garage_db_control_16a_wallcharger_operation ##################################################################### # CHARGE NOW UNTIL FULL ##################################################################### - conditions: - condition: template value_template: "{{ selected_mode == 'Charge now until full' }}" sequence: - action: switch.turn_on target: entity_id: switch.garage_db_control_16a_wallcharger_operation # If the car never starts charging, or is already full, turn off # after a 5 minute grace period. - delay: "00:05:00" - condition: state entity_id: input_select.ev_16a_charge_mode state: "Charge now until full" - condition: state entity_id: binary_sensor.garage_db_control_16a_wallcharger_charging state: "off" - action: switch.turn_off target: entity_id: switch.garage_db_control_16a_wallcharger_operation - action: input_select.select_option target: entity_id: input_select.ev_16a_charge_mode data: option: "Normal (Offpeak)" ##################################################################### # STOP CHARGING NOW ##################################################################### - conditions: - condition: template value_template: "{{ selected_mode == 'Stop charging now' }}" sequence: - action: switch.turn_off target: entity_id: switch.garage_db_control_16a_wallcharger_operation - action: input_select.select_option target: entity_id: input_select.ev_16a_charge_mode data: option: "Normal (Offpeak)" ############################################################################# # 1 HOUR TOPUP WATCHDOG ############################################################################# - id: ev_16a_charger_1hr_topup_watchdog alias: "16A EV Charger - 1hr Topup Watchdog" mode: single triggers: - trigger: time_pattern minutes: "/1" - trigger: homeassistant event: start conditions: - condition: state entity_id: input_select.ev_16a_charge_mode state: "1hr topup now" - condition: template value_template: > {{ as_timestamp(now()) >= as_timestamp(states('input_datetime.ev_16a_1hr_topup_until')) }} actions: - variables: is_offpeak_now: > {% set start = states('input_datetime.ev_16a_offpeak_start') %} {% set end = states('input_datetime.ev_16a_offpeak_end') %} {% set now_time = now().strftime('%H:%M:%S') %} {% if start == end %} {{ true }} {% elif start < end %} {{ start <= now_time < end }} {% else %} {{ now_time >= start or now_time < end }} {% endif %} - action: input_select.select_option target: entity_id: input_select.ev_16a_charge_mode data: option: "Normal (Offpeak)" - choose: # If the 1 hour topup finishes during offpeak, do not flick the relay. # Leave it on and let the Normal Offpeak end automation turn it off. - conditions: - condition: template value_template: "{{ is_offpeak_now }}" sequence: - if: - condition: state entity_id: switch.garage_db_control_16a_wallcharger_operation state: "off" then: - action: switch.turn_on target: entity_id: switch.garage_db_control_16a_wallcharger_operation default: - if: - condition: state entity_id: switch.garage_db_control_16a_wallcharger_operation state: "on" then: - action: switch.turn_off target: entity_id: switch.garage_db_control_16a_wallcharger_operation ############################################################################# # CHARGING COMPLETE MONITOR ############################################################################# - id: ev_16a_charger_stop_when_charging_complete alias: "16A EV Charger - Stop When Charging Complete" mode: single triggers: - trigger: state entity_id: binary_sensor.garage_db_control_16a_wallcharger_charging to: "off" for: "00:05:00" conditions: - condition: template value_template: > {{ states('input_select.ev_16a_charge_mode') in ['Offpeak until charged', 'Charge now until full'] }} actions: - action: switch.turn_off target: entity_id: switch.garage_db_control_16a_wallcharger_operation - action: input_select.select_option target: entity_id: input_select.ev_16a_charge_mode data: option: "Normal (Offpeak)"