From f02853036f74dd0400480bc08267e730828b0ae2 Mon Sep 17 00:00:00 2001 From: Mark Gardner Date: Sat, 10 Jul 2021 16:43:29 +0100 Subject: [PATCH] Added first iteration of Go capability --- README.md | 16 ++++++ .../octopusagile/OctopusAgile/Agile.py | 49 ++++++------------- custom_components/octopusagile/__init__.py | 41 ++++++---------- 3 files changed, 46 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 57bbb51..8ff9b20 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,22 @@ octopusagile: serial: 00000000 auth: abc00000000 startdate: "2020-05-08" + # Easily switch between agile and go rates. + # If you include gorate, it'll override + # the times for the times specified in gotimes + gorate: 5 + # You can leave godayrate out to go by + # Agile rates outside of offpeak period + godayrate: 16.26 + gotimes: + - "23:30:00" + - "00:00:00" + - "00:30:00" + - "01:00:00" + - "01:30:00" + - "02:00:00" + - "02:30:00" + - "03:00:00" moneymakers: - switch.water_heater: null - climate.downstairs: diff --git a/custom_components/octopusagile/OctopusAgile/Agile.py b/custom_components/octopusagile/OctopusAgile/Agile.py index 81e5a0a..74c2775 100644 --- a/custom_components/octopusagile/OctopusAgile/Agile.py +++ b/custom_components/octopusagile/OctopusAgile/Agile.py @@ -24,7 +24,7 @@ def round_time(self, t): minute = 30 return (t.replace(second=0, microsecond=0, minute=minute, hour=t.hour)) - def __init__(self, area_code=None, auth=None, mpan=None, serial=None, gas=None): + def __init__(self, area_code=None, auth=None, mpan=None, serial=None, gas=None, gorate=None, godayrate=None, gotimes=[]): self.base_url = 'https://api.octopus.energy/v1' self.meter_points_url = f'{self.base_url}/electricity-meter-points/' self.cost_url = f'{self.base_url}/products/AGILE-18-02-21/electricity-tariffs' @@ -33,6 +33,10 @@ def __init__(self, area_code=None, auth=None, mpan=None, serial=None, gas=None): self.MPAN = mpan self.SERIAL = serial + self.gorate = gorate + self.godayrate = godayrate + self.gotimes = gotimes + if area_code is None: self.area_code = self.find_region(self.MPAN) else: @@ -193,6 +197,17 @@ def get_raw_rates_json(self, date_from, date_to=None): f'standard-unit-rates/{ date_from }{ date_to }', headers=headers) # print(r) results = r.json() + # go_times = ["17:00:00", "17:30:00"] + if self.gorate is not None: + for result in results["results"]: + is_go_time = any(gotime in result["valid_from"] for gotime in self.gotimes) + if is_go_time: + result["value_inc_vat"] = round(self.gorate, 2) + result["value_exc_vat"] = round(self.gorate/1.05,2) + elif self.godayrate is not None: + result["value_inc_vat"] = round(self.godayrate, 2) + result["value_exc_vat"] = round(self.godayrate/1.05,2) + _LOGGER.debug(r.url) return results @@ -335,38 +350,6 @@ def get_consumption(self, start, end): consumption = requests.get(url=consumption_url_str, auth=(self.auth, '')) return consumption.json() - def aggregate_consumption(self): - week_days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday") - - end = datetime.now().date() - start = end - timedelta(days=30) - - data = self.get_consumption(start, end) - hourly = {} - daily = {} - hour_daily = {} - - for record in data['results']: - start = dateutil.parser.parse(record['interval_start']) - if start.minute not in [0, 30]: continue - - if f"{start.hour}:{start.minute:02}" not in hourly: - hourly[f"{start.hour}:{start.minute:02}"] = [] - hourly[f"{start.hour}:{start.minute:02}"].append(record['consumption']) - - if week_days[start.weekday()] not in daily: - daily[week_days[start.weekday()]] = [] - daily[week_days[start.weekday()]].append(record['consumption'] * 48) - - if f"{week_days[start.weekday()]} {start.hour}:{start.minute:02}" not in hour_daily: - hour_daily[f"{week_days[start.weekday()]} {start.hour}:{start.minute:02}"] = [] - hour_daily[f"{week_days[start.weekday()]} {start.hour}:{start.minute:02}"].append(record['consumption']) - - for d in [hourly, daily, hour_daily]: - for key, value in d.items(): - d[key]=round(sum(value) / len(value), 3) - return hourly, daily, hour_daily - def calculcate_cost(self, start, end): jconsumption = self.get_consumption(start, end) jcost = self.get_raw_rates_json(f"{start.isoformat()}T00:00:00Z", f"{end.isoformat()}T23:59:59Z") diff --git a/custom_components/octopusagile/__init__.py b/custom_components/octopusagile/__init__.py index 8e39bc5..8ebe127 100644 --- a/custom_components/octopusagile/__init__.py +++ b/custom_components/octopusagile/__init__.py @@ -58,7 +58,10 @@ def setup(hass, config): auth = config["octopusagile"]["auth"] mpan = config["octopusagile"]["mpan"] serial = config["octopusagile"]["serial"] - myrates = Agile(area_code=region_code, auth=auth, mpan=mpan, serial=serial) + gorate = config["octopusagile"].get("gorate", None) + godayrate = config["octopusagile"].get("godayrate", None) + gotimes = config["octopusagile"].get("gotimes", []) + myrates = Agile(area_code=region_code, auth=auth, mpan=mpan, serial=serial, gorate=gorate, godayrate=godayrate, gotimes=gotimes) hass.states.set(f"octopusagile.region_code", region_code) startdate = config["octopusagile"]["startdate"] hass.states.set(f"octopusagile.startdate", startdate) @@ -79,12 +82,6 @@ def setup(hass, config): device_times = data.get("device_times", {}) for entity_id, vals in device_times.items(): hass.states.set(f"octopusagile.{entity_id}", vals["start_time"], vals["attribs"]) - avg_rate_inc_peak = data.get("avg_rate_inc_peak") - if avg_rate_inc_peak is not None: - hass.states.set(f"octopusagile.avg_rate_inc_peak", avg_rate_inc_peak, {"unit_of_measurement": "p/kWh"}) - avg_rate_exc_peak = data.get("avg_rate_exc_peak") - if avg_rate_exc_peak is not None: - hass.states.set(f"octopusagile.avg_rate_exc_peak", avg_rate_exc_peak, {"unit_of_measurement": "p/kWh"}) f.close() except IOError: print(f"{datatorefile} does not exist") @@ -210,15 +207,19 @@ def handle_update_timers(call): hass.states.set(f"octopusagile.timers", "", {"timers":timer_list}) - + jsonstr = json.dumps({"timers":timer_list, "rates":new_rates, "all_rates": new_rates}) + f = open(datatorefile,"w") + f.write(jsonstr) + f.close() # Calc averages for the next day # Including peak date_from = datetime.strftime(datetime.utcnow(), '%Y-%m-%dT23:00:00Z') date_to = datetime.strftime((datetime.utcnow() + timedelta(days=1)), f"%Y-%m-%dT23:00:00Z") rates_exc_peak = myrates.get_rates(date_from, date_to)["date_rates"] - avg_rate_inc_peak = round(sum(rates_exc_peak.values())/len(rates_exc_peak.values()), 2) - hass.states.set(f"octopusagile.avg_rate_inc_peak", avg_rate_inc_peak, {"unit_of_measurement": "p/kWh"}) + if len(rates_exc_peak.values()) > 0: + avg_rate_inc_peak = round(sum(rates_exc_peak.values())/len(rates_exc_peak.values()), 2) + hass.states.set(f"octopusagile.avg_rate_inc_peak", avg_rate_inc_peak) # Excluding peak date_from = datetime.strftime(datetime.utcnow(), '%Y-%m-%dT23:00:00Z') @@ -229,19 +230,9 @@ def handle_update_timers(call): date_to = datetime.strftime((datetime.utcnow() + timedelta(days=1)), f"%Y-%m-%dT23:00:00Z") rates_exc_peak.update(myrates.get_rates(date_from, date_to)["date_rates"]) - avg_rate_exc_peak = round(sum(rates_exc_peak.values())/len(rates_exc_peak.values()), 2) - hass.states.set(f"octopusagile.avg_rate_exc_peak", avg_rate_exc_peak, {"unit_of_measurement": "p/kWh"}) - - jsonstr = json.dumps({ - "timers":timer_list, - "rates":new_rates, - "all_rates": new_rates, - "avg_rate_exc_peak": avg_rate_exc_peak, - "avg_rate_inc_peak": avg_rate_inc_peak - }) - f = open(datatorefile,"w") - f.write(jsonstr) - f.close() + if len(rates_exc_peak.values()) > 0: + avg_rate_exc_peak = round(sum(rates_exc_peak.values())/len(rates_exc_peak.values()), 2) + hass.states.set(f"octopusagile.avg_rate_exc_peak", avg_rate_exc_peak) def handle_half_hour_timer(call): """Handle the service call.""" @@ -371,10 +362,6 @@ def handle_update_consumption(call): round(monthlycost/100, 2), attributes={'unit_of_measurement': '£', 'icon': 'mdi:cash'}) - hourly, daily, hour_daily = myrates.aggregate_consumption() - hass.states.set(f"octopusagile.hourly_consumption", "", hourly) - hass.states.set(f"octopusagile.daily_consumption", "", daily) - hass.states.set(f"octopusagile.hour_daily_consumption", "", hour_daily) def half_hour_timer(nowtime): roundedtime = myrates.round_time(nowtime)