mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Bump aioshelly to 9.0.0 (#114025)
* Update Shelly to use initialize from aioshelly * Save indentation in _async_device_connect * Use firmware_supported property from aioshelly * Review comments * Bump aioshelly * Fix lint errors * Test RPC initialized update
This commit is contained in:
parent
33412dd9f6
commit
291df6dafe
@ -3,23 +3,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
from typing import Any, Final
|
||||
from typing import Final
|
||||
|
||||
from aioshelly.block_device import BlockDevice, BlockUpdateType
|
||||
from aioshelly.block_device import BlockDevice
|
||||
from aioshelly.common import ConnectionOptions
|
||||
from aioshelly.const import DEFAULT_COAP_PORT, RPC_GENERATIONS
|
||||
from aioshelly.exceptions import (
|
||||
DeviceConnectionError,
|
||||
FirmwareUnsupported,
|
||||
InvalidAuthError,
|
||||
MacAddressMismatchError,
|
||||
)
|
||||
from aioshelly.rpc_device import RpcDevice, RpcUpdateType
|
||||
from aioshelly.rpc_device import RpcDevice
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
@ -53,12 +52,9 @@ from .coordinator import (
|
||||
)
|
||||
from .utils import (
|
||||
async_create_issue_unsupported_firmware,
|
||||
async_shutdown_device,
|
||||
get_block_device_sleep_period,
|
||||
get_coap_context,
|
||||
get_device_entry_gen,
|
||||
get_http_port,
|
||||
get_rpc_device_wakeup_period,
|
||||
get_ws_context,
|
||||
)
|
||||
|
||||
@ -154,7 +150,6 @@ async def _async_setup_block_entry(hass: HomeAssistant, entry: ConfigEntry) -> b
|
||||
async_get_clientsession(hass),
|
||||
coap_context,
|
||||
options,
|
||||
False,
|
||||
)
|
||||
|
||||
dev_reg = dr_async_get(hass)
|
||||
@ -186,57 +181,38 @@ async def _async_setup_block_entry(hass: HomeAssistant, entry: ConfigEntry) -> b
|
||||
data[CONF_SLEEP_PERIOD] = sleep_period = BLOCK_EXPECTED_SLEEP_PERIOD
|
||||
hass.config_entries.async_update_entry(entry, data=data)
|
||||
|
||||
async def _async_block_device_setup() -> None:
|
||||
"""Set up a block based device that is online."""
|
||||
shelly_entry_data.block = ShellyBlockCoordinator(hass, entry, device)
|
||||
shelly_entry_data.block.async_setup()
|
||||
|
||||
platforms = BLOCK_SLEEPING_PLATFORMS
|
||||
|
||||
if not entry.data.get(CONF_SLEEP_PERIOD):
|
||||
shelly_entry_data.rest = ShellyRestCoordinator(hass, device, entry)
|
||||
platforms = BLOCK_PLATFORMS
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, platforms)
|
||||
|
||||
@callback
|
||||
def _async_device_online(_: Any, update_type: BlockUpdateType) -> None:
|
||||
LOGGER.debug("Device %s is online, resuming setup", entry.title)
|
||||
shelly_entry_data.device = None
|
||||
|
||||
if sleep_period is None:
|
||||
data = {**entry.data}
|
||||
data[CONF_SLEEP_PERIOD] = get_block_device_sleep_period(device.settings)
|
||||
data["model"] = device.settings["device"]["type"]
|
||||
hass.config_entries.async_update_entry(entry, data=data)
|
||||
|
||||
hass.async_create_task(_async_block_device_setup(), eager_start=True)
|
||||
|
||||
if sleep_period == 0:
|
||||
# Not a sleeping device, finish setup
|
||||
LOGGER.debug("Setting up online block device %s", entry.title)
|
||||
try:
|
||||
await device.initialize()
|
||||
if not device.firmware_supported:
|
||||
async_create_issue_unsupported_firmware(hass, entry)
|
||||
raise ConfigEntryNotReady
|
||||
except (DeviceConnectionError, MacAddressMismatchError) as err:
|
||||
raise ConfigEntryNotReady(repr(err)) from err
|
||||
except InvalidAuthError as err:
|
||||
raise ConfigEntryAuthFailed(repr(err)) from err
|
||||
except FirmwareUnsupported as err:
|
||||
async_create_issue_unsupported_firmware(hass, entry)
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
await _async_block_device_setup()
|
||||
shelly_entry_data.block = ShellyBlockCoordinator(hass, entry, device)
|
||||
shelly_entry_data.block.async_setup()
|
||||
shelly_entry_data.rest = ShellyRestCoordinator(hass, device, entry)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, BLOCK_PLATFORMS)
|
||||
elif sleep_period is None or device_entry is None:
|
||||
# Need to get sleep info or first time sleeping device setup, wait for device
|
||||
shelly_entry_data.device = device
|
||||
LOGGER.debug(
|
||||
"Setup for device %s will resume when device is online", entry.title
|
||||
)
|
||||
device.subscribe_updates(_async_device_online)
|
||||
shelly_entry_data.block = ShellyBlockCoordinator(hass, entry, device)
|
||||
shelly_entry_data.block.async_setup(BLOCK_SLEEPING_PLATFORMS)
|
||||
else:
|
||||
# Restore sensors for sleeping device
|
||||
LOGGER.debug("Setting up offline block device %s", entry.title)
|
||||
await _async_block_device_setup()
|
||||
shelly_entry_data.block = ShellyBlockCoordinator(hass, entry, device)
|
||||
shelly_entry_data.block.async_setup()
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
entry, BLOCK_SLEEPING_PLATFORMS
|
||||
)
|
||||
|
||||
ir.async_delete_issue(
|
||||
hass, DOMAIN, FIRMWARE_UNSUPPORTED_ISSUE_ID.format(unique=entry.unique_id)
|
||||
@ -260,7 +236,6 @@ async def _async_setup_rpc_entry(hass: HomeAssistant, entry: ConfigEntry) -> boo
|
||||
async_get_clientsession(hass),
|
||||
ws_context,
|
||||
options,
|
||||
False,
|
||||
)
|
||||
|
||||
dev_reg = dr_async_get(hass)
|
||||
@ -276,58 +251,38 @@ async def _async_setup_rpc_entry(hass: HomeAssistant, entry: ConfigEntry) -> boo
|
||||
sleep_period = entry.data.get(CONF_SLEEP_PERIOD)
|
||||
shelly_entry_data = get_entry_data(hass)[entry.entry_id]
|
||||
|
||||
async def _async_rpc_device_setup() -> None:
|
||||
"""Set up a RPC based device that is online."""
|
||||
shelly_entry_data.rpc = ShellyRpcCoordinator(hass, entry, device)
|
||||
shelly_entry_data.rpc.async_setup()
|
||||
|
||||
platforms = RPC_SLEEPING_PLATFORMS
|
||||
|
||||
if not entry.data.get(CONF_SLEEP_PERIOD):
|
||||
shelly_entry_data.rpc_poll = ShellyRpcPollingCoordinator(
|
||||
hass, entry, device
|
||||
)
|
||||
platforms = RPC_PLATFORMS
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, platforms)
|
||||
|
||||
@callback
|
||||
def _async_device_online(_: Any, update_type: RpcUpdateType) -> None:
|
||||
LOGGER.debug("Device %s is online, resuming setup", entry.title)
|
||||
shelly_entry_data.device = None
|
||||
|
||||
if sleep_period is None:
|
||||
data = {**entry.data}
|
||||
data[CONF_SLEEP_PERIOD] = get_rpc_device_wakeup_period(device.status)
|
||||
hass.config_entries.async_update_entry(entry, data=data)
|
||||
|
||||
hass.async_create_task(_async_rpc_device_setup(), eager_start=True)
|
||||
|
||||
if sleep_period == 0:
|
||||
# Not a sleeping device, finish setup
|
||||
LOGGER.debug("Setting up online RPC device %s", entry.title)
|
||||
try:
|
||||
await device.initialize()
|
||||
except FirmwareUnsupported as err:
|
||||
async_create_issue_unsupported_firmware(hass, entry)
|
||||
raise ConfigEntryNotReady from err
|
||||
if not device.firmware_supported:
|
||||
async_create_issue_unsupported_firmware(hass, entry)
|
||||
raise ConfigEntryNotReady
|
||||
except (DeviceConnectionError, MacAddressMismatchError) as err:
|
||||
raise ConfigEntryNotReady(repr(err)) from err
|
||||
except InvalidAuthError as err:
|
||||
raise ConfigEntryAuthFailed(repr(err)) from err
|
||||
|
||||
await _async_rpc_device_setup()
|
||||
shelly_entry_data.rpc = ShellyRpcCoordinator(hass, entry, device)
|
||||
shelly_entry_data.rpc.async_setup()
|
||||
shelly_entry_data.rpc_poll = ShellyRpcPollingCoordinator(hass, entry, device)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, RPC_PLATFORMS)
|
||||
elif sleep_period is None or device_entry is None:
|
||||
# Need to get sleep info or first time sleeping device setup, wait for device
|
||||
shelly_entry_data.device = device
|
||||
LOGGER.debug(
|
||||
"Setup for device %s will resume when device is online", entry.title
|
||||
)
|
||||
device.subscribe_updates(_async_device_online)
|
||||
shelly_entry_data.rpc = ShellyRpcCoordinator(hass, entry, device)
|
||||
shelly_entry_data.rpc.async_setup(RPC_SLEEPING_PLATFORMS)
|
||||
else:
|
||||
# Restore sensors for sleeping device
|
||||
LOGGER.debug("Setting up offline block device %s", entry.title)
|
||||
await _async_rpc_device_setup()
|
||||
LOGGER.debug("Setting up offline RPC device %s", entry.title)
|
||||
shelly_entry_data.rpc = ShellyRpcCoordinator(hass, entry, device)
|
||||
shelly_entry_data.rpc.async_setup()
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
entry, RPC_SLEEPING_PLATFORMS
|
||||
)
|
||||
|
||||
ir.async_delete_issue(
|
||||
hass, DOMAIN, FIRMWARE_UNSUPPORTED_ISSUE_ID.format(unique=entry.unique_id)
|
||||
@ -339,11 +294,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
shelly_entry_data = get_entry_data(hass)[entry.entry_id]
|
||||
|
||||
# If device is present, block/rpc coordinator is not setup yet
|
||||
if (device := shelly_entry_data.device) is not None:
|
||||
await async_shutdown_device(device)
|
||||
return True
|
||||
|
||||
platforms = RPC_SLEEPING_PLATFORMS
|
||||
if not entry.data.get(CONF_SLEEP_PERIOD):
|
||||
platforms = RPC_PLATFORMS
|
||||
|
@ -11,7 +11,6 @@ from aioshelly.const import BLOCK_GENERATIONS, DEFAULT_HTTP_PORT, RPC_GENERATION
|
||||
from aioshelly.exceptions import (
|
||||
CustomPortNotSupported,
|
||||
DeviceConnectionError,
|
||||
FirmwareUnsupported,
|
||||
InvalidAuthError,
|
||||
)
|
||||
from aioshelly.rpc_device import RpcDevice
|
||||
@ -103,6 +102,7 @@ async def validate_input(
|
||||
ws_context,
|
||||
options,
|
||||
)
|
||||
await rpc_device.initialize()
|
||||
await rpc_device.shutdown()
|
||||
|
||||
sleep_period = get_rpc_device_wakeup_period(rpc_device.status)
|
||||
@ -121,6 +121,7 @@ async def validate_input(
|
||||
coap_context,
|
||||
options,
|
||||
)
|
||||
await block_device.initialize()
|
||||
block_device.shutdown()
|
||||
return {
|
||||
"title": block_device.name,
|
||||
@ -154,8 +155,6 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self.info = await self._async_get_info(host, port)
|
||||
except DeviceConnectionError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except FirmwareUnsupported:
|
||||
return self.async_abort(reason="unsupported_firmware")
|
||||
except Exception: # pylint: disable=broad-except
|
||||
LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
@ -287,8 +286,6 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self.info = await self._async_get_info(host, DEFAULT_HTTP_PORT)
|
||||
except DeviceConnectionError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
except FirmwareUnsupported:
|
||||
return self.async_abort(reason="unsupported_firmware")
|
||||
|
||||
if not mac:
|
||||
# We could not get the mac address from the name
|
||||
@ -366,14 +363,14 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
if user_input is not None:
|
||||
try:
|
||||
info = await self._async_get_info(host, port)
|
||||
except (DeviceConnectionError, InvalidAuthError, FirmwareUnsupported):
|
||||
except (DeviceConnectionError, InvalidAuthError):
|
||||
return self.async_abort(reason="reauth_unsuccessful")
|
||||
|
||||
if get_device_entry_gen(self.entry) != 1:
|
||||
user_input[CONF_USERNAME] = "admin"
|
||||
try:
|
||||
await validate_input(self.hass, host, port, info, user_input)
|
||||
except (DeviceConnectionError, InvalidAuthError, FirmwareUnsupported):
|
||||
except (DeviceConnectionError, InvalidAuthError):
|
||||
return self.async_abort(reason="reauth_unsuccessful")
|
||||
|
||||
return self.async_update_reload_and_abort(
|
||||
|
@ -15,7 +15,12 @@ from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCal
|
||||
from aioshelly.rpc_device import RpcDevice, RpcUpdateType
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import ATTR_DEVICE_ID, CONF_HOST, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_ID,
|
||||
CONF_HOST,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
@ -58,7 +63,9 @@ from .const import (
|
||||
BLEScannerMode,
|
||||
)
|
||||
from .utils import (
|
||||
async_create_issue_unsupported_firmware,
|
||||
async_shutdown_device,
|
||||
get_block_device_sleep_period,
|
||||
get_device_entry_gen,
|
||||
get_http_port,
|
||||
get_rpc_device_wakeup_period,
|
||||
@ -73,7 +80,6 @@ class ShellyEntryData:
|
||||
"""Class for sharing data within a given config entry."""
|
||||
|
||||
block: ShellyBlockCoordinator | None = None
|
||||
device: BlockDevice | RpcDevice | None = None
|
||||
rest: ShellyRestCoordinator | None = None
|
||||
rpc: ShellyRpcCoordinator | None = None
|
||||
rpc_poll: ShellyRpcPollingCoordinator | None = None
|
||||
@ -98,6 +104,7 @@ class ShellyCoordinatorBase(DataUpdateCoordinator[None], Generic[_DeviceT]):
|
||||
self.entry = entry
|
||||
self.device = device
|
||||
self.device_id: str | None = None
|
||||
self._pending_platforms: list[Platform] | None = None
|
||||
device_name = device.name if device.initialized else entry.title
|
||||
interval_td = timedelta(seconds=update_interval)
|
||||
super().__init__(hass, LOGGER, name=device_name, update_interval=interval_td)
|
||||
@ -131,8 +138,9 @@ class ShellyCoordinatorBase(DataUpdateCoordinator[None], Generic[_DeviceT]):
|
||||
"""Sleep period of the device."""
|
||||
return self.entry.data.get(CONF_SLEEP_PERIOD, 0)
|
||||
|
||||
def async_setup(self) -> None:
|
||||
def async_setup(self, pending_platforms: list[Platform] | None = None) -> None:
|
||||
"""Set up the coordinator."""
|
||||
self._pending_platforms = pending_platforms
|
||||
dev_reg = dr_async_get(self.hass)
|
||||
device_entry = dev_reg.async_get_or_create(
|
||||
config_entry_id=self.entry.entry_id,
|
||||
@ -146,6 +154,45 @@ class ShellyCoordinatorBase(DataUpdateCoordinator[None], Generic[_DeviceT]):
|
||||
)
|
||||
self.device_id = device_entry.id
|
||||
|
||||
async def _async_device_connect(self) -> None:
|
||||
"""Connect to a Shelly Block device."""
|
||||
LOGGER.debug("Connecting to Shelly Device - %s", self.name)
|
||||
try:
|
||||
await self.device.initialize()
|
||||
update_device_fw_info(self.hass, self.device, self.entry)
|
||||
except DeviceConnectionError as err:
|
||||
raise UpdateFailed(f"Device disconnected: {repr(err)}") from err
|
||||
except InvalidAuthError:
|
||||
self.entry.async_start_reauth(self.hass)
|
||||
return
|
||||
|
||||
if not self.device.firmware_supported:
|
||||
async_create_issue_unsupported_firmware(self.hass, self.entry)
|
||||
return
|
||||
|
||||
if not self._pending_platforms:
|
||||
return
|
||||
|
||||
LOGGER.debug("Device %s is online, resuming setup", self.entry.title)
|
||||
platforms = self._pending_platforms
|
||||
self._pending_platforms = None
|
||||
|
||||
data = {**self.entry.data}
|
||||
|
||||
# Update sleep_period
|
||||
old_sleep_period = data[CONF_SLEEP_PERIOD]
|
||||
if isinstance(self.device, RpcDevice):
|
||||
new_sleep_period = get_rpc_device_wakeup_period(self.device.status)
|
||||
elif isinstance(self.device, BlockDevice):
|
||||
new_sleep_period = get_block_device_sleep_period(self.device.settings)
|
||||
|
||||
if new_sleep_period != old_sleep_period:
|
||||
data[CONF_SLEEP_PERIOD] = new_sleep_period
|
||||
self.hass.config_entries.async_update_entry(self.entry, data=data)
|
||||
|
||||
# Resume platform setup
|
||||
await self.hass.config_entries.async_forward_entry_setups(self.entry, platforms)
|
||||
|
||||
async def _async_reload_entry(self) -> None:
|
||||
"""Reload entry."""
|
||||
self._debounced_reload.async_cancel()
|
||||
@ -179,7 +226,7 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]):
|
||||
|
||||
self._last_cfg_changed: int | None = None
|
||||
self._last_mode: str | None = None
|
||||
self._last_effect: int | None = None
|
||||
self._last_effect: str | None = None
|
||||
self._last_input_events_count: dict = {}
|
||||
self._last_target_temp: float | None = None
|
||||
self._push_update_failures: int = 0
|
||||
@ -211,15 +258,14 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]):
|
||||
if not self.device.initialized:
|
||||
return
|
||||
|
||||
assert self.device.blocks
|
||||
|
||||
# For buttons which are battery powered - set initial value for last_event_count
|
||||
if self.model in SHBTN_MODELS and self._last_input_events_count.get(1) is None:
|
||||
for block in self.device.blocks:
|
||||
if block.type != "device":
|
||||
continue
|
||||
|
||||
if len(block.wakeupEvent) == 1 and block.wakeupEvent[0] == "button":
|
||||
wakeup_event = cast(list, block.wakeupEvent)
|
||||
if len(wakeup_event) == 1 and wakeup_event[0] == "button":
|
||||
self._last_input_events_count[1] = -1
|
||||
|
||||
break
|
||||
@ -228,7 +274,7 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]):
|
||||
cfg_changed = 0
|
||||
for block in self.device.blocks:
|
||||
if block.type == "device" and block.cfgChanged is not None:
|
||||
cfg_changed = block.cfgChanged
|
||||
cfg_changed = cast(int, block.cfgChanged)
|
||||
|
||||
# Shelly TRV sends information about changing the configuration for no
|
||||
# reason, reloading the config entry is not needed for it.
|
||||
@ -314,14 +360,16 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]):
|
||||
self, device_: BlockDevice, update_type: BlockUpdateType
|
||||
) -> None:
|
||||
"""Handle device update."""
|
||||
if update_type == BlockUpdateType.COAP_PERIODIC:
|
||||
if update_type is BlockUpdateType.ONLINE:
|
||||
self.hass.async_create_task(self._async_device_connect(), eager_start=True)
|
||||
elif update_type is BlockUpdateType.COAP_PERIODIC:
|
||||
self._push_update_failures = 0
|
||||
ir.async_delete_issue(
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
PUSH_UPDATE_ISSUE_ID.format(unique=self.mac),
|
||||
)
|
||||
elif update_type == BlockUpdateType.COAP_REPLY:
|
||||
elif update_type is BlockUpdateType.COAP_REPLY:
|
||||
self._push_update_failures += 1
|
||||
if self._push_update_failures == MAX_PUSH_UPDATE_FAILURES:
|
||||
LOGGER.debug(
|
||||
@ -346,9 +394,9 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]):
|
||||
)
|
||||
self.async_set_updated_data(None)
|
||||
|
||||
def async_setup(self) -> None:
|
||||
def async_setup(self, pending_platforms: list[Platform] | None = None) -> None:
|
||||
"""Set up the coordinator."""
|
||||
super().async_setup()
|
||||
super().async_setup(pending_platforms)
|
||||
self.device.subscribe_updates(self._async_handle_update)
|
||||
|
||||
def shutdown(self) -> None:
|
||||
@ -538,14 +586,7 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]):
|
||||
if self.device.connected:
|
||||
return
|
||||
|
||||
LOGGER.debug("Reconnecting to Shelly RPC Device - %s", self.name)
|
||||
try:
|
||||
await self.device.initialize()
|
||||
update_device_fw_info(self.hass, self.device, self.entry)
|
||||
except DeviceConnectionError as err:
|
||||
raise UpdateFailed(f"Device disconnected: {repr(err)}") from err
|
||||
except InvalidAuthError:
|
||||
await self.async_shutdown_device_and_start_reauth()
|
||||
await self._async_device_connect()
|
||||
|
||||
async def _async_disconnected(self) -> None:
|
||||
"""Handle device disconnected."""
|
||||
@ -612,7 +653,9 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]):
|
||||
self, device_: RpcDevice, update_type: RpcUpdateType
|
||||
) -> None:
|
||||
"""Handle device update."""
|
||||
if update_type is RpcUpdateType.INITIALIZED:
|
||||
if update_type is RpcUpdateType.ONLINE:
|
||||
self.hass.async_create_task(self._async_device_connect(), eager_start=True)
|
||||
elif update_type is RpcUpdateType.INITIALIZED:
|
||||
self.hass.async_create_task(self._async_connected(), eager_start=True)
|
||||
self.async_set_updated_data(None)
|
||||
elif update_type is RpcUpdateType.DISCONNECTED:
|
||||
@ -624,9 +667,9 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]):
|
||||
elif update_type is RpcUpdateType.EVENT and (event := self.device.event):
|
||||
self._async_device_event_handler(event)
|
||||
|
||||
def async_setup(self) -> None:
|
||||
def async_setup(self, pending_platforms: list[Platform] | None = None) -> None:
|
||||
"""Set up the coordinator."""
|
||||
super().async_setup()
|
||||
super().async_setup(pending_platforms)
|
||||
self.device.subscribe_updates(self._async_handle_update)
|
||||
if self.device.initialized:
|
||||
# If we are already initialized, we are connected
|
||||
|
@ -74,7 +74,7 @@ def async_setup_block_attribute_entities(
|
||||
|
||||
for block in coordinator.device.blocks:
|
||||
for sensor_id in block.sensor_ids:
|
||||
description = sensors.get((block.type, sensor_id))
|
||||
description = sensors.get((cast(str, block.type), sensor_id))
|
||||
if description is None:
|
||||
continue
|
||||
|
||||
|
@ -78,7 +78,7 @@ def async_setup_block_entry(
|
||||
for block in coordinator.device.blocks:
|
||||
if block.type == "light":
|
||||
blocks.append(block)
|
||||
elif block.type == "relay":
|
||||
elif block.type == "relay" and block.channel is not None:
|
||||
if not is_block_channel_type_light(
|
||||
coordinator.device.settings, int(block.channel)
|
||||
):
|
||||
|
@ -9,7 +9,7 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioshelly"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aioshelly==8.2.0"],
|
||||
"requirements": ["aioshelly==9.0.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_http._tcp.local.",
|
||||
|
@ -38,7 +38,6 @@
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"unsupported_firmware": "The device is using an unsupported firmware version.",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"reauth_unsuccessful": "Re-authentication was unsuccessful, please remove the integration and set it up again."
|
||||
}
|
||||
|
@ -104,8 +104,12 @@ def async_setup_block_entry(
|
||||
relay_blocks = []
|
||||
assert coordinator.device.blocks
|
||||
for block in coordinator.device.blocks:
|
||||
if block.type != "relay" or is_block_channel_type_light(
|
||||
coordinator.device.settings, int(block.channel)
|
||||
if (
|
||||
block.type != "relay"
|
||||
or block.channel is not None
|
||||
and is_block_channel_type_light(
|
||||
coordinator.device.settings, int(block.channel)
|
||||
)
|
||||
):
|
||||
continue
|
||||
|
||||
|
@ -368,7 +368,7 @@ aioruuvigateway==0.1.0
|
||||
aiosenz==1.0.0
|
||||
|
||||
# homeassistant.components.shelly
|
||||
aioshelly==8.2.0
|
||||
aioshelly==9.0.0
|
||||
|
||||
# homeassistant.components.skybell
|
||||
aioskybell==22.7.0
|
||||
|
@ -341,7 +341,7 @@ aioruuvigateway==0.1.0
|
||||
aiosenz==1.0.0
|
||||
|
||||
# homeassistant.components.shelly
|
||||
aioshelly==8.2.0
|
||||
aioshelly==9.0.0
|
||||
|
||||
# homeassistant.components.skybell
|
||||
aioskybell==22.7.0
|
||||
|
@ -319,6 +319,11 @@ async def mock_block_device():
|
||||
{}, BlockUpdateType.COAP_REPLY
|
||||
)
|
||||
|
||||
def online():
|
||||
block_device_mock.return_value.subscribe_updates.call_args[0][0](
|
||||
{}, BlockUpdateType.ONLINE
|
||||
)
|
||||
|
||||
device = Mock(
|
||||
spec=BlockDevice,
|
||||
blocks=MOCK_BLOCKS,
|
||||
@ -337,6 +342,7 @@ async def mock_block_device():
|
||||
block_device_mock.return_value.mock_update_reply = Mock(
|
||||
side_effect=update_reply
|
||||
)
|
||||
block_device_mock.return_value.mock_online = Mock(side_effect=online)
|
||||
|
||||
yield block_device_mock.return_value
|
||||
|
||||
@ -376,16 +382,28 @@ async def mock_rpc_device():
|
||||
{}, RpcUpdateType.EVENT
|
||||
)
|
||||
|
||||
def online():
|
||||
rpc_device_mock.return_value.subscribe_updates.call_args[0][0](
|
||||
{}, RpcUpdateType.ONLINE
|
||||
)
|
||||
|
||||
def disconnected():
|
||||
rpc_device_mock.return_value.subscribe_updates.call_args[0][0](
|
||||
{}, RpcUpdateType.DISCONNECTED
|
||||
)
|
||||
|
||||
def initialized():
|
||||
rpc_device_mock.return_value.subscribe_updates.call_args[0][0](
|
||||
{}, RpcUpdateType.INITIALIZED
|
||||
)
|
||||
|
||||
device = _mock_rpc_device()
|
||||
rpc_device_mock.return_value = device
|
||||
rpc_device_mock.return_value.mock_disconnected = Mock(side_effect=disconnected)
|
||||
rpc_device_mock.return_value.mock_update = Mock(side_effect=update)
|
||||
rpc_device_mock.return_value.mock_event = Mock(side_effect=event)
|
||||
rpc_device_mock.return_value.mock_online = Mock(side_effect=online)
|
||||
rpc_device_mock.return_value.mock_initialized = Mock(side_effect=initialized)
|
||||
|
||||
yield rpc_device_mock.return_value
|
||||
|
||||
|
@ -144,7 +144,7 @@ async def test_block_sleeping_binary_sensor(
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
@ -180,7 +180,7 @@ async def test_block_restored_sleeping_binary_sensor(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
@ -206,7 +206,7 @@ async def test_block_restored_sleeping_binary_sensor_no_last_state(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
@ -263,6 +263,7 @@ async def test_rpc_sleeping_binary_sensor(
|
||||
) -> None:
|
||||
"""Test RPC online sleeping binary sensor."""
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_cloud"
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
|
||||
config_entry = await init_integration(hass, 2, sleep_period=1000)
|
||||
|
||||
# Sensor should be created when device is online
|
||||
@ -273,7 +274,7 @@ async def test_rpc_sleeping_binary_sensor(
|
||||
)
|
||||
|
||||
# Make device online
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
@ -344,6 +345,10 @@ async def test_rpc_restored_sleeping_binary_sensor_no_last_state(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_rpc_device, "initialized", True)
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Mock update
|
||||
mock_rpc_device.mock_update()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -64,7 +64,7 @@ async def test_climate_hvac_mode(
|
||||
await init_integration(hass, 1, sleep_period=1000, model=MODEL_VALVE)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Test initial hvac mode - off
|
||||
@ -125,7 +125,7 @@ async def test_climate_set_temperature(
|
||||
await init_integration(hass, 1, sleep_period=1000)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
@ -192,7 +192,7 @@ async def test_climate_set_preset_mode(
|
||||
await init_integration(hass, 1, sleep_period=1000, model=MODEL_VALVE)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
@ -278,7 +278,7 @@ async def test_block_restored_climate(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == HVACMode.OFF
|
||||
@ -349,7 +349,7 @@ async def test_block_restored_climate_us_customery(
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "targetTemp", 4.0)
|
||||
monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "temp", 18.2)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == HVACMode.OFF
|
||||
@ -451,7 +451,7 @@ async def test_block_set_mode_connection_error(
|
||||
await init_integration(hass, 1, sleep_period=1000)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
@ -476,7 +476,7 @@ async def test_block_set_mode_auth_error(
|
||||
entry = await init_integration(hass, 1, sleep_period=1000)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
@ -534,7 +534,7 @@ async def test_block_restored_climate_auth_error(
|
||||
type(mock_block_device).settings = PropertyMock(
|
||||
return_value={}, side_effect=InvalidAuthError
|
||||
)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
@ -561,7 +561,7 @@ async def test_device_not_calibrated(
|
||||
await init_integration(hass, 1, sleep_period=1000, model=MODEL_VALVE)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_status = MOCK_STATUS_COAP.copy()
|
||||
|
@ -10,7 +10,6 @@ from aioshelly.const import DEFAULT_HTTP_PORT, MODEL_1, MODEL_PLUS_2PM
|
||||
from aioshelly.exceptions import (
|
||||
CustomPortNotSupported,
|
||||
DeviceConnectionError,
|
||||
FirmwareUnsupported,
|
||||
InvalidAuthError,
|
||||
)
|
||||
import pytest
|
||||
@ -433,25 +432,6 @@ async def test_user_setup_ignored_device(
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_form_firmware_unsupported(hass: HomeAssistant) -> None:
|
||||
"""Test we abort if device firmware is unsupported."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.get_info",
|
||||
side_effect=FirmwareUnsupported,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{"host": "1.1.1.1"},
|
||||
)
|
||||
|
||||
assert result2["type"] is FlowResultType.ABORT
|
||||
assert result2["reason"] == "unsupported_firmware"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exc", "base_error"),
|
||||
[
|
||||
@ -757,22 +737,6 @@ async def test_zeroconf_with_wifi_ap_ip(hass: HomeAssistant) -> None:
|
||||
assert entry.data["host"] == "2.2.2.2"
|
||||
|
||||
|
||||
async def test_zeroconf_firmware_unsupported(hass: HomeAssistant) -> None:
|
||||
"""Test we abort if device firmware is unsupported."""
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.get_info",
|
||||
side_effect=FirmwareUnsupported,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
data=DISCOVERY_INFO,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "unsupported_firmware"
|
||||
|
||||
|
||||
async def test_zeroconf_cannot_connect(hass: HomeAssistant) -> None:
|
||||
"""Test we get the form."""
|
||||
with patch(
|
||||
@ -927,11 +891,7 @@ async def test_reauth_unsuccessful(
|
||||
assert result["reason"] == "reauth_unsuccessful"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"error",
|
||||
[DeviceConnectionError, FirmwareUnsupported],
|
||||
)
|
||||
async def test_reauth_get_info_error(hass: HomeAssistant, error: Exception) -> None:
|
||||
async def test_reauth_get_info_error(hass: HomeAssistant) -> None:
|
||||
"""Test reauthentication flow failed with error in get_info()."""
|
||||
entry = MockConfigEntry(
|
||||
domain="shelly", unique_id="test-mac", data={"host": "0.0.0.0", "gen": 2}
|
||||
@ -940,7 +900,7 @@ async def test_reauth_get_info_error(hass: HomeAssistant, error: Exception) -> N
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.get_info",
|
||||
side_effect=error,
|
||||
side_effect=DeviceConnectionError,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@ -1154,6 +1114,7 @@ async def test_zeroconf_sleeping_device_not_triggers_refresh(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test zeroconf discovery does not triggers refresh for sleeping device."""
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
|
||||
entry = MockConfigEntry(
|
||||
domain="shelly",
|
||||
unique_id="AABBCCDDEEFF",
|
||||
@ -1163,10 +1124,11 @@ async def test_zeroconf_sleeping_device_not_triggers_refresh(
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "online, resuming setup" in caplog.text
|
||||
assert len(mock_rpc_device.initialize.mock_calls) == 1
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.get_info",
|
||||
@ -1186,7 +1148,7 @@ async def test_zeroconf_sleeping_device_not_triggers_refresh(
|
||||
hass, dt_util.utcnow() + timedelta(seconds=ENTRY_RELOAD_COOLDOWN)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_rpc_device.initialize.mock_calls) == 0
|
||||
assert len(mock_rpc_device.initialize.mock_calls) == 1
|
||||
assert "device did not update" not in caplog.text
|
||||
|
||||
|
||||
|
@ -1,14 +1,10 @@
|
||||
"""Tests for Shelly coordinator."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
from unittest.mock import AsyncMock, Mock, call, patch
|
||||
|
||||
from aioshelly.const import MODEL_BULB, MODEL_BUTTON1
|
||||
from aioshelly.exceptions import (
|
||||
DeviceConnectionError,
|
||||
FirmwareUnsupported,
|
||||
InvalidAuthError,
|
||||
)
|
||||
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
||||
@ -29,13 +25,13 @@ from homeassistant.components.shelly.const import (
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import ATTR_DEVICE_ID, STATE_ON, STATE_UNAVAILABLE
|
||||
from homeassistant.core import Event, HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.device_registry import (
|
||||
CONNECTION_NETWORK_MAC,
|
||||
async_entries_for_config_entry,
|
||||
async_get as async_get_dev_reg,
|
||||
format_mac,
|
||||
)
|
||||
import homeassistant.helpers.issue_registry as ir
|
||||
|
||||
from . import (
|
||||
MOCK_MAC,
|
||||
@ -216,28 +212,25 @@ async def test_block_rest_update_auth_error(
|
||||
assert flow["context"].get("entry_id") == entry.entry_id
|
||||
|
||||
|
||||
async def test_block_firmware_unsupported(
|
||||
async def test_block_sleeping_device_firmware_unsupported(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_block_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test block device polling authentication error."""
|
||||
monkeypatch.setattr(
|
||||
mock_block_device,
|
||||
"update",
|
||||
AsyncMock(side_effect=FirmwareUnsupported),
|
||||
)
|
||||
entry = await init_integration(hass, 1)
|
||||
"""Test block sleeping device firmware not supported."""
|
||||
monkeypatch.setattr(mock_block_device, "firmware_supported", False)
|
||||
entry = await init_integration(hass, 1, sleep_period=3600)
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
# Move time to generate polling
|
||||
freezer.tick(timedelta(seconds=UPDATE_PERIOD_MULTIPLIER * 15))
|
||||
async_fire_time_changed(hass)
|
||||
# Make device online
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert (
|
||||
DOMAIN,
|
||||
"firmware_unsupported_123456789ABC",
|
||||
) in issue_registry.issues
|
||||
|
||||
|
||||
async def test_block_polling_connection_error(
|
||||
@ -290,20 +283,28 @@ async def test_block_rest_update_connection_error(
|
||||
|
||||
|
||||
async def test_block_sleeping_device_no_periodic_updates(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_block_device: Mock
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_block_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test block sleeping device no periodic updates."""
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_temperature"
|
||||
await init_integration(hass, 1, sleep_period=1000)
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings,
|
||||
"sleep_mode",
|
||||
{"period": 60, "unit": "m"},
|
||||
)
|
||||
await init_integration(hass, 1, sleep_period=3600)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert get_entity_state(hass, entity_id) == "22.1"
|
||||
|
||||
# Move time to generate polling
|
||||
freezer.tick(timedelta(seconds=UPDATE_PERIOD_MULTIPLIER * 1000))
|
||||
freezer.tick(timedelta(seconds=UPDATE_PERIOD_MULTIPLIER * 3600))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@ -352,7 +353,7 @@ async def test_block_button_click_event(
|
||||
entry = await init_integration(hass, 1, model=MODEL_BUTTON1, sleep_period=1000)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
@ -529,6 +530,7 @@ async def test_rpc_update_entry_sleep_period(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test RPC update entry sleep period."""
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 600)
|
||||
entry = await init_integration(hass, 2, sleep_period=600)
|
||||
register_entity(
|
||||
hass,
|
||||
@ -539,7 +541,7 @@ async def test_rpc_update_entry_sleep_period(
|
||||
)
|
||||
|
||||
# Make device online
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.data["sleep_period"] == 600
|
||||
@ -554,10 +556,14 @@ async def test_rpc_update_entry_sleep_period(
|
||||
|
||||
|
||||
async def test_rpc_sleeping_device_no_periodic_updates(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_rpc_device: Mock
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_rpc_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test RPC sleeping device no periodic updates."""
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_temperature"
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
|
||||
entry = await init_integration(hass, 2, sleep_period=1000)
|
||||
register_entity(
|
||||
hass,
|
||||
@ -568,7 +574,7 @@ async def test_rpc_sleeping_device_no_periodic_updates(
|
||||
)
|
||||
|
||||
# Make device online
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert get_entity_state(hass, entity_id) == "22.9"
|
||||
@ -581,25 +587,25 @@ async def test_rpc_sleeping_device_no_periodic_updates(
|
||||
assert get_entity_state(hass, entity_id) is STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_rpc_firmware_unsupported(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_rpc_device: Mock
|
||||
async def test_rpc_sleeping_device_firmware_unsupported(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test RPC update entry unsupported firmware."""
|
||||
entry = await init_integration(hass, 2)
|
||||
register_entity(
|
||||
hass,
|
||||
SENSOR_DOMAIN,
|
||||
"test_name_temperature",
|
||||
"temperature:0-temperature_0",
|
||||
entry,
|
||||
)
|
||||
"""Test RPC sleeping device firmware not supported."""
|
||||
monkeypatch.setattr(mock_rpc_device, "firmware_supported", False)
|
||||
entry = await init_integration(hass, 2, sleep_period=3600)
|
||||
|
||||
# Move time to generate sleep period update
|
||||
freezer.tick(timedelta(seconds=600 * SLEEP_PERIOD_MULTIPLIER))
|
||||
async_fire_time_changed(hass)
|
||||
# Make device online
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert (
|
||||
DOMAIN,
|
||||
"firmware_unsupported_123456789ABC",
|
||||
) in issue_registry.issues
|
||||
|
||||
|
||||
async def test_rpc_reconnect_auth_error(
|
||||
@ -753,11 +759,12 @@ async def test_rpc_update_entry_fw_ver(
|
||||
hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test RPC update entry firmware version."""
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 600)
|
||||
entry = await init_integration(hass, 2, sleep_period=600)
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
|
||||
# Make device online
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.unique_id
|
||||
@ -779,3 +786,23 @@ async def test_rpc_update_entry_fw_ver(
|
||||
)
|
||||
assert device
|
||||
assert device.sw_version == "99.0.0"
|
||||
|
||||
|
||||
async def test_rpc_runs_connected_events_when_initialized(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test RPC runs connected events when initialized."""
|
||||
monkeypatch.setattr(mock_rpc_device, "initialized", False)
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert call.script_list() not in mock_rpc_device.mock_calls
|
||||
|
||||
# Mock initialized event
|
||||
monkeypatch.setattr(mock_rpc_device, "initialized", True)
|
||||
mock_rpc_device.mock_initialized()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# BLE script list is called during connected events
|
||||
assert call.script_list() in mock_rpc_device.mock_calls
|
||||
|
@ -8,7 +8,6 @@ from aioshelly.common import ConnectionOptions
|
||||
from aioshelly.const import MODEL_PLUS_2PM
|
||||
from aioshelly.exceptions import (
|
||||
DeviceConnectionError,
|
||||
FirmwareUnsupported,
|
||||
InvalidAuthError,
|
||||
MacAddressMismatchError,
|
||||
)
|
||||
@ -27,6 +26,7 @@ from homeassistant.components.shelly.const import (
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, STATE_ON, STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.device_registry import (
|
||||
CONNECTION_NETWORK_MAC,
|
||||
DeviceRegistry,
|
||||
@ -145,27 +145,46 @@ async def test_setup_entry_not_shelly(
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gen", [1, 2, 3])
|
||||
@pytest.mark.parametrize("side_effect", [DeviceConnectionError, FirmwareUnsupported])
|
||||
async def test_device_connection_error(
|
||||
hass: HomeAssistant,
|
||||
gen: int,
|
||||
side_effect: Exception,
|
||||
mock_block_device: Mock,
|
||||
mock_rpc_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test device connection error."""
|
||||
monkeypatch.setattr(
|
||||
mock_block_device, "initialize", AsyncMock(side_effect=side_effect)
|
||||
mock_block_device, "initialize", AsyncMock(side_effect=DeviceConnectionError)
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
mock_rpc_device, "initialize", AsyncMock(side_effect=side_effect)
|
||||
mock_rpc_device, "initialize", AsyncMock(side_effect=DeviceConnectionError)
|
||||
)
|
||||
|
||||
entry = await init_integration(hass, gen)
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gen", [1, 2, 3])
|
||||
async def test_device_unsupported_firmware(
|
||||
hass: HomeAssistant,
|
||||
gen: int,
|
||||
mock_block_device: Mock,
|
||||
mock_rpc_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test device init with unsupported firmware."""
|
||||
monkeypatch.setattr(mock_block_device, "firmware_supported", False)
|
||||
monkeypatch.setattr(mock_rpc_device, "firmware_supported", False)
|
||||
|
||||
entry = await init_integration(hass, gen)
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
assert (
|
||||
DOMAIN,
|
||||
"firmware_unsupported_123456789ABC",
|
||||
) in issue_registry.issues
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gen", [1, 2, 3])
|
||||
async def test_mac_mismatch_error(
|
||||
hass: HomeAssistant,
|
||||
@ -217,12 +236,13 @@ async def test_device_auth_error(
|
||||
assert flow["context"].get("entry_id") == entry.entry_id
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("entry_sleep", "device_sleep"), [(None, 0), (1000, 1000)])
|
||||
@pytest.mark.parametrize(("entry_sleep", "device_sleep"), [(None, 0), (3600, 3600)])
|
||||
async def test_sleeping_block_device_online(
|
||||
hass: HomeAssistant,
|
||||
entry_sleep: int | None,
|
||||
device_sleep: int,
|
||||
mock_block_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
device_reg: DeviceRegistry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
@ -234,10 +254,17 @@ async def test_sleeping_block_device_online(
|
||||
connections={(CONNECTION_NETWORK_MAC, format_mac(MOCK_MAC))},
|
||||
)
|
||||
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings,
|
||||
"sleep_mode",
|
||||
{"period": int(device_sleep / 60), "unit": "m"},
|
||||
)
|
||||
entry = await init_integration(hass, 1, sleep_period=entry_sleep)
|
||||
assert "will resume when device is online" in caplog.text
|
||||
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "online, resuming setup" in caplog.text
|
||||
assert entry.data["sleep_period"] == device_sleep
|
||||
|
||||
@ -248,13 +275,17 @@ async def test_sleeping_rpc_device_online(
|
||||
entry_sleep: int | None,
|
||||
device_sleep: int,
|
||||
mock_rpc_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test sleeping RPC device online."""
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", device_sleep)
|
||||
entry = await init_integration(hass, 2, sleep_period=entry_sleep)
|
||||
assert "will resume when device is online" in caplog.text
|
||||
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "online, resuming setup" in caplog.text
|
||||
assert entry.data["sleep_period"] == device_sleep
|
||||
|
||||
@ -270,7 +301,9 @@ async def test_sleeping_rpc_device_online_new_firmware(
|
||||
assert "will resume when device is online" in caplog.text
|
||||
|
||||
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "sys", "wakeup_period", 1500)
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "online, resuming setup" in caplog.text
|
||||
assert entry.data["sleep_period"] == 1500
|
||||
|
||||
@ -413,9 +446,12 @@ async def test_entry_missing_port(hass: HomeAssistant) -> None:
|
||||
}
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=data, unique_id=MOCK_MAC)
|
||||
entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.shelly.RpcDevice.create", return_value=Mock()
|
||||
) as rpc_device_mock:
|
||||
with (
|
||||
patch("homeassistant.components.shelly.RpcDevice.initialize"),
|
||||
patch(
|
||||
"homeassistant.components.shelly.RpcDevice.create", return_value=Mock()
|
||||
) as rpc_device_mock,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@ -435,9 +471,12 @@ async def test_rpc_entry_custom_port(hass: HomeAssistant) -> None:
|
||||
}
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=data, unique_id=MOCK_MAC)
|
||||
entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.shelly.RpcDevice.create", return_value=Mock()
|
||||
) as rpc_device_mock:
|
||||
with (
|
||||
patch("homeassistant.components.shelly.RpcDevice.initialize"),
|
||||
patch(
|
||||
"homeassistant.components.shelly.RpcDevice.create", return_value=Mock()
|
||||
) as rpc_device_mock,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -33,12 +33,17 @@ async def test_block_number_update(
|
||||
) -> None:
|
||||
"""Test block device number update."""
|
||||
entity_id = "number.test_name_valve_position"
|
||||
await init_integration(hass, 1, sleep_period=1000)
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings,
|
||||
"sleep_mode",
|
||||
{"period": 60, "unit": "m"},
|
||||
)
|
||||
await init_integration(hass, 1, sleep_period=3600)
|
||||
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "50"
|
||||
@ -93,7 +98,7 @@ async def test_block_restored_number(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "50"
|
||||
@ -130,20 +135,27 @@ async def test_block_restored_number_no_last_state(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "50"
|
||||
|
||||
|
||||
async def test_block_number_set_value(
|
||||
hass: HomeAssistant, mock_block_device: Mock
|
||||
hass: HomeAssistant,
|
||||
mock_block_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test block device number set value."""
|
||||
await init_integration(hass, 1, sleep_period=1000)
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings,
|
||||
"sleep_mode",
|
||||
{"period": 60, "unit": "m"},
|
||||
)
|
||||
await init_integration(hass, 1, sleep_period=3600)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_block_device.reset_mock()
|
||||
@ -162,15 +174,20 @@ async def test_block_set_value_connection_error(
|
||||
hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test block device set value connection error."""
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings,
|
||||
"sleep_mode",
|
||||
{"period": 60, "unit": "m"},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
mock_block_device,
|
||||
"http_request",
|
||||
AsyncMock(side_effect=DeviceConnectionError),
|
||||
)
|
||||
await init_integration(hass, 1, sleep_period=1000)
|
||||
await init_integration(hass, 1, sleep_period=3600)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
@ -186,15 +203,20 @@ async def test_block_set_value_auth_error(
|
||||
hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test block device set value authentication error."""
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings,
|
||||
"sleep_mode",
|
||||
{"period": 60, "unit": "m"},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
mock_block_device,
|
||||
"http_request",
|
||||
AsyncMock(side_effect=InvalidAuthError),
|
||||
)
|
||||
entry = await init_integration(hass, 1, sleep_period=1000)
|
||||
entry = await init_integration(hass, 1, sleep_period=3600)
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
@ -164,7 +164,7 @@ async def test_block_sleeping_sensor(
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "22.1"
|
||||
@ -206,7 +206,7 @@ async def test_block_restored_sleeping_sensor(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "22.1"
|
||||
@ -232,7 +232,7 @@ async def test_block_restored_sleeping_sensor_no_last_state(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "22.1"
|
||||
@ -305,7 +305,7 @@ async def test_block_not_matched_restored_sleeping_sensor(
|
||||
mock_block_device.blocks[SENSOR_BLOCK_ID], "description", "other_desc"
|
||||
)
|
||||
monkeypatch.setattr(mock_block_device, "initialized", True)
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "20.4"
|
||||
@ -448,6 +448,7 @@ async def test_rpc_sleeping_sensor(
|
||||
) -> None:
|
||||
"""Test RPC online sleeping sensor."""
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_temperature"
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
|
||||
entry = await init_integration(hass, 2, sleep_period=1000)
|
||||
|
||||
# Sensor should be created when device is online
|
||||
@ -462,7 +463,7 @@ async def test_rpc_sleeping_sensor(
|
||||
)
|
||||
|
||||
# Make device online
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "22.9"
|
||||
@ -501,6 +502,10 @@ async def test_rpc_restored_sleeping_sensor(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_rpc_device, "initialized", True)
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Mock update
|
||||
mock_rpc_device.mock_update()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@ -533,6 +538,10 @@ async def test_rpc_restored_sleeping_sensor_no_last_state(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_rpc_device, "initialized", True)
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Mock update
|
||||
mock_rpc_device.mock_update()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@ -583,19 +592,21 @@ async def test_rpc_sleeping_update_entity_service(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test RPC sleeping device when the update_entity service is used."""
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_temperature"
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
|
||||
await init_integration(hass, 2, sleep_period=1000)
|
||||
|
||||
# Entity should be created when device is online
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
# Make device online
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
@ -627,19 +638,25 @@ async def test_block_sleeping_update_entity_service(
|
||||
hass: HomeAssistant,
|
||||
mock_block_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test block sleeping device when the update_entity service is used."""
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_temperature"
|
||||
await init_integration(hass, 1, sleep_period=1000)
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings,
|
||||
"sleep_mode",
|
||||
{"period": 60, "unit": "m"},
|
||||
)
|
||||
await init_integration(hass, 1, sleep_period=3600)
|
||||
|
||||
# Sensor should be created when device is online
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
# Make device online
|
||||
mock_block_device.mock_update()
|
||||
mock_block_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "22.1"
|
||||
|
@ -335,6 +335,7 @@ async def test_rpc_sleeping_update(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test RPC sleeping device update entity."""
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 1000)
|
||||
monkeypatch.setitem(mock_rpc_device.shelly, "ver", "1")
|
||||
monkeypatch.setitem(
|
||||
mock_rpc_device.status["sys"],
|
||||
@ -350,7 +351,7 @@ async def test_rpc_sleeping_update(
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
# Make device online
|
||||
mock_rpc_device.mock_update()
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
@ -411,6 +412,10 @@ async def test_rpc_restored_sleeping_update(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_rpc_device, "initialized", True)
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Mock update
|
||||
mock_rpc_device.mock_update()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@ -456,6 +461,10 @@ async def test_rpc_restored_sleeping_update_no_last_state(
|
||||
|
||||
# Make device online
|
||||
monkeypatch.setattr(mock_rpc_device, "initialized", True)
|
||||
mock_rpc_device.mock_online()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Mock update
|
||||
mock_rpc_device.mock_update()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user