entity.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. """
  2. Common functionality for Tuya Local entities
  3. """
  4. import json
  5. import logging
  6. from homeassistant.const import (
  7. CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
  8. UnitOfArea,
  9. UnitOfTemperature,
  10. )
  11. from homeassistant.helpers.entity import EntityCategory
  12. _LOGGER = logging.getLogger(__name__)
  13. # These attributes should not be included in the extra state attributes
  14. BLACKLISTED_ATTRIBUTES = ["state", "available"]
  15. class TuyaLocalEntity:
  16. """Common functions for all entity types."""
  17. def _init_begin(self, device, config):
  18. self._device = device
  19. self._config = config
  20. self._attr_dps = []
  21. self._attr_translation_key = (
  22. config.translation_key or config.translation_only_key
  23. )
  24. self._attr_translation_placeholders = config.translation_placeholders
  25. return {c.name: c for c in config.dps()}
  26. def _init_end(self, dps):
  27. for d in dps.values():
  28. if not d.hidden and d.name not in BLACKLISTED_ATTRIBUTES:
  29. self._attr_dps.append(d)
  30. @property
  31. def should_poll(self):
  32. return False
  33. @property
  34. def available(self):
  35. return self._device.has_returned_state and self._config.available(self._device)
  36. @property
  37. def has_entity_name(self):
  38. return True
  39. @property
  40. def name(self):
  41. """Return the name for the UI."""
  42. own_name = self._config.name
  43. if not own_name and not self.use_device_name:
  44. # super has the translation logic
  45. own_name = getattr(super(), "name")
  46. return own_name
  47. @property
  48. def use_device_name(self):
  49. """Return whether to use the device name for the entity name"""
  50. own_name = (
  51. self._config.name
  52. or self._config.translation_key
  53. or (self._default_to_device_class_name() and self._config.device_class)
  54. )
  55. return not own_name
  56. @property
  57. def unique_id(self):
  58. """Return the unique id for this entity."""
  59. return self._config.unique_id(self._device.unique_id)
  60. @property
  61. def device_info(self):
  62. """Return the device's information."""
  63. return self._device.device_info
  64. @property
  65. def entity_category(self):
  66. """Return the entitiy's category."""
  67. return (
  68. None
  69. if self._config.entity_category is None
  70. else EntityCategory(self._config.entity_category)
  71. )
  72. @property
  73. def icon(self):
  74. """Return the icon to use in the frontend for this device."""
  75. icon = self._config.icon(self._device)
  76. if icon:
  77. return icon
  78. else:
  79. return super().icon
  80. @property
  81. def extra_state_attributes(self):
  82. """Get additional attributes that the platform itself does not support."""
  83. attr = {}
  84. for a in self._attr_dps:
  85. value = a.get_value(self._device)
  86. if value is not None or not a.optional:
  87. # Decode json attributes for user convenience
  88. if a.rawtype == "json":
  89. try:
  90. value = json.loads(value)
  91. except json.JSONDecodeError:
  92. if value is not None:
  93. _LOGGER.warning(
  94. "Failed to decode JSON for attribute %s: %s",
  95. a.name,
  96. value,
  97. )
  98. attr[a.name] = value
  99. return attr
  100. @property
  101. def entity_registry_enabled_default(self):
  102. """Disable deprecated entities on new installations"""
  103. return self._config.enabled_by_default(self._device)
  104. async def async_update(self):
  105. await self._device.async_refresh()
  106. async def async_added_to_hass(self):
  107. self._device.register_entity(self)
  108. _LOGGER.debug("Adding %s for %s", self._config.config_id, self._device.name)
  109. if self._config.deprecated:
  110. _LOGGER.warning(self._config.deprecation_message)
  111. async def async_will_remove_from_hass(self):
  112. _LOGGER.debug("Removing %s for %s", self._config.config_id, self._device.name)
  113. await self._device.async_unregister_entity(self)
  114. def on_receive(self, dps, full_poll):
  115. """Override to process dps directly as they are received"""
  116. pass
  117. UNIT_ASCII_MAP = {
  118. "C": UnitOfTemperature.CELSIUS.value,
  119. "F": UnitOfTemperature.FAHRENHEIT.value,
  120. "ugm3": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
  121. "m2": UnitOfArea.SQUARE_METERS,
  122. }
  123. def unit_from_ascii(unit):
  124. if unit in UNIT_ASCII_MAP:
  125. return UNIT_ASCII_MAP[unit]
  126. return unit