select.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """Select platform for Solar of Things integration."""
  2. from __future__ import annotations
  3. import logging
  4. from homeassistant.components.select import SelectEntity
  5. from homeassistant.config_entries import ConfigEntry
  6. from homeassistant.core import HomeAssistant
  7. from homeassistant.helpers.entity_platform import AddEntitiesCallback
  8. from homeassistant.helpers.update_coordinator import CoordinatorEntity
  9. from .const import DOMAIN
  10. _LOGGER = logging.getLogger(__name__)
  11. # Real API key: outputSourcePrioritySetting (integer value)
  12. # 0 = USO Utility first
  13. # 1 = SUB Solar first (grid supplement)
  14. # 2 = SBU Solar + Battery first (grid only last resort)
  15. OUTPUT_MODE_BY_VALUE: dict[int, str] = {
  16. 0: "Utility First (USO)",
  17. 1: "Solar First (SUB)",
  18. 2: "Solar+Battery First (SBU)",
  19. }
  20. OUTPUT_MODES = list(OUTPUT_MODE_BY_VALUE.values())
  21. # Real API key: chargerSourcePrioritySetting (integer value)
  22. # 0 = CSO Solar + Utility charging (utility has priority)
  23. # 1 = SNU Solar First for charging, utility as fallback
  24. # 2 = OSO Solar Only charging
  25. CHARGER_PRIORITY_BY_VALUE: dict[int, str] = {
  26. 0: "Solar + Utility (CSO)",
  27. 1: "Solar First (SNU)",
  28. 2: "Solar Only (OSO)",
  29. }
  30. CHARGER_PRIORITIES = list(CHARGER_PRIORITY_BY_VALUE.values())
  31. async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None:
  32. data = hass.data[DOMAIN][entry.entry_id]
  33. api = data["api"]
  34. station_id: str = data["station_id"]
  35. device_coordinators = data["device_coordinators"]
  36. entities: list[SelectEntity] = []
  37. for device_id, coordinator in device_coordinators.items():
  38. device_name = (coordinator.device_meta or {}).get("name") or device_id
  39. entities.extend(
  40. [
  41. SolarOfThingsOperatingModeSelect(api, coordinator, station_id, device_id, device_name),
  42. SolarOfThingsBatteryPrioritySelect(api, coordinator, station_id, device_id, device_name),
  43. ]
  44. )
  45. async_add_entities(entities)
  46. class _BaseSelect(CoordinatorEntity, SelectEntity):
  47. def __init__(self, api, coordinator, station_id: str, device_id: str, device_name: str) -> None:
  48. super().__init__(coordinator)
  49. self._api = api
  50. self._station_id = station_id
  51. self._device_id = device_id
  52. self._device_name = device_name
  53. @property
  54. def device_info(self):
  55. return {
  56. "identifiers": {(DOMAIN, self._device_id)},
  57. "name": self._device_name,
  58. "manufacturer": "Siseli",
  59. "model": (self.coordinator.data.get("device_meta") or {}).get("model") if self.coordinator.data else None,
  60. "via_device": (DOMAIN, self._station_id),
  61. }
  62. class SolarOfThingsOperatingModeSelect(_BaseSelect):
  63. """Select entity for Output Source Priority (outputSourcePrioritySetting).
  64. Reflects the real device API key. Values 0/1/2 map to USO/SUB/SBU.
  65. """
  66. def __init__(self, api, coordinator, station_id: str, device_id: str, device_name: str) -> None:
  67. super().__init__(api, coordinator, station_id, device_id, device_name)
  68. self._attr_name = f"{device_name} Output Source Priority"
  69. self._attr_unique_id = f"{DOMAIN}_{station_id}_{device_id}_operating_mode"
  70. self._attr_options = OUTPUT_MODES
  71. self._attr_icon = "mdi:cog"
  72. @property
  73. def current_option(self) -> str | None:
  74. settings = ((self.coordinator.data or {}).get("settings") or {})
  75. entry = settings.get("outputSourcePrioritySetting")
  76. if entry is None:
  77. return None
  78. raw = entry.get("value") if isinstance(entry, dict) else entry
  79. try:
  80. return OUTPUT_MODE_BY_VALUE.get(int(raw))
  81. except (TypeError, ValueError):
  82. return None
  83. async def async_select_option(self, option: str) -> None:
  84. await self.hass.async_add_executor_job(self._api.set_operating_mode, self._device_id, option)
  85. await self.coordinator.async_request_refresh()
  86. class SolarOfThingsBatteryPrioritySelect(_BaseSelect):
  87. """Select entity for Charger Source Priority (chargerSourcePrioritySetting).
  88. Reflects the real device API key. Values 0/1/2 map to CSO/SNU/OSO.
  89. """
  90. def __init__(self, api, coordinator, station_id: str, device_id: str, device_name: str) -> None:
  91. super().__init__(api, coordinator, station_id, device_id, device_name)
  92. self._attr_name = f"{device_name} Charger Source Priority"
  93. self._attr_unique_id = f"{DOMAIN}_{station_id}_{device_id}_battery_priority"
  94. self._attr_options = CHARGER_PRIORITIES
  95. self._attr_icon = "mdi:battery-sync"
  96. @property
  97. def current_option(self) -> str | None:
  98. settings = ((self.coordinator.data or {}).get("settings") or {})
  99. entry = settings.get("chargerSourcePrioritySetting")
  100. if entry is None:
  101. return None
  102. raw = entry.get("value") if isinstance(entry, dict) else entry
  103. try:
  104. return CHARGER_PRIORITY_BY_VALUE.get(int(raw))
  105. except (TypeError, ValueError):
  106. return None
  107. async def async_select_option(self, option: str) -> None:
  108. await self.hass.async_add_executor_job(self._api.set_battery_priority, self._device_id, option)
  109. await self.coordinator.async_request_refresh()