Skip to content

Commit

Permalink
Added test skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
Pluimvee committed Oct 9, 2024
1 parent 6010ab4 commit 58e4ccd
Show file tree
Hide file tree
Showing 5 changed files with 656 additions and 60 deletions.
122 changes: 62 additions & 60 deletions custom_components/entsoe/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,66 +70,7 @@ def query_day_ahead_prices(

if response.status_code == 200:
try:
root = self._remove_namespace(ET.fromstring(response.content))
_LOGGER.debug(f"content: {root}")
series = {}

# Just pick the first TimeSeries data (DE casus in which multiple alternative answers are given)
# We could/should verify if the periods in the timeseries really overlap and serve as alternative response
# and we could/should find the most suitable timeseries instead of just the first
timeseries = root.find(".//TimeSeries")

# for all periods in this timeseries.....-> we still asume the time intervals do not overlap, and are in sequence
for period in timeseries.findall(".//Period"):
# there can be different resolutions for each period (BE casus in which historical is quarterly and future is hourly)
resolution = period.find(".//resolution").text

# for now supporting 60 and 15 minutes resolutions (ISO8601 defined)
if resolution == "PT60M" or resolution == "PT1H":
resolution = "PT60M"
elif resolution != "PT15M":
continue

response_start = period.find(".//timeInterval/start").text
start_time = (
datetime.strptime(response_start, "%Y-%m-%dT%H:%MZ")
.replace(tzinfo=pytz.UTC)
.astimezone()
)
start_time.replace(minute=0) # ensure we start from the whole hour

response_end = period.find(".//timeInterval/end").text
end_time = (
datetime.strptime(response_end, "%Y-%m-%dT%H:%MZ")
.replace(tzinfo=pytz.UTC)
.astimezone()
)
_LOGGER.debug(
f"Period found is from {start_time} till {end_time} with resolution {resolution}"
)
if resolution == "PT60M":
series.update(self.process_PT60M_points(period, start_time))
else:
series.update(self.process_PT15M_points(period, start_time))

# Now fill in any missing hours
current_time = start_time
last_price = series[current_time]

while current_time < end_time: # upto excluding! the endtime
if current_time in series:
last_price = series[
current_time
] # Update to the current price
else:
_LOGGER.debug(
f"Extending the price {last_price} of the previous hour to {current_time}"
)
series[current_time] = (
last_price # Fill with the last known price
)
current_time += timedelta(hours=1)

series = self.parse_price_document(response.content)
return dict(sorted(series.items()))

except Exception as exc:
Expand All @@ -139,6 +80,67 @@ def query_day_ahead_prices(
print(f"Failed to retrieve data: {response.status_code}")
return None

# lets process the received document
def parse_price_document(self, document: str) -> str:

root = self._remove_namespace(ET.fromstring(response.content))
_LOGGER.debug(f"content: {root}")
series = {}

# Just pick the first TimeSeries data (DE casus in which multiple alternative answers are given)
# We could/should verify if the periods in the timeseries really overlap and serve as alternative response
# and we could/should find the most suitable timeseries instead of just the first
timeseries = root.find(".//TimeSeries")

# for all periods in this timeseries.....-> we still asume the time intervals do not overlap, and are in sequence
for period in timeseries.findall(".//Period"):
# there can be different resolutions for each period (BE casus in which historical is quarterly and future is hourly)
resolution = period.find(".//resolution").text

# for now supporting 60 and 15 minutes resolutions (ISO8601 defined)
if resolution == "PT60M" or resolution == "PT1H":
resolution = "PT60M"
elif resolution != "PT15M":
continue

response_start = period.find(".//timeInterval/start").text
start_time = (
datetime.strptime(response_start, "%Y-%m-%dT%H:%MZ")
.replace(tzinfo=pytz.UTC)
.astimezone()
)
start_time.replace(minute=0) # ensure we start from the whole hour

response_end = period.find(".//timeInterval/end").text
end_time = (
datetime.strptime(response_end, "%Y-%m-%dT%H:%MZ")
.replace(tzinfo=pytz.UTC)
.astimezone()
)
_LOGGER.debug(
f"Period found is from {start_time} till {end_time} with resolution {resolution}"
)
if resolution == "PT60M":
series.update(self.process_PT60M_points(period, start_time))
else:
series.update(self.process_PT15M_points(period, start_time))

# Now fill in any missing hours
current_time = start_time
last_price = series[current_time]

while current_time < end_time: # upto excluding! the endtime
if current_time in series:
last_price = series[current_time] # Update to the current price
else:
_LOGGER.debug(
f"Extending the price {last_price} of the previous hour to {current_time}"
)
series[current_time] = last_price # Fill with the last known price
current_time += timedelta(hours=1)

return series

# processing hourly prices info -> thats easy
def process_PT60M_points(self, period: Element, start_time: datetime):
data = {}
Expand Down
Empty file.
129 changes: 129 additions & 0 deletions custom_components/entsoe/test/datasets/BE_60M.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<Publication_MarketDocument xmlns="urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:3">
<mRID>c3891474d3014008b9c6036658010b88</mRID>
<revisionNumber>1</revisionNumber>
<type>A44</type>
<sender_MarketParticipant.mRID codingScheme="A01">10X1001A1001A450</sender_MarketParticipant.mRID>
<sender_MarketParticipant.marketRole.type>A32</sender_MarketParticipant.marketRole.type>
<receiver_MarketParticipant.mRID codingScheme="A01">10X1001A1001A450</receiver_MarketParticipant.mRID>
<receiver_MarketParticipant.marketRole.type>A33</receiver_MarketParticipant.marketRole.type>
<createdDateTime>2024-10-07T14:34:54Z</createdDateTime>
<period.timeInterval>
<start>2024-10-07T22:00Z</start>
<end>2024-10-08T22:00Z</end>
</period.timeInterval>
<TimeSeries>
<mRID>1</mRID>
<auction.type>A01</auction.type>
<businessType>A62</businessType>
<in_Domain.mRID codingScheme="A01">10YBE----------2</in_Domain.mRID>
<out_Domain.mRID codingScheme="A01">10YBE----------2</out_Domain.mRID>
<contract_MarketAgreement.type>A01</contract_MarketAgreement.type>
<currency_Unit.name>EUR</currency_Unit.name>
<price_Measure_Unit.name>MWH</price_Measure_Unit.name>
<curveType>A03</curveType>
<Period>
<timeInterval>
<start>2024-10-07T22:00Z</start>
<end>2024-10-08T22:00Z</end>
</timeInterval>
<resolution>PT60M</resolution>
<Point>
<position>1</position>
<price.amount>64.98</price.amount>
</Point>
<Point>
<position>2</position>
<price.amount>57.86</price.amount>
</Point>
<Point>
<position>3</position>
<price.amount>53.73</price.amount>
</Point>
<Point>
<position>4</position>
<price.amount>47.52</price.amount>
</Point>
<Point>
<position>5</position>
<price.amount>47.05</price.amount>
</Point>
<Point>
<position>6</position>
<price.amount>56.89</price.amount>
</Point>
<Point>
<position>7</position>
<price.amount>77.77</price.amount>
</Point>
<Point>
<position>8</position>
<price.amount>88.24</price.amount>
</Point>
<Point>
<position>9</position>
<price.amount>100</price.amount>
</Point>
<Point>
<position>10</position>
<price.amount>84.92</price.amount>
</Point>
<Point>
<position>11</position>
<price.amount>74.6</price.amount>
</Point>
<Point>
<position>12</position>
<price.amount>68.82</price.amount>
</Point>
<Point>
<position>13</position>
<price.amount>60.56</price.amount>
</Point>
<Point>
<position>14</position>
<price.amount>63.86</price.amount>
</Point>
<Point>
<position>15</position>
<price.amount>68.1</price.amount>
</Point>
<Point>
<position>16</position>
<price.amount>68.37</price.amount>
</Point>
<Point>
<position>17</position>
<price.amount>76.35</price.amount>
</Point>
<Point>
<position>18</position>
<price.amount>54.04</price.amount>
</Point>
<Point>
<position>19</position>
<price.amount>98.97</price.amount>
</Point>
<Point>
<position>20</position>
<price.amount>115.47</price.amount>
</Point>
<Point>
<position>21</position>
<price.amount>86.85</price.amount>
</Point>
<Point>
<position>22</position>
<price.amount>69.59</price.amount>
</Point>
<Point>
<position>23</position>
<price.amount>57.42</price.amount>
</Point>
<Point>
<position>24</position>
<price.amount>50</price.amount>
</Point>
</Period>
</TimeSeries>
</Publication_MarketDocument>
Loading

0 comments on commit 58e4ccd

Please sign in to comment.