switchbot-api 2.3.1__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {switchbot_api-2.3.1 → switchbot_api-2.4.0}/PKG-INFO +3 -2
- {switchbot_api-2.3.1 → switchbot_api-2.4.0}/README.md +1 -1
- switchbot_api-2.4.0/pyproject.toml +106 -0
- {switchbot_api-2.3.1 → switchbot_api-2.4.0}/switchbot_api/__init__.py +135 -67
- switchbot_api-2.4.0/switchbot_api/exceptions.py +17 -0
- switchbot_api-2.3.1/pyproject.toml +0 -51
- {switchbot_api-2.3.1 → switchbot_api-2.4.0}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: switchbot_api
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: An asynchronous library to use Switchbot API
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Ravaka Razafimanantsoa
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
-
Requires-Dist: aiohttp (>=3.
|
|
15
|
+
Requires-Dist: aiohttp (>=3.0.0)
|
|
16
16
|
Project-URL: Repository, https://github.com/SeraphicCorp/py-switchbot-api
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
|
|
@@ -45,3 +45,4 @@ poetry install
|
|
|
45
45
|
poetry run black switchbot_api/
|
|
46
46
|
poetry run mypy switchbot_api/
|
|
47
47
|
```
|
|
48
|
+
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "switchbot_api"
|
|
3
|
+
version = "2.4.0"
|
|
4
|
+
description = "An asynchronous library to use Switchbot API"
|
|
5
|
+
authors = ["Ravaka Razafimanantsoa <contact@ravaka.dev>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
packages = [{include = "switchbot_api"}]
|
|
9
|
+
repository = "https://github.com/SeraphicCorp/py-switchbot-api"
|
|
10
|
+
|
|
11
|
+
[tool.poetry.dependencies]
|
|
12
|
+
python = "^3.10"
|
|
13
|
+
aiohttp = ">=3.0.0"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
[tool.poetry.group.dev.dependencies]
|
|
17
|
+
coverage = {version = "7.6.10", extras = ["toml"]}
|
|
18
|
+
mypy = "^1.4.1"
|
|
19
|
+
pytest-asyncio = "^0.25.2"
|
|
20
|
+
pytest-freezer = "^0.4.9"
|
|
21
|
+
pytest = "^8.3.4"
|
|
22
|
+
syrupy = "^4.8.1"
|
|
23
|
+
aioresponses = "^0.7.7"
|
|
24
|
+
ruff = "^0.9.2"
|
|
25
|
+
pytest-cov = "^6.0.0"
|
|
26
|
+
covdefaults = "^2.3.0"
|
|
27
|
+
codespell = "^2.3.0"
|
|
28
|
+
pre-commit = "^4.0.1"
|
|
29
|
+
pre-commit-hooks = "^5.0.0"
|
|
30
|
+
yamllint = "^1.35.1"
|
|
31
|
+
|
|
32
|
+
[tool.mypy]
|
|
33
|
+
# Specify the target platform details in config, so your developers are
|
|
34
|
+
# free to run mypy on Windows, Linux, or macOS and get consistent
|
|
35
|
+
# results.
|
|
36
|
+
platform = "linux"
|
|
37
|
+
python_version = "3.11"
|
|
38
|
+
|
|
39
|
+
# show error messages from unrelated files
|
|
40
|
+
follow_imports = "normal"
|
|
41
|
+
|
|
42
|
+
# suppress errors about unsatisfied imports
|
|
43
|
+
ignore_missing_imports = true
|
|
44
|
+
|
|
45
|
+
# be strict
|
|
46
|
+
check_untyped_defs = true
|
|
47
|
+
disallow_any_generics = true
|
|
48
|
+
disallow_incomplete_defs = true
|
|
49
|
+
disallow_subclassing_any = true
|
|
50
|
+
disallow_untyped_calls = true
|
|
51
|
+
disallow_untyped_decorators = true
|
|
52
|
+
disallow_untyped_defs = true
|
|
53
|
+
no_implicit_optional = true
|
|
54
|
+
strict_optional = true
|
|
55
|
+
warn_incomplete_stub = true
|
|
56
|
+
warn_no_return = true
|
|
57
|
+
warn_redundant_casts = true
|
|
58
|
+
warn_return_any = true
|
|
59
|
+
warn_unused_configs = true
|
|
60
|
+
warn_unused_ignores = true
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
addopts = "--cov"
|
|
65
|
+
asyncio_mode = "auto"
|
|
66
|
+
pythonpath = ["switchbot_api"]
|
|
67
|
+
|
|
68
|
+
[tool.coverage.report]
|
|
69
|
+
skip_empty = true
|
|
70
|
+
show_missing = true
|
|
71
|
+
fail_under = 50
|
|
72
|
+
|
|
73
|
+
[tool.coverage.run]
|
|
74
|
+
plugins = ["covdefaults"]
|
|
75
|
+
source = ["switchbot_api"]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint]
|
|
79
|
+
ignore = [
|
|
80
|
+
"ANN401", # Opinioated warning on disallowing dynamically typed expressions
|
|
81
|
+
"COM812", # Conflicts with other rules
|
|
82
|
+
"D203", # Conflicts with other rules
|
|
83
|
+
"D213", # Conflicts with other rules
|
|
84
|
+
"D417", # False positives in some occasions
|
|
85
|
+
"ISC001", # Conflicts with other rules
|
|
86
|
+
"PLR2004", # Just annoying, not really useful
|
|
87
|
+
]
|
|
88
|
+
select = ["ALL"]
|
|
89
|
+
|
|
90
|
+
[tool.ruff.lint.flake8-pytest-style]
|
|
91
|
+
fixture-parentheses = false
|
|
92
|
+
mark-parentheses = false
|
|
93
|
+
|
|
94
|
+
[tool.ruff.lint.isort]
|
|
95
|
+
known-first-party = ["switchbot_api"]
|
|
96
|
+
force-sort-within-sections = true
|
|
97
|
+
split-on-trailing-comma = false
|
|
98
|
+
combine-as-imports = true
|
|
99
|
+
|
|
100
|
+
[tool.ruff.lint.mccabe]
|
|
101
|
+
max-complexity = 25
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
[build-system]
|
|
105
|
+
requires = ["poetry-core"]
|
|
106
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
"""Tools to query the SwitchBot API."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import base64
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import Enum, StrEnum
|
|
4
8
|
import hashlib
|
|
5
9
|
import hmac
|
|
6
10
|
import logging
|
|
11
|
+
import socket
|
|
7
12
|
import time
|
|
13
|
+
from typing import Any, Self, TypeVar
|
|
8
14
|
import uuid
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
from enum import Enum
|
|
11
|
-
from typing import TypeVar
|
|
12
15
|
|
|
13
|
-
from aiohttp import ClientSession
|
|
16
|
+
from aiohttp import ClientError, ClientResponseError, ClientSession
|
|
17
|
+
from aiohttp.hdrs import METH_GET, METH_POST
|
|
18
|
+
|
|
19
|
+
from switchbot_api.exceptions import (
|
|
20
|
+
SwitchBotAuthenticationError,
|
|
21
|
+
SwitchBotConnectionError,
|
|
22
|
+
SwitchBotDeviceOfflineError,
|
|
23
|
+
)
|
|
14
24
|
|
|
15
25
|
_API_HOST = "https://api.switch-bot.com"
|
|
16
26
|
|
|
@@ -18,18 +28,6 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
18
28
|
NON_OBSERVED_REMOTE_TYPES = ["Others"]
|
|
19
29
|
|
|
20
30
|
|
|
21
|
-
class CannotConnect(Exception):
|
|
22
|
-
"""Cannot connect to the SwitchBot API."""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class InvalidAuth(Exception):
|
|
26
|
-
"""Invalid auth for the SwitchBot API."""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class DeviceOffline(Exception):
|
|
30
|
-
"""Device currently offline."""
|
|
31
|
-
|
|
32
|
-
|
|
33
31
|
@dataclass
|
|
34
32
|
class Device:
|
|
35
33
|
"""Device."""
|
|
@@ -39,7 +37,7 @@ class Device:
|
|
|
39
37
|
device_type: str
|
|
40
38
|
hub_device_id: str
|
|
41
39
|
|
|
42
|
-
def __init__(self, **kwargs) -> None:
|
|
40
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
43
41
|
"""Initialize."""
|
|
44
42
|
self.device_id = kwargs["deviceId"]
|
|
45
43
|
self.device_name = kwargs["deviceName"]
|
|
@@ -56,7 +54,7 @@ class Remote:
|
|
|
56
54
|
device_type: str
|
|
57
55
|
hub_device_id: str
|
|
58
56
|
|
|
59
|
-
def __init__(self, **kwargs) -> None:
|
|
57
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
60
58
|
"""Initialize."""
|
|
61
59
|
self.device_id = kwargs["deviceId"]
|
|
62
60
|
self.device_name = kwargs["deviceName"]
|
|
@@ -72,7 +70,7 @@ class PowerState(Enum):
|
|
|
72
70
|
|
|
73
71
|
|
|
74
72
|
class Commands(Enum):
|
|
75
|
-
|
|
73
|
+
"""Base command class."""
|
|
76
74
|
|
|
77
75
|
|
|
78
76
|
class CommonCommands(Commands):
|
|
@@ -97,7 +95,11 @@ class AirConditionerCommands(Commands):
|
|
|
97
95
|
class HumidifierCommands(Commands):
|
|
98
96
|
"""Humidifier commands."""
|
|
99
97
|
|
|
100
|
-
|
|
98
|
+
# parameter: auto, set to Auto Mode
|
|
99
|
+
# 101, set atomization efficiency to 34%
|
|
100
|
+
# 102, set atomization efficiency to 67%
|
|
101
|
+
# 103, set atomization efficiency to 100%
|
|
102
|
+
SET_MODE = "setMode"
|
|
101
103
|
|
|
102
104
|
|
|
103
105
|
class TVCommands(Commands):
|
|
@@ -140,6 +142,14 @@ class FanCommands(Commands):
|
|
|
140
142
|
HIGH_SPEED = "highSpeed"
|
|
141
143
|
|
|
142
144
|
|
|
145
|
+
class BatteryCirculatorFanCommands(Commands):
|
|
146
|
+
"""Command types for [Battery Circulator Fan] API."""
|
|
147
|
+
|
|
148
|
+
SET_WIND_SPEED = "setWindSpeed"
|
|
149
|
+
SET_WIND_MODE = "setWindMode"
|
|
150
|
+
SET_NIGHT_LIGHT_MODE = "setNightLightMode"
|
|
151
|
+
|
|
152
|
+
|
|
143
153
|
class LightCommands(Commands):
|
|
144
154
|
"""Light commands."""
|
|
145
155
|
|
|
@@ -148,7 +158,7 @@ class LightCommands(Commands):
|
|
|
148
158
|
|
|
149
159
|
|
|
150
160
|
class LockCommands(Commands):
|
|
151
|
-
"""Lock commands"""
|
|
161
|
+
"""Lock commands."""
|
|
152
162
|
|
|
153
163
|
LOCK = "lock"
|
|
154
164
|
UNLOCK = "unlock"
|
|
@@ -178,25 +188,43 @@ class BotCommands(Commands):
|
|
|
178
188
|
PRESS = "press"
|
|
179
189
|
|
|
180
190
|
|
|
191
|
+
class BatteryCirculatorFanMode(StrEnum):
|
|
192
|
+
"""Fan mode types [Battery Circulator Fan] API."""
|
|
193
|
+
|
|
194
|
+
DIRECT = "direct"
|
|
195
|
+
NATURAL = "natural"
|
|
196
|
+
SLEEP = "sleep"
|
|
197
|
+
BABY = "baby"
|
|
198
|
+
|
|
199
|
+
|
|
181
200
|
T = TypeVar("T", bound=CommonCommands)
|
|
182
201
|
|
|
183
202
|
|
|
184
203
|
class SwitchBotAPI:
|
|
185
204
|
"""SwitchBot API."""
|
|
186
205
|
|
|
187
|
-
|
|
206
|
+
_close_session: bool = False
|
|
207
|
+
|
|
208
|
+
def __init__(
|
|
209
|
+
self, token: str, secret: str, session: ClientSession | None = None
|
|
210
|
+
) -> None:
|
|
188
211
|
"""Initialize."""
|
|
189
212
|
self.token = token
|
|
190
213
|
self.secret = secret
|
|
214
|
+
self.session = session
|
|
191
215
|
|
|
192
|
-
def make_headers(self, token: str, secret: str):
|
|
216
|
+
def make_headers(self, token: str, secret: str) -> dict[str, Any]:
|
|
193
217
|
"""Make headers."""
|
|
194
218
|
nonce = uuid.uuid4()
|
|
195
219
|
timestamp = int(round(time.time() * 1000))
|
|
196
220
|
string_to_sign = bytes(f"{token}{timestamp}{nonce}", "utf-8")
|
|
197
221
|
secret_bytes = bytes(secret, "utf-8")
|
|
198
222
|
|
|
199
|
-
sign = base64.b64encode(
|
|
223
|
+
sign = base64.b64encode(
|
|
224
|
+
hmac.new(
|
|
225
|
+
secret_bytes, msg=string_to_sign, digestmod=hashlib.sha256
|
|
226
|
+
).digest()
|
|
227
|
+
)
|
|
200
228
|
|
|
201
229
|
return {
|
|
202
230
|
"Authorization": token,
|
|
@@ -207,81 +235,95 @@ class SwitchBotAPI:
|
|
|
207
235
|
"nonce": str(nonce),
|
|
208
236
|
}
|
|
209
237
|
|
|
210
|
-
async def _request(
|
|
211
|
-
|
|
212
|
-
|
|
238
|
+
async def _request(
|
|
239
|
+
self, method: str, path: str, json: dict[str, Any] | None = None
|
|
240
|
+
) -> dict[str, Any]:
|
|
241
|
+
if not self.session:
|
|
242
|
+
self.session = ClientSession()
|
|
243
|
+
self._close_session = True
|
|
244
|
+
try:
|
|
245
|
+
response = await self.session.request(
|
|
246
|
+
method,
|
|
213
247
|
f"{_API_HOST}/v1.1/{path}",
|
|
214
248
|
headers=self.make_headers(self.token, self.secret),
|
|
215
249
|
json=json,
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
250
|
+
)
|
|
251
|
+
except (
|
|
252
|
+
ClientError,
|
|
253
|
+
ClientResponseError,
|
|
254
|
+
socket.gaierror,
|
|
255
|
+
) as exception:
|
|
256
|
+
msg = "Error occurred while communicating with the SwitchBot API"
|
|
257
|
+
raise SwitchBotConnectionError(msg) from exception
|
|
258
|
+
if response.status == 403:
|
|
259
|
+
raise SwitchBotAuthenticationError
|
|
260
|
+
|
|
261
|
+
body = await response.json()
|
|
262
|
+
|
|
263
|
+
if response.status >= 400:
|
|
264
|
+
_LOGGER.error("Error %d: %s", response.status, body)
|
|
265
|
+
raise SwitchBotConnectionError
|
|
266
|
+
|
|
267
|
+
match body.get("statusCode"):
|
|
268
|
+
case 100:
|
|
269
|
+
return body["body"] # type: ignore[no-any-return]
|
|
270
|
+
case 161 | 171:
|
|
271
|
+
# SwitchBot docs claim that 161 is the code for device
|
|
272
|
+
# being offline, and 171 for a _hub_ being offline.
|
|
273
|
+
# In testing, the Plug Mini (JP) return 171 when not
|
|
274
|
+
# online too.
|
|
275
|
+
raise SwitchBotDeviceOfflineError
|
|
276
|
+
case _:
|
|
277
|
+
_LOGGER.error("Error %d: %s", response.status, body)
|
|
278
|
+
raise SwitchBotConnectionError
|
|
238
279
|
|
|
239
280
|
async def list_devices(self) -> list[Device | Remote]:
|
|
240
281
|
"""List devices."""
|
|
241
|
-
body = await self._request("devices")
|
|
282
|
+
body = await self._request(METH_GET, "devices")
|
|
242
283
|
_LOGGER.debug("Devices: %s", body)
|
|
243
|
-
devices = [Device(**device) for device in body.get("deviceList")]
|
|
284
|
+
devices = [Device(**device) for device in body.get("deviceList")] # type: ignore[union-attr]
|
|
244
285
|
remotes = [
|
|
245
286
|
Remote(**remote)
|
|
246
|
-
for remote in body.get("infraredRemoteList")
|
|
287
|
+
for remote in body.get("infraredRemoteList") # type: ignore[union-attr]
|
|
247
288
|
if remote.get("remoteType") not in NON_OBSERVED_REMOTE_TYPES
|
|
248
289
|
]
|
|
249
290
|
return [*devices, *remotes]
|
|
250
291
|
|
|
251
|
-
async def get_status(self, device_id: str):
|
|
292
|
+
async def get_status(self, device_id: str) -> dict[str, Any]:
|
|
252
293
|
"""No status for IR devices."""
|
|
253
|
-
|
|
254
|
-
return body
|
|
294
|
+
return await self._request(METH_GET, f"devices/{device_id}/status")
|
|
255
295
|
|
|
256
|
-
async def get_webook_configuration(self):
|
|
296
|
+
async def get_webook_configuration(self) -> dict[str, Any]:
|
|
257
297
|
"""List webhooks."""
|
|
258
298
|
json = {"action": "queryUrl"}
|
|
259
|
-
return await self._request("webhook/queryWebhook",
|
|
299
|
+
return await self._request(METH_POST, "webhook/queryWebhook", json=json)
|
|
260
300
|
|
|
261
|
-
async def setup_webhook(self, url: str):
|
|
262
|
-
"""
|
|
301
|
+
async def setup_webhook(self, url: str) -> None:
|
|
302
|
+
"""Set up webhook to receive device status updates."""
|
|
263
303
|
json = {"deviceList": "ALL", "action": "setupWebhook", "url": url}
|
|
264
|
-
await self._request("webhook/setupWebhook",
|
|
304
|
+
await self._request(METH_POST, "webhook/setupWebhook", json=json)
|
|
265
305
|
|
|
266
|
-
async def delete_webhook(self, url: str):
|
|
306
|
+
async def delete_webhook(self, url: str) -> None:
|
|
267
307
|
"""Delete webhook."""
|
|
268
308
|
json = {"action": "deleteWebhook", "url": url}
|
|
269
|
-
await self._request("webhook/deleteWebhook",
|
|
309
|
+
await self._request(METH_POST, "webhook/deleteWebhook", json=json)
|
|
270
310
|
|
|
271
311
|
async def send_command(
|
|
272
312
|
self,
|
|
273
313
|
device_id: str,
|
|
274
314
|
command: T | str,
|
|
275
315
|
command_type: str = "command",
|
|
276
|
-
parameters: dict | str = "default",
|
|
277
|
-
):
|
|
316
|
+
parameters: dict | str = "default", # type: ignore[type-arg]
|
|
317
|
+
) -> None:
|
|
278
318
|
"""Send command to device.
|
|
279
319
|
|
|
280
320
|
Args:
|
|
281
321
|
device_id (str): The ID of the device.
|
|
282
322
|
command (string | extends CommonCommands): The command to be sent.
|
|
283
|
-
command_type (str, optional): The type of the command.
|
|
284
|
-
|
|
323
|
+
command_type (str, optional): The type of the command.
|
|
324
|
+
Defaults to "command".
|
|
325
|
+
parameters (dict | str, optional): The parameters for the command.
|
|
326
|
+
Defaults to "default".
|
|
285
327
|
|
|
286
328
|
Example JSON:
|
|
287
329
|
{
|
|
@@ -289,10 +331,36 @@ class SwitchBotAPI:
|
|
|
289
331
|
"command": "ボタン", // the name of the customized button
|
|
290
332
|
"parameter": "default"
|
|
291
333
|
}
|
|
334
|
+
|
|
292
335
|
"""
|
|
293
336
|
json = {
|
|
294
337
|
"commandType": command_type,
|
|
295
338
|
"command": command.value if isinstance(command, Commands) else command,
|
|
296
339
|
"parameter": parameters,
|
|
297
340
|
}
|
|
298
|
-
await self._request(f"devices/{device_id}/commands",
|
|
341
|
+
await self._request(METH_POST, f"devices/{device_id}/commands", json=json)
|
|
342
|
+
|
|
343
|
+
async def close(self) -> None:
|
|
344
|
+
"""Close the client session."""
|
|
345
|
+
if self.session and self._close_session:
|
|
346
|
+
await self.session.close()
|
|
347
|
+
|
|
348
|
+
async def __aenter__(self) -> Self:
|
|
349
|
+
"""Async enter.
|
|
350
|
+
|
|
351
|
+
Returns
|
|
352
|
+
-------
|
|
353
|
+
The SwitchBotAPI object.
|
|
354
|
+
|
|
355
|
+
"""
|
|
356
|
+
return self
|
|
357
|
+
|
|
358
|
+
async def __aexit__(self, *_exc_info: object) -> None:
|
|
359
|
+
"""Async exit.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
----
|
|
363
|
+
_exc_info: Exec type.
|
|
364
|
+
|
|
365
|
+
"""
|
|
366
|
+
await self.close()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Exceptions for the SwitchBot API."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SwitchBotError(Exception):
|
|
5
|
+
"""General SwitchBot error occurred."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SwitchBotConnectionError(SwitchBotError):
|
|
9
|
+
"""Cannot connect to the SwitchBot API."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SwitchBotAuthenticationError(SwitchBotError):
|
|
13
|
+
"""Invalid auth for the SwitchBot API."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SwitchBotDeviceOfflineError(SwitchBotError):
|
|
17
|
+
"""Device currently offline."""
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "switchbot_api"
|
|
3
|
-
version = "2.3.1"
|
|
4
|
-
description = "An asynchronous library to use Switchbot API"
|
|
5
|
-
authors = ["Ravaka Razafimanantsoa <contact@ravaka.dev>"]
|
|
6
|
-
license = "MIT"
|
|
7
|
-
readme = "README.md"
|
|
8
|
-
packages = [{include = "switchbot_api"}]
|
|
9
|
-
repository = "https://github.com/SeraphicCorp/py-switchbot-api"
|
|
10
|
-
|
|
11
|
-
[tool.poetry.dependencies]
|
|
12
|
-
python = "^3.10"
|
|
13
|
-
aiohttp = "^3.8.4"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
[tool.poetry.group.dev.dependencies]
|
|
17
|
-
pytest = "^7.2.2"
|
|
18
|
-
flake8 = "^6.0.0"
|
|
19
|
-
black = ">=23.1,<25.0"
|
|
20
|
-
coverage = "^7.2.1"
|
|
21
|
-
parameterized = "^0.8.1"
|
|
22
|
-
isort = "^5.12.0"
|
|
23
|
-
mypy = "^1.4.1"
|
|
24
|
-
|
|
25
|
-
[tool.black]
|
|
26
|
-
line-length = 120
|
|
27
|
-
|
|
28
|
-
[tool.isort]
|
|
29
|
-
profile = "black"
|
|
30
|
-
|
|
31
|
-
[tool.mypy]
|
|
32
|
-
check_untyped_defs = true
|
|
33
|
-
|
|
34
|
-
[tool.pytest.ini_options]
|
|
35
|
-
pythonpath = ["switchbot_api"]
|
|
36
|
-
|
|
37
|
-
[tool.coverage.run]
|
|
38
|
-
branch = true
|
|
39
|
-
omit = [
|
|
40
|
-
"*/test_*.py",
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
[tool.coverage.report]
|
|
44
|
-
skip_empty = true
|
|
45
|
-
show_missing = true
|
|
46
|
-
fail_under = 0
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
[build-system]
|
|
50
|
-
requires = ["poetry-core"]
|
|
51
|
-
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|