PySwitchbot 0.46.1__tar.gz → 0.47.2__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.
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/PKG-INFO +1 -1
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/PySwitchbot.egg-info/SOURCES.txt +1 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/setup.py +1 -1
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parser.py +8 -1
- pyswitchbot-0.47.2/switchbot/adv_parsers/hub2.py +40 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/const.py +1 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/discovery.py +3 -2
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/tests/test_adv_parser.py +34 -37
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/LICENSE +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/MANIFEST.in +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/README.md +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/setup.cfg +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/__init__.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/lock.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/api_config.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/base_light.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/device.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/lock.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/enum.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/switchbot/models.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/tests/test_base_cover.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-0.46.1 → pyswitchbot-0.47.2}/tests/test_curtain.py +0 -0
|
@@ -21,6 +21,7 @@ switchbot/adv_parsers/bulb.py
|
|
|
21
21
|
switchbot/adv_parsers/ceiling_light.py
|
|
22
22
|
switchbot/adv_parsers/contact.py
|
|
23
23
|
switchbot/adv_parsers/curtain.py
|
|
24
|
+
switchbot/adv_parsers/hub2.py
|
|
24
25
|
switchbot/adv_parsers/humidifier.py
|
|
25
26
|
switchbot/adv_parsers/light_strip.py
|
|
26
27
|
switchbot/adv_parsers/lock.py
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Library to handle connection with Switchbot."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import logging
|
|
@@ -15,6 +16,7 @@ from .adv_parsers.bulb import process_color_bulb
|
|
|
15
16
|
from .adv_parsers.ceiling_light import process_woceiling
|
|
16
17
|
from .adv_parsers.contact import process_wocontact
|
|
17
18
|
from .adv_parsers.curtain import process_wocurtain
|
|
19
|
+
from .adv_parsers.hub2 import process_wohub2
|
|
18
20
|
from .adv_parsers.humidifier import process_wohumidifier
|
|
19
21
|
from .adv_parsers.light_strip import process_wostrip
|
|
20
22
|
from .adv_parsers.lock import process_wolock
|
|
@@ -54,7 +56,6 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
|
|
|
54
56
|
"modelName": SwitchbotModel.BOT,
|
|
55
57
|
"modelFriendlyName": "Bot",
|
|
56
58
|
"func": process_wohand,
|
|
57
|
-
"service_uuids": {"cba20d00-224d-11e6-9fb8-0002a5d5c51b"},
|
|
58
59
|
"manufacturer_id": 89,
|
|
59
60
|
},
|
|
60
61
|
"s": {
|
|
@@ -100,6 +101,12 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
|
|
|
100
101
|
"func": process_wosensorth,
|
|
101
102
|
"manufacturer_id": 2409,
|
|
102
103
|
},
|
|
104
|
+
"v": {
|
|
105
|
+
"modelName": SwitchbotModel.HUB2,
|
|
106
|
+
"modelFriendlyName": "Hub 2",
|
|
107
|
+
"func": process_wohub2,
|
|
108
|
+
"manufacturer_id": 2409,
|
|
109
|
+
},
|
|
103
110
|
"g": {
|
|
104
111
|
"modelName": SwitchbotModel.PLUG_MINI,
|
|
105
112
|
"modelFriendlyName": "Plug Mini",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Hub2 parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def process_wohub2(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
|
|
9
|
+
"""Process woHub2 sensor manufacturer data."""
|
|
10
|
+
temp_data = None
|
|
11
|
+
|
|
12
|
+
if mfr_data:
|
|
13
|
+
status = mfr_data[12]
|
|
14
|
+
temp_data = mfr_data[13:16]
|
|
15
|
+
|
|
16
|
+
if not temp_data:
|
|
17
|
+
return {}
|
|
18
|
+
|
|
19
|
+
_temp_sign = 1 if temp_data[1] & 0b10000000 else -1
|
|
20
|
+
_temp_c = _temp_sign * (
|
|
21
|
+
(temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
|
|
22
|
+
)
|
|
23
|
+
_temp_f = (_temp_c * 9 / 5) + 32
|
|
24
|
+
_temp_f = (_temp_f * 10) / 10
|
|
25
|
+
humidity = temp_data[2] & 0b01111111
|
|
26
|
+
light_level = status & 0b11111
|
|
27
|
+
|
|
28
|
+
if _temp_c == 0 and humidity == 0:
|
|
29
|
+
return {}
|
|
30
|
+
|
|
31
|
+
_wohub2_data = {
|
|
32
|
+
# Data should be flat, but we keep the original structure for now
|
|
33
|
+
"temp": {"c": _temp_c, "f": _temp_f},
|
|
34
|
+
"temperature": _temp_c,
|
|
35
|
+
"fahrenheit": bool(temp_data[2] & 0b10000000),
|
|
36
|
+
"humidity": humidity,
|
|
37
|
+
"lightLevel": light_level,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return _wohub2_data
|
|
@@ -42,11 +42,11 @@ class GetSwitchbotDevices:
|
|
|
42
42
|
|
|
43
43
|
devices = None
|
|
44
44
|
devices = bleak.BleakScanner(
|
|
45
|
+
detection_callback=self.detection_callback,
|
|
45
46
|
# TODO: Find new UUIDs to filter on. For example, see
|
|
46
47
|
# https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/4ad138bb09f0fbbfa41b152ca327a78c1d0b6ba9/devicetypes/meter.md
|
|
47
48
|
adapter=self._interface,
|
|
48
49
|
)
|
|
49
|
-
devices.register_detection_callback(self.detection_callback)
|
|
50
50
|
|
|
51
51
|
async with CONNECT_LOCK:
|
|
52
52
|
await devices.start()
|
|
@@ -111,7 +111,8 @@ class GetSwitchbotDevices:
|
|
|
111
111
|
base_meters = await self._get_devices_by_model("T")
|
|
112
112
|
plus_meters = await self._get_devices_by_model("i")
|
|
113
113
|
io_meters = await self._get_devices_by_model("w")
|
|
114
|
-
|
|
114
|
+
hub2_meters = await self._get_devices_by_model("v")
|
|
115
|
+
return {**base_meters, **plus_meters, **io_meters, **hub2_meters}
|
|
115
116
|
|
|
116
117
|
async def get_contactsensors(self) -> dict[str, SwitchBotAdvertisement]:
|
|
117
118
|
"""Return all WoContact/Contact sensor devices with services data."""
|
|
@@ -964,6 +964,40 @@ def test_wosensor_active_zero_data():
|
|
|
964
964
|
)
|
|
965
965
|
|
|
966
966
|
|
|
967
|
+
def test_wohub2_passive_and_active():
|
|
968
|
+
"""Test parsing wosensor as passive with active data as well."""
|
|
969
|
+
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
970
|
+
adv_data = generate_advertisement_data(
|
|
971
|
+
manufacturer_data={
|
|
972
|
+
2409: b"\xaa\xbb\xcc\xdd\xee\xff\x00\xfffT\x1a\xf1\x82\x07\x9a2\x00"
|
|
973
|
+
},
|
|
974
|
+
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"v\x00"},
|
|
975
|
+
tx_power=-127,
|
|
976
|
+
rssi=-50,
|
|
977
|
+
)
|
|
978
|
+
result = parse_advertisement_data(ble_device, adv_data)
|
|
979
|
+
assert result == SwitchBotAdvertisement(
|
|
980
|
+
address="aa:bb:cc:dd:ee:ff",
|
|
981
|
+
data={
|
|
982
|
+
"data": {
|
|
983
|
+
"fahrenheit": False,
|
|
984
|
+
"humidity": 50,
|
|
985
|
+
"lightLevel": 2,
|
|
986
|
+
"temp": {"c": 26.7, "f": 80.06},
|
|
987
|
+
"temperature": 26.7,
|
|
988
|
+
},
|
|
989
|
+
"isEncrypted": False,
|
|
990
|
+
"model": "v",
|
|
991
|
+
"modelFriendlyName": "Hub 2",
|
|
992
|
+
"modelName": SwitchbotModel.HUB2,
|
|
993
|
+
"rawAdvData": b"v\x00",
|
|
994
|
+
},
|
|
995
|
+
device=ble_device,
|
|
996
|
+
rssi=-50,
|
|
997
|
+
active=True,
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
|
|
967
1001
|
def test_woiosensor_passive_and_active():
|
|
968
1002
|
"""Test parsing woiosensor as passive with active data as well."""
|
|
969
1003
|
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
@@ -1286,43 +1320,6 @@ def test_motion_with_light_detected():
|
|
|
1286
1320
|
)
|
|
1287
1321
|
|
|
1288
1322
|
|
|
1289
|
-
def test_motion_sensor_motion_passive():
|
|
1290
|
-
"""Test parsing motion sensor with motion data."""
|
|
1291
|
-
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
1292
|
-
adv_data = generate_advertisement_data(
|
|
1293
|
-
manufacturer_data={2409: b"\xc0!\x9a\xe8\xbcIi\\\x008"},
|
|
1294
|
-
service_data={},
|
|
1295
|
-
tx_power=-127,
|
|
1296
|
-
rssi=-87,
|
|
1297
|
-
)
|
|
1298
|
-
result = parse_advertisement_data(
|
|
1299
|
-
ble_device, adv_data, SwitchbotModel.MOTION_SENSOR
|
|
1300
|
-
)
|
|
1301
|
-
assert result == SwitchBotAdvertisement(
|
|
1302
|
-
address="aa:bb:cc:dd:ee:ff",
|
|
1303
|
-
data={
|
|
1304
|
-
"data": {
|
|
1305
|
-
"battery": None,
|
|
1306
|
-
"iot": None,
|
|
1307
|
-
"is_light": False,
|
|
1308
|
-
"led": None,
|
|
1309
|
-
"light_intensity": None,
|
|
1310
|
-
"motion_detected": True,
|
|
1311
|
-
"sense_distance": None,
|
|
1312
|
-
"tested": None,
|
|
1313
|
-
},
|
|
1314
|
-
"isEncrypted": False,
|
|
1315
|
-
"model": "s",
|
|
1316
|
-
"modelFriendlyName": "Motion Sensor",
|
|
1317
|
-
"modelName": SwitchbotModel.MOTION_SENSOR,
|
|
1318
|
-
"rawAdvData": None,
|
|
1319
|
-
},
|
|
1320
|
-
device=ble_device,
|
|
1321
|
-
rssi=-87,
|
|
1322
|
-
active=False,
|
|
1323
|
-
)
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
1323
|
def test_parsing_lock_active():
|
|
1327
1324
|
"""Test parsing lock with active data."""
|
|
1328
1325
|
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|