datetime.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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. day = day or 1
  82. hours = hours or 0
  83. minutes = minutes or 0
  84. seconds = seconds or 0
  85. delta = timedelta(
  86. years=int(year) - 1970,
  87. months=int(month) - 1,
  88. days=int(day) - 1,
  89. hours=int(hours),
  90. minutes=int(minutes),
  91. seconds=int(seconds),
  92. )
  93. return (EPOCH.astimezone(tz) + delta).datetime()
  94. async def async_set_value(self, value: datetime):
  95. """Set the datetime."""
  96. settings = {}
  97. # Use Local time if split into components
  98. if self._year_dps:
  99. tz = time.now().astimezone().tzinfo
  100. value = value.astimezone(tz)
  101. year = value.year
  102. month = value.month
  103. day = value.day
  104. hour = value.hour
  105. minute = value.minute
  106. second = value.second
  107. if self._year_dps:
  108. settings.update(
  109. self._year_dps.get_values_to_set(self._device, year, settings)
  110. )
  111. month = month + (year - 1970) * 12
  112. if self._month_dps:
  113. settings.update(
  114. self._month_dps.get_values_to_set(self._device, month, settings)
  115. )
  116. else:
  117. if self._year_dps is None:
  118. from_year = 1970
  119. else:
  120. from_year = value.year
  121. day = (
  122. day
  123. + (
  124. datetime(value.year, value.month, 1) - datetime(from_year, 1, 1)
  125. ).days
  126. - 1
  127. )
  128. if self._day_dps:
  129. settings.update(
  130. self._day_dps.get_values_to_set(self._device, day, settings)
  131. )
  132. else:
  133. hour = hour + day * 24
  134. if self._hour_dps:
  135. settings.update(
  136. self._hour_dps.get_values_to_set(self._device, hour, settings)
  137. )
  138. else:
  139. minute = minute + hour * 60
  140. if self._minute_dps:
  141. settings.update(
  142. self._minute_dps.get_values_to_set(self._device, minute, settings)
  143. )
  144. else:
  145. second = second + minute * 60
  146. if self._second_dps:
  147. settings.update(
  148. self._second_dps.get_values_to_set(self._device, second, settings)
  149. )
  150. else:
  151. _LOGGER.debug(
  152. "%s: Discarding unused precision: %d seconds",
  153. self.name,
  154. second,
  155. )
  156. await self._device.async_set_properties(settings)