From b62e6abccfd8711595234b4aba7687cd3802617a Mon Sep 17 00:00:00 2001 From: schizza Date: Sun, 7 Apr 2024 11:05:01 +0200 Subject: [PATCH 1/2] Autoloading new sensors. --- custom_components/sws12500/__init__.py | 75 +++++++++++++---------- custom_components/sws12500/config_flow.py | 19 +++--- custom_components/sws12500/const.py | 8 +-- custom_components/sws12500/sensor.py | 54 +++++++++------- custom_components/sws12500/utils.py | 45 +++++--------- 5 files changed, 104 insertions(+), 97 deletions(-) diff --git a/custom_components/sws12500/__init__.py b/custom_components/sws12500/__init__.py index fc9aa31..ef89a2b 100644 --- a/custom_components/sws12500/__init__.py +++ b/custom_components/sws12500/__init__.py @@ -8,15 +8,22 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import InvalidStateError, PlatformNotReady -from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from .const import API_ID, API_KEY, DEFAULT_URL, DEV_DBG, DOMAIN, WINDY_ENABLED -from .utils import anonymize, check_disabled, remap_items +from .const import ( + API_ID, + API_KEY, + DEFAULT_URL, + DEV_DBG, + DOMAIN, + SENSORS_TO_LOAD, + WINDY_ENABLED, +) +from .utils import anonymize, check_disabled, remap_items, update_options from .windy_func import WindyPush _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.SENSOR] +PLATFORMS: list[Platform] = [Platform.SENSOR] class IncorrectDataError(InvalidStateError): @@ -57,7 +64,11 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator): remaped_items = remap_items(data) - await check_disabled(self.hass, remaped_items, self.config_entry.options.get(DEV_DBG)) + if sensors := check_disabled(self.hass, remaped_items, self.config): + await update_options( + self.hass, self.config_entry, SENSORS_TO_LOAD, sensors + ) + # await self.hass.config_entries.async_reload(self.config.entry_id) self.async_set_updated_data(remaped_items) @@ -77,7 +88,10 @@ def register_path( "GET", url_path, coordinator.recieved_data ) except RuntimeError as Ex: # pylint: disable=(broad-except) - if "Added route will never be executed, method GET is already registered" in Ex.args: + if ( + "Added route will never be executed, method GET is already registered" + in Ex.args + ): _LOGGER.info("Handler to URL (%s) already registred", url_path) return True @@ -90,38 +104,22 @@ def register_path( ) return True + def unregister_path(hass: HomeAssistant): """Unregister path to handle incoming data.""" _LOGGER.error( - "Unable to delete webhook from API! Restart HA before adding integration!" + """Unable to delete webhook from API! Restart HA before adding integration! + If this error is raised while adding sensors or reloading configuration, you can ignore this error + """ ) -class Weather(WeatherDataUpdateCoordinator): - """Weather class.""" - - def __init__(self, hass: HomeAssistant, config) -> None: - """Init class.""" - self.hass = hass - super().__init__(hass, config) - - async def setup_update_listener(self, hass: HomeAssistant, entry: ConfigEntry): - """Update setup listener.""" - await hass.config_entries.async_reload(entry.entry_id) - - _LOGGER.info("Settings updated") - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the config entry for my device.""" coordinator = WeatherDataUpdateCoordinator(hass, entry) - hass.data.setdefault(DOMAIN, {}) - - hass.data[DOMAIN][entry.entry_id] = coordinator - - weather = Weather(hass, entry) + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator if not register_path(hass, DEFAULT_URL, coordinator): _LOGGER.error("Fatal: path not registered!") @@ -129,10 +127,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - entry.async_on_unload(entry.add_update_listener(weather.setup_update_listener)) + entry.async_on_unload(entry.add_update_listener(update_listener)) return True +async def update_listener(hass: HomeAssistant, entry: ConfigEntry): + """Update setup listener.""" + await hass.config_entries.async_reload(entry.entry_id) + + _LOGGER.info("Settings updated") + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" @@ -145,10 +149,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return _ok -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the component. +# async def async_setup(hass: HomeAssistant, entry: ConfigEntry) -> bool: +# """Set up the component. - This component can only be configured through the Integrations UI. - """ - hass.data.setdefault(DOMAIN, {}) - return True +# This component can only be configured through the Integrations UI. +# """ +# hass.data.setdefault(DOMAIN, {}) + +# await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + +# return True diff --git a/custom_components/sws12500/config_flow.py b/custom_components/sws12500/config_flow.py index 3923ad6..964fdd8 100644 --- a/custom_components/sws12500/config_flow.py +++ b/custom_components/sws12500/config_flow.py @@ -13,6 +13,7 @@ from .const import ( DEV_DBG, DOMAIN, INVALID_CREDENTIALS, + SENSORS_TO_LOAD, WINDY_API_KEY, WINDY_ENABLED, WINDY_LOGGER_ENABLED, @@ -46,6 +47,10 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): WINDY_LOGGER_ENABLED: self.config_entry.options.get(WINDY_LOGGER_ENABLED) if isinstance(self.config_entry.options.get(WINDY_LOGGER_ENABLED), bool) else False, } + self.sensors: dict[str, Any] = { + SENSORS_TO_LOAD: self.config_entry.options.get(SENSORS_TO_LOAD) if isinstance(self.config_entry.options.get(SENSORS_TO_LOAD), list) else [] + } + self.user_data_schema = { vol.Required(API_ID, default=self.user_data[API_ID] or ""): str, vol.Required(API_KEY, default=self.user_data[API_KEY] or ""): str, @@ -85,17 +90,12 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): elif user_input[API_KEY] == user_input[API_ID]: errors["base"] = "valid_credentials_match" else: - # retain Windy options - data: dict = {} - data[WINDY_API_KEY] = self.config_entry.options.get(WINDY_API_KEY) - data[WINDY_ENABLED] = self.config_entry.options.get(WINDY_ENABLED) - data[WINDY_LOGGER_ENABLED] = self.config_entry.options.get( - WINDY_LOGGER_ENABLED - ) - # retain windy data user_input.update(self.windy_data) + #retain sensors + user_input.update(self.sensors) + return self.async_create_entry(title=DOMAIN, data=user_input) self.user_data = user_input @@ -133,6 +133,9 @@ class ConfigOptionsFlowHandler(config_entries.OptionsFlow): # retain user_data user_input.update(self.user_data) + #retain senors + user_input.update(self.sensors) + return self.async_create_entry(title=DOMAIN, data=user_input) diff --git a/custom_components/sws12500/const.py b/custom_components/sws12500/const.py index b38c8f3..e8b0c55 100644 --- a/custom_components/sws12500/const.py +++ b/custom_components/sws12500/const.py @@ -11,6 +11,8 @@ ICON = "mdi:weather" API_KEY = "API_KEY" API_ID = "API_ID" +SENSORS_TO_LOAD: Final = "sensors_to_load" + DEV_DBG: Final = "dev_debug_checkbox" WINDY_API_KEY = "WINDY_API_KEY" @@ -85,7 +87,5 @@ REMAP_ITEMS: dict = { "soilmoisture": CH2_HUMIDITY, } -DISABLED_BY_DEFAULT: Final = [ - CH2_TEMP, - CH2_HUMIDITY -] +DISABLED_BY_DEFAULT: Final = [CH2_TEMP, CH2_HUMIDITY] + diff --git a/custom_components/sws12500/sensor.py b/custom_components/sws12500/sensor.py index e696faf..0c439bd 100644 --- a/custom_components/sws12500/sensor.py +++ b/custom_components/sws12500/sensor.py @@ -25,7 +25,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceEntryType -from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity import DeviceInfo, generate_entity_id from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -42,6 +42,7 @@ from .const import ( OUTSIDE_HUMIDITY, OUTSIDE_TEMP, RAIN, + SENSORS_TO_LOAD, SOLAR_RADIATION, UV, WIND_DIR, @@ -177,6 +178,7 @@ SENSOR_TYPES: tuple[WeatherSensorEntityDescription, ...] = ( ), WeatherSensorEntityDescription( key=UV, + name=UV, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UV_INDEX, icon="mdi:sunglasses", @@ -191,7 +193,6 @@ SENSOR_TYPES: tuple[WeatherSensorEntityDescription, ...] = ( suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:weather-sunny", translation_key=CH2_TEMP, - entity_registry_visible_default=False, value_fn=lambda data: cast(float, data), ), WeatherSensorEntityDescription( @@ -201,7 +202,6 @@ SENSOR_TYPES: tuple[WeatherSensorEntityDescription, ...] = ( device_class=SensorDeviceClass.HUMIDITY, icon="mdi:weather-sunny", translation_key=CH2_HUMIDITY, - entity_registry_visible_default=False, value_fn=lambda data: cast(int, data), ), ) @@ -215,10 +215,18 @@ async def async_setup_entry( """Set up Weather Station sensors.""" coordinator: WeatherDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] - sensors = [] - for description in SENSOR_TYPES: - sensors.append(WeatherSensor(hass, description, coordinator)) - async_add_entities(sensors) + + sensors_to_load: list = [] + sensors: list = [] + + # Check if we have some sensors to load. + if sensors_to_load := config_entry.options.get(SENSORS_TO_LOAD): + sensors = [ + WeatherSensor(hass, description, coordinator) + for description in SENSOR_TYPES + if description.key in sensors_to_load + ] + async_add_entities(sensors) class WeatherSensor( @@ -226,7 +234,6 @@ class WeatherSensor( ): """Implementation of Weather Sensor entity.""" - entity_description: WeatherSensorEntityDescription _attr_has_entity_name = True _attr_should_poll = False @@ -244,20 +251,18 @@ class WeatherSensor( self._attr_unique_id = description.key self._data = None - async def async_added_to_hass(self) -> None: - """Handle disabled entities that has previous data.""" + # async def async_added_to_hass(self) -> None: + # """Handle listeners to reloaded sensors.""" - await super().async_added_to_hass() + # await super().async_added_to_hass() - self.coordinator.async_add_listener(self._handle_coordinator_update) + # self.coordinator.async_add_listener(self._handle_coordinator_update) - prev_state_data = await self.async_get_last_sensor_data() - prev_state = await self.async_get_last_state() - if not prev_state: - return - self._data = prev_state_data.native_value - if not self.entity_registry_visible_default: - self.entity_registry_visible_default = True + # # prev_state_data = await self.async_get_last_sensor_data() + # # prev_state = await self.async_get_last_state() + # # if not prev_state: + # # return + # # self._data = prev_state_data.native_value @callback def _handle_coordinator_update(self) -> None: @@ -272,9 +277,14 @@ class WeatherSensor( return self.entity_description.value_fn(self._data) @property - def state_class(self) -> str: - """Return stateClass.""" - return str(self.entity_description.state_class) + def suggested_entity_id(self) -> str: + """Return name.""" + return generate_entity_id("sensor.{}", self.entity_description.key) + + # @property + # def translation_key(self) -> str: + # """"Returns translation key.""" + # return self.entity_description.translation_key @property def device_info(self) -> DeviceInfo: diff --git a/custom_components/sws12500/utils.py b/custom_components/sws12500/utils.py index 274d574..aad89c0 100644 --- a/custom_components/sws12500/utils.py +++ b/custom_components/sws12500/utils.py @@ -3,16 +3,14 @@ import logging from homeassistant.config_entries import ConfigEntry -from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er -from .const import DISABLED_BY_DEFAULT, DOMAIN, REMAP_ITEMS +from .const import DEV_DBG, REMAP_ITEMS, SENSORS_TO_LOAD _LOGGER = logging.getLogger(__name__) -def update_options( +async def update_options( hass: HomeAssistant, entry: ConfigEntry, update_key, update_value ) -> None: """Update config.options entry.""" @@ -23,7 +21,7 @@ def update_options( conf[update_key] = update_value - hass.config_entries.async_update_entry(entry, options=conf) + return hass.config_entries.async_update_entry(entry, options=conf) def anonymize(data): @@ -47,37 +45,26 @@ def remap_items(entities): return items -async def check_disabled(hass: HomeAssistant, items, log: bool = False): - """Check if we have data for disabed sensors. +def check_disabled(hass: HomeAssistant, items, config_entry: ConfigEntry) -> list | None: + """Check if we have data for unloaded sensors. - If so, then enable senosor. + If so, then add sensor to load queue. - Returns True if sensor found else False + Returns list of found sensors or None """ - _ER = er.async_get(hass) - - eid: str = None + log: bool = config_entry.options.get(DEV_DBG) entityFound: bool = False + loaded_sensors: list = config_entry.options.get(SENSORS_TO_LOAD) if config_entry.options.get(SENSORS_TO_LOAD) else [] - for disabled in DISABLED_BY_DEFAULT: + for item in items: if log: - _LOGGER.info("Checking %s", disabled) - - if disabled in items: - eid = _ER.async_get_entity_id(Platform.SENSOR, DOMAIN, disabled) - is_disabled = _ER.entities[eid].hidden + _LOGGER.info("Checking %s", item) + if item not in loaded_sensors: + loaded_sensors.append(item) + entityFound = True if log: - _LOGGER.info("Found sensor %s", eid) + _LOGGER.info("Add sensor (%s) to loading queue", item) - if is_disabled: - if log: - _LOGGER.info("Sensor %s is hidden. Making visible", eid) - _ER.async_update_entity(eid, hidden_by=None) - entityFound = True - - elif not is_disabled and log: - _LOGGER.info("Sensor %s is visible.", eid) - - return entityFound + return loaded_sensors if entityFound else None From 70bfd562689dac72aeb063289a100b5e22113e88 Mon Sep 17 00:00:00 2001 From: schizza Date: Wed, 17 Apr 2024 13:12:47 +0200 Subject: [PATCH 2/2] Ability to add newly discovered sensors. * Add discovered sensors to loading queue. * Notify users that we have new sensors and we need to restart Home Assistant --- custom_components/sws12500/__init__.py | 52 +++++++----- custom_components/sws12500/manifest.json | 2 +- custom_components/sws12500/property.py | 46 ----------- custom_components/sws12500/sensor.py | 25 +++--- custom_components/sws12500/strings.json | 6 ++ .../sws12500/translations/cs.json | 6 ++ .../sws12500/translations/en.json | 6 ++ custom_components/sws12500/utils.py | 80 ++++++++++++++++--- 8 files changed, 132 insertions(+), 91 deletions(-) delete mode 100644 custom_components/sws12500/property.py diff --git a/custom_components/sws12500/__init__.py b/custom_components/sws12500/__init__.py index ef89a2b..fa5d886 100644 --- a/custom_components/sws12500/__init__.py +++ b/custom_components/sws12500/__init__.py @@ -19,7 +19,15 @@ from .const import ( SENSORS_TO_LOAD, WINDY_ENABLED, ) -from .utils import anonymize, check_disabled, remap_items, update_options +from .utils import ( + anonymize, + check_disabled, + loaded_sensors, + remap_items, + translated_notification, + translations, + update_options, +) from .windy_func import WindyPush _LOGGER = logging.getLogger(__name__) @@ -65,10 +73,22 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator): remaped_items = remap_items(data) if sensors := check_disabled(self.hass, remaped_items, self.config): - await update_options( - self.hass, self.config_entry, SENSORS_TO_LOAD, sensors + translate_sensors = [ + await translations(self.hass, DOMAIN, f"sensor.{t_key}", key="name", category="entity") + for t_key in sensors + ] + human_readable = "\n".join(translate_sensors) + + await translated_notification( + self.hass, + DOMAIN, + "added", + {"added_sensors": f"{human_readable}\n"}, ) - # await self.hass.config_entries.async_reload(self.config.entry_id) + if _loaded_sensors := loaded_sensors(self.config_entry): + sensors.extend(_loaded_sensors) + await update_options(self.hass, self.config_entry, SENSORS_TO_LOAD, sensors) + await self.hass.config_entries.async_reload(self.config.entry_id) self.async_set_updated_data(remaped_items) @@ -131,11 +151,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True -async def update_listener(hass: HomeAssistant, entry: ConfigEntry): - """Update setup listener.""" - await hass.config_entries.async_reload(entry.entry_id) - _LOGGER.info("Settings updated") +async def update_listener(hass: HomeAssistant, entry: ConfigEntry): + """Update setup listener.""" + # Disabled as on fire async_reload, integration stops writing data, + # and we don't need to reload config entry for proper work. + + # await hass.config_entries.async_reload(entry.entry_id) + + _LOGGER.info("Settings updated") async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -147,15 +171,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: unregister_path(hass) return _ok - - -# async def async_setup(hass: HomeAssistant, entry: ConfigEntry) -> bool: -# """Set up the component. - -# This component can only be configured through the Integrations UI. -# """ -# hass.data.setdefault(DOMAIN, {}) - -# await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - -# return True diff --git a/custom_components/sws12500/manifest.json b/custom_components/sws12500/manifest.json index 5a21dd4..3cd2411 100644 --- a/custom_components/sws12500/manifest.json +++ b/custom_components/sws12500/manifest.json @@ -10,6 +10,6 @@ "iot_class": "local_push", "requirements": [], "ssdp": [], - "version": "0.1.2", + "version": "0.1.3", "zeroconf": [] } diff --git a/custom_components/sws12500/property.py b/custom_components/sws12500/property.py deleted file mode 100644 index eae6870..0000000 --- a/custom_components/sws12500/property.py +++ /dev/null @@ -1,46 +0,0 @@ - @property - def translation_key(self): - """Return translation key.""" - return self.entity_description.translation_key - - @property - def device_class(self): - """Return device class.""" - return self.entity_description.device_class - - @property - def name(self) -> str: - """Return the name of the switch.""" - return str(self.entity_description.name) - - @property - def unique_id(self) -> str: - """Return a unique, Home Assistant friendly identifier for this entity.""" - return self.entity_description.key - - @property - def native_value(self): - """Return value of entity.""" - return self._state - - @property - def icon(self) -> str: - """Return icon of entity.""" - return str(self.entity_description.icon) - - @property - def native_unit_of_measurement(self) -> str: - """Return unit of measurement.""" - return str(self.entity_description.native_unit_of_measurement) - - @property - def state_class(self) -> str: - """Return stateClass.""" - - return str(self.entity_description.state_class) - - @property - def suggested_unit_of_measurement(self) -> str: - """Return sugestet_unit_of_measurement.""" - return str(self.entity_description.suggested_unit_of_measurement) - \ No newline at end of file diff --git a/custom_components/sws12500/sensor.py b/custom_components/sws12500/sensor.py index 0c439bd..a8d1c24 100644 --- a/custom_components/sws12500/sensor.py +++ b/custom_components/sws12500/sensor.py @@ -251,24 +251,26 @@ class WeatherSensor( self._attr_unique_id = description.key self._data = None - # async def async_added_to_hass(self) -> None: - # """Handle listeners to reloaded sensors.""" + async def async_added_to_hass(self) -> None: + """Handle listeners to reloaded sensors.""" - # await super().async_added_to_hass() + await super().async_added_to_hass() - # self.coordinator.async_add_listener(self._handle_coordinator_update) + self.coordinator.async_add_listener(self._handle_coordinator_update) - # # prev_state_data = await self.async_get_last_sensor_data() - # # prev_state = await self.async_get_last_state() - # # if not prev_state: - # # return - # # self._data = prev_state_data.native_value + # prev_state_data = await self.async_get_last_sensor_data() + # prev_state = await self.async_get_last_state() + # if not prev_state: + # return + # self._data = prev_state_data.native_value @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" self._data = self.coordinator.data.get(self.entity_description.key) + super()._handle_coordinator_update() + self.async_write_ha_state() @property @@ -281,11 +283,6 @@ class WeatherSensor( """Return name.""" return generate_entity_id("sensor.{}", self.entity_description.key) - # @property - # def translation_key(self) -> str: - # """"Returns translation key.""" - # return self.entity_description.translation_key - @property def device_info(self) -> DeviceInfo: """Device info.""" diff --git a/custom_components/sws12500/strings.json b/custom_components/sws12500/strings.json index 3b0be99..501fe5f 100644 --- a/custom_components/sws12500/strings.json +++ b/custom_components/sws12500/strings.json @@ -78,5 +78,11 @@ "ch2_temp": { "name": "Channel 2 temperature" }, "ch2_humidity": { "name": "Channel 2 humidity" } } + }, + "notify": { + "added": { + "title": "New sensors for SWS 12500 found.", + "message": "{added_sensors}\nHomeAssistant needs to be restarted for proper integreation run." + } } } diff --git a/custom_components/sws12500/translations/cs.json b/custom_components/sws12500/translations/cs.json index dbe7ffa..45579cc 100644 --- a/custom_components/sws12500/translations/cs.json +++ b/custom_components/sws12500/translations/cs.json @@ -77,5 +77,11 @@ "ch2_temp": { "name": "Teplota senzoru 2" }, "ch2_humidity": { "name": "Vlhkost sensoru 2" } } + }, + "notify": { + "added": { + "title": "Nalezeny nové senzory pro SWS 12500.", + "message": "{added_sensors}\nJe třeba restartovat Home Assistenta pro správnou funkci komponenty." + } } } diff --git a/custom_components/sws12500/translations/en.json b/custom_components/sws12500/translations/en.json index 3b0be99..501fe5f 100644 --- a/custom_components/sws12500/translations/en.json +++ b/custom_components/sws12500/translations/en.json @@ -78,5 +78,11 @@ "ch2_temp": { "name": "Channel 2 temperature" }, "ch2_humidity": { "name": "Channel 2 humidity" } } + }, + "notify": { + "added": { + "title": "New sensors for SWS 12500 found.", + "message": "{added_sensors}\nHomeAssistant needs to be restarted for proper integreation run." + } } } diff --git a/custom_components/sws12500/utils.py b/custom_components/sws12500/utils.py index aad89c0..e560723 100644 --- a/custom_components/sws12500/utils.py +++ b/custom_components/sws12500/utils.py @@ -2,23 +2,76 @@ import logging +from homeassistant.components import persistent_notification from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers.translation import async_get_translations from .const import DEV_DBG, REMAP_ITEMS, SENSORS_TO_LOAD _LOGGER = logging.getLogger(__name__) +async def translations( + hass: HomeAssistant, + translation_domain: str, + translation_key: str, + *, + key: str = "message", + category: str = "notify" +) -> str: + """Get translated keys for domain.""" + + localize_key = f"component.{translation_domain}.{category}.{translation_key}.{key}" + + language = hass.config.language + + _translations = await async_get_translations( + hass, language, category, [translation_domain] + ) + if localize_key in _translations: + return _translations[localize_key] + +async def translated_notification( + hass: HomeAssistant, + translation_domain: str, + translation_key: str, + translation_placeholders: dict[str, str] | None = None, + notification_id: str | None = None, + *, + key: str = "message", + category: str = "notify" +) -> str: + """Translate notification.""" + + localize_key = f"component.{translation_domain}.{category}.{translation_key}.{key}" + + localize_title = f"component.{translation_domain}.{category}.{translation_key}.title" + + language = hass.config.language + + _translations = await async_get_translations( + hass, language, category, [translation_domain] + ) + if localize_key in _translations: + if not translation_placeholders: + persistent_notification.async_create( + hass, + _translations[localize_key], + _translations[localize_title], + notification_id, + ) + else: + message = _translations[localize_key].format(**translation_placeholders) + persistent_notification.async_create( + hass, message, _translations[localize_title], notification_id + ) + async def update_options( hass: HomeAssistant, entry: ConfigEntry, update_key, update_value ) -> None: """Update config.options entry.""" - conf = {} - - for k in entry.options: - conf[k] = entry.options[k] - + conf = {**entry.options} conf[update_key] = update_value return hass.config_entries.async_update_entry(entry, options=conf) @@ -44,8 +97,14 @@ def remap_items(entities): return items +def loaded_sensors(config_entry: ConfigEntry) -> list | None: + """Get loaded sensors.""" -def check_disabled(hass: HomeAssistant, items, config_entry: ConfigEntry) -> list | None: + return config_entry.options.get(SENSORS_TO_LOAD) if config_entry.options.get(SENSORS_TO_LOAD) else [] + +def check_disabled( + hass: HomeAssistant, items, config_entry: ConfigEntry +) -> list | None: """Check if we have data for unloaded sensors. If so, then add sensor to load queue. @@ -55,16 +114,17 @@ def check_disabled(hass: HomeAssistant, items, config_entry: ConfigEntry) -> lis log: bool = config_entry.options.get(DEV_DBG) entityFound: bool = False - loaded_sensors: list = config_entry.options.get(SENSORS_TO_LOAD) if config_entry.options.get(SENSORS_TO_LOAD) else [] + _loaded_sensors = loaded_sensors(config_entry) + missing_sensors: list = [] for item in items: if log: _LOGGER.info("Checking %s", item) - if item not in loaded_sensors: - loaded_sensors.append(item) + if item not in _loaded_sensors: + missing_sensors.append(item) entityFound = True if log: _LOGGER.info("Add sensor (%s) to loading queue", item) - return loaded_sensors if entityFound else None + return missing_sensors if entityFound else None