PySwitchbot 0.63.0__tar.gz → 0.64.1__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.63.0 → pyswitchbot-0.64.1}/PKG-INFO +1 -1
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/PySwitchbot.egg-info/SOURCES.txt +1 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/setup.py +1 -1
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parser.py +13 -1
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/blind_tilt.py +1 -1
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/hub3.py +1 -1
- pyswitchbot-0.64.1/switchbot/adv_parsers/lock.py +85 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/const/__init__.py +2 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/const/lock.py +1 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/lock.py +16 -3
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_adv_parser.py +395 -250
- pyswitchbot-0.64.1/tests/test_lock.py +42 -0
- pyswitchbot-0.63.0/switchbot/adv_parsers/lock.py +0 -57
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/LICENSE +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/MANIFEST.in +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/README.md +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/pyproject.toml +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/setup.cfg +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/__init__.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/air_purifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/fan.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/hub2.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/hubmini_matter.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/keypad.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/leak.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/relay_switch.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/remote.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/roller_shade.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/adv_parsers/vacuum.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/api_config.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/const/air_purifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/const/evaporative_humidifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/const/fan.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/const/hub2.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/const/hub3.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/air_purifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/base_light.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/device.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/evaporative_humidifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/fan.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/keypad.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/relay_switch.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/roller_shade.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/devices/vacuum.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/discovery.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/enum.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/helpers.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/switchbot/models.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_air_purifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_base_cover.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_curtain.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_evaporative_humidifier.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_fan.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_hub2.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_hub3.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_relay_switch.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_roller_shade.py +0 -0
- {pyswitchbot-0.63.0 → pyswitchbot-0.64.1}/tests/test_vacuum.py +0 -0
|
@@ -25,7 +25,7 @@ from .adv_parsers.humidifier import process_evaporative_humidifier, process_wohu
|
|
|
25
25
|
from .adv_parsers.keypad import process_wokeypad
|
|
26
26
|
from .adv_parsers.leak import process_leak
|
|
27
27
|
from .adv_parsers.light_strip import process_wostrip
|
|
28
|
-
from .adv_parsers.lock import process_wolock, process_wolock_pro
|
|
28
|
+
from .adv_parsers.lock import process_lock2, process_wolock, process_wolock_pro
|
|
29
29
|
from .adv_parsers.meter import process_wosensorth, process_wosensorth_c
|
|
30
30
|
from .adv_parsers.motion import process_wopresence
|
|
31
31
|
from .adv_parsers.plug import process_woplugmini
|
|
@@ -300,6 +300,18 @@ SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
|
|
|
300
300
|
"func": process_hub3,
|
|
301
301
|
"manufacturer_id": 2409,
|
|
302
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
|
+
},
|
|
303
315
|
}
|
|
304
316
|
|
|
305
317
|
_SWITCHBOT_MODEL_TO_CHAR = {
|
|
@@ -14,7 +14,7 @@ def process_woblindtilt(
|
|
|
14
14
|
|
|
15
15
|
_tilt = max(min(device_data[2] & 0b01111111, 100), 0)
|
|
16
16
|
_in_motion = bool(device_data[2] & 0b10000000)
|
|
17
|
-
_light_level = (device_data[
|
|
17
|
+
_light_level = (device_data[3] >> 4) & 0b00001111
|
|
18
18
|
_calibrated = bool(device_data[1] & 0b00000001)
|
|
19
19
|
|
|
20
20
|
return {
|
|
@@ -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
|
|
@@ -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)
|