-
-
Notifications
You must be signed in to change notification settings - Fork 28.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial caldav config flow with broken calendar platform * Set up calendar entities * Remove separate caldav entity * Update tests after merge * Readbility improvements * Address lint issues * Apply suggestions from code review Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Add checking for duplicate configuration entries * Use verify SSL as input into caldav and simplify test setup --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
- Loading branch information
1 parent
06c9719
commit a95aa4e
Showing
12 changed files
with
752 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,61 @@ | ||
"""The caldav component.""" | ||
|
||
import logging | ||
|
||
import caldav | ||
from caldav.lib.error import AuthorizationError, DAVError | ||
import requests | ||
|
||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import ( | ||
CONF_PASSWORD, | ||
CONF_URL, | ||
CONF_USERNAME, | ||
CONF_VERIFY_SSL, | ||
Platform, | ||
) | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady | ||
|
||
from .const import DOMAIN | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
PLATFORMS: list[Platform] = [Platform.CALENDAR] | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Set up CalDAV from a config entry.""" | ||
hass.data.setdefault(DOMAIN, {}) | ||
|
||
client = caldav.DAVClient( | ||
entry.data[CONF_URL], | ||
username=entry.data[CONF_USERNAME], | ||
password=entry.data[CONF_PASSWORD], | ||
ssl_verify_cert=entry.data[CONF_VERIFY_SSL], | ||
) | ||
try: | ||
await hass.async_add_executor_job(client.principal) | ||
except AuthorizationError as err: | ||
if err.reason == "Unauthorized": | ||
raise ConfigEntryAuthFailed("Credentials error from CalDAV server") from err | ||
# AuthorizationError can be raised if the url is incorrect or | ||
# on some other unexpected server response. | ||
_LOGGER.warning("Unexpected CalDAV server response: %s", err) | ||
return False | ||
except requests.ConnectionError as err: | ||
raise ConfigEntryNotReady("Connection error from CalDAV server") from err | ||
except DAVError as err: | ||
raise ConfigEntryNotReady("CalDAV client error") from err | ||
|
||
hass.data[DOMAIN][entry.entry_id] = client | ||
|
||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) | ||
|
||
return True | ||
|
||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Unload a config entry.""" | ||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
"""Configuration flow for CalDav.""" | ||
|
||
from collections.abc import Mapping | ||
import logging | ||
from typing import Any | ||
|
||
import caldav | ||
from caldav.lib.error import AuthorizationError, DAVError | ||
import requests | ||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries | ||
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME, CONF_VERIFY_SSL | ||
from homeassistant.data_entry_flow import FlowResult | ||
from homeassistant.helpers import config_validation as cv | ||
|
||
from .const import DOMAIN | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
STEP_USER_DATA_SCHEMA = vol.Schema( | ||
{ | ||
vol.Required(CONF_URL): str, | ||
vol.Required(CONF_USERNAME): cv.string, | ||
vol.Optional(CONF_PASSWORD, default=""): cv.string, | ||
vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, | ||
} | ||
) | ||
|
||
|
||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Handle a config flow for caldav.""" | ||
|
||
VERSION = 1 | ||
_reauth_entry: config_entries.ConfigEntry | None = None | ||
|
||
async def async_step_user( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Handle the initial step.""" | ||
errors: dict[str, str] = {} | ||
if user_input is not None: | ||
self._async_abort_entries_match( | ||
{ | ||
CONF_URL: user_input[CONF_URL], | ||
CONF_USERNAME: user_input[CONF_USERNAME], | ||
} | ||
) | ||
if error := await self._test_connection(user_input): | ||
errors["base"] = error | ||
else: | ||
return self.async_create_entry( | ||
title=user_input[CONF_USERNAME], data=user_input | ||
) | ||
|
||
return self.async_show_form( | ||
step_id="user", | ||
data_schema=STEP_USER_DATA_SCHEMA, | ||
errors=errors, | ||
) | ||
|
||
async def _test_connection(self, user_input: dict[str, Any]) -> str | None: | ||
"""Test the connection to the CalDAV server and return an error if any.""" | ||
client = caldav.DAVClient( | ||
user_input[CONF_URL], | ||
username=user_input[CONF_USERNAME], | ||
password=user_input[CONF_PASSWORD], | ||
ssl_verify_cert=user_input[CONF_VERIFY_SSL], | ||
) | ||
try: | ||
await self.hass.async_add_executor_job(client.principal) | ||
except AuthorizationError as err: | ||
_LOGGER.warning("Authorization Error connecting to CalDAV server: %s", err) | ||
if err.reason == "Unauthorized": | ||
return "invalid_auth" | ||
# AuthorizationError can be raised if the url is incorrect or | ||
# on some other unexpected server response. | ||
return "cannot_connect" | ||
except requests.ConnectionError as err: | ||
_LOGGER.warning("Connection Error connecting to CalDAV server: %s", err) | ||
return "cannot_connect" | ||
except DAVError as err: | ||
_LOGGER.warning("CalDAV client error: %s", err) | ||
return "cannot_connect" | ||
except Exception: # pylint: disable=broad-except | ||
_LOGGER.exception("Unexpected exception") | ||
return "unknown" | ||
return None | ||
|
||
async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: | ||
"""Perform reauth upon an API authentication error.""" | ||
self._reauth_entry = self.hass.config_entries.async_get_entry( | ||
self.context["entry_id"] | ||
) | ||
return await self.async_step_reauth_confirm() | ||
|
||
async def async_step_reauth_confirm( | ||
self, user_input: dict[str, str] | None = None | ||
) -> FlowResult: | ||
"""Confirm reauth dialog.""" | ||
errors = {} | ||
assert self._reauth_entry | ||
if user_input is not None: | ||
user_input = {**self._reauth_entry.data, **user_input} | ||
|
||
if error := await self._test_connection(user_input): | ||
errors["base"] = error | ||
else: | ||
self.hass.config_entries.async_update_entry( | ||
self._reauth_entry, data=user_input | ||
) | ||
await self.hass.config_entries.async_reload(self._reauth_entry.entry_id) | ||
return self.async_abort(reason="reauth_successful") | ||
|
||
return self.async_show_form( | ||
description_placeholders={ | ||
CONF_USERNAME: self._reauth_entry.data[CONF_USERNAME], | ||
}, | ||
step_id="reauth_confirm", | ||
data_schema=vol.Schema( | ||
{ | ||
vol.Required(CONF_PASSWORD): str, | ||
} | ||
), | ||
errors=errors, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""Constands for CalDAV.""" | ||
|
||
from typing import Final | ||
|
||
DOMAIN: Final = "caldav" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"config": { | ||
"step": { | ||
"user": { | ||
"data": { | ||
"url": "[%key:common::config_flow::data::url%]", | ||
"username": "[%key:common::config_flow::data::username%]", | ||
"password": "[%key:common::config_flow::data::password%]", | ||
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]" | ||
}, | ||
"description": "Please enter your CalDAV server credentials" | ||
}, | ||
"reauth_confirm": { | ||
"description": "The password for {username} is invalid.", | ||
"title": "[%key:common::config_flow::title::reauth%]", | ||
"data": { | ||
"password": "[%key:common::config_flow::data::password%]" | ||
} | ||
} | ||
}, | ||
"create_entry": { | ||
"default": "[%key:common::config_flow::create_entry::authenticated%]" | ||
}, | ||
"error": { | ||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", | ||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", | ||
"unknown": "[%key:common::config_flow::error::unknown%]" | ||
}, | ||
"abort": { | ||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", | ||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,6 +78,7 @@ | |
"bsblan", | ||
"bthome", | ||
"buienradar", | ||
"caldav", | ||
"canary", | ||
"cast", | ||
"cert_expiry", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.