Skip to content

Commit

Permalink
refactor: change the amazon config flow, use less searching (#939)
Browse files Browse the repository at this point in the history
* refactor: change the amazon config flow, use less searching

* update translations

* add reconfigure test

* update test

* fix test

* fix error found with test

* fix typos

* more typo fixing

* add missing step to test

* add check for errors to test

* fix test

* fix test again

* adjust test again

* add all config options to test

* sort the resources in test results

* clean up code

* update config migration
  • Loading branch information
firstof9 authored Jul 6, 2024
1 parent 5bf4fbc commit dc995d5
Show file tree
Hide file tree
Showing 31 changed files with 1,496 additions and 1,900 deletions.
25 changes: 13 additions & 12 deletions custom_components/mail_and_packages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from .const import (
CONF_AMAZON_DAYS,
CONF_AMAZON_DOMAIN,
CONF_AMAZON_FWDS,
CONF_IMAGE_SECURITY,
CONF_IMAP_SECURITY,
Expand All @@ -21,7 +22,6 @@
CONF_VERIFY_SSL,
COORDINATOR,
DEFAULT_AMAZON_DAYS,
DEFAULT_AMAZON_FWDS,
DOMAIN,
ISSUE_URL,
PLATFORMS,
Expand Down Expand Up @@ -57,12 +57,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b

# Variables for data coordinator
config = config_entry.data
host = config.get(CONF_HOST)
the_timeout = config.get(CONF_IMAP_TIMEOUT)
interval = config.get(CONF_SCAN_INTERVAL)

# Setup the data coordinator
coordinator = MailDataUpdateCoordinator(hass, host, the_timeout, interval, config)
coordinator = MailDataUpdateCoordinator(hass, config)

# Fetch initial data so we have data when entities subscribe
await coordinator.async_refresh()
Expand Down Expand Up @@ -103,10 +100,10 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
async def async_migrate_entry(hass, config_entry):
"""Migrate an old config entry."""
version = config_entry.version
new_version = 7
new_version = 8

_LOGGER.debug("Migrating from version %s", version)
updated_config = config_entry.data.copy()
updated_config = {**config_entry.data}

# 1 -> 4: Migrate format
if version == 1:
Expand Down Expand Up @@ -150,7 +147,7 @@ async def async_migrate_entry(hass, config_entry):
if CONF_AMAZON_FWDS in updated_config and updated_config[CONF_AMAZON_FWDS] == [
'""'
]:
updated_config[CONF_AMAZON_FWDS] = DEFAULT_AMAZON_FWDS
updated_config[CONF_AMAZON_FWDS] = []

if version <= 5:
if CONF_VERIFY_SSL not in updated_config:
Expand All @@ -160,6 +157,10 @@ async def async_migrate_entry(hass, config_entry):
if CONF_IMAP_SECURITY not in updated_config:
updated_config[CONF_IMAP_SECURITY] = "SSL"

if version <= 7:
if CONF_AMAZON_DOMAIN not in updated_config:
updated_config[CONF_AMAZON_DOMAIN] = "amazon.com"

if updated_config != config_entry.data:
hass.config_entries.async_update_entry(
config_entry, data=updated_config, version=new_version
Expand All @@ -173,11 +174,11 @@ async def async_migrate_entry(hass, config_entry):
class MailDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching mail data."""

def __init__(self, hass, host, the_timeout, interval, config):
def __init__(self, hass, config):
"""Initialize."""
self.interval = timedelta(minutes=interval)
self.name = f"Mail and Packages ({host})"
self.timeout = the_timeout
self.interval = timedelta(minutes=config.get(CONF_SCAN_INTERVAL))
self.name = f"Mail and Packages ({config.get(CONF_HOST)})"
self.timeout = config.get(CONF_IMAP_TIMEOUT)
self.config = config
self.hass = hass
self._data = {}
Expand Down
120 changes: 106 additions & 14 deletions custom_components/mail_and_packages/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .const import (
CONF_ALLOW_EXTERNAL,
CONF_AMAZON_DAYS,
CONF_AMAZON_DOMAIN,
CONF_AMAZON_FWDS,
CONF_CUSTOM_IMG,
CONF_CUSTOM_IMG_FILE,
Expand All @@ -32,6 +33,7 @@
CONF_VERIFY_SSL,
DEFAULT_ALLOW_EXTERNAL,
DEFAULT_AMAZON_DAYS,
DEFAULT_AMAZON_DOMAIN,
DEFAULT_AMAZON_FWDS,
DEFAULT_CUSTOM_IMG,
DEFAULT_CUSTOM_IMG_FILE,
Expand All @@ -48,6 +50,7 @@

ERROR_MAILBOX_FAIL = "Problem getting mailbox listing using 'INBOX' message"
IMAP_SECURITY = ["none", "startTLS", "SSL"]
AMAZON_SENSORS = ["amazon_packages", "amazon_delivered", "amazon_exception"]
_LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -89,13 +92,16 @@ async def _validate_user_input(user_input: dict) -> tuple:
errors = {}

# Validate amazon forwarding email addresses
if isinstance(user_input[CONF_AMAZON_FWDS], str):
status, amazon_list = await _check_amazon_forwards(user_input[CONF_AMAZON_FWDS])
if status[0] == "ok":
user_input[CONF_AMAZON_FWDS] = amazon_list
else:
user_input[CONF_AMAZON_FWDS] = amazon_list
errors[CONF_AMAZON_FWDS] = status[0]
if CONF_AMAZON_FWDS in user_input:
if isinstance(user_input[CONF_AMAZON_FWDS], str):
status, amazon_list = await _check_amazon_forwards(
user_input[CONF_AMAZON_FWDS]
)
if status[0] == "ok":
user_input[CONF_AMAZON_FWDS] = amazon_list
else:
user_input[CONF_AMAZON_FWDS] = amazon_list
errors[CONF_AMAZON_FWDS] = status[0]

# Check for ffmpeg if option enabled
if user_input[CONF_GENERATE_MP4]:
Expand Down Expand Up @@ -197,10 +203,6 @@ def _get_default(key: str, fallback_default: Any = None) -> None:
vol.Required(
CONF_RESOURCES, default=_get_default(CONF_RESOURCES)
): cv.multi_select(get_resources()),
vol.Optional(
CONF_AMAZON_FWDS, default=_get_default(CONF_AMAZON_FWDS, "(none)")
): cv.string,
vol.Optional(CONF_AMAZON_DAYS, default=_get_default(CONF_AMAZON_DAYS)): int,
vol.Optional(
CONF_SCAN_INTERVAL, default=_get_default(CONF_SCAN_INTERVAL)
): vol.All(vol.Coerce(int), vol.Range(min=5)),
Expand Down Expand Up @@ -242,11 +244,33 @@ def _get_default(key: str, fallback_default: Any = None) -> None:
)


def _get_schema_step_amazon(user_input: list, default_dict: list) -> Any:
"""Get a schema using the default_dict as a backup."""
if user_input is None:
user_input = {}

def _get_default(key: str, fallback_default: Any = None) -> None:
"""Get default value for key."""
return user_input.get(key, default_dict.get(key, fallback_default))

return vol.Schema(
{
vol.Required(
CONF_AMAZON_DOMAIN, default=_get_default(CONF_AMAZON_DOMAIN)
): cv.string,
vol.Optional(
CONF_AMAZON_FWDS, default=_get_default(CONF_AMAZON_FWDS, "(none)")
): cv.string,
vol.Optional(CONF_AMAZON_DAYS, default=_get_default(CONF_AMAZON_DAYS)): int,
}
)


@config_entries.HANDLERS.register(DOMAIN)
class MailAndPackagesFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Config flow for Mail and Packages."""

VERSION = 7
VERSION = 8
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL

def __init__(self):
Expand Down Expand Up @@ -299,7 +323,12 @@ async def async_step_config_2(self, user_input=None):
if user_input is not None:
self._errors, user_input = await _validate_user_input(user_input)
self._data.update(user_input)
_LOGGER.debug("RESOURCES: %s", self._data[CONF_RESOURCES])
if len(self._errors) == 0:
if any(
sensor in self._data[CONF_RESOURCES] for sensor in AMAZON_SENSORS
):
return await self.async_step_config_amazon()
if self._data[CONF_CUSTOM_IMG]:
return await self.async_step_config_3()
return self.async_create_entry(
Expand All @@ -319,8 +348,6 @@ async def _show_config_2(self, user_input):
CONF_DURATION: DEFAULT_GIF_DURATION,
CONF_IMAGE_SECURITY: DEFAULT_IMAGE_SECURITY,
CONF_IMAP_TIMEOUT: DEFAULT_IMAP_TIMEOUT,
CONF_AMAZON_FWDS: DEFAULT_AMAZON_FWDS,
CONF_AMAZON_DAYS: DEFAULT_AMAZON_DAYS,
CONF_GENERATE_MP4: False,
CONF_ALLOW_EXTERNAL: DEFAULT_ALLOW_EXTERNAL,
CONF_CUSTOM_IMG: DEFAULT_CUSTOM_IMG,
Expand Down Expand Up @@ -359,6 +386,36 @@ async def _show_config_3(self, user_input):
errors=self._errors,
)

async def async_step_config_amazon(self, user_input=None):
"""Configure form step amazon."""
self._errors = {}
if user_input is not None:
self._data.update(user_input)
self._errors, user_input = await _validate_user_input(self._data)
if len(self._errors) == 0:
if self._data[CONF_CUSTOM_IMG]:
return await self.async_step_config_3()
return self.async_create_entry(
title=self._data[CONF_HOST], data=self._data
)
return await self._show_config_amazon(user_input)

return await self._show_config_amazon(user_input)

async def _show_config_amazon(self, user_input):
"""Step 3 setup."""
# Defaults
defaults = {
CONF_AMAZON_FWDS: DEFAULT_AMAZON_FWDS,
CONF_AMAZON_DAYS: DEFAULT_AMAZON_DAYS,
}

return self.async_show_form(
step_id="config_amazon",
data_schema=_get_schema_step_amazon(user_input, defaults),
errors=self._errors,
)

async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None):
"""Add reconfigure step to allow to reconfigure a config entry."""
self._entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
Expand Down Expand Up @@ -400,6 +457,10 @@ async def async_step_reconfig_2(self, user_input=None):
self._errors, user_input = await _validate_user_input(user_input)
self._data.update(user_input)
if len(self._errors) == 0:
if any(
sensor in self._data[CONF_RESOURCES] for sensor in AMAZON_SENSORS
):
return await self.async_step_reconfig_amazon()
if self._data[CONF_CUSTOM_IMG]:
return await self.async_step_reconfig_3()
self.hass.config_entries.async_update_entry(
Expand Down Expand Up @@ -454,3 +515,34 @@ async def _show_reconfig_3(self, user_input):
data_schema=_get_schema_step_3(user_input, defaults),
errors=self._errors,
)

async def async_step_reconfig_amazon(self, user_input=None):
"""Configure form step amazon."""
self._errors = {}
if user_input is not None:
self._data.update(user_input)
self._errors, user_input = await _validate_user_input(self._data)
if len(self._errors) == 0:
if self._data[CONF_CUSTOM_IMG]:
return await self.async_step_reconfig_3()
return self.async_create_entry(
title=self._data[CONF_HOST], data=self._data
)
return await self._show_reconfig_amazon(user_input)

return await self._show_reconfig_amazon(user_input)

async def _show_reconfig_amazon(self, user_input):
"""Step 3 setup."""
# Defaults
defaults = {
CONF_AMAZON_DOMAIN: DEFAULT_AMAZON_DOMAIN,
CONF_AMAZON_FWDS: DEFAULT_AMAZON_FWDS,
CONF_AMAZON_DAYS: DEFAULT_AMAZON_DAYS,
}

return self.async_show_form(
step_id="reconfig_amazon",
data_schema=_get_schema_step_amazon(user_input, defaults),
errors=self._errors,
)
2 changes: 2 additions & 0 deletions custom_components/mail_and_packages/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
CONF_AMAZON_DAYS = "amazon_days"
CONF_VERIFY_SSL = "verify_ssl"
CONF_IMAP_SECURITY = "imap_security"
CONF_AMAZON_DOMAIN = "amazon_domain"

# Defaults
DEFAULT_CAMERA_NAME = "Mail USPS Camera"
Expand All @@ -73,6 +74,7 @@
DEFAULT_CUSTOM_IMG = False
DEFAULT_CUSTOM_IMG_FILE = "custom_components/mail_and_packages/images/mail_none.gif"
DEFAULT_AMAZON_DAYS = 3
DEFAULT_AMAZON_DOMAIN = "amazon.com"

# Amazon
AMAZON_DOMAINS = [
Expand Down
Loading

0 comments on commit dc995d5

Please sign in to comment.