Battery Charge Throttle

Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

Battery Charge throttle

The LifePo4 (LFP) batteries used by FoxESS systems are very stable and well suited to being used as a solar home battery.

LFP batteries have a very flat voltage profile across 85% of their usable range, with a rapid fall below 10% minsoc, and a rapid increase when nearing full.
The Foxess battery charger (in the inverter) slows the charge it nears 100% SoC to avoid unnecessary heating and stress, it does this in large steps by ramping charge current often something like (4kw>2kw>1kw>off) - the BMS having access to all of the cell temperatures, voltages and SoC's has final control of the cut-off point and this is why occasionally you see the charge end whilst the battery is only 95% (ish) this happens more often with solar charge because SoC has drifted over time.

The more advanced battery chargers watch individual voltage and cell temperatures, and they reduce the charge current whilst holding charge voltage - this avoids the rapid internal temperature rise and it gives the batteries the maximum time for the charge to be absorbed safely and also helps improve top balancing.

The Foxess charger (in the inverter) ramps the charge current down as it approaches full (the BMS controls it using the BMS charge rate register) but it is not that fine a control, and often occurs too near the limit and does not appear to voltage hold whilst reducing current.

It does a reasonable job, in winter the warming of the batteries is a good thing but in summer it can add excessive heat stress and so I have adapted the charge profile using a Home Assistant automation and RS485 interface to the inverter so that it gives me a finer control, I use this to gradually taper the charge current once the battery passes 80% SoC and it has a very soft profile above 90%.

Using this automation will change the day charge profile to look as follows -
graph.jpg


and the night charge profile as this -
nightprofile.png

The pre-requisites for this automation are having a Foxess Hybrid inverter and batteries (HV,Mira,ECS) and a Home Assistant which is connected by RS485 interface using the Foxess_modbus integration that can be found at https://github.com/nathanmarlor/foxess_modbus

I would recommend you only implement this if you are an advanced user and understand the implications of charge control.

This automation requires the creation of a number of helpers and a template sensor.

Beginning with the Helpers

The first helper is a toggle that enables or disables the charge throttle automation - I typically have it switched on all the time, the main reason I turn it off is speed of charge - reducing the battery current takes the charge much longer to complete, it will achieve 80% in the usual time it takes, whereas the final 20% can take several hours to complete - very useful when charging from solar in summer, but not so useful when charging off-grid during a short eco tariff period in winter.

Helpers can be created in Home Assistant by going into Settings, Devices & Services, click on 'Helpers' at top right of the screen, click '+ CREATE HELPER' and the select Toggle.

It should be set as follows -
IMG_1422.PNG

The second helper is used by the automation to record what the battery_soc was last time it was triggered, and makes it not trigger again until the battery_soc changes.

As above go to create a helper, choose 'Number' and set is as follows -
bc.jpg

The third helper is a number that is used to hold the calculated charge value in amps, it should be sets as follows -
IMG_1424.PNG

The next helper is used to hold the maximum battery dc charge current - this is the maximum value the charge current will be for a normal charge, it is also used as the base multiplier for the charge current calculation.

To calculate the desired charge current from your normal charge rate - if you have a normal charge rate of 4kw, you need look at your nominal battery voltage (the mid point between high and low) - for example on an HV2600 pack with 4 batteries this will be approx 211V. Current (A) = Power(W) / Volts(V) so in this example 4kw = 4000W/211V = 19A and you would set the maximum charge rate to 19A

If desired you can reduce this to limit the max charge your inverter uses (useful in summer to balance charge with export), or increase it, the maxmimum charge you can achieve is usually the Hybrid inverter limit or the maximum PV string output if greater i.e. H1-3.7 is 3.7kw, H1-5.0 is 5kw.

It should be set as follows -
IMG_1425.PNG

The final helper is used to select Day or Night mode, Day mode uses the day charge curve (longer and slower charge to make use of the full days solar, it slows charge dramatically above 80% SoC), Night mode uses the night charge curve (shorter and faster in duration to make use of low tariff grid charging, it only operates from 90% SoC).

It should be set as follows -
bctnm.jpg

SENSORS
Then create a sensor that is used simply as a display to show you the current charge rate in watts - this is based on the current charge rate and battery voltage.

It looks like this

Code: Select all

      - name: "Current charge rate"
        unit_of_measurement: "W"
        device_class: power
        state: >
           {{ (( ( (states('sensor.batvolt') | float(0)) * states('number.max_charge_current') | float(0)))) | round(0) }}

JINJA MACRO
Finally you need to create 2 Jinja imports, these are useful to create small macros that are re-usable, for example if you regularly convert temperature between C and F you can create a jinja template, store it as a macro and it becomes re-usable (similar to a macro or function in code)

in the /config/custom_templates folder create a file called charge_curve_day.jinja

copy and paste this code into the file and save it

Code: Select all

{% macro get_charge_limit(entity_id) %}

{% if states(entity_id)|int in [80] %}
  {{ 0.8 }}
{% elif states(entity_id)|int in [81] %}
  {{ 0.66 }}
{% elif states(entity_id)|int in [82] %}
  {{ 0.57 }}
{% elif states(entity_id)|int in [83] %}
  {{ 0.5 }}
{% elif states(entity_id)|int in [84] %}
  {{ 0.45 }}
{% elif states(entity_id)|int in [85] %}
  {{ 0.42 }}
{% elif states(entity_id)|int in [86] %}
  {{ 0.40 }}
{% elif states(entity_id)|int in [87] %}
  {{ 0.38 }}
{% elif states(entity_id)|int in [88] %}
  {{ 0.36 }}
{% elif states(entity_id)|int in [89] %}
  {{ 0.34 }}
{% elif states(entity_id)|int in [90] %}
  {{ 0.33 }}
{% elif states(entity_id)|int in [91] %}
  {{ 0.32 }}
{% elif states(entity_id)|int in [92] %}
  {{ 0.30 }}
{% elif states(entity_id)|int in [93] %}
  {{ 0.29 }}
{% elif states(entity_id)|int in [94] %}
  {{ 0.28 }}
{% elif states(entity_id)|int in [95] %}
  {{ 0.25 }}
{% elif states(entity_id)|int in [96] %}
  {{ 0.24 }}
{% elif states(entity_id)|int in [97] %}
  {{ 0.22 }}
{% elif states(entity_id)|int in [98] %}
  {{ 0.2 }}
{% elif states(entity_id)|int in [99] %}
  {{ 0.18 }}
{% elif states(entity_id)|int in [100] %}
  {{ 0.15 }}
{% else %}
  {{ 1 }}
{% endif %}

{% endmacro %}


Then in the /config/custom_templates folder create a second file called charge_curve_night.jinja

copy and paste this code into the file and save it

Code: Select all



{% macro get_charge_limit(entity_id) %}

{% if states(entity_id)|int in [80] %}
  {{ 1 }}
{% elif states(entity_id)|int in [81] %}
  {{ 1 }}
{% elif states(entity_id)|int in [82] %}
  {{ 1 }}
{% elif states(entity_id)|int in [83] %}
  {{ 1 }}
{% elif states(entity_id)|int in [84] %}
  {{ 1 }}
{% elif states(entity_id)|int in [85] %}
  {{ 1 }}
{% elif states(entity_id)|int in [86] %}
  {{ 1 }}
{% elif states(entity_id)|int in [87] %}
  {{ 1 }}
{% elif states(entity_id)|int in [88] %}
  {{ 1 }}
{% elif states(entity_id)|int in [89] %}
  {{ 1 }}
{% elif states(entity_id)|int in [90] %}
  {{ 0.8 }}
{% elif states(entity_id)|int in [91] %}
  {{ 0.66 }}
{% elif states(entity_id)|int in [92] %}
  {{ 0.57 }}
{% elif states(entity_id)|int in [93] %}
  {{ 0.49 }}
{% elif states(entity_id)|int in [94] %}
  {{ 0.42 }}
{% elif states(entity_id)|int in [95] %}
  {{ 0.36 }}
{% elif states(entity_id)|int in [96] %}
  {{ 0.3 }}
{% elif states(entity_id)|int in [97] %}
  {{ 0.24 }}
{% elif states(entity_id)|int in [98] %}
  {{ 0.2 }}
{% elif states(entity_id)|int in [99] %}
  {{ 0.17 }}
{% elif states(entity_id)|int in [100] %}
  {{ 0.14 }}
{% else %}
  {{ 1 }}
{% endif %}

{% endmacro %}



Once it is saved these jinja import macros are loaded at startup, or by going to Developer Tools, Services and select 'Home Assistant Core Integration: Reload custom Jinja2 templates' - click 'Call Service ' and they will be available to use.

You can test in Developer Tools, Templates with the following test (for day mode)-

Code: Select all

{% from 'charge_curve_day.jinja' import get_charge_limit %}
{{ get_charge_limit('sensor.battery_soc')|float(1) }}
It will return '1' for less than 80% battery_soc and a fraction when 80% or above

This code is the multiplier % applied to the maximum battery dc charge limit, the % stored against each value is the % used at that battery_soc for example in day mode at 85% soc the value is 0.42, in the max charge example above this would then give a charge rate of 19A * 0.42 = 7.98A (rounded to 8) and using the nominal 211v it would be charging at 1,684W


DASHBOARD CARDS
Create a card in your dashboard with the following

Code: Select all

type: entities
entities:
  - entity: sensor.battery_soc
  - entity: input_number.battery_max_charge
    name: Maximum Charge Limit
  - entity: input_number.battery_charge_limit
    name: Calculated Charge (A)
  - entity: sensor.batvolt
  - entity: sensor.current_charge_rate
    name: Calculated Charge rate (W)
  - entity: input_boolean.battery_charge_throttle
  - entity: input_boolean.battery_charge_throttle_night_mode
state_color: true


AUTOMATION
And finally add the automation which makes sense of all this -

Settings, Automations, '+ CREATE AUTOMATION', 'create new automation (start from scratch)', switch to YAML mode by clicking the three dots in the top right corner and 'edit in yaml', then copy this code over the top of the blank code there.

Code: Select all

alias: Battery Charge Throttle
description: ""
trigger:
  - platform: time_pattern
    seconds: /30
condition:
  - condition: or
    conditions:
      - condition: and
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity_id: sensor.battery_soc
                above: input_number.battery_charge_last_soc
              - condition: numeric_state
                entity_id: sensor.battery_soc
                below: input_number.battery_charge_last_soc
          - condition: or
            conditions:
              - condition: and
                conditions:
                  - condition: numeric_state
                    entity_id: sensor.battery_soc
                    below: 79
                  - condition: or
                    conditions:
                      - condition: numeric_state
                        entity_id: input_number.battery_charge_limit
                        below: input_number.battery_max_charge
                      - condition: numeric_state
                        entity_id: input_number.battery_charge_limit
                        above: input_number.battery_max_charge
              - condition: and
                conditions:
                  - condition: numeric_state
                    entity_id: sensor.battery_soc
                    above: 79
      - condition: time
        after: "07:00:00"
        before: "07:01:00"
      - condition: time
        after: "20:00:00"
        before: "20:01:00"
action:
  - if:
      - condition: or
        conditions:
          - condition: time
            after: "07:00:00"
            before: "07:01:00"
          - condition: time
            before: "20:01:00"
            after: "20:00:00"
    then:
      - if:
          - condition: time
            after: "20:00:00"
            before: "20:01:00"
        then:
          - if:
              - condition: state
                entity_id: input_boolean.battery_charge_throttle_night_mode
                state: "off"
            then:
              - service: input_boolean.turn_on
                data: {}
                target:
                  entity_id: input_boolean.battery_charge_throttle_night_mode
              - service: logbook.log
                data:
                  entity_id: input_boolean.battery_charge_throttle_night_mode
                  message: Battery Charge Throttle Night Mode turned ON
                  name: Charge Throttle
                  domain: battery
      - if:
          - condition: time
            after: "07:00:00"
            before: "07:01:00"
        then:
          - if:
              - condition: state
                entity_id: input_boolean.battery_charge_throttle_night_mode
                state: "on"
            then:
              - service: input_boolean.turn_off
                data: {}
                target:
                  entity_id: input_boolean.battery_charge_throttle_night_mode
              - service: logbook.log
                data:
                  entity_id: input_boolean.battery_charge_throttle_night_mode
                  message: Battery Charge Throttle Night Mode turned OFF
                  name: Charge Throttle
                  domain: battery
    else:
      - if:
          - condition: and
            conditions:
              - condition: numeric_state
                entity_id: sensor.battery_soc
                above: 79
              - condition: numeric_state
                entity_id: sensor.battery_charge
                above: 0.15
                enabled: true
              - condition: state
                entity_id: input_boolean.battery_charge_throttle
                state: "on"
        then:
          - if:
              - condition: numeric_state
                entity_id: input_number.battery_charge_limit
                above: 0.1
            then:
              - service: input_number.set_value
                data:
                  value: >-
                    {% if states('input_boolean.battery_charge_throttle_night_mode')=="on" %}
                      {% from 'charge_curve_night.jinja' import get_charge_limit %}
                    {% else %}
                      {% from 'charge_curve_day.jinja' import get_charge_limit %}
                    {% endif %}
                    {% set cl = get_charge_limit('sensor.battery_soc')|float(1) %}
                    {% if cl > 1 or cl <= 0 %}
                      {% set bcl = (1 * (states('input_number.battery_max_charge')|int(8)) ) %}
                    {% else %}
                      {% set bcl = (cl * (states('input_number.battery_max_charge')|int(8)) ) %}
                    {% endif %}
                    {{ bcl|round(1) }}
                target:
                  entity_id: input_number.battery_charge_limit
              - if:
                  - condition: template
                    value_template: >-
                      {{ (states('input_number.battery_charge_limit')|round(1)
                      != states('number.max_charge_current')|round(1)) }}
                then:
                  - service: logbook.log
                    data:
                      entity_id: number.max_charge_current
                      message: >-
                        Setting Max Charge Current ={{(states('input_number.battery_charge_limit')|round(1,0))}},
                        SoC={{ states( 'sensor.battery_soc' )|int }} , 
                        Night Mode={{states('input_boolean.battery_charge_throttle_night_mode') }}
                      name: Charge Current
                      domain: battery
                  - service: number.set_value
                    data:
                      value: >-
                        {{states('input_number.battery_charge_limit')|round(1,0) }}
                    target:
                      entity_id: number.max_charge_current
                  - service: logbook.log
                    data:
                      entity_id: number.max_charge_current
                      message: >-
                        Max Charge Current is set to ={{(states('number.max_charge_current')|round(1,0))}},
                        SoC={{ states( 'sensor.battery_soc' )|int }} , Night Mode={{states('input_boolean.battery_charge_throttle_night_mode')}}
                      name: Charge Current
                      domain: battery
                else:
                  - service: logbook.log
                    data:
                      entity_id: number.max_charge_current
                      message: >-
                        Max Charge Current is already set to ={{(states('number.max_charge_current')|round(1,0))}} no need to set again, 
                        SoC={{ states( 'sensor.battery_soc' )|int }} , 
                        Night Mode={{states('input_boolean.battery_charge_throttle_night_mode') }}
                      name: Charge Current
                      domain: battery
            else:
              - service: logbook.log
                data:
                  entity_id: number.max_charge_current
                  message: >-
                    Battery Charge limit invalid ={{(states('input_number.battery_charge_limit')|float(0))|round(1)}},
                    SoC={{ states( 'sensor.battery_soc' )|int }}, Night Mode={{states('input_boolean.battery_charge_throttle_night_mode')}}
                  name: Charge Current
                  domain: battery
        else:
          - if:
              - condition: or
                conditions:
                  - condition: state
                    entity_id: input_boolean.battery_charge_throttle
                    state: "off"
                  - condition: numeric_state
                    entity_id: sensor.battery_soc
                    below: 79
            then:
              - if:
                  - condition: or
                    conditions:
                      - condition: numeric_state
                        entity_id: input_number.battery_charge_limit
                        below: input_number.battery_max_charge
                      - condition: numeric_state
                        entity_id: input_number.battery_charge_limit
                        above: input_number.battery_max_charge
                then:
                  - service: input_number.set_value
                    data:
                      value: "{{ states('input_number.battery_max_charge')|int(8) }}"
                    target:
                      entity_id: input_number.battery_charge_limit
                  - service: logbook.log
                    data:
                      entity_id: number.max_charge_current
                      message: >-
                        Reset Max Charge Current ={{(states('number.max_charge_current')|float(0))|round(1)}}
                      name: Charge Current
                      domain: battery
                  - service: number.set_value
                    data:
                      value: >-
                        {{ states('input_number.battery_charge_limit')|round(1,0)}}
                    target:
                      entity_id: number.max_charge_current
      - service: input_number.set_value
        data:
          value: >-
            {% set Batsoc = states('sensor.battery_soc')|int(0) %} {{ Batsoc  }}   
        target:
          entity_id: input_number.battery_charge_last_soc
mode: single

Some examples of the sensors at various battery_soc's (this is an 8 pack system, ~422V nominal)
bc1.jpg
bc3.jpg
bc5.jpg
Last edited by Dave Foster on Thu Aug 03, 2023 9:55 am, edited 15 times in total.
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

Awesome, I look forward to setting this up when I get some free time (hopefully this weekend!)
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

calum wrote: Fri Jul 07, 2023 1:27 pm Awesome, I look forward to setting this up when I get some free time (hopefully this weekend!)
have fun, just to say in the automation the notifications are sent to my iphone device id, when you create the automation, change to visual editor and click on the device name in the notify, it will show you all of your devices you can send a message to.
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

This weekend didn't happen but the thought occurs that turning this off when the sun is below the horizon (per the built in "Sun" sensor) might be a way to work around this.

Alternatively I wonder if there's a way to swap out a different jinja table based on the same thing - ie when the sun is below the horizon, any charging happening will be via the grid, and hence limited in time, so maybe taper the charge rate a bit later and a bit less aggressively in that scenario?

I've 13.x kWh usable capacity and at an observed charge rate of 3.5kW overnight, I've not got much time to play with in getting it charged overnight (4 hours Octopus Go window), but it still feels like it's worthwhile trying to temper the 'crash stop' charging behaviour that is otherwise observed. We installed a heat pump in the spring so when we get into heating season we will drain that battery and recharge it every single day. I'm trying to square the circle of using it for the purpose it was bought for whilst not wearing it out any faster than I have to....

Anyway, I'd best get the base automation working first while there's some sun to test it on, before I start thinking too hard about winter time.
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

No problem, it'll be good to get another pair of eyes looking at what improvements can be made.

I wrote it 3-4 4 months ago, and quite a bit has changed since then - Antony has added the charge current selects, so in theory I can do away with the script and one of the helpers as they basically do the same thing.

It would have been easier to do some maths to get to the charge rate, but I wanted absolute control over the profile so it can be changed at any point to speed up, slow down etc..

We could add a 'night mode' which has a harder profile maybe only throttling from 90% to 100% so it charges faster off grid, 'night mode' could be triggered by looking at a number of things such as elevation, time of sunset etc.. or it could be as simple as if it is after 10pm and the battery is charging from grid, then switch automatically and then reset to day mode at 7am. It would be a nice idea to be able to select which profile to use during the day just in case you want to charge faster occasionally.

One amendment I was considering is a 90% soc cut limit so the battery doesn't get too stressed, this could be a toggle switch on., or triggered by time of year, day etc..

I originally wrote it for my own system so hopefully it will be of use to others, and be good to get some feedback on possible improvements :)
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

All in the best open source tradition - doing something to scratch your own itch, and then sharing it around and finding lots of people had the same need for a backscratcher!

Yep there's lots of refinements you could think of. For instance, you probably wouldn't need to use the 90% SoC limit for the V2 batteries since they already do that internally, as we learned yesterday.
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

Got it set up but very overcast here today so it never actually got chance to do anything! We'll see what happens tomorrow which looks like it will be sunny enough to fill the battery (or at least get over the 80% SoC marker)

I'm also reminding myself here that I made two small changes to mine, specifically:

input_number.bc_last_soc => input_number.battery_charge_last_soc (for my future clarity!)

tools.jinja => charge_curve_day.jinja (in case I end up with two different charge curve .jinja files as discussed above)
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

I know the names made perfect sense at the time but when I look at it now, I wonder what I was thinking at the time 🤔

Tomorrow looks to be a much better day so fingers crossed 👍
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

So - none of it is working, other than the calculated Max Charge Value :lol:

Firstly, the script isn't getting fired by the automation - I note that the script is called by this line in the automation YAML:

Code: Select all

service: script.set_max_charge_current
However in the information dialog for the script (which I did not change the name for!) it identifies itself as "script.1689699150453". I'll try changing the automation and see if that fixes it.

All that notwithstanding, even when I run the automation and then the script, it doesn't seem to do anything. Do I need to be logging all the entities for the helpers etc? I have a set of includes in my recorder: setup which are quite specific, do I need to add them for this to work?

Edit: No it does do stuff, it's successfully updated my charge limits, but it's not writing to the logbook when it does so...

EDIT 2: Yep, if it's not being logged in recorder: it won't go in the logbook (this might be very obvious but worth noting for anyone else who's done some pruning of what they log in HA)

EDIT 3: I updated the line in the automation that called the script to match the reference number and that part works now. I wonder if something changed in HA since you wrote this originally?
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

Right, to follow up, I've got it all working now (I think). It seems to be behaving as expected, so I'll keep an eye on my battery imbalance sensors and see if this can avoid the big "spike" up to about 3.5% when hitting 100% SOC.

A separate jinja file with a different curve for night time charging definitely seems like a worthwhile addition. I really struggle with trying to write YAML code but from a logic flow point of view, but it feels like the easiest way to achieve this would be to have two versions of this code...

Code: Select all

          - service: input_number.set_value
            data:
              value: >-
                {% from 'charge_curve_day.jinja' import get_charge_limit %} {%
                set cl = get_charge_limit('sensor.battery_soc')|float(1) %} {%
                if cl > 1 or cl <= 0 %}
                  {% set bcl = (1 * (states('input_number.battery_max_charge')|int(8)) ) %}
                {% else %}
                  {% set bcl = (cl * (states('input_number.battery_max_charge')|int(8)) ) %}
                {% endif %} {{ bcl|round(1) }}
            target:
              entity_id: input_number.battery_charge_limit
...one for each .jinja file, depending on the state of the sensor.sun sensor, or a timer.

As an aside, I was reading back over your OP and I wondered about this line:
If desired you can reduce this to limit the max charge your inverter uses (useful in summer to balance charge with export), or increase it, but the maxmimum charge you can achieve is the Hybrid inverter limit i.e. H1-3.7 is 3.7kw, H1-5.0 is 5kw.
Since the batteries are DC coupled, as long as you're charging from the panels there doesn't seem to be a limit as to how much juice you can shove into the batteries? I've seen the system push well over 4kW into our 6 unit bank for tens of seconds on sunny days. Of course, it's different if you're charging from the grid because of the AC->DC conversion.
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

Nice one, glad you've got it working.

I hadn't clocked the script using it's unique id, I think you can write it in yaml using it's name but if you select the name from the list it uses the 'unique_id' it has been given (which you never get to see unless you have a look in the scripts.yaml file).

That's the bit I can do away with now in favour of using the direct method so i'll look at changing that bit soon.

Yes agree, I think having another jinja import for the night curve makes perfect sense, then it's up to you where after 80% it starts to throttle, how fast etc.. - typically on a grid charge I think you'd be safe with full charge to 90% and ramping down thereafter - and probably a toggle switch so you can decide whether it charges faster during the day and the automation toggles it at a specific time for night time, and back off in the morning ready for sun up.

On the charge current yes you're absolutely right, the batteries can take pretty much whatever the PV produces, it's only the inverter output that is limited - the Foxess systems don't like being over-rated as much as some other manufacturers, so often the installers don't rate the panels much past it's export limit - the best way to get the most out of the system without clipping is to save a bit of space in the batteries for the peak solar period - i'd thought of doing it in this integration, but i'll make the night/day and script changes first :)
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

I've just been doing a bit of rough maths, with a 3.7kW inverter (that actually gives an observed charging rate of 3.5kW into the batteries) I barely have time to cut the charging rate if I want to charge from 10% to 100% SoC in my 4h off peak window :lol:

Last winter we regularly charged from 'empty' to 'full' in that time, but looking back at my data we were only getting 10-11kWh into the battery, mainly down to it being cold (I hadn't built the insulated battery enclosure yet) and all the kit being on the old firmware. At least based on nominal capacities, the working goes something like:

6 HV2600 batteries -> 6 x 2.56kWh = 15.36kWh
90% DoD -> 13.8kWh usable capacity
4 hours off peak charging at 3.5kWh -> 14kWh, ie not much room to manouver

All that said, I've never actually seen 13.8kWh into or out of this battery bank, although 13.2-13.4 hasn't been uncommon this summer. It probably won't be at the optimum temperature all winter mind. So I'll be experimenting with running with a MaxSoC of 98% when grid charging, and / or just going up to 100% but giving myself an extra hour to do it 'gently'. In the cold weather we'll be pulling a fair bit from the grid anyway, so it makes sense to use the batteries to try to time shift that demand away from peak demand. Sure, the last half kWh or so going into the battery might be at peak rates, but (probably) better to pull it out of the grid at 5am than 5pm.

Anyway, that's just my use case, clearly other folks with different sized inverters and battery banks will want to be able to set things up differently according to their needs, but when it's scheduled charging the taper profile is a bit more important if you are sailing close to the wind. Of course if you have the ability to charge a battery faster at lower states of charge, you get more time to baby it at the other end.
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

Ahhh yes I understand your pain, I used to have an H1 3.7 with 7 batteries, max charge was around 3.5kw so couldn't charge up enough if I totally flattened them, over a couple of days I could manage it back up (now have an H1 6 which spoils me but I don't like charging past 4.5kw).

Have you thought about moving to Octopus Intelligent, it's a lot of messing with the smart charge slots but it gives you 6 hours each day and you only have to go through the pain of arranging a smart charge once a month, i'm on GO until September and seriously considering trying it out - worst case can always switch back to go.
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

I've expressed an interest in it, but our combination of EV and charger (Ioniq 5 / Zappi) apparently isn't supported yet. Separately have read that are Hyundai dragging their feet on what they need to do to support it, which seems odd as Kia are all certified.

I am looking around at other tariffs, but for now I'll probably stick with Go through the winter since that's a known quantity and see what the consumption pattern is like with the heatpump. We don't do so much driving these days so a tariff to suit the heatpump rather than the EV might make more sense eg Cosy Octopus might also be worth a look.

As an aside, I am slightly kicking myself that I didn't go for a bigger inverter, given the price difference for the hardware is not actually that great. However I was talked down from it because the installer didn't think it was worthwhile doing a G99 for a couple of extra panels - we have 14, but we could have physically fitted 16, maybe 17 on the roof. Of course what I didn't properly realise then is that the bigger inverter is more useful for more than just turning the DC from the panels into "mains", and also how often we might want the additional capacity. Do you have a G99 approval or do you just put an export limit on your inverter? Did you sell your old H1? I realise I could theoretically just get an H1-6.0 and swap them over, but it's highly unlikely to be justifiable financially, especially as we didn't get those extra panels.
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

I think Zappi is supposed to be coming soon, so i’m keeping my fingers crossed as i’d rather interface at the charger than the car,

Yep I made the same mistake when I first installed and went with the H1 3.7 on the basis of it’s only G98 and mostly i’m a consumer rather than an exporter - I try and use everything I generate and only on long sunny days do I have to grudgingly allow export having all kinds of automated electrikery to use up the surplus. I’m still on G98 safety and have a 3680W power limit set for belt and braces, but if i’m exporting i’ve got my sums wrong :)

I kept the H1-3.7 still in a box in the study, I thought it might come in useful for something so wasn’t in a rush to sell it, I guess it could be used like an AC1 or for extra pv arrays.
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

@Calum
I've made a few changes to the code, a couple were to bring the notes and my code in line with the name changes you made as that will make it easier when I add in the night/day mode - i've modified the name of the bc_last_soc counter to be battery_charge_last_soc, and have changed the name of the jinja import file to be charge_curve_day.jinja - all the notes above and the code have been updated to reflect these naming conventions.

I realised when you turn off the battery charge throttle switch the charge limit does not get reset to maximum, if you leave it switched on it used to happen when the battery_soc dropped below 80% but if you turn it off it never got reset - this has now been corrected in the version above, when you turn it off at the next soc change it will reset to maximum charge.

I also did away with the script as the latest integration exposes the dc charge current setting directly and so the script is no longer required, nor is the helper that it used 'input_number.max_charge_current' - once you update to this version you can delete the script and remove that helper (or leave it in, it won't do any harm).

Because the sensor for current_charge_rate used this helper it requires a change to the template, so replace 'input_number.max_charge_current' with the integration version called 'number.max_charge_current' (see sensor code segment above) and refresh templates in Developer Tools.

Then just over-write your battery charge throttle automation code with the version above - if you have updated your device name where it sends a notification you will need to reselect the device in the automation editor again.

Hopefully that should be it, a lot easier without the script and we're using the same name conventions - i'll get onto the day/night profile when i've got a spare hour or two.

UPDATE: I am testing the night/day mode now (Saturday 22/7/23) so you might want to hold off updating for a few days, unfortunately in the absence of solar today and tomorrow it might take me 2 or 3 days to properly test it, but it looks like night mode will get a grid charge test tonight :(
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

Hi Dave, all sounds sensible, I'll hold off on any changes until you get chance to test things your end. I've not needed to worry about charging the battery to full on the grid, just gave it a wee top up last night to get us through the neverending rainy weather!
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

calum wrote: Sun Jul 23, 2023 8:34 pm Hi Dave, all sounds sensible, I'll hold off on any changes until you get chance to test things your end. I've not needed to worry about charging the battery to full on the grid, just gave it a wee top up last night to get us through the neverending rainy weather!
Hi Calum,

Ok all tested, and i've updated the notes above - I've changed the notifications so that everything goes to the logbook now rather than to a special device, it's easier and more consistent as it works anywhere without changes.

As you're already running you only need to add the second jinja import file (charge_curve_night), the final helper battery_charge_throttle_night_mode and overwrite your automation with the one above, I had previously changed the last_soc and tools.jinja to match yours so it should all just work - but please double check it (and note the change to the rate sensor which uses 'number.' and not 'input_number' as it now talks directly to the modbus integration and not via the script).

Finally just add to your HA card the entity for battery charge throttle night mode and then you can see it's state.

Operationally it uses time to make the night/day change - at 7am it will switch to 'day' mode and at 8pm it will change to 'night' mode.

I've preset the night mode charge to do nothing until 90% then taper down - On my system it takes that last 10% one hour to complete so you might want to tweak it to suit your charge times.

Night looks like this on my system -
IMG_1433.PNG
You can change the mode back to night during the day if you want to accelerate your charge and it will automatically switch back the following day
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

OK I've made all the updates here, with any luck this afternoon will be a test of the "day" curve under the new logic. I'll try the night mode toggle and check the different charge curve. Actually using it at night will be interesting as I've not had cause to charge battery past 90% overnight for a while now!
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

calum wrote: Fri Jul 28, 2023 1:19 pm OK I've made all the updates here, with any luck this afternoon will be a test of the "day" curve under the new logic. I'll try the night mode toggle and check the different charge curve. Actually using it at night will be interesting as I've not had cause to charge battery past 90% overnight for a while now!
Great, let me know how you get on, i’ve only done the one overnight charge so far, tbh I only needed a bit of charge but wanted to check it worked ;)
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

As it turned out the weather did almost all the charge tapering for us yesterday, it was bright and sunny when the battery was charging unrestricted, and by the time it got into the 85%+ regime of the day curve, it had clouded over sufficiently that it mostly really wasn't bumping up against the limits. That said the logbook shows the changes in amps and there is a small part of the battery charge trace that shows a stairstep, so it very much looks like the logic is doing its thing.

I forgot to add the day / night toggle to my recorder section so that didn't show up in the logbook, not sure if it actually matters for the functionality though?

I'll do a few test charge ups overnight soon and see how that goes, probably test out the 98% max SoC idea and also see how long that last few points of SoC take, and therefore if I need to tweak my night-time .jinja file's values.
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

calum wrote: Sat Jul 29, 2023 12:35 pm I forgot to add the day / night toggle to my recorder section so that didn't show up in the logbook, not sure if it actually matters for the functionality though?
Ahh ok, do you exclude everything from your Logbook by default?, no your right it won’t make any functional difference you just won’t have any change history for it.
calum
Posts: 389
Joined: Fri Feb 24, 2023 11:00 am
Location: Stockport

I don't have any settings (as far as I know!) that actually change what goes in the logbook, but my recorder setup is include only, so unless I explicitly add something it doesn't get recorded.

That goes into MariaDB for 30 days, I have a separate set of includes for InfluxDB for long term storage.

I'll add the day/night toggle status into the recorder includes to verify it is working properly, although of course I will be able to see it switch over if I remember to watch the toggle at the right time!
H1-3.7 / 6xHV2600 / 14x400W / RS485 Modbus->HA
FoxESS Modbus HA Integration
Contact Fox here
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

calum wrote: Mon Jul 31, 2023 11:20 am I don't have any settings (as far as I know!) that actually change what goes in the logbook, but my recorder setup is include only, so unless I explicitly add something it doesn't get recorded.

That goes into MariaDB for 30 days, I have a separate set of includes for InfluxDB for long term storage.

I'll add the day/night toggle status into the recorder includes to verify it is working properly, although of course I will be able to see it switch over if I remember to watch the toggle at the right time!
Ok, yes got it - if you run the recorder as include only the entry in the logbook would also be excluded by default.
Dave Foster
Posts: 822
Joined: Thu Oct 13, 2022 7:21 pm

@Calum - just done a quick fix to the automation - it wouldn't normally happen but I have a habit of turning my max battery charge current down at night when i'm charging my car so my HV batteries don't discharge, instead I let them have a trickle charge rate.

This fix isn't critical if you don't manually reduce the max battery charge, but here it is anyway (possibly easier to copy the code above in entirety but i've shown the changes below)

The fix is in the test for SoC below 79 where it resets the battery charge limit, it used to happen if the battery charge limit was 'below' the max battery charge - but i've now added 'above' to that as well so it resets if they are not the same.

I have changed what was a single test when 'below', it now tests for 'below' OR 'above' (it is in 2 places)

The code above is modified, these are the conditions I changed here

The first one in the conditions near the top (first in visual, then yaml modes)
ttt.jpg
ttt1.jpg

The second one is near the bottom of the code in the IF
ttt3.jpg
ttt2.jpg
You can replace the single test yaml with this in both case -

Code: Select all

condition: or
conditions:
  - condition: numeric_state
    entity_id: input_number.battery_charge_limit
    below: input_number.battery_max_charge
  - condition: numeric_state
    entity_id: input_number.battery_charge_limit
    above: input_number.battery_max_charge
Post Reply