|
1 | | -""" config_flow.py """ |
| 1 | +"""Config flow.""" |
2 | 2 |
|
| 3 | +import asyncio |
3 | 4 | import logging |
| 5 | + |
4 | 6 | import voluptuous as vol |
5 | 7 |
|
6 | | -from homeassistant.core import callback |
7 | 8 | from homeassistant import config_entries |
| 9 | +from homeassistant.core import callback |
8 | 10 |
|
9 | 11 | from .const import DOMAIN, KEYS |
10 | | -from .ecu_api import APsystemsSocket, APsystemsInvalidData |
11 | 12 |
|
12 | 13 | _LOGGER = logging.getLogger(__name__) |
13 | 14 |
|
14 | | -STEP_USER_DATA_SCHEMA = vol.Schema({ |
15 | | - vol.Required(KEYS[0], default= ''): str, |
16 | | - vol.Required(KEYS[1], default= 300): int, |
17 | | - vol.Optional(KEYS[2], default= 5): int, # Port retries |
18 | | - vol.Optional(KEYS[3], default= "ECU-WIFI_local"): str, |
19 | | - vol.Optional(KEYS[4], default= "default"): str, |
20 | | - vol.Optional(KEYS[5], default= False): bool, |
21 | | -}) |
22 | 15 |
|
23 | | -@config_entries.HANDLERS.register(DOMAIN) |
24 | | -class FlowHandler(config_entries.ConfigFlow): |
| 16 | +async def validate_ip(ip_address: str) -> bool: |
| 17 | + """Validate EMA server ip.""" |
| 18 | + try: |
| 19 | + reader, writer = await asyncio.wait_for( |
| 20 | + asyncio.open_connection(ip_address, 8995), timeout=3.0 |
| 21 | + ) |
| 22 | + # Close the connection neatly |
| 23 | + writer.close() |
| 24 | + await writer.wait_closed() |
| 25 | + except (OSError, TimeoutError): |
| 26 | + return False |
| 27 | + else: |
| 28 | + return True |
| 29 | + |
| 30 | + |
| 31 | +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): |
| 32 | + """Integration configuration.""" |
25 | 33 |
|
26 | 34 | VERSION = 1 |
| 35 | + CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH |
| 36 | + |
| 37 | + @staticmethod |
| 38 | + @callback |
| 39 | + def async_get_options_flow(config_entry): |
| 40 | + """Get stored configuration data to present in async_step_init.""" |
| 41 | + _LOGGER.debug("async_get_options_flow called: %s", config_entry) |
| 42 | + return OptionsFlowHandler(config_entry) |
27 | 43 |
|
28 | 44 | async def async_step_user(self, user_input=None): |
29 | | - errors = {} |
30 | | - if user_input is None: |
31 | | - # Show form because user input is empty. |
32 | | - return self.async_show_form( |
33 | | - step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors |
34 | | - ) |
| 45 | + """First step: Initial set-up of integration options.""" |
| 46 | + _LOGGER.debug("async_step_user") |
| 47 | + schema = vol.Schema( |
| 48 | + { |
| 49 | + vol.Required(KEYS[0], default="3.67.1.32"): str, |
| 50 | + vol.Required(KEYS[1], default="1800"): str, |
| 51 | + vol.Required(KEYS[2], default="300"): str, |
| 52 | + vol.Required(KEYS[3], default="660"): str, |
| 53 | + vol.Required(KEYS[4], default=True): bool, |
| 54 | + } |
| 55 | + ) |
| 56 | + |
| 57 | + if user_input is not None: |
| 58 | + if await validate_ip(user_input["ema_host"]): |
| 59 | + return self.async_create_entry( |
| 60 | + title="APsystems ECU proxy", data=user_input |
| 61 | + ) |
35 | 62 |
|
36 | | - # User input is not empty, processing input. |
37 | | - retries = user_input.get(KEYS[2], 5) # Get port_retries from user input, default to 5 |
38 | | - show_graphs = user_input.get(KEYS[5], False) # Get show_graphs from user input, default to False |
39 | | - _LOGGER.warning("1. show_graphs = %s", show_graphs) |
40 | | - ecu_id = await test_ecu_connection(self.hass, user_input["ecu_host"], retries, show_graphs) |
41 | | - _LOGGER.debug("ecu_id = %s", ecu_id) |
42 | | - |
43 | | - if ecu_id: |
44 | | - return self.async_create_entry(title=f"ECU: {ecu_id}", data=user_input) |
45 | | - else: |
46 | | - errors["ecu_host"] = "no_ecu_found" |
47 | 63 | return self.async_show_form( |
48 | | - step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors |
| 64 | + step_id="user", |
| 65 | + data_schema=schema, |
| 66 | + errors={"base": "Could not connect to the specified EMA host"}, |
49 | 67 | ) |
| 68 | + return self.async_show_form(step_id="user", data_schema=schema) |
50 | 69 |
|
51 | | - # Enable the integration to be reconfigured in the UI |
52 | | - @staticmethod |
53 | | - @callback |
54 | | - def async_get_options_flow(config_entry): |
55 | | - return OptionsFlowHandler() |
56 | 70 |
|
57 | 71 | class OptionsFlowHandler(config_entries.OptionsFlow): |
58 | | - """Regular change of integration configuration.""" |
| 72 | + """Regular change of integration options.""" |
59 | 73 |
|
60 | | - #def __init__(self, config_entry: config_entries.ConfigEntry) -> None: |
61 | | - def __init__(self) -> None: |
62 | | - """Init options flow.""" |
63 | | - # self.config_entry = config_entry |
| 74 | + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: |
| 75 | + """Init options handler.""" |
| 76 | + self.config_entry = config_entry |
64 | 77 |
|
65 | 78 | async def async_step_init(self, user_input=None): |
66 | | - """Altering the integration configuration.""" |
| 79 | + """Second step: Altering the integration options.""" |
67 | 80 | errors = {} |
68 | 81 | current_options = ( |
69 | 82 | self.config_entry.data |
70 | 83 | if not self.config_entry.options |
71 | 84 | else self.config_entry.options |
72 | 85 | ) |
73 | | - |
74 | 86 | _LOGGER.debug("async_step_init with configuration: %s", current_options) |
75 | 87 |
|
76 | 88 | schema = vol.Schema( |
77 | 89 | { |
78 | | - vol.Required(KEYS[0], default=current_options.get(KEYS[0])): str, |
79 | | - vol.Required(KEYS[1], default=current_options.get(KEYS[1])): int, |
80 | | - vol.Optional(KEYS[2], default=current_options.get(KEYS[2])): int, # Port retries |
81 | | - vol.Optional(KEYS[3], default=current_options.get(KEYS[3])): str, |
82 | | - vol.Optional(KEYS[4], default=current_options.get(KEYS[4])): str, |
83 | | - vol.Optional(KEYS[5], default=current_options.get(KEYS[5])): bool, |
| 90 | + vol.Required(key, default=current_options.get(key)): ( |
| 91 | + str if key != "send_to_ema" else bool |
| 92 | + ) |
| 93 | + for key in KEYS |
84 | 94 | } |
85 | 95 | ) |
86 | 96 |
|
87 | | - if user_input: |
88 | | - retries = user_input.get(KEYS[2], 5) # Get port_retries from user input, default to 5 |
89 | | - show_graphs = user_input.get(KEYS[5], False) # Get show_graphs from user input, default is False |
90 | | - _LOGGER.warning("2. show_graphs = %s", show_graphs) |
91 | | - ecu_id = await test_ecu_connection(self.hass, user_input["ecu_host"], retries, show_graphs) |
92 | | - if ecu_id: |
| 97 | + if user_input is not None: |
| 98 | + if await validate_ip(user_input["ema_host"]): |
93 | 99 | self.hass.config_entries.async_update_entry( |
94 | 100 | self.config_entry, data=user_input |
95 | 101 | ) |
96 | 102 | return self.async_create_entry(title="", data={}) |
97 | | - else: |
98 | | - errors["ecu_host"] = "no_ecu_found" |
99 | | - return self.async_show_form( |
100 | | - step_id="init", data_schema=STEP_USER_DATA_SCHEMA, errors=errors |
101 | | - ) |
102 | | - else: |
103 | | - errors = {} |
104 | | - return self.async_show_form( |
105 | | - step_id="init", data_schema=schema, errors=errors |
106 | | - ) |
107 | | - |
108 | 103 |
|
109 | | -async def test_ecu_connection(hass, ecu_host, retries, show_graphs): |
110 | | - """Test the connection to the ECU and return the ECU ID if successful.""" |
111 | | - try: |
112 | | - _LOGGER.warning("3. show_graphs = %s", show_graphs) |
113 | | - ap_ecu = APsystemsSocket(ecu_host, show_graphs) |
114 | | - # Pass the retries parameter dynamically |
115 | | - _LOGGER.warning("4. show_graphs = %s", show_graphs) |
116 | | - test_query = await ap_ecu.query_ecu(retries, show_graphs) |
117 | | - ecu_id = test_query.get("ecu_id", None) |
118 | | - return ecu_id |
119 | | - |
120 | | - except APsystemsInvalidData as err: |
121 | | - _LOGGER.debug(f"APsystemsInvalidData exception: {err}") |
122 | | - return None |
123 | | - except Exception as err: |
124 | | - _LOGGER.debug(f"Unknown error occurred during ECU query: {err}") |
125 | | - return None |
| 104 | + errors["base"] = "Could not connect to the specified EMA host" |
| 105 | + return self.async_show_form(step_id="init", data_schema=schema, errors=errors) |
0 commit comments