From 4c2b494004e5672db121f5ef48020d1431ef7de8 Mon Sep 17 00:00:00 2001 From: fboundy Date: Tue, 20 Feb 2024 19:15:37 +0000 Subject: [PATCH 1/6] Additional error trapping when HA returns unknown or unavailable --- README.md | 2 +- apps/pv_opt/pv_opt.py | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9d2c299..7100f94 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PV Opt: Home Assistant Solar/Battery Optimiser v3.8.10 +# PV Opt: Home Assistant Solar/Battery Optimiser v3.8.11 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. diff --git a/apps/pv_opt/pv_opt.py b/apps/pv_opt/pv_opt.py index a79a2ae..8ef485c 100644 --- a/apps/pv_opt/pv_opt.py +++ b/apps/pv_opt/pv_opt.py @@ -20,7 +20,7 @@ # USE_TARIFF = True -VERSION = "3.8.10" +VERSION = "3.8.11" DEBUG = False DATE_TIME_FORMAT_LONG = "%Y-%m-%d %H:%M:%S%z" @@ -819,16 +819,21 @@ def get_ha_value(self, entity_id): # if the state is None return None if state is not None: + if state in ['unknown', 'unavailable']: + e = f"HA returned {state} for state of {entity_id}" + self._status(f"ERROR: {e}") + raise ValueError(e) # if the state is 'on' or 'off' then it's a bool - if state.lower() in ["on", "off", "true", "false"]: + elif state.lower() in ["on", "off", "true", "false"]: value = state.lower() in ["on", "true"] # see if we can coerce it into an int 1st and then a floar - for t in [int, float]: - try: - value = t(state) - except: - pass + else: + for t in [int, float]: + try: + value = t(state) + except: + pass # if none of the above return a string if value is None: From aa1249a1621bcc1608f796a83bca93e08b534d25 Mon Sep 17 00:00:00 2001 From: fboundy Date: Wed, 21 Feb 2024 17:38:15 +0000 Subject: [PATCH 2/6] Allow button to be "unknown" state --- apps/pv_opt/pv_opt.py | 2 +- apps/pv_opt/solis.py | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/pv_opt/pv_opt.py b/apps/pv_opt/pv_opt.py index 8ef485c..9acddb8 100644 --- a/apps/pv_opt/pv_opt.py +++ b/apps/pv_opt/pv_opt.py @@ -819,7 +819,7 @@ def get_ha_value(self, entity_id): # if the state is None return None if state is not None: - if state in ['unknown', 'unavailable']: + if (state in ['unknown', 'unavailable']) and (entity_id != 'button'): e = f"HA returned {state} for state of {entity_id}" self._status(f"ERROR: {e}") raise ValueError(e) diff --git a/apps/pv_opt/solis.py b/apps/pv_opt/solis.py index e7885d0..9b3271f 100644 --- a/apps/pv_opt/solis.py +++ b/apps/pv_opt/solis.py @@ -400,17 +400,23 @@ def _solis_control_charge_discharge(self, direction, enable, **kwargs): entity_id = self.host.config["id_timed_charge_discharge_button"] self.host.call_service("button/press", entity_id=entity_id) time.sleep(0.5) - time_pressed = pd.Timestamp(self.host.get_state(entity_id)) + try: + time_pressed = pd.Timestamp(self.host.get_state(entity_id)) - dt = (pd.Timestamp.now(self.host.tz) - time_pressed).total_seconds() - if dt < 10: - self.log(f"Successfully pressed button {entity_id}") + dt = (pd.Timestamp.now(self.host.tz) - time_pressed).total_seconds() + if dt < 10: + self.log(f"Successfully pressed button {entity_id}") - else: + else: + self.log( + f"Failed to press button {entity_id}. Last pressed at {time_pressed.strftime(TIMEFORMAT)} ({dt:0.2f} seconds ago)" + ) + except: self.log( - f"Failed to press button {entity_id}. Last pressed at {time_pressed.strftime(TIMEFORMAT)} ({dt:0.2f} seconds ago)" + f"Failed to press button {entity_id}: it appears to never have been pressed." ) + else: self.log("Inverter already at correct time settings") From 152002a281c71900a57b1835242896a72c68239c Mon Sep 17 00:00:00 2001 From: fboundy Date: Wed, 21 Feb 2024 17:38:44 +0000 Subject: [PATCH 3/6] 3.8.12 --- README.md | 2 +- apps/pv_opt/pv_opt.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7100f94..3341634 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PV Opt: Home Assistant Solar/Battery Optimiser v3.8.11 +# PV Opt: Home Assistant Solar/Battery Optimiser v3.8.12 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. diff --git a/apps/pv_opt/pv_opt.py b/apps/pv_opt/pv_opt.py index 9acddb8..50dbf45 100644 --- a/apps/pv_opt/pv_opt.py +++ b/apps/pv_opt/pv_opt.py @@ -20,7 +20,7 @@ # USE_TARIFF = True -VERSION = "3.8.11" +VERSION = "3.8.12" DEBUG = False DATE_TIME_FORMAT_LONG = "%Y-%m-%d %H:%M:%S%z" From fdac4ca6fe1af0ba5740805182c976f71acbce80 Mon Sep 17 00:00:00 2001 From: fboundy Date: Thu, 22 Feb 2024 18:34:08 +0000 Subject: [PATCH 4/6] Fix 'button' check for unknown/unavailable --- apps/pv_opt/pv_opt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/pv_opt/pv_opt.py b/apps/pv_opt/pv_opt.py index 50dbf45..2918acd 100644 --- a/apps/pv_opt/pv_opt.py +++ b/apps/pv_opt/pv_opt.py @@ -819,7 +819,7 @@ def get_ha_value(self, entity_id): # if the state is None return None if state is not None: - if (state in ['unknown', 'unavailable']) and (entity_id != 'button'): + if (state in ['unknown', 'unavailable']) and (entity_id[:6] != 'button'): e = f"HA returned {state} for state of {entity_id}" self._status(f"ERROR: {e}") raise ValueError(e) From d2498deea79e2c00754f5996b6dca757eb338b07 Mon Sep 17 00:00:00 2001 From: fboundy Date: Thu, 22 Feb 2024 18:44:27 +0000 Subject: [PATCH 5/6] Check for valid SOC at start of window --- apps/pv_opt/pv_opt.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/pv_opt/pv_opt.py b/apps/pv_opt/pv_opt.py index 2918acd..e36b4a5 100644 --- a/apps/pv_opt/pv_opt.py +++ b/apps/pv_opt/pv_opt.py @@ -274,7 +274,7 @@ def hass2df(self, entity_id, days=2, log=False): hist = self.get_history(entity_id=entity_id, days=days) - if len(hist) >0: + if (hist is not None) and (len(hist) >0): df = pd.DataFrame(hist[0]).set_index("last_updated")["state"] df.index = pd.to_datetime(df.index, format="ISO8601") @@ -283,7 +283,7 @@ def hass2df(self, entity_id, days=2, log=False): df = df[df != "unknown"] else: - raise ValueError(f"No data returned from HASS entity {entity_id}") + self.log(f"No data returned from HASS entity {entity_id}", level="ERROR") df = None return df @@ -1385,6 +1385,11 @@ def optimise(self): self.soc_now = self.get_config("id_battery_soc") x = self.hass2df(self.config["id_battery_soc"], days=1, log=self.debug) + if x is None: + self.log("") + self.log("Unable to get SOC at start of current window.", level="ERROR") + return + if self.debug: self.log(f">>> soc_now: {self.soc_now}") self.log(f">>> x: {x}") From 12f6a8562299bba9d7b8668d378f62c2f81e5ce2 Mon Sep 17 00:00:00 2001 From: fboundy Date: Thu, 22 Feb 2024 18:45:10 +0000 Subject: [PATCH 6/6] 3.8.14 --- README.md | 2 +- apps/pv_opt/pv_opt.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3341634..531ec29 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PV Opt: Home Assistant Solar/Battery Optimiser v3.8.12 +# PV Opt: Home Assistant Solar/Battery Optimiser v3.8.14 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. diff --git a/apps/pv_opt/pv_opt.py b/apps/pv_opt/pv_opt.py index e36b4a5..bc4c939 100644 --- a/apps/pv_opt/pv_opt.py +++ b/apps/pv_opt/pv_opt.py @@ -20,7 +20,7 @@ # USE_TARIFF = True -VERSION = "3.8.12" +VERSION = "3.8.14" DEBUG = False DATE_TIME_FORMAT_LONG = "%Y-%m-%d %H:%M:%S%z"