From ce0d314952ee145818c9a1f91d73798d685a288a Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Wed, 7 Jan 2026 14:30:23 +0800 Subject: [PATCH 1/4] 1. add new commands 2. add utils for AI Art Frame 3. reformat code --- pyproject.toml | 2 +- switchbot_api/__init__.py | 9 +++++++++ switchbot_api/commands.py | 17 +++++++++++++++-- switchbot_api/exceptions.py | 3 +++ switchbot_api/utils.py | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 switchbot_api/utils.py diff --git a/pyproject.toml b/pyproject.toml index 15694e0..ba55e8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "switchbot_api" -version = "2.9.0" +version = "2.10.0" description = "An asynchronous library to use Switchbot API" authors = ["Ravaka Razafimanantsoa "] license = "MIT" diff --git a/switchbot_api/__init__.py b/switchbot_api/__init__.py index 3717f77..23ba82e 100644 --- a/switchbot_api/__init__.py +++ b/switchbot_api/__init__.py @@ -46,11 +46,15 @@ VacuumCleanerV2Commands, VacuumCleanerV3Commands, VacuumCommands, + KeyPadCommands, + ArtFrameCommands, + LockV3Commands ) from switchbot_api.exceptions import ( SwitchBotAuthenticationError, SwitchBotConnectionError, SwitchBotDeviceOfflineError, + SwitchBotDeviceRequestError, ) from switchbot_api.models import ( BatteryCirculatorFanMode, @@ -59,6 +63,7 @@ VacuumCleanMode, VacuumFanSpeed, VacuumFanSpeedV2, + BatteryLevel ) __all__ = [ @@ -101,6 +106,10 @@ "VacuumCommands", "VacuumFanSpeed", "VacuumFanSpeedV2", + "KeyPadCommands", + "ArtFrameCommands", + "LockV3Commands", + "BatteryLevel" ] _API_HOST = "https://api.switch-bot.com" diff --git a/switchbot_api/commands.py b/switchbot_api/commands.py index f2f25e5..9e0a4f5 100644 --- a/switchbot_api/commands.py +++ b/switchbot_api/commands.py @@ -93,7 +93,20 @@ class LockV2Commands(Commands): @classmethod def get_supported_devices(cls) -> list[str]: """Get supported devices.""" - return ["Smart Lock", "Smart Lock Pro", "Smart Lock Ultra"] + return ["Smart Lock", "Smart Lock Pro", "Smart Lock Ultra", "Smart Lock Vision"] + +class LockV3Commands(Commands): + """Lock commands.""" + + LOCK = "lock" + UNLOCK = "unlock" + DEADBOLT = "deadbolt" + NIGHT_LATCH_UNLOCK = "nightLatchUnlock" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Smart Lock Vision Pro"] class HumidifierCommands(Commands): @@ -430,4 +443,4 @@ def get_supported_devices(cls) -> list[str]: return ["Keypad", "Keypad Touch", "Keypad Vision", "Keypad Vision pro"] -T = TypeVar("T", bound=CommonCommands) +T = TypeVar("T", bound=Commands) diff --git a/switchbot_api/exceptions.py b/switchbot_api/exceptions.py index 5de28b8..d4949b6 100644 --- a/switchbot_api/exceptions.py +++ b/switchbot_api/exceptions.py @@ -15,3 +15,6 @@ class SwitchBotAuthenticationError(SwitchBotError): class SwitchBotDeviceOfflineError(SwitchBotError): """Device currently offline.""" + +class SwitchBotDeviceRequestError(SwitchBotError): + """Device request error.""" diff --git a/switchbot_api/utils.py b/switchbot_api/utils.py new file mode 100644 index 0000000..f53d645 --- /dev/null +++ b/switchbot_api/utils.py @@ -0,0 +1,18 @@ +"""util for SwitchBot API.""" +import aiohttp + +from .exceptions import SwitchBotDeviceRequestError + + +async def get_file_stream_from_cloud(url: str, timeout:float=5) -> bytes: + """get file stream from cloud.""" + # now only for download Picture + try: + async with aiohttp.ClientSession() as session: + async with session.get(url, timeout=timeout) as response: + if response.status != 200: + raise SwitchBotDeviceRequestError("get file stream error") + file_stream = await response.read() + return file_stream + except Exception as e: + raise SwitchBotDeviceRequestError(f"{e}") \ No newline at end of file From 1e2c02c97253ecc417bde63e3e8d2cb6c7706492 Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Wed, 7 Jan 2026 14:49:38 +0800 Subject: [PATCH 2/4] 1. add new commands 2. add utils for AI Art Frame 3. reformat code --- switchbot_api/__init__.py | 17 ++++++++--------- switchbot_api/exceptions.py | 1 + switchbot_api/utils.py | 28 +++++++++++++++++++--------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/switchbot_api/__init__.py b/switchbot_api/__init__.py index 23ba82e..a3b869e 100644 --- a/switchbot_api/__init__.py +++ b/switchbot_api/__init__.py @@ -18,6 +18,7 @@ from switchbot_api.commands import ( AirConditionerCommands, AirPurifierCommands, + ArtFrameCommands, BatteryCirculatorFanCommands, BlindTiltCommands, BotCommands, @@ -30,9 +31,11 @@ FanCommands, HumidifierCommands, HumidifierV2Commands, + KeyPadCommands, LightCommands, LockCommands, LockV2Commands, + LockV3Commands, OthersCommands, RGBWLightCommands, RGBWWLightCommands, @@ -46,31 +49,29 @@ VacuumCleanerV2Commands, VacuumCleanerV3Commands, VacuumCommands, - KeyPadCommands, - ArtFrameCommands, - LockV3Commands ) from switchbot_api.exceptions import ( SwitchBotAuthenticationError, SwitchBotConnectionError, SwitchBotDeviceOfflineError, - SwitchBotDeviceRequestError, ) from switchbot_api.models import ( BatteryCirculatorFanMode, + BatteryLevel, PowerState, SmartRadiatorThermostatMode, VacuumCleanMode, VacuumFanSpeed, VacuumFanSpeedV2, - BatteryLevel ) __all__ = [ "AirConditionerCommands", "AirPurifierCommands", + "ArtFrameCommands", "BatteryCirculatorFanCommands", "BatteryCirculatorFanMode", + "BatteryLevel", "BlindTiltCommands", "BotCommands", "CeilingLightCommands", @@ -83,9 +84,11 @@ "FanCommands", "HumidifierCommands", "HumidifierV2Commands", + "KeyPadCommands", "LightCommands", "LockCommands", "LockV2Commands", + "LockV3Commands", "OthersCommands", "PowerState", "RGBWLightCommands", @@ -106,10 +109,6 @@ "VacuumCommands", "VacuumFanSpeed", "VacuumFanSpeedV2", - "KeyPadCommands", - "ArtFrameCommands", - "LockV3Commands", - "BatteryLevel" ] _API_HOST = "https://api.switch-bot.com" diff --git a/switchbot_api/exceptions.py b/switchbot_api/exceptions.py index d4949b6..bd91ec9 100644 --- a/switchbot_api/exceptions.py +++ b/switchbot_api/exceptions.py @@ -16,5 +16,6 @@ class SwitchBotAuthenticationError(SwitchBotError): class SwitchBotDeviceOfflineError(SwitchBotError): """Device currently offline.""" + class SwitchBotDeviceRequestError(SwitchBotError): """Device request error.""" diff --git a/switchbot_api/utils.py b/switchbot_api/utils.py index f53d645..f8e068b 100644 --- a/switchbot_api/utils.py +++ b/switchbot_api/utils.py @@ -1,18 +1,28 @@ """util for SwitchBot API.""" + import aiohttp +from aiohttp import ClientResponse from .exceptions import SwitchBotDeviceRequestError -async def get_file_stream_from_cloud(url: str, timeout:float=5) -> bytes: - """get file stream from cloud.""" +def check_response_status(response: ClientResponse) -> None: + """Check https response status.""" + if response.status != 200: + msg = f"status code != 200 (actual: {response.status})" + raise SwitchBotDeviceRequestError(msg) + + +async def get_file_stream_from_cloud(url: str, timeout: float = 5) -> bytes: + """Get file stream from cloud.""" # now only for download Picture try: - async with aiohttp.ClientSession() as session: - async with session.get(url, timeout=timeout) as response: - if response.status != 200: - raise SwitchBotDeviceRequestError("get file stream error") - file_stream = await response.read() - return file_stream + async with ( + aiohttp.ClientSession() as session, + session.get(url, timeout=aiohttp.ClientTimeout(total=timeout)) as response, + ): + check_response_status(response) + return await response.read() except Exception as e: - raise SwitchBotDeviceRequestError(f"{e}") \ No newline at end of file + msg = f"{e}" + raise SwitchBotDeviceRequestError(msg) from e From 81e8b967dc12c1eb8d692a4c2966c0356b345a40 Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Wed, 7 Jan 2026 14:50:19 +0800 Subject: [PATCH 3/4] format code --- switchbot_api/commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/switchbot_api/commands.py b/switchbot_api/commands.py index 9e0a4f5..f79565c 100644 --- a/switchbot_api/commands.py +++ b/switchbot_api/commands.py @@ -95,6 +95,7 @@ def get_supported_devices(cls) -> list[str]: """Get supported devices.""" return ["Smart Lock", "Smart Lock Pro", "Smart Lock Ultra", "Smart Lock Vision"] + class LockV3Commands(Commands): """Lock commands.""" From b8f1663ced4f515a3b3de8a59faec2266bf2e248 Mon Sep 17 00:00:00 2001 From: XiaoLing-git Date: Thu, 8 Jan 2026 18:00:32 +0800 Subject: [PATCH 4/4] fix --- switchbot_api/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/switchbot_api/commands.py b/switchbot_api/commands.py index f79565c..7747498 100644 --- a/switchbot_api/commands.py +++ b/switchbot_api/commands.py @@ -444,4 +444,4 @@ def get_supported_devices(cls) -> list[str]: return ["Keypad", "Keypad Touch", "Keypad Vision", "Keypad Vision pro"] -T = TypeVar("T", bound=Commands) +T = TypeVar("T", bound=CommonCommands)