sensor.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. """Sensor platform for Solar of Things integration."""
  2. from __future__ import annotations
  3. from datetime import datetime
  4. import logging
  5. from homeassistant.components.sensor import (
  6. SensorEntity,
  7. SensorDeviceClass,
  8. SensorStateClass,
  9. )
  10. from homeassistant.config_entries import ConfigEntry
  11. from homeassistant.const import (
  12. UnitOfPower,
  13. UnitOfEnergy,
  14. UnitOfElectricCurrent,
  15. UnitOfElectricPotential,
  16. PERCENTAGE,
  17. )
  18. from homeassistant.core import HomeAssistant
  19. from homeassistant.helpers.entity_platform import AddEntitiesCallback
  20. from homeassistant.helpers.update_coordinator import CoordinatorEntity
  21. from .const import DOMAIN, SENSOR_DEFINITIONS
  22. _LOGGER = logging.getLogger(__name__)
  23. async def async_setup_entry(
  24. hass: HomeAssistant,
  25. entry: ConfigEntry,
  26. async_add_entities: AddEntitiesCallback,
  27. ) -> None:
  28. """Set up Solar of Things sensor based on a config entry."""
  29. coordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"]
  30. entities = []
  31. # Create time-series sensors
  32. for key, definition in SENSOR_DEFINITIONS.items():
  33. if not key.startswith("monthly_"):
  34. entities.append(
  35. SolarOfThingsSensor(
  36. coordinator=coordinator,
  37. entry=entry,
  38. sensor_key=key,
  39. sensor_definition=definition,
  40. )
  41. )
  42. # Create monthly sensors if station_id is available
  43. if entry.data.get("station_id"):
  44. for key, definition in SENSOR_DEFINITIONS.items():
  45. if key.startswith("monthly_"):
  46. entities.append(
  47. SolarOfThingsMonthlySensor(
  48. coordinator=coordinator,
  49. entry=entry,
  50. sensor_key=key,
  51. sensor_definition=definition,
  52. )
  53. )
  54. async_add_entities(entities)
  55. class SolarOfThingsSensor(CoordinatorEntity, SensorEntity):
  56. """Representation of a Solar of Things Sensor."""
  57. def __init__(
  58. self,
  59. coordinator,
  60. entry: ConfigEntry,
  61. sensor_key: str,
  62. sensor_definition: dict,
  63. ) -> None:
  64. """Initialize the sensor."""
  65. super().__init__(coordinator)
  66. self._entry = entry
  67. self._sensor_key = sensor_key
  68. self._sensor_definition = sensor_definition
  69. # Entity attributes
  70. self._attr_name = f"{entry.data.get('station_id', entry.data.get('device_id'))} {sensor_definition['name']}"
  71. self._attr_unique_id = f"{entry.entry_id}_{sensor_key}"
  72. self._attr_native_unit_of_measurement = sensor_definition.get("unit")
  73. self._attr_icon = sensor_definition.get("icon")
  74. # Set device class based on unit
  75. unit = sensor_definition.get("unit")
  76. if unit == "W":
  77. self._attr_device_class = SensorDeviceClass.POWER
  78. self._attr_native_unit_of_measurement = UnitOfPower.WATT
  79. self._attr_state_class = SensorStateClass.MEASUREMENT
  80. elif unit == "kWh":
  81. self._attr_device_class = SensorDeviceClass.ENERGY
  82. self._attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
  83. self._attr_state_class = SensorStateClass.TOTAL_INCREASING
  84. elif unit == "A":
  85. self._attr_device_class = SensorDeviceClass.CURRENT
  86. self._attr_native_unit_of_measurement = UnitOfElectricCurrent.AMPERE
  87. self._attr_state_class = SensorStateClass.MEASUREMENT
  88. elif unit == "V":
  89. self._attr_device_class = SensorDeviceClass.VOLTAGE
  90. self._attr_native_unit_of_measurement = UnitOfElectricPotential.VOLT
  91. self._attr_state_class = SensorStateClass.MEASUREMENT
  92. elif unit == "%":
  93. if "battery" in sensor_key.lower():
  94. self._attr_device_class = SensorDeviceClass.BATTERY
  95. self._attr_native_unit_of_measurement = PERCENTAGE
  96. self._attr_state_class = SensorStateClass.MEASUREMENT
  97. @property
  98. def device_info(self):
  99. """Return device information."""
  100. return {
  101. "identifiers": {(DOMAIN, self._entry.entry_id)},
  102. "name": f"Solar Station {self._entry.data.get('station_id', self._entry.data.get('device_id'))}",
  103. "manufacturer": "Siseli",
  104. "model": "Solar Inverter",
  105. }
  106. @property
  107. def native_value(self):
  108. """Return the state of the sensor."""
  109. if self.coordinator.data and "time_series" in self.coordinator.data:
  110. value = self.coordinator.data["time_series"].get(self._sensor_key)
  111. if value is not None:
  112. try:
  113. return round(float(value), 2)
  114. except (ValueError, TypeError):
  115. return None
  116. return None
  117. @property
  118. def available(self) -> bool:
  119. """Return if entity is available."""
  120. return self.coordinator.last_update_success and self.native_value is not None
  121. class SolarOfThingsMonthlySensor(CoordinatorEntity, SensorEntity):
  122. """Representation of a Solar of Things Monthly Sensor."""
  123. def __init__(
  124. self,
  125. coordinator,
  126. entry: ConfigEntry,
  127. sensor_key: str,
  128. sensor_definition: dict,
  129. ) -> None:
  130. """Initialize the sensor."""
  131. super().__init__(coordinator)
  132. self._entry = entry
  133. self._sensor_key = sensor_key
  134. self._sensor_definition = sensor_definition
  135. # Entity attributes
  136. self._attr_name = f"{entry.data.get('station_id')} {sensor_definition['name']}"
  137. self._attr_unique_id = f"{entry.entry_id}_{sensor_key}"
  138. self._attr_native_unit_of_measurement = sensor_definition.get("unit")
  139. self._attr_icon = sensor_definition.get("icon")
  140. # Set device class
  141. unit = sensor_definition.get("unit")
  142. if unit == "kWh":
  143. self._attr_device_class = SensorDeviceClass.ENERGY
  144. self._attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
  145. self._attr_state_class = SensorStateClass.TOTAL
  146. elif unit == "%":
  147. self._attr_native_unit_of_measurement = PERCENTAGE
  148. self._attr_state_class = SensorStateClass.MEASUREMENT
  149. @property
  150. def device_info(self):
  151. """Return device information."""
  152. return {
  153. "identifiers": {(DOMAIN, self._entry.entry_id)},
  154. "name": f"Solar Station {self._entry.data.get('station_id')}",
  155. "manufacturer": "Siseli",
  156. "model": "Solar Inverter",
  157. }
  158. @property
  159. def native_value(self):
  160. """Return the state of the sensor."""
  161. if self.coordinator.data and "monthly" in self.coordinator.data:
  162. value = self.coordinator.data["monthly"].get(self._sensor_key)
  163. if value is not None:
  164. try:
  165. return round(float(value), 2)
  166. except (ValueError, TypeError):
  167. return None
  168. return None
  169. @property
  170. def available(self) -> bool:
  171. """Return if entity is available."""
  172. return self.coordinator.last_update_success