springfall2008 / batpred

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

What would it take to include Sofar inverters? #395

Closed onetoomany closed 9 months ago

onetoomany commented 11 months ago

I've just upgraded my inverter to Sofar HYD, and keen to have BatPred running for it. What would it take to have BatPred run this inverter as well?

To control the inverter, the best way seems to be through RS485 via Sofar2MQTT. This gives a number of MQTT topic subsets that cover SOC, grid power, inverter power, house load etc...., and MQTT publish topics to force charge or discharge the battery at variable rates. This is done through a mode called "Passive Mode" on the inverter, which isn't by time of day but is fully controlled by RS485 commands.

I saw the previous post about getting Solax inverters included, and the list of entities needed. Before I complete something similar, question is whether its more use to you in the MQTT topic format or not. Alternatively, as I can name the entities anything I want, would it would be simpler to have a specification that I can name my entities against and this become a more versatile solution?

For example:

Charging: (variable rate)

    service: mqtt.publish
    data:
      qos: "1"
      retain: true
      topic: Sofar2mqtt/set/charge
      payload: "3000"

Discharging, as above with Sofar2mqtt/set/discharge Auto, again with Sofar2mqtt/set/auto and payload of "true"

SOC:

    - name: "sofar_inverter_battery_charge"
      state_topic: "Sofar2mqtt/state"
      unit_of_measurement: "W"
      device_class: power
      state_class: measurement
      value_template: > 
        {% if (value_json.battery_power < 32767) %}
        {{ (value_json.battery_power) * 10 }}
        {% else %}
        {{ 0 }}
        {% endif %} 
springfall2008 commented 11 months ago

Hi,

The cleanest way to add support would be to control the inverter via Home Assistant entities as is being supported for other inverters. If you have the list of entities with their contents then it might not be too hard to map.

Basically you need information about the system, ideally can be read via HA but could be hard-wired:

Then you need to be able to read directly the following:

Then you need a way to:

onetoomany commented 11 months ago

Battery size - hard wired, as only the SOC is read Max charge rate - hard wired, but typically 3kw for this inverter / 65A Max discharge rate - hard wired, but typically 3kw for this inverter / 65A

The inverter has a certain level of battery control in it, with a max discharge of 15% and a max charge of 100% For force charge or discharge, its a case of sending the MQTT command to start, then sending another command to return to Auto - I can't say charge for 30mins.

SOC: sensor.sofar_inverter_battery_SOC Battery charge: sensor.sofar_inverter_battery_charge (W) Battery discharge: sensor.sofar_inverter_battery_discharge (W) Battery power (ie net of the above): sensor.sofar_inverter_battery_power (W)

Grid power: sensor.sofar_grid_power (W) System power: sensor.sofar_inverter_system_power (W) House load: sensor.sofar_house_consumption_power (W) PV: sensor.sofar_solar_power (W)

Exported energy: sensor.sofar_energy_today_exported (kWh) Imported energy: sensor.sofar_energy_today_purchase (kWh) Consumed energy: sensor.sofar_energy_today_consumption (kWh) PV Total energy: sensor.sofar_energy_today_generation (kWh)

Force charge: switch.sofar_charge_mode_3000 (or 1000, 1500, 2000, 2500 etc..) Force discharge: switch.sofar_discharge_mode_3000 Force auto: switch.sofar_auto_mode Force battery save: switch.sofar_auto_saver_mode (i.e. charge from PV but no discharge) (not sure why these are down as switches not buttons)

onetoomany commented 11 months ago

I've managed to get PredBat reading my data and creating a valid plan. Next steps is to get it to control the inverter.

In the instructions, there are supposed binary_sensor.predbat_charging and binary_sensor.predbat_discharging that I thought I could create a trigger off to change the state of my inverter - however, these don't seem to exist in my sensors. How can I identify if my inverter should be changing between charging, discharging and auto?

Ideally, I'd use these sensors to start a charge until the sensor switches off again - i.e. if the plan says I need to add 0.5kwh to the battery, the sensor will switch off when I get there rather than at the end of the slot. Is this how they should work?

I'm also not sure what behaviour I'm meant to provide to my inverter for a FreezeChrg and HoldChrg period? I assume both would map to Battery Save (i.e. don't discharge the battery, but charge with any excess solar if available).

springfall2008 commented 11 months ago

Can you share your apps.yaml so far so it can be added?

I don't think you want to automate based on the sensors, we would want Predbat to trigger the charge or discharge itself. Can you maybe share an automation that starts/stops charging and discharging and then that can be used to add to Predbat code?

onetoomany commented 11 months ago

There are four parts to this:

  1. A small ESP8266 and TTL>RS485 circuit as per https://github.com/cmcgerty/Sofar2mqtt, which communicates with HA via MQTT
  2. The inverter needs to be put into Passive mode
  3. yaml package for converting the MQTT messages to sensors - sofar_inverter.yaml.txt
  4. the app.yaml config for PredBat - apps.yaml.txt

Example commands to start / stop charging are:

  - alias: Set inverter to Charge at 3kW
    service: mqtt.publish
    data:
      qos: "1"
      retain: true
      topic: Sofar2mqtt/set/charge
      payload: "3000"

  - alias: Set inverter to Discharge at 2kW
    service: mqtt.publish
    data:
      qos: "1"
      retain: true
      topic: Sofar2mqtt/set/discharge
      payload: "2000"

  - alias: Set inverter to Auto
    service: mqtt.publish
    data:
      qos: "1"
      retain: true
      topic: Sofar2mqtt/set/auto
      payload: "true"

  - alias: Set inverter to Battery Save
    service: mqtt.publish
    data:
      qos: "1"
      retain: true
      topic: Sofar2mqtt/set/auto
      payload: "battery_save"
springfall2008 commented 10 months ago

I'll try to have a go at supporting this soon, I don't think mqtt is hard to add

springfall2008 commented 10 months ago

I've committed some changes to : https://github.com/springfall2008/batpred/tree/Sofar - The python updated and the template Predbat config file (https://github.com/springfall2008/batpred/blob/Sofar/templates/sofar.yaml) which you will need take my updates on.

Can you give it a try, I'll add in your other files once something is working?

onetoomany commented 10 months ago

I've done a (very) manual install of branch - not sure if I've done it right, but I overwrote the predbat.py code and the apps.yaml.

I'm seeing this in the logs: 2023-12-13 10:21:27.253775 INFO pred_bat: Inverter 0 current charge rate is 3000.0 and new target is 2600 2023-12-13 10:21:27.254779 INFO pred_bat: ERROR: Exception raised argument of type 'int' is not iterable As such, I'm not seeing any action on the inverter.

I'm also seeing a lot of warnings along the lines of: 2023-12-13 10:21:27.205542 WARNING pred_bat: pred_bat: Entity predbat.low_rate_end not found in namespace default 2023-12-13 10:21:27.205799 INFO AppDaemon: pred_bat: Entity predbat.low_rate_end created in namespace: default

springfall2008 commented 10 months ago

For the first one I think 'battery_max_rate: 3000' needs adding to apps.yaml.

For the exception, can you go to 'settings, system, logs, appdaemon' and paste in the stack trace?

springfall2008 commented 10 months ago

'm also seeing a lot of warnings along the lines of: 2023-12-13 10:21:27.205542 WARNING pred_bat: pred_bat: Entity predbat.low_rate_end not found in namespace default 2023-12-13 10:21:27.205799 INFO AppDaemon: pred_bat: Entity predbat.low_rate_end created in namespace: default

These are good, they happen when Predbat creates sensors for the first time

onetoomany commented 10 months ago
TypeError: argument of type 'int' is not iterable

2023-12-13 10:21:27.261076 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 10:25:01.569609 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 10:25:01.570012 WARNING pred_bat: Unexpected error in worker for App pred_bat:
2023-12-13 10:25:01.570112 WARNING pred_bat: Worker Ags: {'id': '8d036e7bbad9476092a6330e419f5a46', 'name': 'pred_bat', 'objectid': 'a160f4009d1e4bd19236d6fb2fc78179', 'type': 'scheduler', 'function': <bound method PredBat.run_time_loop of <predbat.PredBat object at 0x7ff93eca89d0>>, 'pin_app': True, 'pin_thread': 0, 'kwargs': {'interval': 300, 'random_start': 0, 'random_end': 0, '__thread_id': 'thread-0'}}
2023-12-13 10:25:01.570180 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 10:25:01.571079 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 "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9701, in run_time_loop
    raise e
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9697, in run_time_loop
    self.update_pred(scheduled=True)
  File "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9167, in update_pred
    status, status_extra = self.execute_plan()
                           ^^^^^^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 8162, in execute_plan
    inverter.adjust_charge_rate(inverter.battery_rate_max_charge * 60 * 1000)
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1227, in adjust_charge_rate
    entity = self.base.get_entity(self.base.get_arg("charge_rate", indirect=False, index=self.id))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/appdaemon/adapi.py", line 3188, in get_entity
    self._check_entity(namespace, entity)
  File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 231, in inner_sync_wrapper
    f = run_coroutine_threadsafe(self, coro(self, *args, **kwargs))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 313, in run_coroutine_threadsafe
    result = future.result(self.AD.internal_function_timeout)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.11/site-packages/appdaemon/adapi.py", line 588, in _check_entity
    if "." not in entity:
       ^^^^^^^^^^^^^^^^^
TypeError: argument of type 'int' is not iterable
springfall2008 commented 10 months ago

Thanks, I updated the YAML template and predbat.py on the branch, effectively it was trying to use the number as an entity which didn't work.

onetoomany commented 10 months ago

2023-12-13 15:00:03.606184 INFO pred_bat: ERROR: Exception raised 'Inverter' object has no attribute 'call_service'

and from the appdaemon log:

AttributeError: 'Inverter' object has no attribute 'call_service'

2023-12-13 15:10:01.935502 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 15:15:01.569999 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 15:15:01.570388 WARNING pred_bat: Unexpected error in worker for App pred_bat:
2023-12-13 15:15:01.570521 WARNING pred_bat: Worker Ags: {'id': '18623fcedf254c8faab58b3c51441a5b', 'name': 'pred_bat', 'objectid': 'd7aa4f9da61445c7b0660cf33ae13a44', 'type': 'scheduler', 'function': <bound method PredBat.run_time_loop of <predbat.PredBat object at 0x7ff93f218c50>>, 'pin_app': True, 'pin_thread': 0, 'kwargs': {'interval': 300, 'random_start': 0, 'random_end': 0, '__thread_id': 'thread-0'}}
2023-12-13 15:15:01.570608 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 15:15:01.571914 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 "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9701, in run_time_loop
    raise e
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9697, in run_time_loop
    self.update_pred(scheduled=True)
  File "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9167, in update_pred
    status, status_extra = self.execute_plan()
                           ^^^^^^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 8338, in execute_plan
    inverter.adjust_battery_target(self.charge_limit_percent_best[0])
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1339, in adjust_battery_target
    self.mimic_target_soc(soc)
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1131, in mimic_target_soc
    self.alt_charge_discharge_enable("charge", True, grid=True, timed=False)
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1740, in alt_charge_discharge_enable
    self.mqtt_message('set/charge', payload=self.battery_rate_max_charge)
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1751, in mqtt_message
    self.call_service("mqtt/publish", qos=1, retain=True, topic=(self.inv_mqtt_topic + '/' + topic), payload=payload)
    ^^^^^^^^^^^^^^^^^
AttributeError: 'Inverter' object has no attribute 'call_service'
springfall2008 commented 10 months ago

I've just pushed a fix for that, please update and re-try

onetoomany commented 10 months ago

I've been running it this evening. A couple of issues still: 2023-12-13 21:25:01.596044 INFO pred_bat: Inverter 0 Current Target SOC is 38 already at target 2023-12-13 21:25:01.597545 INFO pred_bat: ERROR: Exception raised argument of type 'NoneType' is not iterable

The MQTT Sofar2mqtt/set/charge looks like at least an order of magnitude out - "0.05". The value needed is the charge rate in W, so 3000 = 3kW.

TypeError: argument of type 'NoneType' is not iterable

2023-12-13 21:20:01.554319 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 21:25:01.604998 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 21:25:01.605174 WARNING pred_bat: Unexpected error in worker for App pred_bat:
2023-12-13 21:25:01.605325 WARNING pred_bat: Worker Ags: {'id': '2796611130374988ac8979f660ae04ba', 'name': 'pred_bat', 'objectid': 'e7ed0dc28b3e41c0921015819db5efa5', 'type': 'scheduler', 'function': <bound method PredBat.run_time_loop of <predbat.PredBat object at 0x7ff93f2cf510>>, 'pin_app': True, 'pin_thread': 0, 'kwargs': {'interval': 300, 'random_start': 0, 'random_end': 0, '__thread_id': 'thread-0'}}
2023-12-13 21:25:01.605443 WARNING pred_bat: ------------------------------------------------------------
2023-12-13 21:25:01.606760 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 "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9701, in run_time_loop
    raise e
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9697, in run_time_loop
    self.update_pred(scheduled=True)
  File "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9167, in update_pred
    status, status_extra = self.execute_plan()
                           ^^^^^^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 8338, in execute_plan
    inverter.adjust_battery_target(self.charge_limit_percent_best[0])
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1339, in adjust_battery_target
    self.mimic_target_soc(soc)
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1132, in mimic_target_soc
    self.set_current_from_power("charge", self.battery_rate_max_charge)
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1768, in set_current_from_power
    entity = self.base.get_entity(self.base.get_arg(f"timed_{direction}_current", indirect=False, index=self.id))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/appdaemon/adapi.py", line 3188, in get_entity
    self._check_entity(namespace, entity)
  File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 231, in inner_sync_wrapper
    f = run_coroutine_threadsafe(self, coro(self, *args, **kwargs))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 313, in run_coroutine_threadsafe
    result = future.result(self.AD.internal_function_timeout)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.11/site-packages/appdaemon/adapi.py", line 588, in _check_entity
    if "." not in entity:
       ^^^^^^^^^^^^^^^^^
TypeError: argument of type 'NoneType' is not iterable
springfall2008 commented 10 months ago

Okay, I've pushed another fix to predbat.py

Sorry this is so slow, I can't test it myself.

onetoomany commented 10 months ago

Only just been able to test this today, (and didn't want to disturb things during powerup and saver session)

  1. MQTT charge command still an order of magnitude out - saying 0.05 not 3000 There are also a few MQTT commands that don't do anything - no harm, but no use either: e.g. set/target_soc set/reserve

  2. Some errors in the logs 2023-12-14 19:59:04.546605 INFO pred_bat: ERROR: Exception raised 'PredBat' object has no attribute 'base'

2023-12-14 19:59:04.556722 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 "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9832, in update_time_loop
    raise e
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9827, in update_time_loop
    self.update_pred(scheduled=False)
  File "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9183, in update_pred
    self.fetch_sensor_data()
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 8733, in fetch_sensor_data
    self.base.call_notify("Predbat: Joined Octopus saving event: start {}, end {}, price per kWh {}".format(start, end, saving_rate))
    ^^^^^^^^^
AttributeError: 'PredBat' object has no attribute 'base'
gcoan commented 10 months ago

The last of those error messages was when predbat sent a mobile notification that it was joining the saving session. Bug on my part, it's fixed in the latest release. Only occurs at the moment of joining the saving session, it joins ok but then crashes when sending the mobile alert. Next 5 minute run of predbat as you've already joined the saving session it runs ok

springfall2008 commented 10 months ago

Can you try the latest from 'main' please, these things have been fixed

onetoomany commented 10 months ago

This latest version seems to have regressed to an error that was fixed yesterday. Also, the MQTT message for set charge is still coming through as 0.05.

2023-12-15 12:35:01.520027 INFO pred_bat: ERROR: Exception raised argument of type 'NoneType' is not iterable

TypeError: argument of type 'NoneType' is not iterable

2023-12-15 12:30:05.520072 WARNING pred_bat: ------------------------------------------------------------
2023-12-15 12:35:01.526707 WARNING pred_bat: ------------------------------------------------------------
2023-12-15 12:35:01.527384 WARNING pred_bat: Unexpected error in worker for App pred_bat:
2023-12-15 12:35:01.527676 WARNING pred_bat: Worker Ags: {'id': '204daae70b4a4df9a51df251c685e547', 'name': 'pred_bat', 'objectid': '4d03feca25794dcfaabf8811c03279d7', 'type': 'scheduler', 'function': <bound method PredBat.run_time_loop of <predbat.PredBat object at 0x7fd68faf4e10>>, 'pin_app': True, 'pin_thread': 0, 'kwargs': {'interval': 300, 'random_start': 0, 'random_end': 0, '__thread_id': 'thread-0'}}
2023-12-15 12:35:01.527884 WARNING pred_bat: ------------------------------------------------------------
2023-12-15 12:35:01.529227 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 "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9899, in run_time_loop
    raise e
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9895, in run_time_loop
    self.update_pred(scheduled=True)
  File "/usr/lib/python3.11/site-packages/appdaemon/adbase.py", line 35, in f_app_lock
    return f(*args, **kw)
           ^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 9235, in update_pred
    status, status_extra = self.execute_plan()
                           ^^^^^^^^^^^^^^^^^^^
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 8404, in execute_plan
    inverter.adjust_battery_target(self.charge_limit_percent_best[0])
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1342, in adjust_battery_target
    self.mimic_target_soc(soc)
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1135, in mimic_target_soc
    self.set_current_from_power("charge", self.battery_rate_max_charge)
  File "/homeassistant/appdaemon/apps/batpred/predbat.py", line 1782, in set_current_from_power
    entity = self.base.get_entity(self.base.get_arg(f"timed_{direction}_current", indirect=False, index=self.id))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/appdaemon/adapi.py", line 3188, in get_entity
    self._check_entity(namespace, entity)
  File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 231, in inner_sync_wrapper
    f = run_coroutine_threadsafe(self, coro(self, *args, **kwargs))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/appdaemon/utils.py", line 313, in run_coroutine_threadsafe
    result = future.result(self.AD.internal_function_timeout)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.11/site-packages/appdaemon/adapi.py", line 588, in _check_entity
    if "." not in entity:
       ^^^^^^^^^^^^^^^^^
TypeError: argument of type 'NoneType' is not iterable
springfall2008 commented 10 months ago

What version is being reported in the log or in the version sensor?

I did just push another fix to main related to this crash so you might want to update

onetoomany commented 10 months ago

THIS_VERSION = "v7.14.12"

springfall2008 commented 10 months ago

v7.14.13 is on main right now, so maybe try that and see if it changes anything. I'll release it once its stable.

onetoomany commented 10 months ago

I've not seen it charge yet, but seeing the following:

  1. Sofar2mqtt/set/auto payload should be lowercase "true" not "True". Not sure if this makes a difference looking at the ESP code though.
  2. the reserve value is being ignored - which is why I've not seen it charge. I've got a reserve set to 20% in the apps.yaml, yet the plan allows for my SOC to drop to 4%. The inverter kicks in on its own accord if the SOC drops below the reserve.
springfall2008 commented 10 months ago

How can the reserve be set by Predbat, what's the MQTT command for that?

onetoomany commented 10 months ago

I don't believe it can - we have to monitor it and set the inverter to standby ("Sofar2mqtt/set/standby") when we hit the lower reserve, or auto when hit the upper reserve.

springfall2008 commented 10 months ago

I've added a fix on main for now which disables the set_reserve/set_reserve_hold if the inverter doesn't have a reserve. We could emulate as a next step.

springfall2008 commented 10 months ago

https://github.com/springfall2008/batpred/releases/tag/v7.14.14

onetoomany commented 10 months ago

Thanks - I'm still seeing the following:

  1. Sofar2mqtt/set/auto still coming as "True" not "true"

  2. The SOC Predbat plan will drop below the "reserve" value set in apps.yaml. I'd have thought Predbat would treat the apps.yaml value as the lower limit for the plan. The behaviour I would expect would be:

    • The plan never drops below the "reserve" value
    • When the SOC hits that reserve value, a command is sent to the inverter to put it into either "battery save" or "standby" mode. Thinking about it, "battery save" mode could be better, as this means the battery will charge from any excess PV but not from the grid. The command for these are: Sofar2mqtt/set/auto with payload "battery_save" and Sofar2mqtt/set/standby
  3. Sofar2mqtt/set/target_soc - this has no effect on the inverter so is not needed

springfall2008 commented 10 months ago
  1. I've fixed that on 'main' now
  2. I think you need to change the set_reserve_min in Home Assistant. The value in the apps.yaml is meant to point to a register that reads the current reserve value from the inverter, then Predbat will overwrite it with its own value. As Sofar is configured as not having a reserve then this register is ignored.
  3. The target_soc was mostly put there for other inverters in the future, if it's harmless we can ignore it.
onetoomany commented 10 months ago

Great - this all seems to work! What's needed to support the documentation for Sofar inverters, so others can use this too?

springfall2008 commented 10 months ago

Please review the documentation here and send me any updates:

https://github.com/springfall2008/batpred/blob/sofar/docs/other-inverters.md

onetoomany commented 10 months ago

Thanks - documentation looks like it covers it.

The only tweaks I'd make are:

  1. to explicitly state which entity you mean by "Note: You will need to change the min reserve in Home Assistant to match your minimum battery level." - namely 'input_number.predbat_set_reserve_min'
  2. state that the inverter needs to be put into "Passive Mode" for the sofar2mqtt to control the inverter.

Thanks again for your help in getting this to work!