__init__.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. """Library to handle connection with Switchbot"""
  2. import time
  3. import binascii
  4. import logging
  5. import bluepy
  6. DEFAULT_RETRY_COUNT = 3
  7. DEFAULT_RETRY_TIMEOUT = .2
  8. UUID = "cba20d00-224d-11e6-9fb8-0002a5d5c51b"
  9. HANDLE = "cba20002-224d-11e6-9fb8-0002a5d5c51b"
  10. KEY_PASSWORD_PREFIX = "5711"
  11. PRESS_KEY = "570100"
  12. ON_KEY = "570101"
  13. OFF_KEY = "570102"
  14. ON_KEY_SUFFIX = "01"
  15. OFF_KEY_SUFFIX = "02"
  16. PRESS_KEY_SUFFIX = "00"
  17. _LOGGER = logging.getLogger(__name__)
  18. class Switchbot:
  19. """Representation of a Switchbot."""
  20. def __init__(self, mac, retry_count=DEFAULT_RETRY_COUNT, password=None) -> None:
  21. self._mac = mac
  22. self._device = None
  23. self._retry_count = retry_count
  24. if password is None or password == "":
  25. self._password_encoded = None
  26. else:
  27. self._password_encoded = '%x' % (binascii.crc32(password.encode('ascii')) & 0xffffffff)
  28. def _connect(self) -> None:
  29. if self._device is not None:
  30. return
  31. try:
  32. _LOGGER.debug("Connecting to Switchbot...")
  33. self._device = bluepy.btle.Peripheral(self._mac,
  34. bluepy.btle.ADDR_TYPE_RANDOM)
  35. _LOGGER.debug("Connected to Switchbot.")
  36. except bluepy.btle.BTLEException:
  37. _LOGGER.debug("Failed connecting to Switchbot.", exc_info=True)
  38. self._device = None
  39. raise
  40. def _disconnect(self) -> None:
  41. if self._device is None:
  42. return
  43. _LOGGER.debug("Disconnecting")
  44. try:
  45. self._device.disconnect()
  46. except bluepy.btle.BTLEException:
  47. _LOGGER.warning("Error disconnecting from Switchbot.", exc_info=True)
  48. finally:
  49. self._device = None
  50. def _commandkey(self, key) -> str:
  51. if self._password_encoded is None:
  52. return key
  53. key_suffix = PRESS_KEY_SUFFIX
  54. if key == ON_KEY:
  55. key_suffix = ON_KEY_SUFFIX
  56. elif key == OFF_KEY:
  57. key_suffix = OFF_KEY_SUFFIX
  58. return KEY_PASSWORD_PREFIX + self._password_encoded + key_suffix
  59. def _writekey(self, key) -> bool:
  60. _LOGGER.debug("Prepare to send")
  61. hand_service = self._device.getServiceByUUID(UUID)
  62. hand = hand_service.getCharacteristics(HANDLE)[0]
  63. _LOGGER.debug("Sending command, %s", key)
  64. write_result = hand.write(binascii.a2b_hex(key), withResponse=True)
  65. if not write_result:
  66. _LOGGER.error("Sent command but didn't get a response from Switchbot confirming command was sent. "
  67. "Please check the Switchbot.")
  68. else:
  69. _LOGGER.info("Successfully sent command to Switchbot (MAC: %s).", self._mac)
  70. return write_result
  71. def _sendcommand(self, key, retry) -> bool:
  72. send_success = False
  73. command = self._commandkey(key)
  74. _LOGGER.debug("Sending command to switchbot %s", command)
  75. try:
  76. self._connect()
  77. send_success = self._writekey(command)
  78. except bluepy.btle.BTLEException:
  79. _LOGGER.warning("Error talking to Switchbot.", exc_info=True)
  80. finally:
  81. self._disconnect()
  82. if send_success:
  83. return True
  84. if retry < 1:
  85. _LOGGER.error("Switchbot communication failed. Stopping trying.", exc_info=True)
  86. return False
  87. _LOGGER.warning("Cannot connect to Switchbot. Retrying (remaining: %d)...", retry)
  88. time.sleep(DEFAULT_RETRY_TIMEOUT)
  89. return self._sendcommand(key, retry - 1)
  90. def turn_on(self) -> bool:
  91. """Turn device on."""
  92. return self._sendcommand(ON_KEY, self._retry_count)
  93. def turn_off(self) -> bool:
  94. """Turn device off."""
  95. return self._sendcommand(OFF_KEY, self._retry_count)
  96. def press(self) -> bool:
  97. """Press command to device."""
  98. return self._sendcommand(PRESS_KEY, self._retry_count)