diff --git a/makefile b/makefile index 8d91a38..8a70cbc 100644 --- a/makefile +++ b/makefile @@ -8,6 +8,8 @@ build: format:pre_commit poetry run black switchbot_api/ poetry run mypy switchbot_api/ + poetry run ruff check --fix switchbot_api/__init__.py + poetry run ruff check --output-format=github . clean: rm -rf dist diff --git a/pyproject.toml b/pyproject.toml index eca652b..fd195d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "switchbot_api" -version = "2.6.0" +version = "2.7.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 75d6f1a..60cb9b4 100644 --- a/switchbot_api/__init__.py +++ b/switchbot_api/__init__.py @@ -4,23 +4,97 @@ import base64 from dataclasses import dataclass -from enum import Enum, StrEnum import hashlib import hmac import logging import socket import time -from typing import Any, Self, TypeVar +from typing import Any, Self import uuid from aiohttp import ClientError, ClientResponseError, ClientSession from aiohttp.hdrs import METH_GET, METH_POST +from switchbot_api.commands import ( + AirConditionerCommands, + AirPurifierCommands, + BatteryCirculatorFanCommands, + BlindTiltCommands, + BotCommands, + CeilingLightCommands, + Commands, + CommonCommands, + CurtainCommands, + DoorBellCommands, + DVDCommands, + FanCommands, + HumidifierCommands, + HumidifierV2Commands, + LightCommands, + LockCommands, + LockV2Commands, + OthersCommands, + RGBWLightCommands, + RGBWWLightCommands, + RollerShadeCommands, + SpeakerCommands, + Switch2PMCommands, + SwitchCommands, + T, + TVCommands, + VacuumCleanerV2Commands, + VacuumCleanerV3Commands, + VacuumCommands, +) from switchbot_api.exceptions import ( SwitchBotAuthenticationError, SwitchBotConnectionError, SwitchBotDeviceOfflineError, ) +from switchbot_api.models import ( + BatteryCirculatorFanMode, + PowerState, + VacuumCleanMode, + VacuumFanSpeed, + VacuumFanSpeedV2, +) + +__all__ = [ + "AirConditionerCommands", + "AirPurifierCommands", + "BatteryCirculatorFanCommands", + "BatteryCirculatorFanMode", + "BlindTiltCommands", + "BotCommands", + "CeilingLightCommands", + "Commands", + "CommonCommands", + "CurtainCommands", + "DVDCommands", + "DoorBellCommands", + "FanCommands", + "HumidifierCommands", + "HumidifierV2Commands", + "LightCommands", + "LockCommands", + "LockV2Commands", + "OthersCommands", + "PowerState", + "RGBWLightCommands", + "RGBWWLightCommands", + "RollerShadeCommands", + "SpeakerCommands", + "Switch2PMCommands", + "SwitchCommands", + "T", + "TVCommands", + "VacuumCleanMode", + "VacuumCleanerV2Commands", + "VacuumCleanerV3Commands", + "VacuumCommands", + "VacuumFanSpeed", + "VacuumFanSpeedV2", +] _API_HOST = "https://api.switch-bot.com" @@ -66,261 +140,6 @@ def __init__(self, **kwargs: Any) -> None: self.hub_device_id = kwargs["hubDeviceId"] -class PowerState(Enum): - """Power state.""" - - ON = "on" - OFF = "off" - - -class Commands(Enum): - """Base command class.""" - - -class CommonCommands(Commands): - """Common commands.""" - - ON = "turnOn" - OFF = "turnOff" - TOGGLE = "toggle" - - -class OthersCommands(Commands): - """Others commands.""" - - CUSTOMIZE = "customize" # Command {user-defined button name} - - -class AirConditionerCommands(Commands): - """Air conditioner commands.""" - - SET_ALL = "setAll" # parameter: {temperature},{mode},{fan speed},{power state} - - -class HumidifierCommands(Commands): - """Humidifier commands.""" - - # parameter: auto, set to Auto Mode - # 101, set atomization efficiency to 34% - # 102, set atomization efficiency to 67% - # 103, set atomization efficiency to 100% - SET_MODE = "setMode" - - -class HumidifierV2Commands(Commands): - """Humidifier 2 commands.""" - - SET_MODE = "setMode" - SET_CHILD_LOCK = "setChildLock" - - -class AirPurifierCommands(Commands): - """Purifier commands.""" - - # Supported Device List: - # Air Purifier VOC - # Air Purifier Table VOC - # Air Purifier PM2.5 - # Air Purifier Table PM2.5 - - SET_MODE = "setMode" - SET_CHILD_LOCK = "setChildLock" - - -class CurtainCommands(Commands): - """Curtain & Curtain3 commands.""" - - SET_POSITION = "setPosition" - PAUSE = "pause" - - -class SwitchCommands(Commands): - """Switch commands.""" - - # Supported Device List: - # Relay Switch 1 - # Relay Switch 1PM - SET_MODE = "setMode" - - -class Switch2PMCommands(Commands): - """Switch commands.""" - - # Supported Device List: - # Relay Switch 2PM - SET_MODE = "setMode" - SET_POSITION = "setPosition" - - -class RGBWLightCommands(Commands): - """RGBWLight commands.""" - - # Supported Device List: - # Strip Light - SET_BRIGHTNESS = "setBrightness" - SET_COLOR = "setColor" - - -class RGBWWLightCommands(Commands): - """RGBWwLight commands.""" - - # Supported Device List: - # Floor Lamp - # Strip Light 3 - # Color Bulb - SET_BRIGHTNESS = "setBrightness" - SET_COLOR = "setColor" - SET_COLOR_TEMPERATURE = "setColorTemperature" - - -class DoorBellCommands(Commands): - """Door Bell commands.""" - - ENABLE = "enableMotionDetection" - DISABLE = "disableMotionDetection" - - -class VacuumCleanerV2Commands(Commands): - """VacuumCleanerV2 commands.""" - - # Supported Device List: - # K20+ Pro - # Robot Vacuum Cleaner K10+ Pro Combo - START_CLEAN = "startClean" - PAUSE = "pause" - DOCK = "dock" - SET_VOLUME = "setVolume" - CHANGE_PARAM = "changeParam" - - -class VacuumCleanerV3Commands(Commands): - """VacuumCleanerV3 commands.""" - - # Supported Device List: - # Floor Cleaning Robot S10 - # S20 - START_CLEAN = "startClean" - PAUSE = "pause" - DOCK = "dock" - SET_VOLUME = "setVolume" - CHANGE_PARAM = "changeParam" - ADD_WATER_FOR_HUMI = "addWaterForHumi" - SELF_CLEAN = "selfClean" - - -class BlindTiltCommands(Commands): - """Blind Tilt commands.""" - - SET_POSITION = "setPosition" - FULLY_OPEN = "fullyOpen" - CLOSE_UP = "closeUp" - CLOSE_DOWN = "closeDown" - - -class RollerShadeCommands(Commands): - """Roller Shade commands.""" - - SET_POSITION = "setPosition" - - -class TVCommands(Commands): - """TV commands.""" - - SET_CHANNEL = "SetChannel" - VOLUME_ADD = "volumeAdd" - VOLUME_SUB = "volumeSub" - CHANNEL_ADD = "channelAdd" - CHANNEL_SUB = "channelSub" - - -class DVDCommands(Commands): - """DVD commands.""" - - SET_MUTE = "setMute" - FAST_FORWARD = "FastForward" - REWIND = "Rewind" - NEXT = "Next" - PREVIOUS = "Previous" - PAUSE = "Pause" - PLAY = "Play" - STOP = "Stop" - - -class SpeakerCommands(Commands): - """Speaker commands.""" - - VOLUME_ADD = "volumeAdd" - VOLUME_SUB = "volumeSub" - - -class FanCommands(Commands): - """Fan commands.""" - - SWING = "swing" - TIMER = "timer" - LOW_SPEED = "lowSpeed" - MIDDLE_SPEED = "middleSpeed" - HIGH_SPEED = "highSpeed" - - -class BatteryCirculatorFanCommands(Commands): - """Command types for [Battery Circulator Fan] API.""" - - SET_WIND_SPEED = "setWindSpeed" - SET_WIND_MODE = "setWindMode" - SET_NIGHT_LIGHT_MODE = "setNightLightMode" - - -class LightCommands(Commands): - """Light commands.""" - - BRIGHTNESS_UP = "brightnessUp" - BRIGHTNESS_DOWN = "brightnessDown" - - -class LockCommands(Commands): - """Lock commands.""" - - LOCK = "lock" - UNLOCK = "unlock" - - -class CeilingLightCommands(Commands): - """Ceiling light commands.""" - - # 1-100 - SET_BRIGHTNESS = "setBrightness" - # 2700-6500 - SET_COLOR_TEMPERATURE = "setColorTemperature" - - -class VacuumCommands(Commands): - """Vacuum commands.""" - - START = "start" - STOP = "stop" - DOCK = "dock" - POW_LEVEL = "PowLevel" - - -class BotCommands(Commands): - """Bot commands.""" - - PRESS = "press" - - -class BatteryCirculatorFanMode(StrEnum): - """Fan mode types [Battery Circulator Fan] API.""" - - DIRECT = "direct" - NATURAL = "natural" - SLEEP = "sleep" - BABY = "baby" - - -T = TypeVar("T", bound=CommonCommands) - - class SwitchBotAPI: """SwitchBot API.""" diff --git a/switchbot_api/commands.py b/switchbot_api/commands.py new file mode 100644 index 0000000..580ee7b --- /dev/null +++ b/switchbot_api/commands.py @@ -0,0 +1,387 @@ +"""Base Commands for SwitchBot API.""" + +from __future__ import annotations + +from enum import Enum +from typing import TypeVar + + +class Commands(Enum): + """Base command class.""" + + @classmethod + def is_supported(cls, device_type: str) -> bool: + """Is this commands supported this device type.""" + return device_type in cls.get_supported_devices() + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return [] + + +class CommonCommands(Commands): + """Common commands.""" + + ON = "turnOn" + OFF = "turnOff" + TOGGLE = "toggle" + PRESS = "press" + + # Considering the wide range of CommonCommands, they are not inherited here. + @classmethod + def is_supported(cls, device_type: str) -> bool: + """Is this commands supported this device type.""" + error_msg = "CommonCommands not implement this method" + raise NotImplementedError(error_msg) + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + error_msg = "CommonCommands not implement this method" + raise NotImplementedError(error_msg) + + +class BotCommands(Commands): + """Bot commands.""" + + PRESS = "press" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Bot"] + + +class OthersCommands(Commands): + """Others commands.""" + + CUSTOMIZE = "customize" # Command {user-defined button name} + + +class CurtainCommands(Commands): + """Curtain & Curtain3 commands.""" + + SET_POSITION = "setPosition" # parameter(str): index0,mode0,position0 e.g. 0,ff,80 + PAUSE = "pause" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Curtain", "Curtain 3"] + + +class LockCommands(Commands): + """Lock commands.""" + + LOCK = "lock" + UNLOCK = "unlock" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Smart Lock", "Smart Lock Lite", "Smart Lock Pro", "Smart Lock Ultra"] + + +class LockV2Commands(Commands): + """Lock commands.""" + + LOCK = "lock" + UNLOCK = "unlock" + DEADBOLT = "deadbolt" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Smart Lock", "Smart Lock Pro", "Smart Lock Ultra"] + + +class HumidifierCommands(Commands): + """Humidifier commands.""" + + # parameter: auto, set to Auto Mode + # 101, set atomization efficiency to 34% + # 102, set atomization efficiency to 67% + # 103, set atomization efficiency to 100% + SET_MODE = "setMode" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Humidifier"] + + +class HumidifierV2Commands(Commands): + """Humidifier 2 commands.""" + + SET_MODE = "setMode" + SET_CHILD_LOCK = "setChildLock" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Humidifier 2"] + + +class AirPurifierCommands(Commands): + """Purifier commands.""" + + # Supported Device list: + # Air Purifier VOC + # Air Purifier Table VOC + # Air Purifier PM2.5 + # Air Purifier Table PM2.5 + + SET_MODE = "setMode" + SET_CHILD_LOCK = "setChildLock" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return [ + "Air Purifier VOC", + "Air Purifier Table VOC", + "Air Purifier PM2.5", + "Air Purifier Table PM2.5", + ] + + +class AirConditionerCommands(Commands): + """Air conditioner commands.""" + + SET_ALL = "setAll" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Air Conditioner"] + + +class SwitchCommands(Commands): + """Switch commands.""" + + # Supported Device list: + # Relay Switch 1 + # Relay Switch 1PM + SET_MODE = "setMode" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Relay Switch 1", "Relay Switch 1PM"] + + +class Switch2PMCommands(Commands): + """Switch commands.""" + + # Supported Device list: + # Relay Switch 2PM + SET_MODE = "setMode" + SET_POSITION = "setPosition" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Relay Switch 2PM"] + + +class RGBWLightCommands(Commands): + """RGBWLight commands.""" + + # Supported Device list: + # Strip Light + SET_BRIGHTNESS = "setBrightness" + SET_COLOR = "setColor" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Strip Light"] + + +class RGBWWLightCommands(Commands): + """RGBWwLight commands.""" + + # Supported Device list: + # Floor Lamp + # Strip Light 3 + # Color Bulb + SET_BRIGHTNESS = "setBrightness" + SET_COLOR = "setColor" + SET_COLOR_TEMPERATURE = "setColorTemperature" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Strip Light 3", "Floor Lamp", "Color Bulb"] + + +class DoorBellCommands(Commands): + """Door Bell commands.""" + + ENABLE = "enableMotionDetection" + DISABLE = "disableMotionDetection" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Video Doorbell"] + + +class VacuumCommands(Commands): + """Vacuum commands.""" + + START = "start" + STOP = "stop" + DOCK = "dock" + POW_LEVEL = "PowLevel" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return [ + "K10+", + "K10+ Pro", + "Robot Vacuum Cleaner S1", + "Robot Vacuum Cleaner S1 Plus", + ] + + +class VacuumCleanerV2Commands(Commands): + """VacuumCleanerV2 commands.""" + + # Supported Device list: + # K20+ Pro + # Robot Vacuum Cleaner K10+ Pro Combo + START_CLEAN = "startClean" + PAUSE = "pause" + DOCK = "dock" + SET_VOLUME = "setVolume" + CHANGE_PARAM = "changeParam" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["K20+ Pro", "Robot Vacuum Cleaner K10+ Pro Combo"] + + +class VacuumCleanerV3Commands(Commands): + """VacuumCleanerV3 commands.""" + + # Supported Device list: + # Floor Cleaning Robot S10 + # S20 + START_CLEAN = "startClean" + PAUSE = "pause" + DOCK = "dock" + SET_VOLUME = "setVolume" + CHANGE_PARAM = "changeParam" + ADD_WATER_FOR_HUMI = "addWaterForHumi" + SELF_CLEAN = "selfClean" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Robot Vacuum Cleaner S10", "S20"] + + +class CeilingLightCommands(Commands): + """Ceiling light commands.""" + + # 1-100 + SET_BRIGHTNESS = "setBrightness" + # 2700-6500 + SET_COLOR_TEMPERATURE = "setColorTemperature" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Ceiling Light", "Ceiling Light Pro"] + + +class BlindTiltCommands(Commands): + """Blind Tilt commands.""" + + SET_POSITION = "setPosition" + FULLY_OPEN = "fullyOpen" + CLOSE_UP = "closeUp" + CLOSE_DOWN = "closeDown" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Blind Tilt"] + + +class RollerShadeCommands(Commands): + """Roller Shade commands.""" + + SET_POSITION = "setPosition" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Roller Shade"] + + +class BatteryCirculatorFanCommands(Commands): + """Command types for [Battery Circulator Fan] API.""" + + SET_WIND_SPEED = "setWindSpeed" + SET_WIND_MODE = "setWindMode" + SET_NIGHT_LIGHT_MODE = "setNightLightMode" + + @classmethod + def get_supported_devices(cls) -> list[str]: + """Get supported devices.""" + return ["Circulator Fan", "Battery Circulator Fan"] + + +class TVCommands(Commands): + """TV commands.""" + + SET_CHANNEL = "SetChannel" + VOLUME_ADD = "volumeAdd" + VOLUME_SUB = "volumeSub" + CHANNEL_ADD = "channelAdd" + CHANNEL_SUB = "channelSub" + + +class DVDCommands(Commands): + """DVD commands.""" + + SET_MUTE = "setMute" + FAST_FORWARD = "FastForward" + REWIND = "Rewind" + NEXT = "Next" + PREVIOUS = "Previous" + PAUSE = "Pause" + PLAY = "Play" + STOP = "Stop" + + +class SpeakerCommands(Commands): + """Speaker commands.""" + + VOLUME_ADD = "volumeAdd" + VOLUME_SUB = "volumeSub" + + +class FanCommands(Commands): + """Fan commands.""" + + SWING = "swing" + TIMER = "timer" + LOW_SPEED = "lowSpeed" + MIDDLE_SPEED = "middleSpeed" + HIGH_SPEED = "highSpeed" + + +class LightCommands(Commands): + """Light commands.""" + + BRIGHTNESS_UP = "brightnessUp" + BRIGHTNESS_DOWN = "brightnessDown" + + +T = TypeVar("T", bound=CommonCommands) diff --git a/switchbot_api/models.py b/switchbot_api/models.py new file mode 100644 index 0000000..93bfd57 --- /dev/null +++ b/switchbot_api/models.py @@ -0,0 +1,47 @@ +"""constant for SwitchBot API.""" + +from __future__ import annotations + +from enum import Enum, StrEnum + + +class PowerState(Enum): + """Power state.""" + + ON = "on" + OFF = "off" + + +class BatteryCirculatorFanMode(StrEnum): + """Fan mode types [Battery Circulator Fan] API.""" + + DIRECT = "direct" + NATURAL = "natural" + SLEEP = "sleep" + BABY = "baby" + + +class VacuumFanSpeed(StrEnum): + """Fan options for VacuumCommands supported devices.""" + + VACUUM_FAN_SPEED_QUIET = "0" + VACUUM_FAN_SPEED_STANDARD = "1" + VACUUM_FAN_SPEED_STRONG = "2" + VACUUM_FAN_SPEED_MAX = "3" + + +class VacuumFanSpeedV2(StrEnum): + """Fan options for VacuumV2Commands & VacuumV3Commands supported devices.""" + + VACUUM_FAN_SPEED_QUIET = "1" + VACUUM_FAN_SPEED_STANDARD = "2" + VACUUM_FAN_SPEED_STRONG = "3" + VACUUM_FAN_SPEED_MAX = "4" + + +class VacuumCleanMode(StrEnum): + """Clean mode for Vacuum.""" + + SWEEP = "sweep" + MOP = "mop" + SWEEP_MOP = "sweep_mop"