bot.py 3.9 KB

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