lock.py 5.9 KB

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