siren.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. """
  2. Setup for Tuya siren devices
  3. """
  4. import logging
  5. from homeassistant.components.siren import SirenEntity, SirenEntityFeature
  6. from homeassistant.components.siren.const import (
  7. ATTR_DURATION,
  8. ATTR_TONE,
  9. ATTR_VOLUME_LEVEL,
  10. )
  11. from .device import TuyaLocalDevice
  12. from .entity import TuyaLocalEntity
  13. from .helpers.config import async_tuya_setup_platform
  14. from .helpers.device_config import TuyaEntityConfig
  15. _LOGGER = logging.getLogger(__name__)
  16. async def async_setup_entry(hass, config_entry, async_add_entities):
  17. config = {**config_entry.data, **config_entry.options}
  18. await async_tuya_setup_platform(
  19. hass,
  20. async_add_entities,
  21. config,
  22. "siren",
  23. TuyaLocalSiren,
  24. )
  25. class TuyaLocalSiren(TuyaLocalEntity, SirenEntity):
  26. """Representation of a Tuya siren"""
  27. def __init__(self, device: TuyaLocalDevice, config: TuyaEntityConfig):
  28. """
  29. Initialize the siren.
  30. Args:
  31. device (TuyaLocalDevice): The device API instance.
  32. config (TuyaEntityConfig): The config for this entity.
  33. """
  34. super().__init__()
  35. dps_map = self._init_begin(device, config)
  36. self._tone_dp = dps_map.get(ATTR_TONE, None)
  37. self._volume_dp = dps_map.get(ATTR_VOLUME_LEVEL, None)
  38. self._duration_dp = dps_map.get(ATTR_DURATION, None)
  39. self._switch_dp = dps_map.get("switch", None)
  40. self._init_end(dps_map)
  41. # All control of features is through the turn_on service, so we need to
  42. # support that, even if the siren does not support direct control
  43. support = SirenEntityFeature(0)
  44. if self._tone_dp:
  45. support |= (
  46. SirenEntityFeature.TONES
  47. | SirenEntityFeature.TURN_ON
  48. | SirenEntityFeature.TURN_OFF
  49. )
  50. self._attr_available_tones = [
  51. x for x in self._tone_dp.values(device) if x != "off"
  52. ]
  53. self._default_tone = self._tone_dp.default
  54. if self._volume_dp:
  55. support |= SirenEntityFeature.VOLUME_SET
  56. if self._duration_dp:
  57. support |= SirenEntityFeature.DURATION
  58. if self._switch_dp:
  59. support |= SirenEntityFeature.TURN_ON | SirenEntityFeature.TURN_OFF
  60. self._attr_supported_features = support
  61. @property
  62. def is_on(self):
  63. """Return whether the siren is on."""
  64. if self._switch_dp:
  65. return self._switch_dp.get_value(self._device)
  66. if self._tone_dp:
  67. return self._tone_dp.get_value(self._device) != "off"
  68. async def async_turn_on(self, **kwargs) -> None:
  69. await self._async_turn_on_locked(**kwargs)
  70. async def _async_turn_on_locked(self, **kwargs) -> None:
  71. tone = kwargs.get(ATTR_TONE, None)
  72. duration = kwargs.get(ATTR_DURATION, None)
  73. volume = kwargs.get(ATTR_VOLUME_LEVEL, None)
  74. set_dps = {}
  75. if self._tone_dp:
  76. if tone is None and not self._switch_dp:
  77. tone = self._tone_dp.get_value(self._device)
  78. if tone == "off":
  79. tone = self._default_tone
  80. if tone is not None:
  81. _LOGGER.info("%s sending tone %s", self._config.config_id, tone)
  82. set_dps = {
  83. **set_dps,
  84. **self._tone_dp.get_values_to_set(self._device, tone, set_dps),
  85. }
  86. if duration is not None and self._duration_dp:
  87. _LOGGER.info("%s setting duration to %s", self._config.config_id, duration)
  88. set_dps = {
  89. **set_dps,
  90. **self._duration_dp.get_values_to_set(self._device, duration, set_dps),
  91. }
  92. if volume is not None and self._volume_dp:
  93. # Volume is a float, range 0.0-1.0 in Home Assistant
  94. # In tuya it is likely an integer or a fixed list of values.
  95. # For integer, expect scale and step to do the conversion,
  96. # for fixed values, we need to snap to closest value.
  97. if self._volume_dp.values(self._device):
  98. volume = min(
  99. self._volume_dp.values(self._device),
  100. key=lambda x: abs(x - volume),
  101. )
  102. _LOGGER.info("%s setting volume to %s", self._config.config_id, volume)
  103. set_dps = {
  104. **set_dps,
  105. **self._volume_dp.get_values_to_set(self._device, volume, set_dps),
  106. }
  107. if self._switch_dp and not self.is_on:
  108. _LOGGER.info("%s turning on siren", self._config.config_id)
  109. set_dps = {
  110. **set_dps,
  111. **self._switch_dp.get_values_to_set(self._device, True, set_dps),
  112. }
  113. await self._device.async_set_properties(set_dps)
  114. async def async_turn_off(self) -> None:
  115. """Turn off the siren"""
  116. if self._switch_dp:
  117. _LOGGER.info("%s turning off siren", self._config.config_id)
  118. await self._switch_dp.async_set_value(self._device, False)
  119. elif self._tone_dp:
  120. _LOGGER.info("%s setting siren tone to off", self._config.config_id)
  121. await self._tone_dp.async_set_value(self._device, "off")