light_strip.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. from __future__ import annotations
  2. import logging
  3. from typing import Any
  4. from bleak.backends.device import BLEDevice
  5. from ..const import SwitchbotModel
  6. from ..const.light import StripLightColorMode
  7. from .base_light import SwitchbotSequenceBaseLight
  8. from .device import (
  9. REQ_HEADER,
  10. SwitchbotEncryptedDevice,
  11. SwitchbotOperationError,
  12. update_after_operation,
  13. )
  14. STRIP_COMMMAND_HEADER = "4901"
  15. STRIP_REQUEST = f"{REQ_HEADER}4A01"
  16. STRIP_COMMAND = f"{REQ_HEADER}{STRIP_COMMMAND_HEADER}"
  17. # Strip keys
  18. STRIP_ON_KEY = f"{STRIP_COMMAND}01"
  19. STRIP_OFF_KEY = f"{STRIP_COMMAND}02"
  20. COLOR_TEMP_KEY = f"{STRIP_COMMAND}11"
  21. RGB_BRIGHTNESS_KEY = f"{STRIP_COMMAND}12"
  22. BRIGHTNESS_KEY = f"{STRIP_COMMAND}14"
  23. DEVICE_GET_VERSION_KEY = "570003"
  24. _LOGGER = logging.getLogger(__name__)
  25. EFFECT_DICT = {
  26. "Christmas": [
  27. "570F49070200033C01",
  28. "570F490701000600009902006D0EFF0021",
  29. "570F490701000603009902006D0EFF0021",
  30. ],
  31. "Halloween": ["570F49070200053C04", "570F490701000300FF6A009E00ED00EA0F"],
  32. "Sunset": [
  33. "570F49070200033C3C",
  34. "570F490701000900FF9000ED8C04DD5800",
  35. "570F490701000903FF2E008E0B004F0500",
  36. "570F4907010009063F0010270056140033",
  37. ],
  38. "Vitality": [
  39. "570F49070200053C02",
  40. "570F490701000600C5003FD9530AEC9800",
  41. "570F490701000603FFDF0000895500468B",
  42. ],
  43. "Flashing": [
  44. "570F49070200053C02",
  45. "570F4907010006000000FF00FF00FF0000",
  46. "570F490701000603FFFF0000FFFFA020F0",
  47. ],
  48. "Strobe": ["570F49070200043C02", "570F490701000300FF00E19D70FFFF0515"],
  49. "Fade": [
  50. "570F49070200043C04",
  51. "570F490701000500FF5481FF00E19D70FF",
  52. "570F490701000503FF0515FF7FEB",
  53. ],
  54. "Smooth": [
  55. "570F49070200033C02",
  56. "570F4907010007000036FC00F6FF00ED13",
  57. "570F490701000703F6FF00FF8300FF0800",
  58. "570F490701000706FF00E1",
  59. ],
  60. "Forest": [
  61. "570F49070200033C06",
  62. "570F490701000400006400228B223CB371",
  63. "570F49070100040390EE90",
  64. ],
  65. "Ocean": [
  66. "570F49070200033C06",
  67. "570F4907010007004400FF0061FF007BFF",
  68. "570F490701000703009DFF00B2FF00CBFF",
  69. "570F49070100070600E9FF",
  70. ],
  71. "Autumn": [
  72. "570F49070200043C05",
  73. "570F490701000700D10035922D13A16501",
  74. "570F490701000703AB9100DD8C00F4AA29",
  75. "570F490701000706E8D000",
  76. ],
  77. "Cool": [
  78. "570F49070200043C04",
  79. "570F490701000600001A63006C9A00468B",
  80. "570F490701000603009DA50089BE4378B6",
  81. ],
  82. "Flow": [
  83. "570F49070200033C02",
  84. "570F490701000600FF00D8E100FFAA00FF",
  85. "570F4907010006037F00FF5000FF1900FF",
  86. ],
  87. "Relax": [
  88. "570F49070200033C03",
  89. "570F490701000400FF8C00FF7200FF1D00",
  90. "570F490701000403FF5500",
  91. ],
  92. "Modern": [
  93. "570F49070200043C03",
  94. "570F49070100060089231A5F8969829E5A",
  95. "570F490701000603BCB05EEDBE5AFF9D60",
  96. ],
  97. "Rose": [
  98. "570F49070200043C04",
  99. "570F490701000500FF1969BC215F7C0225",
  100. "570F490701000503600C2B35040C",
  101. ],
  102. }
  103. class SwitchbotLightStrip(SwitchbotSequenceBaseLight):
  104. """Representation of a Switchbot light strip."""
  105. @property
  106. def color_modes(self) -> set[StripLightColorMode]:
  107. """Return the supported color modes."""
  108. return {StripLightColorMode.RGB}
  109. @property
  110. def color_mode(self) -> StripLightColorMode:
  111. """Return the current color mode."""
  112. return StripLightColorMode(self._get_adv_value("color_mode") or 10)
  113. @property
  114. def get_effect_list(self) -> list[str]:
  115. """Return the list of supported effects."""
  116. return list(EFFECT_DICT.keys())
  117. @update_after_operation
  118. async def turn_on(self) -> bool:
  119. """Turn device on."""
  120. result = await self._send_command(STRIP_ON_KEY)
  121. return self._check_command_result(result, 0, {1})
  122. @update_after_operation
  123. async def turn_off(self) -> bool:
  124. """Turn device off."""
  125. result = await self._send_command(STRIP_OFF_KEY)
  126. return self._check_command_result(result, 0, {1})
  127. @update_after_operation
  128. async def set_brightness(self, brightness: int) -> bool:
  129. """Set brightness."""
  130. assert 0 <= brightness <= 100, "Brightness must be between 0 and 100"
  131. result = await self._send_command(f"{BRIGHTNESS_KEY}{brightness:02X}")
  132. return self._check_command_result(result, 0, {1})
  133. @update_after_operation
  134. async def set_rgb(self, brightness: int, r: int, g: int, b: int) -> bool:
  135. """Set rgb."""
  136. assert 0 <= brightness <= 100, "Brightness must be between 0 and 100"
  137. assert 0 <= r <= 255, "r must be between 0 and 255"
  138. assert 0 <= g <= 255, "g must be between 0 and 255"
  139. assert 0 <= b <= 255, "b must be between 0 and 255"
  140. result = await self._send_command(
  141. f"{RGB_BRIGHTNESS_KEY}{brightness:02X}{r:02X}{g:02X}{b:02X}"
  142. )
  143. return self._check_command_result(result, 0, {1})
  144. @update_after_operation
  145. async def set_effect(self, effect: str) -> bool:
  146. """Set effect."""
  147. effect_template = EFFECT_DICT.get(effect)
  148. if not effect_template:
  149. raise SwitchbotOperationError(f"Effect {effect} not supported")
  150. result = await self._send_multiple_commands(effect_template)
  151. if result:
  152. self._override_state({"effect": effect})
  153. return result
  154. async def get_basic_info(
  155. self,
  156. device_get_basic_info: str = STRIP_REQUEST,
  157. device_get_version_info: str = DEVICE_GET_VERSION_KEY,
  158. ) -> dict[str, Any] | None:
  159. """Get device basic settings."""
  160. if not (_data := await self._get_basic_info(device_get_basic_info)):
  161. return None
  162. if not (_version_info := await self._get_basic_info(device_get_version_info)):
  163. return None
  164. _LOGGER.debug(
  165. "data: %s, version info: %s, address: %s",
  166. _data,
  167. _version_info,
  168. self._device.address,
  169. )
  170. self._state["r"] = _data[3]
  171. self._state["g"] = _data[4]
  172. self._state["b"] = _data[5]
  173. self._state["cw"] = int.from_bytes(_data[7:9], "big")
  174. return {
  175. "isOn": bool(_data[1] & 0b10000000),
  176. "brightness": _data[2] & 0b01111111,
  177. "r": self._state["r"],
  178. "g": self._state["g"],
  179. "b": self._state["b"],
  180. "cw": self._state["cw"],
  181. "color_mode": _data[10] & 0b00001111,
  182. "firmware": _version_info[2] / 10.0,
  183. }
  184. class SwitchbotStripLight3(SwitchbotEncryptedDevice, SwitchbotLightStrip):
  185. """Support for switchbot strip light3 and floor lamp."""
  186. def __init__(
  187. self,
  188. device: BLEDevice,
  189. key_id: str,
  190. encryption_key: str,
  191. interface: int = 0,
  192. model: SwitchbotModel = SwitchbotModel.STRIP_LIGHT_3,
  193. **kwargs: Any,
  194. ) -> None:
  195. super().__init__(device, key_id, encryption_key, model, interface, **kwargs)
  196. @classmethod
  197. async def verify_encryption_key(
  198. cls,
  199. device: BLEDevice,
  200. key_id: str,
  201. encryption_key: str,
  202. model: SwitchbotModel = SwitchbotModel.STRIP_LIGHT_3,
  203. **kwargs: Any,
  204. ) -> bool:
  205. return await super().verify_encryption_key(
  206. device, key_id, encryption_key, model, **kwargs
  207. )
  208. @property
  209. def color_modes(self) -> set[StripLightColorMode]:
  210. """Return the supported color modes."""
  211. return {StripLightColorMode.RGB, StripLightColorMode.COLOR_TEMP}
  212. @update_after_operation
  213. async def set_color_temp(self, brightness: int, color_temp: int) -> bool:
  214. """Set color temp."""
  215. assert 0 <= brightness <= 100
  216. assert self.min_temp <= color_temp <= self.max_temp
  217. result = await self._send_command(
  218. f"{COLOR_TEMP_KEY}{brightness:02X}{color_temp:04X}"
  219. )
  220. return self._check_command_result(result, 0, {1})