Skip to content

Commit d28fa0c

Browse files
authored
Merge pull request #43 from kzosabe/develop
Release 0.3.1
2 parents 1daa8fb + d5739de commit d28fa0c

12 files changed

Lines changed: 495 additions & 86 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ __pycache__/
55
/dist/
66
/docs/_build
77
/.tox
8+
/.coverage
9+
/htmlcov
810
.idea
911
!.keep

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
0.3.1, 2021-11-27
2+
-------------------------
3+
4+
- Bug fix
5+
- Fix import issue when using physical device objects
6+
17
0.3.0, 2021-11-27
28
-------------------------
39

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
project = "switchbot-client"
66
copyright = "2021, kzosabe"
77
author = "kzosabe"
8-
release = "0.3.0"
8+
release = "0.3.1"
99
extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"]
1010
templates_path = ["_templates"]
1111
exclude_patterns = ["_build", ".DS_Store"]

poetry.lock

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

pyproject.toml

Lines changed: 3 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.3.0"
3+
version = "0.3.1"
44
description = "A Python client library for SwitchBot API."
55
license = "Apache-2.0 or MIT"
66
authors = [
@@ -30,12 +30,12 @@ black = ">=20.8b1"
3030
flake8 = "^4.0.1"
3131
isort = "^5.9.3"
3232
mypy = "^0.910"
33-
pylint = "^2.10.2"
33+
pylint = "^2.12.1"
3434
pytest = "^6.2.5"
3535
pytest-cov = "^3.0.0"
3636
pytest-mock = "^3.6.1"
3737
tox = "^3.24.4"
38-
Sphinx = "^4.3.0"
38+
Sphinx = "^4.3.1"
3939

4040
[build-system]
4141
requires = ["poetry_core>=1.0.0"]

scripts/cov.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/bash -eu
2+
cd "$( dirname "$0" )"/..
3+
poetry run pytest -v --cov=switchbot_client --cov-report=html

switchbot_client/api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ def _load_config(self):
128128
def _check_api_response(original_response: requests.Response):
129129
response = original_response.json()
130130
if "message" not in response:
131-
raise RuntimeError("format error")
131+
raise RuntimeError("format error", original_response.text)
132132
if response["message"] == "Unauthorized":
133133
raise RuntimeError(
134134
"Http 401 Error. User permission is denied due to invalid token.",
135-
original_response.text,
135+
response,
136136
)
137137
return SwitchBotAPIResponse(response["statusCode"], response["message"], response["body"])

switchbot_client/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
class AppConstants:
2-
VERSION = "0.3.0"
2+
VERSION = "0.3.1"

switchbot_client/devices/physical.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22

33
from typing import TYPE_CHECKING
44

5+
from switchbot_client.devices.status import (
6+
BotDeviceStatus,
7+
ColorBulbDeviceStatus,
8+
ContactSensorDeviceStatus,
9+
CurtainDeviceStatus,
10+
DeviceStatus,
11+
HumidifierDeviceStatus,
12+
MeterDeviceStatus,
13+
MotionSensorDeviceStatus,
14+
PlugDeviceStatus,
15+
SmartFanDeviceStatus,
16+
)
517
from switchbot_client.enums import ControlCommand, DeviceType
618
from switchbot_client.types import APIPhysicalDeviceObject
719

820
from .base import SwitchBotCommandResult, SwitchBotDevice
921

1022
if TYPE_CHECKING:
1123
from switchbot_client import SwitchBotClient
12-
from switchbot_client.devices.status import (
13-
BotDeviceStatus,
14-
ColorBulbDeviceStatus,
15-
ContactSensorDeviceStatus,
16-
CurtainDeviceStatus,
17-
DeviceStatus,
18-
HumidifierDeviceStatus,
19-
MeterDeviceStatus,
20-
MotionSensorDeviceStatus,
21-
PlugDeviceStatus,
22-
SmartFanDeviceStatus,
23-
)
2424

2525

2626
class SwitchBotPhysicalDevice(SwitchBotDevice):

tests/test_api.py

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44
import requests
55

6+
from switchbot_client import ControlCommand
67
from switchbot_client.api import SwitchBotAPIClient
78
from switchbot_client.constants import AppConstants
89
from switchbot_client.devices import SwitchBotPhysicalDevice, SwitchBotRemoteDevice
@@ -33,10 +34,15 @@ def test_init_by_config_file(patch_path_exists, mocker):
3334

3435
@patch("os.path.exists")
3536
def test_init_by_another_config_file(patch_path_exists, mocker):
36-
m = mocker.patch("builtins.open", mocker.mock_open(read_data="token: foo"))
37+
m = mocker.patch(
38+
"builtins.open",
39+
mocker.mock_open(read_data="token: foo\napi_host_domain: https://new-api.example.com"),
40+
)
3741
patch_path_exists.return_value = True
3842
sut = SwitchBotAPIClient(config_file_path="~/.config/switch-bot-client/another-config.yml")
3943
m.assert_called_with("~/.config/switch-bot-client/another-config.yml", encoding="utf-8")
44+
assert sut.token == "foo"
45+
assert sut.api_host_domain == "https://new-api.example.com"
4046

4147

4248
@patch("os.path.exists")
@@ -155,3 +161,158 @@ def mock_get(*args, **kwargs):
155161
assert sut.status_code == expected.get("statusCode")
156162
assert sut.message == expected.get("message")
157163
assert sut.body == expected.get("body")
164+
165+
166+
def test_devices_status_wrong_device_error(monkeypatch):
167+
expected = {
168+
"statusCode": 190,
169+
"message": "error message",
170+
"body": {},
171+
}
172+
173+
class MockResponse:
174+
@staticmethod
175+
def json():
176+
return expected
177+
178+
def mock_get(*args, **kwargs):
179+
assert kwargs["headers"]["user-agent"] == f"switchbot-client/{AppConstants.VERSION}"
180+
return MockResponse()
181+
182+
monkeypatch.setattr(requests, "get", mock_get)
183+
client = SwitchBotAPIClient("token")
184+
185+
with pytest.raises(RuntimeError) as e:
186+
sut = client.devices_status("device_foo")
187+
assert "Wrong device ID or trying to get infrared virtual device status" in str(e.value)
188+
189+
190+
def test_devices_status_broken_response(monkeypatch):
191+
expected = {
192+
"someInvalidResponse": "panic",
193+
}
194+
195+
class MockResponse:
196+
def __init__(self):
197+
self.text = """{
198+
"someInvalidResponse": "panic",
199+
}"""
200+
201+
@staticmethod
202+
def json():
203+
return expected
204+
205+
def mock_get(*args, **kwargs):
206+
assert kwargs["headers"]["user-agent"] == f"switchbot-client/{AppConstants.VERSION}"
207+
return MockResponse()
208+
209+
monkeypatch.setattr(requests, "get", mock_get)
210+
client = SwitchBotAPIClient("token")
211+
212+
with pytest.raises(RuntimeError) as e:
213+
sut = client.devices_status("device_foo")
214+
assert "format error" in str(e.value)
215+
216+
217+
def test_devices_status_unauthorized(monkeypatch):
218+
expected = {
219+
"message": "Unauthorized",
220+
}
221+
222+
class MockResponse:
223+
@staticmethod
224+
def json():
225+
return expected
226+
227+
def mock_get(*args, **kwargs):
228+
assert kwargs["headers"]["user-agent"] == f"switchbot-client/{AppConstants.VERSION}"
229+
return MockResponse()
230+
231+
monkeypatch.setattr(requests, "get", mock_get)
232+
client = SwitchBotAPIClient("token")
233+
234+
with pytest.raises(RuntimeError) as e:
235+
sut = client.devices_status("device_foo")
236+
assert "Http 401 Error. User permission is denied due to invalid token." in str(e.value)
237+
238+
239+
def test_devices_commands(monkeypatch):
240+
expected = {
241+
"statusCode": 100,
242+
"message": "success",
243+
"body": {},
244+
}
245+
246+
class MockResponse:
247+
@staticmethod
248+
def json():
249+
return expected
250+
251+
def mock_post(*args, **kwargs):
252+
assert kwargs["headers"]["user-agent"] == f"switchbot-client/{AppConstants.VERSION}"
253+
return MockResponse()
254+
255+
monkeypatch.setattr(requests, "post", mock_post)
256+
client = SwitchBotAPIClient("token")
257+
sut = client.devices_commands(
258+
"device_foo",
259+
ControlCommand.ColorBulb.SET_BRIGHTNESS,
260+
parameter="50",
261+
command_type="command",
262+
)
263+
assert sut.status_code == expected.get("statusCode")
264+
assert sut.message == expected.get("message")
265+
assert sut.body == expected.get("body")
266+
267+
268+
def test_scenes(monkeypatch):
269+
expected = {
270+
"statusCode": 100,
271+
"message": "success",
272+
"body": [
273+
{"sceneId": "S1", "sceneName": "Scene 1"},
274+
{"sceneId": "S2", "sceneName": "Scene 2"},
275+
],
276+
}
277+
278+
class MockResponse:
279+
@staticmethod
280+
def json():
281+
return expected
282+
283+
def mock_get(*args, **kwargs):
284+
assert kwargs["headers"]["user-agent"] == f"switchbot-client/{AppConstants.VERSION}"
285+
return MockResponse()
286+
287+
monkeypatch.setattr(requests, "get", mock_get)
288+
client = SwitchBotAPIClient("token")
289+
sut = client.scenes()
290+
assert sut.status_code == expected.get("statusCode")
291+
assert sut.message == expected.get("message")
292+
assert sut.body == expected.get("body")
293+
294+
295+
def test_scenes_execute(monkeypatch):
296+
expected = {
297+
"statusCode": 100,
298+
"message": "success",
299+
"body": {},
300+
}
301+
302+
class MockResponse:
303+
@staticmethod
304+
def json():
305+
return expected
306+
307+
def mock_post(*args, **kwargs):
308+
assert kwargs["headers"]["user-agent"] == f"switchbot-client/{AppConstants.VERSION}"
309+
return MockResponse()
310+
311+
monkeypatch.setattr(requests, "post", mock_post)
312+
client = SwitchBotAPIClient("token")
313+
sut = client.scenes_execute(
314+
"scene_foo",
315+
)
316+
assert sut.status_code == expected.get("statusCode")
317+
assert sut.message == expected.get("message")
318+
assert sut.body == expected.get("body")

0 commit comments

Comments
 (0)