From 50df0cd6c42af75afd6f3dd5abec03f2d7861221 Mon Sep 17 00:00:00 2001 From: Alar Aun Date: Fri, 27 Sep 2024 18:23:36 +0300 Subject: [PATCH] Add energy_scale (kWh, MWh) option --- custom_components/entsoe/__init__.py | 4 ++++ custom_components/entsoe/config_flow.py | 22 +++++++++++++++++++ custom_components/entsoe/const.py | 4 ++++ custom_components/entsoe/coordinator.py | 8 ++++--- custom_components/entsoe/sensor.py | 19 +++++++++------- custom_components/entsoe/translations/en.json | 5 ++++- 6 files changed, 50 insertions(+), 12 deletions(-) diff --git a/custom_components/entsoe/__init__.py b/custom_components/entsoe/__init__.py index 059e2c3..07477b7 100644 --- a/custom_components/entsoe/__init__.py +++ b/custom_components/entsoe/__init__.py @@ -13,10 +13,12 @@ CALCULATION_MODE, CONF_API_KEY, CONF_AREA, + CONF_ENERGY_SCALE, CONF_CALCULATION_MODE, CONF_MODIFYER, CONF_VAT_VALUE, DEFAULT_MODIFYER, + DEFAULT_ENERGY_SCALE, DOMAIN, ) from .coordinator import EntsoeCoordinator @@ -40,6 +42,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Initialise the coordinator and save it as domain-data api_key = entry.options[CONF_API_KEY] area = entry.options[CONF_AREA] + energy_scale = entry.options.get(CONF_ENERGY_SCALE, DEFAULT_ENERGY_SCALE) modifyer = entry.options.get(CONF_MODIFYER, DEFAULT_MODIFYER) vat = entry.options.get(CONF_VAT_VALUE, 0) calculation_mode = entry.options.get( @@ -49,6 +52,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass, api_key=api_key, area=area, + energy_scale=energy_scale, modifyer=modifyer, calculation_mode=calculation_mode, VAT=vat, diff --git a/custom_components/entsoe/config_flow.py b/custom_components/entsoe/config_flow.py index 254b709..4adb001 100644 --- a/custom_components/entsoe/config_flow.py +++ b/custom_components/entsoe/config_flow.py @@ -21,16 +21,19 @@ from .const import ( AREA_INFO, + ENERGY_SCALES, CALCULATION_MODE, CONF_ADVANCED_OPTIONS, CONF_API_KEY, CONF_AREA, CONF_CALCULATION_MODE, CONF_CURRENCY, + CONF_ENERGY_SCALE, CONF_ENTITY_NAME, CONF_MODIFYER, CONF_VAT_VALUE, DEFAULT_CURRENCY, + DEFAULT_ENERGY_SCALE, DEFAULT_MODIFYER, DOMAIN, UNIQUE_ID, @@ -47,6 +50,7 @@ def __init__(self): self.api_key = None self.modifyer = None self.currency = None + self.energy_scale = None self.name = "" VERSION = 1 @@ -87,6 +91,7 @@ async def async_step_user( user_input[CONF_VAT_VALUE] = 0 user_input[CONF_MODIFYER] = DEFAULT_MODIFYER user_input[CONF_CURRENCY] = DEFAULT_CURRENCY + user_input[CONF_ENERGY_SCALE] = DEFAULT_ENERGY_SCALE user_input[CONF_CALCULATION_MODE] = CALCULATION_MODE["default"] if not already_configured: return self.async_create_entry( @@ -97,6 +102,7 @@ async def async_step_user( CONF_AREA: user_input[CONF_AREA], CONF_MODIFYER: user_input[CONF_MODIFYER], CONF_CURRENCY: user_input[CONF_CURRENCY], + CONF_ENERGY_SCALE: user_input[CONF_ENERGY_SCALE], CONF_ADVANCED_OPTIONS: user_input[CONF_ADVANCED_OPTIONS], CONF_VAT_VALUE: user_input[CONF_VAT_VALUE], CONF_ENTITY_NAME: user_input[CONF_ENTITY_NAME], @@ -160,6 +166,9 @@ async def async_step_extra(self, user_input=None): if user_input[CONF_CURRENCY] in (None, ""): user_input[CONF_CURRENCY] = DEFAULT_CURRENCY + if user_input[CONF_ENERGY_SCALE] in (None, ""): + user_input[CONF_ENERGY_SCALE] = DEFAULT_ENERGY_SCALE + template_ok = await self._valid_template(user_input[CONF_MODIFYER]) if not already_configured: @@ -173,6 +182,7 @@ async def async_step_extra(self, user_input=None): CONF_AREA: user_input[CONF_AREA], CONF_MODIFYER: user_input[CONF_MODIFYER], CONF_CURRENCY: user_input[CONF_CURRENCY], + CONF_ENERGY_SCALE: user_input[CONF_ENERGY_SCALE], CONF_VAT_VALUE: user_input[CONF_VAT_VALUE], CONF_ENTITY_NAME: user_input[CONF_ENTITY_NAME], CONF_CALCULATION_MODE: user_input[ @@ -198,6 +208,9 @@ async def async_step_extra(self, user_input=None): vol.Optional(CONF_CURRENCY, default=DEFAULT_CURRENCY): vol.All( vol.Coerce(str) ), + vol.Optional(CONF_ENERGY_SCALE, default=DEFAULT_ENERGY_SCALE): vol.In( + list(ENERGY_SCALES.keys()) + ), vol.Optional( CONF_CALCULATION_MODE, default=CALCULATION_MODE["default"] ): SelectSelector( @@ -260,6 +273,9 @@ async def async_step_init( if user_input[CONF_CURRENCY] in (None, ""): user_input[CONF_CURRENCY] = DEFAULT_CURRENCY + if user_input[CONF_ENERGY_SCALE] in (None, ""): + user_input[CONF_ENERGY_SCALE] = DEFAULT_ENERGY_SCALE + template_ok = await self._valid_template(user_input[CONF_MODIFYER]) if template_ok: @@ -305,6 +321,12 @@ async def async_step_init( CONF_CURRENCY, DEFAULT_CURRENCY ), ): vol.All(vol.Coerce(str)), + vol.Optional( + CONF_ENERGY_SCALE, + default=self.config_entry.options.get( + CONF_ENERGY_SCALE, DEFAULT_ENERGY_SCALE + ), + ): vol.In(list(ENERGY_SCALES.keys())), vol.Optional( CONF_CALCULATION_MODE, default=calculation_mode_default, diff --git a/custom_components/entsoe/const.py b/custom_components/entsoe/const.py index f486450..99f1e4c 100644 --- a/custom_components/entsoe/const.py +++ b/custom_components/entsoe/const.py @@ -10,12 +10,14 @@ CONF_AREA = "area" CONF_MODIFYER = "modifyer" CONF_CURRENCY = "currency" +CONF_ENERGY_SCALE = "energy_scale" CONF_ADVANCED_OPTIONS = "advanced_options" CONF_CALCULATION_MODE = "calculation_mode" CONF_VAT_VALUE = "VAT_value" DEFAULT_MODIFYER = "{{current_price}}" DEFAULT_CURRENCY = CURRENCY_EURO +DEFAULT_ENERGY_SCALE = "kWh" # default is only for internal use / backwards compatibility CALCULATION_MODE = { @@ -25,6 +27,8 @@ "publish": "publish", } +ENERGY_SCALES = { "kWh": 1000, "MWh": 1 } + # Commented ones are not working at entsoe AREA_INFO = { "AT": {"code": "AT", "name": "Austria", "VAT": 0.21, "Currency": "EUR"}, diff --git a/custom_components/entsoe/coordinator.py b/custom_components/entsoe/coordinator.py index 51d665b..28cdd1a 100644 --- a/custom_components/entsoe/coordinator.py +++ b/custom_components/entsoe/coordinator.py @@ -12,7 +12,7 @@ from requests.exceptions import HTTPError from .api_client import EntsoeClient -from .const import AREA_INFO, CALCULATION_MODE, DEFAULT_MODIFYER +from .const import AREA_INFO, ENERGY_SCALES, CALCULATION_MODE, DEFAULT_MODIFYER # depending on timezone les than 24 hours could be returned. MIN_HOURS = 20 @@ -26,6 +26,7 @@ def __init__( hass: HomeAssistant, api_key, area, + energy_scale, modifyer, calculation_mode=CALCULATION_MODE["default"], VAT=0, @@ -35,6 +36,7 @@ def __init__( self.api_key = api_key self.modifyer = modifyer self.area = AREA_INFO[area]["code"] + self.energy_scale = energy_scale self.calculation_mode = calculation_mode self.vat = VAT self.today = None @@ -64,10 +66,10 @@ def calc_price(self, value, fake_dt=None, no_template=False) -> float: # Used to inject the current hour. # so template can be simplified using now if no_template: - price = round(value / 1000, 5) + price = round(value / ENERGY_SCALES[self.energy_scale], 5) return price - price = value / 1000 + price = value / ENERGY_SCALES[self.energy_scale] if fake_dt is not None: def faker(): diff --git a/custom_components/entsoe/sensor.py b/custom_components/entsoe/sensor.py index 2106bab..33b3025 100644 --- a/custom_components/entsoe/sensor.py +++ b/custom_components/entsoe/sensor.py @@ -16,7 +16,7 @@ SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, UnitOfEnergy +from homeassistant.const import PERCENTAGE from homeassistant.core import HassJob, HomeAssistant from homeassistant.helpers import event from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo @@ -28,8 +28,10 @@ from .const import ( ATTRIBUTION, CONF_CURRENCY, + CONF_ENERGY_SCALE, CONF_ENTITY_NAME, DEFAULT_CURRENCY, + DEFAULT_ENERGY_SCALE, DOMAIN, ) from .coordinator import EntsoeCoordinator @@ -44,13 +46,13 @@ class EntsoeEntityDescription(SensorEntityDescription): value_fn: Callable[[dict], StateType] = None -def sensor_descriptions(currency: str) -> tuple[EntsoeEntityDescription, ...]: +def sensor_descriptions(currency: str, energy_scale: str) -> tuple[EntsoeEntityDescription, ...]: """Construct EntsoeEntityDescription.""" return ( EntsoeEntityDescription( key="current_price", name="Current electricity market price", - native_unit_of_measurement=f"{currency}/{UnitOfEnergy.KILO_WATT_HOUR}", + native_unit_of_measurement=f"{currency}/{energy_scale}", state_class=SensorStateClass.MEASUREMENT, icon="mdi:currency-eur", suggested_display_precision=3, @@ -59,7 +61,7 @@ def sensor_descriptions(currency: str) -> tuple[EntsoeEntityDescription, ...]: EntsoeEntityDescription( key="next_hour_price", name="Next hour electricity market price", - native_unit_of_measurement=f"{currency}/{UnitOfEnergy.KILO_WATT_HOUR}", + native_unit_of_measurement=f"{currency}/{energy_scale}", state_class=SensorStateClass.MEASUREMENT, icon="mdi:currency-eur", suggested_display_precision=3, @@ -68,7 +70,7 @@ def sensor_descriptions(currency: str) -> tuple[EntsoeEntityDescription, ...]: EntsoeEntityDescription( key="min_price", name="Lowest energy price", - native_unit_of_measurement=f"{currency}/{UnitOfEnergy.KILO_WATT_HOUR}", + native_unit_of_measurement=f"{currency}/{energy_scale}", state_class=SensorStateClass.MEASUREMENT, icon="mdi:currency-eur", suggested_display_precision=3, @@ -77,7 +79,7 @@ def sensor_descriptions(currency: str) -> tuple[EntsoeEntityDescription, ...]: EntsoeEntityDescription( key="max_price", name="Highest energy price", - native_unit_of_measurement=f"{currency}/{UnitOfEnergy.KILO_WATT_HOUR}", + native_unit_of_measurement=f"{currency}/{energy_scale}", state_class=SensorStateClass.MEASUREMENT, icon="mdi:currency-eur", suggested_display_precision=3, @@ -86,7 +88,7 @@ def sensor_descriptions(currency: str) -> tuple[EntsoeEntityDescription, ...]: EntsoeEntityDescription( key="avg_price", name="Average electricity price", - native_unit_of_measurement=f"{currency}/{UnitOfEnergy.KILO_WATT_HOUR}", + native_unit_of_measurement=f"{currency}/{energy_scale}", state_class=SensorStateClass.MEASUREMENT, icon="mdi:currency-eur", suggested_display_precision=3, @@ -129,7 +131,8 @@ async def async_setup_entry( entities = [] entity = {} for description in sensor_descriptions( - currency=config_entry.options.get(CONF_CURRENCY, DEFAULT_CURRENCY) + currency=config_entry.options.get(CONF_CURRENCY, DEFAULT_CURRENCY), + energy_scale=config_entry.options.get(CONF_ENERGY_SCALE, DEFAULT_ENERGY_SCALE) ): entity = description entities.append( diff --git a/custom_components/entsoe/translations/en.json b/custom_components/entsoe/translations/en.json index 985b9a3..dcb11e1 100644 --- a/custom_components/entsoe/translations/en.json +++ b/custom_components/entsoe/translations/en.json @@ -9,6 +9,7 @@ "advanced_options": "I want to set VAT, template and calculation method (next step)", "modifyer": "Price Modifyer Template (Optional)", "currency": "Currency of the modified price (Optional)", + "energy_scale": "Energy scale (Optional)", "name": "Name (Optional)" } }, @@ -16,7 +17,8 @@ "data": { "VAT_value": "VAT tariff", "modifyer": "Price Modifyer Template (Optional)", - "currency": "Currency of the modified price (Optional)" + "currency": "Currency of the modified price (Optional)", + "energy_scale": "Energy scale (Optional)" } } }, @@ -35,6 +37,7 @@ "area": "Area*", "modifyer": "Price Modifyer Template (Optional)", "currency": "Currency of the modified price (Optional)", + "energy_scale": "Energy scale (Optional)", "VAT_value": "VAT tariff", "name": "Name (Optional)" }