فهرست منبع

refactor: replace model-default override boilerplate with `_model` classvar (#476)

J. Nick Koston 1 روز پیش
والد
کامیت
e75f8c71ca

+ 1 - 26
switchbot/devices/air_purifier.py

@@ -5,8 +5,6 @@ from __future__ import annotations
 import logging
 from typing import Any, ClassVar
 
-from bleak.backends.device import BLEDevice
-
 from ..adv_parsers.air_purifier import get_air_purifier_mode
 from ..const import SwitchbotModel
 from ..const.air_purifier import AirPurifierMode, AirQualityLevel
@@ -40,6 +38,7 @@ READ_LED_STATUS_COMMAND = "570f4d07"
 class SwitchbotAirPurifier(SwitchbotSequenceBaseLight, SwitchbotEncryptedDevice):
     """Representation of a Switchbot Air Purifier."""
 
+    _model = SwitchbotModel.AIR_PURIFIER_US
     _turn_on_command = f"{COMMAND_HEAD}010100"
     _turn_off_command = f"{COMMAND_HEAD}010000"
     _open_child_lock_command = f"{COMMAND_HEAD}0301"
@@ -78,30 +77,6 @@ class SwitchbotAirPurifier(SwitchbotSequenceBaseLight, SwitchbotEncryptedDevice)
         }
     )
 
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.AIR_PURIFIER_US,
-        **kwargs: Any,
-    ) -> None:
-        super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
-
-    @classmethod
-    async def verify_encryption_key(
-        cls,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        model: SwitchbotModel = SwitchbotModel.AIR_PURIFIER_US,
-        **kwargs: Any,
-    ) -> bool:
-        return await super().verify_encryption_key(
-            device, key_id, encryption_key, model, **kwargs
-        )
-
     @property
     def color_modes(self) -> set[ColorMode]:
         """Return the supported color modes."""

+ 2 - 26
switchbot/devices/art_frame.py

@@ -3,8 +3,6 @@
 import logging
 from typing import Any
 
-from bleak.backends.device import BLEDevice
-
 from ..const import SwitchbotModel
 from .device import (
     SwitchbotEncryptedDevice,
@@ -20,30 +18,8 @@ COMMAND_SET_IMAGE = "570F7A02{}"
 class SwitchbotArtFrame(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
     """Representation of a Switchbot Art Frame."""
 
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.ART_FRAME,
-        **kwargs: Any,
-    ) -> None:
-        super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
-        self.response_flag = True
-
-    @classmethod
-    async def verify_encryption_key(
-        cls,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        model: SwitchbotModel = SwitchbotModel.ART_FRAME,
-        **kwargs: Any,
-    ) -> bool:
-        return await super().verify_encryption_key(
-            device, key_id, encryption_key, model, **kwargs
-        )
+    _model = SwitchbotModel.ART_FRAME
+    response_flag: bool = True
 
     async def get_basic_info(self) -> dict[str, Any] | None:
         """Get device basic settings."""

+ 17 - 3
switchbot/devices/device.py

@@ -996,16 +996,22 @@ class SwitchbotDevice(SwitchbotBaseDevice):
 class SwitchbotEncryptedDevice(SwitchbotDevice):
     """A Switchbot device that uses encryption."""
 
+    _model: SwitchbotModel | None = None
+
     def __init__(
         self,
         device: BLEDevice,
         key_id: str,
         encryption_key: str,
-        model: SwitchbotModel,
         interface: int = 0,
+        model: SwitchbotModel | None = None,
         **kwargs: Any,
     ) -> None:
         """Switchbot base class constructor for encrypted devices."""
+        if model is None:
+            model = self._model
+        if model is None:
+            raise ValueError("model must be provided or set on the subclass as _model")
         if len(key_id) == 0:
             raise ValueError("key_id is missing")
         if len(key_id) != 2:
@@ -1080,12 +1086,20 @@ class SwitchbotEncryptedDevice(SwitchbotDevice):
         device: BLEDevice,
         key_id: str,
         encryption_key: str,
-        model: SwitchbotModel,
+        model: SwitchbotModel | None = None,
         **kwargs: Any,
     ) -> bool:
+        if model is None:
+            model = cls._model
+        if model is None:
+            raise ValueError("model must be provided or set on the subclass as _model")
         try:
             switchbot_device = cls(
-                device, key_id=key_id, encryption_key=encryption_key, model=model
+                device,
+                key_id=key_id,
+                encryption_key=encryption_key,
+                model=model,
+                **kwargs,
             )
         except ValueError:
             return False

+ 2 - 27
switchbot/devices/evaporative_humidifier.py

@@ -1,8 +1,6 @@
 import logging
 from typing import Any
 
-from bleak.backends.device import BLEDevice
-
 from ..adv_parsers.humidifier import calculate_temperature_and_humidity
 from ..const import SwitchbotModel
 from ..const.evaporative_humidifier import (
@@ -47,33 +45,10 @@ DEVICE_GET_BASIC_SETTINGS_KEY = "570f4481"
 class SwitchbotEvaporativeHumidifier(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
     """Representation of a Switchbot Evaporative Humidifier"""
 
+    _model = SwitchbotModel.EVAPORATIVE_HUMIDIFIER
     _turn_on_command = COMMAND_TURN_ON
     _turn_off_command = f"{COMMAND_HEADER}0f430100"
-
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.EVAPORATIVE_HUMIDIFIER,
-        **kwargs: Any,
-    ) -> None:
-        self._force_next_update = False
-        super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
-
-    @classmethod
-    async def verify_encryption_key(
-        cls,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        model: SwitchbotModel = SwitchbotModel.EVAPORATIVE_HUMIDIFIER,
-        **kwargs: Any,
-    ) -> bool:
-        return await super().verify_encryption_key(
-            device, key_id, encryption_key, model, **kwargs
-        )
+    _force_next_update: bool = False
 
     async def get_basic_info(self) -> dict[str, Any] | None:
         """Get device basic settings."""

+ 1 - 1
switchbot/devices/keypad_vision.py

@@ -27,7 +27,7 @@ class SwitchbotKeypadVision(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
         **kwargs: Any,
     ) -> None:
         """Initialize Keypad Vision (Pro) device."""
-        super().__init__(device, key_id, encryption_key, model, **kwargs)
+        super().__init__(device, key_id, encryption_key, model=model, **kwargs)
 
     @classmethod
     async def verify_encryption_key(

+ 2 - 49
switchbot/devices/light_strip.py

@@ -2,8 +2,6 @@ from __future__ import annotations
 
 from typing import Any
 
-from bleak.backends.device import BLEDevice
-
 from ..const import SwitchbotModel
 from ..const.light import ColorMode, RGBICStripLightColorMode, StripLightColorMode
 from .base_light import SwitchbotSequenceBaseLight
@@ -274,29 +272,7 @@ class SwitchbotLightStrip(SwitchbotSequenceBaseLight):
 class SwitchbotStripLight3(SwitchbotEncryptedDevice, SwitchbotLightStrip):
     """Support for switchbot strip light3 and floor lamp."""
 
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.STRIP_LIGHT_3,
-        **kwargs: Any,
-    ) -> None:
-        super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
-
-    @classmethod
-    async def verify_encryption_key(
-        cls,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        model: SwitchbotModel = SwitchbotModel.STRIP_LIGHT_3,
-        **kwargs: Any,
-    ) -> bool:
-        return await super().verify_encryption_key(
-            device, key_id, encryption_key, model, **kwargs
-        )
+    _model = SwitchbotModel.STRIP_LIGHT_3
 
     @property
     def color_modes(self) -> set[ColorMode]:
@@ -307,32 +283,9 @@ class SwitchbotStripLight3(SwitchbotEncryptedDevice, SwitchbotLightStrip):
 class SwitchbotRgbicLight(SwitchbotEncryptedDevice, SwitchbotLightStrip):
     """Support for Switchbot RGBIC lights."""
 
+    _model = SwitchbotModel.RGBICWW_STRIP_LIGHT
     _effect_dict = RGBIC_EFFECTS
 
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.RGBICWW_STRIP_LIGHT,
-        **kwargs: Any,
-    ) -> None:
-        super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
-
-    @classmethod
-    async def verify_encryption_key(
-        cls,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        model: SwitchbotModel = SwitchbotModel.RGBICWW_STRIP_LIGHT,
-        **kwargs: Any,
-    ) -> bool:
-        return await super().verify_encryption_key(
-            device, key_id, encryption_key, model, **kwargs
-        )
-
     @property
     def color_modes(self) -> set[ColorMode]:
         """Return the supported color modes."""

+ 7 - 16
switchbot/devices/lock.py

@@ -83,15 +83,20 @@ COMMAND_RESULT_EXPECTED_VALUES = {1, 6}
 class SwitchbotLock(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
     """Representation of a Switchbot Lock."""
 
+    _model = SwitchbotModel.LOCK
+    _notifications_enabled: bool = False
+
     def __init__(
         self,
         device: BLEDevice,
         key_id: str,
         encryption_key: str,
         interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.LOCK,
+        model: SwitchbotModel | None = None,
         **kwargs: Any,
     ) -> None:
+        if model is None:
+            model = self._model
         if model not in (
             SwitchbotModel.LOCK,
             SwitchbotModel.LOCK_PRO,
@@ -102,21 +107,7 @@ class SwitchbotLock(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
             SwitchbotModel.LOCK_PRO_WIFI,
         ):
             raise ValueError("initializing SwitchbotLock with a non-lock model")
-        self._notifications_enabled: bool = False
-        super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
-
-    @classmethod
-    async def verify_encryption_key(
-        cls,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        model: SwitchbotModel = SwitchbotModel.LOCK,
-        **kwargs: Any,
-    ) -> bool:
-        return await super().verify_encryption_key(
-            device, key_id, encryption_key, model, **kwargs
-        )
+        super().__init__(device, key_id, encryption_key, interface, model, **kwargs)
 
     async def lock(self) -> bool:
         """Send lock command."""

+ 4 - 48
switchbot/devices/relay_switch.py

@@ -2,8 +2,6 @@ import logging
 import time
 from typing import Any
 
-from bleak.backends.device import BLEDevice
-
 from ..const import SwitchbotModel
 from ..helpers import parse_power_data, parse_uint24_be
 from ..models import SwitchBotAdvertisement
@@ -59,33 +57,10 @@ MULTI_CHANNEL_COMMANDS_GET_VOLTAGE_AND_CURRENT = {
 class SwitchbotRelaySwitch(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
     """Representation of a Switchbot relay switch 1pm."""
 
+    _model = SwitchbotModel.RELAY_SWITCH_1PM
     _turn_on_command = f"{COMMAND_CONTROL}010100"
     _turn_off_command = f"{COMMAND_CONTROL}010000"
 
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.RELAY_SWITCH_1PM,
-        **kwargs: Any,
-    ) -> None:
-        super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
-
-    @classmethod
-    async def verify_encryption_key(
-        cls,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        model: SwitchbotModel = SwitchbotModel.RELAY_SWITCH_1PM,
-        **kwargs: Any,
-    ) -> bool:
-        return await super().verify_encryption_key(
-            device, key_id, encryption_key, model, **kwargs
-        )
-
     def _reset_power_data(self, data: dict[str, Any]) -> None:
         """Reset power-related data to 0."""
         for key in ["power", "current", "voltage"]:
@@ -213,36 +188,17 @@ class SwitchbotRelaySwitch(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
 class SwitchbotGarageDoorOpener(SwitchbotRelaySwitch):
     """Representation of a Switchbot garage door opener."""
 
+    _model = SwitchbotModel.GARAGE_DOOR_OPENER
     _open_command = f"{COMMAND_CONTROL}110129"
     _close_command = f"{COMMAND_CONTROL}110229"
     _press_command = f"{COMMAND_CONTROL}110329"  # for garage door opener toggle
 
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.GARAGE_DOOR_OPENER,
-        **kwargs: Any,
-    ) -> None:
-        super().__init__(device, key_id, encryption_key, interface, model, **kwargs)
-
 
 class SwitchbotRelaySwitch2PM(SwitchbotRelaySwitch):
     """Representation of a Switchbot relay switch 2pm."""
 
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.RELAY_SWITCH_2PM,
-        **kwargs: Any,
-    ) -> None:
-        super().__init__(device, key_id, encryption_key, interface, model, **kwargs)
-        self._channel = 2
+    _model = SwitchbotModel.RELAY_SWITCH_2PM
+    _channel: int = 2
 
     @property
     def channel(self) -> int:

+ 1 - 26
switchbot/devices/smart_thermostat_radiator.py

@@ -3,8 +3,6 @@
 import logging
 from typing import Any
 
-from bleak.backends.device import BLEDevice
-
 from ..const import SwitchbotModel
 from ..const.climate import ClimateAction, ClimateMode
 from ..const.climate import SmartThermostatRadiatorMode as STRMode
@@ -50,33 +48,10 @@ class SwitchbotSmartThermostatRadiator(
 ):
     """Representation of a Switchbot Smart Thermostat Radiator."""
 
+    _model = SwitchbotModel.SMART_THERMOSTAT_RADIATOR
     _turn_off_command = "570100"
     _turn_on_command = "570101"
 
-    def __init__(
-        self,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        interface: int = 0,
-        model: SwitchbotModel = SwitchbotModel.SMART_THERMOSTAT_RADIATOR,
-        **kwargs: Any,
-    ) -> None:
-        super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
-
-    @classmethod
-    async def verify_encryption_key(
-        cls,
-        device: BLEDevice,
-        key_id: str,
-        encryption_key: str,
-        model: SwitchbotModel = SwitchbotModel.SMART_THERMOSTAT_RADIATOR,
-        **kwargs: Any,
-    ) -> bool:
-        return await super().verify_encryption_key(
-            device, key_id, encryption_key, model, **kwargs
-        )
-
     @property
     def min_temperature(self) -> float:
         """Return the minimum target temperature."""

+ 5 - 23
tests/test_air_purifier.py

@@ -1,11 +1,10 @@
-from unittest.mock import AsyncMock, MagicMock, patch
+from unittest.mock import AsyncMock, MagicMock
 
 import pytest
 from bleak.backends.device import BLEDevice
 
 from switchbot import (
     SwitchBotAdvertisement,
-    SwitchbotEncryptedDevice,
     SwitchbotModel,
     SwitchbotOperationError,
 )
@@ -254,29 +253,12 @@ async def test_get_basic_info(device_case, info_case):
     assert info["light_sensitive"] == result[10]
 
 
-@pytest.mark.asyncio
-@patch.object(SwitchbotEncryptedDevice, "verify_encryption_key", new_callable=AsyncMock)
-async def test_verify_encryption_key(mock_parent_verify):
+def test_default_model_classvar():
     ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
-    key_id = "ff"
-    encryption_key = "ffffffffffffffffffffffffffffffff"
-
-    mock_parent_verify.return_value = True
-
-    result = await air_purifier.SwitchbotAirPurifier.verify_encryption_key(
-        device=ble_device,
-        key_id=key_id,
-        encryption_key=encryption_key,
-    )
-
-    mock_parent_verify.assert_awaited_once_with(
-        ble_device,
-        key_id,
-        encryption_key,
-        SwitchbotModel.AIR_PURIFIER_US,
+    device = air_purifier.SwitchbotAirPurifier(
+        ble_device, "ff", "ffffffffffffffffffffffffffffffff"
     )
-
-    assert result is True
+    assert device._model == SwitchbotModel.AIR_PURIFIER_US
 
 
 def test_get_modes():

+ 3 - 24
tests/test_art_frame.py

@@ -5,7 +5,6 @@ from bleak.backends.device import BLEDevice
 
 from switchbot import SwitchBotAdvertisement
 from switchbot.devices.art_frame import COMMAND_SET_IMAGE, SwitchbotArtFrame
-from switchbot.devices.device import SwitchbotEncryptedDevice
 
 from . import ART_FRAME_INFO
 from .test_adv_parser import AdvTestCase, generate_ble_device
@@ -195,27 +194,7 @@ async def test_set_image_with_valid_index() -> None:
         device._send_command.assert_awaited_with(COMMAND_SET_IMAGE.format("14"))
 
 
-@pytest.mark.asyncio
-@patch.object(SwitchbotEncryptedDevice, "verify_encryption_key", new_callable=AsyncMock)
-async def test_verify_encryption_key(mock_parent_verify: AsyncMock) -> None:
+def test_default_model_classvar() -> None:
     ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
-    key_id = "ff"
-    encryption_key = "ffffffffffffffffffffffffffffffff"
-
-    mock_parent_verify.return_value = True
-
-    result = await SwitchbotArtFrame.verify_encryption_key(
-        device=ble_device,
-        key_id=key_id,
-        encryption_key=encryption_key,
-        model=ART_FRAME_INFO.modelName,
-    )
-
-    mock_parent_verify.assert_awaited_once_with(
-        ble_device,
-        key_id,
-        encryption_key,
-        ART_FRAME_INFO.modelName,
-    )
-
-    assert result is True
+    device = SwitchbotArtFrame(ble_device, "ff", "ffffffffffffffffffffffffffffffff")
+    assert device._model == ART_FRAME_INFO.modelName

+ 49 - 4
tests/test_encrypted_device.py

@@ -56,22 +56,30 @@ async def test_encrypted_device_init_validation() -> None:
     # Test empty key_id
     with pytest.raises(ValueError, match="key_id is missing"):
         MockEncryptedDevice(
-            ble_device, "", "0123456789abcdef0123456789abcdef", SwitchbotModel.LOCK
+            ble_device,
+            "",
+            "0123456789abcdef0123456789abcdef",
+            model=SwitchbotModel.LOCK,
         )
 
     # Test invalid key_id length
     with pytest.raises(ValueError, match="key_id is invalid"):
         MockEncryptedDevice(
-            ble_device, "1", "0123456789abcdef0123456789abcdef", SwitchbotModel.LOCK
+            ble_device,
+            "1",
+            "0123456789abcdef0123456789abcdef",
+            model=SwitchbotModel.LOCK,
         )
 
     # Test empty encryption_key
     with pytest.raises(ValueError, match="encryption_key is missing"):
-        MockEncryptedDevice(ble_device, "01", "", SwitchbotModel.LOCK)
+        MockEncryptedDevice(ble_device, "01", "", model=SwitchbotModel.LOCK)
 
     # Test invalid encryption_key length
     with pytest.raises(ValueError, match="encryption_key is invalid"):
-        MockEncryptedDevice(ble_device, "01", "0123456789abcdef", SwitchbotModel.LOCK)
+        MockEncryptedDevice(
+            ble_device, "01", "0123456789abcdef", model=SwitchbotModel.LOCK
+        )
 
 
 @pytest.mark.asyncio
@@ -529,6 +537,43 @@ async def test_empty_data_encryption_decryption() -> None:
     assert decrypted == b""
 
 
+@pytest.mark.asyncio
+async def test_verify_encryption_key_falls_back_to_classvar() -> None:
+    """verify_encryption_key resolves `model` from `cls._model` when omitted."""
+
+    class ClassvarModelDevice(MockEncryptedDevice):
+        _model = SwitchbotModel.LOCK
+
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "Test Device")
+    with patch.object(
+        ClassvarModelDevice,
+        "get_basic_info",
+        new_callable=AsyncMock,
+        return_value={"ok": True},
+    ):
+        result = await ClassvarModelDevice.verify_encryption_key(
+            ble_device, "01", "0123456789abcdef0123456789abcdef"
+        )
+    assert result is True
+
+
+@pytest.mark.asyncio
+async def test_verify_encryption_key_without_model_or_classvar_raises() -> None:
+    """verify_encryption_key raises when neither `model=` nor `cls._model` is set."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "Test Device")
+    with pytest.raises(ValueError, match="model must be provided"):
+        await MockEncryptedDevice.verify_encryption_key(
+            ble_device, "01", "0123456789abcdef0123456789abcdef"
+        )
+
+
+def test_init_without_model_or_classvar_raises() -> None:
+    """__init__ raises when neither `model=` nor `cls._model` is set."""
+    ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "Test Device")
+    with pytest.raises(ValueError, match="model must be provided"):
+        MockEncryptedDevice(ble_device, "01", "0123456789abcdef0123456789abcdef")
+
+
 @pytest.mark.asyncio
 async def test_decrypt_with_none_iv_during_disconnect() -> None:
     """Test that decryption returns empty bytes when IV is None during expected disconnect."""

+ 6 - 23
tests/test_evaporative_humidifier.py

@@ -1,5 +1,5 @@
 import datetime
-from unittest.mock import AsyncMock, MagicMock, patch
+from unittest.mock import AsyncMock, MagicMock
 
 import pytest
 from bleak.backends.device import BLEDevice
@@ -12,7 +12,7 @@ from switchbot import (
     SwitchbotModel,
 )
 from switchbot.devices import evaporative_humidifier
-from switchbot.devices.device import SwitchbotEncryptedDevice, SwitchbotOperationError
+from switchbot.devices.device import SwitchbotOperationError
 
 from .test_adv_parser import generate_ble_device
 
@@ -311,29 +311,12 @@ async def test_set_child_lock(enabled, command):
     device._send_command.assert_awaited_once_with(command)
 
 
-@pytest.mark.asyncio
-@patch.object(SwitchbotEncryptedDevice, "verify_encryption_key", new_callable=AsyncMock)
-async def test_verify_encryption_key(mock_parent_verify):
+def test_default_model_classvar():
     ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
-    key_id = "ff"
-    encryption_key = "ffffffffffffffffffffffffffffffff"
-
-    mock_parent_verify.return_value = True
-
-    result = await evaporative_humidifier.SwitchbotEvaporativeHumidifier.verify_encryption_key(
-        device=ble_device,
-        key_id=key_id,
-        encryption_key=encryption_key,
+    device = evaporative_humidifier.SwitchbotEvaporativeHumidifier(
+        ble_device, "ff", "ffffffffffffffffffffffffffffffff"
     )
-
-    mock_parent_verify.assert_awaited_once_with(
-        ble_device,
-        key_id,
-        encryption_key,
-        SwitchbotModel.EVAPORATIVE_HUMIDIFIER,
-    )
-
-    assert result is True
+    assert device._model == SwitchbotModel.EVAPORATIVE_HUMIDIFIER
 
 
 def test_evaporative_humidifier_modes():

+ 4 - 21
tests/test_lock.py

@@ -50,28 +50,11 @@ def test_lock_init_with_invalid_model(model: str):
         create_device_for_command_testing(model)
 
 
-@pytest.mark.asyncio
-@pytest.mark.parametrize(
-    "model",
-    [
-        SwitchbotModel.LOCK,
-        SwitchbotModel.LOCK_LITE,
-        SwitchbotModel.LOCK_PRO,
-        SwitchbotModel.LOCK_ULTRA,
-        SwitchbotModel.LOCK_VISION,
-        SwitchbotModel.LOCK_VISION_PRO,
-        SwitchbotModel.LOCK_PRO_WIFI,
-    ],
-)
-async def test_verify_encryption_key(model: str):
-    """Test verify_encryption_key method."""
+def test_default_model_classvar():
+    """The classvar default is SwitchbotModel.LOCK."""
     ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
-    with patch("switchbot.devices.lock.super") as mock_super:
-        mock_super().verify_encryption_key = AsyncMock(return_value=True)
-        result = await lock.SwitchbotLock.verify_encryption_key(
-            ble_device, "key_id", "encryption_key", model
-        )
-        assert result is True
+    device = lock.SwitchbotLock(ble_device, "ff", "ffffffffffffffffffffffffffffffff")
+    assert device._model == SwitchbotModel.LOCK
 
 
 @pytest.mark.asyncio

+ 9 - 30
tests/test_relay_switch.py

@@ -1,9 +1,9 @@
-from unittest.mock import AsyncMock, MagicMock, patch
+from unittest.mock import AsyncMock, MagicMock
 
 import pytest
 from bleak.backends.device import BLEDevice
 
-from switchbot import SwitchBotAdvertisement, SwitchbotEncryptedDevice, SwitchbotModel
+from switchbot import SwitchBotAdvertisement, SwitchbotModel
 from switchbot.devices import relay_switch
 from switchbot.devices.device import _merge_data as merge_data
 
@@ -393,39 +393,18 @@ async def test_get_basic_info_garage_door_opener(rawAdvData, model, info_data):
     assert info["door_open"] is True
 
 
-@pytest.mark.asyncio
 @pytest.mark.parametrize(
-    "model",
+    ("dev_cls", "expected_model"),
     [
-        SwitchbotModel.RELAY_SWITCH_1,
-        SwitchbotModel.RELAY_SWITCH_1PM,
-        SwitchbotModel.GARAGE_DOOR_OPENER,
-        SwitchbotModel.RELAY_SWITCH_2PM,
+        (relay_switch.SwitchbotRelaySwitch, SwitchbotModel.RELAY_SWITCH_1PM),
+        (relay_switch.SwitchbotGarageDoorOpener, SwitchbotModel.GARAGE_DOOR_OPENER),
+        (relay_switch.SwitchbotRelaySwitch2PM, SwitchbotModel.RELAY_SWITCH_2PM),
     ],
 )
-@patch.object(SwitchbotEncryptedDevice, "verify_encryption_key", new_callable=AsyncMock)
-async def test_verify_encryption_key(mock_parent_verify, model):
+def test_default_model_classvar(dev_cls, expected_model):
     ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
-    key_id = "ff"
-    encryption_key = "ffffffffffffffffffffffffffffffff"
-
-    mock_parent_verify.return_value = True
-
-    result = await relay_switch.SwitchbotRelaySwitch.verify_encryption_key(
-        device=ble_device,
-        key_id=key_id,
-        encryption_key=encryption_key,
-        model=model,
-    )
-
-    mock_parent_verify.assert_awaited_once_with(
-        ble_device,
-        key_id,
-        encryption_key,
-        model,
-    )
-
-    assert result is True
+    device = dev_cls(ble_device, "ff", "ffffffffffffffffffffffffffffffff")
+    assert device._model == expected_model
 
 
 @pytest.mark.parametrize(

+ 6 - 24
tests/test_smart_thermostat_radiator.py

@@ -1,4 +1,4 @@
-from unittest.mock import AsyncMock, MagicMock, patch
+from unittest.mock import AsyncMock, MagicMock
 
 import pytest
 from bleak.backends.device import BLEDevice
@@ -6,7 +6,7 @@ from bleak.backends.device import BLEDevice
 from switchbot import SwitchBotAdvertisement
 from switchbot.const.climate import ClimateAction, ClimateMode
 from switchbot.const.climate import SmartThermostatRadiatorMode as STRMode
-from switchbot.devices.device import SwitchbotEncryptedDevice, SwitchbotOperationError
+from switchbot.devices.device import SwitchbotOperationError
 from switchbot.devices.smart_thermostat_radiator import (
     COMMAND_SET_MODE,
     COMMAND_SET_TEMP,
@@ -213,27 +213,9 @@ async def test_get_basic_info_parsing(basic_info, result) -> None:
     assert info["door_open"] == result[12]
 
 
-@pytest.mark.asyncio
-@patch.object(SwitchbotEncryptedDevice, "verify_encryption_key", new_callable=AsyncMock)
-async def test_verify_encryption_key(mock_parent_verify):
+def test_default_model_classvar():
     ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
-    key_id = "ff"
-    encryption_key = "ffffffffffffffffffffffffffffffff"
-
-    mock_parent_verify.return_value = True
-
-    result = await SwitchbotSmartThermostatRadiator.verify_encryption_key(
-        device=ble_device,
-        key_id=key_id,
-        encryption_key=encryption_key,
-        model=SMART_THERMOSTAT_RADIATOR_INFO.modelName,
-    )
-
-    mock_parent_verify.assert_awaited_once_with(
-        ble_device,
-        key_id,
-        encryption_key,
-        SMART_THERMOSTAT_RADIATOR_INFO.modelName,
+    device = SwitchbotSmartThermostatRadiator(
+        ble_device, "ff", "ffffffffffffffffffffffffffffffff"
     )
-
-    assert result is True
+    assert device._model == SMART_THERMOSTAT_RADIATOR_INFO.modelName

+ 12 - 27
tests/test_strip_light.py

@@ -1,4 +1,4 @@
-from unittest.mock import AsyncMock, MagicMock, patch
+from unittest.mock import AsyncMock, MagicMock
 
 import pytest
 from bleak.backends.device import BLEDevice
@@ -7,7 +7,7 @@ from switchbot import SwitchBotAdvertisement, SwitchbotModel
 from switchbot.const.light import ColorMode
 from switchbot.devices import light_strip
 from switchbot.devices.base_light import SwitchbotBaseLight
-from switchbot.devices.device import SwitchbotEncryptedDevice, SwitchbotOperationError
+from switchbot.devices.device import SwitchbotOperationError
 
 from . import (
     FLOOR_LAMP_INFO,
@@ -304,32 +304,17 @@ async def test_set_effect_normalizes_case(device_case):
         assert device.get_effect() == test_effect  # Stored as provided
 
 
-@pytest.mark.asyncio
-@patch.object(SwitchbotEncryptedDevice, "verify_encryption_key", new_callable=AsyncMock)
-async def test_verify_encryption_key(mock_parent_verify, device_case):
+@pytest.mark.parametrize(
+    ("dev_cls", "expected_model"),
+    [
+        (light_strip.SwitchbotStripLight3, SwitchbotModel.STRIP_LIGHT_3),
+        (light_strip.SwitchbotRgbicLight, SwitchbotModel.RGBICWW_STRIP_LIGHT),
+    ],
+)
+def test_default_model_classvar(dev_cls, expected_model):
     ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
-    key_id = "ff"
-    encryption_key = "ffffffffffffffffffffffffffffffff"
-
-    mock_parent_verify.return_value = True
-
-    adv_info, dev_cls = device_case
-
-    result = await dev_cls.verify_encryption_key(
-        device=ble_device,
-        key_id=key_id,
-        encryption_key=encryption_key,
-        model=adv_info.modelName,
-    )
-
-    mock_parent_verify.assert_awaited_once_with(
-        ble_device,
-        key_id,
-        encryption_key,
-        adv_info.modelName,
-    )
-
-    assert result is True
+    device = dev_cls(ble_device, "ff", "ffffffffffffffffffffffffffffffff")
+    assert device._model == expected_model
 
 
 def create_strip_light_device(init_data: dict | None = None):