Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Local Calendar integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from pathlib import Path
7 import shutil
8 from typing import Any
9 
10 from ical.calendar_stream import CalendarStream
11 from ical.exceptions import CalendarParseError
12 import voluptuous as vol
13 
14 from homeassistant.components.file_upload import process_uploaded_file
15 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
16 from homeassistant.core import HomeAssistant
17 from homeassistant.exceptions import HomeAssistantError
18 from homeassistant.helpers import selector
19 from homeassistant.util import slugify
20 
21 from .const import (
22  ATTR_CREATE_EMPTY,
23  ATTR_IMPORT_ICS_FILE,
24  CONF_CALENDAR_NAME,
25  CONF_ICS_FILE,
26  CONF_IMPORT,
27  CONF_STORAGE_KEY,
28  DOMAIN,
29  STORAGE_PATH,
30 )
31 
32 _LOGGER = logging.getLogger(__name__)
33 
34 STEP_USER_DATA_SCHEMA = vol.Schema(
35  {
36  vol.Required(CONF_CALENDAR_NAME): str,
37  vol.Optional(CONF_IMPORT, default=ATTR_CREATE_EMPTY): selector.SelectSelector(
38  selector.SelectSelectorConfig(
39  options=[
40  ATTR_CREATE_EMPTY,
41  ATTR_IMPORT_ICS_FILE,
42  ],
43  translation_key=CONF_IMPORT,
44  )
45  ),
46  }
47 )
48 
49 STEP_IMPORT_DATA_SCHEMA = vol.Schema(
50  {
51  vol.Required(CONF_ICS_FILE): selector.FileSelector(
52  config=selector.FileSelectorConfig(accept=".ics")
53  ),
54  }
55 )
56 
57 
58 class LocalCalendarConfigFlow(ConfigFlow, domain=DOMAIN):
59  """Handle a config flow for Local Calendar."""
60 
61  VERSION = 1
62 
63  def __init__(self) -> None:
64  """Initialize the config flow."""
65  self.datadata: dict[str, Any] = {}
66 
67  async def async_step_user(
68  self, user_input: dict[str, Any] | None = None
69  ) -> ConfigFlowResult:
70  """Handle the initial step."""
71  if user_input is None:
72  return self.async_show_formasync_show_formasync_show_form(
73  step_id="user", data_schema=STEP_USER_DATA_SCHEMA
74  )
75 
76  key = slugify(user_input[CONF_CALENDAR_NAME])
77  self._async_abort_entries_match_async_abort_entries_match({CONF_STORAGE_KEY: key})
78  user_input[CONF_STORAGE_KEY] = key
79  if user_input.get(CONF_IMPORT) == ATTR_IMPORT_ICS_FILE:
80  self.datadata = user_input
81  return await self.async_step_import_ics_fileasync_step_import_ics_file()
82  return self.async_create_entryasync_create_entryasync_create_entry(
83  title=user_input[CONF_CALENDAR_NAME],
84  data=user_input,
85  )
86 
88  self, user_input: dict[str, Any] | None = None
89  ) -> ConfigFlowResult:
90  """Handle optional iCal (.ics) import."""
91  errors = {}
92  if user_input is not None:
93  try:
94  await self.hass.async_add_executor_job(
95  save_uploaded_ics_file,
96  self.hass,
97  user_input[CONF_ICS_FILE],
98  self.datadata[CONF_STORAGE_KEY],
99  )
100  except HomeAssistantError as err:
101  _LOGGER.debug("Error saving uploaded file: %s", err)
102  errors[CONF_ICS_FILE] = "invalid_ics_file"
103  else:
104  return self.async_create_entryasync_create_entryasync_create_entry(
105  title=self.datadata[CONF_CALENDAR_NAME], data=self.datadata
106  )
107 
108  return self.async_show_formasync_show_formasync_show_form(
109  step_id="import_ics_file",
110  data_schema=STEP_IMPORT_DATA_SCHEMA,
111  errors=errors,
112  )
113 
114 
116  hass: HomeAssistant, uploaded_file_id: str, storage_key: str
117 ):
118  """Validate the uploaded file and move it to the storage directory."""
119 
120  with process_uploaded_file(hass, uploaded_file_id) as file:
121  ics = file.read_text(encoding="utf8")
122  try:
123  CalendarStream.from_ics(ics)
124  except CalendarParseError as err:
125  raise HomeAssistantError("Failed to upload file: Invalid ICS file") from err
126  dest_path = Path(hass.config.path(STORAGE_PATH.format(key=storage_key)))
127  shutil.move(file, dest_path)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:69
ConfigFlowResult async_step_import_ics_file(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:89
ConfigFlowResult async_create_entry(self, *str title, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None, Mapping[str, Any]|None options=None)
None _async_abort_entries_match(self, dict[str, Any]|None match_dict=None)
ConfigFlowResult async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
Iterator[Path] process_uploaded_file(HomeAssistant hass, str file_id)
Definition: __init__.py:36
def save_uploaded_ics_file(HomeAssistant hass, str uploaded_file_id, str storage_key)
Definition: config_flow.py:117