From f51048ba4540ffe313c58de34d2d2399deb328c9 Mon Sep 17 00:00:00 2001 From: frlequ Date: Fri, 3 Jan 2025 20:55:43 +0100 Subject: [PATCH] Const and Bug Fixes --- .../energy_and_tariff_costs/__init__.py | 72 +++++++++++++------ .../energy_and_tariff_costs/const.py | 28 +++++++- .../energy_and_tariff_costs/number.py | 67 ++++++++++------- .../energy_and_tariff_costs/sensor.py | 66 +++++++++-------- 4 files changed, 151 insertions(+), 82 deletions(-) diff --git a/custom_components/energy_and_tariff_costs/__init__.py b/custom_components/energy_and_tariff_costs/__init__.py index 026ddc6..7bb5cc2 100644 --- a/custom_components/energy_and_tariff_costs/__init__.py +++ b/custom_components/energy_and_tariff_costs/__init__.py @@ -1,10 +1,38 @@ +# __init__.py + from homeassistant.core import HomeAssistant from homeassistant.config_entries import ConfigEntry from homeassistant.helpers import device_registry as dr from .const import ( - DOMAIN, VT_PRICE, MT_PRICE, TAX, - BLOK_1_CONS_PRICE, BLOK_2_CONS_PRICE, BLOK_3_CONS_PRICE, BLOK_4_CONS_PRICE, BLOK_5_CONS_PRICE, - BLOK_1_TAR_PRICE, BLOK_2_TAR_PRICE, BLOK_3_TAR_PRICE, BLOK_4_TAR_PRICE, BLOK_5_TAR_PRICE + DOMAIN, + VT_PRICE, + MT_PRICE, + TAX, + ADDITIONAL_PRICE, + BLOK_1_CONS_PRICE, + BLOK_2_CONS_PRICE, + BLOK_3_CONS_PRICE, + BLOK_4_CONS_PRICE, + BLOK_5_CONS_PRICE, + BLOK_1_TAR_PRICE, + BLOK_2_TAR_PRICE, + BLOK_3_TAR_PRICE, + BLOK_4_TAR_PRICE, + BLOK_5_TAR_PRICE, + VT_PRICE_INITIAL, + MT_PRICE_INITIAL, + TAX_INITIAL, + ADDITIONAL_PRICE_INITIAL, + BLOK_1_CONS_PRICE_INITIAL, + BLOK_2_CONS_PRICE_INITIAL, + BLOK_3_CONS_PRICE_INITIAL, + BLOK_4_CONS_PRICE_INITIAL, + BLOK_5_CONS_PRICE_INITIAL, + BLOK_1_TAR_PRICE_INITIAL, + BLOK_2_TAR_PRICE_INITIAL, + BLOK_3_TAR_PRICE_INITIAL, + BLOK_4_TAR_PRICE_INITIAL, + BLOK_5_TAR_PRICE_INITIAL, ) async def async_setup(hass: HomeAssistant, config: dict): @@ -17,30 +45,30 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={identifiers}, - name="Energy Costs Device", - manufacturer="Test Manufacturer", - model="Test Model" + name="Energy & Tariff Costs", + manufacturer="Energy & Tariff Costs", + model="Beta" ) hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { "device_identifiers": identifiers, - "vt_price_initial": 0.084000, - "mt_price_initial": 0.070000, - "tax_initial": 22.0, - "additional_price_initial": 1.96, - - "blok_1_consumption_price_initial": 0.019580, - "blok_2_consumption_price_initial": 0.018440, - "blok_3_consumption_price_initial": 0.018370, - "blok_4_consumption_price_initial": 0.018380, - "blok_5_consumption_price_initial": 0.0, - - "blok_1_tariff_price_initial": 3.613240, - "blok_2_tariff_price_initial": 0.882400, - "blok_3_tariff_price_initial": 0.191370, - "blok_4_tariff_price_initial": 0.013160, - "blok_5_tariff_price_initial": 0.0, + VT_PRICE: VT_PRICE_INITIAL, + MT_PRICE: MT_PRICE_INITIAL, + TAX: TAX_INITIAL, + ADDITIONAL_PRICE: ADDITIONAL_PRICE_INITIAL, + + BLOK_1_CONS_PRICE: BLOK_1_CONS_PRICE_INITIAL, + BLOK_2_CONS_PRICE: BLOK_2_CONS_PRICE_INITIAL, + BLOK_3_CONS_PRICE: BLOK_3_CONS_PRICE_INITIAL, + BLOK_4_CONS_PRICE: BLOK_4_CONS_PRICE_INITIAL, + BLOK_5_CONS_PRICE: BLOK_5_CONS_PRICE_INITIAL, + + BLOK_1_TAR_PRICE: BLOK_1_TAR_PRICE_INITIAL, + BLOK_2_TAR_PRICE: BLOK_2_TAR_PRICE_INITIAL, + BLOK_3_TAR_PRICE: BLOK_3_TAR_PRICE_INITIAL, + BLOK_4_TAR_PRICE: BLOK_4_TAR_PRICE_INITIAL, + BLOK_5_TAR_PRICE: BLOK_5_TAR_PRICE_INITIAL, } await hass.config_entries.async_forward_entry_setup(entry, "number") diff --git a/custom_components/energy_and_tariff_costs/const.py b/custom_components/energy_and_tariff_costs/const.py index ccabefa..cd7afe1 100644 --- a/custom_components/energy_and_tariff_costs/const.py +++ b/custom_components/energy_and_tariff_costs/const.py @@ -1,27 +1,51 @@ +# const.py + DOMAIN = "energy_and_tariff_costs" INITIALS = "etc" +# Price Keys VT_PRICE = "energy_high_tariff_price" MT_PRICE = "energy_low_tariff_price" TAX = "tax" ADDITIONAL_PRICE = "additional_price" +# Consumption Price Keys BLOK_1_CONS_PRICE = "blok_1_consumption_price" BLOK_2_CONS_PRICE = "blok_2_consumption_price" BLOK_3_CONS_PRICE = "blok_3_consumption_price" BLOK_4_CONS_PRICE = "blok_4_consumption_price" BLOK_5_CONS_PRICE = "blok_5_consumption_price" +# Tariff Price Keys BLOK_1_TAR_PRICE = "blok_1_tariff_price" BLOK_2_TAR_PRICE = "blok_2_tariff_price" BLOK_3_TAR_PRICE = "blok_3_tariff_price" BLOK_4_TAR_PRICE = "blok_4_tariff_price" BLOK_5_TAR_PRICE = "blok_5_tariff_price" +# Initial Values (Januar 2025) +VT_PRICE_INITIAL = 0.084000 +MT_PRICE_INITIAL = 0.070000 +TAX_INITIAL = 22.0 +ADDITIONAL_PRICE_INITIAL = 1.96 + +BLOK_1_CONS_PRICE_INITIAL = 0.01998 +BLOK_2_CONS_PRICE_INITIAL = 0.01833 +BLOK_3_CONS_PRICE_INITIAL = 0.01809 +BLOK_4_CONS_PRICE_INITIAL = 0.01855 +BLOK_5_CONS_PRICE_INITIAL = 0.01873 + +BLOK_1_TAR_PRICE_INITIAL = 3.42250 +BLOK_2_TAR_PRICE_INITIAL = 0.91224 +BLOK_3_TAR_PRICE_INITIAL = 0.16297 +BLOK_4_TAR_PRICE_INITIAL = 0.00407 +BLOK_5_TAR_PRICE_INITIAL = 0.0 + +# Sensor Definitions MOJELEKTRO_PEAK = "sensor.moj_elektro_monthly_input_peak" MOJELEKTRO_OFFPEAK = "sensor.moj_elektro_monthly_input_offpeak" -# New block sensors +# New Block Sensors MOJELEKTRO_BLOK_1 = "sensor.moj_elektro_casovni_blok_1" MOJELEKTRO_BLOK_2 = "sensor.moj_elektro_casovni_blok_2" MOJELEKTRO_BLOK_3 = "sensor.moj_elektro_casovni_blok_3" @@ -32,4 +56,4 @@ MOJELEKTRO_DAILY_BLOK_2 = "sensor.moj_elektro_daily_input_blok_2" MOJELEKTRO_DAILY_BLOK_3 = "sensor.moj_elektro_daily_input_blok_3" MOJELEKTRO_DAILY_BLOK_4 = "sensor.moj_elektro_daily_input_blok_4" -MOJELEKTRO_DAILY_BLOK_5 = "sensor.moj_elektro_daily_input_blok_5" \ No newline at end of file +MOJELEKTRO_DAILY_BLOK_5 = "sensor.moj_elektro_daily_input_blok_5" diff --git a/custom_components/energy_and_tariff_costs/number.py b/custom_components/energy_and_tariff_costs/number.py index b6285a7..f034100 100644 --- a/custom_components/energy_and_tariff_costs/number.py +++ b/custom_components/energy_and_tariff_costs/number.py @@ -1,10 +1,12 @@ +# number.py + from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.components.number import NumberEntity, NumberMode from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from .const import ( - DOMAIN, INITIALS, VT_PRICE, MT_PRICE, TAX,ADDITIONAL_PRICE, + DOMAIN, INITIALS, VT_PRICE, MT_PRICE, TAX, ADDITIONAL_PRICE, BLOK_1_CONS_PRICE, BLOK_2_CONS_PRICE, BLOK_3_CONS_PRICE, BLOK_4_CONS_PRICE, BLOK_5_CONS_PRICE, BLOK_1_TAR_PRICE, BLOK_2_TAR_PRICE, BLOK_3_TAR_PRICE, BLOK_4_TAR_PRICE, BLOK_5_TAR_PRICE ) @@ -12,31 +14,29 @@ def friendly_name_from_id(name: str) -> str: return name.replace("_", " ").title() -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback): +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback +): data = hass.data[DOMAIN][entry.entry_id] device_identifiers = data["device_identifiers"] - to_create = [ - (VT_PRICE, data["vt_price_initial"]), - (MT_PRICE, data["mt_price_initial"]), - (TAX, data["tax_initial"]), - - (BLOK_1_CONS_PRICE, data["blok_1_consumption_price_initial"]), - (BLOK_2_CONS_PRICE, data["blok_2_consumption_price_initial"]), - (BLOK_3_CONS_PRICE, data["blok_3_consumption_price_initial"]), - (BLOK_4_CONS_PRICE, data["blok_4_consumption_price_initial"]), - (BLOK_5_CONS_PRICE, data["blok_5_consumption_price_initial"]), - - (BLOK_1_TAR_PRICE, data["blok_1_tariff_price_initial"]), - (BLOK_2_TAR_PRICE, data["blok_2_tariff_price_initial"]), - (BLOK_3_TAR_PRICE, data["blok_3_tariff_price_initial"]), - (BLOK_4_TAR_PRICE, data["blok_4_tariff_price_initial"]), - (BLOK_5_TAR_PRICE, data["blok_5_tariff_price_initial"]), - - (ADDITIONAL_PRICE, data["additional_price_initial"]) - - + (VT_PRICE, data[VT_PRICE]), + (MT_PRICE, data[MT_PRICE]), + (TAX, data[TAX]), + (ADDITIONAL_PRICE, data[ADDITIONAL_PRICE]), + (BLOK_1_CONS_PRICE, data[BLOK_1_CONS_PRICE]), + (BLOK_2_CONS_PRICE, data[BLOK_2_CONS_PRICE]), + (BLOK_3_CONS_PRICE, data[BLOK_3_CONS_PRICE]), + (BLOK_4_CONS_PRICE, data[BLOK_4_CONS_PRICE]), + (BLOK_5_CONS_PRICE, data[BLOK_5_CONS_PRICE]), + (BLOK_1_TAR_PRICE, data[BLOK_1_TAR_PRICE]), + (BLOK_2_TAR_PRICE, data[BLOK_2_TAR_PRICE]), + (BLOK_3_TAR_PRICE, data[BLOK_3_TAR_PRICE]), + (BLOK_4_TAR_PRICE, data[BLOK_4_TAR_PRICE]), + (BLOK_5_TAR_PRICE, data[BLOK_5_TAR_PRICE]), ] entities = [ @@ -47,7 +47,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e async_add_entities(entities, True) class EnergyCostNumber(NumberEntity, RestoreEntity): - def __init__(self, entry: ConfigEntry, name: str, initial_value: float, device_identifiers): + def __init__( + self, + entry: ConfigEntry, + name: str, + initial_value: float, + device_identifiers + ): self._entry = entry self._name_id = name self.entity_id = f"number.{INITIALS}_{name}" @@ -62,8 +68,6 @@ def __init__(self, entry: ConfigEntry, name: str, initial_value: float, device_i self._attr_max_value = 10.0 self._attr_step = 0.0001 self._attr_icon = "mdi:currency-eur" - - # Don't set self._state here; wait for restore or fallback to initial value in async_added_to_hass async def async_added_to_hass(self): await super().async_added_to_hass() @@ -79,7 +83,8 @@ async def async_added_to_hass(self): self._state = self._initial_value @property - def state(self): + def native_value(self) -> float: + """Return the current value.""" return self._state @property @@ -91,6 +96,14 @@ def device_info(self): "model": "Beta", } - async def async_set_value(self, value: float): + async def async_set_native_value(self, value: float): + """Set the new value.""" self._state = value self.async_write_ha_state() + # Here you can add any additional logic needed when the value is set, + # such as updating `hass.data` or interacting with other components. + + # Optional: If you prefer to use async_set_value instead of async_set_native_value + async def async_set_value(self, value: float): + """Set the new value.""" + await self.async_set_native_value(value) diff --git a/custom_components/energy_and_tariff_costs/sensor.py b/custom_components/energy_and_tariff_costs/sensor.py index 1e148fa..97276db 100644 --- a/custom_components/energy_and_tariff_costs/sensor.py +++ b/custom_components/energy_and_tariff_costs/sensor.py @@ -152,9 +152,9 @@ def device_info(self) -> dict: """Return device information for the sensor.""" return { "identifiers": {tuple(self._device_identifiers)}, # e.g., {('unique_device_identifier',)} - "name": "Energy Costs Device", - "manufacturer": "Test Manufacturer", - "model": "Test Model", + "name": "Energy & Tariff Costs", + "manufacturer": "Energy & Tariff Costs", + "model": "Beta", } @property @@ -199,10 +199,12 @@ def _find_entity_id(self, name: str) -> Optional[str]: async def _get_monthly_sum_if_daily_sensor(self, sensor_id: str) -> Optional[float]: """ - Sum the monthly consumption from a daily sensor, ignoring the first value of each day - if a second value exists. + Sum the monthly consumption from a daily sensor by using only the last state of each day. + Ignores all other states within the same day. """ - if not sensor_id.startswith(f"sensor.moj_elektro_daily_input_"): + DAILY_SENSOR_PREFIX = "sensor.moj_elektro_daily_input_" + + if not sensor_id.startswith(DAILY_SENSOR_PREFIX): _LOGGER.debug("Sensor %s is not a daily sensor", sensor_id) return None # Not a daily sensor @@ -243,34 +245,36 @@ async def _get_monthly_sum_if_daily_sensor(self, sensor_id: str) -> Optional[flo total = 0.0 for day, day_states in states_by_day.items(): _LOGGER.debug("Processing day: %s with %d states", day, len(day_states)) - if len(day_states) > 1: - _LOGGER.debug( - "Multiple states found for %s on %s. Ignoring the first state.", - sensor_id, day - ) - # Ignore the first state - states_to_sum = day_states[1:] + + if not day_states: + _LOGGER.debug("No states to process for %s on %s", sensor_id, day) + continue # Skip days with no states + + # Identify the last state of the day based on last_changed timestamp + last_state = max(day_states, key=lambda s: s.last_changed) + _LOGGER.debug( + "Last state for %s on %s: %s", + sensor_id, day, last_state.state + ) + + if last_state.state not in (None, "unknown", "unavailable"): + try: + val = float(last_state.state) + total += val + _LOGGER.debug( + "Added value %f from sensor %s on %s", + val, sensor_id, day + ) + except ValueError: + _LOGGER.error( + "Invalid state value '%s' in sensor %s on %s", + last_state.state, sensor_id, day + ) else: _LOGGER.debug( - "Single state found for %s on %s. Including it in the sum.", - sensor_id, day + "Ignoring state '%s' for sensor %s on %s", + last_state.state, sensor_id, day ) - states_to_sum = day_states - - for s in states_to_sum: - if s.state not in (None, "unknown", "unavailable"): - try: - val = float(s.state) - total += val - _LOGGER.debug( - "Added value %f from sensor %s on %s", - val, sensor_id, day - ) - except ValueError: - _LOGGER.error( - "Invalid state value '%s' in sensor %s on %s", - s.state, sensor_id, day - ) _LOGGER.debug("Monthly sum for sensor %s: %f", sensor_id, total) return total