Overnight Charge Settings with Optimum Charge Rate

Post Reply
RyanMorgan
Posts: 17
Joined: Fri Oct 14, 2022 8:16 am

Someone on the group asked if I could share my YAML for the overnight charge automation I built.
It's made up of a custom sensor, two input number and 3 separate automations.

How does it work?
Well in a nutshell, you choose a target SoC level that you want and when the set time arrives (usually the start of an off-peak period like Octopus Go or IO), it will set a controlled charge.
It works out the optimum charge rate (A) required to charge the batteries to the desired SoC over a charge time set by you (usually the duration of an off-peak period but can be less if you want to charge faster).
Once the charge time has run its course, the charging will stop and another automation will hold the charge until the settings are reset at a later set time (usually the end of an off-peak period).
If you set a desired SoC level that is below the current SoC at the start of the charge period - it will allow the batteries to discharge to the desired level and then hold until the end of the charge cycle.

During the winter, I have the setting permanently set at 100% and I rely on this automation to charge to 100% at the optimum charge rate to utilise the full off-peak period.
During the summer, there is usually enough solar generated to charge the battery during the day - so depending on the forecasted solar, I might set it lower to leave enough battery space to store the solar without the risk of exporting.

Let's say the next day is forecasted to fill my battery, I might set a low SoC and the automation will control the discharge down to the desired level.
Conversely if the next day isn't as bright but I still want to add some charge, it will work out the optimum charge rate when adding the desired amount.
For example, if I only wanted to add 20% charge over 5 hours, it would set the charge rate to 2.2A in my case.
If I wanted to charge from 10% to 100% over 5 hours, it would set the charge rate to 9A.
You get the picture :)

That being said, it could be argued that now Octopus pay double the the import rate to export - you may find that it's worth going to 100% anyway and simply export the excess. So the charge setting may become redundant and locked to 100%.

--------------------------------

Let's get set up...

You'll need:
  • The FoxESS - Modbus integration (by Nathan Marlor)
  • An input number for the SoC level you want.
  • An input number for the amount of time you want to charge over.
  • A custom sensor to calculate the charge rate (A) required depending on desired charge amount and charge time.
  • An automation to set the overnight charge at a specific time (usually the start of an off-peak period).
  • An automation to reset the charge settings at a specific time (usually at the end of an off-peak period).
  • An automation that monitors for when the charge reaches the desired level and then sets the battery to hold.

1) Set up an input number and name it "Fox Battery Overnight Setting".
Copy the settings in the image below.
You can modify the step size if you want more precise control over the SoC - I've set 5% as the step size personally.
Fox setting input number.png

2) Set up an input number and name it "Charge Hours".
Copy the settings in the image below.
You can amend the hours accordingly for the slider - I have it set from 0.5 hours to 6 hours which is more than enough for most off-peak periods.
charge time required.png

3) Copy the sensor YAML into your configuration file under one of your templates.
This is a "new" style sensor format so will need to be placed carefully in your YAML to group with existing sensors under the new format. If you use the old style format, make sure this is placed above the row that says "sensor:".
Just make sure that the sensors and input numbers in this code match what you have for your HA.
If you have just one inverter, have Nathan Marlors FoxESS Modbus integration and have the above two input sensors set up - then this code should match perfectly.

Code: Select all

template:
  - sensor:
    - name: "Charge Rate (A)"
      unit_of_measurement: "A"
      state: >
        {% if states('sensor.battery_soc') in ['unknown', 'unavailable'] %}
          {{ 0 }}
        {% else %}
          {% if (states('sensor.battery_soc')|float > (states('input_number.fox_battery_overnight_setting')|float * 100)) %}
            {{ 0 | float }}
          {% else %}
            {% set batneeded = ((states('input_number.fox_battery_overnight_setting') | float - states('sensor.battery_soc') | float) / 100) %}
            {% set batcapacity = ((states('sensor.bms_kwh_remaining')|float / states('sensor.battery_soc')|float) * 100) %}
            {% set chargeneeded = ((batcapacity * 1000) * batneeded) %}
            {% set ampscalc = (chargeneeded / states('sensor.batvolt')|float) |round(0,default=0) %}
            {% set outcome = (ampscalc / states('input_number.charge_hours')|float) |round(1,default=0) %}
            {% set result = 25 if outcome > 25 else outcome %}
            {{ result }}
          {% endif %}
        {% endif %}
4) Copy the first automation that sets the charge period.
You'll need to do/check the following:
  • Check that the trigger time is suitable - I have the IO tariff, so it starts at 11.30PM.
  • You'll need to find the inverter ID for your own inverter - easiest way to do this is to set it using the visual editor after copying the YAML code into place.
  • You'll also need to check the charge times you need - I have two set up as my off-peak period goes over midnight - however with Go, you can just have one set up and delete the other.

Code: Select all

alias: Set FoxESS Behaviour Overnight
description: ""
trigger:
  - platform: time
    at: "23:30:00"
condition: []
action:
  - if:
      - condition: numeric_state
        entity_id: sensor.battery_soc
        below: input_number.fox_battery_overnight_setting
    then:
      - service: automation.turn_on
        data: {}
        target:
          entity_id: automation.check_for_battery_soc_reached
      - service: number.set_value
        target:
          entity_id: number.max_charge_current
        data:
          value: "{{ (states('sensor.charge_rate_a') | float) }}"
      - service: foxess_modbus.update_charge_period
        data:
          inverter: 6be5bb4feedac189c5bf85cac8ea83f0
          enable_force_charge: true
          enable_charge_from_grid: true
          start: "23:30:00"
          end: "23:59:00"
          charge_period: "1"
      - service: foxess_modbus.update_charge_period
        data:
          inverter: 6be5bb4feedac189c5bf85cac8ea83f0
          enable_force_charge: true
          enable_charge_from_grid: true
          start: "00:01:00"
          end: "05:30:00"
          charge_period: "2"
  - service: number.set_value
    target:
      entity_id: number.max_soc
    data:
      value: "{{ (states('input_number.fox_battery_overnight_setting') | int(0)) }}"
  - service: number.set_value
    target:
      entity_id: number.min_soc_on_grid
    data:
      value: "{{ (states('input_number.fox_battery_overnight_setting') | int(0)) }}"
mode: single

5) Copy the second automation that resets the inverter back to normal settings for day use.
You'll need to do/check the following:
  • Check that the trigger time is suitable - I have the IO tariff, so it ends at 05:30AM.
  • You'll need to find the inverter ID for your own inverter - easiest way to do this is to set it using the visual editor after copying the YAML code into place.
  • You'll also need to check the charge times reflect the previous automation - if you only have one window set then you need only one window in this automation too.

Code: Select all

alias: Reset Inverter Settings at Peak
description: ""
trigger:
  - platform: time
    at: "05:30:00"
condition: []
action:
  - device_id: 6be5bb4feedac189c5bf85cac8ea83f0
    domain: number
    entity_id: number.min_soc_on_grid
    type: set_value
    value: 10
  - device_id: 6be5bb4feedac189c5bf85cac8ea83f0
    domain: number
    entity_id: number.max_soc
    type: set_value
    value: 100
  - service: foxess_modbus.update_charge_period
    data:
      enable_force_charge: false
      enable_charge_from_grid: false
      start: "00:00:00"
      end: "00:30:00"
      charge_period: "1"
      inverter: 6be5bb4feedac189c5bf85cac8ea83f0
  - service: foxess_modbus.update_charge_period
    data:
      enable_force_charge: false
      enable_charge_from_grid: false
      start: "00:00:00"
      end: "00:30:00"
      charge_period: "2"
      inverter: 6be5bb4feedac189c5bf85cac8ea83f0
  - service: automation.turn_off
    data:
      stop_actions: true
    target:
      entity_id: automation.check_for_battery_soc_reached
  - service: number.set_value
    target:
      entity_id: number.max_charge_current
    data:
      value: "25"
mode: single

6) Copy the third and final automation that is enabled by the first automation when the desired SoC is higher that the current SoC.
You'll need to do/check the following:
  • Make sure this is disabled as soon as you create it.
  • You'll need to find the inverter ID for your own inverter - easiest way to do this is to set it using the visual editor after copying the YAML code into place.
  • You'll also need to check the charge times reflect the first automation - if you only have one window set then you need only one window in this automation too.

Code: Select all

alias: Check for Battery SoC Reached
description: ""
trigger:
  - platform: time_pattern
    seconds: /30
condition:
  - condition: or
    conditions:
      - condition: numeric_state
        entity_id: sensor.battery_soc
        above: input_number.fox_battery_overnight_setting
action:
  - service: foxess_modbus.update_charge_period
    data:
      enable_force_charge: true
      enable_charge_from_grid: false
      start: "23:30:00"
      end: "23:59:00"
      charge_period: "1"
      inverter: 6be5bb4feedac189c5bf85cac8ea83f0
  - service: foxess_modbus.update_charge_period
    data:
      enable_force_charge: true
      enable_charge_from_grid: false
      start: "00:01:00"
      end: "05:30:00"
      charge_period: "2"
      inverter: 6be5bb4feedac189c5bf85cac8ea83f0
  - device_id: 6be5bb4feedac189c5bf85cac8ea83f0
    domain: number
    entity_id: number.min_soc_on_grid
    type: set_value
    value: 10
  - device_id: 6be5bb4feedac189c5bf85cac8ea83f0
    domain: number
    entity_id: number.max_soc
    type: set_value
    value: 100
mode: single
--------------------------

That should be it - I'd monitor that the automation is up and works the first time it's due to trigger.
There are some warnings:
  • If the HA is down or if the automation fails for any reason (mine has never failed aside from HA being unavailable due to an internet problem a while back) then the charge automation won't trigger.
  • Similarly, if it has been trigger and something happens to HA, then it will not revert back to normal settings and your inverter SoC will continue to be held and your home will pull from the grid.



If you want to have the card in the image below on your desktop - create one with the YAML code below.
You can also create buttons that trigger the first two automations and force them to run if needed during the day.
Fox Charge Settings.png

Code: Select all

type: entities
entities:
  - entity: input_number.fox_battery_overnight_setting
  - entity: input_number.charge_hours
  - entity: sensor.charge_rate_a
  - entity: number.max_charge_current
  - entity: number.max_discharge_current
  - entity: number.min_soc
  - entity: number.min_soc_on_grid
  - entity: number.max_soc
  - entity: select.work_mode


Any issues, questions or suggestions - don't hesitate to leave a message below or get in touch on Facebook
Last edited by RyanMorgan on Mon Dec 04, 2023 5:28 pm, edited 1 time in total.
HappyFeet
Posts: 4
Joined: Thu Nov 30, 2023 7:35 pm

Thanks for this guide can't wait to get it setup. However I stuck on Step 3 where you mentioned about setting up the sensor yaml template. Please can you help with how and where the files are meant to reside?

Thanks
RyanMorgan
Posts: 17
Joined: Fri Oct 14, 2022 8:16 am

HappyFeet wrote: Thu Nov 30, 2023 7:37 pm Thanks for this guide can't wait to get it setup. However I stuck on Step 3 where you mentioned about setting up the sensor yaml template. Please can you help with how and where the files are meant to reside?

Thanks
Hi there!

The yaml code needs to be inserted into the configuration.yaml file which you can see and edit by installing file editor within home assistant. You can find this by going to Settings > Add on.

Once you have installed it, you should be able to navigate to find the file above easily :)
Once you’ve inserted then code, save and exit the file and then restart HA.

I’ve added a screenshot below using my phone app to show what it should look like.
IMG_0067.jpeg
HappyFeet
Posts: 4
Joined: Thu Nov 30, 2023 7:35 pm

RyanMorgan wrote: Fri Dec 01, 2023 12:10 am
HappyFeet wrote: Thu Nov 30, 2023 7:37 pm Thanks for this guide can't wait to get it setup. However I stuck on Step 3 where you mentioned about setting up the sensor yaml template. Please can you help with how and where the files are meant to reside?

Thanks
Hi there!

The yaml code needs to be inserted into the configuration.yaml file which you can see and edit by installing file editor within home assistant. You can find this by going to Settings > Add on.

Once you have installed it, you should be able to navigate to find the file above easily :)
Once you’ve inserted then code, save and exit the file and then restart HA.

I’ve added a screenshot below using my phone app to show what it should look like.

IMG_0067.jpeg
Thanks Ryan. I have a separate sensors.yaml file and get this error when doing a quick reload:
Failed to reload configuration
Cannot quick reload all YAML configurations because the configuration is not valid: Invalid config for [sensor.template]: expected dictionary for dictionary value @ data['sensors']['name']. Got 'Charge Rate (A)' expected dictionary for dictionary value @ data['sensors']['state']. Got "{% if states('sensor.fox_ess_h1_battery_soc') in ['unknown', 'unavailable'] %}\n {{ 0 }}\n{% else %}\n {% if (states('sensor.fox_ess_h1_battery_soc')|float > (states('input_number.fox_battery_overnight_setting')|float * 100)) %}\n {{ 0 | float }}\n {% else %}\n {% set batneeded = ((states('input_number.fox_battery_overnight_setting') | float - states('sensor.fox_ess_h1_battery_soc') | float) / 100) %}\n {% set batcapacity = ((states('sensor.fox_ess_h1_bms_kwh_remaining')|float / ... expected dictionary for dictionary value @ data['sensors']['unit_of_measurement']. Got 'A'. (See ?, line ?).

I have attached what my sensor code looks like.

Thanks
Attachments
sensors.yaml.png
RyanMorgan
Posts: 17
Joined: Fri Oct 14, 2022 8:16 am

HappyFeet wrote: Fri Dec 01, 2023 8:37 am

Thanks Ryan. I have a separate sensors.yaml file and get this error when doing a quick reload:
Failed to reload configuration
Cannot quick reload all YAML configurations because the configuration is not valid: Invalid config for [sensor.template]: expected dictionary for dictionary value @ data['sensors']['name']. Got 'Charge Rate (A)' expected dictionary for dictionary value @ data['sensors']['state']. Got "{% if states('sensor.fox_ess_h1_battery_soc') in ['unknown', 'unavailable'] %}\n {{ 0 }}\n{% else %}\n {% if (states('sensor.fox_ess_h1_battery_soc')|float > (states('input_number.fox_battery_overnight_setting')|float * 100)) %}\n {{ 0 | float }}\n {% else %}\n {% set batneeded = ((states('input_number.fox_battery_overnight_setting') | float - states('sensor.fox_ess_h1_battery_soc') | float) / 100) %}\n {% set batcapacity = ((states('sensor.fox_ess_h1_bms_kwh_remaining')|float / ... expected dictionary for dictionary value @ data['sensors']['unit_of_measurement']. Got 'A'. (See ?, line ?).

I have attached what my sensor code looks like.

Thanks

It looks as if you’ve set up the sensor in the old template format at the beginning.
Try make sure you copy exactly the first 5 lines starting with “template:” and make sure the indentations line up exactly too.
HappyFeet
Posts: 4
Joined: Thu Nov 30, 2023 7:35 pm

RyanMorgan wrote: Sat Dec 02, 2023 12:59 am
HappyFeet wrote: Fri Dec 01, 2023 8:37 am

Thanks Ryan. I have a separate sensors.yaml file and get this error when doing a quick reload:
Failed to reload configuration
Cannot quick reload all YAML configurations because the configuration is not valid: Invalid config for [sensor.template]: expected dictionary for dictionary value @ data['sensors']['name']. Got 'Charge Rate (A)' expected dictionary for dictionary value @ data['sensors']['state']. Got "{% if states('sensor.fox_ess_h1_battery_soc') in ['unknown', 'unavailable'] %}\n {{ 0 }}\n{% else %}\n {% if (states('sensor.fox_ess_h1_battery_soc')|float > (states('input_number.fox_battery_overnight_setting')|float * 100)) %}\n {{ 0 | float }}\n {% else %}\n {% set batneeded = ((states('input_number.fox_battery_overnight_setting') | float - states('sensor.fox_ess_h1_battery_soc') | float) / 100) %}\n {% set batcapacity = ((states('sensor.fox_ess_h1_bms_kwh_remaining')|float / ... expected dictionary for dictionary value @ data['sensors']['unit_of_measurement']. Got 'A'. (See ?, line ?).

I have attached what my sensor code looks like.

Thanks

It looks as if you’ve set up the sensor in the old template format at the beginning.
Try make sure you copy exactly the first 5 lines starting with “template:” and make sure the indentations line up exactly too.
Thanks Ryan. I am getting this error now seems like it's not liking the code above maybe?
Attachments
sensors.yaml.png
RyanMorgan
Posts: 17
Joined: Fri Oct 14, 2022 8:16 am

HappyFeet wrote: Sat Dec 02, 2023 4:43 pm

Thanks Ryan. I am getting this error now seems like it's not liking the code above maybe?
It looks absolutely fine, it probably down to the way you've place this sensor within the config file itself. It's very touchy and in my opinion, quite annoying!

It seems to me like you may have inserted lines 30 - 49 inside a "sensor:" grouping.
Try cutting this code out (30-49) and pasting it above wherever it says "sensor:" in your code - this is likely to be in the first couple of lines of this file.


If this fails, I'd be happy to DM you so you can send me your file to check over?
HappyFeet
Posts: 4
Joined: Thu Nov 30, 2023 7:35 pm

RyanMorgan wrote: Mon Dec 04, 2023 5:25 pm
HappyFeet wrote: Sat Dec 02, 2023 4:43 pm

Thanks Ryan. I am getting this error now seems like it's not liking the code above maybe?
It looks absolutely fine, it probably down to the way you've place this sensor within the config file itself. It's very touchy and in my opinion, quite annoying!

It seems to me like you may have inserted lines 30 - 49 inside a "sensor:" grouping.
Try cutting this code out (30-49) and pasting it above wherever it says "sensor:" in your code - this is likely to be in the first couple of lines of this file.


If this fails, I'd be happy to DM you so you can send me your file to check over?
Hi Ryan,

Apologies for the delay in coming back. I tried what you mentioned by placing the lines 30-49 at the top (above the existing sensor) but I think it's still complaining about the separator and might be due to the old type of sensors, mixed in with the new ones.

Old template:
- platform: template
sensors:

New template:
template:
- sensor:
- name: "Charge Rate (A)"

Also have this:
- platform: foxess
username:
password:
deviceID:

Thanks
Post Reply