PySwitchbot 0.64.1__tar.gz → 0.66.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/PKG-INFO +1 -1
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/PySwitchbot.egg-info/PKG-INFO +1 -1
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/PySwitchbot.egg-info/SOURCES.txt +7 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/setup.py +1 -1
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/__init__.py +18 -3
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parser.py +32 -7
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/hub2.py +2 -1
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/hub3.py +2 -1
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/hubmini_matter.py +3 -1
- pyswitchbot-0.66.0/switchbot/adv_parsers/humidifier.py +107 -0
- pyswitchbot-0.66.0/switchbot/adv_parsers/light_strip.py +32 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/meter.py +3 -1
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/plug.py +3 -1
- pyswitchbot-0.66.0/switchbot/adv_parsers/relay_switch.py +48 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/const/__init__.py +22 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/const/evaporative_humidifier.py +14 -0
- pyswitchbot-0.66.0/switchbot/const/light.py +34 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/base_light.py +25 -11
- pyswitchbot-0.66.0/switchbot/devices/bulb.py +143 -0
- pyswitchbot-0.66.0/switchbot/devices/ceiling_light.py +105 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/device.py +118 -74
- pyswitchbot-0.66.0/switchbot/devices/evaporative_humidifier.py +256 -0
- pyswitchbot-0.66.0/switchbot/devices/light_strip.py +259 -0
- pyswitchbot-0.66.0/switchbot/devices/relay_switch.py +300 -0
- pyswitchbot-0.66.0/switchbot/helpers.py +75 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_adv_parser.py +652 -70
- pyswitchbot-0.66.0/tests/test_bulb.py +220 -0
- pyswitchbot-0.66.0/tests/test_ceiling_light.py +179 -0
- pyswitchbot-0.66.0/tests/test_colormode_imports.py +88 -0
- pyswitchbot-0.66.0/tests/test_encrypted_device.py +367 -0
- pyswitchbot-0.66.0/tests/test_evaporative_humidifier.py +357 -0
- pyswitchbot-0.66.0/tests/test_helpers.py +72 -0
- pyswitchbot-0.66.0/tests/test_relay_switch.py +453 -0
- pyswitchbot-0.66.0/tests/test_strip_light.py +302 -0
- pyswitchbot-0.64.1/switchbot/adv_parsers/humidifier.py +0 -93
- pyswitchbot-0.64.1/switchbot/adv_parsers/light_strip.py +0 -21
- pyswitchbot-0.64.1/switchbot/adv_parsers/relay_switch.py +0 -32
- pyswitchbot-0.64.1/switchbot/devices/bulb.py +0 -94
- pyswitchbot-0.64.1/switchbot/devices/ceiling_light.py +0 -69
- pyswitchbot-0.64.1/switchbot/devices/evaporative_humidifier.py +0 -212
- pyswitchbot-0.64.1/switchbot/devices/light_strip.py +0 -84
- pyswitchbot-0.64.1/switchbot/devices/relay_switch.py +0 -136
- pyswitchbot-0.64.1/switchbot/helpers.py +0 -17
- pyswitchbot-0.64.1/tests/test_evaporative_humidifier.py +0 -202
- pyswitchbot-0.64.1/tests/test_relay_switch.py +0 -73
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/LICENSE +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/MANIFEST.in +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/PySwitchbot.egg-info/dependency_links.txt +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/PySwitchbot.egg-info/requires.txt +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/PySwitchbot.egg-info/top_level.txt +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/README.md +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/pyproject.toml +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/setup.cfg +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/__init__.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/air_purifier.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/bot.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/bulb.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/contact.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/curtain.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/fan.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/keypad.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/leak.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/lock.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/motion.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/remote.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/roller_shade.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/adv_parsers/vacuum.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/api_config.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/const/air_purifier.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/const/fan.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/const/hub2.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/const/hub3.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/const/lock.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/__init__.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/air_purifier.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/base_cover.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/blind_tilt.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/bot.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/contact.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/curtain.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/fan.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/humidifier.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/keypad.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/lock.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/meter.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/motion.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/plug.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/roller_shade.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/devices/vacuum.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/discovery.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/enum.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/switchbot/models.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_air_purifier.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_base_cover.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_blind_tilt.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_curtain.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_fan.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_hub2.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_hub3.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_lock.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_roller_shade.py +0 -0
- {pyswitchbot-0.64.1 → pyswitchbot-0.66.0}/tests/test_vacuum.py +0 -0
|
@@ -45,6 +45,7 @@ switchbot/const/evaporative_humidifier.py
|
|
|
45
45
|
switchbot/const/fan.py
|
|
46
46
|
switchbot/const/hub2.py
|
|
47
47
|
switchbot/const/hub3.py
|
|
48
|
+
switchbot/const/light.py
|
|
48
49
|
switchbot/const/lock.py
|
|
49
50
|
switchbot/devices/__init__.py
|
|
50
51
|
switchbot/devices/air_purifier.py
|
|
@@ -73,12 +74,18 @@ tests/test_adv_parser.py
|
|
|
73
74
|
tests/test_air_purifier.py
|
|
74
75
|
tests/test_base_cover.py
|
|
75
76
|
tests/test_blind_tilt.py
|
|
77
|
+
tests/test_bulb.py
|
|
78
|
+
tests/test_ceiling_light.py
|
|
79
|
+
tests/test_colormode_imports.py
|
|
76
80
|
tests/test_curtain.py
|
|
81
|
+
tests/test_encrypted_device.py
|
|
77
82
|
tests/test_evaporative_humidifier.py
|
|
78
83
|
tests/test_fan.py
|
|
84
|
+
tests/test_helpers.py
|
|
79
85
|
tests/test_hub2.py
|
|
80
86
|
tests/test_hub3.py
|
|
81
87
|
tests/test_lock.py
|
|
82
88
|
tests/test_relay_switch.py
|
|
83
89
|
tests/test_roller_shade.py
|
|
90
|
+
tests/test_strip_light.py
|
|
84
91
|
tests/test_vacuum.py
|
|
@@ -11,8 +11,15 @@ from bleak_retry_connector import (
|
|
|
11
11
|
from .adv_parser import SwitchbotSupportedType, parse_advertisement_data
|
|
12
12
|
from .const import (
|
|
13
13
|
AirPurifierMode,
|
|
14
|
+
BulbColorMode,
|
|
15
|
+
CeilingLightColorMode,
|
|
16
|
+
ColorMode,
|
|
14
17
|
FanMode,
|
|
18
|
+
HumidifierAction,
|
|
19
|
+
HumidifierMode,
|
|
20
|
+
HumidifierWaterLevel,
|
|
15
21
|
LockStatus,
|
|
22
|
+
StripLightColorMode,
|
|
16
23
|
SwitchbotAccountConnectionError,
|
|
17
24
|
SwitchbotApiError,
|
|
18
25
|
SwitchbotAuthenticationError,
|
|
@@ -25,14 +32,14 @@ from .devices.bot import Switchbot
|
|
|
25
32
|
from .devices.bulb import SwitchbotBulb
|
|
26
33
|
from .devices.ceiling_light import SwitchbotCeilingLight
|
|
27
34
|
from .devices.curtain import SwitchbotCurtain
|
|
28
|
-
from .devices.device import
|
|
35
|
+
from .devices.device import SwitchbotDevice, SwitchbotEncryptedDevice
|
|
29
36
|
from .devices.evaporative_humidifier import SwitchbotEvaporativeHumidifier
|
|
30
37
|
from .devices.fan import SwitchbotFan
|
|
31
38
|
from .devices.humidifier import SwitchbotHumidifier
|
|
32
|
-
from .devices.light_strip import SwitchbotLightStrip
|
|
39
|
+
from .devices.light_strip import SwitchbotLightStrip, SwitchbotStripLight3
|
|
33
40
|
from .devices.lock import SwitchbotLock
|
|
34
41
|
from .devices.plug import SwitchbotPlugMini
|
|
35
|
-
from .devices.relay_switch import SwitchbotRelaySwitch
|
|
42
|
+
from .devices.relay_switch import SwitchbotRelaySwitch, SwitchbotRelaySwitch2PM
|
|
36
43
|
from .devices.roller_shade import SwitchbotRollerShade
|
|
37
44
|
from .devices.vacuum import SwitchbotVacuum
|
|
38
45
|
from .discovery import GetSwitchbotDevices
|
|
@@ -40,10 +47,16 @@ from .models import SwitchBotAdvertisement
|
|
|
40
47
|
|
|
41
48
|
__all__ = [
|
|
42
49
|
"AirPurifierMode",
|
|
50
|
+
"BulbColorMode",
|
|
51
|
+
"CeilingLightColorMode",
|
|
43
52
|
"ColorMode",
|
|
44
53
|
"FanMode",
|
|
45
54
|
"GetSwitchbotDevices",
|
|
55
|
+
"HumidifierAction",
|
|
56
|
+
"HumidifierMode",
|
|
57
|
+
"HumidifierWaterLevel",
|
|
46
58
|
"LockStatus",
|
|
59
|
+
"StripLightColorMode",
|
|
47
60
|
"SwitchBotAdvertisement",
|
|
48
61
|
"Switchbot",
|
|
49
62
|
"Switchbot",
|
|
@@ -68,7 +81,9 @@ __all__ = [
|
|
|
68
81
|
"SwitchbotPlugMini",
|
|
69
82
|
"SwitchbotPlugMini",
|
|
70
83
|
"SwitchbotRelaySwitch",
|
|
84
|
+
"SwitchbotRelaySwitch2PM",
|
|
71
85
|
"SwitchbotRollerShade",
|
|
86
|
+
"SwitchbotStripLight3",
|
|
72
87
|
"SwitchbotSupportedType",
|
|
73
88
|
"SwitchbotSupportedType",
|
|
74
89
|
"SwitchbotVacuum",
|
|
@@ -24,14 +24,15 @@ from .adv_parsers.hubmini_matter import process_hubmini_matter
|
|
|
24
24
|
from .adv_parsers.humidifier import process_evaporative_humidifier, process_wohumidifier
|
|
25
25
|
from .adv_parsers.keypad import process_wokeypad
|
|
26
26
|
from .adv_parsers.leak import process_leak
|
|
27
|
-
from .adv_parsers.light_strip import process_wostrip
|
|
27
|
+
from .adv_parsers.light_strip import process_light, process_wostrip
|
|
28
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
|
|
32
32
|
from .adv_parsers.relay_switch import (
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
process_garage_door_opener,
|
|
34
|
+
process_relay_switch_2pm,
|
|
35
|
+
process_relay_switch_common_data,
|
|
35
36
|
)
|
|
36
37
|
from .adv_parsers.remote import process_woremote
|
|
37
38
|
from .adv_parsers.roller_shade import process_worollershade
|
|
@@ -115,13 +116,13 @@ SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
|
|
|
115
116
|
},
|
|
116
117
|
"4": {
|
|
117
118
|
"modelName": SwitchbotModel.METER_PRO,
|
|
118
|
-
"modelFriendlyName": "Meter",
|
|
119
|
+
"modelFriendlyName": "Meter Pro",
|
|
119
120
|
"func": process_wosensorth,
|
|
120
121
|
"manufacturer_id": 2409,
|
|
121
122
|
},
|
|
122
123
|
"5": {
|
|
123
124
|
"modelName": SwitchbotModel.METER_PRO_C,
|
|
124
|
-
"modelFriendlyName": "Meter",
|
|
125
|
+
"modelFriendlyName": "Meter Pro CO2",
|
|
125
126
|
"func": process_wosensorth_c,
|
|
126
127
|
"manufacturer_id": 2409,
|
|
127
128
|
},
|
|
@@ -207,13 +208,13 @@ SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
|
|
|
207
208
|
"<": {
|
|
208
209
|
"modelName": SwitchbotModel.RELAY_SWITCH_1PM,
|
|
209
210
|
"modelFriendlyName": "Relay Switch 1PM",
|
|
210
|
-
"func":
|
|
211
|
+
"func": process_relay_switch_common_data,
|
|
211
212
|
"manufacturer_id": 2409,
|
|
212
213
|
},
|
|
213
214
|
";": {
|
|
214
215
|
"modelName": SwitchbotModel.RELAY_SWITCH_1,
|
|
215
216
|
"modelFriendlyName": "Relay Switch 1",
|
|
216
|
-
"func":
|
|
217
|
+
"func": process_relay_switch_common_data,
|
|
217
218
|
"manufacturer_id": 2409,
|
|
218
219
|
},
|
|
219
220
|
"b": {
|
|
@@ -312,6 +313,30 @@ SUPPORTED_TYPES: dict[str | bytes, SwitchbotSupportedType] = {
|
|
|
312
313
|
"func": process_lock2,
|
|
313
314
|
"manufacturer_id": 2409,
|
|
314
315
|
},
|
|
316
|
+
">": {
|
|
317
|
+
"modelName": SwitchbotModel.GARAGE_DOOR_OPENER,
|
|
318
|
+
"modelFriendlyName": "Garage Door Opener",
|
|
319
|
+
"func": process_garage_door_opener,
|
|
320
|
+
"manufacturer_id": 2409,
|
|
321
|
+
},
|
|
322
|
+
"=": {
|
|
323
|
+
"modelName": SwitchbotModel.RELAY_SWITCH_2PM,
|
|
324
|
+
"modelFriendlyName": "Relay Switch 2PM",
|
|
325
|
+
"func": process_relay_switch_2pm,
|
|
326
|
+
"manufacturer_id": 2409,
|
|
327
|
+
},
|
|
328
|
+
b"\x00\x10\xd0\xb0": {
|
|
329
|
+
"modelName": SwitchbotModel.FLOOR_LAMP,
|
|
330
|
+
"modelFriendlyName": "Floor Lamp",
|
|
331
|
+
"func": process_light,
|
|
332
|
+
"manufacturer_id": 2409,
|
|
333
|
+
},
|
|
334
|
+
b"\x00\x10\xd0\xb1": {
|
|
335
|
+
"modelName": SwitchbotModel.STRIP_LIGHT_3,
|
|
336
|
+
"modelFriendlyName": "Strip Light 3",
|
|
337
|
+
"func": process_light,
|
|
338
|
+
"manufacturer_id": 2409,
|
|
339
|
+
},
|
|
315
340
|
}
|
|
316
341
|
|
|
317
342
|
_SWITCHBOT_MODEL_TO_CHAR = {
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from ..const.hub2 import LIGHT_INTENSITY_MAP
|
|
8
|
+
from ..helpers import celsius_to_fahrenheit
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def process_wohub2(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
|
|
@@ -22,7 +23,7 @@ def process_wohub2(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]
|
|
|
22
23
|
_temp_c = _temp_sign * (
|
|
23
24
|
(temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
|
|
24
25
|
)
|
|
25
|
-
_temp_f = (_temp_c
|
|
26
|
+
_temp_f = celsius_to_fahrenheit(_temp_c)
|
|
26
27
|
_temp_f = (_temp_f * 10) / 10
|
|
27
28
|
humidity = temp_data[2] & 0b01111111
|
|
28
29
|
light_level = status & 0b11111
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from ..const.hub3 import LIGHT_INTENSITY_MAP
|
|
8
|
+
from ..helpers import celsius_to_fahrenheit
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def process_hub3(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
|
|
@@ -26,7 +27,7 @@ def process_hub3(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
|
|
|
26
27
|
_temp_c = _temp_sign * (
|
|
27
28
|
(temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
|
|
28
29
|
)
|
|
29
|
-
_temp_f = round((
|
|
30
|
+
_temp_f = round(celsius_to_fahrenheit(_temp_c), 1)
|
|
30
31
|
humidity = temp_data[2] & 0b01111111
|
|
31
32
|
motion_detected = bool(device_data[10] & 0b10000000)
|
|
32
33
|
|
|
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
+
from ..helpers import celsius_to_fahrenheit
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
def process_hubmini_matter(
|
|
9
11
|
data: bytes | None, mfr_data: bytes | None
|
|
@@ -21,7 +23,7 @@ def process_hubmini_matter(
|
|
|
21
23
|
_temp_c = _temp_sign * (
|
|
22
24
|
(temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
|
|
23
25
|
)
|
|
24
|
-
_temp_f = (_temp_c
|
|
26
|
+
_temp_f = celsius_to_fahrenheit(_temp_c)
|
|
25
27
|
_temp_f = (_temp_f * 10) / 10
|
|
26
28
|
humidity = temp_data[2] & 0b01111111
|
|
27
29
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Humidifier adv parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import timedelta
|
|
7
|
+
|
|
8
|
+
from ..const.evaporative_humidifier import (
|
|
9
|
+
HumidifierMode,
|
|
10
|
+
HumidifierWaterLevel,
|
|
11
|
+
)
|
|
12
|
+
from ..helpers import celsius_to_fahrenheit
|
|
13
|
+
|
|
14
|
+
_LOGGER = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# mfr_data: 943cc68d3d2e
|
|
17
|
+
# data: 650000cd802b6300
|
|
18
|
+
# data: 650000cd802b6300
|
|
19
|
+
# data: 658000c9802b6300
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Low: 658000c5222b6300
|
|
23
|
+
# Med: 658000c5432b6300
|
|
24
|
+
# High: 658000c5642b6300
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def calculate_temperature_and_humidity(
|
|
28
|
+
data: bytes, is_meter_binded: bool = True
|
|
29
|
+
) -> tuple[float | None, float | None, int | None]:
|
|
30
|
+
"""Calculate temperature and humidity based on the given flag."""
|
|
31
|
+
if len(data) < 3 or not is_meter_binded:
|
|
32
|
+
return None, None, None
|
|
33
|
+
|
|
34
|
+
humidity = data[0] & 0b01111111
|
|
35
|
+
if humidity > 100:
|
|
36
|
+
return None, None, None
|
|
37
|
+
|
|
38
|
+
_temp_sign = 1 if data[1] & 0b10000000 else -1
|
|
39
|
+
_temp_c = _temp_sign * ((data[1] & 0b01111111) + ((data[2] >> 4) / 10))
|
|
40
|
+
_temp_f = celsius_to_fahrenheit(_temp_c)
|
|
41
|
+
|
|
42
|
+
return _temp_c, _temp_f, humidity
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def process_wohumidifier(
|
|
46
|
+
data: bytes | None, mfr_data: bytes | None
|
|
47
|
+
) -> dict[str, bool | int]:
|
|
48
|
+
"""Process WoHumi services data."""
|
|
49
|
+
if data is None:
|
|
50
|
+
return {
|
|
51
|
+
"isOn": None,
|
|
52
|
+
"level": None,
|
|
53
|
+
"switchMode": True,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
"isOn": bool(data[1]),
|
|
58
|
+
"level": data[4],
|
|
59
|
+
"switchMode": True,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def process_evaporative_humidifier(
|
|
64
|
+
data: bytes | None, mfr_data: bytes | None
|
|
65
|
+
) -> dict[str, bool | int]:
|
|
66
|
+
"""Process WoHumi services data."""
|
|
67
|
+
if mfr_data is None:
|
|
68
|
+
return {}
|
|
69
|
+
|
|
70
|
+
seq_number = mfr_data[6]
|
|
71
|
+
is_on = bool(mfr_data[7] & 0b10000000)
|
|
72
|
+
mode = HumidifierMode(mfr_data[7] & 0b00001111)
|
|
73
|
+
over_humidify_protection = bool(mfr_data[8] & 0b10000000)
|
|
74
|
+
child_lock = bool(mfr_data[8] & 0b00100000)
|
|
75
|
+
tank_removed = bool(mfr_data[8] & 0b00000100)
|
|
76
|
+
tilted_alert = bool(mfr_data[8] & 0b00000010)
|
|
77
|
+
filter_missing = bool(mfr_data[8] & 0b00000001)
|
|
78
|
+
is_meter_binded = bool(mfr_data[9] & 0b10000000)
|
|
79
|
+
|
|
80
|
+
_temp_c, _temp_f, humidity = calculate_temperature_and_humidity(
|
|
81
|
+
mfr_data[9:12], is_meter_binded
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
water_level = HumidifierWaterLevel(mfr_data[11] & 0b00000011).name.lower()
|
|
85
|
+
filter_run_time = timedelta(
|
|
86
|
+
hours=int.from_bytes(mfr_data[12:14], byteorder="big") & 0xFFF
|
|
87
|
+
)
|
|
88
|
+
target_humidity = mfr_data[16] & 0b01111111
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
"seq_number": seq_number,
|
|
92
|
+
"isOn": is_on,
|
|
93
|
+
"mode": mode,
|
|
94
|
+
"over_humidify_protection": over_humidify_protection,
|
|
95
|
+
"child_lock": child_lock,
|
|
96
|
+
"tank_removed": tank_removed,
|
|
97
|
+
"tilted_alert": tilted_alert,
|
|
98
|
+
"filter_missing": filter_missing,
|
|
99
|
+
"is_meter_binded": is_meter_binded,
|
|
100
|
+
"humidity": humidity,
|
|
101
|
+
"temperature": _temp_c,
|
|
102
|
+
"temp": {"c": _temp_c, "f": _temp_f},
|
|
103
|
+
"water_level": water_level,
|
|
104
|
+
"filter_run_time": filter_run_time,
|
|
105
|
+
"filter_alert": filter_run_time.days >= 10,
|
|
106
|
+
"target_humidity": target_humidity,
|
|
107
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Light strip adv parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import struct
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def process_wostrip(
|
|
9
|
+
data: bytes | None, mfr_data: bytes | None
|
|
10
|
+
) -> dict[str, bool | int]:
|
|
11
|
+
"""Process WoStrip services data."""
|
|
12
|
+
if mfr_data is None:
|
|
13
|
+
return {}
|
|
14
|
+
return {
|
|
15
|
+
"sequence_number": mfr_data[6],
|
|
16
|
+
"isOn": bool(mfr_data[7] & 0b10000000),
|
|
17
|
+
"brightness": mfr_data[7] & 0b01111111,
|
|
18
|
+
"delay": bool(mfr_data[8] & 0b10000000),
|
|
19
|
+
"network_state": (mfr_data[8] & 0b01110000) >> 4,
|
|
20
|
+
"color_mode": mfr_data[8] & 0b00001111,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def process_light(data: bytes | None, mfr_data: bytes | None) -> dict[str, bool | int]:
|
|
25
|
+
"""Support for strip light 3 and floor lamp."""
|
|
26
|
+
common_data = process_wostrip(data, mfr_data)
|
|
27
|
+
if not common_data:
|
|
28
|
+
return {}
|
|
29
|
+
|
|
30
|
+
light_data = {"cw": struct.unpack(">H", mfr_data[16:18])[0]}
|
|
31
|
+
|
|
32
|
+
return common_data | light_data
|
|
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
import struct
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
from ..helpers import celsius_to_fahrenheit
|
|
9
|
+
|
|
8
10
|
CO2_UNPACK = struct.Struct(">H").unpack_from
|
|
9
11
|
|
|
10
12
|
|
|
@@ -28,7 +30,7 @@ def process_wosensorth(data: bytes | None, mfr_data: bytes | None) -> dict[str,
|
|
|
28
30
|
_temp_c = _temp_sign * (
|
|
29
31
|
(temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
|
|
30
32
|
)
|
|
31
|
-
_temp_f = (_temp_c
|
|
33
|
+
_temp_f = celsius_to_fahrenheit(_temp_c)
|
|
32
34
|
_temp_f = (_temp_f * 10) / 10
|
|
33
35
|
humidity = temp_data[2] & 0b01111111
|
|
34
36
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from ..helpers import parse_power_data
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
def process_woplugmini(
|
|
7
9
|
data: bytes | None, mfr_data: bytes | None
|
|
@@ -13,5 +15,5 @@ def process_woplugmini(
|
|
|
13
15
|
"switchMode": True,
|
|
14
16
|
"isOn": mfr_data[7] == 0x80,
|
|
15
17
|
"wifi_rssi": -mfr_data[9],
|
|
16
|
-
"power": (
|
|
18
|
+
"power": parse_power_data(mfr_data, 10, 10.0, 0x7FFF), # W
|
|
17
19
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Relay Switch adv parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def process_relay_switch_common_data(
|
|
9
|
+
data: bytes | None, mfr_data: bytes | None
|
|
10
|
+
) -> dict[str, Any]:
|
|
11
|
+
"""Process relay switch 1 and 1PM common data."""
|
|
12
|
+
if mfr_data is None:
|
|
13
|
+
return {}
|
|
14
|
+
return {
|
|
15
|
+
"switchMode": True, # for compatibility, useless
|
|
16
|
+
"sequence_number": mfr_data[6],
|
|
17
|
+
"isOn": bool(mfr_data[7] & 0b10000000),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def process_garage_door_opener(
|
|
22
|
+
data: bytes | None, mfr_data: bytes | None
|
|
23
|
+
) -> dict[str, Any]:
|
|
24
|
+
"""Process garage door opener services data."""
|
|
25
|
+
if mfr_data is None:
|
|
26
|
+
return {}
|
|
27
|
+
common_data = process_relay_switch_common_data(data, mfr_data)
|
|
28
|
+
common_data["door_open"] = not bool(mfr_data[7] & 0b00100000)
|
|
29
|
+
return common_data
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def process_relay_switch_2pm(
|
|
33
|
+
data: bytes | None, mfr_data: bytes | None
|
|
34
|
+
) -> dict[int, dict[str, Any]]:
|
|
35
|
+
"""Process Relay Switch 2PM services data."""
|
|
36
|
+
if mfr_data is None:
|
|
37
|
+
return {}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
1: {
|
|
41
|
+
**process_relay_switch_common_data(data, mfr_data),
|
|
42
|
+
},
|
|
43
|
+
2: {
|
|
44
|
+
"switchMode": True, # for compatibility, useless
|
|
45
|
+
"sequence_number": mfr_data[6],
|
|
46
|
+
"isOn": bool(mfr_data[7] & 0b01000000),
|
|
47
|
+
},
|
|
48
|
+
}
|
|
@@ -4,7 +4,18 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from ..enum import StrEnum
|
|
6
6
|
from .air_purifier import AirPurifierMode
|
|
7
|
+
from .evaporative_humidifier import (
|
|
8
|
+
HumidifierAction,
|
|
9
|
+
HumidifierMode,
|
|
10
|
+
HumidifierWaterLevel,
|
|
11
|
+
)
|
|
7
12
|
from .fan import FanMode
|
|
13
|
+
from .light import (
|
|
14
|
+
BulbColorMode,
|
|
15
|
+
CeilingLightColorMode,
|
|
16
|
+
ColorMode,
|
|
17
|
+
StripLightColorMode,
|
|
18
|
+
)
|
|
8
19
|
|
|
9
20
|
# Preserve old LockStatus export for backwards compatibility
|
|
10
21
|
from .lock import LockStatus
|
|
@@ -78,6 +89,10 @@ class SwitchbotModel(StrEnum):
|
|
|
78
89
|
HUB3 = "Hub3"
|
|
79
90
|
LOCK_ULTRA = "Lock Ultra"
|
|
80
91
|
LOCK_LITE = "Lock Lite"
|
|
92
|
+
GARAGE_DOOR_OPENER = "Garage Door Opener"
|
|
93
|
+
RELAY_SWITCH_2PM = "Relay Switch 2PM"
|
|
94
|
+
STRIP_LIGHT_3 = "Strip Light 3"
|
|
95
|
+
FLOOR_LAMP = "Floor Lamp"
|
|
81
96
|
|
|
82
97
|
|
|
83
98
|
__all__ = [
|
|
@@ -85,8 +100,15 @@ __all__ = [
|
|
|
85
100
|
"DEFAULT_RETRY_TIMEOUT",
|
|
86
101
|
"DEFAULT_SCAN_TIMEOUT",
|
|
87
102
|
"AirPurifierMode",
|
|
103
|
+
"BulbColorMode",
|
|
104
|
+
"CeilingLightColorMode",
|
|
105
|
+
"ColorMode",
|
|
88
106
|
"FanMode",
|
|
107
|
+
"HumidifierAction",
|
|
108
|
+
"HumidifierMode",
|
|
109
|
+
"HumidifierWaterLevel",
|
|
89
110
|
"LockStatus",
|
|
111
|
+
"StripLightColorMode",
|
|
90
112
|
"SwitchbotAccountConnectionError",
|
|
91
113
|
"SwitchbotApiError",
|
|
92
114
|
"SwitchbotAuthenticationError",
|
|
@@ -13,6 +13,10 @@ class HumidifierMode(Enum):
|
|
|
13
13
|
AUTO = 7
|
|
14
14
|
DRYING_FILTER = 8
|
|
15
15
|
|
|
16
|
+
@classmethod
|
|
17
|
+
def get_modes(cls) -> list[str]:
|
|
18
|
+
return [mode.name.lower() for mode in cls]
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
class HumidifierWaterLevel(Enum):
|
|
18
22
|
EMPTY = 0
|
|
@@ -20,6 +24,16 @@ class HumidifierWaterLevel(Enum):
|
|
|
20
24
|
MEDIUM = 2
|
|
21
25
|
HIGH = 3
|
|
22
26
|
|
|
27
|
+
@classmethod
|
|
28
|
+
def get_levels(cls) -> list[str]:
|
|
29
|
+
return [level.name.lower() for level in cls]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class HumidifierAction(Enum):
|
|
33
|
+
OFF = 0
|
|
34
|
+
HUMIDIFYING = 1
|
|
35
|
+
DRYING = 2
|
|
36
|
+
|
|
23
37
|
|
|
24
38
|
OVER_HUMIDIFY_PROTECTION_MODES = {
|
|
25
39
|
HumidifierMode.QUIET,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ColorMode(Enum):
|
|
5
|
+
OFF = 0
|
|
6
|
+
COLOR_TEMP = 1
|
|
7
|
+
RGB = 2
|
|
8
|
+
EFFECT = 3
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StripLightColorMode(Enum):
|
|
12
|
+
RGB = 2
|
|
13
|
+
SCENE = 3
|
|
14
|
+
MUSIC = 4
|
|
15
|
+
CONTROLLER = 5
|
|
16
|
+
COLOR_TEMP = 6
|
|
17
|
+
UNKNOWN = 10
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BulbColorMode(Enum):
|
|
21
|
+
COLOR_TEMP = 1
|
|
22
|
+
RGB = 2
|
|
23
|
+
DYNAMIC = 3
|
|
24
|
+
UNKNOWN = 10
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CeilingLightColorMode(Enum):
|
|
28
|
+
COLOR_TEMP = 0
|
|
29
|
+
NIGHT = 1
|
|
30
|
+
MUSIC = 4
|
|
31
|
+
UNKNOWN = 10
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
DEFAULT_COLOR_TEMP = 4001
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import time
|
|
5
4
|
from abc import abstractmethod
|
|
6
5
|
from typing import Any
|
|
7
6
|
|
|
8
7
|
from ..helpers import create_background_task
|
|
9
8
|
from ..models import SwitchBotAdvertisement
|
|
10
|
-
from .device import
|
|
9
|
+
from .device import SwitchbotDevice
|
|
11
10
|
|
|
12
11
|
_LOGGER = logging.getLogger(__name__)
|
|
13
12
|
|
|
@@ -43,9 +42,10 @@ class SwitchbotBaseLight(SwitchbotDevice):
|
|
|
43
42
|
return self._get_adv_value("brightness") or 0
|
|
44
43
|
|
|
45
44
|
@property
|
|
46
|
-
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def color_mode(self) -> Any:
|
|
47
47
|
"""Return the current color mode."""
|
|
48
|
-
|
|
48
|
+
raise NotImplementedError("Subclasses must implement color mode")
|
|
49
49
|
|
|
50
50
|
@property
|
|
51
51
|
def min_temp(self) -> int:
|
|
@@ -57,10 +57,19 @@ class SwitchbotBaseLight(SwitchbotDevice):
|
|
|
57
57
|
"""Return maximum color temp."""
|
|
58
58
|
return 6500
|
|
59
59
|
|
|
60
|
+
@property
|
|
61
|
+
def get_effect_list(self) -> list[str] | None:
|
|
62
|
+
"""Return the list of supported effects."""
|
|
63
|
+
return None
|
|
64
|
+
|
|
60
65
|
def is_on(self) -> bool | None:
|
|
61
66
|
"""Return bulb state from cache."""
|
|
62
67
|
return self._get_adv_value("isOn")
|
|
63
68
|
|
|
69
|
+
def get_effect(self):
|
|
70
|
+
"""Return the current effect."""
|
|
71
|
+
return self._get_adv_value("effect")
|
|
72
|
+
|
|
64
73
|
@abstractmethod
|
|
65
74
|
async def turn_on(self) -> bool:
|
|
66
75
|
"""Turn device on."""
|
|
@@ -81,13 +90,18 @@ class SwitchbotBaseLight(SwitchbotDevice):
|
|
|
81
90
|
async def set_rgb(self, brightness: int, r: int, g: int, b: int) -> bool:
|
|
82
91
|
"""Set rgb."""
|
|
83
92
|
|
|
84
|
-
def
|
|
85
|
-
"""
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
async def _send_multiple_commands(self, keys: list[str]) -> bool:
|
|
94
|
+
"""
|
|
95
|
+
Send multiple commands to device.
|
|
96
|
+
|
|
97
|
+
Since we current have no way to tell which command the device
|
|
98
|
+
needs we send both.
|
|
99
|
+
"""
|
|
100
|
+
final_result = False
|
|
101
|
+
for key in keys:
|
|
102
|
+
result = await self._send_command(key)
|
|
103
|
+
final_result |= self._check_command_result(result, 0, {1})
|
|
104
|
+
return final_result
|
|
91
105
|
|
|
92
106
|
|
|
93
107
|
class SwitchbotSequenceBaseLight(SwitchbotBaseLight):
|