diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index 48e27203288..6009f8613fe 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -7,6 +7,7 @@ import logging from typing import Any, Final, cast import aioshelly +from aioshelly.block_device import BlockDevice import async_timeout import voluptuous as vol @@ -89,7 +90,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: temperature_unit = "C" if hass.config.units.is_metric else "F" - options = aioshelly.ConnectionOptions( + options = aioshelly.common.ConnectionOptions( entry.data[CONF_HOST], entry.data.get(CONF_USERNAME), entry.data.get(CONF_PASSWORD), @@ -98,7 +99,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: coap_context = await get_coap_context(hass) - device = await aioshelly.Device.create( + device = await BlockDevice.create( aiohttp_client.async_get_clientsession(hass), coap_context, options, @@ -134,7 +135,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.debug("Setting up online device %s", entry.title) try: async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): - await device.initialize(True) + await device.initialize() except (asyncio.TimeoutError, OSError) as err: raise ConfigEntryNotReady from err @@ -146,7 +147,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: "Setup for device %s will resume when device is online", entry.title ) device.subscribe_updates(_async_device_online) - await device.coap_request("s") else: # Restore sensors for sleeping device _LOGGER.debug("Setting up offline device %s", entry.title) @@ -156,7 +156,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_device_setup( - hass: HomeAssistant, entry: ConfigEntry, device: aioshelly.Device + hass: HomeAssistant, entry: ConfigEntry, device: BlockDevice ) -> None: """Set up a device that is online.""" device_wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][ @@ -179,7 +179,7 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator): """Wrapper for a Shelly device with Home Assistant specific functions.""" def __init__( - self, hass: HomeAssistant, entry: ConfigEntry, device: aioshelly.Device + self, hass: HomeAssistant, entry: ConfigEntry, device: BlockDevice ) -> None: """Initialize the Shelly device wrapper.""" self.device_id: str | None = None @@ -208,7 +208,9 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator): ) self._last_input_events_count: dict = {} - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._handle_ha_stop) + entry.async_on_unload( + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._handle_ha_stop) + ) @callback def _async_device_updates_handler(self) -> None: @@ -216,6 +218,8 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator): 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: @@ -298,7 +302,7 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator): # This is duplicate but otherwise via_device can't work identifiers={(DOMAIN, self.mac)}, manufacturer="Shelly", - model=aioshelly.MODEL_NAMES.get(self.model, self.model), + model=aioshelly.const.MODEL_NAMES.get(self.model, self.model), sw_version=sw_version, ) self.device_id = entry.id @@ -306,10 +310,8 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator): def shutdown(self) -> None: """Shutdown the wrapper.""" - if self.device: - self.device.shutdown() - self._async_remove_device_updates_handler() - self.device = None + self.device.shutdown() + self._async_remove_device_updates_handler() @callback def _handle_ha_stop(self, _event: Event) -> None: @@ -321,7 +323,7 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator): class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator): """Rest Wrapper for a Shelly device with Home Assistant specific functions.""" - def __init__(self, hass: HomeAssistant, device: aioshelly.Device) -> None: + def __init__(self, hass: HomeAssistant, device: BlockDevice) -> None: """Initialize the Shelly device wrapper.""" if ( device.settings["device"]["type"] diff --git a/homeassistant/components/shelly/binary_sensor.py b/homeassistant/components/shelly/binary_sensor.py index f4b2daf8159..02183c3628e 100644 --- a/homeassistant/components/shelly/binary_sensor.py +++ b/homeassistant/components/shelly/binary_sensor.py @@ -48,7 +48,7 @@ SENSORS: Final = { ("sensor", "dwIsOpened"): BlockAttributeDescription( name="Door", device_class=DEVICE_CLASS_OPENING, - available=lambda block: cast(bool, block.dwIsOpened != -1), + available=lambda block: cast(int, block.dwIsOpened) != -1, ), ("sensor", "flood"): BlockAttributeDescription( name="Flood", device_class=DEVICE_CLASS_MOISTURE diff --git a/homeassistant/components/shelly/config_flow.py b/homeassistant/components/shelly/config_flow.py index c4ddbc0b0aa..da4413e16b7 100644 --- a/homeassistant/components/shelly/config_flow.py +++ b/homeassistant/components/shelly/config_flow.py @@ -7,6 +7,7 @@ from typing import Any, Dict, Final, cast import aiohttp import aioshelly +from aioshelly.block_device import BlockDevice import async_timeout import voluptuous as vol @@ -39,13 +40,13 @@ async def validate_input( Data has the keys from DATA_SCHEMA with values provided by the user. """ - options = aioshelly.ConnectionOptions( + options = aioshelly.common.ConnectionOptions( host, data.get(CONF_USERNAME), data.get(CONF_PASSWORD) ) coap_context = await get_coap_context(hass) async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): - device = await aioshelly.Device.create( + device = await BlockDevice.create( aiohttp_client.async_get_clientsession(hass), coap_context, options, @@ -82,7 +83,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): info = await self._async_get_info(host) except HTTP_CONNECT_ERRORS: errors["base"] = "cannot_connect" - except aioshelly.FirmwareUnsupported: + except aioshelly.exceptions.FirmwareUnsupported: return self.async_abort(reason="unsupported_firmware") except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") @@ -165,7 +166,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.info = info = await self._async_get_info(discovery_info["host"]) except HTTP_CONNECT_ERRORS: return self.async_abort(reason="cannot_connect") - except aioshelly.FirmwareUnsupported: + except aioshelly.exceptions.FirmwareUnsupported: return self.async_abort(reason="unsupported_firmware") await self.async_set_unique_id(info["mac"]) @@ -206,7 +207,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_show_form( step_id="confirm_discovery", description_placeholders={ - "model": aioshelly.MODEL_NAMES.get( + "model": aioshelly.const.MODEL_NAMES.get( self.info["type"], self.info["type"] ), "host": self.host, @@ -219,7 +220,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): return cast( Dict[str, Any], - await aioshelly.get_info( + await aioshelly.common.get_info( aiohttp_client.async_get_clientsession(self.hass), host, ), diff --git a/homeassistant/components/shelly/cover.py b/homeassistant/components/shelly/cover.py index 73b8b1baae3..40441ab74d3 100644 --- a/homeassistant/components/shelly/cover.py +++ b/homeassistant/components/shelly/cover.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any, cast -from aioshelly import Block +from aioshelly.block_device import Block from homeassistant.components.cover import ( ATTR_POSITION, @@ -57,7 +57,7 @@ class ShellyCover(ShellyBlockEntity, CoverEntity): if self.control_result: return cast(bool, self.control_result["current_pos"] == 0) - return cast(bool, self.block.rollerPos == 0) + return cast(int, self.block.rollerPos) == 0 @property def current_cover_position(self) -> int: @@ -73,7 +73,7 @@ class ShellyCover(ShellyBlockEntity, CoverEntity): if self.control_result: return cast(bool, self.control_result["state"] == "close") - return cast(bool, self.block.roller == "close") + return self.block.roller == "close" @property def is_opening(self) -> bool: @@ -81,7 +81,7 @@ class ShellyCover(ShellyBlockEntity, CoverEntity): if self.control_result: return cast(bool, self.control_result["state"] == "open") - return cast(bool, self.block.roller == "open") + return self.block.roller == "open" @property def supported_features(self) -> int: diff --git a/homeassistant/components/shelly/device_trigger.py b/homeassistant/components/shelly/device_trigger.py index eae2953e5b8..5d90a10dabc 100644 --- a/homeassistant/components/shelly/device_trigger.py +++ b/homeassistant/components/shelly/device_trigger.py @@ -60,6 +60,8 @@ async def async_validate_trigger_config( trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) + assert wrapper.device.blocks + for block in wrapper.device.blocks: input_triggers = get_input_triggers(wrapper.device, block) if trigger in input_triggers: @@ -93,6 +95,8 @@ async def async_get_triggers( ) return triggers + assert wrapper.device.blocks + for block in wrapper.device.blocks: input_triggers = get_input_triggers(wrapper.device, block) diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index 743dd07414e..a7b75116132 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -6,7 +6,7 @@ from dataclasses import dataclass import logging from typing import Any, Callable, Final, cast -import aioshelly +from aioshelly.block_device import Block import async_timeout from homeassistant.components.sensor import ATTR_STATE_CLASS @@ -62,6 +62,8 @@ async def async_setup_block_attribute_entities( """Set up entities for block attributes.""" blocks = [] + assert wrapper.device.blocks + for block in wrapper.device.blocks: for sensor_id in block.sensor_ids: description = sensors.get((block.type, sensor_id)) @@ -175,10 +177,10 @@ class BlockAttributeDescription: device_class: str | None = None state_class: str | None = None default_enabled: bool = True - available: Callable[[aioshelly.Block], bool] | None = None + available: Callable[[Block], bool] | None = None # Callable (settings, block), return true if entity should be removed - removal_condition: Callable[[dict, aioshelly.Block], bool] | None = None - extra_state_attributes: Callable[[aioshelly.Block], dict | None] | None = None + removal_condition: Callable[[dict, Block], bool] | None = None + extra_state_attributes: Callable[[Block], dict | None] | None = None @dataclass @@ -198,7 +200,7 @@ class RestAttributeDescription: class ShellyBlockEntity(entity.Entity): """Helper class to represent a block.""" - def __init__(self, wrapper: ShellyDeviceWrapper, block: aioshelly.Block) -> None: + def __init__(self, wrapper: ShellyDeviceWrapper, block: Block) -> None: """Initialize Shelly entity.""" self.wrapper = wrapper self.block = block @@ -267,7 +269,7 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity): def __init__( self, wrapper: ShellyDeviceWrapper, - block: aioshelly.Block, + block: Block, attribute: str, description: BlockAttributeDescription, ) -> None: @@ -418,7 +420,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti def __init__( self, wrapper: ShellyDeviceWrapper, - block: aioshelly.Block, + block: Block | None, attribute: str, description: BlockAttributeDescription, entry: entity_registry.RegistryEntry | None = None, @@ -429,7 +431,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti self.last_state: StateType = None self.wrapper = wrapper self.attribute = attribute - self.block = block + self.block: Block | None = block # type: ignore[assignment] self.description = description self._unit = self.description.unit @@ -468,6 +470,8 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti _, entity_block, entity_sensor = self.unique_id.split("-") + assert self.wrapper.device.blocks + for block in self.wrapper.device.blocks: if block.description != entity_block: continue diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index 86624410708..9ecc16ecc5a 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -5,7 +5,7 @@ import asyncio import logging from typing import Any, Final, cast -from aioshelly import Block +from aioshelly.block_device import Block import async_timeout from homeassistant.components.light import ( @@ -117,7 +117,7 @@ class ShellyLight(ShellyBlockEntity, LightEntity): self._supported_features |= SUPPORT_EFFECT if wrapper.model in MODELS_SUPPORTING_LIGHT_TRANSITION: - match = FIRMWARE_PATTERN.search(wrapper.device.settings.get("fw")) + match = FIRMWARE_PATTERN.search(wrapper.device.settings.get("fw", "")) if ( match is not None and int(match[0]) >= LIGHT_TRANSITION_MIN_FIRMWARE_DATE diff --git a/homeassistant/components/shelly/manifest.json b/homeassistant/components/shelly/manifest.json index ab87c4cef38..0c1e90eaaee 100644 --- a/homeassistant/components/shelly/manifest.json +++ b/homeassistant/components/shelly/manifest.json @@ -3,7 +3,7 @@ "name": "Shelly", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/shelly", - "requirements": ["aioshelly==0.6.4"], + "requirements": ["aioshelly==1.0.0"], "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index d8d530ed94c..7ffaae82daa 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -40,7 +40,7 @@ SENSORS: Final = { device_class=sensor.DEVICE_CLASS_BATTERY, state_class=sensor.STATE_CLASS_MEASUREMENT, removal_condition=lambda settings, _: settings.get("external_power") == 1, - available=lambda block: cast(bool, block.battery != -1), + available=lambda block: cast(int, block.battery) != -1, ), ("device", "deviceTemp"): BlockAttributeDescription( name="Device Temperature", @@ -162,7 +162,7 @@ SENSORS: Final = { value=lambda value: round(value, 1), device_class=sensor.DEVICE_CLASS_TEMPERATURE, state_class=sensor.STATE_CLASS_MEASUREMENT, - available=lambda block: cast(bool, block.extTemp != 999), + available=lambda block: cast(int, block.extTemp) != 999, ), ("sensor", "humidity"): BlockAttributeDescription( name="Humidity", @@ -170,14 +170,14 @@ SENSORS: Final = { value=lambda value: round(value, 1), device_class=sensor.DEVICE_CLASS_HUMIDITY, state_class=sensor.STATE_CLASS_MEASUREMENT, - available=lambda block: cast(bool, block.extTemp != 999), + available=lambda block: cast(int, block.extTemp) != 999, ), ("sensor", "luminosity"): BlockAttributeDescription( name="Luminosity", unit=LIGHT_LUX, device_class=sensor.DEVICE_CLASS_ILLUMINANCE, state_class=sensor.STATE_CLASS_MEASUREMENT, - available=lambda block: cast(bool, block.luminosity != -1), + available=lambda block: cast(int, block.luminosity) != -1, ), ("sensor", "tilt"): BlockAttributeDescription( name="Tilt", @@ -191,7 +191,7 @@ SENSORS: Final = { icon="mdi:progress-wrench", value=lambda value: round(100 - (value / 3600 / SHAIR_MAX_WORK_HOURS), 1), extra_state_attributes=lambda block: { - "Operational hours": round(block.totalWorkTime / 3600, 1) + "Operational hours": round(cast(int, block.totalWorkTime) / 3600, 1) }, ), ("adc", "adc"): BlockAttributeDescription( diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index 3e35ba878e4..b36bcd42d59 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any, cast -from aioshelly import Block +from aioshelly.block_device import Block from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index d1e2947d5ac..dfd4b1dc78a 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta import logging from typing import Any, Final, cast -import aioshelly +from aioshelly.block_device import BLOCK_VALUE_UNIT, COAP, Block, BlockDevice from homeassistant.const import EVENT_HOMEASSISTANT_STOP, TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant, callback @@ -40,18 +40,20 @@ async def async_remove_shelly_entity( def temperature_unit(block_info: dict[str, Any]) -> str: """Detect temperature unit.""" - if block_info[aioshelly.BLOCK_VALUE_UNIT] == "F": + if block_info[BLOCK_VALUE_UNIT] == "F": return TEMP_FAHRENHEIT return TEMP_CELSIUS -def get_device_name(device: aioshelly.Device) -> str: +def get_device_name(device: BlockDevice) -> str: """Naming for device.""" return cast(str, device.settings["name"] or device.settings["device"]["hostname"]) -def get_number_of_channels(device: aioshelly.Device, block: aioshelly.Block) -> int: +def get_number_of_channels(device: BlockDevice, block: Block) -> int: """Get number of channels for block type.""" + assert isinstance(device.shelly, dict) + channels = None if block.type == "input": @@ -71,8 +73,8 @@ def get_number_of_channels(device: aioshelly.Device, block: aioshelly.Block) -> def get_entity_name( - device: aioshelly.Device, - block: aioshelly.Block, + device: BlockDevice, + block: Block | None, description: str | None = None, ) -> str: """Naming for switch and sensors.""" @@ -84,10 +86,7 @@ def get_entity_name( return channel_name -def get_device_channel_name( - device: aioshelly.Device, - block: aioshelly.Block, -) -> str: +def get_device_channel_name(device: BlockDevice, block: Block | None) -> str: """Get name based on device and channel name.""" entity_name = get_device_name(device) @@ -98,8 +97,10 @@ def get_device_channel_name( ): return entity_name + assert block.channel + channel_name: str | None = None - mode = block.type + "s" + mode = cast(str, block.type) + "s" if mode in device.settings: channel_name = device.settings[mode][int(block.channel)].get("name") @@ -114,7 +115,7 @@ def get_device_channel_name( return f"{entity_name} channel {chr(int(block.channel)+base)}" -def is_momentary_input(settings: dict[str, Any], block: aioshelly.Block) -> bool: +def is_momentary_input(settings: dict[str, Any], block: Block) -> bool: """Return true if input button settings is set to a momentary type.""" # Shelly Button type is fixed to momentary and no btn_type if settings["device"]["type"] in SHBTN_MODELS: @@ -150,9 +151,7 @@ def get_device_uptime(status: dict[str, Any], last_uptime: str | None) -> str: return last_uptime -def get_input_triggers( - device: aioshelly.Device, block: aioshelly.Block -) -> list[tuple[str, str]]: +def get_input_triggers(device: BlockDevice, block: Block) -> list[tuple[str, str]]: """Return list of input triggers for block.""" if "inputEvent" not in block.sensor_ids or "inputEventCnt" not in block.sensor_ids: return [] @@ -165,6 +164,7 @@ def get_input_triggers( if block.type == "device" or get_number_of_channels(device, block) == 1: subtype = "button" else: + assert block.channel subtype = f"button{int(block.channel)+1}" if device.settings["device"]["type"] in SHBTN_MODELS: @@ -181,9 +181,9 @@ def get_input_triggers( @singleton.singleton("shelly_coap") -async def get_coap_context(hass: HomeAssistant) -> aioshelly.COAP: +async def get_coap_context(hass: HomeAssistant) -> COAP: """Get CoAP context to be used in all Shelly devices.""" - context = aioshelly.COAP() + context = COAP() if DOMAIN in hass.data: port = hass.data[DOMAIN].get(CONF_COAP_PORT, DEFAULT_COAP_PORT) else: diff --git a/requirements_all.txt b/requirements_all.txt index 9f1129892bb..d96a8792be4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -240,7 +240,7 @@ aiopylgtv==0.4.0 aiorecollect==1.0.8 # homeassistant.components.shelly -aioshelly==0.6.4 +aioshelly==1.0.0 # homeassistant.components.switcher_kis aioswitcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 25522896a2a..2f483571e3f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -161,7 +161,7 @@ aiopylgtv==0.4.0 aiorecollect==1.0.8 # homeassistant.components.shelly -aioshelly==0.6.4 +aioshelly==1.0.0 # homeassistant.components.switcher_kis aioswitcher==2.0.5 diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index 463c9111a60..81118f928d3 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -32,10 +32,10 @@ async def test_form(hass): assert result["errors"] == {} with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False}, ), patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock( return_value=Mock( settings=MOCK_SETTINGS, @@ -78,10 +78,10 @@ async def test_title_without_name(hass): settings["device"] = settings["device"].copy() settings["device"]["hostname"] = "shelly1pm-12345" with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False}, ), patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock( return_value=Mock( settings=settings, @@ -119,7 +119,7 @@ async def test_form_auth(hass): assert result["errors"] == {} with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={"mac": "test-mac", "type": "SHSW-1", "auth": True}, ): result2 = await hass.config_entries.flow.async_configure( @@ -131,7 +131,7 @@ async def test_form_auth(hass): assert result["errors"] == {} with patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock( return_value=Mock( settings=MOCK_SETTINGS, @@ -172,7 +172,7 @@ async def test_form_errors_get_info(hass, error): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with patch("aioshelly.get_info", side_effect=exc): + with patch("aioshelly.common.get_info", side_effect=exc): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {"host": "1.1.1.1"}, @@ -193,8 +193,10 @@ async def test_form_errors_test_connection(hass, error): ) with patch( - "aioshelly.get_info", return_value={"mac": "test-mac", "auth": False} - ), patch("aioshelly.Device.create", new=AsyncMock(side_effect=exc)): + "aioshelly.common.get_info", return_value={"mac": "test-mac", "auth": False} + ), patch( + "aioshelly.block_device.BlockDevice.create", new=AsyncMock(side_effect=exc) + ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {"host": "1.1.1.1"}, @@ -217,7 +219,7 @@ async def test_form_already_configured(hass): ) with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False}, ): result2 = await hass.config_entries.flow.async_configure( @@ -252,10 +254,10 @@ async def test_user_setup_ignored_device(hass): settings["fw"] = "20201124-092534/v1.9.0@57ac4ad8" with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False}, ), patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock( return_value=Mock( settings=settings, @@ -287,7 +289,10 @@ async def test_form_firmware_unsupported(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with patch("aioshelly.get_info", side_effect=aioshelly.FirmwareUnsupported): + with patch( + "aioshelly.common.get_info", + side_effect=aioshelly.exceptions.FirmwareUnsupported, + ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {"host": "1.1.1.1"}, @@ -313,14 +318,17 @@ async def test_form_auth_errors_test_connection(hass, error): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with patch("aioshelly.get_info", return_value={"mac": "test-mac", "auth": True}): + with patch( + "aioshelly.common.get_info", + return_value={"mac": "test-mac", "auth": True}, + ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {"host": "1.1.1.1"}, ) with patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock(side_effect=exc), ): result3 = await hass.config_entries.flow.async_configure( @@ -336,10 +344,10 @@ async def test_zeroconf(hass): await setup.async_setup_component(hass, "persistent_notification", {}) with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False}, ), patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock( return_value=Mock( settings=MOCK_SETTINGS, @@ -388,7 +396,7 @@ async def test_zeroconf_sleeping_device(hass): await setup.async_setup_component(hass, "persistent_notification", {}) with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={ "mac": "test-mac", "type": "SHSW-1", @@ -396,7 +404,7 @@ async def test_zeroconf_sleeping_device(hass): "sleep_mode": True, }, ), patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock( return_value=Mock( settings={ @@ -460,7 +468,7 @@ async def test_zeroconf_sleeping_device_error(hass, error): await setup.async_setup_component(hass, "persistent_notification", {}) with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={ "mac": "test-mac", "type": "SHSW-1", @@ -468,7 +476,7 @@ async def test_zeroconf_sleeping_device_error(hass, error): "sleep_mode": True, }, ), patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock(side_effect=exc), ): result = await hass.config_entries.flow.async_init( @@ -489,7 +497,7 @@ async def test_zeroconf_already_configured(hass): entry.add_to_hass(hass) with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False}, ): result = await hass.config_entries.flow.async_init( @@ -506,7 +514,10 @@ async def test_zeroconf_already_configured(hass): async def test_zeroconf_firmware_unsupported(hass): """Test we abort if device firmware is unsupported.""" - with patch("aioshelly.get_info", side_effect=aioshelly.FirmwareUnsupported): + with patch( + "aioshelly.common.get_info", + side_effect=aioshelly.exceptions.FirmwareUnsupported, + ): result = await hass.config_entries.flow.async_init( DOMAIN, data=DISCOVERY_INFO, @@ -519,7 +530,7 @@ async def test_zeroconf_firmware_unsupported(hass): async def test_zeroconf_cannot_connect(hass): """Test we get the form.""" - with patch("aioshelly.get_info", side_effect=asyncio.TimeoutError): + with patch("aioshelly.common.get_info", side_effect=asyncio.TimeoutError): result = await hass.config_entries.flow.async_init( DOMAIN, data=DISCOVERY_INFO, @@ -534,7 +545,7 @@ async def test_zeroconf_require_auth(hass): await setup.async_setup_component(hass, "persistent_notification", {}) with patch( - "aioshelly.get_info", + "aioshelly.common.get_info", return_value={"mac": "test-mac", "type": "SHSW-1", "auth": True}, ): result = await hass.config_entries.flow.async_init( @@ -546,7 +557,7 @@ async def test_zeroconf_require_auth(hass): assert result["errors"] == {} with patch( - "aioshelly.Device.create", + "aioshelly.block_device.BlockDevice.create", new=AsyncMock( return_value=Mock( settings=MOCK_SETTINGS,