fan.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. """Library to handle connection with Switchbot."""
  2. from __future__ import annotations
  3. import logging
  4. from typing import Any
  5. from ..const.fan import FanMode
  6. from .device import (
  7. DEVICE_GET_BASIC_SETTINGS_KEY,
  8. SwitchbotSequenceDevice,
  9. update_after_operation,
  10. )
  11. _LOGGER = logging.getLogger(__name__)
  12. COMMAND_HEAD = "570f41"
  13. COMMAND_TURN_ON = f"{COMMAND_HEAD}0101"
  14. COMMAND_TURN_OFF = f"{COMMAND_HEAD}0102"
  15. COMMAND_START_OSCILLATION = f"{COMMAND_HEAD}020101ff"
  16. COMMAND_STOP_OSCILLATION = f"{COMMAND_HEAD}020102ff"
  17. COMMAND_SET_MODE = {
  18. FanMode.NORMAL.name: f"{COMMAND_HEAD}030101ff",
  19. FanMode.NATURAL.name: f"{COMMAND_HEAD}030102ff",
  20. FanMode.SLEEP.name: f"{COMMAND_HEAD}030103",
  21. FanMode.BABY.name: f"{COMMAND_HEAD}030104",
  22. }
  23. COMMAND_SET_PERCENTAGE = f"{COMMAND_HEAD}0302" # +speed
  24. COMMAND_GET_BASIC_INFO = "570f428102"
  25. class SwitchbotFan(SwitchbotSequenceDevice):
  26. """Representation of a Switchbot Circulator Fan."""
  27. def __init__(self, device, password=None, interface=0, **kwargs):
  28. super().__init__(device, password, interface, **kwargs)
  29. async def get_basic_info(self) -> dict[str, Any] | None:
  30. """Get device basic settings."""
  31. if not (_data := await self._get_basic_info(COMMAND_GET_BASIC_INFO)):
  32. return None
  33. if not (_data1 := await self._get_basic_info(DEVICE_GET_BASIC_SETTINGS_KEY)):
  34. return None
  35. _LOGGER.debug("data: %s", _data)
  36. battery = _data[2] & 0b01111111
  37. isOn = bool(_data[3] & 0b10000000)
  38. oscillating = bool(_data[3] & 0b01100000)
  39. _mode = _data[8] & 0b00000111
  40. mode = FanMode(_mode).name if 1 <= _mode <= 4 else None
  41. speed = _data[9]
  42. firmware = _data1[2] / 10.0
  43. return {
  44. "battery": battery,
  45. "isOn": isOn,
  46. "oscillating": oscillating,
  47. "mode": mode,
  48. "speed": speed,
  49. "firmware": firmware,
  50. }
  51. async def _get_basic_info(self, cmd: str) -> bytes | None:
  52. """Return basic info of device."""
  53. _data = await self._send_command(key=cmd, retry=self._retry_count)
  54. if _data in (b"\x07", b"\x00"):
  55. _LOGGER.error("Unsuccessful, please try again")
  56. return None
  57. return _data
  58. @update_after_operation
  59. async def set_preset_mode(self, preset_mode: str) -> bool:
  60. """Send command to set fan preset_mode."""
  61. return await self._send_command(COMMAND_SET_MODE[preset_mode])
  62. @update_after_operation
  63. async def set_percentage(self, percentage: int) -> bool:
  64. """Send command to set fan percentage."""
  65. return await self._send_command(f"{COMMAND_SET_PERCENTAGE}{percentage:02X}")
  66. @update_after_operation
  67. async def set_oscillation(self, oscillating: bool) -> bool:
  68. """Send command to set fan oscillation"""
  69. if oscillating:
  70. return await self._send_command(COMMAND_START_OSCILLATION)
  71. else:
  72. return await self._send_command(COMMAND_STOP_OSCILLATION)
  73. @update_after_operation
  74. async def turn_on(self) -> bool:
  75. """Turn on the fan."""
  76. return await self._send_command(COMMAND_TURN_ON)
  77. @update_after_operation
  78. async def turn_off(self) -> bool:
  79. """Turn off the fan."""
  80. return await self._send_command(COMMAND_TURN_OFF)
  81. def get_current_percentage(self) -> Any:
  82. """Return cached percentage."""
  83. return self._get_adv_value("speed")
  84. def is_on(self) -> bool | None:
  85. """Return fan state from cache."""
  86. return self._get_adv_value("isOn")
  87. def get_oscillating_state(self) -> Any:
  88. """Return cached oscillating."""
  89. return self._get_adv_value("oscillating")
  90. def get_current_mode(self) -> Any:
  91. """Return cached mode."""
  92. return self._get_adv_value("mode")