diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index e46d5a81c0e..1d59816c661 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -36,10 +36,10 @@ from .const import ( RPC_POLL, ) from .coordinator import ( - BlockDeviceWrapper, - RpcDeviceWrapper, - RpcPollingWrapper, - ShellyDeviceRestWrapper, + ShellyBlockCoordinator, + ShellyRestCoordinator, + ShellyRpcCoordinator, + ShellyRpcPollingCoordinator, ) from .utils import get_block_device_sleep_period, get_coap_context, get_device_entry_gen @@ -200,17 +200,17 @@ def async_block_device_setup( hass: HomeAssistant, entry: ConfigEntry, device: BlockDevice ) -> None: """Set up a block based device that is online.""" - device_wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][ + block_coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][ BLOCK - ] = BlockDeviceWrapper(hass, entry, device) - device_wrapper.async_setup() + ] = ShellyBlockCoordinator(hass, entry, device) + block_coordinator.async_setup() platforms = BLOCK_SLEEPING_PLATFORMS if not entry.data.get(CONF_SLEEP_PERIOD): hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][ REST - ] = ShellyDeviceRestWrapper(hass, device, entry) + ] = ShellyRestCoordinator(hass, device, entry) platforms = BLOCK_PLATFORMS hass.config_entries.async_setup_platforms(entry, platforms) @@ -237,14 +237,14 @@ async def async_setup_rpc_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool except (AuthRequired, InvalidAuthError) as err: raise ConfigEntryAuthFailed from err - device_wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][ + rpc_coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][ RPC - ] = RpcDeviceWrapper(hass, entry, device) - device_wrapper.async_setup() + ] = ShellyRpcCoordinator(hass, entry, device) + rpc_coordinator.async_setup() - hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][RPC_POLL] = RpcPollingWrapper( - hass, entry, device - ) + hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][ + RPC_POLL + ] = ShellyRpcPollingCoordinator(hass, entry, device) hass.config_entries.async_setup_platforms(entry, RPC_PLATFORMS) @@ -265,7 +265,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: device = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id].get(DEVICE) if device is not None: - # If device is present, device wrapper is not setup yet + # If device is present, block coordinator is not setup yet device.shutdown() return True @@ -283,10 +283,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -def get_block_device_wrapper( +def get_block_device_coordinator( hass: HomeAssistant, device_id: str -) -> BlockDeviceWrapper | None: - """Get a Shelly block device wrapper for the given device id.""" +) -> ShellyBlockCoordinator | None: + """Get a Shelly block device coordinator for the given device id.""" if not hass.data.get(DOMAIN): return None @@ -296,16 +296,18 @@ def get_block_device_wrapper( if not hass.data[DOMAIN][DATA_CONFIG_ENTRY].get(config_entry): continue - if wrapper := hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry].get(BLOCK): - return cast(BlockDeviceWrapper, wrapper) + if coordinator := hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry].get( + BLOCK + ): + return cast(ShellyBlockCoordinator, coordinator) return None -def get_rpc_device_wrapper( +def get_rpc_device_coordinator( hass: HomeAssistant, device_id: str -) -> RpcDeviceWrapper | None: - """Get a Shelly RPC device wrapper for the given device id.""" +) -> ShellyRpcCoordinator | None: + """Get a Shelly RPC device coordinator for the given device id.""" if not hass.data.get(DOMAIN): return None @@ -315,7 +317,9 @@ def get_rpc_device_wrapper( if not hass.data[DOMAIN][DATA_CONFIG_ENTRY].get(config_entry): continue - if wrapper := hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry].get(RPC): - return cast(RpcDeviceWrapper, wrapper) + if coordinator := hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry].get( + RPC + ): + return cast(ShellyRpcCoordinator, coordinator) return None diff --git a/homeassistant/components/shelly/button.py b/homeassistant/components/shelly/button.py index 144b100e8eb..48213d706ef 100644 --- a/homeassistant/components/shelly/button.py +++ b/homeassistant/components/shelly/button.py @@ -15,10 +15,11 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import slugify -from . import BlockDeviceWrapper, RpcDeviceWrapper from .const import BLOCK, DATA_CONFIG_ENTRY, DOMAIN, RPC, SHELLY_GAS_MODELS +from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator from .utils import get_block_device_name, get_device_entry_gen, get_rpc_device_name @@ -42,31 +43,31 @@ BUTTONS: Final = [ name="Reboot", device_class=ButtonDeviceClass.RESTART, entity_category=EntityCategory.CONFIG, - press_action=lambda wrapper: wrapper.device.trigger_reboot(), + press_action=lambda coordinator: coordinator.device.trigger_reboot(), ), ShellyButtonDescription( key="self_test", name="Self Test", icon="mdi:progress-wrench", entity_category=EntityCategory.DIAGNOSTIC, - press_action=lambda wrapper: wrapper.device.trigger_shelly_gas_self_test(), - supported=lambda wrapper: wrapper.device.model in SHELLY_GAS_MODELS, + press_action=lambda coordinator: coordinator.device.trigger_shelly_gas_self_test(), + supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS, ), ShellyButtonDescription( key="mute", name="Mute", icon="mdi:volume-mute", entity_category=EntityCategory.CONFIG, - press_action=lambda wrapper: wrapper.device.trigger_shelly_gas_mute(), - supported=lambda wrapper: wrapper.device.model in SHELLY_GAS_MODELS, + press_action=lambda coordinator: coordinator.device.trigger_shelly_gas_mute(), + supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS, ), ShellyButtonDescription( key="unmute", name="Unmute", icon="mdi:volume-high", entity_category=EntityCategory.CONFIG, - press_action=lambda wrapper: wrapper.device.trigger_shelly_gas_unmute(), - supported=lambda wrapper: wrapper.device.model in SHELLY_GAS_MODELS, + press_action=lambda coordinator: coordinator.device.trigger_shelly_gas_unmute(), + supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS, ), ] @@ -77,54 +78,54 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set buttons for device.""" - wrapper: RpcDeviceWrapper | BlockDeviceWrapper | None = None + coordinator: ShellyRpcCoordinator | ShellyBlockCoordinator | None = None if get_device_entry_gen(config_entry) == 2: - if rpc_wrapper := hass.data[DOMAIN][DATA_CONFIG_ENTRY][ + if rpc_coordinator := hass.data[DOMAIN][DATA_CONFIG_ENTRY][ config_entry.entry_id ].get(RPC): - wrapper = cast(RpcDeviceWrapper, rpc_wrapper) + coordinator = cast(ShellyRpcCoordinator, rpc_coordinator) else: - if block_wrapper := hass.data[DOMAIN][DATA_CONFIG_ENTRY][ + if block_coordinator := hass.data[DOMAIN][DATA_CONFIG_ENTRY][ config_entry.entry_id ].get(BLOCK): - wrapper = cast(BlockDeviceWrapper, block_wrapper) + coordinator = cast(ShellyBlockCoordinator, block_coordinator) - if wrapper is not None: + if coordinator is not None: entities = [] for button in BUTTONS: - if not button.supported(wrapper): + if not button.supported(coordinator): continue - entities.append(ShellyButton(wrapper, button)) + entities.append(ShellyButton(coordinator, button)) async_add_entities(entities) -class ShellyButton(ButtonEntity): +class ShellyButton(CoordinatorEntity, ButtonEntity): """Defines a Shelly base button.""" entity_description: ShellyButtonDescription def __init__( self, - wrapper: RpcDeviceWrapper | BlockDeviceWrapper, + coordinator: ShellyRpcCoordinator | ShellyBlockCoordinator, description: ShellyButtonDescription, ) -> None: """Initialize Shelly button.""" + super().__init__(coordinator) self.entity_description = description - self.wrapper = wrapper - if isinstance(wrapper, RpcDeviceWrapper): - device_name = get_rpc_device_name(wrapper.device) + if isinstance(coordinator, ShellyRpcCoordinator): + device_name = get_rpc_device_name(coordinator.device) else: - device_name = get_block_device_name(wrapper.device) + device_name = get_block_device_name(coordinator.device) self._attr_name = f"{device_name} {description.name}" self._attr_unique_id = slugify(self._attr_name) self._attr_device_info = DeviceInfo( - connections={(CONNECTION_NETWORK_MAC, wrapper.mac)} + connections={(CONNECTION_NETWORK_MAC, coordinator.mac)} ) async def async_press(self) -> None: """Triggers the Shelly button press service.""" - await self.entity_description.press_action(self.wrapper) + await self.entity_description.press_action(self.coordinator) diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index f98c048d569..5ab5b728aa4 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -20,12 +20,12 @@ from homeassistant.components.climate import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.core import HomeAssistant, State, callback -from homeassistant.helpers import device_registry, entity_registry, update_coordinator +from homeassistant.helpers import device_registry, entity_registry from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import BlockDeviceWrapper from .const import ( AIOSHELLY_DEVICE_TIMEOUT_SEC, BLOCK, @@ -34,6 +34,7 @@ from .const import ( LOGGER, SHTRV_01_TEMPERATURE_SETTINGS, ) +from .coordinator import ShellyBlockCoordinator from .utils import get_device_entry_gen @@ -47,37 +48,41 @@ async def async_setup_entry( if get_device_entry_gen(config_entry) == 2: return - wrapper: BlockDeviceWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ + coordinator: ShellyBlockCoordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ config_entry.entry_id ][BLOCK] - if wrapper.device.initialized: - async_setup_climate_entities(async_add_entities, wrapper) + if coordinator.device.initialized: + async_setup_climate_entities(async_add_entities, coordinator) else: - async_restore_climate_entities(hass, config_entry, async_add_entities, wrapper) + async_restore_climate_entities( + hass, config_entry, async_add_entities, coordinator + ) @callback def async_setup_climate_entities( async_add_entities: AddEntitiesCallback, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, ) -> None: """Set up online climate devices.""" device_block: Block | None = None sensor_block: Block | None = None - assert wrapper.device.blocks + assert coordinator.device.blocks - for block in wrapper.device.blocks: + for block in coordinator.device.blocks: if block.type == "device": device_block = block if hasattr(block, "targetTemp"): sensor_block = block if sensor_block and device_block: - LOGGER.debug("Setup online climate device %s", wrapper.name) - async_add_entities([BlockSleepingClimate(wrapper, sensor_block, device_block)]) + LOGGER.debug("Setup online climate device %s", coordinator.name) + async_add_entities( + [BlockSleepingClimate(coordinator, sensor_block, device_block)] + ) @callback @@ -85,7 +90,7 @@ def async_restore_climate_entities( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, ) -> None: """Restore sleeping climate devices.""" @@ -99,16 +104,14 @@ def async_restore_climate_entities( if entry.domain != CLIMATE_DOMAIN: continue - LOGGER.debug("Setup sleeping climate device %s", wrapper.name) + LOGGER.debug("Setup sleeping climate device %s", coordinator.name) LOGGER.debug("Found entry %s [%s]", entry.original_name, entry.domain) - async_add_entities([BlockSleepingClimate(wrapper, None, None, entry)]) + async_add_entities([BlockSleepingClimate(coordinator, None, None, entry)]) break class BlockSleepingClimate( - update_coordinator.CoordinatorEntity, - RestoreEntity, - ClimateEntity, + CoordinatorEntity[ShellyBlockCoordinator], RestoreEntity, ClimateEntity ): """Representation of a Shelly climate device.""" @@ -124,16 +127,14 @@ class BlockSleepingClimate( def __init__( self, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, sensor_block: Block | None, device_block: Block | None, entry: entity_registry.RegistryEntry | None = None, ) -> None: """Initialize climate.""" + super().__init__(coordinator) - super().__init__(wrapper) - - self.wrapper = wrapper self.block: Block | None = sensor_block self.control_result: dict[str, Any] | None = None self.device_block: Block | None = device_block @@ -142,11 +143,11 @@ class BlockSleepingClimate( self._preset_modes: list[str] = [] if self.block is not None and self.device_block is not None: - self._unique_id = f"{self.wrapper.mac}-{self.block.description}" + self._unique_id = f"{self.coordinator.mac}-{self.block.description}" assert self.block.channel self._preset_modes = [ PRESET_NONE, - *wrapper.device.settings["thermostats"][int(self.block.channel)][ + *coordinator.device.settings["thermostats"][int(self.block.channel)][ "schedule_profile_names" ], ] @@ -163,7 +164,7 @@ class BlockSleepingClimate( @property def name(self) -> str: """Name of entity.""" - return self.wrapper.name + return self.coordinator.name @property def target_temperature(self) -> float | None: @@ -184,7 +185,7 @@ class BlockSleepingClimate( """Device availability.""" if self.device_block is not None: return not cast(bool, self.device_block.valveError) - return self.wrapper.last_update_success + return self.coordinator.last_update_success @property def hvac_mode(self) -> HVACMode: @@ -229,7 +230,9 @@ class BlockSleepingClimate( def device_info(self) -> DeviceInfo: """Device info.""" return { - "connections": {(device_registry.CONNECTION_NETWORK_MAC, self.wrapper.mac)} + "connections": { + (device_registry.CONNECTION_NETWORK_MAC, self.coordinator.mac) + } } def _check_is_off(self) -> bool: @@ -244,7 +247,7 @@ class BlockSleepingClimate( LOGGER.debug("Setting state for entity %s, state: %s", self.name, kwargs) try: async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): - return await self.wrapper.device.http_request( + return await self.coordinator.device.http_request( "get", f"thermostat/{self._channel}", kwargs ) except (asyncio.TimeoutError, OSError) as err: @@ -254,7 +257,7 @@ class BlockSleepingClimate( kwargs, repr(err), ) - self.wrapper.last_update_success = False + self.coordinator.last_update_success = False return None async def async_set_temperature(self, **kwargs: Any) -> None: @@ -302,13 +305,13 @@ class BlockSleepingClimate( @callback def _handle_coordinator_update(self) -> None: """Handle device update.""" - if not self.wrapper.device.initialized: + if not self.coordinator.device.initialized: self.async_write_ha_state() return - assert self.wrapper.device.blocks + assert self.coordinator.device.blocks - for block in self.wrapper.device.blocks: + for block in self.coordinator.device.blocks: if block.type == "device": self.device_block = block if hasattr(block, "targetTemp"): @@ -322,11 +325,11 @@ class BlockSleepingClimate( try: self._preset_modes = [ PRESET_NONE, - *self.wrapper.device.settings["thermostats"][ + *self.coordinator.device.settings["thermostats"][ int(self.block.channel) ]["schedule_profile_names"], ] except AuthRequired: - self.wrapper.entry.async_start_reauth(self.hass) + self.coordinator.entry.async_start_reauth(self.hass) else: self.async_write_ha_state() diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 02a4e6ffba1..db485167fe3 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -14,8 +14,9 @@ import async_timeout from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_DEVICE_ID, CONF_HOST, EVENT_HOMEASSISTANT_STOP from homeassistant.core import Event, HomeAssistant, callback -from homeassistant.helpers import device_registry, update_coordinator +from homeassistant.helpers import device_registry from homeassistant.helpers.debounce import Debouncer +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import ( AIOSHELLY_DEVICE_TIMEOUT_SEC, @@ -44,13 +45,13 @@ from .const import ( from .utils import device_update_info, get_block_device_name, get_rpc_device_name -class BlockDeviceWrapper(update_coordinator.DataUpdateCoordinator): - """Wrapper for a Shelly block based device with Home Assistant specific functions.""" +class ShellyBlockCoordinator(DataUpdateCoordinator): + """Coordinator for a Shelly block based device.""" def __init__( self, hass: HomeAssistant, entry: ConfigEntry, device: BlockDevice ) -> None: - """Initialize the Shelly device wrapper.""" + """Initialize the Shelly block device coordinator.""" self.device_id: str | None = None if sleep_period := entry.data[CONF_SLEEP_PERIOD]: @@ -186,7 +187,7 @@ class BlockDeviceWrapper(update_coordinator.DataUpdateCoordinator): """Fetch data.""" if sleep_period := self.entry.data.get(CONF_SLEEP_PERIOD): # Sleeping device, no point polling it, just mark it unavailable - raise update_coordinator.UpdateFailed( + raise UpdateFailed( f"Sleeping device did not update within {sleep_period} seconds interval" ) @@ -196,7 +197,7 @@ class BlockDeviceWrapper(update_coordinator.DataUpdateCoordinator): await self.device.update() device_update_info(self.hass, self.device, self.entry) except OSError as err: - raise update_coordinator.UpdateFailed("Error fetching data") from err + raise UpdateFailed("Error fetching data") from err @property def model(self) -> str: @@ -214,7 +215,7 @@ class BlockDeviceWrapper(update_coordinator.DataUpdateCoordinator): return self.device.firmware_version if self.device.initialized else "" def async_setup(self) -> None: - """Set up the wrapper.""" + """Set up the coordinator.""" dev_reg = device_registry.async_get(self.hass) entry = dev_reg.async_get_or_create( config_entry_id=self.entry.entry_id, @@ -265,23 +266,23 @@ class BlockDeviceWrapper(update_coordinator.DataUpdateCoordinator): LOGGER.debug("Result of OTA update call: %s", result) def shutdown(self) -> None: - """Shutdown the wrapper.""" + """Shutdown the coordinator.""" self.device.shutdown() @callback def _handle_ha_stop(self, _event: Event) -> None: """Handle Home Assistant stopping.""" - LOGGER.debug("Stopping BlockDeviceWrapper for %s", self.name) + LOGGER.debug("Stopping block device coordinator for %s", self.name) self.shutdown() -class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator): - """Rest Wrapper for a Shelly device with Home Assistant specific functions.""" +class ShellyRestCoordinator(DataUpdateCoordinator): + """Coordinator for a Shelly REST device.""" def __init__( self, hass: HomeAssistant, device: BlockDevice, entry: ConfigEntry ) -> None: - """Initialize the Shelly device wrapper.""" + """Initialize the Shelly REST device coordinator.""" if ( device.settings["device"]["type"] in BATTERY_DEVICES_WITH_PERMANENT_CONNECTION @@ -316,7 +317,7 @@ class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator): return device_update_info(self.hass, self.device, self.entry) except OSError as err: - raise update_coordinator.UpdateFailed("Error fetching data") from err + raise UpdateFailed("Error fetching data") from err @property def mac(self) -> str: @@ -324,13 +325,13 @@ class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator): return cast(str, self.device.settings["device"]["mac"]) -class RpcDeviceWrapper(update_coordinator.DataUpdateCoordinator): - """Wrapper for a Shelly RPC based device with Home Assistant specific functions.""" +class ShellyRpcCoordinator(DataUpdateCoordinator): + """Coordinator for a Shelly RPC based device.""" def __init__( self, hass: HomeAssistant, entry: ConfigEntry, device: RpcDevice ) -> None: - """Initialize the Shelly device wrapper.""" + """Initialize the Shelly RPC device coordinator.""" self.device_id: str | None = None device_name = get_rpc_device_name(device) if device.initialized else entry.title @@ -413,7 +414,7 @@ class RpcDeviceWrapper(update_coordinator.DataUpdateCoordinator): await self.device.initialize() device_update_info(self.hass, self.device, self.entry) except OSError as err: - raise update_coordinator.UpdateFailed("Device disconnected") from err + raise UpdateFailed("Device disconnected") from err @property def model(self) -> str: @@ -431,7 +432,7 @@ class RpcDeviceWrapper(update_coordinator.DataUpdateCoordinator): return self.device.firmware_version if self.device.initialized else "" def async_setup(self) -> None: - """Set up the wrapper.""" + """Set up the coordinator.""" dev_reg = device_registry.async_get(self.hass) entry = dev_reg.async_get_or_create( config_entry_id=self.entry.entry_id, @@ -482,17 +483,17 @@ class RpcDeviceWrapper(update_coordinator.DataUpdateCoordinator): LOGGER.debug("OTA update call successful") async def shutdown(self) -> None: - """Shutdown the wrapper.""" + """Shutdown the coordinator.""" await self.device.shutdown() async def _handle_ha_stop(self, _event: Event) -> None: """Handle Home Assistant stopping.""" - LOGGER.debug("Stopping RpcDeviceWrapper for %s", self.name) + LOGGER.debug("Stopping RPC device coordinator for %s", self.name) await self.shutdown() -class RpcPollingWrapper(update_coordinator.DataUpdateCoordinator): - """Polling Wrapper for a Shelly RPC based device.""" +class ShellyRpcPollingCoordinator(DataUpdateCoordinator): + """Polling coordinator for a Shelly RPC based device.""" def __init__( self, hass: HomeAssistant, entry: ConfigEntry, device: RpcDevice @@ -513,14 +514,14 @@ class RpcPollingWrapper(update_coordinator.DataUpdateCoordinator): async def _async_update_data(self) -> None: """Fetch data.""" if not self.device.connected: - raise update_coordinator.UpdateFailed("Device disconnected") + raise UpdateFailed("Device disconnected") try: LOGGER.debug("Polling Shelly RPC Device - %s", self.name) async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): await self.device.update_status() except (OSError, aioshelly.exceptions.RPCTimeout) as err: - raise update_coordinator.UpdateFailed("Device disconnected") from err + raise UpdateFailed("Device disconnected") from err @property def model(self) -> str: diff --git a/homeassistant/components/shelly/cover.py b/homeassistant/components/shelly/cover.py index e28fe22a528..e94cd6a9e86 100644 --- a/homeassistant/components/shelly/cover.py +++ b/homeassistant/components/shelly/cover.py @@ -15,8 +15,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import BlockDeviceWrapper, RpcDeviceWrapper from .const import BLOCK, DATA_CONFIG_ENTRY, DOMAIN, RPC +from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator from .entity import ShellyBlockEntity, ShellyRpcEntity from .utils import get_device_entry_gen, get_rpc_key_ids @@ -40,13 +40,13 @@ def async_setup_block_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up cover for device.""" - wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][BLOCK] - blocks = [block for block in wrapper.device.blocks if block.type == "roller"] + coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][BLOCK] + blocks = [block for block in coordinator.device.blocks if block.type == "roller"] if not blocks: return - async_add_entities(BlockShellyCover(wrapper, block) for block in blocks) + async_add_entities(BlockShellyCover(coordinator, block) for block in blocks) @callback @@ -56,14 +56,14 @@ def async_setup_rpc_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up entities for RPC device.""" - wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][RPC] + coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][RPC] - cover_key_ids = get_rpc_key_ids(wrapper.device.status, "cover") + cover_key_ids = get_rpc_key_ids(coordinator.device.status, "cover") if not cover_key_ids: return - async_add_entities(RpcShellyCover(wrapper, id_) for id_ in cover_key_ids) + async_add_entities(RpcShellyCover(coordinator, id_) for id_ in cover_key_ids) class BlockShellyCover(ShellyBlockEntity, CoverEntity): @@ -71,14 +71,14 @@ class BlockShellyCover(ShellyBlockEntity, CoverEntity): _attr_device_class = CoverDeviceClass.SHUTTER - def __init__(self, wrapper: BlockDeviceWrapper, block: Block) -> None: + def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None: """Initialize block cover.""" - super().__init__(wrapper, block) + super().__init__(coordinator, block) self.control_result: dict[str, Any] | None = None self._attr_supported_features: int = ( CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP ) - if self.wrapper.device.settings["rollers"][0]["positioning"]: + if self.coordinator.device.settings["rollers"][0]["positioning"]: self._attr_supported_features |= CoverEntityFeature.SET_POSITION @property @@ -147,9 +147,9 @@ class RpcShellyCover(ShellyRpcEntity, CoverEntity): _attr_device_class = CoverDeviceClass.SHUTTER - def __init__(self, wrapper: RpcDeviceWrapper, id_: int) -> None: + def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) -> None: """Initialize rpc cover.""" - super().__init__(wrapper, f"cover:{id_}") + super().__init__(coordinator, f"cover:{id_}") self._id = id_ self._attr_supported_features: int = ( CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP diff --git a/homeassistant/components/shelly/device_trigger.py b/homeassistant/components/shelly/device_trigger.py index fe253ebacb6..32b3432b0aa 100644 --- a/homeassistant/components/shelly/device_trigger.py +++ b/homeassistant/components/shelly/device_trigger.py @@ -22,7 +22,7 @@ from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.typing import ConfigType -from . import get_block_device_wrapper, get_rpc_device_wrapper +from . import get_block_device_coordinator, get_rpc_device_coordinator from .const import ( ATTR_CHANNEL, ATTR_CLICK_TYPE, @@ -78,23 +78,23 @@ async def async_validate_trigger_config( trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) if config[CONF_TYPE] in RPC_INPUTS_EVENTS_TYPES: - rpc_wrapper = get_rpc_device_wrapper(hass, config[CONF_DEVICE_ID]) - if not rpc_wrapper or not rpc_wrapper.device.initialized: + rpc_coordinator = get_rpc_device_coordinator(hass, config[CONF_DEVICE_ID]) + if not rpc_coordinator or not rpc_coordinator.device.initialized: return config - input_triggers = get_rpc_input_triggers(rpc_wrapper.device) + input_triggers = get_rpc_input_triggers(rpc_coordinator.device) if trigger in input_triggers: return config elif config[CONF_TYPE] in BLOCK_INPUTS_EVENTS_TYPES: - block_wrapper = get_block_device_wrapper(hass, config[CONF_DEVICE_ID]) - if not block_wrapper or not block_wrapper.device.initialized: + block_coordinator = get_block_device_coordinator(hass, config[CONF_DEVICE_ID]) + if not block_coordinator or not block_coordinator.device.initialized: return config - assert block_wrapper.device.blocks + assert block_coordinator.device.blocks - for block in block_wrapper.device.blocks: - input_triggers = get_block_input_triggers(block_wrapper.device, block) + for block in block_coordinator.device.blocks: + input_triggers = get_block_input_triggers(block_coordinator.device, block) if trigger in input_triggers: return config @@ -109,24 +109,24 @@ async def async_get_triggers( """List device triggers for Shelly devices.""" triggers: list[dict[str, str]] = [] - if rpc_wrapper := get_rpc_device_wrapper(hass, device_id): - input_triggers = get_rpc_input_triggers(rpc_wrapper.device) + if rpc_coordinator := get_rpc_device_coordinator(hass, device_id): + input_triggers = get_rpc_input_triggers(rpc_coordinator.device) append_input_triggers(triggers, input_triggers, device_id) return triggers - if block_wrapper := get_block_device_wrapper(hass, device_id): - if block_wrapper.model in SHBTN_MODELS: + if block_coordinator := get_block_device_coordinator(hass, device_id): + if block_coordinator.model in SHBTN_MODELS: input_triggers = get_shbtn_input_triggers() append_input_triggers(triggers, input_triggers, device_id) return triggers - if not block_wrapper.device.initialized: + if not block_coordinator.device.initialized: return triggers - assert block_wrapper.device.blocks + assert block_coordinator.device.blocks - for block in block_wrapper.device.blocks: - input_triggers = get_block_input_triggers(block_wrapper.device, block) + for block in block_coordinator.device.blocks: + input_triggers = get_block_input_triggers(block_coordinator.device, block) append_input_triggers(triggers, input_triggers, device_id) return triggers diff --git a/homeassistant/components/shelly/diagnostics.py b/homeassistant/components/shelly/diagnostics.py index 47dc18d377b..114522e31ac 100644 --- a/homeassistant/components/shelly/diagnostics.py +++ b/homeassistant/components/shelly/diagnostics.py @@ -6,8 +6,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant -from . import BlockDeviceWrapper, RpcDeviceWrapper from .const import BLOCK, DATA_CONFIG_ENTRY, DOMAIN, RPC +from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator TO_REDACT = {CONF_USERNAME, CONF_PASSWORD} @@ -21,21 +21,21 @@ async def async_get_config_entry_diagnostics( device_settings: str | dict = "not initialized" device_status: str | dict = "not initialized" if BLOCK in data: - block_wrapper: BlockDeviceWrapper = data[BLOCK] + block_coordinator: ShellyBlockCoordinator = data[BLOCK] device_info = { - "name": block_wrapper.name, - "model": block_wrapper.model, - "sw_version": block_wrapper.sw_version, + "name": block_coordinator.name, + "model": block_coordinator.model, + "sw_version": block_coordinator.sw_version, } - if block_wrapper.device.initialized: + if block_coordinator.device.initialized: device_settings = { k: v - for k, v in block_wrapper.device.settings.items() + for k, v in block_coordinator.device.settings.items() if k in ["cloud", "coiot"] } device_status = { k: v - for k, v in block_wrapper.device.status.items() + for k, v in block_coordinator.device.status.items() if k in [ "update", @@ -51,19 +51,19 @@ async def async_get_config_entry_diagnostics( ] } else: - rpc_wrapper: RpcDeviceWrapper = data[RPC] + rpc_coordinator: ShellyRpcCoordinator = data[RPC] device_info = { - "name": rpc_wrapper.name, - "model": rpc_wrapper.model, - "sw_version": rpc_wrapper.sw_version, + "name": rpc_coordinator.name, + "model": rpc_coordinator.model, + "sw_version": rpc_coordinator.sw_version, } - if rpc_wrapper.device.initialized: + if rpc_coordinator.device.initialized: device_settings = { - k: v for k, v in rpc_wrapper.device.config.items() if k in ["cloud"] + k: v for k, v in rpc_coordinator.device.config.items() if k in ["cloud"] } device_status = { k: v - for k, v in rpc_wrapper.device.status.items() + for k, v in rpc_coordinator.device.status.items() if k in ["sys", "wifi"] } diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index a38bef54bea..21512238cd4 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -11,23 +11,13 @@ import async_timeout from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import ( - device_registry, - entity, - entity_registry, - update_coordinator, -) +from homeassistant.helpers import device_registry, entity, entity_registry from homeassistant.helpers.entity import DeviceInfo, EntityDescription from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import StateType +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import ( - BlockDeviceWrapper, - RpcDeviceWrapper, - RpcPollingWrapper, - ShellyDeviceRestWrapper, -) from .const import ( AIOSHELLY_DEVICE_TIMEOUT_SEC, BLOCK, @@ -38,6 +28,12 @@ from .const import ( RPC, RPC_POLL, ) +from .coordinator import ( + ShellyBlockCoordinator, + ShellyRestCoordinator, + ShellyRpcCoordinator, + ShellyRpcPollingCoordinator, +) from .utils import ( async_remove_shelly_entity, get_block_entity_name, @@ -58,20 +54,20 @@ def async_setup_entry_attribute_entities( ], ) -> None: """Set up entities for attributes.""" - wrapper: BlockDeviceWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ + coordinator: ShellyBlockCoordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ config_entry.entry_id ][BLOCK] - if wrapper.device.initialized: + if coordinator.device.initialized: async_setup_block_attribute_entities( - hass, async_add_entities, wrapper, sensors, sensor_class + hass, async_add_entities, coordinator, sensors, sensor_class ) else: async_restore_block_attribute_entities( hass, config_entry, async_add_entities, - wrapper, + coordinator, sensors, sensor_class, description_class, @@ -82,16 +78,16 @@ def async_setup_entry_attribute_entities( def async_setup_block_attribute_entities( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, sensors: Mapping[tuple[str, str], BlockEntityDescription], sensor_class: Callable, ) -> None: """Set up entities for block attributes.""" blocks = [] - assert wrapper.device.blocks + assert coordinator.device.blocks - for block in wrapper.device.blocks: + for block in coordinator.device.blocks: for sensor_id in block.sensor_ids: description = sensors.get((block.type, sensor_id)) if description is None: @@ -103,10 +99,10 @@ def async_setup_block_attribute_entities( # Filter and remove entities that according to settings should not create an entity if description.removal_condition and description.removal_condition( - wrapper.device.settings, block + coordinator.device.settings, block ): domain = sensor_class.__module__.split(".")[-1] - unique_id = f"{wrapper.mac}-{block.description}-{sensor_id}" + unique_id = f"{coordinator.mac}-{block.description}-{sensor_id}" async_remove_shelly_entity(hass, domain, unique_id) else: blocks.append((block, sensor_id, description)) @@ -116,7 +112,7 @@ def async_setup_block_attribute_entities( async_add_entities( [ - sensor_class(wrapper, block, sensor_id, description) + sensor_class(coordinator, block, sensor_id, description) for block, sensor_id, description in blocks ] ) @@ -127,7 +123,7 @@ def async_restore_block_attribute_entities( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, sensors: Mapping[tuple[str, str], BlockEntityDescription], sensor_class: Callable, description_class: Callable[ @@ -152,7 +148,7 @@ def async_restore_block_attribute_entities( description = description_class(entry) entities.append( - sensor_class(wrapper, None, attribute, description, entry, sensors) + sensor_class(coordinator, None, attribute, description, entry, sensors) ) if not entities: @@ -170,40 +166,44 @@ def async_setup_entry_rpc( sensor_class: Callable, ) -> None: """Set up entities for REST sensors.""" - wrapper: RpcDeviceWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ + coordinator: ShellyRpcCoordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ config_entry.entry_id ][RPC] - polling_wrapper: RpcPollingWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ - config_entry.entry_id - ][RPC_POLL] + polling_coordinator: ShellyRpcPollingCoordinator = hass.data[DOMAIN][ + DATA_CONFIG_ENTRY + ][config_entry.entry_id][RPC_POLL] entities = [] for sensor_id in sensors: description = sensors[sensor_id] - key_instances = get_rpc_key_instances(wrapper.device.status, description.key) + key_instances = get_rpc_key_instances( + coordinator.device.status, description.key + ) for key in key_instances: # Filter non-existing sensors - if description.sub_key not in wrapper.device.status[ + if description.sub_key not in coordinator.device.status[ key - ] and not description.supported(wrapper.device.status[key]): + ] and not description.supported(coordinator.device.status[key]): continue # Filter and remove entities that according to settings/status should not create an entity if description.removal_condition and description.removal_condition( - wrapper.device.config, wrapper.device.status, key + coordinator.device.config, coordinator.device.status, key ): domain = sensor_class.__module__.split(".")[-1] - unique_id = f"{wrapper.mac}-{key}-{sensor_id}" + unique_id = f"{coordinator.mac}-{key}-{sensor_id}" async_remove_shelly_entity(hass, domain, unique_id) else: - if description.use_polling_wrapper: + if description.use_polling_coordinator: entities.append( - sensor_class(polling_wrapper, key, sensor_id, description) + sensor_class(polling_coordinator, key, sensor_id, description) ) else: - entities.append(sensor_class(wrapper, key, sensor_id, description)) + entities.append( + sensor_class(coordinator, key, sensor_id, description) + ) if not entities: return @@ -220,7 +220,7 @@ def async_setup_entry_rest( sensor_class: Callable, ) -> None: """Set up entities for REST sensors.""" - wrapper: ShellyDeviceRestWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ + coordinator: ShellyRestCoordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ config_entry.entry_id ][REST] @@ -228,7 +228,7 @@ def async_setup_entry_rest( for sensor_id in sensors: description = sensors.get(sensor_id) - if not wrapper.device.settings.get("sleep_mode"): + if not coordinator.device.settings.get("sleep_mode"): entities.append((sensor_id, description)) if not entities: @@ -236,7 +236,7 @@ def async_setup_entry_rest( async_add_entities( [ - sensor_class(wrapper, sensor_id, description) + sensor_class(coordinator, sensor_id, description) for sensor_id, description in entities ] ) @@ -270,7 +270,7 @@ class RpcEntityDescription(EntityDescription, RpcEntityRequiredKeysMixin): available: Callable[[dict], bool] | None = None removal_condition: Callable[[dict, dict, str], bool] | None = None extra_state_attributes: Callable[[dict, dict], dict | None] | None = None - use_polling_wrapper: bool = False + use_polling_coordinator: bool = False supported: Callable = lambda _: False @@ -282,32 +282,32 @@ class RestEntityDescription(EntityDescription): extra_state_attributes: Callable[[dict], dict | None] | None = None -class ShellyBlockEntity(entity.Entity): +class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]): """Helper class to represent a block entity.""" - def __init__(self, wrapper: BlockDeviceWrapper, block: Block) -> None: + def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None: """Initialize Shelly entity.""" - self.wrapper = wrapper + super().__init__(coordinator) self.block = block - self._attr_name = get_block_entity_name(wrapper.device, block) + self._attr_name = get_block_entity_name(coordinator.device, block) self._attr_should_poll = False self._attr_device_info = DeviceInfo( - connections={(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)} + connections={(device_registry.CONNECTION_NETWORK_MAC, coordinator.mac)} ) - self._attr_unique_id = f"{wrapper.mac}-{block.description}" + self._attr_unique_id = f"{coordinator.mac}-{block.description}" @property def available(self) -> bool: """Available.""" - return self.wrapper.last_update_success + return self.coordinator.last_update_success async def async_added_to_hass(self) -> None: """When entity is added to HASS.""" - self.async_on_remove(self.wrapper.async_add_listener(self._update_callback)) + self.async_on_remove(self.coordinator.async_add_listener(self._update_callback)) async def async_update(self) -> None: """Update entity with latest info.""" - await self.wrapper.async_request_refresh() + await self.coordinator.async_request_refresh() @callback def _update_callback(self) -> None: @@ -327,7 +327,7 @@ class ShellyBlockEntity(entity.Entity): kwargs, repr(err), ) - self.wrapper.last_update_success = False + self.coordinator.last_update_success = False return None @@ -336,36 +336,36 @@ class ShellyRpcEntity(entity.Entity): def __init__( self, - wrapper: RpcDeviceWrapper | RpcPollingWrapper, + coordinator: ShellyRpcCoordinator | ShellyRpcPollingCoordinator, key: str, ) -> None: """Initialize Shelly entity.""" - self.wrapper = wrapper + self.coordinator = coordinator self.key = key self._attr_should_poll = False self._attr_device_info = { - "connections": {(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)} + "connections": {(device_registry.CONNECTION_NETWORK_MAC, coordinator.mac)} } - self._attr_unique_id = f"{wrapper.mac}-{key}" - self._attr_name = get_rpc_entity_name(wrapper.device, key) + self._attr_unique_id = f"{coordinator.mac}-{key}" + self._attr_name = get_rpc_entity_name(coordinator.device, key) @property def available(self) -> bool: """Available.""" - return self.wrapper.device.connected + return self.coordinator.device.connected @property def status(self) -> dict: """Device status by entity key.""" - return cast(dict, self.wrapper.device.status[self.key]) + return cast(dict, self.coordinator.device.status[self.key]) async def async_added_to_hass(self) -> None: """When entity is added to HASS.""" - self.async_on_remove(self.wrapper.async_add_listener(self._update_callback)) + self.async_on_remove(self.coordinator.async_add_listener(self._update_callback)) async def async_update(self) -> None: """Update entity with latest info.""" - await self.wrapper.async_request_refresh() + await self.coordinator.async_request_refresh() @callback def _update_callback(self) -> None: @@ -382,7 +382,7 @@ class ShellyRpcEntity(entity.Entity): ) try: async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): - return await self.wrapper.device.call_rpc(method, params) + return await self.coordinator.device.call_rpc(method, params) except asyncio.TimeoutError as err: LOGGER.error( "Call RPC for entity %s failed, method: %s, params: %s, error: %s", @@ -391,7 +391,7 @@ class ShellyRpcEntity(entity.Entity): params, repr(err), ) - self.wrapper.last_update_success = False + self.coordinator.last_update_success = False return None @@ -402,18 +402,20 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity): def __init__( self, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, block: Block, attribute: str, description: BlockEntityDescription, ) -> None: """Initialize sensor.""" - super().__init__(wrapper, block) + super().__init__(coordinator, block) self.attribute = attribute self.entity_description = description self._attr_unique_id: str = f"{super().unique_id}-{self.attribute}" - self._attr_name = get_block_entity_name(wrapper.device, block, description.name) + self._attr_name = get_block_entity_name( + coordinator.device, block, description.name + ) @property def attribute_value(self) -> StateType: @@ -442,40 +444,42 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity): return self.entity_description.extra_state_attributes(self.block) -class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity): +class ShellyRestAttributeEntity(CoordinatorEntity[ShellyBlockCoordinator]): """Class to load info from REST.""" entity_description: RestEntityDescription def __init__( self, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, attribute: str, description: RestEntityDescription, ) -> None: """Initialize sensor.""" - super().__init__(wrapper) - self.wrapper = wrapper + super().__init__(coordinator) + self.block_coordinator = coordinator self.attribute = attribute self.entity_description = description - self._attr_name = get_block_entity_name(wrapper.device, None, description.name) - self._attr_unique_id = f"{wrapper.mac}-{attribute}" + self._attr_name = get_block_entity_name( + coordinator.device, None, description.name + ) + self._attr_unique_id = f"{coordinator.mac}-{attribute}" self._attr_device_info = DeviceInfo( - connections={(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)} + connections={(device_registry.CONNECTION_NETWORK_MAC, coordinator.mac)} ) self._last_value = None @property def available(self) -> bool: """Available.""" - return self.wrapper.last_update_success + return self.block_coordinator.last_update_success @property def attribute_value(self) -> StateType: """Value of sensor.""" if callable(self.entity_description.value): self._last_value = self.entity_description.value( - self.wrapper.device.status, self._last_value + self.block_coordinator.device.status, self._last_value ) return self._last_value @@ -486,7 +490,7 @@ class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity): return None return self.entity_description.extra_state_attributes( - self.wrapper.device.status + self.block_coordinator.device.status ) @@ -497,18 +501,18 @@ class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity): def __init__( self, - wrapper: RpcDeviceWrapper, + coordinator: ShellyRpcCoordinator, key: str, attribute: str, description: RpcEntityDescription, ) -> None: """Initialize sensor.""" - super().__init__(wrapper, key) + super().__init__(coordinator, key) self.attribute = attribute self.entity_description = description self._attr_unique_id = f"{super().unique_id}-{attribute}" - self._attr_name = get_rpc_entity_name(wrapper.device, key, description.name) + self._attr_name = get_rpc_entity_name(coordinator.device, key, description.name) self._last_value = None @property @@ -516,13 +520,13 @@ class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity): """Value of sensor.""" if callable(self.entity_description.value): self._last_value = self.entity_description.value( - self.wrapper.device.status[self.key].get( + self.coordinator.device.status[self.key].get( self.entity_description.sub_key ), self._last_value, ) else: - self._last_value = self.wrapper.device.status[self.key][ + self._last_value = self.coordinator.device.status[self.key][ self.entity_description.sub_key ] @@ -537,7 +541,7 @@ class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity): return available return self.entity_description.available( - self.wrapper.device.status[self.key][self.entity_description.sub_key] + self.coordinator.device.status[self.key][self.entity_description.sub_key] ) @property @@ -546,11 +550,11 @@ class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity): if self.entity_description.extra_state_attributes is None: return None - assert self.wrapper.device.shelly + assert self.coordinator.device.shelly return self.entity_description.extra_state_attributes( - self.wrapper.device.status[self.key][self.entity_description.sub_key], - self.wrapper.device.shelly, + self.coordinator.device.status[self.key][self.entity_description.sub_key], + self.coordinator.device.shelly, ) @@ -560,7 +564,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti # pylint: disable=super-init-not-called def __init__( self, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, block: Block | None, attribute: str, description: BlockEntityDescription, @@ -570,20 +574,22 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti """Initialize the sleeping sensor.""" self.sensors = sensors self.last_state: StateType = None - self.wrapper = wrapper + self.coordinator = coordinator self.attribute = attribute self.block: Block | None = block # type: ignore[assignment] self.entity_description = description self._attr_should_poll = False self._attr_device_info = DeviceInfo( - connections={(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)} + connections={(device_registry.CONNECTION_NETWORK_MAC, coordinator.mac)} ) if block is not None: - self._attr_unique_id = f"{self.wrapper.mac}-{block.description}-{attribute}" + self._attr_unique_id = ( + f"{self.coordinator.mac}-{block.description}-{attribute}" + ) self._attr_name = get_block_entity_name( - self.wrapper.device, block, self.entity_description.name + self.coordinator.device, block, self.entity_description.name ) elif entry is not None: self._attr_unique_id = entry.unique_id @@ -603,7 +609,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti """Handle device update.""" if ( self.block is not None - or not self.wrapper.device.initialized + or not self.coordinator.device.initialized or self.sensors is None ): super()._update_callback() @@ -611,9 +617,9 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti _, entity_block, entity_sensor = self._attr_unique_id.split("-") - assert self.wrapper.device.blocks + assert self.coordinator.device.blocks - for block in self.wrapper.device.blocks: + for block in self.coordinator.device.blocks: if block.description != entity_block: continue diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index b75e1ad2377..0d0ab5dd029 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -25,7 +25,6 @@ from homeassistant.util.color import ( color_temperature_mired_to_kelvin, ) -from . import BlockDeviceWrapper, RpcDeviceWrapper from .const import ( BLOCK, DATA_CONFIG_ENTRY, @@ -44,6 +43,7 @@ from .const import ( SHBLB_1_RGB_EFFECTS, STANDARD_RGB_EFFECTS, ) +from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator from .entity import ShellyBlockEntity, ShellyRpcEntity from .utils import ( async_remove_shelly_entity, @@ -77,28 +77,28 @@ def async_setup_block_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up entities for block device.""" - wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][BLOCK] + coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][BLOCK] blocks = [] - assert wrapper.device.blocks - for block in wrapper.device.blocks: + assert coordinator.device.blocks + for block in coordinator.device.blocks: if block.type == "light": blocks.append(block) elif block.type == "relay": if not is_block_channel_type_light( - wrapper.device.settings, int(block.channel) + coordinator.device.settings, int(block.channel) ): continue blocks.append(block) - assert wrapper.device.shelly - unique_id = f"{wrapper.mac}-{block.type}_{block.channel}" + assert coordinator.device.shelly + unique_id = f"{coordinator.mac}-{block.type}_{block.channel}" async_remove_shelly_entity(hass, "switch", unique_id) if not blocks: return - async_add_entities(BlockShellyLight(wrapper, block) for block in blocks) + async_add_entities(BlockShellyLight(coordinator, block) for block in blocks) @callback @@ -108,22 +108,22 @@ def async_setup_rpc_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up entities for RPC device.""" - wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][RPC] - switch_key_ids = get_rpc_key_ids(wrapper.device.status, "switch") + coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][RPC] + switch_key_ids = get_rpc_key_ids(coordinator.device.status, "switch") switch_ids = [] for id_ in switch_key_ids: - if not is_rpc_channel_type_light(wrapper.device.config, id_): + if not is_rpc_channel_type_light(coordinator.device.config, id_): continue switch_ids.append(id_) - unique_id = f"{wrapper.mac}-switch:{id_}" + unique_id = f"{coordinator.mac}-switch:{id_}" async_remove_shelly_entity(hass, "switch", unique_id) if not switch_ids: return - async_add_entities(RpcShellyLight(wrapper, id_) for id_ in switch_ids) + async_add_entities(RpcShellyLight(coordinator, id_) for id_ in switch_ids) class BlockShellyLight(ShellyBlockEntity, LightEntity): @@ -131,9 +131,9 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): _attr_supported_color_modes: set[str] - def __init__(self, wrapper: BlockDeviceWrapper, block: Block) -> None: + def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None: """Initialize light.""" - super().__init__(wrapper, block) + super().__init__(coordinator, block) self.control_result: dict[str, Any] | None = None self._attr_supported_color_modes = set() self._attr_min_mireds = MIRED_MIN_VALUE @@ -144,7 +144,7 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): if hasattr(block, "red") and hasattr(block, "green") and hasattr(block, "blue"): self._attr_max_mireds = MIRED_MAX_VALUE_COLOR self._min_kelvin = KELVIN_MIN_VALUE_COLOR - if wrapper.model in RGBW_MODELS: + if coordinator.model in RGBW_MODELS: self._attr_supported_color_modes.add(ColorMode.RGBW) else: self._attr_supported_color_modes.add(ColorMode.RGB) @@ -161,8 +161,8 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): if hasattr(block, "effect"): self._attr_supported_features |= LightEntityFeature.EFFECT - if wrapper.model in MODELS_SUPPORTING_LIGHT_TRANSITION: - match = FIRMWARE_PATTERN.search(wrapper.device.settings.get("fw", "")) + if coordinator.model in MODELS_SUPPORTING_LIGHT_TRANSITION: + match = FIRMWARE_PATTERN.search(coordinator.device.settings.get("fw", "")) if ( match is not None and int(match[0]) >= LIGHT_TRANSITION_MIN_FIRMWARE_DATE @@ -215,7 +215,7 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): def color_mode(self) -> ColorMode: """Return the color mode of the light.""" if self.mode == "color": - if self.wrapper.model in RGBW_MODELS: + if self.coordinator.model in RGBW_MODELS: return ColorMode.RGBW return ColorMode.RGB @@ -268,7 +268,7 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): if not self.supported_features & LightEntityFeature.EFFECT: return None - if self.wrapper.model == "SHBLB-1": + if self.coordinator.model == "SHBLB-1": return list(SHBLB_1_RGB_EFFECTS.values()) return list(STANDARD_RGB_EFFECTS.values()) @@ -284,7 +284,7 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): else: effect_index = self.block.effect - if self.wrapper.model == "SHBLB-1": + if self.coordinator.model == "SHBLB-1": return SHBLB_1_RGB_EFFECTS[effect_index] return STANDARD_RGB_EFFECTS[effect_index] @@ -334,7 +334,7 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): if ATTR_EFFECT in kwargs and ATTR_COLOR_TEMP not in kwargs: # Color effect change - used only in color mode, switch device mode to color set_mode = "color" - if self.wrapper.model == "SHBLB-1": + if self.coordinator.model == "SHBLB-1": effect_dict = SHBLB_1_RGB_EFFECTS else: effect_dict = STANDARD_RGB_EFFECTS @@ -346,13 +346,13 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): LOGGER.error( "Effect '%s' not supported by device %s", kwargs[ATTR_EFFECT], - self.wrapper.model, + self.coordinator.model, ) if ( set_mode and set_mode != self.mode - and self.wrapper.model in DUAL_MODE_LIGHT_MODELS + and self.coordinator.model in DUAL_MODE_LIGHT_MODELS ): params["mode"] = set_mode @@ -385,15 +385,15 @@ class RpcShellyLight(ShellyRpcEntity, LightEntity): _attr_color_mode = ColorMode.ONOFF _attr_supported_color_modes = {ColorMode.ONOFF} - def __init__(self, wrapper: RpcDeviceWrapper, id_: int) -> None: + def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) -> None: """Initialize light.""" - super().__init__(wrapper, f"switch:{id_}") + super().__init__(coordinator, f"switch:{id_}") self._id = id_ @property def is_on(self) -> bool: """If light is on.""" - return bool(self.wrapper.device.status[self.key]["output"]) + return bool(self.coordinator.device.status[self.key]["output"]) async def async_turn_on(self, **kwargs: Any) -> None: """Turn on light.""" diff --git a/homeassistant/components/shelly/logbook.py b/homeassistant/components/shelly/logbook.py index 337b40fff04..c5da9d579f8 100644 --- a/homeassistant/components/shelly/logbook.py +++ b/homeassistant/components/shelly/logbook.py @@ -8,7 +8,7 @@ from homeassistant.const import ATTR_DEVICE_ID from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.typing import EventType -from . import get_block_device_wrapper, get_rpc_device_wrapper +from . import get_block_device_coordinator, get_rpc_device_coordinator from .const import ( ATTR_CHANNEL, ATTR_CLICK_TYPE, @@ -37,15 +37,15 @@ def async_describe_events( input_name = f"{event.data[ATTR_DEVICE]} channel {channel}" if click_type in RPC_INPUTS_EVENTS_TYPES: - rpc_wrapper = get_rpc_device_wrapper(hass, device_id) - if rpc_wrapper and rpc_wrapper.device.initialized: + rpc_coordinator = get_rpc_device_coordinator(hass, device_id) + if rpc_coordinator and rpc_coordinator.device.initialized: key = f"input:{channel-1}" - input_name = get_rpc_entity_name(rpc_wrapper.device, key) + input_name = get_rpc_entity_name(rpc_coordinator.device, key) elif click_type in BLOCK_INPUTS_EVENTS_TYPES: - block_wrapper = get_block_device_wrapper(hass, device_id) - if block_wrapper and block_wrapper.device.initialized: - device_name = get_block_device_name(block_wrapper.device) + block_coordinator = get_block_device_coordinator(hass, device_id) + if block_coordinator and block_coordinator.device.initialized: + device_name = get_block_device_name(block_coordinator.device) input_name = f"{device_name} channel {channel}" return { diff --git a/homeassistant/components/shelly/number.py b/homeassistant/components/shelly/number.py index 6658daf674f..eb61fccb9ef 100644 --- a/homeassistant/components/shelly/number.py +++ b/homeassistant/components/shelly/number.py @@ -119,7 +119,7 @@ class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, NumberEntity): LOGGER.debug("Setting state for entity %s, state: %s", self.name, params) try: async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): - return await self.wrapper.device.http_request("get", path, params) + return await self.coordinator.device.http_request("get", path, params) except (asyncio.TimeoutError, OSError) as err: LOGGER.error( "Setting state for entity %s failed, state: %s, error: %s", diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index bc55aa3e865..0e507b59431 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -32,8 +32,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_registry import RegistryEntry from homeassistant.helpers.typing import StateType -from . import BlockDeviceWrapper from .const import CONF_SLEEP_PERIOD, SHAIR_MAX_WORK_HOURS +from .coordinator import ShellyBlockCoordinator from .entity import ( BlockEntityDescription, RestEntityDescription, @@ -355,7 +355,7 @@ RPC_SENSORS: Final = { state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, - use_polling_wrapper=True, + use_polling_coordinator=True, ), "temperature_0": RpcSensorDescription( key="temperature:0", @@ -376,7 +376,7 @@ RPC_SENSORS: Final = { state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, - use_polling_wrapper=True, + use_polling_coordinator=True, ), "uptime": RpcSensorDescription( key="sys", @@ -386,7 +386,7 @@ RPC_SENSORS: Final = { device_class=SensorDeviceClass.TIMESTAMP, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, - use_polling_wrapper=True, + use_polling_coordinator=True, ), "humidity_0": RpcSensorDescription( key="humidity:0", @@ -465,13 +465,13 @@ class BlockSensor(ShellyBlockAttributeEntity, SensorEntity): def __init__( self, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, block: Block, attribute: str, description: BlockSensorDescription, ) -> None: """Initialize sensor.""" - super().__init__(wrapper, block, attribute, description) + super().__init__(coordinator, block, attribute, description) self._attr_native_unit_of_measurement = description.native_unit_of_measurement if unit_fn := description.unit_fn: @@ -512,7 +512,7 @@ class BlockSleepingSensor(ShellySleepingBlockAttributeEntity, SensorEntity): def __init__( self, - wrapper: BlockDeviceWrapper, + coordinator: ShellyBlockCoordinator, block: Block | None, attribute: str, description: BlockSensorDescription, @@ -520,7 +520,7 @@ class BlockSleepingSensor(ShellySleepingBlockAttributeEntity, SensorEntity): sensors: Mapping[tuple[str, str], BlockSensorDescription] | None = None, ) -> None: """Initialize the sleeping sensor.""" - super().__init__(wrapper, block, attribute, description, entry, sensors) + super().__init__(coordinator, block, attribute, description, entry, sensors) self._attr_native_unit_of_measurement = description.native_unit_of_measurement if block and (unit_fn := description.unit_fn): diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index d65568d0a2a..16f3ca9c163 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -10,8 +10,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import BlockDeviceWrapper, RpcDeviceWrapper from .const import BLOCK, DATA_CONFIG_ENTRY, DOMAIN, RPC +from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator from .entity import ShellyBlockEntity, ShellyRpcEntity from .utils import ( async_remove_shelly_entity, @@ -41,31 +41,31 @@ def async_setup_block_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up entities for block device.""" - wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][BLOCK] + coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][BLOCK] # In roller mode the relay blocks exist but do not contain required info if ( - wrapper.model in ["SHSW-21", "SHSW-25"] - and wrapper.device.settings["mode"] != "relay" + coordinator.model in ["SHSW-21", "SHSW-25"] + and coordinator.device.settings["mode"] != "relay" ): return relay_blocks = [] - assert wrapper.device.blocks - for block in wrapper.device.blocks: + assert coordinator.device.blocks + for block in coordinator.device.blocks: if block.type != "relay" or is_block_channel_type_light( - wrapper.device.settings, int(block.channel) + coordinator.device.settings, int(block.channel) ): continue relay_blocks.append(block) - unique_id = f"{wrapper.mac}-{block.type}_{block.channel}" + unique_id = f"{coordinator.mac}-{block.type}_{block.channel}" async_remove_shelly_entity(hass, "light", unique_id) if not relay_blocks: return - async_add_entities(BlockRelaySwitch(wrapper, block) for block in relay_blocks) + async_add_entities(BlockRelaySwitch(coordinator, block) for block in relay_blocks) @callback @@ -75,31 +75,31 @@ def async_setup_rpc_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up entities for RPC device.""" - wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][RPC] + coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][RPC] - switch_key_ids = get_rpc_key_ids(wrapper.device.status, "switch") + switch_key_ids = get_rpc_key_ids(coordinator.device.status, "switch") switch_ids = [] for id_ in switch_key_ids: - if is_rpc_channel_type_light(wrapper.device.config, id_): + if is_rpc_channel_type_light(coordinator.device.config, id_): continue switch_ids.append(id_) - unique_id = f"{wrapper.mac}-switch:{id_}" + unique_id = f"{coordinator.mac}-switch:{id_}" async_remove_shelly_entity(hass, "light", unique_id) if not switch_ids: return - async_add_entities(RpcRelaySwitch(wrapper, id_) for id_ in switch_ids) + async_add_entities(RpcRelaySwitch(coordinator, id_) for id_ in switch_ids) class BlockRelaySwitch(ShellyBlockEntity, SwitchEntity): """Entity that controls a relay on Block based Shelly devices.""" - def __init__(self, wrapper: BlockDeviceWrapper, block: Block) -> None: + def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None: """Initialize relay switch.""" - super().__init__(wrapper, block) + super().__init__(coordinator, block) self.control_result: dict[str, Any] | None = None @property @@ -130,15 +130,15 @@ class BlockRelaySwitch(ShellyBlockEntity, SwitchEntity): class RpcRelaySwitch(ShellyRpcEntity, SwitchEntity): """Entity that controls a relay on RPC based Shelly devices.""" - def __init__(self, wrapper: RpcDeviceWrapper, id_: int) -> None: + def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) -> None: """Initialize relay switch.""" - super().__init__(wrapper, f"switch:{id_}") + super().__init__(coordinator, f"switch:{id_}") self._id = id_ @property def is_on(self) -> bool: """If switch is on.""" - return bool(self.wrapper.device.status[self.key]["output"]) + return bool(self.coordinator.device.status[self.key]["output"]) async def async_turn_on(self, **kwargs: Any) -> None: """Turn on relay.""" diff --git a/homeassistant/components/shelly/update.py b/homeassistant/components/shelly/update.py index ac4b737a2cc..d9048594a04 100644 --- a/homeassistant/components/shelly/update.py +++ b/homeassistant/components/shelly/update.py @@ -17,8 +17,8 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import BlockDeviceWrapper, RpcDeviceWrapper from .const import BLOCK, CONF_SLEEP_PERIOD, DATA_CONFIG_ENTRY, DOMAIN +from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator from .entity import ( RestEntityDescription, RpcEntityDescription, @@ -67,7 +67,7 @@ REST_UPDATES: Final = { name="Firmware Update", key="fwupdate", latest_version=lambda status: status["update"]["new_version"], - install=lambda wrapper: wrapper.async_trigger_ota_update(), + install=lambda coordinator: coordinator.async_trigger_ota_update(), device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, @@ -76,7 +76,7 @@ REST_UPDATES: Final = { name="Beta Firmware Update", key="fwupdate", latest_version=lambda status: status["update"].get("beta_version"), - install=lambda wrapper: wrapper.async_trigger_ota_update(beta=True), + install=lambda coordinator: coordinator.async_trigger_ota_update(beta=True), device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, @@ -91,7 +91,7 @@ RPC_UPDATES: Final = { latest_version=lambda status: status.get("stable", {"version": None})[ "version" ], - install=lambda wrapper: wrapper.async_trigger_ota_update(), + install=lambda coordinator: coordinator.async_trigger_ota_update(), device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, @@ -101,7 +101,7 @@ RPC_UPDATES: Final = { key="sys", sub_key="available_updates", latest_version=lambda status: status.get("beta", {"version": None})["version"], - install=lambda wrapper: wrapper.async_trigger_ota_update(beta=True), + install=lambda coordinator: coordinator.async_trigger_ota_update(beta=True), device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, @@ -140,18 +140,18 @@ class RestUpdateEntity(ShellyRestAttributeEntity, UpdateEntity): def __init__( self, - wrapper: BlockDeviceWrapper, + block_coordinator: ShellyBlockCoordinator, attribute: str, description: RestEntityDescription, ) -> None: """Initialize update entity.""" - super().__init__(wrapper, attribute, description) + super().__init__(block_coordinator, attribute, description) self._in_progress_old_version: str | None = None @property def installed_version(self) -> str | None: """Version currently in use.""" - version = self.wrapper.device.status["update"]["old_version"] + version = self.block_coordinator.device.status["update"]["old_version"] if version is None: return None @@ -161,7 +161,7 @@ class RestUpdateEntity(ShellyRestAttributeEntity, UpdateEntity): def latest_version(self) -> str | None: """Latest version available for install.""" new_version = self.entity_description.latest_version( - self.wrapper.device.status, + self.block_coordinator.device.status, ) if new_version not in (None, ""): return cast(str, new_version) @@ -177,12 +177,12 @@ class RestUpdateEntity(ShellyRestAttributeEntity, UpdateEntity): self, version: str | None, backup: bool, **kwargs: Any ) -> None: """Install the latest firmware version.""" - config_entry = self.wrapper.entry - block_wrapper = self.hass.data[DOMAIN][DATA_CONFIG_ENTRY][ + config_entry = self.block_coordinator.entry + block_coordinator = self.hass.data[DOMAIN][DATA_CONFIG_ENTRY][ config_entry.entry_id ].get(BLOCK) self._in_progress_old_version = self.installed_version - await self.entity_description.install(block_wrapper) + await self.entity_description.install(block_coordinator) class RpcUpdateEntity(ShellyRpcAttributeEntity, UpdateEntity): @@ -195,28 +195,28 @@ class RpcUpdateEntity(ShellyRpcAttributeEntity, UpdateEntity): def __init__( self, - wrapper: RpcDeviceWrapper, + coordinator: ShellyRpcCoordinator, key: str, attribute: str, description: RpcEntityDescription, ) -> None: """Initialize update entity.""" - super().__init__(wrapper, key, attribute, description) + super().__init__(coordinator, key, attribute, description) self._in_progress_old_version: str | None = None @property def installed_version(self) -> str | None: """Version currently in use.""" - if self.wrapper.device.shelly is None: + if self.coordinator.device.shelly is None: return None - return cast(str, self.wrapper.device.shelly["ver"]) + return cast(str, self.coordinator.device.shelly["ver"]) @property def latest_version(self) -> str | None: """Latest version available for install.""" new_version = self.entity_description.latest_version( - self.wrapper.device.status[self.key][self.entity_description.sub_key], + self.coordinator.device.status[self.key][self.entity_description.sub_key], ) if new_version is not None: return cast(str, new_version) @@ -233,4 +233,4 @@ class RpcUpdateEntity(ShellyRpcAttributeEntity, UpdateEntity): ) -> None: """Install the latest firmware version.""" self._in_progress_old_version = self.installed_version - await self.entity_description.install(self.wrapper) + await self.entity_description.install(self.coordinator)