config_flow.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. """Config flow for Solar of Things integration."""
  2. from __future__ import annotations
  3. import logging
  4. from typing import Any
  5. import voluptuous as vol
  6. from homeassistant import config_entries
  7. from homeassistant.core import HomeAssistant, callback
  8. from homeassistant.data_entry_flow import FlowResult
  9. from homeassistant.exceptions import HomeAssistantError
  10. from homeassistant.helpers import config_validation as cv
  11. from .api import SolarOfThingsAPI
  12. from .const import DOMAIN, CONF_IOT_TOKEN, CONF_STATION_ID, CONF_DEVICE_ID
  13. _LOGGER = logging.getLogger(__name__)
  14. async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
  15. """Validate the user input allows us to connect."""
  16. api = SolarOfThingsAPI(
  17. iot_token=data[CONF_IOT_TOKEN],
  18. station_id=data.get(CONF_STATION_ID),
  19. device_id=data.get(CONF_DEVICE_ID),
  20. )
  21. # Test the connection
  22. if not await hass.async_add_executor_job(api.test_connection):
  23. raise CannotConnect
  24. # Return info that you want to store in the config entry.
  25. return {"title": f"Solar Station {data.get(CONF_STATION_ID, data.get(CONF_DEVICE_ID))}"}
  26. class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
  27. """Handle a config flow for Solar of Things."""
  28. VERSION = 1
  29. def __init__(self) -> None:
  30. """Initialize the config flow."""
  31. self._stations: list[dict[str, Any]] = []
  32. async def async_step_user(
  33. self, user_input: dict[str, Any] | None = None
  34. ) -> FlowResult:
  35. """Handle the initial step."""
  36. errors: dict[str, str] = {}
  37. if user_input is not None:
  38. try:
  39. info = await validate_input(self.hass, user_input)
  40. except CannotConnect:
  41. errors["base"] = "cannot_connect"
  42. except Exception: # pylint: disable=broad-except
  43. _LOGGER.exception("Unexpected exception")
  44. errors["base"] = "unknown"
  45. else:
  46. # Check if this station is already configured
  47. await self.async_set_unique_id(
  48. f"{user_input.get(CONF_STATION_ID)}_{user_input.get(CONF_DEVICE_ID)}"
  49. )
  50. self._abort_if_unique_id_configured()
  51. # Store this station
  52. self._stations.append(user_input)
  53. # Ask if user wants to add more stations
  54. return await self.async_step_add_another()
  55. # Show the form
  56. data_schema = vol.Schema(
  57. {
  58. vol.Required(CONF_IOT_TOKEN): cv.string,
  59. vol.Optional(CONF_STATION_ID): cv.string,
  60. vol.Optional(CONF_DEVICE_ID): cv.string,
  61. }
  62. )
  63. return self.async_show_form(
  64. step_id="user",
  65. data_schema=data_schema,
  66. errors=errors,
  67. description_placeholders={
  68. "docs_url": "https://github.com/Hyllesen/solar-of-things-solar-usage"
  69. },
  70. )
  71. async def async_step_add_another(
  72. self, user_input: dict[str, Any] | None = None
  73. ) -> FlowResult:
  74. """Ask if user wants to add another station."""
  75. if user_input is not None:
  76. if user_input.get("add_another"):
  77. # Reset for next station
  78. return await self.async_step_user()
  79. else:
  80. # Create entries for all stations
  81. entries = []
  82. for station in self._stations:
  83. entries.append(
  84. self.async_create_entry(
  85. title=f"Solar Station {station.get(CONF_STATION_ID, station.get(CONF_DEVICE_ID))}",
  86. data=station,
  87. )
  88. )
  89. # Return the first entry (HA limitation - can only return one)
  90. # Others will be created via async_step_user calls
  91. if entries:
  92. return entries[0]
  93. return self.async_show_form(
  94. step_id="add_another",
  95. data_schema=vol.Schema(
  96. {
  97. vol.Required("add_another", default=False): cv.boolean,
  98. }
  99. ),
  100. description_placeholders={
  101. "station_count": str(len(self._stations))
  102. },
  103. )
  104. @staticmethod
  105. @callback
  106. def async_get_options_flow(
  107. config_entry: config_entries.ConfigEntry,
  108. ) -> OptionsFlow:
  109. """Get the options flow for this handler."""
  110. return OptionsFlow(config_entry)
  111. class OptionsFlow(config_entries.OptionsFlow):
  112. """Handle options flow for Solar of Things."""
  113. def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
  114. """Initialize options flow."""
  115. self.config_entry = config_entry
  116. async def async_step_init(
  117. self, user_input: dict[str, Any] | None = None
  118. ) -> FlowResult:
  119. """Manage the options."""
  120. if user_input is not None:
  121. return self.async_create_entry(title="", data=user_input)
  122. return self.async_show_form(
  123. step_id="init",
  124. data_schema=vol.Schema(
  125. {
  126. vol.Optional(
  127. "update_interval",
  128. default=self.config_entry.options.get("update_interval", 5),
  129. ): vol.All(vol.Coerce(int), vol.Range(min=1, max=60)),
  130. }
  131. ),
  132. )
  133. class CannotConnect(HomeAssistantError):
  134. """Error to indicate we cannot connect."""