Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions examples/switchbot/switchbot_client/switchbot_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,113 @@


class SwitchBotClientProtocol(Protocol):
"""
Protocol defining the interface for SwitchBot client implementations.
SwitchBotクライアントの実装に必要なインターフェースを定義するプロトコル。
"""
def list_devices(self) -> SBListDeviceResponse:
"""
List all available SwitchBot devices.
利用可能なすべてのSwitchBotデバイスを一覧表示する。

Returns:
SBListDeviceResponse: Response containing list of devices.
SBListDeviceResponse: デバイスリストを含むレスポンス。
"""
pass

def get_device_status(self, device_id: str):
"""
Get the current status of a specific device.
特定のデバイスの現在のステータスを取得する。

Args:
device_id (str): ID of the device to query.
device_id (str): クエリするデバイスのID。
"""
pass

def _get_device_status_typed[T: BaseModel](self, device_id: str, cls: Type[T]) -> T:
"""
Get device status with type validation using Pydantic models.
Pydanticモデルを使用して型検証付きでデバイスステータスを取得する。

Args:
device_id (str): ID of the device to query.
device_id (str): クエリするデバイスのID。
cls (Type[T]): Pydantic model class for response validation.
cls (Type[T]): レスポンス検証用のPydanticモデルクラス。

Returns:
T: Validated device status response of type T.
T: 型Tの検証済みデバイスステータスレスポンス。
"""
pass

def commands(self, device_id: str, command: str, command_type: str = "command", parameter: str | int = "default"):
"""
Send a command to a specific device.
特定のデバイスにコマンドを送信する。

Args:
device_id (str): ID of the target device.
device_id (str): ターゲットデバイスのID。
command (str): Command to execute.
command (str): 実行するコマンド。
command_type (str): Type of command (default: "command").
command_type (str): コマンドの種類(デフォルト: "command")。
parameter (str | int): Command parameter (default: "default").
parameter (str | int): コマンドパラメータ(デフォルト: "default")。
"""
pass

def execute_scene(self, scene_id: str):
"""
Execute a specific scene by its ID.
IDで指定したシーンを実行する。

Args:
scene_id (str): ID of the scene to execute.
scene_id (str): 実行するシーンのID。
"""
pass

def list_scenes(self):
"""
List all available scenes.
利用可能なすべてのシーンを一覧表示する。
"""
pass

class SwitchBotBaseClient(SwitchBotClientProtocol):
"""
Base implementation of the SwitchBot client providing core API functionality.
コアAPIの機能を提供するSwitchBotクライアントの基本実装。
"""
def __init__(self, token: str, secret: str):
"""
Initialize the SwitchBot client with authentication credentials.
認証情報でSwitchBotクライアントを初期化する。

Args:
token (str): SwitchBot API token.
token (str): SwitchBot APIトークン。
secret (str): SwitchBot API secret.
secret (str): SwitchBot APIシークレット。
"""
self._token = token
self._secret = secret
self._api_url = "https://api.switch-bot.com/v1.1"

def _generate_sign(self):
"""
Generate authentication signature for API requests.
APIリクエスト用の認証署名を生成する。

Returns:
tuple: Token, timestamp, nonce, and signature.
tuple: トークン、タイムスタンプ、ノンス、署名。
"""
token = self._token
secret = self._secret
nonce = uuid.uuid4()
Expand All @@ -50,6 +132,14 @@ def _generate_sign(self):
return token, t, nonce, sign

def _generate_headers(self):
"""
Generate HTTP headers for API requests including authentication.
認証情報を含むAPIリクエスト用のHTTPヘッダーを生成する。

Returns:
dict: Headers required for API authentication.
dict: API認証に必要なヘッダー。
"""
token, t, nonce, sign = self._generate_sign()
return {
'Authorization': token,
Expand All @@ -61,18 +151,62 @@ def _generate_headers(self):
}

def list_devices(self) -> SBListDeviceResponse:
"""
List all available SwitchBot devices.
利用可能なすべてのSwitchBotデバイスを一覧表示する。

Returns:
SBListDeviceResponse: Response containing list of devices.
SBListDeviceResponse: デバイスリストを含むレスポンス。
"""
res = httpx.get(f'{self._api_url}/devices', headers=self._generate_headers())
return SBListDeviceResponse.model_validate(res.json())

def get_device_status(self, device_id: str):
"""
Get the current status of a specific device.
特定のデバイスの現在のステータスを取得する。

Args:
device_id (str): ID of the device to query.
device_id (str): クエリするデバイスのID。
"""
res = httpx.get(f'{self._api_url}/devices/{device_id}/status', headers=self._generate_headers())
return res.json()

def _get_device_status_typed[T: BaseModel](self, device_id: str, cls: Type[T]) -> T:
"""
Get device status with type validation using Pydantic models.
Pydanticモデルを使用して型検証付きでデバイスステータスを取得する。

Args:
device_id (str): ID of the device to query.
device_id (str): クエリするデバイスのID。
cls (Type[T]): Pydantic model class for response validation.
cls (Type[T]): レスポンス検証用のPydanticモデルクラス。

Returns:
T: Validated device status response of type T.
T: 型Tの検証済みデバイスステータスレスポンス。
"""
res = self.get_device_status(device_id)
return cls.model_validate(res)

def commands(self, device_id: str, command: str, command_type: str = "command", parameter: str | int = "default"):
"""
Send a command to a specific device.
特定のデバイスにコマンドを送信する。

Args:
device_id (str): ID of the target device.
device_id (str): ターゲットデバイスのID。
command (str): Command to execute.
command (str): 実行するコマンド。
command_type (str): Type of command (default: "command").
command_type (str): コマンドの種類(デフォルト: "command")。
parameter (str | int): Command parameter (default: "default").
parameter (str | int): コマンドパラメータ(デフォルト: "default")。
"""
request_body = {
"command_type": command_type,
"command": command,
Expand All @@ -82,10 +216,26 @@ def commands(self, device_id: str, command: str, command_type: str = "command",
return res.json()

def execute_scene(self, scene_id: str):
"""
Execute a specific scene by its ID.
IDで指定したシーンを実行する。

Args:
scene_id (str): ID of the scene to execute.
scene_id (str): 実行するシーンのID。
"""
res = httpx.post(f'{self._api_url}/scenes/{scene_id}/execute', headers=self._generate_headers())
return res.json()

def list_scenes(self):
"""
List all available scenes.
利用可能なすべてのシーンを一覧表示する。

Returns:
dict: Response containing list of scenes.
dict: シーンリストを含むレスポンス。
"""
res = httpx.get(f'{self._api_url}/scenes', headers=self._generate_headers())
return res.json()

41 changes: 34 additions & 7 deletions examples/switchbot/switchbot_client/switchbot_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,39 @@


class SwitchBotClient(SwitchBotBaseClient, SwitchBotDeviceOpsMixin, CaseInsensitiveInvokeMixin):
"""
Main SwitchBot client implementation combining base functionality, device operations, and case-insensitive method invocation.
基本機能、デバイス操作、大文字小文字を区別しないメソッド呼び出しを組み合わせたメインのSwitchBotクライアント実装。

This class inherits from:
- SwitchBotBaseClient: Provides core API communication
- SwitchBotDeviceOpsMixin: Adds device-specific operations
- CaseInsensitiveInvokeMixin: Enables case-insensitive method calls

このクラスは以下のクラスを継承します:
- SwitchBotBaseClient: コアAPIの通信機能を提供
- SwitchBotDeviceOpsMixin: デバイス固有の操作を追加
- CaseInsensitiveInvokeMixin: 大文字小文字を区別しないメソッド呼び出しを可能にする
"""
def __init__(self, token: str, secret: str):
# 多重継承した場合、super()は最初に指定したクラスのメソッドを呼び出すのでsuperで1個ずらしで呼び出すと、
# SwitchBotBaseClient -> SwitchBotClientMixin -> CaseInsensitiveInvokeMixin の順に__init__()が呼び出される
# When multiple inheritance, super() calls the method of the first specified class, so if you call it with a shift of one with super(),
# __init__() is called in the order of SwitchBotBaseClient -> SwitchBotClientMixin -> CaseInsensitiveInvokeMixin
super(SwitchBotClient, self).__init__(token, secret) # SwitchBotBaseClient.__init__(self, token, secret)
super(SwitchBotBaseClient, self).__init__() # SwitchBotClientMixin.__init__(self)
super(SwitchBotDeviceOpsMixin, self).__init__() # CaseInsensitiveInvokeMixin.__init__(self)
"""
Initialize the SwitchBot client with authentication credentials.
認証情報を使用してSwitchBotクライアントを初期化します。

Args:
token (str): SwitchBot API token / SwitchBot APIトークン
secret (str): SwitchBot API secret / SwitchBot APIシークレット

Note:
When using multiple inheritance, super() calls are made in sequence to properly initialize all parent classes:
多重継承を使用する場合、すべての親クラスを適切に初期化するために、super()呼び出しは順番に行われます:
1. SwitchBotBaseClient.__init__(token, secret)
2. SwitchBotDeviceOpsMixin.__init__()
3. CaseInsensitiveInvokeMixin.__init__()
"""
# Initialize parent classes in sequence
# 親クラスを順番に初期化
super(SwitchBotClient, self).__init__(token, secret) # SwitchBotBaseClient.__init__(token, secret)
super(SwitchBotBaseClient, self).__init__() # SwitchBotDeviceOpsMixin.__init__()
super(SwitchBotDeviceOpsMixin, self).__init__() # CaseInsensitiveInvokeMixin.__init__()

Loading