PySwitchbot 1.0.0__tar.gz → 2.0.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-1.0.0 → pyswitchbot-2.0.0}/PKG-INFO +1 -1
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/pyproject.toml +0 -1
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/setup.py +1 -1
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/__init__.py +2 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parser.py +52 -16
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/__init__.py +9 -3
- pyswitchbot-2.0.0/switchbot/devices/air_purifier.py +311 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/base_light.py +26 -8
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/device.py +23 -4
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/lock.py +20 -2
- pyswitchbot-2.0.0/switchbot/enum.py +7 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_adv_parser.py +177 -15
- pyswitchbot-2.0.0/tests/test_air_purifier.py +484 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_bulb.py +8 -18
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_ceiling_light.py +8 -18
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_lock.py +127 -3
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_strip_light.py +8 -16
- pyswitchbot-1.0.0/switchbot/devices/air_purifier.py +0 -131
- pyswitchbot-1.0.0/switchbot/enum.py +0 -20
- pyswitchbot-1.0.0/tests/test_air_purifier.py +0 -231
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/LICENSE +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/MANIFEST.in +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/SOURCES.txt +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/README.md +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/setup.cfg +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/air_purifier.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/art_frame.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/climate_panel.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/fan.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/hub2.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/hub3.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/hubmini_matter.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/keypad.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/keypad_vision.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/leak.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/lock.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/presence_sensor.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/relay_switch.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/remote.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/roller_shade.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/smart_thermostat_radiator.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/vacuum.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/api_config.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/air_purifier.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/climate.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/evaporative_humidifier.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/fan.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/hub2.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/hub3.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/light.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/lock.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/const/presence_sensor.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/art_frame.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/evaporative_humidifier.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/fan.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/keypad.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/keypad_vision.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/meter_pro.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/relay_switch.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/roller_shade.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/smart_thermostat_radiator.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/devices/vacuum.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/discovery.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/helpers.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/models.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/switchbot/utils.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_art_frame.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_base_cover.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_colormode_imports.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_curtain.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_device.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_discovery_callback.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_encrypted_device.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_evaporative_humidifier.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_fan.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_helpers.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_hub2.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_hub3.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_keypad_vision.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_meter_pro.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_relay_switch.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_roller_shade.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_smart_thermostat_radiator.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_utils.py +0 -0
- {pyswitchbot-1.0.0 → pyswitchbot-2.0.0}/tests/test_vacuum.py +0 -0
|
@@ -25,7 +25,6 @@ ignore = [
|
|
|
25
25
|
"S110", # `try`-`except`-`pass` detected, consider logging the exception
|
|
26
26
|
"D106", # Missing docstring in public nested class
|
|
27
27
|
"UP007", # typer needs Optional syntax
|
|
28
|
-
"UP038", # Use `X | Y` in `isinstance` is slower
|
|
29
28
|
"S603", # check for execution of untrusted input
|
|
30
29
|
"S105", # possible hard coded creds
|
|
31
30
|
"TID252", # not for this lib
|
|
@@ -11,6 +11,7 @@ from bleak_retry_connector import (
|
|
|
11
11
|
from .adv_parser import SwitchbotSupportedType, parse_advertisement_data
|
|
12
12
|
from .const import (
|
|
13
13
|
AirPurifierMode,
|
|
14
|
+
AirQualityLevel,
|
|
14
15
|
BulbColorMode,
|
|
15
16
|
CeilingLightColorMode,
|
|
16
17
|
ClimateAction,
|
|
@@ -67,6 +68,7 @@ from .models import SwitchBotAdvertisement
|
|
|
67
68
|
|
|
68
69
|
__all__ = [
|
|
69
70
|
"AirPurifierMode",
|
|
71
|
+
"AirQualityLevel",
|
|
70
72
|
"BulbColorMode",
|
|
71
73
|
"CeilingLightColorMode",
|
|
72
74
|
"ClimateAction",
|
|
@@ -492,50 +492,50 @@ SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
|
|
|
492
492
|
"manufacturer_id": 2409,
|
|
493
493
|
},
|
|
494
494
|
"*": {
|
|
495
|
-
"modelName": SwitchbotModel.
|
|
496
|
-
"modelFriendlyName": "Air Purifier",
|
|
495
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_US,
|
|
496
|
+
"modelFriendlyName": "Air Purifier US",
|
|
497
497
|
"func": process_air_purifier,
|
|
498
498
|
"manufacturer_id": 2409,
|
|
499
499
|
},
|
|
500
500
|
b"\x0a": {
|
|
501
|
-
"modelName": SwitchbotModel.
|
|
502
|
-
"modelFriendlyName": "Air Purifier",
|
|
501
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_US,
|
|
502
|
+
"modelFriendlyName": "Air Purifier US",
|
|
503
503
|
"func": process_air_purifier,
|
|
504
504
|
"manufacturer_id": 2409,
|
|
505
505
|
},
|
|
506
506
|
"+": {
|
|
507
|
-
"modelName": SwitchbotModel.
|
|
508
|
-
"modelFriendlyName": "Air Purifier",
|
|
507
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_JP,
|
|
508
|
+
"modelFriendlyName": "Air Purifier JP",
|
|
509
509
|
"func": process_air_purifier,
|
|
510
510
|
"manufacturer_id": 2409,
|
|
511
511
|
},
|
|
512
512
|
b"\x0b": {
|
|
513
|
-
"modelName": SwitchbotModel.
|
|
514
|
-
"modelFriendlyName": "Air Purifier",
|
|
513
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_JP,
|
|
514
|
+
"modelFriendlyName": "Air Purifier JP",
|
|
515
515
|
"func": process_air_purifier,
|
|
516
516
|
"manufacturer_id": 2409,
|
|
517
517
|
},
|
|
518
518
|
"7": {
|
|
519
|
-
"modelName": SwitchbotModel.
|
|
520
|
-
"modelFriendlyName": "Air Purifier Table",
|
|
519
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_TABLE_US,
|
|
520
|
+
"modelFriendlyName": "Air Purifier Table US",
|
|
521
521
|
"func": process_air_purifier,
|
|
522
522
|
"manufacturer_id": 2409,
|
|
523
523
|
},
|
|
524
524
|
b"\x17": {
|
|
525
|
-
"modelName": SwitchbotModel.
|
|
526
|
-
"modelFriendlyName": "Air Purifier Table",
|
|
525
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_TABLE_US,
|
|
526
|
+
"modelFriendlyName": "Air Purifier Table US",
|
|
527
527
|
"func": process_air_purifier,
|
|
528
528
|
"manufacturer_id": 2409,
|
|
529
529
|
},
|
|
530
530
|
"8": {
|
|
531
|
-
"modelName": SwitchbotModel.
|
|
532
|
-
"modelFriendlyName": "Air Purifier Table",
|
|
531
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_TABLE_JP,
|
|
532
|
+
"modelFriendlyName": "Air Purifier Table JP",
|
|
533
533
|
"func": process_air_purifier,
|
|
534
534
|
"manufacturer_id": 2409,
|
|
535
535
|
},
|
|
536
536
|
b"\x18": {
|
|
537
|
-
"modelName": SwitchbotModel.
|
|
538
|
-
"modelFriendlyName": "Air Purifier Table",
|
|
537
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_TABLE_JP,
|
|
538
|
+
"modelFriendlyName": "Air Purifier Table JP",
|
|
539
539
|
"func": process_air_purifier,
|
|
540
540
|
"manufacturer_id": 2409,
|
|
541
541
|
},
|
|
@@ -755,6 +755,42 @@ SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
|
|
|
755
755
|
"func": process_keypad_vision_pro,
|
|
756
756
|
"manufacturer_id": 2409,
|
|
757
757
|
},
|
|
758
|
+
b"\x00\x11\x69\x09": {
|
|
759
|
+
"modelName": SwitchbotModel.LOCK_VISION_PRO,
|
|
760
|
+
"modelFriendlyName": "Lock Vision Pro",
|
|
761
|
+
"func": process_lock2,
|
|
762
|
+
"manufacturer_id": 2409,
|
|
763
|
+
},
|
|
764
|
+
b"\x01\x11\x69\x09": {
|
|
765
|
+
"modelName": SwitchbotModel.LOCK_VISION_PRO,
|
|
766
|
+
"modelFriendlyName": "Lock Vision Pro",
|
|
767
|
+
"func": process_lock2,
|
|
768
|
+
"manufacturer_id": 2409,
|
|
769
|
+
},
|
|
770
|
+
b"\x00\x11\x69\x08": {
|
|
771
|
+
"modelName": SwitchbotModel.LOCK_VISION,
|
|
772
|
+
"modelFriendlyName": "Lock Vision",
|
|
773
|
+
"func": process_locklite,
|
|
774
|
+
"manufacturer_id": 2409,
|
|
775
|
+
},
|
|
776
|
+
b"\x01\x11\x69\x08": {
|
|
777
|
+
"modelName": SwitchbotModel.LOCK_VISION,
|
|
778
|
+
"modelFriendlyName": "Lock Vision",
|
|
779
|
+
"func": process_locklite,
|
|
780
|
+
"manufacturer_id": 2409,
|
|
781
|
+
},
|
|
782
|
+
b"\x00\x10\xff\x90": {
|
|
783
|
+
"modelName": SwitchbotModel.LOCK_PRO_WIFI,
|
|
784
|
+
"modelFriendlyName": "Lock Pro Wifi",
|
|
785
|
+
"func": process_wolock_pro,
|
|
786
|
+
"manufacturer_id": 2409,
|
|
787
|
+
},
|
|
788
|
+
b"\x01\x10\xff\x90": {
|
|
789
|
+
"modelName": SwitchbotModel.LOCK_PRO_WIFI,
|
|
790
|
+
"modelFriendlyName": "Lock Pro Wifi",
|
|
791
|
+
"func": process_wolock_pro,
|
|
792
|
+
"manufacturer_id": 2409,
|
|
793
|
+
},
|
|
758
794
|
}
|
|
759
795
|
|
|
760
796
|
_SWITCHBOT_MODEL_TO_CHAR: defaultdict[SwitchbotModel, list[str | bytes]] = defaultdict(
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from ..enum import StrEnum
|
|
6
|
-
from .air_purifier import AirPurifierMode
|
|
6
|
+
from .air_purifier import AirPurifierMode, AirQualityLevel
|
|
7
7
|
from .climate import ClimateAction, ClimateMode, SmartThermostatRadiatorMode
|
|
8
8
|
from .evaporative_humidifier import (
|
|
9
9
|
HumidifierAction,
|
|
@@ -85,8 +85,10 @@ class SwitchbotModel(StrEnum):
|
|
|
85
85
|
K10_VACUUM = "K10+ Vacuum"
|
|
86
86
|
K10_PRO_VACUUM = "K10+ Pro Vacuum"
|
|
87
87
|
K10_PRO_COMBO_VACUUM = "K10+ Pro Combo Vacuum"
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
AIR_PURIFIER_US = "Air Purifier US"
|
|
89
|
+
AIR_PURIFIER_JP = "Air Purifier JP"
|
|
90
|
+
AIR_PURIFIER_TABLE_US = "Air Purifier Table US"
|
|
91
|
+
AIR_PURIFIER_TABLE_JP = "Air Purifier Table JP"
|
|
90
92
|
HUB3 = "Hub3"
|
|
91
93
|
LOCK_ULTRA = "Lock Ultra"
|
|
92
94
|
LOCK_LITE = "Lock Lite"
|
|
@@ -105,6 +107,9 @@ class SwitchbotModel(StrEnum):
|
|
|
105
107
|
ART_FRAME = "Art Frame"
|
|
106
108
|
KEYPAD_VISION = "Keypad Vision"
|
|
107
109
|
KEYPAD_VISION_PRO = "Keypad Vision Pro"
|
|
110
|
+
LOCK_VISION_PRO = "Lock Vision Pro"
|
|
111
|
+
LOCK_VISION = "Lock Vision"
|
|
112
|
+
LOCK_PRO_WIFI = "Lock Pro Wifi"
|
|
108
113
|
|
|
109
114
|
|
|
110
115
|
__all__ = [
|
|
@@ -112,6 +117,7 @@ __all__ = [
|
|
|
112
117
|
"DEFAULT_RETRY_TIMEOUT",
|
|
113
118
|
"DEFAULT_SCAN_TIMEOUT",
|
|
114
119
|
"AirPurifierMode",
|
|
120
|
+
"AirQualityLevel",
|
|
115
121
|
"BulbColorMode",
|
|
116
122
|
"CeilingLightColorMode",
|
|
117
123
|
"ClimateAction",
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""Library to handle connection with Switchbot."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any, ClassVar
|
|
7
|
+
|
|
8
|
+
from bleak.backends.device import BLEDevice
|
|
9
|
+
|
|
10
|
+
from ..adv_parsers.air_purifier import get_air_purifier_mode
|
|
11
|
+
from ..const import SwitchbotModel
|
|
12
|
+
from ..const.air_purifier import AirPurifierMode, AirQualityLevel
|
|
13
|
+
from ..const.light import ColorMode
|
|
14
|
+
from ..helpers import _UNPACK_UINT16_BE
|
|
15
|
+
from .base_light import SwitchbotSequenceBaseLight
|
|
16
|
+
from .device import (
|
|
17
|
+
SwitchbotEncryptedDevice,
|
|
18
|
+
SwitchbotOperationError,
|
|
19
|
+
update_after_operation,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
_LOGGER = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
COMMAND_HEAD = "570f4c"
|
|
26
|
+
COMMAND_SET_MODE = {
|
|
27
|
+
AirPurifierMode.LEVEL_1.name.lower(): f"{COMMAND_HEAD}01010100",
|
|
28
|
+
AirPurifierMode.LEVEL_2.name.lower(): f"{COMMAND_HEAD}01010132",
|
|
29
|
+
AirPurifierMode.LEVEL_3.name.lower(): f"{COMMAND_HEAD}01010164",
|
|
30
|
+
AirPurifierMode.AUTO.name.lower(): f"{COMMAND_HEAD}01010200",
|
|
31
|
+
AirPurifierMode.SLEEP.name.lower(): f"{COMMAND_HEAD}01010300",
|
|
32
|
+
AirPurifierMode.PET.name.lower(): f"{COMMAND_HEAD}01010400",
|
|
33
|
+
}
|
|
34
|
+
DEVICE_GET_BASIC_SETTINGS_KEY = "570f4d81"
|
|
35
|
+
COMMAND_SET_PERCENTAGE = f"{COMMAND_HEAD}02{{percentage:02x}}"
|
|
36
|
+
READ_LED_SETTINGS_COMMAND = "570f4d05"
|
|
37
|
+
READ_LED_STATUS_COMMAND = "570f4d07"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SwitchbotAirPurifier(SwitchbotSequenceBaseLight, SwitchbotEncryptedDevice):
|
|
41
|
+
"""Representation of a Switchbot Air Purifier."""
|
|
42
|
+
|
|
43
|
+
_turn_on_command = f"{COMMAND_HEAD}010100"
|
|
44
|
+
_turn_off_command = f"{COMMAND_HEAD}010000"
|
|
45
|
+
_open_child_lock_command = f"{COMMAND_HEAD}0301"
|
|
46
|
+
_close_child_lock_command = f"{COMMAND_HEAD}0300"
|
|
47
|
+
_open_wireless_charging_command = f"{COMMAND_HEAD}0d01"
|
|
48
|
+
_close_wireless_charging_command = f"{COMMAND_HEAD}0d00"
|
|
49
|
+
_open_light_sensitive_switch_command = f"{COMMAND_HEAD}0702"
|
|
50
|
+
_turn_led_on_command = f"{COMMAND_HEAD}0701"
|
|
51
|
+
_turn_led_off_command = f"{COMMAND_HEAD}0700"
|
|
52
|
+
_set_rgb_command = _set_brightness_command = f"{COMMAND_HEAD}0501{{}}"
|
|
53
|
+
_get_basic_info_command = [
|
|
54
|
+
DEVICE_GET_BASIC_SETTINGS_KEY,
|
|
55
|
+
READ_LED_SETTINGS_COMMAND,
|
|
56
|
+
READ_LED_STATUS_COMMAND,
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
_PM25_MODELS: ClassVar[frozenset[SwitchbotModel]] = frozenset(
|
|
60
|
+
{
|
|
61
|
+
SwitchbotModel.AIR_PURIFIER_US,
|
|
62
|
+
SwitchbotModel.AIR_PURIFIER_TABLE_US,
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
_LEVEL_MODES: ClassVar[frozenset[str]] = frozenset(
|
|
67
|
+
{
|
|
68
|
+
AirPurifierMode.LEVEL_1.name.lower(),
|
|
69
|
+
AirPurifierMode.LEVEL_2.name.lower(),
|
|
70
|
+
AirPurifierMode.LEVEL_3.name.lower(),
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
_WIRELESS_MODELS: ClassVar[frozenset[SwitchbotModel]] = frozenset(
|
|
75
|
+
{
|
|
76
|
+
SwitchbotModel.AIR_PURIFIER_TABLE_US,
|
|
77
|
+
SwitchbotModel.AIR_PURIFIER_TABLE_JP,
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
device: BLEDevice,
|
|
84
|
+
key_id: str,
|
|
85
|
+
encryption_key: str,
|
|
86
|
+
interface: int = 0,
|
|
87
|
+
model: SwitchbotModel = SwitchbotModel.AIR_PURIFIER_US,
|
|
88
|
+
**kwargs: Any,
|
|
89
|
+
) -> None:
|
|
90
|
+
super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
async def verify_encryption_key(
|
|
94
|
+
cls,
|
|
95
|
+
device: BLEDevice,
|
|
96
|
+
key_id: str,
|
|
97
|
+
encryption_key: str,
|
|
98
|
+
model: SwitchbotModel = SwitchbotModel.AIR_PURIFIER_US,
|
|
99
|
+
**kwargs: Any,
|
|
100
|
+
) -> bool:
|
|
101
|
+
return await super().verify_encryption_key(
|
|
102
|
+
device, key_id, encryption_key, model, **kwargs
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def color_modes(self) -> set[ColorMode]:
|
|
107
|
+
"""Return the supported color modes."""
|
|
108
|
+
return {ColorMode.RGB}
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def color_mode(self) -> ColorMode:
|
|
112
|
+
"""Return the current color mode."""
|
|
113
|
+
return ColorMode.RGB
|
|
114
|
+
|
|
115
|
+
async def get_basic_info(self) -> dict[str, Any] | None:
|
|
116
|
+
"""Get device basic settings."""
|
|
117
|
+
if not (
|
|
118
|
+
res := await self._get_basic_info_by_multi_commands(
|
|
119
|
+
self._get_basic_info_command
|
|
120
|
+
)
|
|
121
|
+
):
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
_data, led_settings, led_status = res[0], res[1], res[2]
|
|
125
|
+
|
|
126
|
+
_LOGGER.debug(
|
|
127
|
+
"%s %s basic info %s", self._model, self._device.address, _data.hex()
|
|
128
|
+
)
|
|
129
|
+
_LOGGER.debug(
|
|
130
|
+
"%s %s led settings %s",
|
|
131
|
+
self._model,
|
|
132
|
+
self._device.address,
|
|
133
|
+
led_settings.hex(),
|
|
134
|
+
)
|
|
135
|
+
_LOGGER.debug(
|
|
136
|
+
"%s %s led_status %s", self._model, self._device.address, led_status.hex()
|
|
137
|
+
)
|
|
138
|
+
isOn = bool(_data[2] & 0b10000000)
|
|
139
|
+
wireless_charging = bool(_data[2] & 0b01000000)
|
|
140
|
+
version_info = (_data[2] & 0b00110000) >> 4
|
|
141
|
+
_mode = _data[2] & 0b00000111
|
|
142
|
+
isAqiValid = bool(_data[3] & 0b00000100)
|
|
143
|
+
child_lock = bool(_data[3] & 0b00000010)
|
|
144
|
+
_aqi_level = (_data[4] & 0b00000110) >> 1
|
|
145
|
+
aqi_level = AirQualityLevel(_aqi_level).name.lower()
|
|
146
|
+
speed = _data[6] & 0b01111111
|
|
147
|
+
pm25 = _UNPACK_UINT16_BE(_data, 12)[0] & 0xFFF
|
|
148
|
+
firmware = _data[15] / 10.0
|
|
149
|
+
mode = get_air_purifier_mode(_mode, speed)
|
|
150
|
+
self._state["r"] = led_settings[2]
|
|
151
|
+
self._state["g"] = led_settings[3]
|
|
152
|
+
self._state["b"] = led_settings[4]
|
|
153
|
+
brightness = led_settings[5]
|
|
154
|
+
light_sensitive = bool(led_status[1] & 0x02)
|
|
155
|
+
|
|
156
|
+
data = {
|
|
157
|
+
"isOn": isOn,
|
|
158
|
+
"version_info": version_info,
|
|
159
|
+
"mode": mode,
|
|
160
|
+
"isAqiValid": isAqiValid,
|
|
161
|
+
"child_lock": child_lock,
|
|
162
|
+
"aqi_level": aqi_level,
|
|
163
|
+
"speed": speed,
|
|
164
|
+
"firmware": firmware,
|
|
165
|
+
"brightness": brightness,
|
|
166
|
+
"light_sensitive": light_sensitive,
|
|
167
|
+
}
|
|
168
|
+
if self._model in self._WIRELESS_MODELS:
|
|
169
|
+
data["wireless_charging"] = wireless_charging
|
|
170
|
+
|
|
171
|
+
if self._model in self._PM25_MODELS:
|
|
172
|
+
return data | {"pm25": pm25}
|
|
173
|
+
return data
|
|
174
|
+
|
|
175
|
+
@update_after_operation
|
|
176
|
+
async def set_preset_mode(self, preset_mode: str) -> bool:
|
|
177
|
+
"""Send command to set air purifier preset_mode."""
|
|
178
|
+
result = await self._send_command(COMMAND_SET_MODE[preset_mode])
|
|
179
|
+
return self._check_command_result(result, 0, {1})
|
|
180
|
+
|
|
181
|
+
@update_after_operation
|
|
182
|
+
async def set_percentage(self, percentage: int) -> bool:
|
|
183
|
+
"""Set percentage."""
|
|
184
|
+
if not 0 <= percentage <= 100:
|
|
185
|
+
raise ValueError("Percentage must be between 0 and 100")
|
|
186
|
+
self._validate_current_mode()
|
|
187
|
+
|
|
188
|
+
result = await self._send_command(
|
|
189
|
+
COMMAND_SET_PERCENTAGE.format(percentage=percentage)
|
|
190
|
+
)
|
|
191
|
+
return self._check_command_result(result, 0, {1})
|
|
192
|
+
|
|
193
|
+
def _validate_current_mode(self) -> None:
|
|
194
|
+
"""Validate current mode for setting percentage."""
|
|
195
|
+
current_mode = self.get_current_mode()
|
|
196
|
+
if current_mode not in self._LEVEL_MODES:
|
|
197
|
+
raise ValueError("Percentage can only be set in LEVEL modes.")
|
|
198
|
+
|
|
199
|
+
@update_after_operation
|
|
200
|
+
async def set_brightness(self, brightness: int) -> bool:
|
|
201
|
+
"""Set brightness."""
|
|
202
|
+
self._validate_brightness(brightness)
|
|
203
|
+
r, g, b = (
|
|
204
|
+
self._state.get("r", 0),
|
|
205
|
+
self._state.get("g", 0),
|
|
206
|
+
self._state.get("b", 0),
|
|
207
|
+
)
|
|
208
|
+
hex_data = f"{r:02X}{g:02X}{b:02X}{brightness:02X}"
|
|
209
|
+
result = await self._send_command(self._set_brightness_command.format(hex_data))
|
|
210
|
+
return self._check_command_result(result, 0, {1})
|
|
211
|
+
|
|
212
|
+
@update_after_operation
|
|
213
|
+
async def set_rgb(self, brightness: int, r: int, g: int, b: int) -> bool:
|
|
214
|
+
"""
|
|
215
|
+
Set rgb.
|
|
216
|
+
|
|
217
|
+
Note: byte order is reversed from base class (RGB+brightness
|
|
218
|
+
instead of brightness+RGB).
|
|
219
|
+
"""
|
|
220
|
+
self._validate_brightness(brightness)
|
|
221
|
+
self._validate_rgb(r, g, b)
|
|
222
|
+
hex_data = f"{r:02X}{g:02X}{b:02X}{brightness:02X}"
|
|
223
|
+
result = await self._send_command(self._set_rgb_command.format(hex_data))
|
|
224
|
+
return self._check_command_result(result, 0, {1})
|
|
225
|
+
|
|
226
|
+
@update_after_operation
|
|
227
|
+
async def turn_led_on(self) -> bool:
|
|
228
|
+
"""Turn on LED."""
|
|
229
|
+
result = await self._send_command(self._turn_led_on_command)
|
|
230
|
+
return self._check_command_result(result, 0, {1})
|
|
231
|
+
|
|
232
|
+
@update_after_operation
|
|
233
|
+
async def turn_led_off(self) -> bool:
|
|
234
|
+
"""Turn off LED."""
|
|
235
|
+
result = await self._send_command(self._turn_led_off_command)
|
|
236
|
+
return self._check_command_result(result, 0, {1})
|
|
237
|
+
|
|
238
|
+
@update_after_operation
|
|
239
|
+
async def open_light_sensitive_switch(self) -> bool:
|
|
240
|
+
"""
|
|
241
|
+
Open the light sensitive switch.
|
|
242
|
+
|
|
243
|
+
This will allow the LED to automatically adjust brightness based on ambient light.
|
|
244
|
+
The LED will turn on in dark environments and turn off in bright environments.
|
|
245
|
+
"""
|
|
246
|
+
result = await self._send_command(self._open_light_sensitive_switch_command)
|
|
247
|
+
return self._check_command_result(result, 0, {1})
|
|
248
|
+
|
|
249
|
+
@update_after_operation
|
|
250
|
+
async def close_light_sensitive_switch(self) -> bool:
|
|
251
|
+
"""
|
|
252
|
+
Close the light sensitive switch.
|
|
253
|
+
|
|
254
|
+
Since the current protocol does not support obtaining the LED status,
|
|
255
|
+
sending an on or off command will turn off the light sensitive switch.
|
|
256
|
+
"""
|
|
257
|
+
result = await self._send_command(self._turn_led_on_command)
|
|
258
|
+
return self._check_command_result(result, 0, {1})
|
|
259
|
+
|
|
260
|
+
def _check_wireless_charging_supported(self) -> None:
|
|
261
|
+
if self._model not in self._WIRELESS_MODELS:
|
|
262
|
+
raise SwitchbotOperationError(
|
|
263
|
+
"Wireless charging is only available on table versions"
|
|
264
|
+
f" (current model={self._model})"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
@update_after_operation
|
|
268
|
+
async def open_wireless_charging(self) -> bool:
|
|
269
|
+
"""Enable the wireless charging pad (table models only)."""
|
|
270
|
+
self._check_wireless_charging_supported()
|
|
271
|
+
result = await self._send_command(self._open_wireless_charging_command)
|
|
272
|
+
return self._check_command_result(result, 0, {1})
|
|
273
|
+
|
|
274
|
+
@update_after_operation
|
|
275
|
+
async def close_wireless_charging(self) -> bool:
|
|
276
|
+
"""Disable the wireless charging pad (table models only)."""
|
|
277
|
+
self._check_wireless_charging_supported()
|
|
278
|
+
result = await self._send_command(self._close_wireless_charging_command)
|
|
279
|
+
return self._check_command_result(result, 0, {1})
|
|
280
|
+
|
|
281
|
+
def is_on(self) -> bool | None:
|
|
282
|
+
"""Return air purifier state from cache."""
|
|
283
|
+
return self._get_adv_value("isOn")
|
|
284
|
+
|
|
285
|
+
def get_current_aqi_level(self) -> Any:
|
|
286
|
+
"""Return cached aqi level."""
|
|
287
|
+
return self._get_adv_value("aqi_level")
|
|
288
|
+
|
|
289
|
+
def get_current_pm25(self) -> Any:
|
|
290
|
+
"""Return cached pm25."""
|
|
291
|
+
return self._get_adv_value("pm25")
|
|
292
|
+
|
|
293
|
+
def get_current_mode(self) -> Any:
|
|
294
|
+
"""Return cached mode."""
|
|
295
|
+
return self._get_adv_value("mode")
|
|
296
|
+
|
|
297
|
+
def is_child_lock_on(self) -> bool | None:
|
|
298
|
+
"""Return child lock state from cache."""
|
|
299
|
+
return self._get_adv_value("child_lock")
|
|
300
|
+
|
|
301
|
+
def is_wireless_charging_on(self) -> bool | None:
|
|
302
|
+
"""Return wireless charging state from cache."""
|
|
303
|
+
return self._get_adv_value("wireless_charging")
|
|
304
|
+
|
|
305
|
+
def get_current_percentage(self) -> int | None:
|
|
306
|
+
"""Return cached percentage."""
|
|
307
|
+
return self._get_adv_value("speed")
|
|
308
|
+
|
|
309
|
+
def is_light_sensitive_on(self) -> bool | None:
|
|
310
|
+
"""Return light sensitive state from cache."""
|
|
311
|
+
return self._get_adv_value("light_sensitive")
|
|
@@ -75,10 +75,29 @@ class SwitchbotBaseLight(SwitchbotDevice):
|
|
|
75
75
|
"""Return the current effect."""
|
|
76
76
|
return self._get_adv_value("effect")
|
|
77
77
|
|
|
78
|
+
@staticmethod
|
|
79
|
+
def _validate_brightness(brightness: int) -> None:
|
|
80
|
+
if not 0 <= brightness <= 100:
|
|
81
|
+
raise ValueError("Brightness must be between 0 and 100")
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def _validate_rgb(r: int, g: int, b: int) -> None:
|
|
85
|
+
if not 0 <= r <= 255:
|
|
86
|
+
raise ValueError("r must be between 0 and 255")
|
|
87
|
+
if not 0 <= g <= 255:
|
|
88
|
+
raise ValueError("g must be between 0 and 255")
|
|
89
|
+
if not 0 <= b <= 255:
|
|
90
|
+
raise ValueError("b must be between 0 and 255")
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def _validate_color_temp(color_temp: int) -> None:
|
|
94
|
+
if not 2700 <= color_temp <= 6500:
|
|
95
|
+
raise ValueError("Color Temp must be between 2700 and 6500")
|
|
96
|
+
|
|
78
97
|
@update_after_operation
|
|
79
98
|
async def set_brightness(self, brightness: int) -> bool:
|
|
80
99
|
"""Set brightness."""
|
|
81
|
-
|
|
100
|
+
self._validate_brightness(brightness)
|
|
82
101
|
hex_brightness = f"{brightness:02X}"
|
|
83
102
|
self._check_function_support(self._set_brightness_command)
|
|
84
103
|
result = await self._send_command(
|
|
@@ -89,8 +108,8 @@ class SwitchbotBaseLight(SwitchbotDevice):
|
|
|
89
108
|
@update_after_operation
|
|
90
109
|
async def set_color_temp(self, brightness: int, color_temp: int) -> bool:
|
|
91
110
|
"""Set color temp."""
|
|
92
|
-
|
|
93
|
-
|
|
111
|
+
self._validate_brightness(brightness)
|
|
112
|
+
self._validate_color_temp(color_temp)
|
|
94
113
|
hex_data = f"{brightness:02X}{color_temp:04X}"
|
|
95
114
|
self._check_function_support(self._set_color_temp_command)
|
|
96
115
|
result = await self._send_command(self._set_color_temp_command.format(hex_data))
|
|
@@ -99,10 +118,8 @@ class SwitchbotBaseLight(SwitchbotDevice):
|
|
|
99
118
|
@update_after_operation
|
|
100
119
|
async def set_rgb(self, brightness: int, r: int, g: int, b: int) -> bool:
|
|
101
120
|
"""Set rgb."""
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
assert 0 <= g <= 255, "g must be between 0 and 255"
|
|
105
|
-
assert 0 <= b <= 255, "b must be between 0 and 255"
|
|
121
|
+
self._validate_brightness(brightness)
|
|
122
|
+
self._validate_rgb(r, g, b)
|
|
106
123
|
self._check_function_support(self._set_rgb_command)
|
|
107
124
|
hex_data = f"{brightness:02X}{r:02X}{g:02X}{b:02X}"
|
|
108
125
|
result = await self._send_command(self._set_rgb_command.format(hex_data))
|
|
@@ -141,7 +158,8 @@ class SwitchbotBaseLight(SwitchbotDevice):
|
|
|
141
158
|
"""Get device basic settings by sending multiple commands."""
|
|
142
159
|
results = []
|
|
143
160
|
for command in commands:
|
|
144
|
-
|
|
161
|
+
result = await self._send_command(command)
|
|
162
|
+
if not self._check_command_result(result, 0, {1}):
|
|
145
163
|
return None
|
|
146
164
|
results.append(result)
|
|
147
165
|
return results
|
|
@@ -78,10 +78,10 @@ API_MODEL_TO_ENUM: dict[str, SwitchbotModel] = {
|
|
|
78
78
|
"WoFan2": SwitchbotModel.CIRCULATOR_FAN,
|
|
79
79
|
"WoHub2": SwitchbotModel.HUB2,
|
|
80
80
|
"WoRollerShade": SwitchbotModel.ROLLER_SHADE,
|
|
81
|
-
"WoAirPurifierJP": SwitchbotModel.
|
|
82
|
-
"WoAirPurifierUS": SwitchbotModel.
|
|
83
|
-
"WoAirPurifierJPPro": SwitchbotModel.
|
|
84
|
-
"WoAirPurifierUSPro": SwitchbotModel.
|
|
81
|
+
"WoAirPurifierJP": SwitchbotModel.AIR_PURIFIER_JP,
|
|
82
|
+
"WoAirPurifierUS": SwitchbotModel.AIR_PURIFIER_US,
|
|
83
|
+
"WoAirPurifierJPPro": SwitchbotModel.AIR_PURIFIER_TABLE_JP,
|
|
84
|
+
"WoAirPurifierUSPro": SwitchbotModel.AIR_PURIFIER_TABLE_US,
|
|
85
85
|
"WoSweeperMini": SwitchbotModel.K10_VACUUM,
|
|
86
86
|
"WoSweeperMiniPro": SwitchbotModel.K10_PRO_VACUUM,
|
|
87
87
|
"91AgWZ1n": SwitchbotModel.K10_PRO_COMBO_VACUUM,
|
|
@@ -106,6 +106,9 @@ API_MODEL_TO_ENUM: dict[str, SwitchbotModel] = {
|
|
|
106
106
|
"W1128000": SwitchbotModel.SMART_THERMOSTAT_RADIATOR,
|
|
107
107
|
"W1111000": SwitchbotModel.CLIMATE_PANEL,
|
|
108
108
|
"W1130000": SwitchbotModel.ART_FRAME,
|
|
109
|
+
"W1141001": SwitchbotModel.LOCK_VISION_PRO,
|
|
110
|
+
"W1141000": SwitchbotModel.LOCK_VISION,
|
|
111
|
+
"W1114000": SwitchbotModel.LOCK_PRO_WIFI,
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
REQ_HEADER = "570f"
|
|
@@ -213,6 +216,8 @@ class SwitchbotBaseDevice:
|
|
|
213
216
|
_open_command: str | None = None
|
|
214
217
|
_close_command: str | None = None
|
|
215
218
|
_press_command: str | None = None
|
|
219
|
+
_open_child_lock_command: str | None = None
|
|
220
|
+
_close_child_lock_command: str | None = None
|
|
216
221
|
|
|
217
222
|
def __init__(
|
|
218
223
|
self,
|
|
@@ -932,6 +937,20 @@ class SwitchbotBaseDevice:
|
|
|
932
937
|
result = await self._send_command(self._press_command)
|
|
933
938
|
return self._check_command_result(result, 0, {1})
|
|
934
939
|
|
|
940
|
+
@update_after_operation
|
|
941
|
+
async def open_child_lock(self) -> bool:
|
|
942
|
+
"""Open the child lock."""
|
|
943
|
+
self._check_function_support(self._open_child_lock_command)
|
|
944
|
+
result = await self._send_command(self._open_child_lock_command)
|
|
945
|
+
return self._check_command_result(result, 0, {1})
|
|
946
|
+
|
|
947
|
+
@update_after_operation
|
|
948
|
+
async def close_child_lock(self) -> bool:
|
|
949
|
+
"""Close the child lock."""
|
|
950
|
+
self._check_function_support(self._close_child_lock_command)
|
|
951
|
+
result = await self._send_command(self._close_child_lock_command)
|
|
952
|
+
return self._check_command_result(result, 0, {1})
|
|
953
|
+
|
|
935
954
|
|
|
936
955
|
class SwitchbotDevice(SwitchbotBaseDevice):
|
|
937
956
|
"""
|