Skip to content

Commit

Permalink
Merge pull request #97 from fboundy/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
fboundy authored Jan 30, 2024
2 parents 7754c69 + 8afc85e commit 2ac2b6f
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PV Opt: Home Assistant Solar/Battery Optimiser v3.5.2
# PV Opt: Home Assistant Solar/Battery Optimiser v3.5.3

Solar / Battery Charging Optimisation for Home Assistant. This appDaemon application attempts to optimise charging and discharging of a home solar/battery system to minimise cost electricity cost on a daily basis using freely available solar forecast data from SolCast. This is particularly beneficial for Octopus Agile but is also benefeficial for other time-of-use tariffs such as Octopus Flux or simple Economy 7.

Expand Down
20 changes: 10 additions & 10 deletions apps/pv_opt/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pv_opt:
# update_cycle_seconds: 15
# maximum_dod_percent: number.{device_name}_battery_minimum_soc

# id_consumption_today: sensor.{device_name}_house_load_today
id_consumption_today: sensor.{device_name}_consumption_today
# id_grid_import_today: sensor.{device_name}_grid_import_today
# id_grid_export_today: sensor.{device_name}_grid_export_today

Expand Down Expand Up @@ -179,12 +179,12 @@ pv_opt:


# Tariff comparison
id_daily_solar: sensor.{device_name}_power_generation_today
alt_tariffs:
- name: Eco7_Agile
octopus_import_tariff_code: E-2R-VAR-22-11-01-G
octopus_export_tariff_code: E-1R-AGILE-OUTGOING-19-05-13-G

- name: Flux
octopus_import_tariff_code: E-1R-FLUX-IMPORT-23-02-14-G
octopus_export_tariff_code: E-1R-FLUX-EXPORT-23-02-14-G
# id_daily_solar: sensor.{device_name}_power_generation_today
# alt_tariffs:
# - name: Eco7_Agile
# octopus_import_tariff_code: E-2R-VAR-22-11-01-G
# octopus_export_tariff_code: E-1R-AGILE-OUTGOING-19-05-13-G

# - name: Flux
# octopus_import_tariff_code: E-1R-FLUX-IMPORT-23-02-14-G
# octopus_export_tariff_code: E-1R-FLUX-EXPORT-23-02-14-G
32 changes: 24 additions & 8 deletions apps/pv_opt/pv_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#
USE_TARIFF = True

VERSION = "3.5.2"
VERSION = "3.5.3"
DEBUG = False

DATE_TIME_FORMAT_LONG = "%Y-%m-%d %H:%M:%S%z"
Expand Down Expand Up @@ -1452,27 +1452,42 @@ def optimise(self):
# We aren't in a charge/discharge slot and the next one doesn't start before the
# optimiser runs again

str_log = f"Next {direction} window starts in {time_to_slot_start:0.1f} minutes."
str_log = f"Next {direction} window starts in {time_to_slot_start:0.1f} minutes "

# If the next slot isn't soon then just check that current status matches what we see:
did_something = False

if status["charge"]["active"]:
str_log += " but inverter is charging. Disabling charge."
self.log(str_log)
self.inverter.control_charge(enable=False)
did_something = True
elif status["charge"]["start"] != status["charge"]["end"]:
str_log += " but charge start and end times are different."
self.log(str_log)
self.inverter.control_charge(enable=False)
did_something = True

elif status["discharge"]["active"]:
if status["discharge"]["active"]:
str_log += " but inverter is discharging. Disabling discharge."
self.log(str_log)
self.inverter.control_discharge(enable=False)
did_something = True
elif status["discharge"]["start"] != status["discharge"]["end"]:
str_log += " but charge start and end times are different."
self.log(str_log)
self.inverter.control_charge(enable=False)
did_something = True

elif (
if (
direction == "charge"
and self.charge_start_datetime > status["discharge"]["start"]
and status["discharge"]["start"] != status["discharge"]["end"]
):
str_log += " but inverter is has a discharge slot before then. Disabling discharge."
self.log(str_log)
self.inverter.control_discharge(enable=False)
did_something = True

elif (
direction == "discharge"
Expand All @@ -1482,16 +1497,17 @@ def optimise(self):
str_log += " but inverter is has a charge slot before then. Disabling charge."
self.log(str_log)
self.inverter.control_charge(enable=False)
did_something = True

elif status["hold_soc"]["active"]:
if status["hold_soc"]["active"]:
self.inverter.hold_soc(enable=False)
str_log += " but inverter is holding SOC. Disabling."
self.log(str_log)
did_something = True

else:
str_log += " Nothing to do."
if not did_something:
str_log += ". Nothing to do."
self.log(str_log)
did_something = False

if did_something:
if self.get_config("update_cycle_seconds") is not None:
Expand Down
10 changes: 7 additions & 3 deletions apps/pv_opt/solis.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,13 @@ def enable_timed_mode(self):
)

def control_charge(self, enable, **kwargs):
self.enable_timed_mode()
if enable:
self.enable_timed_mode()
self._control_charge_discharge("charge", enable, **kwargs)

def control_discharge(self, enable, **kwargs):
self.enable_timed_mode()
if enable:
self.enable_timed_mode()
self._control_charge_discharge("discharge", enable, **kwargs)

def hold_soc(self, enable, soc=None):
Expand Down Expand Up @@ -313,6 +315,8 @@ def _control_charge_discharge(self, direction, enable, **kwargs):
self._solis_control_charge_discharge(direction, enable, **kwargs)

def _solis_control_charge_discharge(self, direction, enable, **kwargs):
status = self._solis_state()

times = {
"start": kwargs.get("start", None),
"end": kwargs.get("end", None),
Expand All @@ -332,7 +336,7 @@ def _solis_control_charge_discharge(self, direction, enable, **kwargs):

# Disable by setting the times the same
if (enable is not None) and (not enable):
times["start"] = pd.Timestamp.now().floor("1T")
times["start"] = status[direction]["start"]
times["end"] = times["start"]

# Don't span midnight
Expand Down

0 comments on commit 2ac2b6f

Please sign in to comment.