number.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. """Number platform for Solar of Things integration."""
  2. from __future__ import annotations
  3. import logging
  4. from typing import Any
  5. from homeassistant.components.number import (
  6. NumberEntity,
  7. NumberDeviceClass,
  8. NumberMode,
  9. )
  10. from homeassistant.config_entries import ConfigEntry
  11. from homeassistant.const import PERCENTAGE
  12. from homeassistant.core import HomeAssistant
  13. from homeassistant.helpers.entity_platform import AddEntitiesCallback
  14. from homeassistant.helpers.update_coordinator import CoordinatorEntity
  15. from .const import DOMAIN
  16. _LOGGER = logging.getLogger(__name__)
  17. async def async_setup_entry(
  18. hass: HomeAssistant,
  19. entry: ConfigEntry,
  20. async_add_entities: AddEntitiesCallback,
  21. ) -> None:
  22. """Set up Solar of Things number entities based on a config entry."""
  23. coordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"]
  24. api = hass.data[DOMAIN][entry.entry_id]["api"]
  25. entities = [
  26. SolarOfThingsBatteryChargeLimitNumber(
  27. coordinator=coordinator,
  28. entry=entry,
  29. api=api,
  30. ),
  31. SolarOfThingsBatteryDischargeLimitNumber(
  32. coordinator=coordinator,
  33. entry=entry,
  34. api=api,
  35. ),
  36. SolarOfThingsGridChargeLimitNumber(
  37. coordinator=coordinator,
  38. entry=entry,
  39. api=api,
  40. ),
  41. ]
  42. async_add_entities(entities)
  43. class SolarOfThingsBatteryChargeLimitNumber(CoordinatorEntity, NumberEntity):
  44. """Number entity for battery charge limit."""
  45. def __init__(
  46. self,
  47. coordinator,
  48. entry: ConfigEntry,
  49. api,
  50. ) -> None:
  51. """Initialize the number entity."""
  52. super().__init__(coordinator)
  53. self._entry = entry
  54. self._api = api
  55. self._attr_name = f"{entry.data.get('station_id', entry.data.get('device_id'))} Battery Charge Limit"
  56. self._attr_unique_id = f"{entry.entry_id}_battery_charge_limit"
  57. self._attr_native_min_value = 0
  58. self._attr_native_max_value = 100
  59. self._attr_native_step = 1
  60. self._attr_native_unit_of_measurement = PERCENTAGE
  61. self._attr_mode = NumberMode.SLIDER
  62. self._attr_icon = "mdi:battery-arrow-up"
  63. @property
  64. def device_info(self):
  65. """Return device information."""
  66. return {
  67. "identifiers": {(DOMAIN, self._entry.entry_id)},
  68. "name": f"Solar Station {self._entry.data.get('station_id', self._entry.data.get('device_id'))}",
  69. "manufacturer": "Siseli",
  70. "model": "Solar Inverter",
  71. }
  72. @property
  73. def native_value(self) -> float | None:
  74. """Return the current value."""
  75. if self.coordinator.data and "settings" in self.coordinator.data:
  76. return self.coordinator.data["settings"].get("batteryChargeLimit")
  77. return None
  78. async def async_set_native_value(self, value: float) -> None:
  79. """Set new value."""
  80. try:
  81. await self.hass.async_add_executor_job(
  82. self._api.set_battery_charge_limit,
  83. int(value)
  84. )
  85. # Request immediate coordinator update
  86. await self.coordinator.async_request_refresh()
  87. except Exception as err:
  88. _LOGGER.error("Error setting battery charge limit: %s", err)
  89. raise
  90. class SolarOfThingsBatteryDischargeLimitNumber(CoordinatorEntity, NumberEntity):
  91. """Number entity for battery discharge limit."""
  92. def __init__(
  93. self,
  94. coordinator,
  95. entry: ConfigEntry,
  96. api,
  97. ) -> None:
  98. """Initialize the number entity."""
  99. super().__init__(coordinator)
  100. self._entry = entry
  101. self._api = api
  102. self._attr_name = f"{entry.data.get('station_id', entry.data.get('device_id'))} Battery Discharge Limit"
  103. self._attr_unique_id = f"{entry.entry_id}_battery_discharge_limit"
  104. self._attr_native_min_value = 0
  105. self._attr_native_max_value = 100
  106. self._attr_native_step = 1
  107. self._attr_native_unit_of_measurement = PERCENTAGE
  108. self._attr_mode = NumberMode.SLIDER
  109. self._attr_icon = "mdi:battery-arrow-down"
  110. @property
  111. def device_info(self):
  112. """Return device information."""
  113. return {
  114. "identifiers": {(DOMAIN, self._entry.entry_id)},
  115. "name": f"Solar Station {self._entry.data.get('station_id', self._entry.data.get('device_id'))}",
  116. "manufacturer": "Siseli",
  117. "model": "Solar Inverter",
  118. }
  119. @property
  120. def native_value(self) -> float | None:
  121. """Return the current value."""
  122. if self.coordinator.data and "settings" in self.coordinator.data:
  123. return self.coordinator.data["settings"].get("batteryDischargeLimit")
  124. return None
  125. async def async_set_native_value(self, value: float) -> None:
  126. """Set new value."""
  127. try:
  128. await self.hass.async_add_executor_job(
  129. self._api.set_battery_discharge_limit,
  130. int(value)
  131. )
  132. # Request immediate coordinator update
  133. await self.coordinator.async_request_refresh()
  134. except Exception as err:
  135. _LOGGER.error("Error setting battery discharge limit: %s", err)
  136. raise
  137. class SolarOfThingsGridChargeLimitNumber(CoordinatorEntity, NumberEntity):
  138. """Number entity for grid charge limit."""
  139. def __init__(
  140. self,
  141. coordinator,
  142. entry: ConfigEntry,
  143. api,
  144. ) -> None:
  145. """Initialize the number entity."""
  146. super().__init__(coordinator)
  147. self._entry = entry
  148. self._api = api
  149. self._attr_name = f"{entry.data.get('station_id', entry.data.get('device_id'))} Grid Charge Limit"
  150. self._attr_unique_id = f"{entry.entry_id}_grid_charge_limit"
  151. self._attr_native_min_value = 0
  152. self._attr_native_max_value = 5000
  153. self._attr_native_step = 100
  154. self._attr_native_unit_of_measurement = "W"
  155. self._attr_mode = NumberMode.SLIDER
  156. self._attr_device_class = NumberDeviceClass.POWER
  157. self._attr_icon = "mdi:transmission-tower-import"
  158. @property
  159. def device_info(self):
  160. """Return device information."""
  161. return {
  162. "identifiers": {(DOMAIN, self._entry.entry_id)},
  163. "name": f"Solar Station {self._entry.data.get('station_id', self._entry.data.get('device_id'))}",
  164. "manufacturer": "Siseli",
  165. "model": "Solar Inverter",
  166. }
  167. @property
  168. def native_value(self) -> float | None:
  169. """Return the current value."""
  170. if self.coordinator.data and "settings" in self.coordinator.data:
  171. return self.coordinator.data["settings"].get("gridChargeLimit")
  172. return None
  173. async def async_set_native_value(self, value: float) -> None:
  174. """Set new value."""
  175. try:
  176. await self.hass.async_add_executor_job(
  177. self._api.set_grid_charge_limit,
  178. int(value)
  179. )
  180. # Request immediate coordinator update
  181. await self.coordinator.async_request_refresh()
  182. except Exception as err:
  183. _LOGGER.error("Error setting grid charge limit: %s", err)
  184. raise