art_frame.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. """Device handler for the Art Frame."""
  2. import logging
  3. from typing import Any
  4. from ..const import SwitchbotModel
  5. from .device import (
  6. SwitchbotEncryptedDevice,
  7. SwitchbotSequenceDevice,
  8. update_after_operation,
  9. )
  10. _LOGGER = logging.getLogger(__name__)
  11. COMMAND_SET_IMAGE = "570F7A02{}"
  12. class SwitchbotArtFrame(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
  13. """Representation of a Switchbot Art Frame."""
  14. _model = SwitchbotModel.ART_FRAME
  15. response_flag: bool = True
  16. async def get_basic_info(self) -> dict[str, Any] | None:
  17. """Get device basic settings."""
  18. if not (_data := await self._get_basic_info()):
  19. return None
  20. _LOGGER.debug("basic info data: %s", _data.hex())
  21. battery_charging = bool(_data[1] & 0x80)
  22. battery = _data[1] & 0x7F
  23. firmware = _data[2] / 10.0
  24. hardware = _data[3]
  25. display_size = (_data[4] >> 4) & 0x0F
  26. display_mode = (_data[4] >> 3) & 0x01
  27. last_network_status = (_data[4] >> 2) & 0x01
  28. current_image_index = _data[5]
  29. total_num_of_images = _data[6]
  30. all_images_index = [_data[x] for x in range(7, 7 + total_num_of_images)]
  31. basic_info = {
  32. "battery_charging": battery_charging,
  33. "battery": battery,
  34. "firmware": firmware,
  35. "hardware": hardware,
  36. "display_size": display_size,
  37. "display_mode": display_mode,
  38. "last_network_status": last_network_status,
  39. "current_image_index": current_image_index,
  40. "total_num_of_images": total_num_of_images,
  41. "all_images_index": all_images_index,
  42. }
  43. _LOGGER.debug("Art Frame %s basic info: %s", self._device.address, basic_info)
  44. return basic_info
  45. def _select_image_index(self, offset: int) -> int:
  46. """Select the image index based on the current index and offset."""
  47. current_index = self.get_current_image_index()
  48. all_images_index = self.get_all_images_index()
  49. if not all_images_index or len(all_images_index) <= 1:
  50. raise RuntimeError("No images available to select from.")
  51. new_position = (all_images_index.index(current_index) + offset) % len(
  52. all_images_index
  53. )
  54. return all_images_index[new_position]
  55. async def _get_current_image_index(self) -> None:
  56. """Validate the current image index."""
  57. if not await self.get_basic_info():
  58. raise RuntimeError("Failed to retrieve basic info for current image index.")
  59. @update_after_operation
  60. async def next_image(self) -> bool:
  61. """Display the next image."""
  62. await self._get_current_image_index()
  63. idx = self._select_image_index(1)
  64. result = await self._send_command(COMMAND_SET_IMAGE.format(f"{idx:02X}"))
  65. return self._check_command_result(result, 0, {1})
  66. @update_after_operation
  67. async def prev_image(self) -> bool:
  68. """Display the previous image."""
  69. await self._get_current_image_index()
  70. idx = self._select_image_index(-1)
  71. result = await self._send_command(COMMAND_SET_IMAGE.format(f"{idx:02X}"))
  72. return self._check_command_result(result, 0, {1})
  73. @update_after_operation
  74. async def set_image(self, index: int) -> bool:
  75. """Set the image by index."""
  76. await self._get_current_image_index()
  77. total_images = self.get_total_images()
  78. if index < 0 or index >= total_images:
  79. raise ValueError(
  80. f"Image index {index} is out of range. Total images: {total_images}."
  81. )
  82. all_images_index = self.get_all_images_index()
  83. img_index = all_images_index[index]
  84. result = await self._send_command(COMMAND_SET_IMAGE.format(f"{img_index:02X}"))
  85. return self._check_command_result(result, 0, {1})
  86. def get_all_images_index(self) -> list[int] | None:
  87. """Return cached list of all image indexes."""
  88. return self._get_adv_value("all_images_index")
  89. def get_current_image_index(self) -> int | None:
  90. """Return cached current image index."""
  91. return self._get_adv_value("current_image_index")
  92. def get_total_images(self) -> int | None:
  93. """Return cached total number of images."""
  94. return self._get_adv_value("total_num_of_images")