PySwitchbot 0.62.1__tar.gz → 0.63.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.
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/PKG-INFO +1 -1
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/PySwitchbot.egg-info/SOURCES.txt +3 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/setup.py +1 -1
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parser.py +14 -1
- pyswitchbot-0.63.0/switchbot/adv_parsers/hub3.py +56 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/const/__init__.py +1 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/const/air_purifier.py +2 -2
- pyswitchbot-0.63.0/switchbot/const/hub3.py +18 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/air_purifier.py +2 -2
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_adv_parser.py +101 -1
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_air_purifier.py +2 -2
- pyswitchbot-0.63.0/tests/test_hub3.py +13 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/LICENSE +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/MANIFEST.in +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/README.md +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/pyproject.toml +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/setup.cfg +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/__init__.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/air_purifier.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/fan.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/hub2.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/hubmini_matter.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/keypad.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/leak.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/lock.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/relay_switch.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/remote.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/roller_shade.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/adv_parsers/vacuum.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/api_config.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/const/evaporative_humidifier.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/const/fan.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/const/hub2.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/const/lock.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/base_light.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/device.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/evaporative_humidifier.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/fan.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/keypad.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/lock.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/relay_switch.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/roller_shade.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/devices/vacuum.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/discovery.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/enum.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/helpers.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/switchbot/models.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_base_cover.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_curtain.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_evaporative_humidifier.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_fan.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_hub2.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_relay_switch.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_roller_shade.py +0 -0
- {pyswitchbot-0.62.1 → pyswitchbot-0.63.0}/tests/test_vacuum.py +0 -0
|
@@ -25,6 +25,7 @@ switchbot/adv_parsers/contact.py
|
|
|
25
25
|
switchbot/adv_parsers/curtain.py
|
|
26
26
|
switchbot/adv_parsers/fan.py
|
|
27
27
|
switchbot/adv_parsers/hub2.py
|
|
28
|
+
switchbot/adv_parsers/hub3.py
|
|
28
29
|
switchbot/adv_parsers/hubmini_matter.py
|
|
29
30
|
switchbot/adv_parsers/humidifier.py
|
|
30
31
|
switchbot/adv_parsers/keypad.py
|
|
@@ -43,6 +44,7 @@ switchbot/const/air_purifier.py
|
|
|
43
44
|
switchbot/const/evaporative_humidifier.py
|
|
44
45
|
switchbot/const/fan.py
|
|
45
46
|
switchbot/const/hub2.py
|
|
47
|
+
switchbot/const/hub3.py
|
|
46
48
|
switchbot/const/lock.py
|
|
47
49
|
switchbot/devices/__init__.py
|
|
48
50
|
switchbot/devices/air_purifier.py
|
|
@@ -75,6 +77,7 @@ tests/test_curtain.py
|
|
|
75
77
|
tests/test_evaporative_humidifier.py
|
|
76
78
|
tests/test_fan.py
|
|
77
79
|
tests/test_hub2.py
|
|
80
|
+
tests/test_hub3.py
|
|
78
81
|
tests/test_relay_switch.py
|
|
79
82
|
tests/test_roller_shade.py
|
|
80
83
|
tests/test_vacuum.py
|
|
@@ -19,6 +19,7 @@ from .adv_parsers.contact import process_wocontact
|
|
|
19
19
|
from .adv_parsers.curtain import process_wocurtain
|
|
20
20
|
from .adv_parsers.fan import process_fan
|
|
21
21
|
from .adv_parsers.hub2 import process_wohub2
|
|
22
|
+
from .adv_parsers.hub3 import process_hub3
|
|
22
23
|
from .adv_parsers.hubmini_matter import process_hubmini_matter
|
|
23
24
|
from .adv_parsers.humidifier import process_evaporative_humidifier, process_wohumidifier
|
|
24
25
|
from .adv_parsers.keypad import process_wokeypad
|
|
@@ -57,7 +58,7 @@ class SwitchbotSupportedType(TypedDict):
|
|
|
57
58
|
manufacturer_data_length: int | None
|
|
58
59
|
|
|
59
60
|
|
|
60
|
-
SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
|
|
61
|
+
SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
|
|
61
62
|
"d": {
|
|
62
63
|
"modelName": SwitchbotModel.CONTACT_SENSOR,
|
|
63
64
|
"modelFriendlyName": "Contact Sensor",
|
|
@@ -293,6 +294,12 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
|
|
|
293
294
|
"func": process_air_purifier,
|
|
294
295
|
"manufacturer_id": 2409,
|
|
295
296
|
},
|
|
297
|
+
b"\x00\x10\xb9\x40": {
|
|
298
|
+
"modelName": SwitchbotModel.HUB3,
|
|
299
|
+
"modelFriendlyName": "Hub3",
|
|
300
|
+
"func": process_hub3,
|
|
301
|
+
"manufacturer_id": 2409,
|
|
302
|
+
},
|
|
296
303
|
}
|
|
297
304
|
|
|
298
305
|
_SWITCHBOT_MODEL_TO_CHAR = {
|
|
@@ -371,6 +378,12 @@ def _parse_data(
|
|
|
371
378
|
if model_data.get("manufacturer_data_length") == len(_mfr_data):
|
|
372
379
|
_model = model_chr
|
|
373
380
|
break
|
|
381
|
+
if (
|
|
382
|
+
_service_data
|
|
383
|
+
and len(_service_data) > 5
|
|
384
|
+
and _service_data[-4:] in SUPPORTED_TYPES
|
|
385
|
+
):
|
|
386
|
+
_model = _service_data[-4:]
|
|
374
387
|
|
|
375
388
|
if not _model:
|
|
376
389
|
return None
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Air Purifier adv parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ..const.hub3 import LIGHT_INTENSITY_MAP
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def process_hub3(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
|
|
11
|
+
"""Process hub3 sensor manufacturer data."""
|
|
12
|
+
if mfr_data is None:
|
|
13
|
+
return {}
|
|
14
|
+
device_data = mfr_data[6:]
|
|
15
|
+
|
|
16
|
+
seq_num = device_data[0]
|
|
17
|
+
network_state = (device_data[6] & 0b11000000) >> 6
|
|
18
|
+
sensor_inserted = not bool(device_data[6] & 0b00100000)
|
|
19
|
+
light_level = device_data[6] & 0b00001111
|
|
20
|
+
illuminance = calculate_light_intensity(light_level)
|
|
21
|
+
temperature_alarm = bool(device_data[7] & 0b11000000)
|
|
22
|
+
humidity_alarm = bool(device_data[7] & 0b00110000)
|
|
23
|
+
|
|
24
|
+
temp_data = device_data[7:10]
|
|
25
|
+
_temp_sign = 1 if temp_data[1] & 0b10000000 else -1
|
|
26
|
+
_temp_c = _temp_sign * (
|
|
27
|
+
(temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
|
|
28
|
+
)
|
|
29
|
+
_temp_f = round(((_temp_c * 9 / 5) + 32), 1)
|
|
30
|
+
humidity = temp_data[2] & 0b01111111
|
|
31
|
+
motion_detected = bool(device_data[10] & 0b10000000)
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
"sequence_number": seq_num,
|
|
35
|
+
"network_state": network_state,
|
|
36
|
+
"sensor_inserted": sensor_inserted,
|
|
37
|
+
"lightLevel": light_level,
|
|
38
|
+
"illuminance": illuminance,
|
|
39
|
+
"temperature_alarm": temperature_alarm,
|
|
40
|
+
"humidity_alarm": humidity_alarm,
|
|
41
|
+
"temp": {"c": _temp_c, "f": _temp_f},
|
|
42
|
+
"temperature": _temp_c,
|
|
43
|
+
"humidity": humidity,
|
|
44
|
+
"motion_detected": motion_detected,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def calculate_light_intensity(light_level: int) -> int:
|
|
49
|
+
"""
|
|
50
|
+
Convert Hub 3 light level (1-10) to actual light intensity value
|
|
51
|
+
Args:
|
|
52
|
+
light_level: Integer from 1-10
|
|
53
|
+
Returns:
|
|
54
|
+
Corresponding light intensity value or 0 if invalid input
|
|
55
|
+
"""
|
|
56
|
+
return LIGHT_INTENSITY_MAP.get(max(0, min(light_level, 10)), 0)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mapping of light levels to lux measurement values for SwitchBot Hub 3.
|
|
3
|
+
|
|
4
|
+
Source: After-sales consultation, line chart data provided by switchbot developers
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
LIGHT_INTENSITY_MAP = {
|
|
8
|
+
1: 0,
|
|
9
|
+
2: 50,
|
|
10
|
+
3: 90,
|
|
11
|
+
4: 205,
|
|
12
|
+
5: 317,
|
|
13
|
+
6: 510,
|
|
14
|
+
7: 610,
|
|
15
|
+
8: 707,
|
|
16
|
+
9: 801,
|
|
17
|
+
10: 1023,
|
|
18
|
+
}
|
|
@@ -28,8 +28,8 @@ COMMAND_SET_MODE = {
|
|
|
28
28
|
AirPurifierMode.LEVEL_2.name.lower(): f"{COMMAND_HEAD}01010132",
|
|
29
29
|
AirPurifierMode.LEVEL_3.name.lower(): f"{COMMAND_HEAD}01010164",
|
|
30
30
|
AirPurifierMode.AUTO.name.lower(): f"{COMMAND_HEAD}01010200",
|
|
31
|
-
AirPurifierMode.
|
|
32
|
-
AirPurifierMode.
|
|
31
|
+
AirPurifierMode.SLEEP.name.lower(): f"{COMMAND_HEAD}01010300",
|
|
32
|
+
AirPurifierMode.PET.name.lower(): f"{COMMAND_HEAD}01010400",
|
|
33
33
|
}
|
|
34
34
|
DEVICE_GET_BASIC_SETTINGS_KEY = "570f4d81"
|
|
35
35
|
|
|
@@ -2692,7 +2692,7 @@ def test_s10_with_empty_data() -> None:
|
|
|
2692
2692
|
b"+\x00\x00\x15\x04\x00",
|
|
2693
2693
|
{
|
|
2694
2694
|
"isOn": True,
|
|
2695
|
-
"mode": "
|
|
2695
|
+
"mode": "sleep",
|
|
2696
2696
|
"isAqiValid": False,
|
|
2697
2697
|
"child_lock": False,
|
|
2698
2698
|
"speed": 100,
|
|
@@ -2842,3 +2842,103 @@ def test_air_purifier_with_empty_data() -> None:
|
|
|
2842
2842
|
rssi=-97,
|
|
2843
2843
|
active=True,
|
|
2844
2844
|
)
|
|
2845
|
+
|
|
2846
|
+
|
|
2847
|
+
def test_hub3_active() -> None:
|
|
2848
|
+
"""Test parsing hub3 with active data."""
|
|
2849
|
+
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
2850
|
+
adv_data = generate_advertisement_data(
|
|
2851
|
+
manufacturer_data={2409: b"\xb0\xe9\xfen^)\x00\xffh&\xd6d\x83\x03\x994\x80"},
|
|
2852
|
+
service_data={
|
|
2853
|
+
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x00d\x00\x10\xb9@"
|
|
2854
|
+
},
|
|
2855
|
+
rssi=-97,
|
|
2856
|
+
)
|
|
2857
|
+
result = parse_advertisement_data(ble_device, adv_data)
|
|
2858
|
+
assert result == SwitchBotAdvertisement(
|
|
2859
|
+
address="aa:bb:cc:dd:ee:ff",
|
|
2860
|
+
data={
|
|
2861
|
+
"rawAdvData": b"\x00\x00d\x00\x10\xb9@",
|
|
2862
|
+
"data": {
|
|
2863
|
+
"sequence_number": 0,
|
|
2864
|
+
"network_state": 2,
|
|
2865
|
+
"sensor_inserted": True,
|
|
2866
|
+
"lightLevel": 3,
|
|
2867
|
+
"illuminance": 90,
|
|
2868
|
+
"temperature_alarm": False,
|
|
2869
|
+
"humidity_alarm": False,
|
|
2870
|
+
"temp": {"c": 25.3, "f": 77.5},
|
|
2871
|
+
"temperature": 25.3,
|
|
2872
|
+
"humidity": 52,
|
|
2873
|
+
"motion_detected": True,
|
|
2874
|
+
},
|
|
2875
|
+
"isEncrypted": False,
|
|
2876
|
+
"model": b"\x00\x10\xb9@",
|
|
2877
|
+
"modelFriendlyName": "Hub3",
|
|
2878
|
+
"modelName": SwitchbotModel.HUB3,
|
|
2879
|
+
},
|
|
2880
|
+
device=ble_device,
|
|
2881
|
+
rssi=-97,
|
|
2882
|
+
active=True,
|
|
2883
|
+
)
|
|
2884
|
+
|
|
2885
|
+
|
|
2886
|
+
def test_hub3_passive() -> None:
|
|
2887
|
+
"""Test parsing hub3 with passive data."""
|
|
2888
|
+
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
2889
|
+
adv_data = generate_advertisement_data(
|
|
2890
|
+
manufacturer_data={2409: b"\xb0\xe9\xfen^)\x00\xffh&\xd6d\x83\x03\x994\x80"},
|
|
2891
|
+
rssi=-97,
|
|
2892
|
+
)
|
|
2893
|
+
result = parse_advertisement_data(ble_device, adv_data, SwitchbotModel.HUB3)
|
|
2894
|
+
assert result == SwitchBotAdvertisement(
|
|
2895
|
+
address="aa:bb:cc:dd:ee:ff",
|
|
2896
|
+
data={
|
|
2897
|
+
"rawAdvData": None,
|
|
2898
|
+
"data": {
|
|
2899
|
+
"sequence_number": 0,
|
|
2900
|
+
"network_state": 2,
|
|
2901
|
+
"sensor_inserted": True,
|
|
2902
|
+
"lightLevel": 3,
|
|
2903
|
+
"illuminance": 90,
|
|
2904
|
+
"temperature_alarm": False,
|
|
2905
|
+
"humidity_alarm": False,
|
|
2906
|
+
"temp": {"c": 25.3, "f": 77.5},
|
|
2907
|
+
"temperature": 25.3,
|
|
2908
|
+
"humidity": 52,
|
|
2909
|
+
"motion_detected": True,
|
|
2910
|
+
},
|
|
2911
|
+
"isEncrypted": False,
|
|
2912
|
+
"model": b"\x00\x10\xb9@",
|
|
2913
|
+
"modelFriendlyName": "Hub3",
|
|
2914
|
+
"modelName": SwitchbotModel.HUB3,
|
|
2915
|
+
},
|
|
2916
|
+
device=ble_device,
|
|
2917
|
+
rssi=-97,
|
|
2918
|
+
active=False,
|
|
2919
|
+
)
|
|
2920
|
+
|
|
2921
|
+
|
|
2922
|
+
def test_hub3_with_empty_data() -> None:
|
|
2923
|
+
"""Test parsing hub3 with empty data."""
|
|
2924
|
+
ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
|
|
2925
|
+
adv_data = generate_advertisement_data(
|
|
2926
|
+
manufacturer_data={2409: None},
|
|
2927
|
+
service_data={
|
|
2928
|
+
"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00\x00d\x00\x10\xb9@"
|
|
2929
|
+
},
|
|
2930
|
+
rssi=-97,
|
|
2931
|
+
)
|
|
2932
|
+
result = parse_advertisement_data(ble_device, adv_data)
|
|
2933
|
+
assert result == SwitchBotAdvertisement(
|
|
2934
|
+
address="aa:bb:cc:dd:ee:ff",
|
|
2935
|
+
data={
|
|
2936
|
+
"rawAdvData": b"\x00\x00d\x00\x10\xb9@",
|
|
2937
|
+
"data": {},
|
|
2938
|
+
"isEncrypted": False,
|
|
2939
|
+
"model": b"\x00\x10\xb9@",
|
|
2940
|
+
},
|
|
2941
|
+
device=ble_device,
|
|
2942
|
+
rssi=-97,
|
|
2943
|
+
active=True,
|
|
2944
|
+
)
|
|
@@ -171,7 +171,7 @@ async def test__get_basic_info(rawAdvData, model, response, expected):
|
|
|
171
171
|
bytearray(
|
|
172
172
|
b"\x01\xa8\xec\x8c\x08\x00\xb2\x01\x96\x00\x00\x00\xf0\x00\x00\x17"
|
|
173
173
|
),
|
|
174
|
-
[True, 2, "
|
|
174
|
+
[True, 2, "pet", True, False, "excellent", 50, 240, 2.3],
|
|
175
175
|
),
|
|
176
176
|
],
|
|
177
177
|
)
|
|
@@ -226,6 +226,6 @@ def test_get_modes():
|
|
|
226
226
|
"level_2",
|
|
227
227
|
"level_3",
|
|
228
228
|
"auto",
|
|
229
|
-
"pet",
|
|
230
229
|
"sleep",
|
|
230
|
+
"pet",
|
|
231
231
|
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from switchbot.adv_parsers.hub3 import calculate_light_intensity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_calculate_light_intensity():
|
|
5
|
+
"""Test calculating light intensity from Hub 3 light level."""
|
|
6
|
+
# Test valid inputs
|
|
7
|
+
assert calculate_light_intensity(1) == 0
|
|
8
|
+
assert calculate_light_intensity(2) == 50
|
|
9
|
+
assert calculate_light_intensity(5) == 317
|
|
10
|
+
assert calculate_light_intensity(10) == 1023
|
|
11
|
+
|
|
12
|
+
# Test invalid inputs
|
|
13
|
+
assert calculate_light_intensity(0) == 0
|
|
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
|
|
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
|