diff --git a/custom_components/epex_spot/EPEXSpot/Awattar/__init__.py b/custom_components/epex_spot/EPEXSpot/Awattar/__init__.py index 514cae6..a63c858 100644 --- a/custom_components/epex_spot/EPEXSpot/Awattar/__init__.py +++ b/custom_components/epex_spot/EPEXSpot/Awattar/__init__.py @@ -1,27 +1,32 @@ -import logging +"""Awattar API.""" + from datetime import datetime, timedelta, timezone +import logging import aiohttp -from homeassistant.util import dt + +from homeassistant.util import dt as dt_util + +from ...const import EUR_PER_MWH, UOM_EUR_PER_KWH _LOGGER = logging.getLogger(__name__) class Marketprice: - UOM_EUR_PER_MWh = "EUR/MWh" + """Marketprice class for Awattar.""" def __init__(self, data): - assert data["unit"].lower() == self.UOM_EUR_PER_MWh.lower() + assert data["unit"].lower() == EUR_PER_MWH.lower() self._start_time = datetime.fromtimestamp( data["start_timestamp"] / 1000, tz=timezone.utc ) self._end_time = datetime.fromtimestamp( data["end_timestamp"] / 1000, tz=timezone.utc ) - self._price_eur_per_mwh = float(data["marketprice"]) + self._price_per_kwh = round(float(data["marketprice"]) / 1000.0, 6) def __repr__(self): - return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_eur_per_mwh} {self.UOM_EUR_PER_MWh})" # noqa: E501 + return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_per_kwh} {UOM_EUR_PER_KWH})" # noqa: E501 @property def start_time(self): @@ -32,12 +37,8 @@ def end_time(self): return self._end_time @property - def price_eur_per_mwh(self): - return self._price_eur_per_mwh - - @property - def price_ct_per_kwh(self): - return round(self._price_eur_per_mwh / 10, 3) + def price_per_kwh(self): + return self._price_per_kwh def toEpochMilliSec(dt: datetime) -> int: @@ -80,9 +81,9 @@ async def fetch(self): self._marketdata = self._extract_marketdata(data["data"]) async def _fetch_data(self, url): - start = dt.now().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta( - days=1 - ) + start = dt_util.now().replace( + hour=0, minute=0, second=0, microsecond=0 + ) - timedelta(days=1) end = start + timedelta(days=3) async with self._session.get( url, params={"start": toEpochMilliSec(start), "end": toEpochMilliSec(end)} diff --git a/custom_components/epex_spot/EPEXSpot/EPEXSpotWeb/__init__.py b/custom_components/epex_spot/EPEXSpot/EPEXSpotWeb/__init__.py index 5520ca4..9254b8a 100644 --- a/custom_components/epex_spot/EPEXSpot/EPEXSpotWeb/__init__.py +++ b/custom_components/epex_spot/EPEXSpot/EPEXSpotWeb/__init__.py @@ -1,10 +1,14 @@ -import logging +"""EPEX Spot Web Scraper.""" + from datetime import datetime, timedelta, timezone +import logging from zoneinfo import ZoneInfo import aiohttp from bs4 import BeautifulSoup +from ...const import UOM_EUR_PER_KWH, UOM_MWH + _LOGGER = logging.getLogger(__name__) @@ -24,8 +28,7 @@ def _as_date(v): class Marketprice: - UOM_EUR_PER_MWh = "EUR/MWh" - UOM_MWh = "MWh" + """Marketprice class for EPEX Spot Web.""" def __init__( self, start_time, end_time, buy_volume_mwh, sell_volume_mwh, volume_mwh, price @@ -35,10 +38,10 @@ def __init__( self._buy_volume_mwh = _to_float(buy_volume_mwh) self._sell_volume_mwh = _to_float(sell_volume_mwh) self._volume_mwh = _to_float(volume_mwh) - self._price_eur_per_mwh = _to_float(price) + self._price_per_kwh = round(_to_float(price) / 1000.0, 6) def __repr__(self): - return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, buy_volume_mwh: {self._buy_volume_mwh} {self.UOM_MWh}, sell_volume_mwh: {self._sell_volume_mwh} {self.UOM_MWh}, volume_mwh: {self._volume_mwh} {self.UOM_MWh}, marketprice: {self._price_eur_per_mwh} {self.UOM_EUR_PER_MWh})" # noqa: E501 + return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, buy_volume_mwh: {self._buy_volume_mwh} {UOM_MWH}, sell_volume_mwh: {self._sell_volume_mwh} {UOM_MWH}, volume_mwh: {self._volume_mwh} {UOM_MWH}, marketprice: {self._price_per_kwh} {UOM_EUR_PER_KWH})" # noqa: E501 @property def start_time(self): @@ -49,12 +52,8 @@ def end_time(self): return self._end_time @property - def price_eur_per_mwh(self): - return self._price_eur_per_mwh - - @property - def price_ct_per_kwh(self): - return round(self._price_eur_per_mwh / 10, 3) + def price_per_kwh(self): + return self._price_per_kwh @property def buy_volume_mwh(self): diff --git a/custom_components/epex_spot/EPEXSpot/SMARD/__init__.py b/custom_components/epex_spot/EPEXSpot/SMARD/__init__.py index 4efee96..7d9e293 100644 --- a/custom_components/epex_spot/EPEXSpot/SMARD/__init__.py +++ b/custom_components/epex_spot/EPEXSpot/SMARD/__init__.py @@ -1,8 +1,12 @@ -import logging +"""SMARD.de API.""" + from datetime import datetime, timedelta, timezone +import logging import aiohttp +from ...const import UOM_EUR_PER_KWH + # from homeassistant.util import dt _LOGGER = logging.getLogger(__name__) @@ -27,7 +31,7 @@ class Marketprice: - UOM_EUR_PER_MWh = "EUR/MWh" + """Marketprice class for SMARD.de.""" def __init__(self, data): self._start_time = datetime.fromtimestamp(data[0] / 1000, tz=timezone.utc) @@ -35,10 +39,10 @@ def __init__(self, data): hours=1 ) # TODO: this will not work for 1/2h updates - self._price_eur_per_mwh = float(data[1]) + self._price_per_kwh = round(float(data[1]) / 1000.0, 6) def __repr__(self): - return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_eur_per_mwh} {self.UOM_EUR_PER_MWh})" # noqa: E501 + return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_per_kwh} {UOM_EUR_PER_KWH})" # noqa: E501 @property def start_time(self): @@ -49,12 +53,8 @@ def end_time(self): return self._end_time @property - def price_eur_per_mwh(self): - return self._price_eur_per_mwh - - @property - def price_ct_per_kwh(self): - return round(self._price_eur_per_mwh / 10, 3) + def price_per_kwh(self): + return self._price_per_kwh class SMARD: @@ -119,7 +119,7 @@ async def fetch(self): # thats yesterday and today self._marketdata = entries[ -48: - ] # limit number of entries to protect HA recorder + ] # limit number of entries to protect HA recorder else: # latest data is tomorrow, return 72 entries # thats yesterday, today and tomorrow diff --git a/custom_components/epex_spot/EPEXSpot/Tibber/__init__.py b/custom_components/epex_spot/EPEXSpot/Tibber/__init__.py index adca501..7678c89 100644 --- a/custom_components/epex_spot/EPEXSpot/Tibber/__init__.py +++ b/custom_components/epex_spot/EPEXSpot/Tibber/__init__.py @@ -1,6 +1,11 @@ -import aiohttp +"""Tibber API.""" + from datetime import datetime, timedelta +import aiohttp + +from ...const import UOM_EUR_PER_KWH + TIBBER_QUERY = """ { viewer { @@ -30,17 +35,17 @@ class Marketprice: - UOM_CT_PER_kWh = "ct/kWh" + """Marketprice class for Tibber.""" def __init__(self, data): self._start_time = datetime.fromisoformat(data["startsAt"]) self._end_time = self._start_time + timedelta(hours=1) # Tibber already returns the actual net price for the customer # so we can use that - self._price_ct_per_kwh = round(float(data["total"]) * 100, 3) + self._price_per_kwh = round(float(data["total"]), 6) def __repr__(self): - return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_ct_per_kwh} {self.UOM_CT_PER_kWh})" # noqa: E501 + return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_per_kwh} {UOM_EUR_PER_KWH})" # noqa: E501 @property def start_time(self): @@ -51,12 +56,8 @@ def end_time(self): return self._end_time @property - def price_eur_per_mwh(self): - return round(self._price_ct_per_kwh * 10, 2) - - @property - def price_ct_per_kwh(self): - return self._price_ct_per_kwh + def price_per_kwh(self): + return self._price_per_kwh class Tibber: diff --git a/custom_components/epex_spot/EPEXSpot/smartENERGY/__init__.py b/custom_components/epex_spot/EPEXSpot/smartENERGY/__init__.py index 5e916e9..ed5bb76 100644 --- a/custom_components/epex_spot/EPEXSpot/smartENERGY/__init__.py +++ b/custom_components/epex_spot/EPEXSpot/smartENERGY/__init__.py @@ -1,22 +1,26 @@ +"""smartENERGY API.""" + from datetime import datetime, timedelta import logging import aiohttp +from ...const import CT_PER_KWH + _LOGGER = logging.getLogger(__name__) class Marketprice: - UOM_CT_PER_kWh = "ct/kWh" + """Marketprice class for smartENERGY.""" def __init__(self, duration, data): self._start_time = datetime.fromisoformat(data["date"]) self._end_time = self._start_time + timedelta(minutes=duration) # price includes austrian vat (20%) -> remove to be consistent with other data sources - self._price_ct_per_kwh = round(float(data["value"]) / 1.2, 3) + self._price_per_kwh = round(float(data["value"]) / 100.0 / 1.2, 6) def __repr__(self): - return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_ct_per_kwh} {self.UOM_CT_PER_kWh})" # noqa: E501 + return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_per_kwh} {CT_PER_KWH})" # noqa: E501 @property def start_time(self): @@ -30,12 +34,8 @@ def set_end_time(self, end_time): self._end_time = end_time @property - def price_eur_per_mwh(self): - return round(self._price_ct_per_kwh * 10, 2) - - @property - def price_ct_per_kwh(self): - return self._price_ct_per_kwh + def price_per_kwh(self): + return self._price_per_kwh class smartENERGY: @@ -46,7 +46,7 @@ class smartENERGY: def __init__(self, market_area, session: aiohttp.ClientSession): self._session = session self._market_area = market_area - self._duration = 15 # default value, can be overwritten by API response + self._duration = 15 # default value, can be overwritten by API response self._marketdata = [] @property @@ -72,7 +72,7 @@ def marketdata(self): async def fetch(self): data = await self._fetch_data(self.URL) self._duration = data["interval"] - assert data["unit"].lower() == Marketprice.UOM_CT_PER_kWh.lower() + assert data["unit"].lower() == CT_PER_KWH.lower() marketdata = self._extract_marketdata(data["data"]) # override duration and compress data self._duration = 60 @@ -96,12 +96,12 @@ def _compress_marketdata(self, data): if start == None: start = entry continue - is_price_equal = start.price_ct_per_kwh == entry.price_ct_per_kwh + is_price_equal = start.price_per_kwh == entry.price_per_kwh is_continuation = start.end_time == entry.start_time max_start_time = start.start_time + timedelta(minutes=self._duration) is_same_hour = entry.start_time < max_start_time - if (is_price_equal & is_continuation & is_same_hour): + if is_price_equal & is_continuation & is_same_hour: start.set_end_time(entry.end_time) else: entries.append(start) diff --git a/custom_components/epex_spot/SourceShell.py b/custom_components/epex_spot/SourceShell.py index 65f5b43..5a02027 100644 --- a/custom_components/epex_spot/SourceShell.py +++ b/custom_components/epex_spot/SourceShell.py @@ -1,17 +1,20 @@ -import aiohttp +"""SourceShell""" + from datetime import timedelta import logging from typing import Any +import aiohttp + from homeassistant.config_entries import ConfigEntry from homeassistant.util import dt from .const import ( CONF_DURATION, - CONF_EARLIEST_START_TIME, CONF_EARLIEST_START_POST, - CONF_LATEST_END_TIME, + CONF_EARLIEST_START_TIME, CONF_LATEST_END_POST, + CONF_LATEST_END_TIME, CONF_MARKET_AREA, CONF_SOURCE, CONF_SOURCE_AWATTAR, @@ -28,7 +31,7 @@ DEFAULT_TAX, EMPTY_EXTREME_PRICE_INTERVAL_RESP, ) -from .EPEXSpot import SMARD, Awattar, EPEXSpotWeb, smartENERGY, Tibber +from .EPEXSpot import SMARD, Awattar, EPEXSpotWeb, Tibber, smartENERGY from .extreme_price_interval import find_extreme_price_interval, get_start_times _LOGGER = logging.getLogger(__name__) @@ -131,12 +134,12 @@ def update_time(self): self.marketdata, ) sorted_sorted_marketdata_today = sorted( - sorted_marketdata_today, key=lambda e: e.price_eur_per_mwh + sorted_marketdata_today, key=lambda e: e.price_per_kwh ) self._sorted_marketdata_today = sorted_sorted_marketdata_today - def to_net_price(self, price_eur_per_mwh): - net_p = price_eur_per_mwh / 10 # convert from EUR/MWh to ct/kWh + def to_net_price(self, price_per_kwh): + net_p = price_per_kwh # Tibber already reaturns the net price for the customer if "Tibber API" not in self.name: @@ -151,7 +154,7 @@ def to_net_price(self, price_eur_per_mwh): net_p += surcharge_abs net_p *= 1 + (tax / 100) - return round(net_p, 3) + return round(net_p, 6) def find_extreme_price_interval(self, call_data, cmp): duration: timedelta = call_data[CONF_DURATION] @@ -173,10 +176,11 @@ def find_extreme_price_interval(self, call_data, cmp): if result is None: return EMPTY_EXTREME_PRICE_INTERVAL_RESP + _LOGGER.error(f"result: {result}") + return { "start": result["start"], "end": result["start"] + duration, - "price_eur_per_mwh": result["price_per_hour"], - "price_ct_per_kwh": round(result["price_per_hour"] / 10, 3), + "price_per_kwh": round(result["price_per_hour"] / 1000, 6), "net_price_ct_per_kwh": self.to_net_price(result["price_per_hour"]), } diff --git a/custom_components/epex_spot/const.py b/custom_components/epex_spot/const.py index c55137a..db9476a 100644 --- a/custom_components/epex_spot/const.py +++ b/custom_components/epex_spot/const.py @@ -11,10 +11,7 @@ ATTR_VOLUME_MWH = "volume_mwh" ATTR_RANK = "rank" ATTR_QUANTILE = "quantile" -ATTR_PRICE_EUR_PER_MWH = "price_eur_per_mwh" -ATTR_PRICE_CT_PER_KWH = "price_ct_per_kwh" -ATTR_PRICE_GBP_PER_MWH = "price_gbp_per_mwh" -ATTR_PRICE_PENCE_PER_KWH = "price_pence_per_kwh" +ATTR_PRICE_PER_KWH = "price_per_kwh" CONF_SOURCE = "source" CONF_MARKET_AREA = "market_area" @@ -40,13 +37,18 @@ CONF_DURATION = "duration" DEFAULT_SURCHARGE_PERC = 3.0 -DEFAULT_SURCHARGE_ABS = 11.93 +DEFAULT_SURCHARGE_ABS = 0.1193 DEFAULT_TAX = 19.0 EMPTY_EXTREME_PRICE_INTERVAL_RESP = { "start": None, "end": None, - "price_eur_per_mwh": None, - "price_ct_per_kwh": None, + "price_per_kwh": None, "net_price_ct_per_kwh": None, } + +UOM_EUR_PER_KWH = "€/kWh" +UOM_MWH = "MWh" + +EUR_PER_MWH = "EUR/MWh" +CT_PER_KWH = "ct/kWh" diff --git a/custom_components/epex_spot/extreme_price_interval.py b/custom_components/epex_spot/extreme_price_interval.py index dd02b06..caa21c4 100644 --- a/custom_components/epex_spot/extreme_price_interval.py +++ b/custom_components/epex_spot/extreme_price_interval.py @@ -30,14 +30,14 @@ def _calc_interval_price(marketdata, start_time: datetime, duration: timedelta): active_duration_in_this_segment = mp.end_time - start_time total_price += ( - mp.price_eur_per_mwh + mp.price_per_kwh * active_duration_in_this_segment.total_seconds() / SECONDS_PER_HOUR ) start_time = mp.end_time - return round(total_price, 2) + return round(total_price, 6) def _calc_start_times( @@ -91,7 +91,7 @@ def find_extreme_price_interval(marketdata, start_times, duration: timedelta, cm if interval_start_time is None: return None - interval_price = round(interval_price, 2) + interval_price = round(interval_price, 6) return { "start": dt_util.as_local(interval_start_time), diff --git a/custom_components/epex_spot/localization.py b/custom_components/epex_spot/localization.py index d25ef98..332008d 100644 --- a/custom_components/epex_spot/localization.py +++ b/custom_components/epex_spot/localization.py @@ -1,35 +1,24 @@ from dataclasses import dataclass -from .const import ( - ATTR_PRICE_CT_PER_KWH, - ATTR_PRICE_EUR_PER_MWH, - ATTR_PRICE_GBP_PER_MWH, - ATTR_PRICE_PENCE_PER_KWH, -) +from .const import ATTR_PRICE_PER_KWH @dataclass(frozen=True, slots=True) class Localize: - uom_per_mwh: str uom_per_kwh: str icon: str - attr_name_per_mwh: str attr_name_per_kwh: str CURRENCY_MAPPING = { "EUR": Localize( - uom_per_mwh="EUR/MWh", - uom_per_kwh="ct/kWh", + uom_per_kwh="€/kWh", icon="mdi:currency-eur", - attr_name_per_mwh=ATTR_PRICE_EUR_PER_MWH, - attr_name_per_kwh=ATTR_PRICE_CT_PER_KWH, + attr_name_per_kwh=ATTR_PRICE_PER_KWH, ), "GBP": Localize( - uom_per_mwh="GBP/MWh", - uom_per_kwh="pence/kWh", + uom_per_kwh="£/kWh", icon="mdi:currency-gbp", - attr_name_per_mwh=ATTR_PRICE_GBP_PER_MWH, - attr_name_per_kwh=ATTR_PRICE_PENCE_PER_KWH, + attr_name_per_kwh=ATTR_PRICE_PER_KWH, ), } diff --git a/custom_components/epex_spot/sensor.py b/custom_components/epex_spot/sensor.py index 4a41568..53d4c0a 100644 --- a/custom_components/epex_spot/sensor.py +++ b/custom_components/epex_spot/sensor.py @@ -57,10 +57,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(entities) -def to_ct_per_kwh(price_eur_per_mwh): - return round(price_eur_per_mwh / 10, 3) - - class EpexSpotPriceSensorEntity(EpexSpotEntity, SensorEntity): """Home Assistant sensor containing all EPEX spot data.""" @@ -73,11 +69,11 @@ class EpexSpotPriceSensorEntity(EpexSpotEntity, SensorEntity): def __init__(self, coordinator: DataUpdateCoordinator): super().__init__(coordinator, self.entity_description) self._attr_icon = self._localized.icon - self._attr_native_unit_of_measurement = self._localized.uom_per_mwh + self._attr_native_unit_of_measurement = self._localized.uom_per_kwh @property def native_value(self) -> StateType: - return self._source.marketdata_now.price_eur_per_mwh + return self._source.marketdata_now.price_per_kwh @property def extra_state_attributes(self): @@ -85,15 +81,14 @@ def extra_state_attributes(self): { ATTR_START_TIME: dt_util.as_local(e.start_time).isoformat(), ATTR_END_TIME: dt_util.as_local(e.end_time).isoformat(), - self._localized.attr_name_per_mwh: e.price_eur_per_mwh, - self._localized.attr_name_per_kwh: to_ct_per_kwh(e.price_eur_per_mwh), + self._localized.attr_name_per_kwh: e.price_per_kwh, } for e in self._source.marketdata ] return { ATTR_DATA: data, - self._localized.attr_name_per_kwh: to_ct_per_kwh(self.native_value), + self._localized.attr_name_per_kwh: self.native_value, } @@ -103,7 +98,7 @@ class EpexSpotNetPriceSensorEntity(EpexSpotEntity, SensorEntity): entity_description = SensorEntityDescription( key="Net Price", name="Net Price", - suggested_display_precision=2, + suggested_display_precision=6, state_class=SensorStateClass.MEASUREMENT, ) @@ -114,7 +109,7 @@ def __init__(self, coordinator: DataUpdateCoordinator): @property def native_value(self) -> StateType: - return self._source.to_net_price(self._source.marketdata_now.price_eur_per_mwh) + return self._source.to_net_price(self._source.marketdata_now.price_per_kwh) @property def extra_state_attributes(self): @@ -123,7 +118,7 @@ def extra_state_attributes(self): ATTR_START_TIME: dt_util.as_local(e.start_time).isoformat(), ATTR_END_TIME: dt_util.as_local(e.end_time).isoformat(), self._localized.attr_name_per_kwh: self._source.to_net_price( - e.price_eur_per_mwh + e.price_per_kwh ), } for e in self._source.marketdata @@ -244,20 +239,18 @@ def __init__(self, coordinator: DataUpdateCoordinator): @property def native_value(self) -> StateType: - return [ - e.price_eur_per_mwh for e in self._source.sorted_marketdata_today - ].index(self._source.marketdata_now.price_eur_per_mwh) + return [e.price_per_kwh for e in self._source.sorted_marketdata_today].index( + self._source.marketdata_now.price_per_kwh + ) @property def extra_state_attributes(self): - sorted_prices = [ - e.price_eur_per_mwh for e in self._source.sorted_marketdata_today - ] + sorted_prices = [e.price_per_kwh for e in self._source.sorted_marketdata_today] data = [ { ATTR_START_TIME: dt_util.as_local(e.start_time).isoformat(), ATTR_END_TIME: dt_util.as_local(e.end_time).isoformat(), - ATTR_RANK: sorted_prices.index(e.price_eur_per_mwh), + ATTR_RANK: sorted_prices.index(e.price_per_kwh), } for e in self._source.sorted_marketdata_today ] @@ -281,20 +274,20 @@ def __init__(self, coordinator: DataUpdateCoordinator): @property def native_value(self) -> StateType: - current_price = self._source.marketdata_now.price_eur_per_mwh - min_price = self._source.sorted_marketdata_today[0].price_eur_per_mwh - max_price = self._source.sorted_marketdata_today[-1].price_eur_per_mwh + current_price = self._source.marketdata_now.price_per_kwh + min_price = self._source.sorted_marketdata_today[0].price_per_kwh + max_price = self._source.sorted_marketdata_today[-1].price_per_kwh return (current_price - min_price) / (max_price - min_price) @property def extra_state_attributes(self): - min_price = self._source.sorted_marketdata_today[0].price_eur_per_mwh - max_price = self._source.sorted_marketdata_today[-1].price_eur_per_mwh + min_price = self._source.sorted_marketdata_today[0].price_per_kwh + max_price = self._source.sorted_marketdata_today[-1].price_per_kwh data = [ { ATTR_START_TIME: dt_util.as_local(e.start_time).isoformat(), ATTR_END_TIME: dt_util.as_local(e.end_time).isoformat(), - ATTR_QUANTILE: (e.price_eur_per_mwh - min_price) / (max_price - min_price), + ATTR_QUANTILE: (e.price_per_kwh - min_price) / (max_price - min_price), } for e in self._source.sorted_marketdata_today ] @@ -308,19 +301,19 @@ class EpexSpotLowestPriceSensorEntity(EpexSpotEntity, SensorEntity): entity_description = SensorEntityDescription( key="Lowest Price", name="Lowest Price", - suggested_display_precision=2, + suggested_display_precision=6, state_class=SensorStateClass.MEASUREMENT, ) def __init__(self, coordinator: DataUpdateCoordinator): super().__init__(coordinator, self.entity_description) self._attr_icon = self._localized.icon - self._attr_native_unit_of_measurement = self._localized.uom_per_mwh + self._attr_native_unit_of_measurement = self._localized.uom_per_kwh @property def native_value(self) -> StateType: min = self._source.sorted_marketdata_today[0] - return min.price_eur_per_mwh + return min.price_per_kwh @property def extra_state_attributes(self): @@ -328,7 +321,7 @@ def extra_state_attributes(self): return { ATTR_START_TIME: dt_util.as_local(min.start_time).isoformat(), ATTR_END_TIME: dt_util.as_local(min.end_time).isoformat(), - self._localized.attr_name_per_kwh: to_ct_per_kwh(self.native_value), + self._localized.attr_name_per_kwh: self.native_value, } @@ -338,19 +331,19 @@ class EpexSpotHighestPriceSensorEntity(EpexSpotEntity, SensorEntity): entity_description = SensorEntityDescription( key="Highest Price", name="Highest Price", - suggested_display_precision=2, + suggested_display_precision=6, state_class=SensorStateClass.MEASUREMENT, ) def __init__(self, coordinator: DataUpdateCoordinator): super().__init__(coordinator, self.entity_description) self._attr_icon = self._localized.icon - self._attr_native_unit_of_measurement = self._localized.uom_per_mwh + self._attr_native_unit_of_measurement = self._localized.uom_per_kwh @property def native_value(self) -> StateType: max = self._source.sorted_marketdata_today[-1] - return max.price_eur_per_mwh + return max.price_per_kwh @property def extra_state_attributes(self): @@ -358,7 +351,7 @@ def extra_state_attributes(self): return { ATTR_START_TIME: dt_util.as_local(max.start_time).isoformat(), ATTR_END_TIME: dt_util.as_local(max.end_time).isoformat(), - self._localized.attr_name_per_kwh: to_ct_per_kwh(self.native_value), + self._localized.attr_name_per_kwh: self.native_value, } @@ -368,24 +361,24 @@ class EpexSpotAveragePriceSensorEntity(EpexSpotEntity, SensorEntity): entity_description = SensorEntityDescription( key="Average Price", name="Average Price", - suggested_display_precision=2, + suggested_display_precision=6, state_class=SensorStateClass.MEASUREMENT, ) def __init__(self, coordinator: DataUpdateCoordinator): super().__init__(coordinator, self.entity_description) self._attr_icon = self._localized.icon - self._attr_native_unit_of_measurement = self._localized.uom_per_mwh + self._attr_native_unit_of_measurement = self._localized.uom_per_kwh @property def native_value(self) -> StateType: - s = sum(e.price_eur_per_mwh for e in self._source.sorted_marketdata_today) + s = sum(e.price_per_kwh for e in self._source.sorted_marketdata_today) return s / len(self._source.sorted_marketdata_today) @property def extra_state_attributes(self): return { - self._localized.attr_name_per_kwh: to_ct_per_kwh(self.native_value), + self._localized.attr_name_per_kwh: self.native_value, } @@ -395,23 +388,21 @@ class EpexSpotMedianPriceSensorEntity(EpexSpotEntity, SensorEntity): entity_description = SensorEntityDescription( key="Median Price", name="Median Price", - suggested_display_precision=2, + suggested_display_precision=6, state_class=SensorStateClass.MEASUREMENT, ) def __init__(self, coordinator: DataUpdateCoordinator): super().__init__(coordinator, self.entity_description) self._attr_icon = self._localized.icon - self._attr_native_unit_of_measurement = self._localized.uom_per_mwh + self._attr_native_unit_of_measurement = self._localized.uom_per_kwh @property def native_value(self) -> StateType: - return median( - [e.price_eur_per_mwh for e in self._source.sorted_marketdata_today] - ) + return median([e.price_per_kwh for e in self._source.sorted_marketdata_today]) @property def extra_state_attributes(self): return { - self._localized.attr_name_per_kwh: to_ct_per_kwh(self.native_value), + self._localized.attr_name_per_kwh: self.native_value, } diff --git a/custom_components/epex_spot/test_awattar.py b/custom_components/epex_spot/test_awattar.py index 265446b..a5d3bcd 100755 --- a/custom_components/epex_spot/test_awattar.py +++ b/custom_components/epex_spot/test_awattar.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 import aiohttp + import asyncio -from EPEXSpot import Awattar +from .EPEXSpot import Awattar +from .const import UOM_EUR_PER_KWH async def main(): @@ -14,7 +16,7 @@ async def main(): await service.fetch() print(f"count = {len(service.marketdata)}") for e in service.marketdata: - print(f"{e.start_time}: {e.price_eur_per_mwh} {e.UOM_EUR_PER_MWh}") + print(f"{e.start_time}: {e.price_per_kwh} {UOM_EUR_PER_KWH}") asyncio.run(main()) diff --git a/custom_components/epex_spot/test_epex_spot_web.py b/custom_components/epex_spot/test_epex_spot_web.py index 3ac42b4..018758a 100755 --- a/custom_components/epex_spot/test_epex_spot_web.py +++ b/custom_components/epex_spot/test_epex_spot_web.py @@ -1,21 +1,23 @@ #!/usr/bin/env python3 -import aiohttp import asyncio -import EPEXSpot.EPEXSpotWeb +import aiohttp + +from .const import UOM_EUR_PER_KWH +from .EPEXSpot import EPEXSpotWeb async def main(): async with aiohttp.ClientSession() as session: - service = EPEXSpot.EPEXSpotWeb.EPEXSpotWeb(market_area="DE-LU", session=session) + service = EPEXSpotWeb.EPEXSpotWeb(market_area="DE-LU", session=session) print(service.MARKET_AREAS) await service.fetch() print(f"count = {len(service.marketdata)}") for e in service.marketdata: print( - f"{e.start_time}-{e.end_time}: {e.price_eur_per_mwh} {e.UOM_EUR_PER_MWh}" # noqa + f"{e.start_time}-{e.end_time}: {e.price_per_kwh} {UOM_EUR_PER_KWH}" # noqa ) diff --git a/custom_components/epex_spot/test_smard.py b/custom_components/epex_spot/test_smard.py index 1793210..e2923fb 100755 --- a/custom_components/epex_spot/test_smard.py +++ b/custom_components/epex_spot/test_smard.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 -import aiohttp import asyncio -from EPEXSpot import SMARD +import aiohttp + +from .const import UOM_EUR_PER_KWH +from .EPEXSpot import SMARD async def main(): @@ -14,7 +16,7 @@ async def main(): await service.fetch() print(f"count = {len(service.marketdata)}") for e in service.marketdata: - print(f"{e.start_time}: {e.price_eur_per_mwh} {e.UOM_EUR_PER_MWh}") + print(f"{e.start_time}: {e.price_per_kwh} {UOM_EUR_PER_KWH}") asyncio.run(main()) diff --git a/custom_components/epex_spot/test_smartenergy.py b/custom_components/epex_spot/test_smartenergy.py index d0c79e2..15b8dad 100755 --- a/custom_components/epex_spot/test_smartenergy.py +++ b/custom_components/epex_spot/test_smartenergy.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 -import aiohttp import asyncio -from EPEXSpot import smartENERGY +import aiohttp + +from .const import UOM_EUR_PER_KWH +from .EPEXSpot import smartENERGY async def main(): @@ -13,7 +15,7 @@ async def main(): await service.fetch() print(f"count = {len(service.marketdata)}") for e in service.marketdata: - print(f"{e.start_time}: {e.price_ct_per_kwh} {e.UOM_CT_PER_kWh}") + print(f"{e.start_time}: {e.price_per_kwh} {UOM_EUR_PER_KWH}") asyncio.run(main()) diff --git a/custom_components/epex_spot/test_tibber.py b/custom_components/epex_spot/test_tibber.py index 6b49b0f..3d1adfc 100755 --- a/custom_components/epex_spot/test_tibber.py +++ b/custom_components/epex_spot/test_tibber.py @@ -3,7 +3,8 @@ import aiohttp import asyncio -from EPEXSpot import Tibber +from .EPEXSpot import Tibber +from .const import UOM_EUR_PER_KWH DEMO_TOKEN = "5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE" @@ -16,7 +17,7 @@ async def main(): await service.fetch() print(f"count = {len(service.marketdata)}") for e in service.marketdata: - print(f"{e.start_time}: {e.price_ct_per_kwh} {e.UOM_CT_PER_kWh}") + print(f"{e.start_time}: {e.price_per_kwh} {UOM_EUR_PER_KWH}") asyncio.run(main()) diff --git a/custom_components/epex_spot/translations/en.json b/custom_components/epex_spot/translations/en.json index 27aeccb..c5f8d13 100644 --- a/custom_components/epex_spot/translations/en.json +++ b/custom_components/epex_spot/translations/en.json @@ -21,7 +21,7 @@ "description": "Configure surcharges and tax to calculate net price per kWh.", "data": { "percentage_surcharge": "Percentage Surcharge (%)", - "absolute_surcharge": "Absolute Surcharge (ct/kWh)", + "absolute_surcharge": "Absolute Surcharge (€/£ per kWh)", "tax": "Tax (%)" }, "data_description": { @@ -37,7 +37,7 @@ "description": "Configure surcharges and tax to calculate net price per kWh.", "data": { "percentage_surcharge": "Percentage Surcharge (%)", - "absolute_surcharge": "Absolute Surcharge (ct/kWh)", + "absolute_surcharge": "Absolute Surcharge (€/£ per kWh)", "tax": "Tax (%)" }, "data_description": {