|  | @@ -4,10 +4,13 @@ from __future__ import annotations
 | 
											
												
													
														|  |  import asyncio
 |  |  import asyncio
 | 
											
												
													
														|  |  import binascii
 |  |  import binascii
 | 
											
												
													
														|  |  import logging
 |  |  import logging
 | 
											
												
													
														|  | 
 |  | +from dataclasses import dataclass
 | 
											
												
													
														|  |  from typing import Any
 |  |  from typing import Any
 | 
											
												
													
														|  |  from uuid import UUID
 |  |  from uuid import UUID
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  import bleak
 |  |  import bleak
 | 
											
												
													
														|  | 
 |  | +from bleak.backends.device import BLEDevice
 | 
											
												
													
														|  | 
 |  | +from bleak.backends.scanner import AdvertisementData
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  DEFAULT_RETRY_COUNT = 3
 |  |  DEFAULT_RETRY_COUNT = 3
 | 
											
												
													
														|  |  DEFAULT_RETRY_TIMEOUT = 1
 |  |  DEFAULT_RETRY_TIMEOUT = 1
 | 
											
										
											
												
													
														|  | @@ -100,54 +103,71 @@ def _process_wosensorth(data: bytes) -> dict[str, object]:
 | 
											
												
													
														|  |      return _wosensorth_data
 |  |      return _wosensorth_data
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +@dataclass
 | 
											
												
													
														|  | 
 |  | +class SwitchBotAdvertisement:
 | 
											
												
													
														|  | 
 |  | +    """Switchbot advertisement."""
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    address: str
 | 
											
												
													
														|  | 
 |  | +    data: dict[str, Any]
 | 
											
												
													
														|  | 
 |  | +    device: BLEDevice
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +def parse_advertisement_data(
 | 
											
												
													
														|  | 
 |  | +    device: BLEDevice, advertisement_data: AdvertisementData
 | 
											
												
													
														|  | 
 |  | +) -> SwitchBotAdvertisement | None:
 | 
											
												
													
														|  | 
 |  | +    """Parse advertisement data."""
 | 
											
												
													
														|  | 
 |  | +    _services = list(advertisement_data.service_data.values())
 | 
											
												
													
														|  | 
 |  | +    if not _services:
 | 
											
												
													
														|  | 
 |  | +        return
 | 
											
												
													
														|  | 
 |  | +    _service_data = _services[0]
 | 
											
												
													
														|  | 
 |  | +    _model = chr(_service_data[0] & 0b01111111)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    supported_types: dict[str, dict[str, Any]] = {
 | 
											
												
													
														|  | 
 |  | +        "H": {"modelName": "WoHand", "func": _process_wohand},
 | 
											
												
													
														|  | 
 |  | +        "c": {"modelName": "WoCurtain", "func": _process_wocurtain},
 | 
											
												
													
														|  | 
 |  | +        "T": {"modelName": "WoSensorTH", "func": _process_wosensorth},
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    data = {
 | 
											
												
													
														|  | 
 |  | +        "address": device.address,  # MacOS uses UUIDs
 | 
											
												
													
														|  | 
 |  | +        "rawAdvData": list(advertisement_data.service_data.values())[0],
 | 
											
												
													
														|  | 
 |  | +        "data": {
 | 
											
												
													
														|  | 
 |  | +            "rssi": device.rssi,
 | 
											
												
													
														|  | 
 |  | +        },
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    if _model in supported_types:
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        data.update(
 | 
											
												
													
														|  | 
 |  | +            {
 | 
											
												
													
														|  | 
 |  | +                "isEncrypted": bool(_service_data[0] & 0b10000000),
 | 
											
												
													
														|  | 
 |  | +                "model": _model,
 | 
											
												
													
														|  | 
 |  | +                "modelName": supported_types[_model]["modelName"],
 | 
											
												
													
														|  | 
 |  | +                "data": supported_types[_model]["func"](_service_data),
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +        )
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        data["data"]["rssi"] = device.rssi
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    return SwitchBotAdvertisement(device.address, data, device)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  class GetSwitchbotDevices:
 |  |  class GetSwitchbotDevices:
 | 
											
												
													
														|  |      """Scan for all Switchbot devices and return by type."""
 |  |      """Scan for all Switchbot devices and return by type."""
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def __init__(self, interface: int = 0) -> None:
 |  |      def __init__(self, interface: int = 0) -> None:
 | 
											
												
													
														|  |          """Get switchbot devices class constructor."""
 |  |          """Get switchbot devices class constructor."""
 | 
											
												
													
														|  |          self._interface = f"hci{interface}"
 |  |          self._interface = f"hci{interface}"
 | 
											
												
													
														|  | -        self._adv_data: dict[str, Any] = {}
 |  | 
 | 
											
												
													
														|  | 
 |  | +        self._adv_data: dict[str, SwitchBotAdvertisement] = {}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def detection_callback(
 |  |      def detection_callback(
 | 
											
												
													
														|  |          self,
 |  |          self,
 | 
											
												
													
														|  | -        device: bleak.backends.device.BLEDevice,
 |  | 
 | 
											
												
													
														|  | -        advertisement_data: bleak.backends.scanner.AdvertisementData,
 |  | 
 | 
											
												
													
														|  | 
 |  | +        device: BLEDevice,
 | 
											
												
													
														|  | 
 |  | +        advertisement_data: AdvertisementData,
 | 
											
												
													
														|  |      ) -> None:
 |  |      ) -> None:
 | 
											
												
													
														|  | -        """BTLE adv scan callback."""
 |  | 
 | 
											
												
													
														|  | -        _services = list(advertisement_data.service_data.values())
 |  | 
 | 
											
												
													
														|  | -        if not _services:
 |  | 
 | 
											
												
													
														|  | -            return
 |  | 
 | 
											
												
													
														|  | -        _service_data = _services[0]
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        _device = device.address.replace(":", "").lower()
 |  | 
 | 
											
												
													
														|  | -        _model = chr(_service_data[0] & 0b01111111)
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        supported_types: dict[str, dict[str, Any]] = {
 |  | 
 | 
											
												
													
														|  | -            "H": {"modelName": "WoHand", "func": _process_wohand},
 |  | 
 | 
											
												
													
														|  | -            "c": {"modelName": "WoCurtain", "func": _process_wocurtain},
 |  | 
 | 
											
												
													
														|  | -            "T": {"modelName": "WoSensorTH", "func": _process_wosensorth},
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        self._adv_data[_device] = {
 |  | 
 | 
											
												
													
														|  | -            "mac_address": device.address.lower(),
 |  | 
 | 
											
												
													
														|  | -            "rawAdvData": list(advertisement_data.service_data.values())[0],
 |  | 
 | 
											
												
													
														|  | -            "data": {
 |  | 
 | 
											
												
													
														|  | -                "rssi": device.rssi,
 |  | 
 | 
											
												
													
														|  | -            },
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        if _model in supported_types:
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -            self._adv_data[_device].update(
 |  | 
 | 
											
												
													
														|  | -                {
 |  | 
 | 
											
												
													
														|  | -                    "isEncrypted": bool(_service_data[0] & 0b10000000),
 |  | 
 | 
											
												
													
														|  | -                    "model": _model,
 |  | 
 | 
											
												
													
														|  | -                    "modelName": supported_types[_model]["modelName"],
 |  | 
 | 
											
												
													
														|  | -                    "data": supported_types[_model]["func"](_service_data),
 |  | 
 | 
											
												
													
														|  | -                }
 |  | 
 | 
											
												
													
														|  | -            )
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -            self._adv_data[_device]["data"]["rssi"] = device.rssi
 |  | 
 | 
											
												
													
														|  | 
 |  | +        discovery = parse_advertisement_data(device, advertisement_data)
 | 
											
												
													
														|  | 
 |  | +        if discovery:
 | 
											
												
													
														|  | 
 |  | +            self._adv_data[discovery.address] = discovery
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      async def discover(
 |  |      async def discover(
 | 
											
												
													
														|  |          self, retry: int = DEFAULT_RETRY_COUNT, scan_timeout: int = DEFAULT_SCAN_TIMEOUT
 |  |          self, retry: int = DEFAULT_RETRY_COUNT, scan_timeout: int = DEFAULT_SCAN_TIMEOUT
 | 
											
										
											
												
													
														|  | @@ -155,7 +175,6 @@ class GetSwitchbotDevices:
 | 
											
												
													
														|  |          """Find switchbot devices and their advertisement data."""
 |  |          """Find switchbot devices and their advertisement data."""
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          devices = None
 |  |          devices = None
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |          devices = bleak.BleakScanner(
 |  |          devices = bleak.BleakScanner(
 | 
											
												
													
														|  |              # TODO: Find new UUIDs to filter on. For example, see
 |  |              # TODO: Find new UUIDs to filter on. For example, see
 | 
											
												
													
														|  |              # https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/4ad138bb09f0fbbfa41b152ca327a78c1d0b6ba9/devicetypes/meter.md
 |  |              # https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/4ad138bb09f0fbbfa41b152ca327a78c1d0b6ba9/devicetypes/meter.md
 | 
											
										
											
												
													
														|  | @@ -184,46 +203,35 @@ class GetSwitchbotDevices:
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          return self._adv_data
 |  |          return self._adv_data
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    async def get_curtains(self) -> dict:
 |  | 
 | 
											
												
													
														|  | -        """Return all WoCurtain/Curtains devices with services data."""
 |  | 
 | 
											
												
													
														|  | 
 |  | +    async def _get_devices_by_model(
 | 
											
												
													
														|  | 
 |  | +        self,
 | 
											
												
													
														|  | 
 |  | +        model: str,
 | 
											
												
													
														|  | 
 |  | +    ) -> dict:
 | 
											
												
													
														|  | 
 |  | +        """Get switchbot devices by type."""
 | 
											
												
													
														|  |          if not self._adv_data:
 |  |          if not self._adv_data:
 | 
											
												
													
														|  |              await self.discover()
 |  |              await self.discover()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        _curtain_devices = {
 |  | 
 | 
											
												
													
														|  | -            device: data
 |  | 
 | 
											
												
													
														|  | -            for device, data in self._adv_data.items()
 |  | 
 | 
											
												
													
														|  | -            if data.get("model") == "c"
 |  | 
 | 
											
												
													
														|  | 
 |  | +        return {
 | 
											
												
													
														|  | 
 |  | +            address: adv
 | 
											
												
													
														|  | 
 |  | +            for address, adv in self._adv_data.items()
 | 
											
												
													
														|  | 
 |  | +            if adv.data.get("model") == model
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        return _curtain_devices
 |  | 
 | 
											
												
													
														|  | 
 |  | +    async def get_curtains(self) -> dict[str, SwitchBotAdvertisement]:
 | 
											
												
													
														|  | 
 |  | +        """Return all WoCurtain/Curtains devices with services data."""
 | 
											
												
													
														|  | 
 |  | +        return await self._get_devices_by_model("c")
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    async def get_bots(self) -> dict[str, Any] | None:
 |  | 
 | 
											
												
													
														|  | 
 |  | +    async def get_bots(self) -> dict[str, SwitchBotAdvertisement]:
 | 
											
												
													
														|  |          """Return all WoHand/Bot devices with services data."""
 |  |          """Return all WoHand/Bot devices with services data."""
 | 
											
												
													
														|  | -        if not self._adv_data:
 |  | 
 | 
											
												
													
														|  | -            await self.discover()
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        _bot_devices = {
 |  | 
 | 
											
												
													
														|  | -            device: data
 |  | 
 | 
											
												
													
														|  | -            for device, data in self._adv_data.items()
 |  | 
 | 
											
												
													
														|  | -            if data.get("model") == "H"
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        return _bot_devices
 |  | 
 | 
											
												
													
														|  | 
 |  | +        return await self._get_devices_by_model("H")
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    async def get_tempsensors(self) -> dict[str, Any] | None:
 |  | 
 | 
											
												
													
														|  | 
 |  | +    async def get_tempsensors(self) -> dict[str, SwitchBotAdvertisement]:
 | 
											
												
													
														|  |          """Return all WoSensorTH/Temp sensor devices with services data."""
 |  |          """Return all WoSensorTH/Temp sensor devices with services data."""
 | 
											
												
													
														|  | -        if not self._adv_data:
 |  | 
 | 
											
												
													
														|  | -            await self.discover()
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        _bot_temp = {
 |  | 
 | 
											
												
													
														|  | -            device: data
 |  | 
 | 
											
												
													
														|  | -            for device, data in self._adv_data.items()
 |  | 
 | 
											
												
													
														|  | -            if data.get("model") == "T"
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        return _bot_temp
 |  | 
 | 
											
												
													
														|  | 
 |  | +        return await self._get_devices_by_model("T")
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    async def get_device_data(self, mac: str) -> dict[str, Any] | None:
 |  | 
 | 
											
												
													
														|  | 
 |  | +    async def get_device_data(
 | 
											
												
													
														|  | 
 |  | +        self, address: str
 | 
											
												
													
														|  | 
 |  | +    ) -> dict[str, SwitchBotAdvertisement] | None:
 | 
											
												
													
														|  |          """Return data for specific device."""
 |  |          """Return data for specific device."""
 | 
											
												
													
														|  |          if not self._adv_data:
 |  |          if not self._adv_data:
 | 
											
												
													
														|  |              await self.discover()
 |  |              await self.discover()
 | 
											
										
											
												
													
														|  | @@ -231,7 +239,8 @@ class GetSwitchbotDevices:
 | 
											
												
													
														|  |          _switchbot_data = {
 |  |          _switchbot_data = {
 | 
											
												
													
														|  |              device: data
 |  |              device: data
 | 
											
												
													
														|  |              for device, data in self._adv_data.items()
 |  |              for device, data in self._adv_data.items()
 | 
											
												
													
														|  | -            if data.get("mac_address") == mac
 |  | 
 | 
											
												
													
														|  | 
 |  | +            # MacOS uses UUIDs instead of MAC addresses
 | 
											
												
													
														|  | 
 |  | +            if data.get("address") == address
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          return _switchbot_data
 |  |          return _switchbot_data
 | 
											
										
											
												
													
														|  | @@ -242,15 +251,15 @@ class SwitchbotDevice:
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def __init__(
 |  |      def __init__(
 | 
											
												
													
														|  |          self,
 |  |          self,
 | 
											
												
													
														|  | -        mac: str,
 |  | 
 | 
											
												
													
														|  | 
 |  | +        device: BLEDevice,
 | 
											
												
													
														|  |          password: str | None = None,
 |  |          password: str | None = None,
 | 
											
												
													
														|  |          interface: int = 0,
 |  |          interface: int = 0,
 | 
											
												
													
														|  |          **kwargs: Any,
 |  |          **kwargs: Any,
 | 
											
												
													
														|  |      ) -> None:
 |  |      ) -> None:
 | 
											
												
													
														|  |          """Switchbot base class constructor."""
 |  |          """Switchbot base class constructor."""
 | 
											
												
													
														|  |          self._interface = f"hci{interface}"
 |  |          self._interface = f"hci{interface}"
 | 
											
												
													
														|  | -        self._mac = mac.replace("-", ":").lower()
 |  | 
 | 
											
												
													
														|  | -        self._sb_adv_data: dict[str, Any] = {}
 |  | 
 | 
											
												
													
														|  | 
 |  | +        self._device = device
 | 
											
												
													
														|  | 
 |  | +        self._sb_adv_data: SwitchBotAdvertisement | None = None
 | 
											
												
													
														|  |          self._scan_timeout: int = kwargs.pop("scan_timeout", DEFAULT_SCAN_TIMEOUT)
 |  |          self._scan_timeout: int = kwargs.pop("scan_timeout", DEFAULT_SCAN_TIMEOUT)
 | 
											
												
													
														|  |          self._retry_count: int = kwargs.pop("retry_count", DEFAULT_RETRY_COUNT)
 |  |          self._retry_count: int = kwargs.pop("retry_count", DEFAULT_RETRY_COUNT)
 | 
											
												
													
														|  |          if password is None or password == "":
 |  |          if password is None or password == "":
 | 
											
										
											
												
													
														|  | @@ -279,13 +288,11 @@ class SwitchbotDevice:
 | 
											
												
													
														|  |          notify_msg = b""
 |  |          notify_msg = b""
 | 
											
												
													
														|  |          _LOGGER.debug("Sending command to switchbot %s", command)
 |  |          _LOGGER.debug("Sending command to switchbot %s", command)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        if len(self._mac.split(":")) != 6:
 |  | 
 | 
											
												
													
														|  | -            raise ValueError("Expected MAC address, got %s" % repr(self._mac))
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |          async with CONNECT_LOCK:
 |  |          async with CONNECT_LOCK:
 | 
											
												
													
														|  |              try:
 |  |              try:
 | 
											
												
													
														|  |                  async with bleak.BleakClient(
 |  |                  async with bleak.BleakClient(
 | 
											
												
													
														|  | -                    address_or_ble_device=self._mac, timeout=float(self._scan_timeout)
 |  | 
 | 
											
												
													
														|  | 
 |  | +                    address_or_ble_device=self._device,
 | 
											
												
													
														|  | 
 |  | +                    timeout=float(self._scan_timeout),
 | 
											
												
													
														|  |                  ) as client:
 |  |                  ) as client:
 | 
											
												
													
														|  |                      _LOGGER.debug("Connnected to switchbot: %s", client.is_connected)
 |  |                      _LOGGER.debug("Connnected to switchbot: %s", client.is_connected)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -334,15 +341,24 @@ class SwitchbotDevice:
 | 
											
												
													
														|  |          await asyncio.sleep(DEFAULT_RETRY_TIMEOUT)
 |  |          await asyncio.sleep(DEFAULT_RETRY_TIMEOUT)
 | 
											
												
													
														|  |          return await self._sendcommand(key, retry - 1)
 |  |          return await self._sendcommand(key, retry - 1)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    def get_mac(self) -> str:
 |  | 
 | 
											
												
													
														|  | -        """Return mac address of device."""
 |  | 
 | 
											
												
													
														|  | -        return self._mac
 |  | 
 | 
											
												
													
														|  | 
 |  | +    def get_address(self) -> str:
 | 
											
												
													
														|  | 
 |  | +        """Return address of device."""
 | 
											
												
													
														|  | 
 |  | +        return self._device.address
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    def get_battery_percent(self) -> Any:
 |  | 
 | 
											
												
													
														|  | -        """Return device battery level in percent."""
 |  | 
 | 
											
												
													
														|  | 
 |  | +    def _get_adv_value(self, key: str) -> Any:
 | 
											
												
													
														|  | 
 |  | +        """Return value from advertisement data."""
 | 
											
												
													
														|  |          if not self._sb_adv_data:
 |  |          if not self._sb_adv_data:
 | 
											
												
													
														|  |              return None
 |  |              return None
 | 
											
												
													
														|  | -        return self._sb_adv_data["data"]["battery"]
 |  | 
 | 
											
												
													
														|  | 
 |  | +        return self._sb_adv_data.data["data"][key]
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def get_battery_percent(self) -> Any:
 | 
											
												
													
														|  | 
 |  | +        """Return device battery level in percent."""
 | 
											
												
													
														|  | 
 |  | +        return self._get_adv_value("battery")
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def update_from_advertisement(self, advertisement: SwitchBotAdvertisement) -> None:
 | 
											
												
													
														|  | 
 |  | +        """Update device data from advertisement."""
 | 
											
												
													
														|  | 
 |  | +        self._sb_adv_data = advertisement
 | 
											
												
													
														|  | 
 |  | +        self._device = advertisement.device
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      async def get_device_data(
 |  |      async def get_device_data(
 | 
											
												
													
														|  |          self, retry: int = DEFAULT_RETRY_COUNT, interface: int | None = None
 |  |          self, retry: int = DEFAULT_RETRY_COUNT, interface: int | None = None
 | 
											
										
											
												
													
														|  | @@ -353,14 +369,12 @@ class SwitchbotDevice:
 | 
											
												
													
														|  |          else:
 |  |          else:
 | 
											
												
													
														|  |              _interface = int(self._interface.replace("hci", ""))
 |  |              _interface = int(self._interface.replace("hci", ""))
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        dev_id = self._mac.replace(":", "")
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |          _data = await GetSwitchbotDevices(interface=_interface).discover(
 |  |          _data = await GetSwitchbotDevices(interface=_interface).discover(
 | 
											
												
													
														|  |              retry=retry, scan_timeout=self._scan_timeout
 |  |              retry=retry, scan_timeout=self._scan_timeout
 | 
											
												
													
														|  |          )
 |  |          )
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        if _data.get(dev_id):
 |  | 
 | 
											
												
													
														|  | -            self._sb_adv_data = _data[dev_id]
 |  | 
 | 
											
												
													
														|  | 
 |  | +        if self._device.address in _data:
 | 
											
												
													
														|  | 
 |  | +            self._sb_adv_data = _data[self._device.address]
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          return self._sb_adv_data
 |  |          return self._sb_adv_data
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -493,20 +507,18 @@ class Switchbot(SwitchbotDevice):
 | 
											
												
													
														|  |      def switch_mode(self) -> Any:
 |  |      def switch_mode(self) -> Any:
 | 
											
												
													
														|  |          """Return true or false from cache."""
 |  |          """Return true or false from cache."""
 | 
											
												
													
														|  |          # To get actual position call update() first.
 |  |          # To get actual position call update() first.
 | 
											
												
													
														|  | -        if not self._sb_adv_data.get("data"):
 |  | 
 | 
											
												
													
														|  | -            return None
 |  | 
 | 
											
												
													
														|  | -        return self._sb_adv_data["data"].get("switchMode")
 |  | 
 | 
											
												
													
														|  | 
 |  | +        return self._get_adv_value("switchMode")
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def is_on(self) -> Any:
 |  |      def is_on(self) -> Any:
 | 
											
												
													
														|  |          """Return switch state from cache."""
 |  |          """Return switch state from cache."""
 | 
											
												
													
														|  |          # To get actual position call update() first.
 |  |          # To get actual position call update() first.
 | 
											
												
													
														|  | -        if not self._sb_adv_data.get("data"):
 |  | 
 | 
											
												
													
														|  | 
 |  | +        value = self._get_adv_value("isOn")
 | 
											
												
													
														|  | 
 |  | +        if value is None:
 | 
											
												
													
														|  |              return None
 |  |              return None
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          if self._inverse:
 |  |          if self._inverse:
 | 
											
												
													
														|  | -            return not self._sb_adv_data["data"].get("isOn")
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        return self._sb_adv_data["data"].get("isOn")
 |  | 
 | 
											
												
													
														|  | 
 |  | +            return not value
 | 
											
												
													
														|  | 
 |  | +        return value
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  class SwitchbotCurtain(SwitchbotDevice):
 |  |  class SwitchbotCurtain(SwitchbotDevice):
 | 
											
										
											
												
													
														|  | @@ -570,9 +582,7 @@ class SwitchbotCurtain(SwitchbotDevice):
 | 
											
												
													
														|  |      def get_position(self) -> Any:
 |  |      def get_position(self) -> Any:
 | 
											
												
													
														|  |          """Return cached position (0-100) of Curtain."""
 |  |          """Return cached position (0-100) of Curtain."""
 | 
											
												
													
														|  |          # To get actual position call update() first.
 |  |          # To get actual position call update() first.
 | 
											
												
													
														|  | -        if not self._sb_adv_data.get("data"):
 |  | 
 | 
											
												
													
														|  | -            return None
 |  | 
 | 
											
												
													
														|  | -        return self._sb_adv_data["data"].get("position")
 |  | 
 | 
											
												
													
														|  | 
 |  | +        return self._get_adv_value("position")
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      async def get_basic_info(self) -> dict[str, Any] | None:
 |  |      async def get_basic_info(self) -> dict[str, Any] | None:
 | 
											
												
													
														|  |          """Get device basic settings."""
 |  |          """Get device basic settings."""
 | 
											
										
											
												
													
														|  | @@ -676,9 +686,7 @@ class SwitchbotCurtain(SwitchbotDevice):
 | 
											
												
													
														|  |      def get_light_level(self) -> Any:
 |  |      def get_light_level(self) -> Any:
 | 
											
												
													
														|  |          """Return cached light level."""
 |  |          """Return cached light level."""
 | 
											
												
													
														|  |          # To get actual light level call update() first.
 |  |          # To get actual light level call update() first.
 | 
											
												
													
														|  | -        if not self._sb_adv_data.get("data"):
 |  | 
 | 
											
												
													
														|  | -            return None
 |  | 
 | 
											
												
													
														|  | -        return self._sb_adv_data["data"].get("lightLevel")
 |  | 
 | 
											
												
													
														|  | 
 |  | +        return self._get_adv_value("lightLevel")
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def is_reversed(self) -> bool:
 |  |      def is_reversed(self) -> bool:
 | 
											
												
													
														|  |          """Return True if curtain position is opposite from SB data."""
 |  |          """Return True if curtain position is opposite from SB data."""
 | 
											
										
											
												
													
														|  | @@ -687,6 +695,4 @@ class SwitchbotCurtain(SwitchbotDevice):
 | 
											
												
													
														|  |      def is_calibrated(self) -> Any:
 |  |      def is_calibrated(self) -> Any:
 | 
											
												
													
														|  |          """Return True curtain is calibrated."""
 |  |          """Return True curtain is calibrated."""
 | 
											
												
													
														|  |          # To get actual light level call update() first.
 |  |          # To get actual light level call update() first.
 | 
											
												
													
														|  | -        if not self._sb_adv_data.get("data"):
 |  | 
 | 
											
												
													
														|  | -            return None
 |  | 
 | 
											
												
													
														|  | -        return self._sb_adv_data["data"].get("calibration")
 |  | 
 | 
											
												
													
														|  | 
 |  | +        return self._get_adv_value("calibration")
 |