From 422d69f2b39d4e798788d5e3869fe7d13367d4d0 Mon Sep 17 00:00:00 2001 From: Retha Runolfsson <137745329+zerzhang@users.noreply.github.com> Date: Tue, 24 Mar 2026 21:29:02 +0800 Subject: [PATCH] Bump PySwitchbot to 2.0.0 (#165995) Co-authored-by: Joost Lekkerkerker --- .../components/switchbot/__init__.py | 63 ++++++++++++- homeassistant/components/switchbot/const.py | 29 ++++-- homeassistant/components/switchbot/entity.py | 3 +- .../components/switchbot/manifest.json | 2 +- homeassistant/components/switchbot/select.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/switchbot/__init__.py | 2 +- tests/components/switchbot/test_cover.py | 2 +- tests/components/switchbot/test_fan.py | 20 ++--- tests/components/switchbot/test_humidifier.py | 2 +- tests/components/switchbot/test_init.py | 88 +++++++++++++++++++ tests/components/switchbot/test_light.py | 2 +- tests/components/switchbot/test_lock.py | 2 +- tests/components/switchbot/test_switch.py | 2 +- 15 files changed, 189 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py index d5946644e26..5780a92fdd2 100644 --- a/homeassistant/components/switchbot/__init__.py +++ b/homeassistant/components/switchbot/__init__.py @@ -30,6 +30,8 @@ from .const import ( CONNECTABLE_SUPPORTED_MODEL_TYPES, DEFAULT_CURTAIN_SPEED, DEFAULT_RETRY_COUNT, + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER, + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER_TABLE, DOMAIN, ENCRYPTED_MODELS, HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL, @@ -107,8 +109,10 @@ PLATFORMS_BY_TYPE = { Platform.LOCK, Platform.SENSOR, ], - SupportedModels.AIR_PURIFIER.value: [Platform.FAN, Platform.SENSOR], - SupportedModels.AIR_PURIFIER_TABLE.value: [Platform.FAN, Platform.SENSOR], + SupportedModels.AIR_PURIFIER_JP.value: [Platform.FAN, Platform.SENSOR], + SupportedModels.AIR_PURIFIER_US.value: [Platform.FAN, Platform.SENSOR], + SupportedModels.AIR_PURIFIER_TABLE_JP.value: [Platform.FAN, Platform.SENSOR], + SupportedModels.AIR_PURIFIER_TABLE_US.value: [Platform.FAN, Platform.SENSOR], SupportedModels.EVAPORATIVE_HUMIDIFIER: [Platform.HUMIDIFIER, Platform.SENSOR], SupportedModels.FLOOR_LAMP.value: [Platform.LIGHT, Platform.SENSOR], SupportedModels.STRIP_LIGHT_3.value: [Platform.LIGHT, Platform.SENSOR], @@ -162,8 +166,10 @@ CLASS_BY_DEVICE = { SupportedModels.K20_VACUUM.value: switchbot.SwitchbotVacuum, SupportedModels.LOCK_LITE.value: switchbot.SwitchbotLock, SupportedModels.LOCK_ULTRA.value: switchbot.SwitchbotLock, - SupportedModels.AIR_PURIFIER.value: switchbot.SwitchbotAirPurifier, - SupportedModels.AIR_PURIFIER_TABLE.value: switchbot.SwitchbotAirPurifier, + SupportedModels.AIR_PURIFIER_JP.value: switchbot.SwitchbotAirPurifier, + SupportedModels.AIR_PURIFIER_US.value: switchbot.SwitchbotAirPurifier, + SupportedModels.AIR_PURIFIER_TABLE_JP.value: switchbot.SwitchbotAirPurifier, + SupportedModels.AIR_PURIFIER_TABLE_US.value: switchbot.SwitchbotAirPurifier, SupportedModels.EVAPORATIVE_HUMIDIFIER: switchbot.SwitchbotEvaporativeHumidifier, SupportedModels.FLOOR_LAMP.value: switchbot.SwitchbotStripLight3, SupportedModels.STRIP_LIGHT_3.value: switchbot.SwitchbotStripLight3, @@ -183,6 +189,40 @@ CLASS_BY_DEVICE = { _LOGGER = logging.getLogger(__name__) +def _migrate_deprecated_air_purifier_type( + hass: HomeAssistant, entry: SwitchbotConfigEntry +) -> bool: + """Migrate deprecated air purifier sensor types introduced before pySwitchbot 2.0.0. + + The old library used a single AIR_PURIFIER/AIR_PURIFIER_TABLE type; the new + library distinguishes by region (JP/US). The correct type can be detected from + the BLE advertisement local name without connecting or using encryption keys. + + Returns True if migration succeeded, False if device is not in range yet. + """ + address: str = entry.data[CONF_ADDRESS] + if service_info := bluetooth.async_last_service_info( + hass, address.upper(), connectable=True + ): + parsed_adv = switchbot.parse_advertisement_data( + service_info.device, service_info.advertisement + ) + if ( + parsed_adv + and (adv_model := parsed_adv.data.get("modelName")) + in CONNECTABLE_SUPPORTED_MODEL_TYPES + ): + hass.config_entries.async_update_entry( + entry, + data={ + **entry.data, + CONF_SENSOR_TYPE: str(CONNECTABLE_SUPPORTED_MODEL_TYPES[adv_model]), + }, + ) + return True + return False + + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Switchbot Devices component.""" async_setup_services(hass) @@ -203,6 +243,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: SwitchbotConfigEntry) -> data={**entry.data, CONF_ADDRESS: mac}, ) + # Migrate deprecated air purifier sensor types introduced before pySwitchbot 2.0.0. + if entry.data.get(CONF_SENSOR_TYPE) in ( + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER, + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER_TABLE, + ) and not _migrate_deprecated_air_purifier_type(hass, entry): + # Device was not in range; retry when it starts advertising again. + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="device_not_found_error", + translation_placeholders={ + "sensor_type": entry.data[CONF_SENSOR_TYPE], + "address": entry.data[CONF_ADDRESS], + }, + ) + sensor_type: str = entry.data[CONF_SENSOR_TYPE] switchbot_model = HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL[sensor_type] # connectable means we can make connections to the device diff --git a/homeassistant/components/switchbot/const.py b/homeassistant/components/switchbot/const.py index a94c52dba81..9e87e155d80 100644 --- a/homeassistant/components/switchbot/const.py +++ b/homeassistant/components/switchbot/const.py @@ -47,8 +47,10 @@ class SupportedModels(StrEnum): HUB3 = "hub3" LOCK_LITE = "lock_lite" LOCK_ULTRA = "lock_ultra" - AIR_PURIFIER = "air_purifier" - AIR_PURIFIER_TABLE = "air_purifier_table" + AIR_PURIFIER_JP = "air_purifier_jp" + AIR_PURIFIER_US = "air_purifier_us" + AIR_PURIFIER_TABLE_JP = "air_purifier_table_jp" + AIR_PURIFIER_TABLE_US = "air_purifier_table_us" EVAPORATIVE_HUMIDIFIER = "evaporative_humidifier" FLOOR_LAMP = "floor_lamp" STRIP_LIGHT_3 = "strip_light_3" @@ -90,8 +92,10 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = { SwitchbotModel.K10_PRO_COMBO_VACUUM: SupportedModels.K10_PRO_COMBO_VACUUM, SwitchbotModel.LOCK_LITE: SupportedModels.LOCK_LITE, SwitchbotModel.LOCK_ULTRA: SupportedModels.LOCK_ULTRA, - SwitchbotModel.AIR_PURIFIER: SupportedModels.AIR_PURIFIER, - SwitchbotModel.AIR_PURIFIER_TABLE: SupportedModels.AIR_PURIFIER_TABLE, + SwitchbotModel.AIR_PURIFIER_JP: SupportedModels.AIR_PURIFIER_JP, + SwitchbotModel.AIR_PURIFIER_US: SupportedModels.AIR_PURIFIER_US, + SwitchbotModel.AIR_PURIFIER_TABLE_JP: SupportedModels.AIR_PURIFIER_TABLE_JP, + SwitchbotModel.AIR_PURIFIER_TABLE_US: SupportedModels.AIR_PURIFIER_TABLE_US, SwitchbotModel.EVAPORATIVE_HUMIDIFIER: SupportedModels.EVAPORATIVE_HUMIDIFIER, SwitchbotModel.FLOOR_LAMP: SupportedModels.FLOOR_LAMP, SwitchbotModel.STRIP_LIGHT_3: SupportedModels.STRIP_LIGHT_3, @@ -134,8 +138,10 @@ ENCRYPTED_MODELS = { SwitchbotModel.LOCK_PRO, SwitchbotModel.LOCK_LITE, SwitchbotModel.LOCK_ULTRA, - SwitchbotModel.AIR_PURIFIER, - SwitchbotModel.AIR_PURIFIER_TABLE, + SwitchbotModel.AIR_PURIFIER_JP, + SwitchbotModel.AIR_PURIFIER_US, + SwitchbotModel.AIR_PURIFIER_TABLE_JP, + SwitchbotModel.AIR_PURIFIER_TABLE_US, SwitchbotModel.EVAPORATIVE_HUMIDIFIER, SwitchbotModel.FLOOR_LAMP, SwitchbotModel.STRIP_LIGHT_3, @@ -159,8 +165,10 @@ ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[ SwitchbotModel.RELAY_SWITCH_1: switchbot.SwitchbotRelaySwitch, SwitchbotModel.LOCK_LITE: switchbot.SwitchbotLock, SwitchbotModel.LOCK_ULTRA: switchbot.SwitchbotLock, - SwitchbotModel.AIR_PURIFIER: switchbot.SwitchbotAirPurifier, - SwitchbotModel.AIR_PURIFIER_TABLE: switchbot.SwitchbotAirPurifier, + SwitchbotModel.AIR_PURIFIER_JP: switchbot.SwitchbotAirPurifier, + SwitchbotModel.AIR_PURIFIER_US: switchbot.SwitchbotAirPurifier, + SwitchbotModel.AIR_PURIFIER_TABLE_JP: switchbot.SwitchbotAirPurifier, + SwitchbotModel.AIR_PURIFIER_TABLE_US: switchbot.SwitchbotAirPurifier, SwitchbotModel.EVAPORATIVE_HUMIDIFIER: switchbot.SwitchbotEvaporativeHumidifier, SwitchbotModel.FLOOR_LAMP: switchbot.SwitchbotStripLight3, SwitchbotModel.STRIP_LIGHT_3: switchbot.SwitchbotStripLight3, @@ -179,6 +187,11 @@ HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = { str(v): k for k, v in SUPPORTED_MODEL_TYPES.items() } +# Deprecated sensor type values used before pySwitchbot 2.0.0. +# AIR_PURIFIER and AIR_PURIFIER_TABLE were split into JP/US variants. +DEPRECATED_SENSOR_TYPE_AIR_PURIFIER = "air_purifier" +DEPRECATED_SENSOR_TYPE_AIR_PURIFIER_TABLE = "air_purifier_table" + # Config Defaults DEFAULT_RETRY_COUNT = 3 DEFAULT_LOCK_NIGHTLATCH = False diff --git a/homeassistant/components/switchbot/entity.py b/homeassistant/components/switchbot/entity.py index a64950c0f7d..8aa2368cf6a 100644 --- a/homeassistant/components/switchbot/entity.py +++ b/homeassistant/components/switchbot/entity.py @@ -7,8 +7,7 @@ import logging from typing import Any, Concatenate import switchbot -from switchbot import Switchbot, SwitchbotDevice -from switchbot.devices.device import SwitchbotOperationError +from switchbot import Switchbot, SwitchbotDevice, SwitchbotOperationError from homeassistant.components.bluetooth.passive_update_coordinator import ( PassiveBluetoothCoordinatorEntity, diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 90454ca54ad..122a46e2a3d 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -42,5 +42,5 @@ "iot_class": "local_push", "loggers": ["switchbot"], "quality_scale": "gold", - "requirements": ["PySwitchbot==1.1.0"] + "requirements": ["PySwitchbot==2.0.0"] } diff --git a/homeassistant/components/switchbot/select.py b/homeassistant/components/switchbot/select.py index 5322b22f2c3..967ed83d347 100644 --- a/homeassistant/components/switchbot/select.py +++ b/homeassistant/components/switchbot/select.py @@ -6,7 +6,7 @@ from datetime import timedelta import logging import switchbot -from switchbot.devices.device import SwitchbotOperationError +from switchbot import SwitchbotOperationError from homeassistant.components.select import SelectEntity from homeassistant.const import EntityCategory diff --git a/requirements_all.txt b/requirements_all.txt index 64e59211787..7648a3a5805 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -83,7 +83,7 @@ PyRMVtransport==0.3.3 PySrDaliGateway==0.19.3 # homeassistant.components.switchbot -PySwitchbot==1.1.0 +PySwitchbot==2.0.0 # homeassistant.components.switchmate PySwitchmate==0.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index da39925baa0..1fec9a180b3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -83,7 +83,7 @@ PyRMVtransport==0.3.3 PySrDaliGateway==0.19.3 # homeassistant.components.switchbot -PySwitchbot==1.1.0 +PySwitchbot==2.0.0 # homeassistant.components.syncthru PySyncThru==0.8.0 diff --git a/tests/components/switchbot/__init__.py b/tests/components/switchbot/__init__.py index dc9bf76b7b1..8f1ce6e355b 100644 --- a/tests/components/switchbot/__init__.py +++ b/tests/components/switchbot/__init__.py @@ -761,7 +761,7 @@ LOCK_ULTRA_SERVICE_INFO = BluetoothServiceInfoBleak( ) -AIR_PURIFIER_TBALE_PM25_SERVICE_INFO = BluetoothServiceInfoBleak( +AIR_PURIFIER_TABLE_PM25_SERVICE_INFO = BluetoothServiceInfoBleak( name="Air Purifier Table PM25", manufacturer_data={ 2409: b"\xf0\x9e\x9e\x96j\xd6\xa1\x81\x88\xe4\x00\x01\x95\x00\x00", diff --git a/tests/components/switchbot/test_cover.py b/tests/components/switchbot/test_cover.py index 77c6bcf9e68..fd696f67c0e 100644 --- a/tests/components/switchbot/test_cover.py +++ b/tests/components/switchbot/test_cover.py @@ -4,7 +4,7 @@ from collections.abc import Callable from unittest.mock import AsyncMock, patch import pytest -from switchbot.devices.device import SwitchbotOperationError +from switchbot import SwitchbotOperationError from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.cover import ( diff --git a/tests/components/switchbot/test_fan.py b/tests/components/switchbot/test_fan.py index bd0306a133c..4e8fe669fd2 100644 --- a/tests/components/switchbot/test_fan.py +++ b/tests/components/switchbot/test_fan.py @@ -4,7 +4,7 @@ from collections.abc import Callable from unittest.mock import AsyncMock, patch import pytest -from switchbot.devices.device import SwitchbotOperationError +from switchbot import SwitchbotOperationError from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.fan import ( @@ -22,8 +22,8 @@ from homeassistant.exceptions import HomeAssistantError from . import ( AIR_PURIFIER_PM25_SERVICE_INFO, + AIR_PURIFIER_TABLE_PM25_SERVICE_INFO, AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, - AIR_PURIFIER_TBALE_PM25_SERVICE_INFO, AIR_PURIFIER_VOC_SERVICE_INFO, CIRCULATOR_FAN_SERVICE_INFO, ) @@ -103,10 +103,10 @@ async def test_circulator_fan_controlling( @pytest.mark.parametrize( ("service_info", "sensor_type"), [ - (AIR_PURIFIER_VOC_SERVICE_INFO, "air_purifier"), - (AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, "air_purifier_table"), - (AIR_PURIFIER_PM25_SERVICE_INFO, "air_purifier"), - (AIR_PURIFIER_TBALE_PM25_SERVICE_INFO, "air_purifier_table"), + (AIR_PURIFIER_VOC_SERVICE_INFO, "air_purifier_jp"), + (AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, "air_purifier_table_jp"), + (AIR_PURIFIER_PM25_SERVICE_INFO, "air_purifier_us"), + (AIR_PURIFIER_TABLE_PM25_SERVICE_INFO, "air_purifier_table_us"), ], ) @pytest.mark.parametrize( @@ -169,10 +169,10 @@ async def test_air_purifier_controlling( @pytest.mark.parametrize( ("service_info", "sensor_type"), [ - (AIR_PURIFIER_VOC_SERVICE_INFO, "air_purifier"), - (AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, "air_purifier_table"), - (AIR_PURIFIER_PM25_SERVICE_INFO, "air_purifier"), - (AIR_PURIFIER_TBALE_PM25_SERVICE_INFO, "air_purifier_table"), + (AIR_PURIFIER_VOC_SERVICE_INFO, "air_purifier_jp"), + (AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, "air_purifier_table_jp"), + (AIR_PURIFIER_PM25_SERVICE_INFO, "air_purifier_us"), + (AIR_PURIFIER_TABLE_PM25_SERVICE_INFO, "air_purifier_table_us"), ], ) @pytest.mark.parametrize( diff --git a/tests/components/switchbot/test_humidifier.py b/tests/components/switchbot/test_humidifier.py index 6718fe763a8..4293eb479c4 100644 --- a/tests/components/switchbot/test_humidifier.py +++ b/tests/components/switchbot/test_humidifier.py @@ -4,7 +4,7 @@ from collections.abc import Callable from unittest.mock import AsyncMock, patch import pytest -from switchbot.devices.device import SwitchbotOperationError +from switchbot import SwitchbotOperationError from homeassistant.components.humidifier import ( ATTR_HUMIDITY, diff --git a/tests/components/switchbot/test_init.py b/tests/components/switchbot/test_init.py index 6e24187228e..972c00ec4f7 100644 --- a/tests/components/switchbot/test_init.py +++ b/tests/components/switchbot/test_init.py @@ -5,17 +5,25 @@ from unittest.mock import AsyncMock, patch import pytest +from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.switchbot.const import ( CONF_CURTAIN_SPEED, CONF_RETRY_COUNT, DEFAULT_CURTAIN_SPEED, DEFAULT_RETRY_COUNT, + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER, + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER_TABLE, DOMAIN, ) +from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_SENSOR_TYPE from homeassistant.core import HomeAssistant from . import ( + AIR_PURIFIER_PM25_SERVICE_INFO, + AIR_PURIFIER_TABLE_PM25_SERVICE_INFO, + AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, + AIR_PURIFIER_VOC_SERVICE_INFO, HUBMINI_MATTER_SERVICE_INFO, LOCK_SERVICE_INFO, WOCURTAIN_SERVICE_INFO, @@ -211,3 +219,83 @@ async def test_migrate_entry_fails_for_future_version( # Entry should not be loaded due to failed migration assert entry.version == 2 assert entry.minor_version == 1 + + +@pytest.mark.parametrize( + ("old_sensor_type", "service_info", "expected_sensor_type"), + [ + ( + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER, + AIR_PURIFIER_VOC_SERVICE_INFO, + "air_purifier_jp", + ), + ( + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER, + AIR_PURIFIER_PM25_SERVICE_INFO, + "air_purifier_us", + ), + ( + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER_TABLE, + AIR_PURIFIER_TABLE_VOC_SERVICE_INFO, + "air_purifier_table_jp", + ), + ( + DEPRECATED_SENSOR_TYPE_AIR_PURIFIER_TABLE, + AIR_PURIFIER_TABLE_PM25_SERVICE_INFO, + "air_purifier_table_us", + ), + ], +) +async def test_migrate_deprecated_air_purifier_sensor_type( + hass: HomeAssistant, + old_sensor_type: str, + service_info: BluetoothServiceInfoBleak, + expected_sensor_type: str, +) -> None: + """Test that deprecated air_purifier sensor types are migrated via BLE advertisement.""" + inject_bluetooth_service_info(hass, service_info) + + entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_ADDRESS: "aa:bb:cc:dd:ee:ff", + CONF_NAME: "test-name", + CONF_SENSOR_TYPE: old_sensor_type, + }, + unique_id="aabbccddeeff", + version=1, + minor_version=2, + options={CONF_RETRY_COUNT: DEFAULT_RETRY_COUNT}, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.data[CONF_SENSOR_TYPE] == expected_sensor_type + + +async def test_migrate_deprecated_air_purifier_sensor_type_device_not_in_range( + hass: HomeAssistant, +) -> None: + """Test deprecated air_purifier type entry is not loaded when device is out of range.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_ADDRESS: "aa:bb:cc:dd:ee:ff", + CONF_NAME: "test-name", + CONF_SENSOR_TYPE: DEPRECATED_SENSOR_TYPE_AIR_PURIFIER, + }, + unique_id="aabbccddeeff", + version=1, + minor_version=2, + options={CONF_RETRY_COUNT: DEFAULT_RETRY_COUNT}, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + # sensor_type unchanged and entry not loaded; will retry when device advertises + assert entry.data[CONF_SENSOR_TYPE] == DEPRECATED_SENSOR_TYPE_AIR_PURIFIER + assert entry.state is ConfigEntryState.SETUP_RETRY diff --git a/tests/components/switchbot/test_light.py b/tests/components/switchbot/test_light.py index 706597c6052..cb81e170006 100644 --- a/tests/components/switchbot/test_light.py +++ b/tests/components/switchbot/test_light.py @@ -5,7 +5,7 @@ from typing import Any from unittest.mock import AsyncMock, patch import pytest -from switchbot.devices.device import SwitchbotOperationError +from switchbot import SwitchbotOperationError from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.light import ( diff --git a/tests/components/switchbot/test_lock.py b/tests/components/switchbot/test_lock.py index 38b8d24523b..412b4e826fc 100644 --- a/tests/components/switchbot/test_lock.py +++ b/tests/components/switchbot/test_lock.py @@ -4,7 +4,7 @@ from collections.abc import Callable from unittest.mock import AsyncMock, MagicMock, patch import pytest -from switchbot.devices.device import SwitchbotOperationError +from switchbot import SwitchbotOperationError from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN diff --git a/tests/components/switchbot/test_switch.py b/tests/components/switchbot/test_switch.py index 3754dbf8170..28adac8cb00 100644 --- a/tests/components/switchbot/test_switch.py +++ b/tests/components/switchbot/test_switch.py @@ -4,7 +4,7 @@ from collections.abc import Callable from unittest.mock import AsyncMock, patch import pytest -from switchbot.devices.device import SwitchbotOperationError +from switchbot import SwitchbotOperationError from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.switch import (