PySwitchbot 0.62.2__tar.gz → 0.64.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.2 → pyswitchbot-0.64.0}/PKG-INFO +1 -1
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/SOURCES.txt +4 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/setup.py +1 -1
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parser.py +27 -2
- pyswitchbot-0.64.0/switchbot/adv_parsers/hub3.py +56 -0
- pyswitchbot-0.64.0/switchbot/adv_parsers/lock.py +85 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/__init__.py +3 -0
- pyswitchbot-0.64.0/switchbot/const/hub3.py +18 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/lock.py +1 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/lock.py +16 -3
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_adv_parser.py +409 -250
- pyswitchbot-0.64.0/tests/test_hub3.py +13 -0
- pyswitchbot-0.64.0/tests/test_lock.py +42 -0
- pyswitchbot-0.62.2/switchbot/adv_parsers/lock.py +0 -57
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/LICENSE +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/MANIFEST.in +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/README.md +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/pyproject.toml +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/setup.cfg +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/__init__.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/air_purifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/fan.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/hub2.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/hubmini_matter.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/keypad.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/leak.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/relay_switch.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/remote.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/roller_shade.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/vacuum.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/api_config.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/air_purifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/evaporative_humidifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/fan.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/hub2.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/air_purifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/base_light.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/device.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/evaporative_humidifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/fan.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/keypad.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/relay_switch.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/roller_shade.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/vacuum.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/discovery.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/enum.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/helpers.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/models.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_air_purifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_base_cover.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_curtain.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_evaporative_humidifier.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_fan.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_hub2.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_relay_switch.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_roller_shade.py +0 -0
- {pyswitchbot-0.62.2 → pyswitchbot-0.64.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,8 @@ 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
|
|
81
|
+
tests/test_lock.py
|
|
78
82
|
tests/test_relay_switch.py
|
|
79
83
|
tests/test_roller_shade.py
|
|
80
84
|
tests/test_vacuum.py
|
|
@@ -19,12 +19,13 @@ 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
|
|
25
26
|
from .adv_parsers.leak import process_leak
|
|
26
27
|
from .adv_parsers.light_strip import process_wostrip
|
|
27
|
-
from .adv_parsers.lock import process_wolock, process_wolock_pro
|
|
28
|
+
from .adv_parsers.lock import process_lock2, process_wolock, process_wolock_pro
|
|
28
29
|
from .adv_parsers.meter import process_wosensorth, process_wosensorth_c
|
|
29
30
|
from .adv_parsers.motion import process_wopresence
|
|
30
31
|
from .adv_parsers.plug import process_woplugmini
|
|
@@ -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,24 @@ 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
|
+
},
|
|
303
|
+
"-": {
|
|
304
|
+
"modelName": SwitchbotModel.LOCK_LITE,
|
|
305
|
+
"modelFriendlyName": "Lock Lite",
|
|
306
|
+
"func": process_wolock,
|
|
307
|
+
"manufacturer_id": 2409,
|
|
308
|
+
},
|
|
309
|
+
b"\x00\x10\xa5\xb8": {
|
|
310
|
+
"modelName": SwitchbotModel.LOCK_ULTRA,
|
|
311
|
+
"modelFriendlyName": "Lock Ultra",
|
|
312
|
+
"func": process_lock2,
|
|
313
|
+
"manufacturer_id": 2409,
|
|
314
|
+
},
|
|
296
315
|
}
|
|
297
316
|
|
|
298
317
|
_SWITCHBOT_MODEL_TO_CHAR = {
|
|
@@ -371,6 +390,12 @@ def _parse_data(
|
|
|
371
390
|
if model_data.get("manufacturer_data_length") == len(_mfr_data):
|
|
372
391
|
_model = model_chr
|
|
373
392
|
break
|
|
393
|
+
if (
|
|
394
|
+
_service_data
|
|
395
|
+
and len(_service_data) > 5
|
|
396
|
+
and _service_data[-4:] in SUPPORTED_TYPES
|
|
397
|
+
):
|
|
398
|
+
_model = _service_data[-4:]
|
|
374
399
|
|
|
375
400
|
if not _model:
|
|
376
401
|
return None
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Hub3 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,85 @@
|
|
|
1
|
+
"""Lock parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from ..const.lock import LockStatus
|
|
8
|
+
|
|
9
|
+
_LOGGER = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def process_wolock(data: bytes | None, mfr_data: bytes | None) -> dict[str, bool | int]:
|
|
13
|
+
"""Support for lock and lock lite process data."""
|
|
14
|
+
if mfr_data is None:
|
|
15
|
+
return {}
|
|
16
|
+
|
|
17
|
+
_LOGGER.debug("mfr_data: %s", mfr_data.hex())
|
|
18
|
+
if data:
|
|
19
|
+
_LOGGER.debug("data: %s", data.hex())
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
"sequence_number": mfr_data[6],
|
|
23
|
+
"battery": data[2] & 0b01111111 if data else None,
|
|
24
|
+
"calibration": bool(mfr_data[7] & 0b10000000),
|
|
25
|
+
"status": LockStatus((mfr_data[7] & 0b01110000) >> 4),
|
|
26
|
+
"update_from_secondary_lock": bool(mfr_data[7] & 0b00001000),
|
|
27
|
+
"door_open": bool(mfr_data[7] & 0b00000100),
|
|
28
|
+
"double_lock_mode": bool(mfr_data[8] & 0b10000000),
|
|
29
|
+
"unclosed_alarm": bool(mfr_data[8] & 0b00100000),
|
|
30
|
+
"unlocked_alarm": bool(mfr_data[8] & 0b00010000),
|
|
31
|
+
"auto_lock_paused": bool(mfr_data[8] & 0b00000010),
|
|
32
|
+
"night_latch": bool(mfr_data[9] & 0b00000001) if len(mfr_data) > 9 else False,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def parse_common_data(mfr_data: bytes | None) -> dict[str, bool | int]:
|
|
37
|
+
if mfr_data is None:
|
|
38
|
+
return {}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"sequence_number": mfr_data[6],
|
|
42
|
+
"calibration": bool(mfr_data[7] & 0b10000000),
|
|
43
|
+
"status": LockStatus((mfr_data[7] & 0b01111000) >> 4),
|
|
44
|
+
"update_from_secondary_lock": bool(mfr_data[8] & 0b11000000),
|
|
45
|
+
"door_open_from_secondary_lock": bool(mfr_data[8] & 0b00100000),
|
|
46
|
+
"door_open": bool(mfr_data[8] & 0b00010000),
|
|
47
|
+
"auto_lock_paused": bool(mfr_data[8] & 0b00001000),
|
|
48
|
+
"battery": mfr_data[9] & 0b01111111,
|
|
49
|
+
"double_lock_mode": bool(mfr_data[10] & 0b10000000),
|
|
50
|
+
"is_secondary_lock": bool(mfr_data[10] & 0b01000000),
|
|
51
|
+
"manual_unlock_linkage": bool(mfr_data[10] & 0b00100000),
|
|
52
|
+
"unclosed_alarm": bool(mfr_data[11] & 0b10000000),
|
|
53
|
+
"unlocked_alarm": bool(mfr_data[11] & 0b01000000),
|
|
54
|
+
"night_latch": False,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def process_wolock_pro(
|
|
59
|
+
data: bytes | None, mfr_data: bytes | None
|
|
60
|
+
) -> dict[str, bool | int]:
|
|
61
|
+
"""Support for lock pro process data."""
|
|
62
|
+
common_data = parse_common_data(mfr_data)
|
|
63
|
+
if not common_data:
|
|
64
|
+
return {}
|
|
65
|
+
|
|
66
|
+
lock_pro_data = {
|
|
67
|
+
"low_temperature_alarm": bool(mfr_data[11] & 0b00100000),
|
|
68
|
+
"left_battery_compartment_alarm": mfr_data[11] & 0b000000100,
|
|
69
|
+
"right_battery_compartment_alarm": mfr_data[11] & 0b000000010,
|
|
70
|
+
}
|
|
71
|
+
return common_data | lock_pro_data
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def process_lock2(data: bytes | None, mfr_data: bytes | None) -> dict[str, bool | int]:
|
|
75
|
+
"""Support for lock2 process data."""
|
|
76
|
+
common_data = parse_common_data(mfr_data)
|
|
77
|
+
if not common_data:
|
|
78
|
+
return {}
|
|
79
|
+
|
|
80
|
+
lock2_data = {
|
|
81
|
+
"power_alarm": bool(mfr_data[11] & 0b00010000),
|
|
82
|
+
"battery_status": mfr_data[11] & 0b00000111,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return common_data | lock2_data
|
|
@@ -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
|
+
}
|
|
@@ -10,24 +10,32 @@ from bleak.backends.device import BLEDevice
|
|
|
10
10
|
|
|
11
11
|
from ..const import SwitchbotModel
|
|
12
12
|
from ..const.lock import LockStatus
|
|
13
|
-
from .device import SwitchbotEncryptedDevice
|
|
13
|
+
from .device import SwitchbotEncryptedDevice, SwitchbotSequenceDevice
|
|
14
14
|
|
|
15
15
|
COMMAND_HEADER = "57"
|
|
16
16
|
COMMAND_LOCK_INFO = {
|
|
17
17
|
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4f8101",
|
|
18
|
+
SwitchbotModel.LOCK_LITE: f"{COMMAND_HEADER}0f4f8101",
|
|
18
19
|
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4f8102",
|
|
20
|
+
SwitchbotModel.LOCK_ULTRA: f"{COMMAND_HEADER}0f4f8102",
|
|
19
21
|
}
|
|
20
22
|
COMMAND_UNLOCK = {
|
|
21
23
|
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4e01011080",
|
|
24
|
+
SwitchbotModel.LOCK_LITE: f"{COMMAND_HEADER}0f4e01011080",
|
|
22
25
|
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4e0101000080",
|
|
26
|
+
SwitchbotModel.LOCK_ULTRA: f"{COMMAND_HEADER}0f4e0101000080",
|
|
23
27
|
}
|
|
24
28
|
COMMAND_UNLOCK_WITHOUT_UNLATCH = {
|
|
25
29
|
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4e010110a0",
|
|
30
|
+
SwitchbotModel.LOCK_LITE: f"{COMMAND_HEADER}0f4e010110a0",
|
|
26
31
|
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4e01010000a0",
|
|
32
|
+
SwitchbotModel.LOCK_ULTRA: f"{COMMAND_HEADER}0f4e01010000a0",
|
|
27
33
|
}
|
|
28
34
|
COMMAND_LOCK = {
|
|
29
35
|
SwitchbotModel.LOCK: f"{COMMAND_HEADER}0f4e01011000",
|
|
36
|
+
SwitchbotModel.LOCK_LITE: f"{COMMAND_HEADER}0f4e01011000",
|
|
30
37
|
SwitchbotModel.LOCK_PRO: f"{COMMAND_HEADER}0f4e0101000000",
|
|
38
|
+
SwitchbotModel.LOCK_ULTRA: f"{COMMAND_HEADER}0f4e0101000000",
|
|
31
39
|
}
|
|
32
40
|
COMMAND_ENABLE_NOTIFICATIONS = f"{COMMAND_HEADER}0e01001e00008101"
|
|
33
41
|
COMMAND_DISABLE_NOTIFICATIONS = f"{COMMAND_HEADER}0e00"
|
|
@@ -44,7 +52,7 @@ COMMAND_RESULT_EXPECTED_VALUES = {1, 6}
|
|
|
44
52
|
# The return value of the command is 6 when the command is successful but the battery is low.
|
|
45
53
|
|
|
46
54
|
|
|
47
|
-
class SwitchbotLock(SwitchbotEncryptedDevice):
|
|
55
|
+
class SwitchbotLock(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
|
|
48
56
|
"""Representation of a Switchbot Lock."""
|
|
49
57
|
|
|
50
58
|
def __init__(
|
|
@@ -56,7 +64,12 @@ class SwitchbotLock(SwitchbotEncryptedDevice):
|
|
|
56
64
|
model: SwitchbotModel = SwitchbotModel.LOCK,
|
|
57
65
|
**kwargs: Any,
|
|
58
66
|
) -> None:
|
|
59
|
-
if model not in (
|
|
67
|
+
if model not in (
|
|
68
|
+
SwitchbotModel.LOCK,
|
|
69
|
+
SwitchbotModel.LOCK_PRO,
|
|
70
|
+
SwitchbotModel.LOCK_LITE,
|
|
71
|
+
SwitchbotModel.LOCK_ULTRA,
|
|
72
|
+
):
|
|
60
73
|
raise ValueError("initializing SwitchbotLock with a non-lock model")
|
|
61
74
|
self._notifications_enabled: bool = False
|
|
62
75
|
super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
|