lock.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. """
  2. Setup for different kinds of Tuya lock devices
  3. """
  4. from homeassistant.components.lock import LockEntity, LockEntityFeature
  5. from .device import TuyaLocalDevice
  6. from .entity import TuyaLocalEntity
  7. from .helpers.config import async_tuya_setup_platform
  8. from .helpers.device_config import TuyaEntityConfig
  9. async def async_setup_entry(hass, config_entry, async_add_entities):
  10. config = {**config_entry.data, **config_entry.options}
  11. await async_tuya_setup_platform(
  12. hass,
  13. async_add_entities,
  14. config,
  15. "lock",
  16. TuyaLocalLock,
  17. )
  18. class TuyaLocalLock(TuyaLocalEntity, LockEntity):
  19. """Representation of a Tuya Wi-Fi connected lock."""
  20. def __init__(self, device: TuyaLocalDevice, config: TuyaEntityConfig):
  21. """
  22. Initialise the lock.
  23. Args:
  24. device (TuyaLocalDevice): The device API instance.
  25. config (TuyaEntityConfig): The configuration for this entity.
  26. """
  27. super().__init__()
  28. dps_map = self._init_begin(device, config)
  29. self._lock_dp = dps_map.pop("lock", None)
  30. self._lock_state_dp = dps_map.pop("lock_state", None)
  31. self._open_dp = dps_map.pop("open", None)
  32. self._unlock_fp_dp = dps_map.pop("unlock_fingerprint", None)
  33. self._unlock_pw_dp = dps_map.pop("unlock_password", None)
  34. self._unlock_tmppw_dp = dps_map.pop("unlock_temp_pwd", None)
  35. self._unlock_dynpw_dp = dps_map.pop("unlock_dynamic_pwd", None)
  36. self._unlock_offlinepw_dp = dps_map.pop("unlock_offline_pwd", None)
  37. self._unlock_card_dp = dps_map.pop("unlock_card", None)
  38. self._unlock_app_dp = dps_map.pop("unlock_app", None)
  39. self._unlock_key_dp = dps_map.pop("unlock_key", None)
  40. self._unlock_ble_dp = dps_map.pop("unlock_ble", None)
  41. self._unlock_voice_dp = dps_map.pop("unlock_voice", None)
  42. self._unlock_face_dp = dps_map.pop("unlock_face", None)
  43. self._unlock_multi_dp = dps_map.pop("unlock_multi", None)
  44. self._unlock_ibeacon_dp = dps_map.pop("unlock_ibeacon", None)
  45. self._req_unlock_dp = dps_map.pop("request_unlock", None)
  46. self._approve_unlock_dp = dps_map.pop("approve_unlock", None)
  47. self._req_intercom_dp = dps_map.pop("request_intercom", None)
  48. self._approve_intercom_dp = dps_map.pop("approve_intercom", None)
  49. self._jam_dp = dps_map.pop("jammed", None)
  50. self._init_end(dps_map)
  51. if self._open_dp and not self._open_dp.readonly:
  52. self._attr_supported_features = LockEntityFeature.OPEN
  53. @property
  54. def is_locked(self):
  55. """Return the a boolean representing whether the lock is locked."""
  56. lock = None
  57. if self._lock_state_dp:
  58. lock = self._lock_state_dp.get_value(self._device)
  59. if lock is None and self._lock_dp:
  60. lock = self._lock_dp.get_value(self._device)
  61. if lock is None:
  62. for d in (
  63. self._unlock_card_dp,
  64. self._unlock_dynpw_dp,
  65. self._unlock_fp_dp,
  66. self._unlock_offlinepw_dp,
  67. self._unlock_pw_dp,
  68. self._unlock_tmppw_dp,
  69. self._unlock_app_dp,
  70. self._unlock_key_dp,
  71. self._unlock_ble_dp,
  72. self._unlock_voice_dp,
  73. self._unlock_face_dp,
  74. self._unlock_multi_dp,
  75. self._unlock_ibeacon_dp,
  76. ):
  77. if d:
  78. if d.get_value(self._device):
  79. lock = False
  80. elif lock is None:
  81. lock = True
  82. return lock
  83. @property
  84. def is_open(self):
  85. if self._open_dp:
  86. return self._open_dp.get_value(self._device)
  87. @property
  88. def is_jammed(self):
  89. if self._jam_dp:
  90. return self._jam_dp.get_value(self._device)
  91. def unlocker_id(self, dp, type):
  92. if dp:
  93. unlock = dp.get_value(self._device)
  94. if unlock:
  95. if unlock is True:
  96. return f"{type}"
  97. else:
  98. return f"{type} #{unlock}"
  99. @property
  100. def changed_by(self):
  101. for dp, desc in {
  102. self._unlock_app_dp: "App",
  103. self._unlock_ble_dp: "Bluetooth",
  104. self._unlock_card_dp: "Card",
  105. self._unlock_dynpw_dp: "Dynamic Password",
  106. self._unlock_fp_dp: "Finger",
  107. self._unlock_key_dp: "Key",
  108. self._unlock_offlinepw_dp: "Offline Password",
  109. self._unlock_pw_dp: "Password",
  110. self._unlock_tmppw_dp: "Temporary Password",
  111. self._unlock_voice_dp: "Voice",
  112. self._unlock_face_dp: "Face",
  113. self._unlock_multi_dp: "Multifactor",
  114. self._unlock_ibeacon_dp: "iBeacon",
  115. }.items():
  116. by = self.unlocker_id(dp, desc)
  117. if by:
  118. # clear non-persistent dps immediately on reporting, instead
  119. # of waiting for the next poll, to make the lock more responsive
  120. # to multiple attempts
  121. if not dp.persist:
  122. self._device._cached_state.pop(dp.id, None)
  123. return by
  124. async def async_lock(self, **kwargs):
  125. """Lock the lock."""
  126. if self._lock_dp:
  127. await self._lock_dp.async_set_value(self._device, True)
  128. else:
  129. raise NotImplementedError()
  130. async def async_unlock(self, **kwargs):
  131. """Unlock the lock."""
  132. if self._lock_dp:
  133. await self._lock_dp.async_set_value(self._device, False)
  134. elif self._approve_unlock_dp:
  135. if self._req_unlock_dp and not self._req_unlock_dp.get_value(self._device):
  136. raise TimeoutError()
  137. await self._approve_unlock_dp.async_set_value(self._device, True)
  138. elif self._approve_intercom_dp:
  139. if self._req_intercom_dp and not self._req_intercom_dp.get_value(
  140. self._device
  141. ):
  142. raise TimeoutError()
  143. await self._approve_intercom_dp.async_set_value(self._device, True)
  144. else:
  145. raise NotImplementedError()
  146. async def async_open(self, **kwargs):
  147. """Open the door latch."""
  148. if self._open_dp:
  149. await self._open_dp.async_set_value(self._device, True)