lock.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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._open_dp = dps_map.pop("open", None)
  31. self._unlock_fp_dp = dps_map.pop("unlock_fingerprint", None)
  32. self._unlock_pw_dp = dps_map.pop("unlock_password", None)
  33. self._unlock_tmppw_dp = dps_map.pop("unlock_temp_pwd", None)
  34. self._unlock_dynpw_dp = dps_map.pop("unlock_dynamic_pwd", None)
  35. self._unlock_offlinepw_dp = dps_map.pop("unlock_offline_pwd", None)
  36. self._unlock_card_dp = dps_map.pop("unlock_card", None)
  37. self._unlock_app_dp = dps_map.pop("unlock_app", None)
  38. self._unlock_key_dp = dps_map.pop("unlock_key", None)
  39. self._unlock_ble_dp = dps_map.pop("unlock_ble", None)
  40. self._unlock_voice_dp = dps_map.pop("unlock_voice", None)
  41. self._unlock_face_dp = dps_map.pop("unlock_face", None)
  42. self._unlock_multi_dp = dps_map.pop("unlock_multi", None)
  43. self._unlock_ibeacon_dp = dps_map.pop("unlock_ibeacon", None)
  44. self._req_unlock_dp = dps_map.pop("request_unlock", None)
  45. self._approve_unlock_dp = dps_map.pop("approve_unlock", None)
  46. self._req_intercom_dp = dps_map.pop("request_intercom", None)
  47. self._approve_intercom_dp = dps_map.pop("approve_intercom", None)
  48. self._jam_dp = dps_map.pop("jammed", None)
  49. self._init_end(dps_map)
  50. if self._open_dp and not self._open_dp.readonly:
  51. self._attr_supported_features = LockEntityFeature.OPEN
  52. @property
  53. def is_locked(self):
  54. """Return the a boolean representing whether the lock is locked."""
  55. lock = None
  56. if self._lock_dp:
  57. lock = self._lock_dp.get_value(self._device)
  58. else:
  59. for d in (
  60. self._unlock_card_dp,
  61. self._unlock_dynpw_dp,
  62. self._unlock_fp_dp,
  63. self._unlock_offlinepw_dp,
  64. self._unlock_pw_dp,
  65. self._unlock_tmppw_dp,
  66. self._unlock_app_dp,
  67. self._unlock_key_dp,
  68. self._unlock_ble_dp,
  69. self._unlock_voice_dp,
  70. self._unlock_face_dp,
  71. self._unlock_multi_dp,
  72. self._unlock_ibeacon_dp,
  73. ):
  74. if d:
  75. if d.get_value(self._device):
  76. lock = False
  77. elif lock is None:
  78. lock = True
  79. return lock
  80. @property
  81. def is_open(self):
  82. if self._open_dp:
  83. return self._open_dp.get_value(self._device)
  84. @property
  85. def is_jammed(self):
  86. if self._jam_dp:
  87. return self._jam_dp.get_value(self._device)
  88. def unlocker_id(self, dp, type):
  89. if dp:
  90. unlock = dp.get_value(self._device)
  91. if unlock:
  92. if unlock is True:
  93. return f"{type}"
  94. else:
  95. return f"{type} #{unlock}"
  96. @property
  97. def changed_by(self):
  98. for dp, desc in {
  99. self._unlock_app_dp: "App",
  100. self._unlock_ble_dp: "Bluetooth",
  101. self._unlock_card_dp: "Card",
  102. self._unlock_dynpw_dp: "Dynamic Password",
  103. self._unlock_fp_dp: "Finger",
  104. self._unlock_key_dp: "Key",
  105. self._unlock_offlinepw_dp: "Offline Password",
  106. self._unlock_pw_dp: "Password",
  107. self._unlock_tmppw_dp: "Temporary Password",
  108. self._unlock_voice_dp: "Voice",
  109. self._unlock_face_dp: "Face",
  110. self._unlock_multi_dp: "Multifactor",
  111. self._unlock_ibeacon_dp: "iBeacon",
  112. }.items():
  113. by = self.unlocker_id(dp, desc)
  114. if by:
  115. # clear non-persistent dps immediately on reporting, instead
  116. # of waiting for the next poll, to make the lock more responsive
  117. # to multiple attempts
  118. if not dp.persist:
  119. self._device._cached_state.pop(dp.id, None)
  120. return by
  121. async def async_lock(self, **kwargs):
  122. """Lock the lock."""
  123. if self._lock_dp:
  124. await self._lock_dp.async_set_value(self._device, True)
  125. else:
  126. raise NotImplementedError()
  127. async def async_unlock(self, **kwargs):
  128. """Unlock the lock."""
  129. if self._lock_dp:
  130. await self._lock_dp.async_set_value(self._device, False)
  131. elif self._approve_unlock_dp:
  132. if self._req_unlock_dp and not self._req_unlock_dp.get_value(self._device):
  133. raise TimeoutError()
  134. await self._approve_unlock_dp.async_set_value(self._device, True)
  135. elif self._approve_intercom_dp:
  136. if self._req_intercom_dp and not self._req_intercom_dp.get_value(
  137. self._device
  138. ):
  139. raise TimeoutError()
  140. await self._approve_intercom_dp.async_set_value(self._device, True)
  141. else:
  142. raise NotImplementedError()
  143. async def async_open(self, **kwargs):
  144. """Open the door latch."""
  145. if self._open_dp:
  146. await self._open_dp.async_set_value(self._device, True)