datetime.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. """
  2. Setup for Tuya datetime entities
  3. """
  4. import logging
  5. import time
  6. from datetime import datetime, timedelta, timezone
  7. from homeassistant.components.datetime import DateTimeEntity
  8. from .device import TuyaLocalDevice
  9. from .entity import TuyaLocalEntity
  10. from .helpers.config import async_tuya_setup_platform
  11. from .helpers.device_config import TuyaEntityConfig
  12. _LOGGER = logging.getLogger(__name__)
  13. EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
  14. async def async_setup_entry(hass, config_entry, async_add_entities):
  15. config = {**config_entry.data, **config_entry.options}
  16. await async_tuya_setup_platform(
  17. hass,
  18. async_add_entities,
  19. config,
  20. "datetime",
  21. TuyaLocalDateTime,
  22. )
  23. class TuyaLocalDateTime(TuyaLocalEntity, DateTimeEntity):
  24. """Representation of a Tuya DateTime"""
  25. def __init__(self, device: TuyaLocalDevice, config: TuyaEntityConfig):
  26. """
  27. Initialise the datetime entity.
  28. Args:
  29. device (TuyaLocalDevice): the device API instance
  30. config (TuyaEntityConfig): the configuration for this entity
  31. """
  32. super().__init__()
  33. dps_map = self._init_begin(device, config)
  34. self._year_dps = dps_map.pop("year", None)
  35. self._month_dps = dps_map.pop("month", None)
  36. self._day_dps = dps_map.pop("day", None)
  37. self._hour_dps = dps_map.pop("hour", None)
  38. self._minute_dps = dps_map.pop("minute", None)
  39. self._second_dps = dps_map.pop("second", None)
  40. if (
  41. self._year_dps is None
  42. and self._month_dps is None
  43. and self._day_dps is None
  44. and self._hour_dps is None
  45. and self._minute_dps is None
  46. and self._second_dps is None
  47. ):
  48. raise AttributeError(
  49. f"{config.config_id} is missing an hour, minute or second dp"
  50. )
  51. self._init_end(dps_map)
  52. @property
  53. def native_value(self):
  54. """Return the current value of the time."""
  55. year = month = day = hours = minutes = seconds = None
  56. tz = timezone.utc
  57. if self._year_dps:
  58. year = self._year_dps.get_value(self._device)
  59. tz = time.now().astimezone().tzinfo
  60. if self._month_dps:
  61. month = self._month_dps.get_value(self._device)
  62. if self._day_dps:
  63. day = self._day_dps.get_value(self._device)
  64. if self._hour_dps:
  65. hours = self._hour_dps.get_value(self._device)
  66. if self._minute_dps:
  67. minutes = self._minute_dps.get_value(self._device)
  68. if self._second_dps:
  69. seconds = self._second_dps.get_value(self._device)
  70. if (
  71. year is None
  72. and month is None
  73. and day is None
  74. and hours is None
  75. and minutes is None
  76. and seconds is None
  77. ):
  78. return None
  79. year = year or 1970
  80. month = month or 1
  81. days = (day or 1) - 1
  82. hours = hours or 0
  83. minutes = minutes or 0
  84. seconds = seconds or 0
  85. delta = timedelta(
  86. days=int(days),
  87. hours=int(hours),
  88. minutes=int(minutes),
  89. seconds=int(seconds),
  90. )
  91. return datetime(year=year, month=month, day=1, tzinfo=tz) + delta
  92. async def async_set_value(self, value: datetime):
  93. """Set the datetime."""
  94. async with self._device.set_lock:
  95. return await self._async_set_value_locked(value)
  96. async def _async_set_value_locked(self, value: datetime):
  97. settings = {}
  98. # Use Local time if split into components
  99. if self._year_dps:
  100. tz = time.now().astimezone().tzinfo
  101. value = value.astimezone(tz)
  102. year = value.year
  103. month = value.month
  104. day = value.day
  105. hour = value.hour
  106. minute = value.minute
  107. second = value.second
  108. if self._year_dps:
  109. _LOGGER.info("%s setting year to %d", self.name, year)
  110. settings.update(
  111. self._year_dps.get_values_to_set(self._device, year, settings)
  112. )
  113. month = month + (year - 1970) * 12
  114. if self._month_dps:
  115. _LOGGER.info("%s setting month to %d", self.name, month)
  116. settings.update(
  117. self._month_dps.get_values_to_set(self._device, month, settings)
  118. )
  119. else:
  120. if self._year_dps is None:
  121. from_year = 1970
  122. else:
  123. from_year = value.year
  124. day = (
  125. day
  126. + (
  127. datetime(value.year, value.month, 1) - datetime(from_year, 1, 1)
  128. ).days
  129. - 1
  130. )
  131. if self._day_dps:
  132. _LOGGER.info("%s setting day to %d", self.name, day)
  133. settings.update(
  134. self._day_dps.get_values_to_set(self._device, day, settings)
  135. )
  136. else:
  137. hour = hour + day * 24
  138. if self._hour_dps:
  139. _LOGGER.info("%s setting hour to %d", self.name, hour)
  140. settings.update(
  141. self._hour_dps.get_values_to_set(self._device, hour, settings)
  142. )
  143. else:
  144. minute = minute + hour * 60
  145. if self._minute_dps:
  146. _LOGGER.info("%s setting minute to %d", self.name, minute)
  147. settings.update(
  148. self._minute_dps.get_values_to_set(self._device, minute, settings)
  149. )
  150. else:
  151. second = second + minute * 60
  152. if self._second_dps:
  153. _LOGGER.info("%s setting second to %d", self.name, second)
  154. settings.update(
  155. self._second_dps.get_values_to_set(self._device, second, settings)
  156. )
  157. else:
  158. _LOGGER.debug(
  159. "%s: Discarding unused precision: %d seconds",
  160. self.name,
  161. second,
  162. )
  163. await self._device.async_set_properties(settings)