springfall2008 / batpred

Home battery prediction and charging automation for Home Assistant, supporting many inverter types
https://springfall2008.github.io/batpred/
92 stars 32 forks source link

Sungrow Inverter Support #1136

Open jwreford opened 1 month ago

jwreford commented 1 month ago

Hey there,

I have a Sungrow setup, and have been playing around with Batpred. I have most parts working although there are some that don't work because the inverter doesn't expose certain details. I've worked around some parts, but others are giving me a bit of trouble.

I'm using this HA custom component to interface with the Sungrow gear: https://github.com/mkaiser/Sungrow-SHx-Inverter-Modbus-Home-Assistant

This itself works very well and exposes all sorts of functionaly and data.

Here's the top part of the apps.yaml that I've configured (based on the SolarEdge template):

pred_bat:
  module: predbat
  class: PredBat

  # Sets the prefix for all created entities in HA - only change if you want to run more than once instance
  prefix: predbat

  # Timezone to work in
  timezone: Australia/Melbourne

  # XXX: Template configuration, delete this line once you have set up for your system
  #template: True

  # If you are using Predbat outside of HA then set the HA URL and Key (long lived access token here)
  #ha_url: 'http://homeassistant.local:8123'
  #ha_key: 'xxx'

  # Currency, symbol for main currency second symbol for 1/100s e.g. $ c or £ p or e c
  currency_symbols:
   - '$'
   - 'c'

  # Number of threads to use in plan calculation
  # Can be auto for automatic, 0 for off or values 1-N for a fixed number
  threads: auto

  #
  # Sensors, currently more than one can be specified and they will be summed up automatically
  # however if you have two inverters only set one of them as they will both read the same.
  #
  load_today:
    - sensor.daily_consumed_energy
  import_today:
    - sensor.daily_imported_energy_calculated
  export_today:
    - sensor.daily_exported_energy_from_pv
  pv_today:
    - sensor.daily_pv_generation   # This is a custom sensor - see Readme on how to create or comment out
  #
  # Controls/status - must by 1 per inverter
  #
  num_inverters: 1
  inverter_type: "SE"

  # Can be enabled if you have a backup interface
  #inverter:
  #  has_reserve_soc: True

  battery_rate_max:
    - sensor.battery_max_charge_power
  charge_rate:
    - number.battery_charging_power
  discharge_rate:
    - number.battery_discharging_power
  battery_power:
    - sensor.battery_discharging_power
  pv_power:
    - sensor.total_dc_power # This is a custom sensor - see Readme on how to create or comment out
  load_power:
    - sensor.load_power # This is a custom sensor - see Readme on how to create or comment out
  soc_percent:
    - sensor.battery_level
  soc_kw: sensor.current_stored_capacity
  soc_max:
    - input_number.battery_design_capacity_kwh
  # Can be enabled you have a backup interface
  #reserve:
  #  - number.solaredge_i1_backup_reserve
  charge_start_time:
   - "00:00:00"
  charge_end_time:
   - "00:01:00"
  charge_limit:
   - number.reserved_soc_for_backup
  scheduled_charge_enable:
   - off
  scheduled_discharge_enable:
   - off
  discharge_start_time:
   - "00:00:00"
  discharge_end_time:
   - "00:01:00"

  # Services to control charging/discharging
  charge_start_service:
  - service: select.select_option
    entity_id: "input_select.set_sg_ems_mode"
    option: "Forced mode"
  - service: select.select_option
    entity_id: "input_select.set_sg_battery_forced_charge_discharge_cmd"
    option: "Forced charge"
  charge_stop_service:
  - service: select.select_option
    entity_id: "input_select.set_sg_ems_mode"
    option: "Self-consumption mode (default)"
  - service: select.select_option
    entity_id: "input_select.set_sg_battery_forced_charge_discharge_cmd"
    option: "Stop (default)"
  discharge_start_service:
  - service: select.select_option
    entity_id: "input_select.set_sg_ems_mode"
    option: "Forced mode"
  - service: select.select_option
    entity_id: "input_select.set_sg_battery_forced_charge_discharge_cmd"
    option: "Forced discharge"

  # Inverter max AC limit (one per inverter)
  # If you have a second inverter for PV only please add the two values together
  #inverter_limit:
  # - 3600
  # - 3600
  # Export limit is a software limit set on your inverter that prevents exporting above a given level
  # When enabled Predbat will model this limit
  #export_limit:
  # - 3600
  # - 3600
  #
  # The maximum rate the inverter can charge and discharge the battery can be overwritten, this will change
  # the register programming and thus cap the max rates. The default is to use the maximum supported rates (recommended)
  #
  #inverter_limit_charge:
  # - 2000
  #inverter_limit_discharge:
  # - 2600

  # Some inverters don't turn off when the rate is set to 0, still charge or discharge at around 200w
  # The value can be set here in watts to model this (doesn't change operation)
  #inverter_battery_rate_min:
  #  - 200

  # Some batteries tail off their charge rate at high soc%
  # enter the charging curve here as a % of the max charge rate for each soc percentage.
  # the default is 1.0 (full power)
  #battery_charge_power_curve:
  #  91 : 0.91
  #  92 : 0.81
  #  93 : 0.71
  #  94 : 0.62
  #  95 : 0.52
  #  96 : 0.43
  #  97 : 0.33
  #  98 : 0.24
  #  99 : 0.24
  #  100 : 0.24

  # Inverter clock skew in minutes, e.g. 1 means it's 1 minute fast and -1 is 1 minute slow
  # Separate start and end options are applied to the start and end time windows, mostly as you want to start late (not early) and finish early (not late)
  # Separate discharge skew for discharge windows only
  inverter_clock_skew_start: 0
  inverter_clock_skew_end: 0
  inverter_clock_skew_discharge_start: 0
  inverter_clock_skew_discharge_end: 0

  # Clock skew adjusts the Appdaemon time
  # This is the time that Predbat takes actions like starting discharge/charging
  # Only use this for workarounds if your inverter time is correct but Predbat is somehow wrong (AppDaemon issue)
  # 1 means add 1 minute to AppDaemon time, -1 takes it away
  clock_skew: 0

  # Set these to match solcast sensor names
  # The regular expression (re:) makes the solcast bit optional
  # If these don't match find your own names in Home Assistant
  pv_forecast_today: re:(sensor.(solcast_|)(pv_forecast_|)forecast_today)
  pv_forecast_tomorrow: re:(sensor.(solcast_|)(pv_forecast_|)forecast_tomorrow)
  pv_forecast_d3: re:(sensor.(solcast_|)(pv_forecast_|)forecast_(day_3|d3))
  pv_forecast_d4: re:(sensor.(solcast_|)(pv_forecast_|)forecast_(day_4|d4))

Sungrow doesn't expose these details via Modbus:

It's possible to derive them from other data (eg, Daily Total Load is the sum of imported energy and consumed energy generated from Solar/Battery). For the Design Capacity I just created a helper and hardcoded the capacity. The state of charge I calculated from the design capacity and the state of charge in percent.

All that is well and good but an exception is thrown when I try to use my template sensor for Daily Total Load:

changed to Error - Unable to fetch history from sensor.daily_consumed_energy_grid_and_generated

To be fair this could be my problem because I only know the basics about template sensors, so I'm not sure if it doesn't like that there is only one day's worth of history (as I created the sensor yesterday) or if I've created it with some parameters incorrect.

Anyway, progress. Am keen to sort out what's happening. Let me know what other information I can chuck in here.

Cheers!

springfall2008 commented 3 weeks ago

Battery Design Capacity in kWh

You can just type this number into apps.yaml as it doesn't change

Battery State of Charge in kWh

You only need to set one of these, the percent is fine:

  soc_percent:
    - sensor.battery_level
  soc_kw: sensor.current_stored_capacity

Daily Total Load (from all sources, including grid, solar and battery)

Load is supposed to be how much your house consumes, a template sensor should work but you will need to wait 24 hours after creating it before you have some data.

The error:

changed to Error - Unable to fetch history from sensor.daily_consumed_energy_grid_and_generated

Doesn't match the sensor name you set?

  load_today:
    - sensor.daily_consumed_energy

Not sure where the name came from?

jwreford commented 3 weeks ago

Good to know. I'll comment out the capacity.

Ah, OK. Let me clarify. The config I pasted in works (mostly). But daily_consumed_energy entity, as provided by my inverter, doesn't include import from the grid so it throws all the predictions off overnight (understandably). My attempt around that was to create a template sensor that just adds the daily import and the daily_consumed_energy together. I named it daily_consumed_energy_grid_and_generated so it was clear what it does.

However when I set load_today to this entity, I get an exception. It's probably related to the way I've made the template sensor. Here's the trace from the log: ` 2024-06-06 17:35:02.409117 WARNING pred_bat: ------------------------------------------------------------ 2024-06-06 17:40:02.446084 WARNING pred_bat: ------------------------------------------------------------ 2024-06-06 17:40:02.446186 WARNING pred_bat: Unexpected error in worker for App pred_bat: 2024-06-06 17:40:02.446398 WARNING pred_bat: Worker Ags: {'id': 'e9000f03314942b696f02ea273cd0c16', 'name': 'pred_bat', 'objectid': 'de065f2bc88b4774bd364c9e94ad455b', 'type': 'scheduler', 'function': <bound method PredBat.run_time_loop of <predbat.PredBat object at 0x7f136488bad0>>, 'pin_app': True, 'pin_thread': 0, 'kwargs': {'interval': 300, 'random_start': 0, 'random_end': 0, '__thread_id': 'thread-0'}} 2024-06-06 17:40:02.446491 WARNING pred_bat: ------------------------------------------------------------ 2024-06-06 17:40:02.447201 WARNING pred_bat: Traceback (most recent call last): File "/usr/lib/python3.11/site-packages/appdaemon/threading.py", line 1022, in worker funcref(self.AD.sched.sanitize_timer_kwargs(app, args["kwargs"])) File "/config/apps/predbat.py", line 15454, in run_time_loop raise e File "/config/apps/predbat.py", line 15449, in run_time_loop self.update_pred(scheduled=True) File "/config/apps/predbat.py", line 14291, in update_pred status, status_extra = self.execute_plan() ^^^^^^^^^^^^^^^^^^^ File "/config/apps/predbat.py", line 12791, in execute_plan self.reset_inverter() File "/config/apps/predbat.py", line 12767, in reset_inverter inverter.adjust_charge_rate(inverter.battery_rate_max_charge * MINUTE_WATT) File "/config/apps/predbat.py", line 3266, in adjust_charge_rate self.write_and_poll_value( File "/config/apps/predbat.py", line 3429, in write_and_poll_value matched = abs(float(current_state) - new_value) <= fuzzy ^^^^^^^^^^^^^^^^^^^^ TypeError: float() argument must be a string or a real number, not 'NoneType'

2024-06-06 17:40:02.447337 WARNING pred_bat: ------------------------------------------------------------`