From dd6343ac7eca499a3d9677c7dff716bcb91cc7f3 Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:27:38 +0000 Subject: [PATCH 1/2] Automatic unit detected for Watts vs Kwh when reading historical data --- apps/predbat/predbat.py | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py index daf5fc4f7..fa9a1abfc 100644 --- a/apps/predbat/predbat.py +++ b/apps/predbat/predbat.py @@ -1012,6 +1012,7 @@ def find_charge_curve(self): smoothing=False, divide_by=1.0, scale=self.base.battery_scaling, + required_unit="kWh", ) charge_rate = self.base.minute_data( charge_rate_data[0], @@ -1024,6 +1025,7 @@ def find_charge_curve(self): smoothing=False, divide_by=1.0, scale=1.0, + required_unit="W", ) predbat_status = self.base.minute_data_state(predbat_status_data[0], self.base.max_days_previous, self.base.now_utc, "state", "last_updated") battery_power = self.base.minute_data( @@ -1037,6 +1039,7 @@ def find_charge_curve(self): smoothing=False, divide_by=1.0, scale=1.0, + required_unit="W", ) min_len = min(len(soc_kwh), len(charge_rate), len(predbat_status), len(battery_power)) self.log("Find charge curve has {} days of data, max days {}".format(min_len / 60 / 24.0, self.base.max_days_previous)) @@ -3063,7 +3066,7 @@ def load_car_energy(self, now_utc): """ self.car_charging_energy = {} if "car_charging_energy" in self.args: - self.car_charging_energy = self.minute_data_import_export(now_utc, "car_charging_energy", scale=self.car_charging_energy_scale) + self.car_charging_energy = self.minute_data_import_export(now_utc, "car_charging_energy", scale=self.car_charging_energy_scale, required_unit="kWh") else: self.log("Car charging hold {} threshold {}".format(self.car_charging_hold, self.car_charging_threshold * 60.0)) return self.car_charging_energy @@ -3094,7 +3097,7 @@ def get_history_async(self, entity_id, days=None): self.log("Failure to fetch history for {}".format(entity_id)) raise ValueError - def minute_data_import_export(self, now_utc, key, scale=1.0): + def minute_data_import_export(self, now_utc, key, scale=1.0, required_unit=None): """ Download one or more entities for import/export data """ @@ -3111,7 +3114,7 @@ def minute_data_import_export(self, now_utc, key, scale=1.0): if history: import_today = self.minute_data( - history[0], self.max_days_previous, now_utc, "state", "last_updated", backwards=True, smoothing=True, scale=scale, clean_increment=True, accumulate=import_today + history[0], self.max_days_previous, now_utc, "state", "last_updated", backwards=True, smoothing=True, scale=scale, clean_increment=True, accumulate=import_today, required_unit=required_unit ) else: self.log("Error: Unable to fetch history for {}".format(entity_id)) @@ -3120,7 +3123,7 @@ def minute_data_import_export(self, now_utc, key, scale=1.0): return import_today - def minute_data_load(self, now_utc, entity_name, max_days_previous): + def minute_data_load(self, now_utc, entity_name, max_days_previous, required_unit=None): """ Download one or more entities for load data """ @@ -3154,6 +3157,7 @@ def minute_data_load(self, now_utc, entity_name, max_days_previous): scale=self.load_scaling, clean_increment=True, accumulate=load_minutes, + required_unit=required_unit, ) else: self.log("ERROR: Unable to fetch history for {}".format(entity_id)) @@ -3243,6 +3247,7 @@ def minute_data( accumulate=[], adjust_key=None, spreading=None, + required_unit=None, ): """ Turns data from HA into a hash of data indexed by minute with the data being the value @@ -3261,6 +3266,9 @@ def minute_data( self.log("Warning, empty history passed to minute_data, ignoring (check your settings)...") return mdata + if self.debug_enable: + self.log("Loading data from {}".format(history[0])) + # Process history for item in history: # Ignore data without correct keys @@ -3280,6 +3288,19 @@ def minute_data( except (ValueError, TypeError): continue + # Find and converter units + if required_unit and ('attributes' in item): + if 'unit_of_measurement' in item['attributes']: + unit = item['attributes']['unit_of_measurement'] + if unit != required_unit: + if required_unit in ['kW', 'kWh'] and unit in ['W', 'Wh']: + state = state / 1000.0 + elif required_unit in ['W', 'Wh'] and unit in ['kW', 'kWh']: + state = state * 1000.0 + else: + # Ignore data in wrong units if we can't converter + continue + # Divide down the state if required if divide_by: state /= divide_by @@ -8627,7 +8648,7 @@ def fetch_extra_load_forecast(self, now_utc): data = None load_forecast = self.minute_data( - data, self.forecast_days, self.midnight_utc, "energy", "last_updated", backwards=False, clean_increment=False, smoothing=True, divide_by=1.0, scale=1.0 + data, self.forecast_days, self.midnight_utc, "energy", "last_updated", backwards=False, clean_increment=False, smoothing=True, divide_by=1.0, scale=1.0, required_unit="kWh" ) return load_forecast @@ -9663,7 +9684,7 @@ def fetch_sensor_data(self): # Iboost load data if self.iboost_enable: if "iboost_energy_today" in self.args: - self.iboost_energy_today, iboost_energy_age = self.minute_data_load(self.now_utc, "iboost_energy_today", 1) + self.iboost_energy_today, iboost_energy_age = self.minute_data_load(self.now_utc, "iboost_energy_today", 1, required_unit="kWh") if iboost_energy_age >= 1: self.iboost_today = self.dp2(abs(self.iboost_energy_today[0] - self.iboost_energy_today[self.minutes_now])) self.log("IBoost energy today from sensor reads {} kWh".format(self.iboost_today)) @@ -9674,7 +9695,7 @@ def fetch_sensor_data(self): else: # Load data if "load_today" in self.args: - self.load_minutes, self.load_minutes_age = self.minute_data_load(self.now_utc, "load_today", self.max_days_previous) + self.load_minutes, self.load_minutes_age = self.minute_data_load(self.now_utc, "load_today", self.max_days_previous, required_unit="kWh") self.log("Found {} load_today datapoints going back {} days".format(len(self.load_minutes), self.load_minutes_age)) self.load_minutes_now = max(self.load_minutes.get(0, 0) - self.load_minutes.get(self.minutes_now, 0), 0) else: @@ -9684,21 +9705,21 @@ def fetch_sensor_data(self): # Load import today data if "import_today" in self.args: - self.import_today = self.minute_data_import_export(self.now_utc, "import_today", scale=self.import_export_scaling) + self.import_today = self.minute_data_import_export(self.now_utc, "import_today", scale=self.import_export_scaling, required_unit="kWh") self.import_today_now = max(self.import_today.get(0, 0) - self.import_today.get(self.minutes_now, 0), 0) else: self.log("WARN: You have not set import_today in apps.yaml, you will have no previous import data") # Load export today data if "export_today" in self.args: - self.export_today = self.minute_data_import_export(self.now_utc, "export_today", scale=self.import_export_scaling) + self.export_today = self.minute_data_import_export(self.now_utc, "export_today", scale=self.import_export_scaling, required_unit="kWh") self.export_today_now = max(self.export_today.get(0, 0) - self.export_today.get(self.minutes_now, 0), 0) else: self.log("WARN: You have not set export_today in apps.yaml, you will have no previous export data") # PV today data if "pv_today" in self.args: - self.pv_today = self.minute_data_import_export(self.now_utc, "pv_today") + self.pv_today = self.minute_data_import_export(self.now_utc, "pv_today", required_unit="kWh") self.pv_today_now = max(self.pv_today.get(0, 0) - self.pv_today.get(self.minutes_now, 0), 0) else: self.log("WARN: You have not set pv_today in apps.yaml, you will have no previous pv data") From c88724fca085a7ba7dcee1beec6d63120b5cf852 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:29:41 +0000 Subject: [PATCH 2/2] [pre-commit.ci lite] apply automatic fixes --- apps/predbat/predbat.py | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py index fa9a1abfc..b9ba74d2a 100644 --- a/apps/predbat/predbat.py +++ b/apps/predbat/predbat.py @@ -3114,7 +3114,17 @@ def minute_data_import_export(self, now_utc, key, scale=1.0, required_unit=None) if history: import_today = self.minute_data( - history[0], self.max_days_previous, now_utc, "state", "last_updated", backwards=True, smoothing=True, scale=scale, clean_increment=True, accumulate=import_today, required_unit=required_unit + history[0], + self.max_days_previous, + now_utc, + "state", + "last_updated", + backwards=True, + smoothing=True, + scale=scale, + clean_increment=True, + accumulate=import_today, + required_unit=required_unit, ) else: self.log("Error: Unable to fetch history for {}".format(entity_id)) @@ -3289,18 +3299,18 @@ def minute_data( continue # Find and converter units - if required_unit and ('attributes' in item): - if 'unit_of_measurement' in item['attributes']: - unit = item['attributes']['unit_of_measurement'] + if required_unit and ("attributes" in item): + if "unit_of_measurement" in item["attributes"]: + unit = item["attributes"]["unit_of_measurement"] if unit != required_unit: - if required_unit in ['kW', 'kWh'] and unit in ['W', 'Wh']: + if required_unit in ["kW", "kWh"] and unit in ["W", "Wh"]: state = state / 1000.0 - elif required_unit in ['W', 'Wh'] and unit in ['kW', 'kWh']: + elif required_unit in ["W", "Wh"] and unit in ["kW", "kWh"]: state = state * 1000.0 else: # Ignore data in wrong units if we can't converter - continue - + continue + # Divide down the state if required if divide_by: state /= divide_by @@ -8648,7 +8658,17 @@ def fetch_extra_load_forecast(self, now_utc): data = None load_forecast = self.minute_data( - data, self.forecast_days, self.midnight_utc, "energy", "last_updated", backwards=False, clean_increment=False, smoothing=True, divide_by=1.0, scale=1.0, required_unit="kWh" + data, + self.forecast_days, + self.midnight_utc, + "energy", + "last_updated", + backwards=False, + clean_increment=False, + smoothing=True, + divide_by=1.0, + scale=1.0, + required_unit="kWh", ) return load_forecast