bulb.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. from __future__ import annotations
  2. import asyncio
  3. import logging
  4. from enum import Enum
  5. from typing import Any
  6. from switchbot.models import SwitchBotAdvertisement
  7. from .device import SwitchbotDevice, SwitchbotSequenceDevice
  8. REQ_HEADER = "570f"
  9. BULB_COMMMAND_HEADER = "4701"
  10. BULB_REQUEST = f"{REQ_HEADER}4801"
  11. BULB_COMMAND = f"{REQ_HEADER}{BULB_COMMMAND_HEADER}"
  12. # Bulb keys
  13. BULB_ON_KEY = f"{BULB_COMMAND}01"
  14. BULB_OFF_KEY = f"{BULB_COMMAND}02"
  15. RGB_BRIGHTNESS_KEY = f"{BULB_COMMAND}12"
  16. CW_BRIGHTNESS_KEY = f"{BULB_COMMAND}13"
  17. BRIGHTNESS_KEY = f"{BULB_COMMAND}14"
  18. RGB_KEY = f"{BULB_COMMAND}16"
  19. CW_KEY = f"{BULB_COMMAND}17"
  20. _LOGGER = logging.getLogger(__name__)
  21. from .base_light import SwitchbotBaseLight
  22. from .device import ColorMode
  23. class SwitchbotBulb(SwitchbotBaseLight):
  24. """Representation of a Switchbot bulb."""
  25. def __init__(self, *args: Any, **kwargs: Any) -> None:
  26. """Switchbot bulb constructor."""
  27. super().__init__(*args, **kwargs)
  28. self._state: dict[str, Any] = {}
  29. @property
  30. def color_modes(self) -> set[ColorMode]:
  31. """Return the supported color modes."""
  32. return {ColorMode.RGB, ColorMode.COLOR_TEMP}
  33. async def update(self) -> None:
  34. """Update state of device."""
  35. result = await self._send_command(BULB_REQUEST)
  36. self._update_state(result)
  37. async def turn_on(self) -> bool:
  38. """Turn device on."""
  39. result = await self._send_command(BULB_ON_KEY)
  40. self._update_state(result)
  41. return self._check_command_result(result, 1, {0x80})
  42. async def turn_off(self) -> bool:
  43. """Turn device off."""
  44. result = await self._send_command(BULB_OFF_KEY)
  45. self._update_state(result)
  46. return self._check_command_result(result, 1, {0x00})
  47. async def set_brightness(self, brightness: int) -> bool:
  48. """Set brightness."""
  49. assert 0 <= brightness <= 100, "Brightness must be between 0 and 100"
  50. result = await self._send_command(f"{BRIGHTNESS_KEY}{brightness:02X}")
  51. self._update_state(result)
  52. return self._check_command_result(result, 1, {0x80})
  53. async def set_color_temp(self, brightness: int, color_temp: int) -> bool:
  54. """Set color temp."""
  55. assert 0 <= brightness <= 100, "Brightness must be between 0 and 100"
  56. assert 2700 <= color_temp <= 6500, "Color Temp must be between 0 and 100"
  57. result = await self._send_command(
  58. f"{CW_BRIGHTNESS_KEY}{brightness:02X}{color_temp:04X}"
  59. )
  60. self._update_state(result)
  61. return self._check_command_result(result, 1, {0x80})
  62. async def set_rgb(self, brightness: int, r: int, g: int, b: int) -> bool:
  63. """Set rgb."""
  64. assert 0 <= brightness <= 100, "Brightness must be between 0 and 100"
  65. assert 0 <= r <= 255, "r must be between 0 and 255"
  66. assert 0 <= g <= 255, "g must be between 0 and 255"
  67. assert 0 <= b <= 255, "b must be between 0 and 255"
  68. result = await self._send_command(
  69. f"{RGB_BRIGHTNESS_KEY}{brightness:02X}{r:02X}{g:02X}{b:02X}"
  70. )
  71. self._update_state(result)
  72. return self._check_command_result(result, 1, {0x80})
  73. def _update_state(self, result: bytes | None) -> None:
  74. """Update device state."""
  75. if not result or len(result) < 10:
  76. return
  77. self._state["r"] = result[3]
  78. self._state["g"] = result[4]
  79. self._state["b"] = result[5]
  80. self._state["cw"] = int(result[6:8].hex(), 16)
  81. self._override_adv_data = {
  82. "isOn": result[1] == 0x80,
  83. "color_mode": result[10],
  84. }
  85. _LOGGER.debug(
  86. "%s: Bulb update state: %s = %s", self.name, result.hex(), self._state
  87. )
  88. self._fire_callbacks()