Files
homeassistant_config/config/custom_components/vigieau/config_flow.py
2024-05-31 13:07:35 +02:00

213 lines
7.8 KiB
Python

import logging
from typing import Any, Optional, Tuple
import voluptuous as vol
from homeassistant.core import callback, HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant import config_entries
from .api import InseeApi, AddressApi
from .const import (
DOMAIN,
CONF_LOCATION_MODE,
LOCATION_MODES,
HA_COORD,
ZIP_CODE,
CONF_INSEE_CODE,
CONF_CODE_POSTAL,
CONF_CITY,
DEVICE_ID_KEY,
)
from homeassistant.helpers.selector import LocationSelector
from homeassistant import config_entries
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from .api import InseeApi, AddressApi
from .const import (
DOMAIN,
CONF_LOCATION_MODE,
LOCATION_MODES,
HA_COORD,
ZIP_CODE,
CONF_INSEE_CODE,
CONF_CODE_POSTAL,
CONF_CITY,
SELECT_COORD,
CONF_LOCATION_MAP,
)
_LOGGER = logging.getLogger(__name__)
# Description of the config flow:
# async_step_user is called when user starts to configure the integration
# we follow with a flow of form/menu
# eventually we call async_create_entry with a dictionnary of data
# HA calls async_setup_entry with a ConfigEntry which wraps this data (defined in __init__.py)
# in async_setup_entry we call hass.config_entries.async_forward_entry_setups to setup each relevant platform (sensor in our case)
# HA calls async_setup_entry from sensor.py
LOCATION_SCHEMA = vol.Schema(
{vol.Required(CONF_LOCATION_MODE, default=HA_COORD): vol.In(LOCATION_MODES)}
)
ZIPCODE_SCHEMA = vol.Schema({vol.Required(CONF_CODE_POSTAL, default=""): cv.string})
async def get_insee_code_fromzip(hass: HomeAssistant, data: dict) -> None:
"""Get Insee code from zip code"""
session = async_get_clientsession(hass)
try:
client = InseeApi(session)
return await client.get_data(data)
except ValueError as exc:
raise exc
async def get_insee_code_fromcoord(
hass: HomeAssistant, lat=None, lon=None
) -> Tuple[str, str, float, float]:
"""Get Insee code from GPS coords"""
session = async_get_clientsession(hass)
try:
client = AddressApi(session)
if lat is None or lon is None:
lon = hass.config.as_dict()["longitude"]
lat = hass.config.as_dict()["latitude"]
return await client.get_data(lat, lon)
except ValueError as exc:
raise exc
def _build_place_key(city) -> str:
return f"{city['code']};{city['nom']};{city['centre']['coordinates'][0]};{city['centre']['coordinates'][1]}"
class SetupConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 4
def __init__(self):
"""Initialize"""
self.data = {}
self.city_insee = []
@callback
def _show_setup_form(self, step_id=None, user_input=None, schema=None, errors=None):
"""Show the setup form to the user."""
if user_input is None:
user_input = {}
return self.async_show_form(
step_id=step_id,
data_schema=schema,
errors=errors or {},
)
async def async_step_user(self, user_input: Optional[dict[str, Any]] = None):
"""Called once with None as user_input, then a second time with user provided input"""
errors = {}
if user_input is not None:
self.data[CONF_LOCATION_MODE] = user_input[CONF_LOCATION_MODE]
if user_input[CONF_LOCATION_MODE] == HA_COORD:
try:
city_infos = await get_insee_code_fromcoord(self.hass)
except ValueError:
errors["base"] = "noinsee"
if not errors:
self.data[CONF_INSEE_CODE] = city_infos[0]
self.data[CONF_CITY] = city_infos[1]
self.data[CONF_LATITUDE] = city_infos[2]
self.data[CONF_LONGITUDE] = city_infos[3]
self.data[DEVICE_ID_KEY] = city_infos[0]
return await self.async_step_location(user_input=self.data)
elif user_input[CONF_LOCATION_MODE] == ZIP_CODE:
self.data = user_input
return await self.async_step_location()
elif user_input[CONF_LOCATION_MODE] == SELECT_COORD:
return await self.async_step_map_select()
return self._show_setup_form("user", user_input, LOCATION_SCHEMA, errors)
async def async_step_map_select(self, user_input=None):
COORD_SCHEMA = vol.Schema(
{
vol.Required(
CONF_LOCATION_MAP,
default={
CONF_LATITUDE: self.hass.config.latitude,
CONF_LONGITUDE: self.hass.config.longitude,
},
): LocationSelector()
}
)
errors = {}
if user_input is not None:
try:
city_infos = await get_insee_code_fromcoord(
self.hass,
user_input[CONF_LOCATION_MAP][CONF_LATITUDE],
user_input[CONF_LOCATION_MAP][CONF_LONGITUDE],
)
except ValueError:
errors["base"] = "noinsee"
if not errors:
self.data[CONF_INSEE_CODE] = city_infos[0]
self.data[CONF_CITY] = city_infos[1]
# TODO(kamaradclimber): it's not clear whether we should take lat/long from user input
# or from address api results.
self.data[CONF_LATITUDE] = city_infos[2]
self.data[CONF_LONGITUDE] = city_infos[3]
self.data[DEVICE_ID_KEY] = city_infos[0]
return await self.async_step_location(user_input=self.data)
return self._show_setup_form("map_select", None, COORD_SCHEMA, errors)
async def async_step_location(self, user_input=None):
"""Handle location step"""
errors = {}
if user_input is not None:
city_insee = user_input.get(CONF_INSEE_CODE)
if not city_insee:
# get INSEE Code
try:
self.city_insee = await get_insee_code_fromzip(
self.hass, user_input[CONF_CODE_POSTAL]
)
except ValueError:
errors["base"] = "noinsee"
if not errors:
return await self.async_step_multilocation()
else:
return self._show_setup_form(
"location", user_input, ZIPCODE_SCHEMA, errors
)
return self.async_create_entry(title="vigieau", data=self.data)
return self._show_setup_form("location", None, ZIPCODE_SCHEMA, errors)
async def async_step_multilocation(self, user_input=None):
"""Handle location step"""
errors = {}
locations_for_form = {}
for city in self.city_insee:
locations_for_form[_build_place_key(city)] = f"{city['nom']}"
if not user_input:
if len(self.city_insee) > 1:
return self.async_show_form(
step_id="multilocation",
data_schema=vol.Schema(
{
vol.Required("city", default=[]): vol.In(
locations_for_form
),
}
),
errors=errors,
)
user_input = {CONF_CITY: _build_place_key(self.city_insee[0])}
city_infos = user_input[CONF_CITY].split(";")
self.data[CONF_INSEE_CODE] = city_infos[0]
self.data[CONF_CITY] = city_infos[1]
self.data[CONF_LONGITUDE] = city_infos[2]
self.data[CONF_LATITUDE] = city_infos[3]
self.data[DEVICE_ID_KEY] = city_infos[0]
return await self.async_step_location(self.data)