PySwitchbot 1.1.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.1.0 → pyswitchbot-2.0.0}/PKG-INFO +1 -1
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/setup.py +1 -1
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/__init__.py +2 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parser.py +16 -16
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/__init__.py +6 -3
- pyswitchbot-2.0.0/switchbot/devices/air_purifier.py +311 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/base_light.py +26 -8
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/device.py +20 -4
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_adv_parser.py +17 -15
- pyswitchbot-2.0.0/tests/test_air_purifier.py +484 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_bulb.py +8 -18
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_ceiling_light.py +8 -18
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_lock.py +1 -1
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_strip_light.py +8 -16
- pyswitchbot-1.1.0/switchbot/devices/air_purifier.py +0 -131
- pyswitchbot-1.1.0/tests/test_air_purifier.py +0 -231
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/LICENSE +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/MANIFEST.in +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/SOURCES.txt +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/README.md +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/pyproject.toml +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/setup.cfg +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/air_purifier.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/art_frame.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/climate_panel.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/fan.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/hub2.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/hub3.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/hubmini_matter.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/humidifier.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/keypad.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/keypad_vision.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/leak.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/light_strip.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/lock.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/meter.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/plug.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/presence_sensor.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/relay_switch.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/remote.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/roller_shade.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/smart_thermostat_radiator.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/adv_parsers/vacuum.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/api_config.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/air_purifier.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/climate.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/evaporative_humidifier.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/fan.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/hub2.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/hub3.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/light.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/lock.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/const/presence_sensor.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/art_frame.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/bulb.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/ceiling_light.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/evaporative_humidifier.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/fan.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/keypad.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/keypad_vision.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/light_strip.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/lock.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/meter_pro.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/relay_switch.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/roller_shade.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/smart_thermostat_radiator.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/devices/vacuum.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/discovery.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/enum.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/helpers.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/models.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/switchbot/utils.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_art_frame.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_base_cover.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_colormode_imports.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_curtain.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_device.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_discovery_callback.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_encrypted_device.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_evaporative_humidifier.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_fan.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_helpers.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_hub2.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_hub3.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_keypad_vision.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_meter_pro.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_relay_switch.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_roller_shade.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_smart_thermostat_radiator.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_utils.py +0 -0
- {pyswitchbot-1.1.0 → pyswitchbot-2.0.0}/tests/test_vacuum.py +0 -0
|
@@ -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
|
},
|
|
@@ -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"
|
|
@@ -115,6 +117,7 @@ __all__ = [
|
|
|
115
117
|
"DEFAULT_RETRY_TIMEOUT",
|
|
116
118
|
"DEFAULT_SCAN_TIMEOUT",
|
|
117
119
|
"AirPurifierMode",
|
|
120
|
+
"AirQualityLevel",
|
|
118
121
|
"BulbColorMode",
|
|
119
122
|
"CeilingLightColorMode",
|
|
120
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,
|
|
@@ -216,6 +216,8 @@ class SwitchbotBaseDevice:
|
|
|
216
216
|
_open_command: str | None = None
|
|
217
217
|
_close_command: str | None = None
|
|
218
218
|
_press_command: str | None = None
|
|
219
|
+
_open_child_lock_command: str | None = None
|
|
220
|
+
_close_child_lock_command: str | None = None
|
|
219
221
|
|
|
220
222
|
def __init__(
|
|
221
223
|
self,
|
|
@@ -935,6 +937,20 @@ class SwitchbotBaseDevice:
|
|
|
935
937
|
result = await self._send_command(self._press_command)
|
|
936
938
|
return self._check_command_result(result, 0, {1})
|
|
937
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
|
+
|
|
938
954
|
|
|
939
955
|
class SwitchbotDevice(SwitchbotBaseDevice):
|
|
940
956
|
"""
|
|
@@ -2364,8 +2364,8 @@ def test_s10_with_empty_data() -> None:
|
|
|
2364
2364
|
"sequence_number": 161,
|
|
2365
2365
|
},
|
|
2366
2366
|
"7",
|
|
2367
|
-
"Air Purifier Table",
|
|
2368
|
-
SwitchbotModel.
|
|
2367
|
+
"Air Purifier Table US",
|
|
2368
|
+
SwitchbotModel.AIR_PURIFIER_TABLE_US,
|
|
2369
2369
|
),
|
|
2370
2370
|
AdvTestCase(
|
|
2371
2371
|
b'\xcc\x8d\xa2\xa7\x92>\t"\x80\x000\x00\x0f\x00\x00',
|
|
@@ -2382,8 +2382,8 @@ def test_s10_with_empty_data() -> None:
|
|
|
2382
2382
|
"sequence_number": 9,
|
|
2383
2383
|
},
|
|
2384
2384
|
"*",
|
|
2385
|
-
"Air Purifier",
|
|
2386
|
-
SwitchbotModel.
|
|
2385
|
+
"Air Purifier US",
|
|
2386
|
+
SwitchbotModel.AIR_PURIFIER_US,
|
|
2387
2387
|
),
|
|
2388
2388
|
AdvTestCase(
|
|
2389
2389
|
b"\xcc\x8d\xa2\xa7\xe4\xa6\x0b\x83\x88d\x00\xea`\x00\x00",
|
|
@@ -2400,8 +2400,8 @@ def test_s10_with_empty_data() -> None:
|
|
|
2400
2400
|
"sequence_number": 11,
|
|
2401
2401
|
},
|
|
2402
2402
|
"+",
|
|
2403
|
-
"Air Purifier",
|
|
2404
|
-
SwitchbotModel.
|
|
2403
|
+
"Air Purifier JP",
|
|
2404
|
+
SwitchbotModel.AIR_PURIFIER_JP,
|
|
2405
2405
|
),
|
|
2406
2406
|
AdvTestCase(
|
|
2407
2407
|
b"\xcc\x8d\xa2\xa7\xc1\xae\x9b\x81\x8c\xb2\x00\x01\x94\x00\x00",
|
|
@@ -2418,8 +2418,8 @@ def test_s10_with_empty_data() -> None:
|
|
|
2418
2418
|
"sequence_number": 155,
|
|
2419
2419
|
},
|
|
2420
2420
|
"8",
|
|
2421
|
-
"Air Purifier Table",
|
|
2422
|
-
SwitchbotModel.
|
|
2421
|
+
"Air Purifier Table JP",
|
|
2422
|
+
SwitchbotModel.AIR_PURIFIER_TABLE_JP,
|
|
2423
2423
|
),
|
|
2424
2424
|
AdvTestCase(
|
|
2425
2425
|
b"\xcc\x8d\xa2\xa7\xc1\xae\x9e\xa1\x8c\x800\x01\x95\x00\x00",
|
|
@@ -2436,8 +2436,8 @@ def test_s10_with_empty_data() -> None:
|
|
|
2436
2436
|
"sequence_number": 158,
|
|
2437
2437
|
},
|
|
2438
2438
|
"8",
|
|
2439
|
-
"Air Purifier Table",
|
|
2440
|
-
SwitchbotModel.
|
|
2439
|
+
"Air Purifier Table JP",
|
|
2440
|
+
SwitchbotModel.AIR_PURIFIER_TABLE_JP,
|
|
2441
2441
|
),
|
|
2442
2442
|
AdvTestCase(
|
|
2443
2443
|
b"\xcc\x8d\xa2\xa7\xc1\xae\x9e\x05\x8c\x800\x01\x95\x00\x00",
|
|
@@ -2454,8 +2454,8 @@ def test_s10_with_empty_data() -> None:
|
|
|
2454
2454
|
"sequence_number": 158,
|
|
2455
2455
|
},
|
|
2456
2456
|
"8",
|
|
2457
|
-
"Air Purifier Table",
|
|
2458
|
-
SwitchbotModel.
|
|
2457
|
+
"Air Purifier Table JP",
|
|
2458
|
+
SwitchbotModel.AIR_PURIFIER_TABLE_JP,
|
|
2459
2459
|
),
|
|
2460
2460
|
],
|
|
2461
2461
|
)
|
|
@@ -2491,7 +2491,9 @@ def test_air_purifier_passive() -> None:
|
|
|
2491
2491
|
},
|
|
2492
2492
|
rssi=-97,
|
|
2493
2493
|
)
|
|
2494
|
-
result = parse_advertisement_data(
|
|
2494
|
+
result = parse_advertisement_data(
|
|
2495
|
+
ble_device, adv_data, SwitchbotModel.AIR_PURIFIER_US
|
|
2496
|
+
)
|
|
2495
2497
|
assert result == SwitchBotAdvertisement(
|
|
2496
2498
|
address="aa:bb:cc:dd:ee:ff",
|
|
2497
2499
|
data={
|
|
@@ -2509,8 +2511,8 @@ def test_air_purifier_passive() -> None:
|
|
|
2509
2511
|
},
|
|
2510
2512
|
"isEncrypted": False,
|
|
2511
2513
|
"model": "*",
|
|
2512
|
-
"modelFriendlyName": "Air Purifier",
|
|
2513
|
-
"modelName": SwitchbotModel.
|
|
2514
|
+
"modelFriendlyName": "Air Purifier US",
|
|
2515
|
+
"modelName": SwitchbotModel.AIR_PURIFIER_US,
|
|
2514
2516
|
},
|
|
2515
2517
|
device=ble_device,
|
|
2516
2518
|
rssi=-97,
|