Skip to content

Commit

Permalink
fix(sensor): Fixed handling of tracker products
Browse files Browse the repository at this point in the history
There have been reports of tracker products that work with the standard APIs and some that don't so we'll favour the standard APIs
  • Loading branch information
BottlecapDave committed Mar 2, 2023
1 parent 2e2d795 commit 2911ebb
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 25 deletions.
47 changes: 25 additions & 22 deletions custom_components/octopus_energy/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ async def async_get_electricity_standard_rates(self, product_code, tariff_code,
try:
data = await self.__async_read_response(response, url)
if data == None:
return None
return await self.__async_get_tracker_rates__(tariff_code, period_from, period_to, self._electricity_price_cap)

results = rates_to_thirty_minute_increments(data, period_from, period_to, tariff_code, self._electricity_price_cap)
except:
_LOGGER.error(f'Failed to extract standard rates: {url}')
Expand All @@ -264,7 +265,7 @@ async def async_get_electricity_day_night_rates(self, product_code, tariff_code,
try:
data = await self.__async_read_response(response, url)
if data == None:
return None
return await self.__async_get_tracker_rates__(tariff_code, period_from, period_to, self._electricity_price_cap)

# Normalise the rates to be in 30 minute increments and remove any rates that fall outside of our day period
day_rates = rates_to_thirty_minute_increments(data, period_from, period_to, tariff_code, self._electricity_price_cap)
Expand Down Expand Up @@ -303,7 +304,7 @@ async def async_get_electricity_rates(self, tariff_code, is_smart_meter, period_
tariff_parts = get_tariff_parts(tariff_code)
product_code = tariff_parts["product_code"]

if (await self.__async_is_tracker_tariff(tariff_code)):
if (self.__async_is_tracker_tariff(tariff_code)):
return await self.__async_get_tracker_rates__(tariff_code, period_from, period_to, self._electricity_price_cap)
elif (tariff_parts["rate"].startswith("1")):
return await self.async_get_electricity_standard_rates(product_code, tariff_code, period_from, period_to)
Expand Down Expand Up @@ -339,7 +340,7 @@ async def async_get_gas_rates(self, tariff_code, period_from, period_to):
tariff_parts = get_tariff_parts(tariff_code)
product_code = tariff_parts["product_code"]

if (await self.__async_is_tracker_tariff(tariff_code)):
if (self.__async_is_tracker_tariff(tariff_code)):
return await self.__async_get_tracker_rates__(tariff_code, period_from, period_to, self._gas_price_cap)

results = []
Expand All @@ -350,7 +351,7 @@ async def async_get_gas_rates(self, tariff_code, period_from, period_to):
try:
data = await self.__async_read_response(response, url)
if data == None:
return None
return await self.__async_get_tracker_rates__(tariff_code, period_from, period_to, self._gas_price_cap)

results = rates_to_thirty_minute_increments(data, period_from, period_to, tariff_code, self._gas_price_cap)
except:
Expand Down Expand Up @@ -399,7 +400,7 @@ async def async_get_electricity_standing_charge(self, tariff_code, period_from,
tariff_parts = get_tariff_parts(tariff_code)
product_code = tariff_parts["product_code"]

if await self.__async_is_tracker_tariff(tariff_code):
if self.__async_is_tracker_tariff(tariff_code):
return await self.__async_get_tracker_standing_charge__(tariff_code, period_from, period_to)

result = None
Expand All @@ -409,7 +410,10 @@ async def async_get_electricity_standing_charge(self, tariff_code, period_from,
async with client.get(url, auth=auth) as response:
try:
data = await self.__async_read_response(response, url)
if (data != None and "results" in data and len(data["results"]) > 0):
if data is None:
return await self.__async_get_tracker_standing_charge__(tariff_code, period_from, period_to)

if ("results" in data and len(data["results"]) > 0):
result = {
"value_inc_vat": float(data["results"][0]["value_inc_vat"])
}
Expand All @@ -424,7 +428,7 @@ async def async_get_gas_standing_charge(self, tariff_code, period_from, period_t
tariff_parts = get_tariff_parts(tariff_code)
product_code = tariff_parts["product_code"]

if await self.__async_is_tracker_tariff(tariff_code):
if self.__async_is_tracker_tariff(tariff_code):
return await self.__async_get_tracker_standing_charge__(tariff_code, period_from, period_to)

result = None
Expand All @@ -434,7 +438,10 @@ async def async_get_gas_standing_charge(self, tariff_code, period_from, period_t
async with client.get(url, auth=auth) as response:
try:
data = await self.__async_read_response(response, url)
if (data != None and "results" in data and len(data["results"]) > 0):
if data is None:
return await self.__async_get_tracker_standing_charge__(tariff_code, period_from, period_to)

if ("results" in data and len(data["results"]) > 0):
result = {
"value_inc_vat": float(data["results"][0]["value_inc_vat"])
}
Expand All @@ -444,27 +451,23 @@ async def async_get_gas_standing_charge(self, tariff_code, period_from, period_t

return result

async def __async_is_tracker_tariff(self, tariff_code):
def __async_is_tracker_tariff(self, tariff_code):
tariff_parts = get_tariff_parts(tariff_code)
product_code = tariff_parts["product_code"]

if product_code in self._product_tracker_cache:
return self._product_tracker_cache[product_code]

async with aiohttp.ClientSession() as client:
auth = aiohttp.BasicAuth(self._api_key, '')
url = f'https://api.octopus.energy/v1/products/{product_code}'
async with client.get(url, auth=auth) as response:
data = await self.__async_read_response(response, url)
if data == None:
return False

is_tracker = "is_tracker" in data and data["is_tracker"]
self._product_tracker_cache[product_code] = is_tracker
return is_tracker
return False

async def __async_get_tracker_rates__(self, tariff_code, period_from, period_to, price_cap: float = None):
"""Get the tracker rates"""
tariff_parts = get_tariff_parts(tariff_code)
product_code = tariff_parts["product_code"]

# If we know our tariff is not a tracker rate, then don't bother asking
if product_code in self._product_tracker_cache and self._product_tracker_cache[product_code] == False:
return None

results = []
async with aiohttp.ClientSession() as client:
Expand All @@ -480,7 +483,6 @@ async def __async_get_tracker_rates__(self, tariff_code, period_from, period_to,
for period in data["periods"]:
valid_from = parse_datetime(f'{period["date"]}T00:00:00Z')
valid_to = parse_datetime(f'{period["date"]}T00:00:00Z') + timedelta(days=1)
vat = float(period["breakdown"]["standing_charge"]["VAT"])

if ((valid_from >= period_from and valid_from <= period_to) or (valid_to >= period_from and valid_to <= period_to)):
items.append(
Expand All @@ -492,6 +494,7 @@ async def __async_get_tracker_rates__(self, tariff_code, period_from, period_to,
)

results = rates_to_thirty_minute_increments({ "results": items }, period_from, period_to, tariff_code, price_cap)
self._product_tracker_cache[product_code] = True
except:
_LOGGER.error(f'Failed to extract tracker gas rates: {url}')
raise
Expand Down
13 changes: 10 additions & 3 deletions tests/integration/api_client/test_get_gas_rates.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ async def test_when_get_gas_rates_is_called_for_existent_tariff_then_rates_are_r
expected_valid_from = expected_valid_to

@pytest.mark.asyncio
@pytest.mark.parametrize("tariff,price_cap",[("G-1R-SILVER-FLEX-22-11-25-C", None),("G-1R-SILVER-FLEX-22-11-25-C", 2)])
async def test_when_get_gas_rates_is_called_with_tracker_tariff_then_rates_are_returned(tariff, price_cap):
@pytest.mark.parametrize("tariff,price_cap,is_tracker",[
("G-1R-SILVER-FLEX-22-11-25-C", None, True),
("G-1R-SILVER-FLEX-22-11-25-C", 2, True),
("G-1R-SILVER-2017-1-H", None, False),
("G-1R-SILVER-2017-1-H", 2, False)
])
async def test_when_get_gas_rates_is_called_with_tracker_tariff_then_rates_are_returned(tariff, price_cap, is_tracker):
# Arrange
context = get_test_context()
period_from = now().replace(hour=0, minute=0, second=0, microsecond=0)
Expand Down Expand Up @@ -72,8 +77,10 @@ async def test_when_get_gas_rates_is_called_with_tracker_tariff_then_rates_are_r
assert "value_inc_vat" in item
if (price_cap is not None and expected_tracker["unit_rate"] > price_cap):
assert item["value_inc_vat"] == price_cap
else:
elif (is_tracker):
assert item["value_inc_vat"] == expected_tracker["unit_rate"]
else:
assert item["value_inc_vat"] > 0

expected_valid_from = expected_valid_to

Expand Down

0 comments on commit 2911ebb

Please sign in to comment.