From 6ae0d5936f940e99bc034da8487373fdf465897a Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 22 Oct 2022 16:48:05 +0100 Subject: [PATCH 01/17] Fix init --- octopus_energy_api/octopus_energy_api.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/octopus_energy_api/octopus_energy_api.py b/octopus_energy_api/octopus_energy_api.py index e69756e..04d4850 100644 --- a/octopus_energy_api/octopus_energy_api.py +++ b/octopus_energy_api/octopus_energy_api.py @@ -1,8 +1,7 @@ from octopus_energy_api.api_interface import api from octopus_energy_api.account import account from octopus_energy_api.urls import urls -from octopus_energy_api.urls import meter_point - +from octopus_energy_api.meter_point import meter_point from datetime import datetime import statistics @@ -21,11 +20,10 @@ def __init__(self, account_number, api_key, mpan=None, serial_number=None): self.properties = [] for property in self.account.properties: meters_points = [] - for meter_point in property['electricity_meter_points']: - mp = meter_point(self._urls, self._api, meter_point) - meters.append(mp) + for elec_meter_point in property["electricity_meter_points"]: + mp = meter_point(self._urls, self._api, elec_meter_point) + meters_points.append(mp) self.properties.append(meters_points) - def account_details(self): """See account data""" From 634a3f88f2f3a80be45f2c428cad336be9117ca5 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 22 Feb 2023 20:57:03 +0100 Subject: [PATCH 02/17] Move to poetry from setuptools --- pyproject.toml | 17 +++++++++++++++++ requirements.txt | 2 -- setup.cfg | 3 --- setup.py | 35 ----------------------------------- 4 files changed, 17 insertions(+), 40 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1923a83 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "octopus-energy-api" +version = "0.8.1" +description = "Wrapper for communicating with the Octopus Energy API" +authors = ["Chris Jones "] +license = "MIT" +readme = "README.md" +packages = [{include = "octopus_energy_api"}] + +[tool.poetry.dependencies] +python = "^3.11" +requests = "^2.28.2" +pytest-mock = "^3.6.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a59c4f9..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests==2.25.1 -pytest-mock==3.6.1 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 9d5f797..0000000 --- a/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -# Inside of setup.cfg -[metadata] -description-file = README.md \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 2144ebf..0000000 --- a/setup.py +++ /dev/null @@ -1,35 +0,0 @@ -from setuptools import setup, Extension -import os - -# read the contents of your README file -from os import path - -this_directory = path.abspath(path.dirname(__file__)) -with open(path.join(this_directory, "README.md"), encoding="utf-8") as f: - long_description = f.read() - -setup( - name="octopus_energy_api", # How you named your package folder (MyLib) - packages=["octopus_energy_api"], # Chose the same as "name" - version="0.8", # Start with a small number and increase it with every change you make - license="MIT", # Chose a license from here: https://help.github.com/articles/licensing-a-repository - description="Wrapper for communicating with the Octopus Energy API", # Give a short description about your library - long_description=long_description, - long_description_content_type="text/markdown", - author="Euan Campbell", # Type in your name - author_email="dev@euan.app", - url="https://github.com/euanacampbell/octopus_energy_api", # Provide either the link to your github or to your website - download_url="https://github.com/euanacampbell/octopus_energy_api/archive/refs/heads/master.tar.gz", # I explain this later on - keywords=["energy", "api", "requests"], # Keywords that define your package best - install_requires=["requests"], # I get to this in a second - classifiers=[ - "Development Status :: 3 - Alpha", # Chose either "3 - Alpha", "4 - Beta" or "5 - Production/Stable" as the current state of your package - "Intended Audience :: Developers", # Define that your audience are developers - "Topic :: Software Development :: Build Tools", - "License :: OSI Approved :: MIT License", # Again, pick a license - "Programming Language :: Python :: 3", # Specify which python versions that you want to support - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - ], -) From 4c16b66766334b7f87efc1615059aff5010325fd Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 22 Feb 2023 20:59:35 +0100 Subject: [PATCH 03/17] personal not work project --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1923a83..f37f4ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "octopus-energy-api" version = "0.8.1" description = "Wrapper for communicating with the Octopus Energy API" -authors = ["Chris Jones "] +authors = ["Euan Campbell ","Chris Jones "] license = "MIT" readme = "README.md" packages = [{include = "octopus_energy_api"}] From 1b9b6e2e56748578feb478ac6d0e67f87cf3b3f9 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 16 Mar 2023 21:46:36 +0000 Subject: [PATCH 04/17] First bash at a meter class and an opperation to lookup the start and end dates of it --- octopus_energy_api/account.py | 6 +++--- octopus_energy_api/meter.py | 8 ++++++++ octopus_energy_api/meter_point.py | 13 +++++++----- octopus_energy_api/octopus_energy_api.py | 26 ++++++++++++++++++++++-- pyproject.toml | 2 +- 5 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 octopus_energy_api/meter.py diff --git a/octopus_energy_api/account.py b/octopus_energy_api/account.py index 0308326..52cca72 100644 --- a/octopus_energy_api/account.py +++ b/octopus_energy_api/account.py @@ -5,10 +5,10 @@ def __init__(self, account_data): setattr(self, k, v) # setting additional values - #self.mpan = self.properties[0]["electricity_meter_points"][-1]["mpan"] - #self.serial_number = self.properties[0]["electricity_meter_points"][-1]["meters"][-1][ + # self.mpan = self.properties[0]["electricity_meter_points"][-1]["mpan"] + # self.serial_number = self.properties[0]["electricity_meter_points"][-1]["meters"][-1][ # "serial_number" - #] + # ] def all_account_data(self): diff --git a/octopus_energy_api/meter.py b/octopus_energy_api/meter.py new file mode 100644 index 0000000..b352fe8 --- /dev/null +++ b/octopus_energy_api/meter.py @@ -0,0 +1,8 @@ +class meter: + def __init__(self, api, mpan, meter_data): + self._api = api + self.mpan = mpan + self.meter_data = meter_data + for k, v in meter_data.items(): + setattr(self, k, v) + print(self._api.discover_meter(self)) diff --git a/octopus_energy_api/meter_point.py b/octopus_energy_api/meter_point.py index 672df25..291c130 100644 --- a/octopus_energy_api/meter_point.py +++ b/octopus_energy_api/meter_point.py @@ -1,10 +1,13 @@ -class meter_point: - def __init__(self, urls, api, meter_point_data): +import octopus_energy_api.meter + - self._urls = urls +class meter_point: + def __init__(self, api, meter_point_data): self._api = api - self.meter_point_data = meter_point_data for k, v in meter_point_data.items(): setattr(self, k, v) - \ No newline at end of file + self.m = [] + for thismeter in self.meters: + am = octopus_energy_api.meter.meter(api, self.mpan, thismeter) + self.m.append(am) diff --git a/octopus_energy_api/octopus_energy_api.py b/octopus_energy_api/octopus_energy_api.py index 04d4850..92b159b 100644 --- a/octopus_energy_api/octopus_energy_api.py +++ b/octopus_energy_api/octopus_energy_api.py @@ -19,11 +19,16 @@ def __init__(self, account_number, api_key, mpan=None, serial_number=None): self.account = account(account_details) self.properties = [] for property in self.account.properties: + p = {} meters_points = [] + for k, v in property.items(): + if "meter_points" not in k: + print(k) for elec_meter_point in property["electricity_meter_points"]: - mp = meter_point(self._urls, self._api, elec_meter_point) + mp = meter_point(self, elec_meter_point) meters_points.append(mp) - self.properties.append(meters_points) + p["meters"] = meters_points + self.properties.append(p) def account_details(self): """See account data""" @@ -34,6 +39,16 @@ def account_details(self): return response + def discover_meter(self, meter): + + url = self._urls.meter_discovery_url(meter.mpan, meter.serial_number) + start = self._api.run(url) + + url = self._urls.meter_discovery_url(meter.mpan, meter.serial_number, "-period") + end = self._api.run(url) + + return [start, end] + def products(self): """Get all product info for Octopus Energy""" @@ -48,6 +63,13 @@ def convert_datetime_to_tz(cls, time): return time.strftime(format_tz) + @classmethod + def convert_to_datetime(cls, time): + + format_tz = "%Y-%m-%dT%H:%M:%S%z" + + return datetime.strptime(time, format_tz) + def consumption(self, start: datetime, end: datetime): """Get all consumption data between 2 datetimes""" diff --git a/pyproject.toml b/pyproject.toml index f37f4ef..62f7fa2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "octopus-energy-api" -version = "0.8.1" +version = "0.8.2a0" description = "Wrapper for communicating with the Octopus Energy API" authors = ["Euan Campbell ","Chris Jones "] license = "MIT" From 0c66bb7034acd743c695c2107d72c601f386cddd Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 29 Jul 2023 21:46:28 +0100 Subject: [PATCH 05/17] Populate the meter object with all useful discoveries --- octopus_energy_api/meter.py | 19 ++++++++++++++++++- octopus_energy_api/octopus_energy_api.py | 9 +++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/octopus_energy_api/meter.py b/octopus_energy_api/meter.py index b352fe8..309d636 100644 --- a/octopus_energy_api/meter.py +++ b/octopus_energy_api/meter.py @@ -5,4 +5,21 @@ def __init__(self, api, mpan, meter_data): self.meter_data = meter_data for k, v in meter_data.items(): setattr(self, k, v) - print(self._api.discover_meter(self)) + foo = self._api.discover_meter(self) + self.start = foo[0] + self.end = foo[1] + self.count = foo[2] + + def __str__(self): + return ( + "MPAN: " + + self.mpan + + " / Serial: " + + self.serial_number + + " / DataPoints: " + + str(self.count) + + " / From: " + + self.start + + " / End: " + + self.end + ) diff --git a/octopus_energy_api/octopus_energy_api.py b/octopus_energy_api/octopus_energy_api.py index 92b159b..e3eaada 100644 --- a/octopus_energy_api/octopus_energy_api.py +++ b/octopus_energy_api/octopus_energy_api.py @@ -42,12 +42,13 @@ def account_details(self): def discover_meter(self, meter): url = self._urls.meter_discovery_url(meter.mpan, meter.serial_number) - start = self._api.run(url) + start = self._api.run(url)["results"][0]["interval_start"] url = self._urls.meter_discovery_url(meter.mpan, meter.serial_number, "-period") - end = self._api.run(url) - - return [start, end] + foo = self._api.run(url) + end = foo["results"][0]["interval_end"] + count = foo["count"] + return [start, end, count] def products(self): """Get all product info for Octopus Energy""" From d2ded3c832b0043b6e15c0822ef6c54b4731af09 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Mon, 25 Dec 2023 17:46:28 +0000 Subject: [PATCH 06/17] deal with meters that have no consumption --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 19b6a9c..adfe83e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: rev: 22.10.0 hooks: - id: black - language_version: python3.10 + language_version: python3.12 args: [--line-length=100] - repo: https://github.com/pycqa/flake8 rev: '5.0.4' From 7d553255be20d06f4651ef8b2453228015ad829e Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Mon, 25 Dec 2023 17:47:10 +0000 Subject: [PATCH 07/17] deal with meters that have no consumption --- octopus_energy_api/meter.py | 39 ++++++++++++++---------- octopus_energy_api/octopus_energy_api.py | 7 +++-- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/octopus_energy_api/meter.py b/octopus_energy_api/meter.py index 309d636..968ccdd 100644 --- a/octopus_energy_api/meter.py +++ b/octopus_energy_api/meter.py @@ -5,21 +5,28 @@ def __init__(self, api, mpan, meter_data): self.meter_data = meter_data for k, v in meter_data.items(): setattr(self, k, v) - foo = self._api.discover_meter(self) - self.start = foo[0] - self.end = foo[1] - self.count = foo[2] + data = self._api.discover_meter(self) + if data: + self.data = True + self.start = data[0] + self.end = data[1] + self.count = data[2] + else: + self.data = False def __str__(self): - return ( - "MPAN: " - + self.mpan - + " / Serial: " - + self.serial_number - + " / DataPoints: " - + str(self.count) - + " / From: " - + self.start - + " / End: " - + self.end - ) + if self.data: + return ( + "MPAN: " + + self.mpan + + " / Serial: " + + self.serial_number + + " / DataPoints: " + + str(self.count) + + " / From: " + + self.start + + " / End: " + + self.end + ) + else: + return "MPAN: " + self.mpan + " / Serial: " + self.serial_number diff --git a/octopus_energy_api/octopus_energy_api.py b/octopus_energy_api/octopus_energy_api.py index e3eaada..05511d4 100644 --- a/octopus_energy_api/octopus_energy_api.py +++ b/octopus_energy_api/octopus_energy_api.py @@ -40,10 +40,11 @@ def account_details(self): return response def discover_meter(self, meter): - url = self._urls.meter_discovery_url(meter.mpan, meter.serial_number) - start = self._api.run(url)["results"][0]["interval_start"] - + answer = self._api.run(url)["results"] + if len(answer) == 0: + return False + start = answer[0]["interval_start"] url = self._urls.meter_discovery_url(meter.mpan, meter.serial_number, "-period") foo = self._api.run(url) end = foo["results"][0]["interval_end"] From d9c6dddc8a10df9ac1798df36f0f18f711b56ea0 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 26 Dec 2023 17:26:32 +0000 Subject: [PATCH 08/17] retrieve consumption for a meter as a dataframe --- octopus_energy_api/meter.py | 26 ++++++++++++++++++++++---- octopus_energy_api/urls.py | 2 +- pyproject.toml | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/octopus_energy_api/meter.py b/octopus_energy_api/meter.py index 968ccdd..f6657ea 100644 --- a/octopus_energy_api/meter.py +++ b/octopus_energy_api/meter.py @@ -1,3 +1,7 @@ +from datetime import datetime +import pandas as pd + + class meter: def __init__(self, api, mpan, meter_data): self._api = api @@ -7,15 +11,16 @@ def __init__(self, api, mpan, meter_data): setattr(self, k, v) data = self._api.discover_meter(self) if data: - self.data = True + self.hasData = True self.start = data[0] self.end = data[1] self.count = data[2] else: - self.data = False + self.hasData = False + print(self.printMeter()) - def __str__(self): - if self.data: + def printMeter(self): + if self.hasData: return ( "MPAN: " + self.mpan @@ -30,3 +35,16 @@ def __str__(self): ) else: return "MPAN: " + self.mpan + " / Serial: " + self.serial_number + + def consumption(self, start: datetime, end: datetime): + """Get all consumption data between 2 datetimes""" + start = start.isoformat().replace("+00:00", "Z") + end = end.isoformat().replace("+00:00", "Z") + + url = self._api._urls().consumption_url(self.mpan, self.serial_number, start, end) + + response = self._api._api.run(url) + if "results" in response: + return pd.DataFrame(response["results"]) + else: + raise Exception(response) diff --git a/octopus_energy_api/urls.py b/octopus_energy_api/urls.py index 0865f79..51ea137 100644 --- a/octopus_energy_api/urls.py +++ b/octopus_energy_api/urls.py @@ -24,7 +24,7 @@ def products_url(cls): @classmethod def consumption_url(cls, mpan, serial, start, end, page_size=25000): - setup = f"/v1/electricity-meter-points/{mpan}/meters/{serial}/consumption" + setup = f"/v1/electricity-meter-points/{mpan}/meters/{serial}/consumption/" params = f"?page_size={page_size}&period_from={start}&period_to={end}&order_by=period" url = cls.build_url(setup, params=params) diff --git a/pyproject.toml b/pyproject.toml index 62f7fa2..3e6c0fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ packages = [{include = "octopus_energy_api"}] python = "^3.11" requests = "^2.28.2" pytest-mock = "^3.6.1" +pandas = "^2.1.4" [build-system] requires = ["poetry-core"] From e950dacb8f04a5a25f0895a0e6f096077aae6cc4 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 26 Dec 2023 17:58:16 +0000 Subject: [PATCH 09/17] always use UTC when talking to the API because thats easier than figuring out what format it wants for timezones --- octopus_energy_api/meter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/octopus_energy_api/meter.py b/octopus_energy_api/meter.py index f6657ea..2c671df 100644 --- a/octopus_energy_api/meter.py +++ b/octopus_energy_api/meter.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone import pandas as pd @@ -38,8 +38,8 @@ def printMeter(self): def consumption(self, start: datetime, end: datetime): """Get all consumption data between 2 datetimes""" - start = start.isoformat().replace("+00:00", "Z") - end = end.isoformat().replace("+00:00", "Z") + start = start.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + end = end.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") url = self._api._urls().consumption_url(self.mpan, self.serial_number, start, end) From 56a106c0f37e8ac27c1e312635742ccdd6bc3860 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 26 Dec 2023 21:14:16 +0000 Subject: [PATCH 10/17] fetch all pages of consumption --- octopus_energy_api/meter.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/octopus_energy_api/meter.py b/octopus_energy_api/meter.py index 2c671df..66f73b5 100644 --- a/octopus_energy_api/meter.py +++ b/octopus_energy_api/meter.py @@ -1,5 +1,6 @@ from datetime import datetime, timezone import pandas as pd +import time class meter: @@ -40,11 +41,20 @@ def consumption(self, start: datetime, end: datetime): """Get all consumption data between 2 datetimes""" start = start.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") end = end.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") - url = self._api._urls().consumption_url(self.mpan, self.serial_number, start, end) + return self._consumptionFetcher(url) + def _consumptionFetcher(self, url): + """Recursive function to fetch all pages of consumption""" response = self._api._api.run(url) if "results" in response: - return pd.DataFrame(response["results"]) + results = pd.DataFrame(response["results"]) else: raise Exception(response) + if response["next"]: + # be kind to the API + time.sleep(5) + nextResults = self._consumptionFetcher(response["next"]) + return pd.concat([results, nextResults], ignore_index=True) + else: + return results From e83663d3992b36661d6c63e73732a2bd75404963 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 29 Dec 2023 15:45:08 +0000 Subject: [PATCH 11/17] add a skeleton tariff class --- octopus_energy_api/tariff.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 octopus_energy_api/tariff.py diff --git a/octopus_energy_api/tariff.py b/octopus_energy_api/tariff.py new file mode 100644 index 0000000..cf5a172 --- /dev/null +++ b/octopus_energy_api/tariff.py @@ -0,0 +1,19 @@ +class tarrif: + def __init__(self, api, tariff_code, fromDT, toDt): + self._api = api + self.fuel = tariff_code[0] + self.registers = tariff_code[2] + self.productCode = tariff_code[5:-2] + self.GSPGroup = tariff_code[-1] + + def __str__(self): + return ( + "Fuel - " + + self.fuel + + ", Registers - " + + self.registers + + ", Product Code - " + + self.productCode + + ", GSP Group - " + + self.GSPGroup + ) From c87b120ac9f769b7296a88b6ef5e5c008c6f7f50 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 29 Dec 2023 17:13:51 +0000 Subject: [PATCH 12/17] move multipage fetcher into the api --- octopus_energy_api/api_interface.py | 24 +++++++++++++++++------- octopus_energy_api/meter.py | 19 +------------------ octopus_energy_api/urls.py | 11 +++++++++-- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/octopus_energy_api/api_interface.py b/octopus_energy_api/api_interface.py index 9bbd10b..550a50c 100644 --- a/octopus_energy_api/api_interface.py +++ b/octopus_energy_api/api_interface.py @@ -1,4 +1,6 @@ import requests +import pandas as pd +import time class api: @@ -6,19 +8,27 @@ def __init__(self, api_key): self._api_key = api_key def create_session(self): - session = requests.session() - session.auth = (self._api_key, "") - return session def run(self, url): - session = self.create_session() - response = session.request(method="GET", url=url) - parsed = response.json() - return parsed + + def pageFetcher(self, url): + """Recursive function to fetch all pages of results""" + response = self.run(url) + if "results" in response: + results = pd.DataFrame(response["results"]) + else: + raise Exception(response) + if response["next"]: + # be kind to the API + time.sleep(5) + nextResults = self.pageFetcher(response["next"]) + return pd.concat([results, nextResults], ignore_index=True) + else: + return results diff --git a/octopus_energy_api/meter.py b/octopus_energy_api/meter.py index 66f73b5..81205ba 100644 --- a/octopus_energy_api/meter.py +++ b/octopus_energy_api/meter.py @@ -1,6 +1,4 @@ from datetime import datetime, timezone -import pandas as pd -import time class meter: @@ -42,19 +40,4 @@ def consumption(self, start: datetime, end: datetime): start = start.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") end = end.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") url = self._api._urls().consumption_url(self.mpan, self.serial_number, start, end) - return self._consumptionFetcher(url) - - def _consumptionFetcher(self, url): - """Recursive function to fetch all pages of consumption""" - response = self._api._api.run(url) - if "results" in response: - results = pd.DataFrame(response["results"]) - else: - raise Exception(response) - if response["next"]: - # be kind to the API - time.sleep(5) - nextResults = self._consumptionFetcher(response["next"]) - return pd.concat([results, nextResults], ignore_index=True) - else: - return results + return self._api._api.pageFetcher(url) diff --git a/octopus_energy_api/urls.py b/octopus_energy_api/urls.py index 51ea137..61ce951 100644 --- a/octopus_energy_api/urls.py +++ b/octopus_energy_api/urls.py @@ -15,10 +15,17 @@ def accounts_url(cls, account_number: str): return url @classmethod - def products_url(cls): + def products_url(cls, productCode: str): + setup = f"/v1/products/{productCode}" - url = cls.build_url("/v1/products/?brand=OCTOPUS_ENERGY") + url = cls.build_url(setup) + + return url + @classmethod + def tariff_url(cls, productCode: str, tariffCode: str): + setup = f"/v1/products/{productCode}/electricity-tariffs/{tariffCode}/standard-unit-rates/" + url = cls.build_url(setup) return url @classmethod From f5aeaf7aa9ccbcce74c28e1bdf7b2650e691254d Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 30 Dec 2023 20:40:53 +0000 Subject: [PATCH 13/17] real tariff lookup function --- octopus_energy_api/tariff.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/octopus_energy_api/tariff.py b/octopus_energy_api/tariff.py index cf5a172..8e8cc3a 100644 --- a/octopus_energy_api/tariff.py +++ b/octopus_energy_api/tariff.py @@ -1,3 +1,6 @@ +from datetime import datetime, timezone + + class tarrif: def __init__(self, api, tariff_code, fromDT, toDt): self._api = api @@ -17,3 +20,15 @@ def __str__(self): + ", GSP Group - " + self.GSPGroup ) + + def tariffCode(self): + return self.fuel + "-" + self.registers + "R-" + self.productCode + "-" + self.GSPGroup + + def lookup(self, start: datetime, end: datetime): + if self.fuel == "E" and int(self.registers) == 1: + start = start.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + end = end.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + url = self._api._urls().tariff_url(self.productCode, self.tariffCode(), start, end) + return self._api._api.pageFetcher(url) + else: + print("Only single register electric meters implemented") From 5b90c54a8853f178d32889299b4bdc56c555a653 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 30 Dec 2023 20:41:29 +0000 Subject: [PATCH 14/17] real tariff lookup function --- octopus_energy_api/urls.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/octopus_energy_api/urls.py b/octopus_energy_api/urls.py index 61ce951..853b624 100644 --- a/octopus_energy_api/urls.py +++ b/octopus_energy_api/urls.py @@ -23,9 +23,10 @@ def products_url(cls, productCode: str): return url @classmethod - def tariff_url(cls, productCode: str, tariffCode: str): + def tariff_url(cls, productCode: str, tariffCode: str, start, end, page_size=1500): setup = f"/v1/products/{productCode}/electricity-tariffs/{tariffCode}/standard-unit-rates/" - url = cls.build_url(setup) + params = f"?page_size={page_size}&period_from={start}&period_to={end}&order_by=period" + url = cls.build_url(setup, params=params) return url @classmethod From 35c4423d0020b7d5686308a42af98935ac241d85 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sun, 31 Dec 2023 10:42:23 +0000 Subject: [PATCH 15/17] Use sane default dates when looking up tarrif details --- octopus_energy_api/tariff.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/octopus_energy_api/tariff.py b/octopus_energy_api/tariff.py index 8e8cc3a..70d5d81 100644 --- a/octopus_energy_api/tariff.py +++ b/octopus_energy_api/tariff.py @@ -2,12 +2,17 @@ class tarrif: - def __init__(self, api, tariff_code, fromDT, toDt): + def __init__(self, api, tariff_code, fromDT, toDT): self._api = api self.fuel = tariff_code[0] self.registers = tariff_code[2] self.productCode = tariff_code[5:-2] self.GSPGroup = tariff_code[-1] + self.fromDT = datetime.fromisoformat(fromDT) + if toDT: + self.toDT = datetime.fromisoformat(toDT) + else: + self.toDT = datetime.now(timezone.utc) def __str__(self): return ( @@ -24,7 +29,11 @@ def __str__(self): def tariffCode(self): return self.fuel + "-" + self.registers + "R-" + self.productCode + "-" + self.GSPGroup - def lookup(self, start: datetime, end: datetime): + def lookup(self, start: datetime = None, end: datetime = None): + if not start: + start = self.fromDT + if not end: + end = self.toDT if self.fuel == "E" and int(self.registers) == 1: start = start.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") end = end.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") From 72c94966061754a29ddb4521c817aaa4f94b8b2b Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Mon, 1 Jan 2024 22:55:06 +0000 Subject: [PATCH 16/17] Trim the start end end dates of the tariff dataframe to match our join/leave dates --- octopus_energy_api/tariff.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/octopus_energy_api/tariff.py b/octopus_energy_api/tariff.py index 70d5d81..ee2b8ae 100644 --- a/octopus_energy_api/tariff.py +++ b/octopus_energy_api/tariff.py @@ -1,4 +1,5 @@ from datetime import datetime, timezone +import pandas as pd class tarrif: @@ -38,6 +39,22 @@ def lookup(self, start: datetime = None, end: datetime = None): start = start.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") end = end.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") url = self._api._urls().tariff_url(self.productCode, self.tariffCode(), start, end) - return self._api._api.pageFetcher(url) + dfPrice = self._api._api.pageFetcher(url) + dfPrice["valid_from"] = pd.to_datetime(dfPrice["valid_from"], utc=True) + dfPrice["valid_to"] = pd.to_datetime(dfPrice["valid_to"], utc=True) + # trim the end date to when we joined the tarrif + setend = {} + setend["valid_to"] = self.toDT + df2 = pd.DataFrame([setend]) + dfPrice.update(df2) + # turn it upside down + dfPrice = dfPrice.sort_values("valid_from").reset_index(drop=True) + # trim the start date to when we joined the tarrif + setstart = {} + setstart["valid_from"] = self.fromDT + df2 = pd.DataFrame([setstart]) + dfPrice.update(df2) + + return dfPrice else: print("Only single register electric meters implemented") From 1a71e90f514a77bbd667680dc68d14f14f07a50a Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 8 Mar 2025 21:18:13 +0000 Subject: [PATCH 17/17] API behavior has changed, we now need to specify a 'period_from' else we only get the last weeks data --- octopus_energy_api/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octopus_energy_api/urls.py b/octopus_energy_api/urls.py index 853b624..04a59e3 100644 --- a/octopus_energy_api/urls.py +++ b/octopus_energy_api/urls.py @@ -43,7 +43,7 @@ def consumption_url(cls, mpan, serial, start, end, page_size=25000): def meter_discovery_url(cls, mpan, serial, order_by="period"): setup = f"/v1/electricity-meter-points/{mpan}/meters/{serial}/consumption/" - params = f"?page_size=1&order_by={order_by}" + params = f"?period_from=2015-08-01T00:00:00Z&page_size=1&order_by={order_by}" url = cls.build_url(setup, params=params)