Skip to content

Commit 464e72c

Browse files
authored
Add basic support for the mini plugs (#53)
1 parent ed87807 commit 464e72c

File tree

1 file changed

+55
-5
lines changed

1 file changed

+55
-5
lines changed

switchbot/__init__.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
DEVICE_SET_MODE_KEY = "5703"
2323
DEVICE_SET_EXTENDED_KEY = "570f"
2424

25+
# Plug Mini keys
26+
PLUG_ON_KEY = "570f50010180"
27+
PLUG_OFF_KEY = "570f50010100"
28+
2529
# Bot keys
2630
PRESS_KEY = "570100"
2731
ON_KEY = "570101"
@@ -56,7 +60,7 @@ def _sb_uuid(comms_type: str = "service") -> UUID | str:
5660
return "Incorrect type, choose between: tx, rx or service"
5761

5862

59-
def _process_wohand(data: bytes) -> dict[str, bool | int]:
63+
def _process_wohand(data: bytes, mfr_data: bytes | None) -> dict[str, bool | int]:
6064
"""Process woHand/Bot services data."""
6165
_switch_mode = bool(data[1] & 0b10000000)
6266

@@ -69,7 +73,9 @@ def _process_wohand(data: bytes) -> dict[str, bool | int]:
6973
return _bot_data
7074

7175

72-
def _process_wocurtain(data: bytes, reverse: bool = True) -> dict[str, bool | int]:
76+
def _process_wocurtain(
77+
data: bytes, mfr_data: bytes | None, reverse: bool = True
78+
) -> dict[str, bool | int]:
7379
"""Process woCurtain/Curtain services data."""
7480

7581
_position = max(min(data[3] & 0b01111111, 100), 0)
@@ -86,7 +92,7 @@ def _process_wocurtain(data: bytes, reverse: bool = True) -> dict[str, bool | in
8692
return _curtain_data
8793

8894

89-
def _process_wosensorth(data: bytes) -> dict[str, object]:
95+
def _process_wosensorth(data: bytes, mfr_data: bytes | None) -> dict[str, object]:
9096
"""Process woSensorTH/Temp sensor services data."""
9197

9298
_temp_sign = 1 if data[4] & 0b10000000 else -1
@@ -104,7 +110,7 @@ def _process_wosensorth(data: bytes) -> dict[str, object]:
104110
return _wosensorth_data
105111

106112

107-
def _process_wocontact(data: bytes) -> dict[str, bool | int]:
113+
def _process_wocontact(data: bytes, mfr_data: bytes | None) -> dict[str, bool | int]:
108114
"""Process woContact Sensor services data."""
109115
return {
110116
"tested": bool(data[1] & 0b10000000),
@@ -117,6 +123,15 @@ def _process_wocontact(data: bytes) -> dict[str, bool | int]:
117123
}
118124

119125

126+
def _process_woplugmini(data: bytes, mfr_data: bytes | None) -> dict[str, bool | int]:
127+
"""Process plug mini."""
128+
return {
129+
"switchMode": True,
130+
"isOn": mfr_data[7] == 0x80,
131+
"wifi_rssi": mfr_data[9],
132+
}
133+
134+
120135
@dataclass
121136
class SwitchBotAdvertisement:
122137
"""Switchbot advertisement."""
@@ -131,9 +146,12 @@ def parse_advertisement_data(
131146
) -> SwitchBotAdvertisement | None:
132147
"""Parse advertisement data."""
133148
_services = list(advertisement_data.service_data.values())
149+
_mgr_datas = list(advertisement_data.manufacturer_data.values())
150+
134151
if not _services:
135152
return
136153
_service_data = _services[0]
154+
_mfr_data = _mgr_datas[0] if _mgr_datas else None
137155
_model = chr(_service_data[0] & 0b01111111)
138156

139157
supported_types: dict[str, dict[str, Any]] = {
@@ -142,6 +160,7 @@ def parse_advertisement_data(
142160
"c": {"modelName": "WoCurtain", "func": _process_wocurtain},
143161
"T": {"modelName": "WoSensorTH", "func": _process_wosensorth},
144162
"i": {"modelName": "WoSensorTH", "func": _process_wosensorth},
163+
"g": {"modelName": "WoPlug", "func": _process_woplugmini},
145164
}
146165

147166
data = {
@@ -159,7 +178,7 @@ def parse_advertisement_data(
159178
"isEncrypted": bool(_service_data[0] & 0b10000000),
160179
"model": _model,
161180
"modelName": supported_types[_model]["modelName"],
162-
"data": supported_types[_model]["func"](_service_data),
181+
"data": supported_types[_model]["func"](_service_data, _mfr_data),
163182
}
164183
)
165184

@@ -722,3 +741,34 @@ def is_calibrated(self) -> Any:
722741
"""Return True curtain is calibrated."""
723742
# To get actual light level call update() first.
724743
return self._get_adv_value("calibration")
744+
745+
746+
class SwitchbotPlugMini(SwitchbotDevice):
747+
"""Representation of a Switchbot plug mini."""
748+
749+
def __init__(self, *args: Any, **kwargs: Any) -> None:
750+
"""Switchbot plug mini constructor."""
751+
super().__init__(*args, **kwargs)
752+
self._settings: dict[str, Any] = {}
753+
754+
async def update(self, interface: int | None = None) -> None:
755+
"""Update state of device."""
756+
await self.get_device_data(retry=self._retry_count, interface=interface)
757+
758+
async def turn_on(self) -> bool:
759+
"""Turn device on."""
760+
result = await self._sendcommand(PLUG_ON_KEY, self._retry_count)
761+
return result[1] == 0x80
762+
763+
async def turn_off(self) -> bool:
764+
"""Turn device off."""
765+
result = await self._sendcommand(PLUG_OFF_KEY, self._retry_count)
766+
return result[1] == 0x00
767+
768+
def is_on(self) -> Any:
769+
"""Return switch state from cache."""
770+
# To get actual position call update() first.
771+
value = self._get_adv_value("isOn")
772+
if value is None:
773+
return None
774+
return value

0 commit comments

Comments
 (0)