Skip to content

Commit 512f8d6

Browse files
authored
Merge branch 'springfall2008:main' into main
2 parents c3cad95 + 1faff22 commit 512f8d6

60 files changed

Lines changed: 8142 additions & 1278 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.cspell/custom-dictionary-workspace.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ enph
202202
enphase
203203
enphaseenergy
204204
Enpower
205+
entityfilter
205206
entrez
206207
epod
207208
epvtoday
@@ -477,6 +478,7 @@ overfitting
477478
oversizing
478479
overvoltage
479480
ownerapi
481+
pac
480482
parseable
481483
pbat
482484
pbgw
@@ -633,6 +635,7 @@ startswith
633635
startt
634636
stdlib
635637
stepline
638+
stevekirtley
636639
Storion
637640
Stromligning
638641
strptime

‎apps/predbat/car_charger_control.py‎

Lines changed: 656 additions & 0 deletions
Large diffs are not rendered by default.

‎apps/predbat/compare.py‎

Lines changed: 149 additions & 93 deletions
Large diffs are not rendered by default.

‎apps/predbat/components.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ def load_component_class(component_info):
714714
"load_ml_source": {"required": False, "config": "load_ml_source", "default": False},
715715
"load_ml_max_days_history": {"required": False, "config": "load_ml_max_days_history", "default": 28},
716716
"load_ml_database_days": {"required": False, "config": "load_ml_database_days", "default": 90},
717+
"load_ml_retrain_interval_hours": {"required": False, "config": "load_ml_retrain_interval_hours", "default": 2},
717718
},
718719
"phase": 2, # Load ML in phase 2 so that any Predbat cloud components (such as GEcloud) have been started and initialised pv_today, etc
719720
"can_restart": True,

‎apps/predbat/config.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,13 @@
883883
"type": "switch",
884884
"default": True,
885885
},
886+
{
887+
"name": "octopus_intelligent_charger_follows_car",
888+
"friendly_name": "Drive the charger to the Intelligent dispatches when Octopus drives the car",
889+
"type": "switch",
890+
"default": False,
891+
"enable": "expert_mode",
892+
},
886893
{
887894
"name": "octopus_saving_auto_join",
888895
"friendly_name": "Octopus Saving Session Auto Join",
@@ -2842,6 +2849,7 @@
28422849
"enphase_automatic": {"type": "boolean"},
28432850
"enphase_automatic_ignore_pv": {"type": "boolean"},
28442851
"octopus_intelligent_slot": {"type": "sensor|sensor_list", "sensor_type": "boolean|action", "entries": "num_cars", "optional_entries": True},
2852+
"octopus_intelligent_planned_slot": {"type": "sensor|sensor_list", "sensor_type": "boolean", "entries": "num_cars", "optional_entries": True},
28452853
"octopus_ready_time": {"type": "sensor|sensor_list", "sensor_type": "string", "entries": "num_cars", "optional_entries": True},
28462854
"octopus_charge_limit": {"type": "sensor|sensor_list", "sensor_type": "float", "entries": "num_cars", "optional_entries": True},
28472855
"octopus_slot_low_rate": {"type": "boolean"},
@@ -2900,6 +2908,7 @@
29002908
"enable_coarse_fine_levels": {"type": "boolean"},
29012909
"load_power_fill_enable": {"type": "boolean"},
29022910
"load_ml_enable": {"type": "boolean"},
2911+
"load_ml_retrain_interval_hours": {"type": "integer", "min": 1, "max": 48},
29032912
"gateway_device_id": {"type": "string", "empty": False},
29042913
"gateway_mqtt_host": {"type": "string", "empty": False},
29052914
"gateway_mqtt_port": {"type": "integer", "zero": False},

‎apps/predbat/const.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,7 @@
171171
# need the same answer to "which of these two states is the more significant one to show".
172172
CHARGE_STATE_PRECEDENCE = ["Charging", "Freeze charging", "Hold charging"]
173173
EXPORT_STATE_PRECEDENCE = ["Exporting", "Freeze exporting", "Hold exporting"]
174+
175+
# A log text that repeats (an API response, an Intelligent slot list) is logged in full again after this long, so
176+
# the full text is never only in a rotated-out log file; in between it is logged as one short line
177+
REPEAT_FULL_LOG_SECONDS = 3600

‎apps/predbat/execute.py‎

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -790,25 +790,32 @@ def execute_plan(self):
790790
# Don't disable discharge during force charge/discharge slots but otherwise turn it off to prevent
791791
# from draining the battery
792792
if not isExporting:
793-
if inverter.inv_has_timed_pause:
794-
if resetPause:
795-
inverter.adjust_pause_mode(pause_discharge=True)
796-
pause_discharge_requested = True
797-
resetPause = False
793+
# Not while this inverter is actually charging, unless it can pause: the battery is being
794+
# filled from the grid, so it cannot be feeding the car. A discharge rate of 0 adds nothing
795+
# then, and some GivEnergy inverters will not grid-charge at all with it at 0 and the SoC on
796+
# the reserve (#5419); pinning reserve just above a rising SoC costs a write for every 1% of
797+
# the climb (#3899). Both are left to reset below for the duration, and applied once charging
798+
# stops - which is the point the inverter returns to demand and the hold starts to mean
799+
# something. A timed pause is one latched write that blocks only discharge, so it stays on
800+
# through the charge, covering the moments the inverter reaches its target before Predbat
801+
# sees it. The sibling iBoost hold below already sits out a charge for the same reason.
802+
hold_deferred = (not inverter.inv_has_timed_pause) and status_per_inverter.get(inverter.id) == "Charging"
803+
if hold_deferred:
804+
self.log("Inverter {} is charging, so the hold for car {} leaves the discharge rate and reserve alone until charging stops".format(inverter.id, car_n))
798805
else:
799-
if discharge_rate is None:
800-
discharge_rate = 0
801-
# Not while actually charging: the battery is being filled from the grid, so it
802-
# cannot be feeding the car, and pinning reserve just above a rising SoC costs a
803-
# write for every 1% of the climb (#3899). Left to reset below for the duration,
804-
# and latched at the SoC reached once charging stops - which is the point the
805-
# inverter returns to demand and the hold starts to mean something. The sibling
806-
# iBoost hold below already sits out a charge for the same reason.
807-
if self.set_reserve_enable and status != "Charging":
808-
inverter.adjust_reserve(min(inverter.soc_percent + 1, 100))
809-
resetReserve = False
806+
if inverter.inv_has_timed_pause:
807+
if resetPause:
808+
inverter.adjust_pause_mode(pause_discharge=True)
809+
pause_discharge_requested = True
810+
resetPause = False
811+
else:
812+
if discharge_rate is None:
813+
discharge_rate = 0
814+
if self.set_reserve_enable:
815+
inverter.adjust_reserve(min(inverter.soc_percent + 1, 100))
816+
resetReserve = False
817+
self.log("Disabling battery discharge whilst car {} is charging".format(car_n))
810818
carHolding = True
811-
self.log("Disabling battery discharge whilst car {} is charging".format(car_n))
812819
if ("Hold for car" not in status) and (status_hold_car == ""):
813820
if status == "Demand":
814821
status = "Hold for car"

0 commit comments

Comments
 (0)