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.
Files changed (87) hide show
  1. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PKG-INFO +1 -1
  2. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/PKG-INFO +1 -1
  3. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/SOURCES.txt +4 -0
  4. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/setup.py +1 -1
  5. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parser.py +27 -2
  6. pyswitchbot-0.64.0/switchbot/adv_parsers/hub3.py +56 -0
  7. pyswitchbot-0.64.0/switchbot/adv_parsers/lock.py +85 -0
  8. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/__init__.py +3 -0
  9. pyswitchbot-0.64.0/switchbot/const/hub3.py +18 -0
  10. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/lock.py +1 -0
  11. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/lock.py +16 -3
  12. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_adv_parser.py +409 -250
  13. pyswitchbot-0.64.0/tests/test_hub3.py +13 -0
  14. pyswitchbot-0.64.0/tests/test_lock.py +42 -0
  15. pyswitchbot-0.62.2/switchbot/adv_parsers/lock.py +0 -57
  16. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/LICENSE +0 -0
  17. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/MANIFEST.in +0 -0
  18. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/dependency_links.txt +0 -0
  19. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/requires.txt +0 -0
  20. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/PySwitchbot.egg-info/top_level.txt +0 -0
  21. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/README.md +0 -0
  22. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/pyproject.toml +0 -0
  23. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/setup.cfg +0 -0
  24. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/__init__.py +0 -0
  25. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/__init__.py +0 -0
  26. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/air_purifier.py +0 -0
  27. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
  28. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/bot.py +0 -0
  29. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/bulb.py +0 -0
  30. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
  31. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/contact.py +0 -0
  32. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/curtain.py +0 -0
  33. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/fan.py +0 -0
  34. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/hub2.py +0 -0
  35. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/hubmini_matter.py +0 -0
  36. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/humidifier.py +0 -0
  37. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/keypad.py +0 -0
  38. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/leak.py +0 -0
  39. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/light_strip.py +0 -0
  40. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/meter.py +0 -0
  41. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/motion.py +0 -0
  42. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/plug.py +0 -0
  43. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/relay_switch.py +0 -0
  44. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/remote.py +0 -0
  45. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/roller_shade.py +0 -0
  46. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/adv_parsers/vacuum.py +0 -0
  47. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/api_config.py +0 -0
  48. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/air_purifier.py +0 -0
  49. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/evaporative_humidifier.py +0 -0
  50. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/fan.py +0 -0
  51. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/const/hub2.py +0 -0
  52. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/__init__.py +0 -0
  53. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/air_purifier.py +0 -0
  54. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/base_cover.py +0 -0
  55. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/base_light.py +0 -0
  56. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/blind_tilt.py +0 -0
  57. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/bot.py +0 -0
  58. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/bulb.py +0 -0
  59. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/ceiling_light.py +0 -0
  60. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/contact.py +0 -0
  61. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/curtain.py +0 -0
  62. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/device.py +0 -0
  63. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/evaporative_humidifier.py +0 -0
  64. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/fan.py +0 -0
  65. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/humidifier.py +0 -0
  66. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/keypad.py +0 -0
  67. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/light_strip.py +0 -0
  68. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/meter.py +0 -0
  69. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/motion.py +0 -0
  70. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/plug.py +0 -0
  71. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/relay_switch.py +0 -0
  72. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/roller_shade.py +0 -0
  73. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/devices/vacuum.py +0 -0
  74. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/discovery.py +0 -0
  75. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/enum.py +0 -0
  76. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/helpers.py +0 -0
  77. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/switchbot/models.py +0 -0
  78. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_air_purifier.py +0 -0
  79. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_base_cover.py +0 -0
  80. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_blind_tilt.py +0 -0
  81. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_curtain.py +0 -0
  82. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_evaporative_humidifier.py +0 -0
  83. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_fan.py +0 -0
  84. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_hub2.py +0 -0
  85. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_relay_switch.py +0 -0
  86. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_roller_shade.py +0 -0
  87. {pyswitchbot-0.62.2 → pyswitchbot-0.64.0}/tests/test_vacuum.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySwitchbot
3
- Version: 0.62.2
3
+ Version: 0.64.0
4
4
  Summary: A library to communicate with Switchbot
5
5
  Home-page: https://github.com/sblibs/pySwitchbot/
6
6
  Author: Daniel Hjelseth Hoyer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySwitchbot
3
- Version: 0.62.2
3
+ Version: 0.64.0
4
4
  Summary: A library to communicate with Switchbot
5
5
  Home-page: https://github.com/sblibs/pySwitchbot/
6
6
  Author: Daniel Hjelseth Hoyer
@@ -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
@@ -20,7 +20,7 @@ setup(
20
20
  "cryptography>=39.0.0",
21
21
  "pyOpenSSL>=23.0.0",
22
22
  ],
23
- version="0.62.2",
23
+ version="0.64.0",
24
24
  description="A library to communicate with Switchbot",
25
25
  long_description=long_description,
26
26
  long_description_content_type="text/markdown",
@@ -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
@@ -75,6 +75,9 @@ class SwitchbotModel(StrEnum):
75
75
  K10_PRO_COMBO_VACUUM = "K10+ Pro Combo Vacuum"
76
76
  AIR_PURIFIER = "Air Purifier"
77
77
  AIR_PURIFIER_TABLE = "Air Purifier Table"
78
+ HUB3 = "Hub3"
79
+ LOCK_ULTRA = "Lock Ultra"
80
+ LOCK_LITE = "Lock Lite"
78
81
 
79
82
 
80
83
  __all__ = [
@@ -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
+ }
@@ -11,3 +11,4 @@ class LockStatus(Enum):
11
11
  LOCKING_STOP = 4 # LOCKING_BLOCKED
12
12
  UNLOCKING_STOP = 5 # UNLOCKING_BLOCKED
13
13
  NOT_FULLY_LOCKED = 6 # LATCH_LOCKED - Only EU lock type
14
+ HALF_LOCKED = 7 # Only Lock2 EU lock type
@@ -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 (SwitchbotModel.LOCK, SwitchbotModel.LOCK_PRO):
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)