Skip to content

Commit 8742a8a

Browse files
authored
Merge pull request #5 from kzosabe/release-0.1.1
Release 0.1.1
2 parents 7c6d7b3 + 358dbd6 commit 8742a8a

File tree

8 files changed

+399
-15
lines changed

8 files changed

+399
-15
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ on:
33
push:
44
branches:
55
- main
6+
- develop
67
pull_request:
78
branches:
89
- main
10+
- develop
911
jobs:
1012
test:
1113
name: Run tests with pytest
1214
runs-on: ubuntu-latest
1315
strategy:
1416
matrix:
15-
python-version: [3.7, 3.8, 3.9]
17+
python-version: [3.6, 3.7, 3.8, 3.9]
1618
steps:
1719
- name: Checkout
1820
uses: actions/checkout@v2

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
0.1.1, 2021-09-05
2+
-------------------------
3+
4+
- Add [object interface](README.md#Object interface)
5+
- Add Python 3.6.x support
6+
7+
0.1.1, 2021-09-05
8+
-------------------------
9+
10+
- Add basic implementation

README.md

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# switchbot-client
22

3+
An unofficial Python client implementation of the SwitchBot API.
4+
35
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/switchbot-client.svg)](https://pypi.org/project/switchbot-client/)
6+
[![PyPI - Library Version](https://img.shields.io/pypi/v/switchbot-client.svg)](https://pypi.org/project/switchbot-client/)
47
[![PyPI - Downloads](https://img.shields.io/pypi/dm/switchbot-client)](https://pypi.org/project/switchbot-client)
58
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-informational?style=flat-square)](README.md#License)
69
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -28,12 +31,12 @@ If the environment variable `SWITCHBOT_OPEN_TOKEN` is present,
2831
this client will automatically use this value.
2932

3033
```shell
31-
export SWITCHBOT_OPEN_TOKEN=yourswitchbotopentoken
32-
python3 your_script_using_switchbot_client.py
34+
export SWITCHBOT_OPEN_TOKEN=your_switchbot_open_token
35+
python3 your_script.py
3336
```
3437

3538
```python
36-
# your_script_using_switchbot_client.py
39+
# your_script.py
3740
client = SwitchBotAPIClient()
3841
result = client.devices()
3942
```
@@ -45,7 +48,7 @@ It is also possible to initialize the client by passing a token directly as an a
4548
```python
4649
from switchbot_client import SwitchBotAPIClient
4750

48-
your_token = "yourswitchbotopentoken"
51+
your_token = "your_switchbot_open_token"
4952
client = SwitchBotAPIClient(token=your_token)
5053
result = client.devices()
5154
```
@@ -76,7 +79,7 @@ You can perform operations on the acquired `deviceId`, such as manipulating it o
7679
from switchbot_client import SwitchBotAPIClient
7780

7881
client = SwitchBotAPIClient()
79-
device_id = "YOURDEVICEID"
82+
device_id = "YOUR_DEVICE_ID"
8083
print(client.devices_status(device_id))
8184
```
8285

@@ -138,6 +141,30 @@ SwitchBotAPIResponse(status_code=100, message='success', body={})
138141
```
139142
The specified scene can be executed immediately.
140143

144+
### Object interface
145+
146+
Devices can be manipulated via an easy-to-use object wrapped API(currently only some device types are supported).
147+
148+
```python
149+
from switchbot_client import SwitchBotAPIClient
150+
from switchbot_client.devices import Light, AirConditioner
151+
152+
client = SwitchBotAPIClient()
153+
154+
# You can get your Lights and Air Conditioners device ids by
155+
# print(client.devices().body["infraredRemoteList"])
156+
157+
light = Light(client, device_id="my_light_device_id")
158+
light.turn_on()
159+
160+
air_conditioner = AirConditioner(client, device_id="my_air_conditioner_device_id")
161+
air_conditioner.set_all(
162+
temperature=25,
163+
mode=AirConditioner.Parameters.MODE_DRY,
164+
fan_speed=AirConditioner.Parameters.FAN_SPEED_AUTO,
165+
power=AirConditioner.Parameters.POWER_ON
166+
)
167+
```
141168

142169
### Examples
143170

@@ -171,9 +198,9 @@ def call_this_function_when_i_go_out():
171198

172199
Licensed under either of
173200

174-
* Apache License, Version 2.0
201+
- Apache License, Version 2.0
175202
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
176-
* MIT license
203+
- MIT license
177204
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
178205

179206
at your option.

poetry.lock

Lines changed: 15 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "switchbot-client"
3-
version = "0.1.0"
3+
version = "0.1.1"
44
description = "A Python client library for SwitchBot API."
55
license = "Apache-2.0 or MIT"
66
authors = [
@@ -9,10 +9,19 @@ authors = [
99
readme = "README.md"
1010
repository = "https://github.com/kzosabe/switchbot-client"
1111
homepage = "https://github.com/kzosabe/switchbot-client"
12+
classifiers = [
13+
"Development Status :: 4 - Beta",
14+
"License :: OSI Approved :: MIT License",
15+
"License :: OSI Approved :: Apache Software License",
16+
"Operating System :: OS Independent",
17+
"Topic :: Software Development :: Libraries :: Python Modules",
18+
"Intended Audience :: Developers",
19+
"Intended Audience :: End Users/Desktop",
20+
]
1221

1322
[tool.poetry.dependencies]
14-
python = "^3.7"
15-
requests = "^2.26"
23+
python = "^3.6.2"
24+
requests = "^2.18"
1625

1726
[tool.poetry.dev-dependencies]
1827
black = "^21.8b0"

switchbot_client/devices.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
from switchbot_client import SwitchBotAPIClient, SwitchBotAPIResponse, ControlCommand
2+
3+
4+
class SwitchBotDevice:
5+
def __init__(self, client: SwitchBotAPIClient, device_id: str):
6+
if client is None:
7+
raise TypeError
8+
self.client = client
9+
if device_id is None:
10+
raise TypeError
11+
self.device_id = device_id
12+
13+
def check_device_type(self, expected_device_type: str):
14+
status = self.client.devices_status(self.device_id)
15+
actual_device_type = status.body["deviceType"]
16+
if actual_device_type != expected_device_type:
17+
raise RuntimeError(
18+
f"Illegal device type. "
19+
f"expected: {expected_device_type}, actual: {actual_device_type}"
20+
)
21+
22+
def check_device_type_for_virtual_infrared(self, expected_device_type: str):
23+
infrared_remote_devices = self.client.devices().body["infraredRemoteList"]
24+
for device in infrared_remote_devices:
25+
if device["deviceId"] == self.device_id:
26+
actual_device_type = device["remoteType"]
27+
if actual_device_type == expected_device_type:
28+
return
29+
raise RuntimeError(
30+
f"Illegal device type. "
31+
f"expected: {expected_device_type}, actual: {actual_device_type}"
32+
)
33+
raise RuntimeError(f"device not found: {self.device_id}")
34+
35+
def status(self) -> SwitchBotAPIResponse:
36+
return self.client.devices_status(self.device_id)
37+
38+
def control(
39+
self, command: str, parameter: str = None, command_type: str = None
40+
) -> SwitchBotAPIResponse:
41+
return self.client.devices_control(self.device_id, command, parameter, command_type)
42+
43+
44+
class Bot(SwitchBotDevice):
45+
def __init__(self, client: SwitchBotAPIClient, device_id: str):
46+
super().__init__(client, device_id)
47+
self.check_device_type("Bot")
48+
49+
def turn_on(self) -> SwitchBotAPIResponse:
50+
return self.control(ControlCommand.Bot.TURN_ON)
51+
52+
def turn_off(self) -> SwitchBotAPIResponse:
53+
return self.control(ControlCommand.Bot.TURN_OFF)
54+
55+
def press(self) -> SwitchBotAPIResponse:
56+
return self.control(ControlCommand.Bot.PRESS)
57+
58+
59+
class Plug(SwitchBotDevice):
60+
def __init__(self, client: SwitchBotAPIClient, device_id: str):
61+
super().__init__(client, device_id)
62+
self.check_device_type("Plug")
63+
64+
def turn_on(self) -> SwitchBotAPIResponse:
65+
return self.control(ControlCommand.Plug.TURN_ON)
66+
67+
def turn_off(self) -> SwitchBotAPIResponse:
68+
return self.control(ControlCommand.Plug.TURN_OFF)
69+
70+
71+
class Curtain(SwitchBotDevice):
72+
class Parameters:
73+
MODE_PERFORMANCE = "0"
74+
MODE_SILENT = "1"
75+
MODE_DEFAULT = "ff"
76+
77+
def __init__(self, client: SwitchBotAPIClient, device_id: str):
78+
super().__init__(client, device_id)
79+
self.check_device_type("Curtain")
80+
81+
def turn_on(self) -> SwitchBotAPIResponse:
82+
return self.control(ControlCommand.Curtain.TURN_ON)
83+
84+
def turn_off(self) -> SwitchBotAPIResponse:
85+
return self.control(ControlCommand.Curtain.TURN_OFF)
86+
87+
def set_position(self, index: int, mode: str, position: int) -> SwitchBotAPIResponse:
88+
"""
89+
mode(Parameters.MODE_XXX): 0 (Performance Mode), 1 (Silent Mode), ff (default mode)
90+
position: 0 ~ 100 (0 means opened, 100 means closed)
91+
"""
92+
return self.control(
93+
ControlCommand.Curtain.SET_POSITION, parameter=f"{index},{mode},{position}"
94+
)
95+
96+
97+
class Humidifier(SwitchBotDevice):
98+
def __init__(self, client: SwitchBotAPIClient, device_id: str):
99+
super().__init__(client, device_id)
100+
self.check_device_type("Humidifier")
101+
102+
def turn_on(self) -> SwitchBotAPIResponse:
103+
return self.control(ControlCommand.Humidifier.TURN_ON)
104+
105+
def turn_off(self) -> SwitchBotAPIResponse:
106+
return self.control(ControlCommand.Humidifier.TURN_OFF)
107+
108+
def set_mode(self, mode: str) -> SwitchBotAPIResponse:
109+
"""
110+
auto or 101 or 102 or 103 or {0~100}
111+
auto: set to Auto Mode
112+
101: set atomization efficiency to 34%
113+
102: set atomization efficiency to 67%
114+
103: set atomization efficiency to 100%
115+
"""
116+
return self.control(ControlCommand.Humidifier.SET_MODE, parameter=mode)
117+
118+
119+
class SmartFan(SwitchBotDevice):
120+
class Parameters:
121+
POWER_ON = "on"
122+
POWER_OFF = "off"
123+
FAN_MODE_STANDARD = 1
124+
FAN_MODE_NATURAL = 2
125+
126+
def __init__(self, client: SwitchBotAPIClient, device_id: str):
127+
super().__init__(client, device_id)
128+
self.check_device_type("SmartFan")
129+
130+
def turn_on(self) -> SwitchBotAPIResponse:
131+
return self.control(ControlCommand.SmartFan.TURN_ON)
132+
133+
def turn_off(self) -> SwitchBotAPIResponse:
134+
return self.control(ControlCommand.SmartFan.TURN_OFF)
135+
136+
def set_all_status(
137+
self, power: str, fan_mode: int, fan_speed: int, shake_range: int
138+
) -> SwitchBotAPIResponse:
139+
"""
140+
power(Parameters.POWER_XXX): off, on
141+
fan_mode(Parameters.FAN_MODE_XXX): 1 (Standard), 2 (Natural)
142+
fan_speed: 1, 2, 3, 4
143+
shake_range: 0 ~ 120
144+
"""
145+
return self.control(
146+
ControlCommand.SmartFan.SET_ALL_STATUS,
147+
parameter=f"{power},{fan_mode},{fan_speed},{shake_range}",
148+
)
149+
150+
151+
class AirConditioner(SwitchBotDevice):
152+
class Parameters:
153+
MODE_AUTO = 1
154+
MODE_COOL = 2
155+
MODE_DRY = 3
156+
MODE_FAN = 4
157+
MODE_HEAT = 5
158+
FAN_SPEED_AUTO = 1
159+
FAN_SPEED_LOW = 2
160+
FAN_SPEED_MEDIUM = 3
161+
FAN_SPEED_HIGH = 4
162+
POWER_ON = "on"
163+
POWER_OFF = "off"
164+
165+
def __init__(self, client: SwitchBotAPIClient, device_id: str):
166+
super().__init__(client, device_id)
167+
self.check_device_type_for_virtual_infrared("Air Conditioner")
168+
169+
def turn_on(self) -> SwitchBotAPIResponse:
170+
return self.control(ControlCommand.VirtualInfrared.TURN_ON)
171+
172+
def turn_off(self) -> SwitchBotAPIResponse:
173+
return self.control(ControlCommand.VirtualInfrared.TURN_OFF)
174+
175+
def set_all(
176+
self, temperature: int, mode: str, fan_speed: str, power: str
177+
) -> SwitchBotAPIResponse:
178+
"""
179+
temperature: temperature in celsius
180+
mode(Parameters.MODE_XXX): 1(auto), 2(cool), 3(dry), 4(fan), 5(heat)
181+
fan_speed(Parameters.FAN_SPEED_XXX): 1(auto), 2(low), 3(medium), 4(high);
182+
power(Parameters.POWER_XXX): on, off
183+
"""
184+
return self.control(
185+
ControlCommand.VirtualInfrared.SET_ALL,
186+
parameter=f"{temperature},{mode},{fan_speed},{power}",
187+
)
188+
189+
190+
class Light(SwitchBotDevice):
191+
def __init__(self, client: SwitchBotAPIClient, device_id: str):
192+
super().__init__(client, device_id)
193+
self.check_device_type_for_virtual_infrared("Light")
194+
195+
def turn_on(self) -> SwitchBotAPIResponse:
196+
return self.control(ControlCommand.VirtualInfrared.TURN_ON)
197+
198+
def turn_off(self) -> SwitchBotAPIResponse:
199+
return self.control(ControlCommand.VirtualInfrared.TURN_OFF)
200+
201+
def brightness_up(self) -> SwitchBotAPIResponse:
202+
return self.control(ControlCommand.VirtualInfrared.BRIGHTNESS_UP)
203+
204+
def brightness_down(self) -> SwitchBotAPIResponse:
205+
return self.control(ControlCommand.VirtualInfrared.BRIGHTNESS_DOWN)

0 commit comments

Comments
 (0)