|
3 | 3 |
|
4 | 4 | from homeassistant.const import CONF_PASSWORD, CONF_EMAIL |
5 | 5 | from homeassistant import config_entries |
6 | | -from homeassistant.core import HomeAssistant |
| 6 | +from homeassistant.core import HomeAssistant, callback |
| 7 | +from homeassistant.helpers.selector import ( |
| 8 | + BooleanSelector, |
| 9 | + NumberSelector, |
| 10 | + NumberSelectorConfig, |
| 11 | + NumberSelectorMode, |
| 12 | + SelectSelector, |
| 13 | + SelectSelectorConfig, |
| 14 | + SelectSelectorMode, |
| 15 | +) |
7 | 16 | import voluptuous as vol |
8 | 17 |
|
9 | | -from .const import DOMAIN |
| 18 | +from .const import ( |
| 19 | + DOMAIN, CONF_ANALYTICS, ANALYTICS_OPTIONS, DEFAULT_ANALYTICS, |
| 20 | + CONF_TOP_N, DEFAULT_TOP_N, CONF_HIDE_DISCONTINUED, DEFAULT_HIDE_DISCONTINUED, |
| 21 | +) |
10 | 22 | from .errors import InvalidCredentialsError |
11 | 23 | from .rohlik_api import RohlikCZAPI |
12 | 24 |
|
13 | | - |
14 | | - |
15 | 25 | _LOGGER = logging.getLogger(__name__) |
16 | 26 |
|
17 | 27 |
|
18 | 28 | async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> tuple[str, dict[str, Any]]: |
19 | | - """Validate the user input allows us to connect. |
20 | | - Data has the keys from DATA_SCHEMA with values provided by the user. |
21 | | - """ |
22 | | - |
23 | | - api = RohlikCZAPI(data[CONF_EMAIL], data[CONF_PASSWORD]) # type: ignore[Any] |
24 | | - |
| 29 | + """Validate the user input allows us to connect.""" |
| 30 | + api = RohlikCZAPI(data[CONF_EMAIL], data[CONF_PASSWORD]) |
25 | 31 | reply = await api.get_data() |
26 | | - |
27 | 32 | title: str = reply["login"]["data"]["user"]["name"] |
28 | | - |
29 | 33 | return title, data |
30 | 34 |
|
31 | 35 |
|
| 36 | +ANALYTICS_SCHEMA = vol.Schema({ |
| 37 | + vol.Optional(CONF_ANALYTICS, default=DEFAULT_ANALYTICS): SelectSelector( |
| 38 | + SelectSelectorConfig( |
| 39 | + options=ANALYTICS_OPTIONS, |
| 40 | + multiple=True, |
| 41 | + mode=SelectSelectorMode.LIST, |
| 42 | + translation_key=CONF_ANALYTICS, |
| 43 | + ) |
| 44 | + ), |
| 45 | + vol.Optional(CONF_TOP_N, default=DEFAULT_TOP_N): NumberSelector( |
| 46 | + NumberSelectorConfig( |
| 47 | + min=5, |
| 48 | + max=200, |
| 49 | + step=5, |
| 50 | + mode=NumberSelectorMode.BOX, |
| 51 | + ) |
| 52 | + ), |
| 53 | + vol.Optional(CONF_HIDE_DISCONTINUED, default=DEFAULT_HIDE_DISCONTINUED): BooleanSelector(), |
| 54 | +}) |
| 55 | + |
| 56 | + |
32 | 57 | class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): |
33 | 58 |
|
34 | 59 | CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL |
35 | | - VERSION = 0.1 |
| 60 | + VERSION = 1 |
36 | 61 |
|
37 | | - async def async_step_user(self, user_input: dict[str, Any] | None = None) -> config_entries.FlowResult: |
| 62 | + def __init__(self) -> None: |
| 63 | + super().__init__() |
| 64 | + self._user_title: str | None = None |
| 65 | + self._user_data: dict[str, Any] = {} |
| 66 | + |
| 67 | + async def async_step_user( |
| 68 | + self, user_input: dict[str, Any] | None = None |
| 69 | + ) -> config_entries.FlowResult: |
38 | 70 |
|
39 | 71 | data_schema: dict[Any, Any] = { |
40 | 72 | vol.Required(CONF_EMAIL, default="e-mail"): str, |
41 | | - vol.Required(CONF_PASSWORD, default="password"): str |
| 73 | + vol.Required(CONF_PASSWORD, default="password"): str, |
42 | 74 | } |
43 | 75 |
|
44 | | - # Set dict for errors |
45 | 76 | errors: dict[str, str] = {} |
46 | 77 |
|
47 | | - # Steps to take if user input is received |
48 | 78 | if user_input is not None: |
49 | 79 | try: |
50 | 80 | info, data = await validate_input(self.hass, user_input) |
51 | | - return self.async_create_entry(title=info, data=data) |
| 81 | + self._user_title = info |
| 82 | + self._user_data = data |
| 83 | + return await self.async_step_analytics() |
52 | 84 |
|
53 | 85 | except InvalidCredentialsError: |
54 | | - errors["base"] = "Invalid credentials provided" |
| 86 | + errors["base"] = "invalid_auth" |
55 | 87 |
|
56 | | - except Exception: # pylint: disable=broad-except |
| 88 | + except Exception: |
57 | 89 | _LOGGER.exception("Unknown exception") |
58 | | - errors["base"] = "Unknown exception" |
| 90 | + errors["base"] = "unknown" |
59 | 91 |
|
60 | | - # If there is no user input or there were errors, show the form again, including any errors that were found with the input. |
61 | 92 | return self.async_show_form( |
62 | 93 | step_id="user", data_schema=vol.Schema(data_schema), errors=errors |
63 | 94 | ) |
| 95 | + |
| 96 | + async def async_step_analytics( |
| 97 | + self, user_input: dict[str, Any] | None = None |
| 98 | + ) -> config_entries.FlowResult: |
| 99 | + """Second step: choose analytics levels.""" |
| 100 | + if user_input is not None: |
| 101 | + return self.async_create_entry( |
| 102 | + title=self._user_title, |
| 103 | + data=self._user_data, |
| 104 | + options={ |
| 105 | + CONF_ANALYTICS: user_input.get(CONF_ANALYTICS, []), |
| 106 | + CONF_TOP_N: int(user_input.get(CONF_TOP_N, DEFAULT_TOP_N)), |
| 107 | + CONF_HIDE_DISCONTINUED: user_input.get(CONF_HIDE_DISCONTINUED, DEFAULT_HIDE_DISCONTINUED), |
| 108 | + }, |
| 109 | + ) |
| 110 | + |
| 111 | + return self.async_show_form( |
| 112 | + step_id="analytics", |
| 113 | + data_schema=ANALYTICS_SCHEMA, |
| 114 | + ) |
| 115 | + |
| 116 | + @staticmethod |
| 117 | + @callback |
| 118 | + def async_get_options_flow(config_entry: config_entries.ConfigEntry): |
| 119 | + return RohlikOptionsFlowHandler() |
| 120 | + |
| 121 | + |
| 122 | +class RohlikOptionsFlowHandler(config_entries.OptionsFlow): |
| 123 | + """Handle options for existing entries (reconfigure analytics).""" |
| 124 | + |
| 125 | + async def async_step_init( |
| 126 | + self, user_input: dict[str, Any] | None = None |
| 127 | + ) -> config_entries.FlowResult: |
| 128 | + if user_input is not None: |
| 129 | + user_input[CONF_TOP_N] = int(user_input.get(CONF_TOP_N, DEFAULT_TOP_N)) |
| 130 | + return self.async_create_entry(title="", data=user_input) |
| 131 | + |
| 132 | + current = self.config_entry.options.get(CONF_ANALYTICS, DEFAULT_ANALYTICS) |
| 133 | + current_top_n = self.config_entry.options.get(CONF_TOP_N, DEFAULT_TOP_N) |
| 134 | + current_hide = self.config_entry.options.get(CONF_HIDE_DISCONTINUED, DEFAULT_HIDE_DISCONTINUED) |
| 135 | + |
| 136 | + return self.async_show_form( |
| 137 | + step_id="init", |
| 138 | + data_schema=vol.Schema({ |
| 139 | + vol.Optional(CONF_ANALYTICS, default=current): SelectSelector( |
| 140 | + SelectSelectorConfig( |
| 141 | + options=ANALYTICS_OPTIONS, |
| 142 | + multiple=True, |
| 143 | + mode=SelectSelectorMode.LIST, |
| 144 | + translation_key=CONF_ANALYTICS, |
| 145 | + ) |
| 146 | + ), |
| 147 | + vol.Optional(CONF_TOP_N, default=current_top_n): NumberSelector( |
| 148 | + NumberSelectorConfig( |
| 149 | + min=5, |
| 150 | + max=200, |
| 151 | + step=5, |
| 152 | + mode=NumberSelectorMode.BOX, |
| 153 | + ) |
| 154 | + ), |
| 155 | + vol.Optional(CONF_HIDE_DISCONTINUED, default=current_hide): BooleanSelector(), |
| 156 | + }), |
| 157 | + ) |
0 commit comments