Skip to content

Commit 9d8a9c2

Browse files
zerzhangpre-commit-ci[bot]bdraco
authored
Add support for climate panel (#403)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston <[email protected]>
1 parent fa7d072 commit 9d8a9c2

File tree

4 files changed

+104
-6
lines changed

4 files changed

+104
-6
lines changed

switchbot/adv_parser.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .adv_parsers.bot import process_wohand
1616
from .adv_parsers.bulb import process_color_bulb
1717
from .adv_parsers.ceiling_light import process_woceiling
18+
from .adv_parsers.climate_panel import process_climate_panel
1819
from .adv_parsers.contact import process_wocontact
1920
from .adv_parsers.curtain import process_wocurtain
2021
from .adv_parsers.fan import process_fan
@@ -370,6 +371,12 @@ class SwitchbotSupportedType(TypedDict):
370371
"func": process_vacuum,
371372
"manufacturer_id": 2409,
372373
},
374+
b"\x00\x10\xf3\xd8": {
375+
"modelName": SwitchbotModel.CLIMATE_PANEL,
376+
"modelFriendlyName": "Climate Panel",
377+
"func": process_climate_panel,
378+
"manufacturer_id": 2409,
379+
},
373380
}
374381

375382
_SWITCHBOT_MODEL_TO_CHAR = {
@@ -452,12 +459,12 @@ def _parse_data(
452459
if model_data.get("manufacturer_data_length") == len(_mfr_data):
453460
_model = model_chr
454461
break
455-
if (
456-
_service_data
457-
and len(_service_data) > 5
458-
and _service_data[-4:] in SUPPORTED_TYPES
459-
):
460-
_model = _service_data[-4:]
462+
463+
if _service_data and len(_service_data) > 5:
464+
for s in (_service_data[-4:], _service_data[-5:-1]):
465+
if s in SUPPORTED_TYPES:
466+
_model = s
467+
break
461468

462469
if not _model:
463470
return None
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Advertisement data parser for climate panel devices."""
2+
3+
import logging
4+
5+
_LOGGER = logging.getLogger(__name__)
6+
7+
8+
def process_climate_panel(
9+
data: bytes | None, mfr_data: bytes | None
10+
) -> dict[str, bool | int | str]:
11+
"""Process Climate Panel data."""
12+
if mfr_data is None:
13+
return {}
14+
15+
seq_number = mfr_data[6]
16+
isOn = bool(mfr_data[7] & 0x80)
17+
battery = mfr_data[7] & 0x7F
18+
humidity_alarm = (mfr_data[8] >> 6) & 0x03
19+
temp_alarm = (mfr_data[8] >> 4) & 0x03
20+
21+
temp_decimal = mfr_data[8] & 0x0F
22+
temp_sign = 1 if (mfr_data[9] & 0x80) else -1
23+
temp_int = mfr_data[9] & 0x7F
24+
temperature = temp_sign * (temp_int + temp_decimal / 10)
25+
26+
humidity = mfr_data[10] & 0x7F
27+
28+
pir_state = bool(mfr_data[15] & 0x80)
29+
is_light = ((mfr_data[15] >> 2) & 0x03) == 0x10
30+
31+
result = {
32+
"sequence_number": seq_number,
33+
"isOn": isOn,
34+
"battery": battery,
35+
"temperature": temperature,
36+
"humidity": humidity,
37+
"temp_alarm": temp_alarm,
38+
"humidity_alarm": humidity_alarm,
39+
"motion_detected": pir_state,
40+
"is_light": is_light,
41+
}
42+
43+
_LOGGER.debug(
44+
"Processed climate panel mfr data: %s, result: %s", mfr_data.hex(), result
45+
)
46+
return result

switchbot/const/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class SwitchbotModel(StrEnum):
9797
RGBICWW_STRIP_LIGHT = "RGBICWW Strip Light"
9898
RGBICWW_FLOOR_LAMP = "RGBICWW Floor Lamp"
9999
K11_VACUUM = "K11+ Vacuum"
100+
CLIMATE_PANEL = "Climate Panel"
100101

101102

102103
__all__ = [

tests/test_adv_parser.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3430,6 +3430,24 @@ def test_humidifer_with_empty_data() -> None:
34303430
"K11+ Vacuum",
34313431
SwitchbotModel.K11_VACUUM,
34323432
),
3433+
AdvTestCase(
3434+
b"\xb0\xe9\xfe\x8e\x98Oi_\x06\x9a,\x00\x00\x00\x00\xe4\x00\x08\x04\x00\x01\x00\x00",
3435+
b"\x00 _\x00\x10\xf3\xd8@",
3436+
{
3437+
"battery": 95,
3438+
"humidity": 44,
3439+
"sequence_number": 105,
3440+
"humidity_alarm": 0,
3441+
"isOn": False,
3442+
"is_light": False,
3443+
"motion_detected": True,
3444+
"temp_alarm": 0,
3445+
"temperature": 26.6,
3446+
},
3447+
b"\x00\x10\xf3\xd8",
3448+
"Climate Panel",
3449+
SwitchbotModel.CLIMATE_PANEL,
3450+
),
34333451
],
34343452
)
34353453
def test_adv_active(test_case: AdvTestCase) -> None:
@@ -3644,6 +3662,24 @@ def test_adv_active(test_case: AdvTestCase) -> None:
36443662
"K11+ Vacuum",
36453663
SwitchbotModel.K11_VACUUM,
36463664
),
3665+
AdvTestCase(
3666+
b"\xb0\xe9\xfe\x8e\x98Oi_\x06\x9a,\x00\x00\x00\x00\xe4\x00\x08\x04\x00\x01\x00\x00",
3667+
None,
3668+
{
3669+
"battery": 95,
3670+
"humidity": 44,
3671+
"sequence_number": 105,
3672+
"humidity_alarm": 0,
3673+
"isOn": False,
3674+
"is_light": False,
3675+
"motion_detected": True,
3676+
"temp_alarm": 0,
3677+
"temperature": 26.6,
3678+
},
3679+
b"\x00\x10\xf3\xd8",
3680+
"Climate Panel",
3681+
SwitchbotModel.CLIMATE_PANEL,
3682+
),
36473683
],
36483684
)
36493685
def test_adv_passive(test_case: AdvTestCase) -> None:
@@ -3809,6 +3845,14 @@ def test_adv_passive(test_case: AdvTestCase) -> None:
38093845
"K11+ Vacuum",
38103846
SwitchbotModel.K11_VACUUM,
38113847
),
3848+
AdvTestCase(
3849+
None,
3850+
b"\x00 _\x00\x10\xf3\xd8@",
3851+
{},
3852+
b"\x00\x10\xf3\xd8",
3853+
"Climate Panel",
3854+
SwitchbotModel.CLIMATE_PANEL,
3855+
),
38123856
],
38133857
)
38143858
def test_adv_with_empty_data(test_case: AdvTestCase) -> None:

0 commit comments

Comments
 (0)