From 0105b22daf476263d960c99f15ca27105073f0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Gir=C3=B3n?= Date: Sat, 16 Mar 2024 21:54:44 +0100 Subject: [PATCH] fix: refresh multiple contracts, statistic id invalid (#19) * fix: trigger refresh multiple coordinators * change to TimestampDataUpdateCoordinator * fix: invalid statistic id, refactor internal id * optimize code, remove unused previous data declaration * internal: call function for error raise * use id instead of name (same value) * bump version --- .../aigues_barcelona/manifest.json | 2 +- custom_components/aigues_barcelona/sensor.py | 54 ++++++++++--------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/custom_components/aigues_barcelona/manifest.json b/custom_components/aigues_barcelona/manifest.json index fc9bb8c..d405b92 100644 --- a/custom_components/aigues_barcelona/manifest.json +++ b/custom_components/aigues_barcelona/manifest.json @@ -14,5 +14,5 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/duhow/hass-aigues-barcelona/issues", "requirements": [], - "version": "0.4.0" + "version": "0.4.1" } diff --git a/custom_components/aigues_barcelona/sensor.py b/custom_components/aigues_barcelona/sensor.py index 3a73c1e..3c70330 100644 --- a/custom_components/aigues_barcelona/sensor.py +++ b/custom_components/aigues_barcelona/sensor.py @@ -25,7 +25,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.update_coordinator import CoordinatorEntity -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.helpers.update_coordinator import TimestampDataUpdateCoordinator from .api import AiguesApiClient from .const import API_ERROR_TOKEN_REVOKED @@ -60,31 +60,29 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie contadores = list() for contract in contracts: - coordinator = ContratoAgua( - hass, username, password, contract, token=token, prev_data=None - ) + coordinator = ContratoAgua(hass, username, password, contract, token=token) + contadores.append(ContadorAgua(coordinator)) - # postpone first refresh to speed up startup - @callback - async def async_first_refresh(*args): - """Force the component to assess the first refresh.""" - await coordinator.async_refresh() + # postpone first refresh to speed up startup + @callback + async def async_first_refresh(*args): + for sensor in contadores: + await sensor.coordinator.async_refresh() - if hass.state == CoreState.running: - await async_first_refresh() - else: - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, async_first_refresh) + # ------ - contadores.append(ContadorAgua(coordinator)) + if hass.state == CoreState.running: + await async_first_refresh() + else: + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, async_first_refresh) _LOGGER.info("about to add entities") - # add sensor entities async_add_entities(contadores) return True -class ContratoAgua(DataUpdateCoordinator): +class ContratoAgua(TimestampDataUpdateCoordinator): def __init__( self, hass: HomeAssistant, @@ -99,6 +97,7 @@ def __init__( self.contract = contract.upper() self.id = contract.lower() + self.internal_sensor_id = f"sensor.contador_{self.id}" if not hass.data[DOMAIN].get(self.contract): # init data shared store @@ -119,6 +118,9 @@ def __init__( update_interval=timedelta(seconds=DEFAULT_SCAN_PERIOD), ) + def __repr__(self): + return f"<{self.__class__.__name__} {self.contract}>" + async def _async_update_data(self): _LOGGER.info(f"Updating coordinator data for {self.contract}") TODAY = datetime.now() @@ -149,7 +151,7 @@ async def _async_update_data(self): _LOGGER.error("Token has expired, cannot check consumptions.") raise ConfigEntryAuthFailed from exp except Exception as exp: - _LOGGER.error("error while getting data: %s", exp) + self.async_set_update_error(exp) if API_ERROR_TOKEN_REVOKED in str(exp): raise ConfigEntryAuthFailed from exp @@ -165,7 +167,10 @@ async def _async_update_data(self): self._data[CONF_STATE] = metric["datetime"] # await self._clear_statistics() - await self._async_import_statistics(consumptions) + try: + await self._async_import_statistics(consumptions) + except: + pass return True @@ -176,7 +181,7 @@ async def _clear_statistics(self) -> None: to_clear = [ x["statistic_id"] for x in all_ids - if x["statistic_id"].startswith(f"sensor.contador_{self.contract}") + if x["statistic_id"].startswith(self.internal_sensor_id) ] if to_clear: @@ -218,7 +223,7 @@ async def _async_import_statistics(self, consumptions) -> None: "has_sum": True, "name": None, "source": "recorder", # required - "statistic_id": f"sensor.contador_{self.contract}", + "statistic_id": self.internal_sensor_id, "unit_of_measurement": UnitOfVolume.CUBIC_METERS, } # _LOGGER.debug(f"Adding metric: {metadata} {stats}") @@ -231,8 +236,7 @@ class ContadorAgua(CoordinatorEntity, SensorEntity): def __init__(self, coordinator) -> None: """Initialize the sensor.""" super().__init__(coordinator) - self._data = coordinator.hass.data[DOMAIN][coordinator.id.upper()] - self._attr_name = f"Contador {coordinator.name}" + self._attr_name = f"Contador {coordinator.id}" self._attr_unique_id = coordinator.id self._attr_icon = "mdi:water-pump" self._attr_has_entity_name = True @@ -243,12 +247,14 @@ def __init__(self, coordinator) -> None: @property def native_value(self): - return self._data.get(CONF_VALUE, None) + return self.coordinator._data.get(CONF_VALUE, None) @property def last_measurement(self): try: - last_measure = datetime.fromisoformat(self._data.get(CONF_STATE, "")) + last_measure = datetime.fromisoformat( + self.coordinator._data.get(CONF_STATE, "") + ) except ValueError: last_measure = None return last_measure