Smarter EV charging volume #2

Reading time: 8 minutes (1687 words)
Author: @pugmiester
Tags: solar , ev , energy , battery

As I mentioning in my previous blog post - Smarter EV charging from solar, I’ve been working on a way to make better use of our home solar generation. I’ll not bore you with all the details, you can go and have a read at my earlier post for that, but the tl;dr version is we need at least 1400W to charge the car but anything lower than that can charge the home battery so adjust charging speeds based on solar generation accordingly.

We’re on version 2 of the automation and I have to say that although it’s not the most elegant or efficient of solutions, it seems to be working fairly well for the last few days where we’ve had reasonable solar generation and Eddie has been at home.

The basic idea is to adjust how quickly we charge the home battery if we are generating enough solar that we could charge the car. This way, we send solar to the car when we’re generating a reasonable amount and in the cloudy spots in between, or before/after we have that 1400W magic number, we feed the house battery or export if it’s already full. This way, we get more sunny miles into Eddie if he’s home and make much more efficient use of the solar we generate so we’re exporting as little as possible.

I really struggled to get started with the automation. It seems really simple for me to just look at my Sunsynk card and see that there’s enough solar that if we were not sending it all to the battery we could be charging Eddie, but turning that into an automation seemed more complicated than it probably should.

What starts the automation running? Sunrise? Time of day? Is Eddie at home?…. Then what happens if he’s unplugged? Or it gets cloudy?

This caused a reasonable amount of decision paralysis for a couple of days at least before I stopped thinking about it as a single automation and started to break it down in smaller automations that were easier to manage.

But what combination of these sensors should start the automation, and how often should it run? Screw it, just run the automation every 5 minutes and if the input_booleans above are in their relevant states, adjust the battery charge/discharge speeds down so we can send some juice to Eddie. 5 minutes seemed like a reasonable compromise between exporting a little excess solar verses constantly adjusting the battery charge/discharge speeds as excess solar updates every 10 seconds or so.

So, lets go over each of these in a little more detail, mainly so I can include the yaml settings for other to share or pick holes in (much more likely).

Is Eddie Plugged In?

I started trying to be clever here and checking calendars and work days (I’ll add something around this as a notification later) but all we really care about at this point is if Eddie is plugged in. If he is, as determined by the Zappi plug status sensor, then we turn on input_boolean.smart_ev_charging_is_eddie_plugged_in. If the Zappi reports anything other than “EV Connected” or “Charging” he’s not plugged in so turn that input_boolean off. I know, I know, there’s a way more efficient way of doing this but it’s working for now.

alias: Smart EV Charging - Is Eddie plugged in
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.myenergi_zappi_REDACTED_plug_status
condition: []
action:
  - choose:
      - conditions:
          - condition: state
            entity_id: sensor.myenergi_zappi_REDACTED_plug_status
            state: EV Connected
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.smart_ev_charging_is_eddie_plugged_in
      - conditions:
          - condition: state
            entity_id: sensor.myenergi_zappi_REDACTED_plug_status
            state: Charging
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.smart_ev_charging_is_eddie_plugged_in
      - conditions:
          - condition: state
            entity_id: sensor.myenergi_zappi_REDACTED_plug_status
            state: Waiting for EV
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.smart_ev_charging_is_eddie_plugged_in
    default:
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.smart_ev_charging_is_eddie_plugged_in
mode: single

Is Eddie already fully charged?

This was was another nice and simple one to automate. Eddie has an integration available through HACS that’s mostly accurate. I say mostly because the connection from HA to the Peugeot cloud seems to be reliable but Eddie only “phones home” periodically so sometimes it takes a while before the values of the sensor update. Again, if the conditions are met, turn on the input_boolean.

alias: Smart EV Charging - Is Eddie over 95%
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.e2008_battery_level
condition: []
action:
  - choose:
      - conditions:
          - condition: numeric_state
            entity_id: sensor.e2008_battery_level
            above: 95
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.smart_ev_charging_is_eddie_over_95_full
    default:
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.smart_ev_charging_is_eddie_over_95_full
mode: single

Do we have enough excess solar?

This one was one of the first pieces of the puzzle I put together so I had it on a dashboard. I use a template sensor to create the final value. I take the PV power and minus from that the current “load” value from it and then minus the load already being provided by the Zappi charger as the inverter just sees that as “load”. The automation below then adjusts the relevant input_boolean.

alias: Smart EV Charging - Do we have enough excess solar
description: ""
trigger:
  - platform: time_pattern
    minutes: /5
condition:
  - condition: state
    entity_id: sun.sun
    state: above_horizon
action:
  - choose:
      - conditions:
          - condition: numeric_state
            entity_id: sensor.available_solar_excess
            above: 1500
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.smart_ev_charging_enough_excess_solar
    default:
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.smart_ev_charging_enough_excess_solar
mode: single

Adjusting the battery charge/discharge speeds

I went round and round in circles here trying to find a way to match generation and excess solar with variable charge/discharge speeds using input numbers and it’s easy to look at and adjust values as solar generation changes but for an automation it’s just simpler to pick and arbitrary number that will give us a reasonable set of values without having to micro mange the settings. I choose 100w for charge and 500w for discharge. 500w covers all of our base load through the day so seemed like a reasonable value.

I noticed during testing that sometimes there’s a little delay or even failure to set the values simultaneously so that’s why I added the delay and then set the values a second time just to be sure and this seems to work pretty reliably.

alias: Smart EV Charging - change battery speeds
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.smart_ev_charging_eddie_smart_charge
condition: []
action:
  - choose:
      - conditions:
          - condition: state
            entity_id: input_boolean.smart_ev_charging_eddie_smart_charge
            state: "on"
        sequence:
          - service: number.set_value
            data:
              value: "100"
            target:
              entity_id: number.givtcp_REDACTED_battery_charge_rate
          - delay:
              hours: 0
              minutes: 0
              seconds: 2
              milliseconds: 0
          - service: number.set_value
            data:
              value: "500"
            target:
              entity_id: number.givtcp_REDACTED_battery_discharge_rate
          - delay:
              hours: 0
              minutes: 0
              seconds: 2
              milliseconds: 0
          - service: number.set_value
            data:
              value: "100"
            target:
              entity_id: number.givtcp_REDACTED_battery_charge_rate
          - delay:
              hours: 0
              minutes: 0
              seconds: 2
              milliseconds: 0
          - service: number.set_value
            data:
              value: "500"
            target:
              entity_id: number.givtcp_REDACTED_battery_discharge_rate
    default:
      - service: number.set_value
        data:
          value: "3600"
        target:
          entity_id: number.givtcp_REDACTED_battery_charge_rate
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: number.set_value
        data:
          value: "3600"
        target:
          entity_id: number.givtcp_REDACTED_battery_discharge_rate
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: number.set_value
        data:
          value: "3600"
        target:
          entity_id: number.givtcp_REDACTED_battery_charge_rate
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: number.set_value
        data:
          value: "3600"
        target:
          entity_id: number.givtcp_REDACTED_battery_discharge_rate
mode: single

Gluing it all together

So, finally, the automation that makes it all work. Here we run every 5 minutes and check the state of the relevant input_booleans. If they are in the right condition confirming that we could charge Eddie, turn on the battery charge input_boolean (automation above) and let its automation work so we can charge Eddie. Otherwise, we switch back to the house battery to soak up the excess solar.

alias: "[Time] Every 5 minutes - Smart EV Charging for Eddie"
description: >-
  Runs every 5 minutes and checks the conditions needed to activate or
  deactivate the Smart EV Charging input_boolean
trigger:
  - platform: time_pattern
    minutes: /5
condition: []
action:
  - choose:
      - conditions:
          - condition: state
            entity_id: input_boolean.smart_ev_charging_is_eddie_plugged_in
            state: "on"
          - condition: state
            entity_id: input_boolean.smart_ev_charging_enough_excess_solar
            state: "on"
          - condition: state
            entity_id: input_boolean.smart_ev_charging_is_eddie_over_95_full
            state: "off"
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.smart_ev_charging_eddie_smart_charge
    default:
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.smart_ev_charging_eddie_smart_charge
mode: single

I put all of the sensors, automations and input_boolean’s onto a single dashboard so I could keep an eye on everything to make sure it was working as expected, and it turns out that was a great idea as it really does help make it obvious how the individual automations are making their relevant decisions.

Smart EV charging dashboard

In the end it turned out to be “relatively” simple to glue all of the parts together. I know it’s not very efficient but it’s working. I’ll add some better logic for version 3 so we’re not wasting resources by running the automation after sunset or before sunrise or if Eddie’s not even at home but for now it works and seems to be helping us use a little more of our generation at source.

Next on the list is working on the overnight charging situation, so we don’t just unload the entire capacity of the home battery into Eddie (like we did this morning) and leave us with next to nothing for the rest of the day, especially if the solar forecast (for tomorrow/today depending exactly when the charge happens) is expected to be pretty grim.