1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
"""Library to handle connection with Switchbot."""
from __future__ import annotations
import logging
from typing import Any
from .device import (
DEVICE_SET_EXTENDED_KEY,
DEVICE_SET_MODE_KEY,
SwitchbotDeviceOverrideStateDuringConnection,
update_after_operation,
)
_LOGGER = logging.getLogger(__name__)
BOT_COMMAND_HEADER = "5701"
# Bot keys
PRESS_KEY = f"{BOT_COMMAND_HEADER}00"
ON_KEY = f"{BOT_COMMAND_HEADER}01"
OFF_KEY = f"{BOT_COMMAND_HEADER}02"
DOWN_KEY = f"{BOT_COMMAND_HEADER}03"
UP_KEY = f"{BOT_COMMAND_HEADER}04"
class Switchbot(SwitchbotDeviceOverrideStateDuringConnection):
"""Representation of a Switchbot."""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Switchbot Bot/WoHand constructor."""
super().__init__(*args, **kwargs)
self._inverse: bool = kwargs.pop("inverse_mode", False)
@update_after_operation
async def turn_on(self) -> bool:
"""Turn device on."""
result = await self._send_command(ON_KEY)
ret = self._check_command_result(result, 0, {1, 5})
self._override_state({"isOn": True})
_LOGGER.debug(
"%s: Turn on result: %s -> %s",
self.name,
result.hex() if result else None,
self._override_adv_data,
)
self._fire_callbacks()
return ret
@update_after_operation
async def turn_off(self) -> bool:
"""Turn device off."""
result = await self._send_command(OFF_KEY)
ret = self._check_command_result(result, 0, {1, 5})
self._override_state({"isOn": False})
_LOGGER.debug(
"%s: Turn off result: %s -> %s",
self.name,
result.hex() if result else None,
self._override_adv_data,
)
self._fire_callbacks()
return ret
@update_after_operation
async def hand_up(self) -> bool:
"""Raise device arm."""
result = await self._send_command(UP_KEY)
return self._check_command_result(result, 0, {1, 5})
@update_after_operation
async def hand_down(self) -> bool:
"""Lower device arm."""
result = await self._send_command(DOWN_KEY)
return self._check_command_result(result, 0, {1, 5})
@update_after_operation
async def press(self) -> bool:
"""Press command to device."""
result = await self._send_command(PRESS_KEY)
return self._check_command_result(result, 0, {1, 5})
@update_after_operation
async def set_switch_mode(
self, switch_mode: bool = False, strength: int = 100, inverse: bool = False
) -> bool:
"""Change bot mode."""
mode_key = format(switch_mode, "b") + format(inverse, "b")
strength_key = f"{strength:0{2}x}" # to hex with padding to double digit
result = await self._send_command(DEVICE_SET_MODE_KEY + strength_key + mode_key)
return self._check_command_result(result, 0, {1})
@update_after_operation
async def set_long_press(self, duration: int = 0) -> bool:
"""Set bot long press duration."""
duration_key = f"{duration:0{2}x}" # to hex with padding to double digit
result = await self._send_command(DEVICE_SET_EXTENDED_KEY + "08" + duration_key)
return self._check_command_result(result, 0, {1})
async def get_basic_info(self) -> dict[str, Any] | None:
"""Get device basic settings."""
if not (_data := await self._get_basic_info()):
return None
return {
"battery": _data[1],
"firmware": _data[2] / 10.0,
"strength": _data[3],
"timers": _data[8],
"switchMode": bool(_data[9] & 16),
"inverseDirection": bool(_data[9] & 1),
"holdSeconds": _data[10],
}
def is_on(self) -> bool | None:
"""Return switch state from cache."""
# To get actual position call update() first.
value = self._get_adv_value("isOn")
if value is None:
return None
if self._inverse:
return not value
return value
|