diff --git a/homeassistant/components/rituals_perfume_genie/__init__.py b/homeassistant/components/rituals_perfume_genie/__init__.py index 7f079c003fc..b607a84b5d1 100644 --- a/homeassistant/components/rituals_perfume_genie/__init__.py +++ b/homeassistant/components/rituals_perfume_genie/__init__.py @@ -1,4 +1,6 @@ """The Rituals Perfume Genie integration.""" +import asyncio + import aiohttp from pyrituals import Account @@ -8,7 +10,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession -from .const import ACCOUNT_HASH, COORDINATORS, DEVICES, DOMAIN +from .const import ACCOUNT_HASH, DOMAIN from .coordinator import RitualsDataUpdateCoordinator PLATFORMS = [ @@ -30,20 +32,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except aiohttp.ClientError as err: raise ConfigEntryNotReady from err - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { - COORDINATORS: {}, - DEVICES: {}, + # Create a coordinator for each diffuser + coordinators = { + diffuser.hublot: RitualsDataUpdateCoordinator(hass, diffuser) + for diffuser in account_devices } - for device in account_devices: - hublot = device.hublot - - coordinator = RitualsDataUpdateCoordinator(hass, device) - await coordinator.async_config_entry_first_refresh() - - hass.data[DOMAIN][entry.entry_id][DEVICES][hublot] = device - hass.data[DOMAIN][entry.entry_id][COORDINATORS][hublot] = coordinator + # Refresh all coordinators + await asyncio.gather( + *[ + coordinator.async_config_entry_first_refresh() + for coordinator in coordinators.values() + ] + ) + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinators await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True diff --git a/homeassistant/components/rituals_perfume_genie/binary_sensor.py b/homeassistant/components/rituals_perfume_genie/binary_sensor.py index 05eac9377ab..95d28c9797b 100644 --- a/homeassistant/components/rituals_perfume_genie/binary_sensor.py +++ b/homeassistant/components/rituals_perfume_genie/binary_sensor.py @@ -1,8 +1,6 @@ """Support for Rituals Perfume Genie binary sensors.""" from __future__ import annotations -from pyrituals import Diffuser - from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, BinarySensorEntity, @@ -12,7 +10,7 @@ from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import COORDINATORS, DEVICES, DOMAIN +from .const import DOMAIN from .coordinator import RitualsDataUpdateCoordinator from .entity import DiffuserEntity @@ -25,13 +23,14 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the diffuser binary sensors.""" - diffusers = hass.data[DOMAIN][config_entry.entry_id][DEVICES] - coordinators = hass.data[DOMAIN][config_entry.entry_id][COORDINATORS] + coordinators: dict[str, RitualsDataUpdateCoordinator] = hass.data[DOMAIN][ + config_entry.entry_id + ] async_add_entities( - DiffuserBatteryChargingBinarySensor(diffuser, coordinators[hublot]) - for hublot, diffuser in diffusers.items() - if diffuser.has_battery + DiffuserBatteryChargingBinarySensor(coordinator) + for coordinator in coordinators.values() + if coordinator.diffuser.has_battery ) @@ -41,13 +40,11 @@ class DiffuserBatteryChargingBinarySensor(DiffuserEntity, BinarySensorEntity): _attr_device_class = BinarySensorDeviceClass.BATTERY_CHARGING _attr_entity_category = EntityCategory.DIAGNOSTIC - def __init__( - self, diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator - ) -> None: + def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None: """Initialize the battery charging binary sensor.""" - super().__init__(diffuser, coordinator, CHARGING_SUFFIX) + super().__init__(coordinator, CHARGING_SUFFIX) @property def is_on(self) -> bool: """Return the state of the battery charging binary sensor.""" - return self._diffuser.charging + return self.coordinator.diffuser.charging diff --git a/homeassistant/components/rituals_perfume_genie/const.py b/homeassistant/components/rituals_perfume_genie/const.py index 05c03419620..35d1c32d306 100644 --- a/homeassistant/components/rituals_perfume_genie/const.py +++ b/homeassistant/components/rituals_perfume_genie/const.py @@ -6,7 +6,4 @@ DOMAIN = "rituals_perfume_genie" ACCOUNT_HASH = "account_hash" -COORDINATORS = "coordinators" -DEVICES = "devices" - UPDATE_INTERVAL = timedelta(minutes=2) diff --git a/homeassistant/components/rituals_perfume_genie/coordinator.py b/homeassistant/components/rituals_perfume_genie/coordinator.py index ac6c252f4d2..b63b28e4de9 100644 --- a/homeassistant/components/rituals_perfume_genie/coordinator.py +++ b/homeassistant/components/rituals_perfume_genie/coordinator.py @@ -14,16 +14,16 @@ _LOGGER = logging.getLogger(__name__) class RitualsDataUpdateCoordinator(DataUpdateCoordinator[None]): """Class to manage fetching Rituals Perfume Genie device data from single endpoint.""" - def __init__(self, hass: HomeAssistant, device: Diffuser) -> None: + def __init__(self, hass: HomeAssistant, diffuser: Diffuser) -> None: """Initialize global Rituals Perfume Genie data updater.""" - self._device = device + self.diffuser = diffuser super().__init__( hass, _LOGGER, - name=f"{DOMAIN}-{device.hublot}", + name=f"{DOMAIN}-{diffuser.hublot}", update_interval=UPDATE_INTERVAL, ) async def _async_update_data(self) -> None: """Fetch data from Rituals.""" - await self._device.update_data() + await self.diffuser.update_data() diff --git a/homeassistant/components/rituals_perfume_genie/entity.py b/homeassistant/components/rituals_perfume_genie/entity.py index fb049842c07..b5e016c2066 100644 --- a/homeassistant/components/rituals_perfume_genie/entity.py +++ b/homeassistant/components/rituals_perfume_genie/entity.py @@ -1,8 +1,6 @@ """Base class for Rituals Perfume Genie diffuser entity.""" from __future__ import annotations -from pyrituals import Diffuser - from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -19,28 +17,25 @@ class DiffuserEntity(CoordinatorEntity[RitualsDataUpdateCoordinator]): def __init__( self, - diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator, entity_suffix: str, ) -> None: """Init from config, hookup diffuser and coordinator.""" super().__init__(coordinator) - self._diffuser = diffuser - - hublot = self._diffuser.hublot - hubname = self._diffuser.name + hublot = coordinator.diffuser.hublot + hubname = coordinator.diffuser.name self._attr_name = f"{hubname}{entity_suffix}" self._attr_unique_id = f"{hublot}{entity_suffix}" self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, hublot)}, manufacturer=MANUFACTURER, - model=MODEL if diffuser.has_battery else MODEL2, + model=MODEL if coordinator.diffuser.has_battery else MODEL2, name=hubname, - sw_version=diffuser.version, + sw_version=coordinator.diffuser.version, ) @property def available(self) -> bool: """Return if the entity is available.""" - return super().available and self._diffuser.is_online + return super().available and self.coordinator.diffuser.is_online diff --git a/homeassistant/components/rituals_perfume_genie/number.py b/homeassistant/components/rituals_perfume_genie/number.py index 232621ff576..3da28f8b6f4 100644 --- a/homeassistant/components/rituals_perfume_genie/number.py +++ b/homeassistant/components/rituals_perfume_genie/number.py @@ -1,14 +1,12 @@ """Support for Rituals Perfume Genie numbers.""" from __future__ import annotations -from pyrituals import Diffuser - from homeassistant.components.number import NumberEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import COORDINATORS, DEVICES, DOMAIN +from .const import DOMAIN from .coordinator import RitualsDataUpdateCoordinator from .entity import DiffuserEntity @@ -24,14 +22,12 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the diffuser numbers.""" - diffusers = hass.data[DOMAIN][config_entry.entry_id][DEVICES] - coordinators = hass.data[DOMAIN][config_entry.entry_id][COORDINATORS] - entities: list[DiffuserEntity] = [] - for hublot, diffuser in diffusers.items(): - coordinator = coordinators[hublot] - entities.append(DiffuserPerfumeAmount(diffuser, coordinator)) - - async_add_entities(entities) + coordinators: dict[str, RitualsDataUpdateCoordinator] = hass.data[DOMAIN][ + config_entry.entry_id + ] + async_add_entities( + DiffuserPerfumeAmount(coordinator) for coordinator in coordinators.values() + ) class DiffuserPerfumeAmount(DiffuserEntity, NumberEntity): @@ -41,16 +37,14 @@ class DiffuserPerfumeAmount(DiffuserEntity, NumberEntity): _attr_native_max_value = MAX_PERFUME_AMOUNT _attr_native_min_value = MIN_PERFUME_AMOUNT - def __init__( - self, diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator - ) -> None: + def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None: """Initialize the diffuser perfume amount number.""" - super().__init__(diffuser, coordinator, PERFUME_AMOUNT_SUFFIX) + super().__init__(coordinator, PERFUME_AMOUNT_SUFFIX) @property def native_value(self) -> int: """Return the current perfume amount.""" - return self._diffuser.perfume_amount + return self.coordinator.diffuser.perfume_amount async def async_set_native_value(self, value: float) -> None: """Set the perfume amount.""" @@ -59,4 +53,4 @@ class DiffuserPerfumeAmount(DiffuserEntity, NumberEntity): f"Can't set the perfume amount to {value}. Perfume amount must be an" " integer." ) - await self._diffuser.set_perfume_amount(int(value)) + await self.coordinator.diffuser.set_perfume_amount(int(value)) diff --git a/homeassistant/components/rituals_perfume_genie/select.py b/homeassistant/components/rituals_perfume_genie/select.py index b7119e6d947..b40c3045577 100644 --- a/homeassistant/components/rituals_perfume_genie/select.py +++ b/homeassistant/components/rituals_perfume_genie/select.py @@ -1,15 +1,13 @@ """Support for Rituals Perfume Genie numbers.""" from __future__ import annotations -from pyrituals import Diffuser - from homeassistant.components.select import SelectEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import AREA_SQUARE_METERS, EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import COORDINATORS, DEVICES, DOMAIN +from .const import DOMAIN from .coordinator import RitualsDataUpdateCoordinator from .entity import DiffuserEntity @@ -22,11 +20,12 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the diffuser select entities.""" - diffusers = hass.data[DOMAIN][config_entry.entry_id][DEVICES] - coordinators = hass.data[DOMAIN][config_entry.entry_id][COORDINATORS] + coordinators: dict[str, RitualsDataUpdateCoordinator] = hass.data[DOMAIN][ + config_entry.entry_id + ] + async_add_entities( - DiffuserRoomSize(diffuser, coordinators[hublot]) - for hublot, diffuser in diffusers.items() + DiffuserRoomSize(coordinator) for coordinator in coordinators.values() ) @@ -38,18 +37,18 @@ class DiffuserRoomSize(DiffuserEntity, SelectEntity): _attr_options = ["15", "30", "60", "100"] _attr_entity_category = EntityCategory.CONFIG - def __init__( - self, diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator - ) -> None: + def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None: """Initialize the diffuser room size select entity.""" - super().__init__(diffuser, coordinator, ROOM_SIZE_SUFFIX) - self._attr_entity_registry_enabled_default = diffuser.has_battery + super().__init__(coordinator, ROOM_SIZE_SUFFIX) + self._attr_entity_registry_enabled_default = ( + self.coordinator.diffuser.has_battery + ) @property def current_option(self) -> str: """Return the diffuser room size.""" - return str(self._diffuser.room_size_square_meter) + return str(self.coordinator.diffuser.room_size_square_meter) async def async_select_option(self, option: str) -> None: """Change the diffuser room size.""" - await self._diffuser.set_room_size_square_meter(int(option)) + await self.coordinator.diffuser.set_room_size_square_meter(int(option)) diff --git a/homeassistant/components/rituals_perfume_genie/sensor.py b/homeassistant/components/rituals_perfume_genie/sensor.py index fa6f75b288f..1f301d3538d 100644 --- a/homeassistant/components/rituals_perfume_genie/sensor.py +++ b/homeassistant/components/rituals_perfume_genie/sensor.py @@ -1,15 +1,13 @@ """Support for Rituals Perfume Genie sensors.""" from __future__ import annotations -from pyrituals import Diffuser - from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import COORDINATORS, DEVICES, DOMAIN +from .const import DOMAIN from .coordinator import RitualsDataUpdateCoordinator from .entity import DiffuserEntity @@ -25,16 +23,21 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the diffuser sensors.""" - diffusers = hass.data[DOMAIN][config_entry.entry_id][DEVICES] - coordinators = hass.data[DOMAIN][config_entry.entry_id][COORDINATORS] + coordinators: dict[str, RitualsDataUpdateCoordinator] = hass.data[DOMAIN][ + config_entry.entry_id + ] + entities: list[DiffuserEntity] = [] - for hublot, diffuser in diffusers.items(): - coordinator = coordinators[hublot] - entities.append(DiffuserPerfumeSensor(diffuser, coordinator)) - entities.append(DiffuserFillSensor(diffuser, coordinator)) - entities.append(DiffuserWifiSensor(diffuser, coordinator)) - if diffuser.has_battery: - entities.append(DiffuserBatterySensor(diffuser, coordinator)) + for coordinator in coordinators.values(): + entities.extend( + [ + DiffuserPerfumeSensor(coordinator), + DiffuserFillSensor(coordinator), + DiffuserWifiSensor(coordinator), + ] + ) + if coordinator.diffuser.has_battery: + entities.append(DiffuserBatterySensor(coordinator)) async_add_entities(entities) @@ -42,45 +45,41 @@ async def async_setup_entry( class DiffuserPerfumeSensor(DiffuserEntity, SensorEntity): """Representation of a diffuser perfume sensor.""" - def __init__( - self, diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator - ) -> None: + def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None: """Initialize the perfume sensor.""" - super().__init__(diffuser, coordinator, PERFUME_SUFFIX) + super().__init__(coordinator, PERFUME_SUFFIX) @property def icon(self) -> str: """Return the perfume sensor icon.""" - if self._diffuser.has_cartridge: + if self.coordinator.diffuser.has_cartridge: return "mdi:tag-text" return "mdi:tag-remove" @property def native_value(self) -> str: """Return the state of the perfume sensor.""" - return self._diffuser.perfume + return self.coordinator.diffuser.perfume class DiffuserFillSensor(DiffuserEntity, SensorEntity): """Representation of a diffuser fill sensor.""" - def __init__( - self, diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator - ) -> None: + def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None: """Initialize the fill sensor.""" - super().__init__(diffuser, coordinator, FILL_SUFFIX) + super().__init__(coordinator, FILL_SUFFIX) @property def icon(self) -> str: """Return the fill sensor icon.""" - if self._diffuser.has_cartridge: + if self.coordinator.diffuser.has_cartridge: return "mdi:beaker" return "mdi:beaker-question" @property def native_value(self) -> str: """Return the state of the fill sensor.""" - return self._diffuser.fill + return self.coordinator.diffuser.fill class DiffuserBatterySensor(DiffuserEntity, SensorEntity): @@ -90,16 +89,14 @@ class DiffuserBatterySensor(DiffuserEntity, SensorEntity): _attr_native_unit_of_measurement = PERCENTAGE _attr_entity_category = EntityCategory.DIAGNOSTIC - def __init__( - self, diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator - ) -> None: + def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None: """Initialize the battery sensor.""" - super().__init__(diffuser, coordinator, BATTERY_SUFFIX) + super().__init__(coordinator, BATTERY_SUFFIX) @property def native_value(self) -> int: """Return the state of the battery sensor.""" - return self._diffuser.battery_percentage + return self.coordinator.diffuser.battery_percentage class DiffuserWifiSensor(DiffuserEntity, SensorEntity): @@ -108,13 +105,11 @@ class DiffuserWifiSensor(DiffuserEntity, SensorEntity): _attr_native_unit_of_measurement = PERCENTAGE _attr_entity_category = EntityCategory.DIAGNOSTIC - def __init__( - self, diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator - ) -> None: + def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None: """Initialize the wifi sensor.""" - super().__init__(diffuser, coordinator, WIFI_SUFFIX) + super().__init__(coordinator, WIFI_SUFFIX) @property def native_value(self) -> int: """Return the state of the wifi sensor.""" - return self._diffuser.wifi_percentage + return self.coordinator.diffuser.wifi_percentage diff --git a/homeassistant/components/rituals_perfume_genie/switch.py b/homeassistant/components/rituals_perfume_genie/switch.py index 3c43a960a2d..92513d9a83b 100644 --- a/homeassistant/components/rituals_perfume_genie/switch.py +++ b/homeassistant/components/rituals_perfume_genie/switch.py @@ -3,14 +3,12 @@ from __future__ import annotations from typing import Any -from pyrituals import Diffuser - from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import COORDINATORS, DEVICES, DOMAIN +from .const import DOMAIN from .coordinator import RitualsDataUpdateCoordinator from .entity import DiffuserEntity @@ -21,14 +19,13 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the diffuser switch.""" - diffusers = hass.data[DOMAIN][config_entry.entry_id][DEVICES] - coordinators = hass.data[DOMAIN][config_entry.entry_id][COORDINATORS] - entities = [] - for hublot, diffuser in diffusers.items(): - coordinator = coordinators[hublot] - entities.append(DiffuserSwitch(diffuser, coordinator)) + coordinators: dict[str, RitualsDataUpdateCoordinator] = hass.data[DOMAIN][ + config_entry.entry_id + ] - async_add_entities(entities) + async_add_entities( + DiffuserSwitch(coordinator) for coordinator in coordinators.values() + ) class DiffuserSwitch(DiffuserEntity, SwitchEntity): @@ -36,27 +33,25 @@ class DiffuserSwitch(DiffuserEntity, SwitchEntity): _attr_icon = "mdi:fan" - def __init__( - self, diffuser: Diffuser, coordinator: RitualsDataUpdateCoordinator - ) -> None: + def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None: """Initialize the diffuser switch.""" - super().__init__(diffuser, coordinator, "") - self._attr_is_on = self._diffuser.is_on + super().__init__(coordinator, "") + self._attr_is_on = self.coordinator.diffuser.is_on async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" - await self._diffuser.turn_on() + await self.coordinator.diffuser.turn_on() self._attr_is_on = True self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" - await self._diffuser.turn_off() + await self.coordinator.diffuser.turn_off() self._attr_is_on = False self.async_write_ha_state() @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" - self._attr_is_on = self._diffuser.is_on + self._attr_is_on = self.coordinator.diffuser.is_on self.async_write_ha_state() diff --git a/tests/components/rituals_perfume_genie/test_switch.py b/tests/components/rituals_perfume_genie/test_switch.py index cb688f528f9..7f33e238118 100644 --- a/tests/components/rituals_perfume_genie/test_switch.py +++ b/tests/components/rituals_perfume_genie/test_switch.py @@ -2,7 +2,7 @@ from __future__ import annotations from homeassistant.components.homeassistant import SERVICE_UPDATE_ENTITY -from homeassistant.components.rituals_perfume_genie.const import COORDINATORS, DOMAIN +from homeassistant.components.rituals_perfume_genie.const import DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ( ATTR_ENTITY_ID, @@ -47,7 +47,7 @@ async def test_switch_handle_coordinator_update(hass: HomeAssistant) -> None: diffuser = mock_diffuser_v1_battery_cartridge() await init_integration(hass, config_entry, [diffuser]) await async_setup_component(hass, "homeassistant", {}) - coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATORS]["lot123v1"] + coordinator = hass.data[DOMAIN][config_entry.entry_id]["lot123v1"] diffuser.is_on = False state = hass.states.get("switch.genie")