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:
Shay Levy 2024-04-14 18:07:26 +03:00 committed by GitHub
parent 33412dd9f6
commit 291df6dafe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 349 additions and 257 deletions

View File

@ -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:
if not device.firmware_supported:
async_create_issue_unsupported_firmware(hass, entry)
raise ConfigEntryNotReady from err
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

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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)
):

View File

@ -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.",

View File

@ -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."
}

View File

@ -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(
if (
block.type != "relay"
or block.channel is not None
and is_block_channel_type_light(
coordinator.device.settings, int(block.channel)
)
):
continue

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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(
with (
patch("homeassistant.components.shelly.RpcDevice.initialize"),
patch(
"homeassistant.components.shelly.RpcDevice.create", return_value=Mock()
) as rpc_device_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(
with (
patch("homeassistant.components.shelly.RpcDevice.initialize"),
patch(
"homeassistant.components.shelly.RpcDevice.create", return_value=Mock()
) as rpc_device_mock:
) as rpc_device_mock,
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()

View File

@ -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

View File

@ -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"

View File

@ -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()