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 .helpers.config import async_tuya_setup_platform
  7. from .helpers.device_config import TuyaEntityConfig
  8. from .helpers.mixin import TuyaLocalEntity
  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)