diff --git a/custom_components/sws12500/__init__.py b/custom_components/sws12500/__init__.py
index fc9aa31..fa5d886 100644
--- a/custom_components/sws12500/__init__.py
+++ b/custom_components/sws12500/__init__.py
@@ -8,15 +8,30 @@ 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,
+ loaded_sensors,
+ remap_items,
+ translated_notification,
+ translations,
+ update_options,
+)
from .windy_func import WindyPush
_LOGGER = logging.getLogger(__name__)
-PLATFORMS = [Platform.SENSOR]
+PLATFORMS: list[Platform] = [Platform.SENSOR]
class IncorrectDataError(InvalidStateError):
@@ -57,7 +72,23 @@ 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):
+ 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"},
+ )
+ 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)
@@ -77,7 +108,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 +124,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,11 +147,21 @@ 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."""
+ # 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:
"""Unload a config entry."""
@@ -143,12 +171,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
unregister_path(hass)
return _ok
-
-
-async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
- """Set up the component.
-
- This component can only be configured through the Integrations UI.
- """
- hass.data.setdefault(DOMAIN, {})
- 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/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 e696faf..a8d1c24 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
@@ -245,25 +252,25 @@ class WeatherSensor(
self._data = None
async def async_added_to_hass(self) -> None:
- """Handle disabled entities that has previous data."""
+ """Handle listeners to reloaded sensors."""
await super().async_added_to_hass()
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:
"""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
@@ -272,9 +279,9 @@ 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 device_info(self) -> DeviceInfo:
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 274d574..e560723 100644
--- a/custom_components/sws12500/utils.py
+++ b/custom_components/sws12500/utils.py
@@ -2,28 +2,79 @@
import logging
+from homeassistant.components import persistent_notification
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 homeassistant.helpers.translation import async_get_translations
-from .const import DISABLED_BY_DEFAULT, DOMAIN, REMAP_ITEMS
+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."""
-def update_options(
+ 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
- hass.config_entries.async_update_entry(entry, options=conf)
+ return hass.config_entries.async_update_entry(entry, options=conf)
def anonymize(data):
@@ -46,38 +97,34 @@ def remap_items(entities):
return items
+def loaded_sensors(config_entry: ConfigEntry) -> list | None:
+ """Get loaded sensors."""
-async def check_disabled(hass: HomeAssistant, items, log: bool = False):
- """Check if we have data for disabed sensors.
+ return config_entry.options.get(SENSORS_TO_LOAD) if config_entry.options.get(SENSORS_TO_LOAD) else []
- If so, then enable senosor.
+def check_disabled(
+ hass: HomeAssistant, items, config_entry: ConfigEntry
+) -> list | None:
+ """Check if we have data for unloaded sensors.
- Returns True if sensor found else False
+ If so, then add sensor to load queue.
+
+ 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 = loaded_sensors(config_entry)
+ missing_sensors: list = []
- 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:
+ missing_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 missing_sensors if entityFound else None