From b5723aa856d66dd95d74a677dd823b280a9b6757 Mon Sep 17 00:00:00 2001 From: Home Assistant Version Control Date: Mon, 27 Jul 2026 13:26:42 +0000 Subject: [PATCH] 61 files --- .../mail_and_packages/__init__.py | 414 ---- .../application_credentials.py | 40 - .../mail_and_packages/binary_sensor.py | 90 - custom_components/mail_and_packages/camera.py | 678 ------ .../mail_and_packages/config_flow.py | 1537 ------------- custom_components/mail_and_packages/const.py | 1983 ----------------- .../mail_and_packages/coordinator.py | 595 ----- .../mail_and_packages/diagnostics.py | 47 - custom_components/mail_and_packages/entity.py | 14 - .../mail_and_packages/helpers.py | 131 -- .../image-no-mailpieces700.jpg | Bin 44385 -> 0 bytes .../mail_and_packages/images/.PLACEHOLDER | 1 - .../mail_and_packages/mail_none.gif | Bin 41094 -> 0 bytes .../mail_and_packages/manifest.json | 22 - .../no_deliveries_amazon.jpg | Bin 35660 -> 0 bytes .../mail_and_packages/no_deliveries_fedex.jpg | Bin 41496 -> 0 bytes .../no_deliveries_generic.jpg | Bin 27660 -> 0 bytes .../mail_and_packages/no_deliveries_ups.jpg | Bin 34834 -> 0 bytes .../no_deliveries_walmart.jpg | Bin 47455 -> 0 bytes .../mail_and_packages/overlay.png | Bin 8764 -> 0 bytes .../mail_and_packages/repairs.py | 58 - custom_components/mail_and_packages/sensor.py | 272 --- .../mail_and_packages/services.yaml | 13 - .../mail_and_packages/shippers/__init__.py | 42 - .../mail_and_packages/shippers/amazon.py | 633 ------ .../mail_and_packages/shippers/base.py | 54 - .../mail_and_packages/shippers/generic.py | 708 ------ .../mail_and_packages/shippers/post_de.py | 372 ---- .../mail_and_packages/shippers/usps.py | 448 ---- .../mail_and_packages/strings.json | 281 --- .../mail_and_packages/translations/ca.json | 287 --- .../mail_and_packages/translations/cs.json | 299 --- .../mail_and_packages/translations/de.json | 293 --- .../mail_and_packages/translations/en.json | 302 --- .../mail_and_packages/translations/es.json | 287 --- .../translations/es_419.json | 287 --- .../mail_and_packages/translations/fi.json | 287 --- .../mail_and_packages/translations/fr.json | 287 --- .../mail_and_packages/translations/hu.json | 287 --- .../mail_and_packages/translations/it.json | 287 --- .../mail_and_packages/translations/ko.json | 287 --- .../mail_and_packages/translations/nl.json | 287 --- .../mail_and_packages/translations/no.json | 287 --- .../mail_and_packages/translations/pl.json | 287 --- .../mail_and_packages/translations/pt.json | 287 --- .../mail_and_packages/translations/pt_BR.json | 287 --- .../mail_and_packages/translations/ru.json | 287 --- .../mail_and_packages/translations/sk.json | 287 --- .../mail_and_packages/translations/sl.json | 287 --- .../mail_and_packages/translations/sv.json | 287 --- .../translations/zh_Hant_HK.json | 287 --- .../mail_and_packages/utils/__init__.py | 1 - .../mail_and_packages/utils/amazon.py | 408 ---- .../mail_and_packages/utils/cache.py | 104 - .../mail_and_packages/utils/date.py | 27 - .../mail_and_packages/utils/email.py | 201 -- .../mail_and_packages/utils/image.py | 489 ---- .../mail_and_packages/utils/imap.py | 769 ------- .../mail_and_packages/utils/shipper.py | 310 --- .../mail_and_packages/vignette.png | Bin 38527 -> 0 bytes custom_components/mail_and_packages/white.png | Bin 171 -> 0 bytes 61 files changed, 16802 deletions(-) delete mode 100644 custom_components/mail_and_packages/__init__.py delete mode 100644 custom_components/mail_and_packages/application_credentials.py delete mode 100644 custom_components/mail_and_packages/binary_sensor.py delete mode 100644 custom_components/mail_and_packages/camera.py delete mode 100644 custom_components/mail_and_packages/config_flow.py delete mode 100644 custom_components/mail_and_packages/const.py delete mode 100644 custom_components/mail_and_packages/coordinator.py delete mode 100644 custom_components/mail_and_packages/diagnostics.py delete mode 100644 custom_components/mail_and_packages/entity.py delete mode 100644 custom_components/mail_and_packages/helpers.py delete mode 100644 custom_components/mail_and_packages/image-no-mailpieces700.jpg delete mode 100644 custom_components/mail_and_packages/images/.PLACEHOLDER delete mode 100644 custom_components/mail_and_packages/mail_none.gif delete mode 100644 custom_components/mail_and_packages/manifest.json delete mode 100644 custom_components/mail_and_packages/no_deliveries_amazon.jpg delete mode 100644 custom_components/mail_and_packages/no_deliveries_fedex.jpg delete mode 100644 custom_components/mail_and_packages/no_deliveries_generic.jpg delete mode 100644 custom_components/mail_and_packages/no_deliveries_ups.jpg delete mode 100644 custom_components/mail_and_packages/no_deliveries_walmart.jpg delete mode 100644 custom_components/mail_and_packages/overlay.png delete mode 100644 custom_components/mail_and_packages/repairs.py delete mode 100644 custom_components/mail_and_packages/sensor.py delete mode 100644 custom_components/mail_and_packages/services.yaml delete mode 100644 custom_components/mail_and_packages/shippers/__init__.py delete mode 100644 custom_components/mail_and_packages/shippers/amazon.py delete mode 100644 custom_components/mail_and_packages/shippers/base.py delete mode 100644 custom_components/mail_and_packages/shippers/generic.py delete mode 100644 custom_components/mail_and_packages/shippers/post_de.py delete mode 100644 custom_components/mail_and_packages/shippers/usps.py delete mode 100644 custom_components/mail_and_packages/strings.json delete mode 100644 custom_components/mail_and_packages/translations/ca.json delete mode 100644 custom_components/mail_and_packages/translations/cs.json delete mode 100644 custom_components/mail_and_packages/translations/de.json delete mode 100644 custom_components/mail_and_packages/translations/en.json delete mode 100644 custom_components/mail_and_packages/translations/es.json delete mode 100644 custom_components/mail_and_packages/translations/es_419.json delete mode 100644 custom_components/mail_and_packages/translations/fi.json delete mode 100644 custom_components/mail_and_packages/translations/fr.json delete mode 100644 custom_components/mail_and_packages/translations/hu.json delete mode 100644 custom_components/mail_and_packages/translations/it.json delete mode 100644 custom_components/mail_and_packages/translations/ko.json delete mode 100644 custom_components/mail_and_packages/translations/nl.json delete mode 100644 custom_components/mail_and_packages/translations/no.json delete mode 100644 custom_components/mail_and_packages/translations/pl.json delete mode 100644 custom_components/mail_and_packages/translations/pt.json delete mode 100644 custom_components/mail_and_packages/translations/pt_BR.json delete mode 100644 custom_components/mail_and_packages/translations/ru.json delete mode 100644 custom_components/mail_and_packages/translations/sk.json delete mode 100644 custom_components/mail_and_packages/translations/sl.json delete mode 100644 custom_components/mail_and_packages/translations/sv.json delete mode 100644 custom_components/mail_and_packages/translations/zh_Hant_HK.json delete mode 100644 custom_components/mail_and_packages/utils/__init__.py delete mode 100644 custom_components/mail_and_packages/utils/amazon.py delete mode 100644 custom_components/mail_and_packages/utils/cache.py delete mode 100644 custom_components/mail_and_packages/utils/date.py delete mode 100644 custom_components/mail_and_packages/utils/email.py delete mode 100644 custom_components/mail_and_packages/utils/image.py delete mode 100644 custom_components/mail_and_packages/utils/imap.py delete mode 100644 custom_components/mail_and_packages/utils/shipper.py delete mode 100644 custom_components/mail_and_packages/vignette.png delete mode 100644 custom_components/mail_and_packages/white.png diff --git a/custom_components/mail_and_packages/__init__.py b/custom_components/mail_and_packages/__init__.py deleted file mode 100644 index 91d48b8..0000000 --- a/custom_components/mail_and_packages/__init__.py +++ /dev/null @@ -1,414 +0,0 @@ -"""Mail and Packages Integration.""" - -import asyncio -import logging - -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_HOST, - CONF_PASSWORD, - CONF_PORT, - CONF_RESOURCES, - CONF_USERNAME, -) -from homeassistant.core import HomeAssistant -from homeassistant.helpers import ( - config_validation as cv, -) -from homeassistant.helpers import ( - device_registry as dr, -) - -from . import const -from .const import ( - ATTR_IMAGE_NAME, - ATTR_IMAGE_PATH, - AUTH_TYPE_PASSWORD, - CONF_AMAZON_CUSTOM_IMG, - CONF_AMAZON_CUSTOM_IMG_FILE, - CONF_AMAZON_DAYS, - CONF_AMAZON_DOMAIN, - CONF_AMAZON_FWDS, - CONF_AUTH_TYPE, - CONF_FEDEX_CUSTOM_IMG, - CONF_FEDEX_CUSTOM_IMG_FILE, - CONF_FOLDER, - CONF_FORWARDED_EMAILS, - CONF_GENERIC_CUSTOM_IMG, - CONF_GENERIC_CUSTOM_IMG_FILE, - CONF_IMAGE_SECURITY, - CONF_IMAP_SECURITY, - CONF_IMAP_TIMEOUT, - CONF_PATH, - CONF_SCAN_INTERVAL, - CONF_STORAGE, - CONF_UPS_CUSTOM_IMG, - CONF_UPS_CUSTOM_IMG_FILE, - CONF_VERIFY_SSL, - CONF_WALMART_CUSTOM_IMG, - CONF_WALMART_CUSTOM_IMG_FILE, - CONFIG_VER, - DEFAULT_AMAZON_CUSTOM_IMG_FILE, - DEFAULT_AMAZON_DAYS, - DEFAULT_FEDEX_CUSTOM_IMG_FILE, - DEFAULT_GENERIC_CUSTOM_IMG_FILE, - DEFAULT_UPS_CUSTOM_IMG_FILE, - DEFAULT_WALMART_CUSTOM_IMG_FILE, - DOMAIN, - ISSUE_URL, - PLATFORMS, - VERSION, -) -from .coordinator import ( - MailAndPackagesConfigEntry, - MailAndPackagesData, - MailDataUpdateCoordinator, -) -from .utils.image import default_image_path, hash_file - -__all__ = [ - "ATTR_IMAGE_NAME", - "ATTR_IMAGE_PATH", - "AUTH_TYPE_PASSWORD", - "CONFIG_VER", - "CONF_AMAZON_CUSTOM_IMG", - "CONF_AMAZON_CUSTOM_IMG_FILE", - "CONF_AMAZON_DAYS", - "CONF_AMAZON_DOMAIN", - "CONF_AMAZON_FWDS", - "CONF_AUTH_TYPE", - "CONF_FEDEX_CUSTOM_IMG", - "CONF_FEDEX_CUSTOM_IMG_FILE", - "CONF_GENERIC_CUSTOM_IMG", - "CONF_GENERIC_CUSTOM_IMG_FILE", - "CONF_IMAGE_SECURITY", - "CONF_IMAP_SECURITY", - "CONF_IMAP_TIMEOUT", - "CONF_PATH", - "CONF_SCAN_INTERVAL", - "CONF_STORAGE", - "CONF_UPS_CUSTOM_IMG", - "CONF_UPS_CUSTOM_IMG_FILE", - "CONF_VERIFY_SSL", - "CONF_WALMART_CUSTOM_IMG", - "CONF_WALMART_CUSTOM_IMG_FILE", - "DEFAULT_AMAZON_CUSTOM_IMG_FILE", - "DEFAULT_AMAZON_DAYS", - "DEFAULT_FEDEX_CUSTOM_IMG_FILE", - "DEFAULT_GENERIC_CUSTOM_IMG_FILE", - "DEFAULT_UPS_CUSTOM_IMG_FILE", - "DEFAULT_WALMART_CUSTOM_IMG_FILE", - "DOMAIN", - "ISSUE_URL", - "PLATFORMS", - "VERSION", - "MailAndPackagesConfigEntry", - "MailAndPackagesData", - "MailDataUpdateCoordinator", - "const", - "default_image_path", - "hash_file", -] - -_LOGGER = logging.getLogger(__name__) - - -CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) - - -async def async_setup(hass: HomeAssistant, config_entry: MailAndPackagesConfigEntry): # pylint: disable=unused-argument - """Disallow configuration via YAML.""" - return True - - -async def async_setup_entry( - hass: HomeAssistant, - config_entry: MailAndPackagesConfigEntry, -) -> bool: - """Load the saved entities.""" - _LOGGER.info( - "Version %s is starting, if you have any issues please report them here: %s", - VERSION, - ISSUE_URL, - ) - # Merge data and options - config = {**config_entry.data, **config_entry.options} - - # Sort the resources - if CONF_RESOURCES in config: - sorted_resources = sorted(config[CONF_RESOURCES]) - if sorted_resources != config[CONF_RESOURCES]: - config[CONF_RESOURCES] = sorted_resources - if CONF_RESOURCES in config_entry.options: - hass.config_entries.async_update_entry( - config_entry, - options={**config_entry.options, CONF_RESOURCES: sorted_resources}, - ) - else: - hass.config_entries.async_update_entry( - config_entry, - data={**config_entry.data, CONF_RESOURCES: sorted_resources}, - ) - - # Setup the data coordinator - coordinator = MailDataUpdateCoordinator(hass, config, config_entry) - - config_entry.runtime_data = MailAndPackagesData(coordinator=coordinator, cameras=[]) - - # Fetch initial data in the background so setup doesn't block - hass.async_create_task(coordinator.async_refresh()) - - await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) - - config_entry.async_on_unload(config_entry.add_update_listener(update_listener)) - - return True - - -async def update_listener( - hass: HomeAssistant, config_entry: MailAndPackagesConfigEntry -) -> None: - """Update listener.""" - _LOGGER.debug("Attempting to reload sensors from the %s integration", DOMAIN) - await hass.config_entries.async_reload(config_entry.entry_id) - - -async def async_remove_config_entry_device( # pylint: disable-next=unused-argument - hass: HomeAssistant, - config_entry: ConfigEntry, - device_entry: dr.DeviceEntry, -) -> bool: - """Remove config entry from a device if its no longer present.""" - return not any( - identifier - for identifier in device_entry.identifiers - if identifier[0] == DOMAIN - and config_entry.runtime_data.get_device(identifier[1]) - ) - - -async def async_unload_entry( - hass: HomeAssistant, - config_entry: MailAndPackagesConfigEntry, -) -> bool: - """Handle removal of an entry.""" - _LOGGER.debug("Attempting to unload sensors from the %s integration", DOMAIN) - - unload_ok = all( - await asyncio.gather( - *[ - hass.config_entries.async_forward_entry_unload(config_entry, platform) - for platform in PLATFORMS - ], - ), - ) - - if unload_ok: - _LOGGER.debug("Successfully removed sensors from the %s integration", DOMAIN) - - return unload_ok - - -async def async_migrate_entry(hass, config_entry): - """Migrate an old config entry.""" - version = config_entry.version - new_version = CONFIG_VER - - _LOGGER.debug("Migrating from version %s", version) - updated_config = {**config_entry.data} - updated_options = {**config_entry.options} - - _migrate_legacy_versions(updated_config, version, config_entry) - _apply_default_config(updated_config) - - # Version 20 migration: split non-IMAP options out of data - if version < 20: - imap_keys = { - CONF_HOST, - CONF_PORT, - CONF_USERNAME, - CONF_PASSWORD, - CONF_IMAP_SECURITY, - CONF_VERIFY_SSL, - CONF_AUTH_TYPE, - "token", - "access_token", - "refresh_token", - "auth_implementation", - } - for key in list(updated_config.keys()): - if key not in imap_keys: - updated_options[key] = updated_config.pop(key) - - if updated_config != config_entry.data or updated_options != config_entry.options: - hass.config_entries.async_update_entry( - config_entry, - data=updated_config, - options=updated_options, - version=new_version, - ) - - _LOGGER.debug("Migration complete to version %s", new_version) - - return True - - -def _migrate_legacy_versions(updated_config, version, config_entry): - """Handle migration of legacy versions.""" - _migrate_versions_1_to_3(updated_config, version, config_entry) - _migrate_versions_4_to_16(updated_config, version) - _migrate_version_17(updated_config, version) - _migrate_version_18(updated_config, version) - - -def _migrate_versions_1_to_3(updated_config, version, config_entry): - """Handle migration for versions 1 to 3.""" - if version == 1: - if CONF_AMAZON_FWDS in updated_config: - if not isinstance(updated_config[CONF_AMAZON_FWDS], list): - updated_config[CONF_AMAZON_FWDS] = [ - x.strip() for x in updated_config[CONF_AMAZON_FWDS].split(",") - ] - else: - updated_config[CONF_AMAZON_FWDS] = [] - else: - _LOGGER.warning("Missing configuration data: %s", CONF_AMAZON_FWDS) - - # Force path change - updated_config[CONF_PATH] = "custom_components/mail_and_packages/images/" - - # Always on image security - if not config_entry.data.get(CONF_IMAGE_SECURITY, False): - updated_config[CONF_IMAGE_SECURITY] = True - - # Add default Amazon Days configuration - updated_config[CONF_AMAZON_DAYS] = DEFAULT_AMAZON_DAYS - - # 2 -> 4 - if version <= 2: - # Force path change - updated_config[CONF_PATH] = "custom_components/mail_and_packages/images/" - - # Always on image security - if not config_entry.data.get(CONF_IMAGE_SECURITY, False): - updated_config[CONF_IMAGE_SECURITY] = True - - # Add default Amazon Days configuration - updated_config[CONF_AMAZON_DAYS] = DEFAULT_AMAZON_DAYS - - if version <= 3: - # Add default Amazon Days configuration - updated_config[CONF_AMAZON_DAYS] = DEFAULT_AMAZON_DAYS - - -def _migrate_versions_4_to_16(updated_config, version): - """Handle migration for versions 4 to 16.""" - _migrate_versions_4_to_7(updated_config, version) - _migrate_versions_15_to_16(updated_config, version) - - -def _migrate_versions_4_to_7(updated_config, version): - """Handle migration for versions 4 to 7.""" - if version <= 4: - if CONF_AMAZON_FWDS in updated_config and updated_config[CONF_AMAZON_FWDS] == [ - '""', - ]: - updated_config[CONF_AMAZON_FWDS] = [] - - if version <= 5: - if CONF_VERIFY_SSL not in updated_config: - updated_config[CONF_VERIFY_SSL] = True - - if version <= 6: - 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" - - -def _migrate_versions_15_to_16(updated_config, version): - """Handle migration for versions 15 to 16.""" - if version <= 15: - if updated_config.get(CONF_IMAP_SECURITY) == "startTLS": - updated_config[CONF_IMAP_SECURITY] = "SSL" - if CONF_AUTH_TYPE not in updated_config: - updated_config[CONF_AUTH_TYPE] = AUTH_TYPE_PASSWORD - - if version <= 16: - if "auth" in updated_config: - auth_data = updated_config.pop("auth") - updated_config.update(auth_data) - - -def _migrate_version_17(updated_config, version): - """Handle migration for version 17.""" - if version <= 17: - fwds = updated_config.get(CONF_FORWARDED_EMAILS) - if isinstance(fwds, str): - if fwds and fwds != "(none)": - updated_config[CONF_FORWARDED_EMAILS] = [ - e.strip() for e in fwds.split(",") if e.strip() - ] - else: - updated_config.pop(CONF_FORWARDED_EMAILS, None) - - -def _migrate_version_18(updated_config, version): - """Handle migration for version 18.""" - if version <= 18: - if CONF_FOLDER in updated_config: - folder = updated_config[CONF_FOLDER] - if isinstance(folder, str): - updated_config[CONF_FOLDER] = folder.strip('"') - elif isinstance(folder, (list, tuple, set)): - updated_config[CONF_FOLDER] = [ - f.strip('"') for f in folder if isinstance(f, str) - ] - - -def _apply_default_config(updated_config): - """Ensure default configurations are present.""" - # Require configs on all migration paths - if CONF_PATH not in updated_config: - updated_config[CONF_PATH] = "custom_components/mail_and_packages/images/" - - if CONF_RESOURCES not in updated_config: - updated_config[CONF_RESOURCES] = [] - - # Add default for image storage config - if CONF_STORAGE not in updated_config: - updated_config[CONF_STORAGE] = "custom_components/mail_and_packages/images/" - - _apply_courier_image_defaults(updated_config) - _apply_walmart_generic_fedex_defaults(updated_config) - - -def _apply_courier_image_defaults(updated_config): - """Apply default Amazon and UPS custom image configurations.""" - if CONF_AMAZON_CUSTOM_IMG not in updated_config: - updated_config[CONF_AMAZON_CUSTOM_IMG] = False - if CONF_AMAZON_CUSTOM_IMG_FILE not in updated_config: - updated_config[CONF_AMAZON_CUSTOM_IMG_FILE] = DEFAULT_AMAZON_CUSTOM_IMG_FILE - if CONF_UPS_CUSTOM_IMG not in updated_config: - updated_config[CONF_UPS_CUSTOM_IMG] = False - if CONF_UPS_CUSTOM_IMG_FILE not in updated_config: - updated_config[CONF_UPS_CUSTOM_IMG_FILE] = DEFAULT_UPS_CUSTOM_IMG_FILE - - -def _apply_walmart_generic_fedex_defaults(updated_config): - """Apply default Walmart, Generic and FedEx custom image configurations.""" - if CONF_WALMART_CUSTOM_IMG not in updated_config: - updated_config[CONF_WALMART_CUSTOM_IMG] = False - if CONF_WALMART_CUSTOM_IMG_FILE not in updated_config: - updated_config[CONF_WALMART_CUSTOM_IMG_FILE] = DEFAULT_WALMART_CUSTOM_IMG_FILE - if CONF_GENERIC_CUSTOM_IMG not in updated_config: - updated_config[CONF_GENERIC_CUSTOM_IMG] = False - if CONF_GENERIC_CUSTOM_IMG_FILE not in updated_config: - updated_config[CONF_GENERIC_CUSTOM_IMG_FILE] = DEFAULT_GENERIC_CUSTOM_IMG_FILE - - if CONF_FEDEX_CUSTOM_IMG not in updated_config: - updated_config[CONF_FEDEX_CUSTOM_IMG] = False - if CONF_FEDEX_CUSTOM_IMG_FILE not in updated_config: - updated_config[CONF_FEDEX_CUSTOM_IMG_FILE] = DEFAULT_FEDEX_CUSTOM_IMG_FILE diff --git a/custom_components/mail_and_packages/application_credentials.py b/custom_components/mail_and_packages/application_credentials.py deleted file mode 100644 index 516255f..0000000 --- a/custom_components/mail_and_packages/application_credentials.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Application credentials for Mail and Packages.""" - -from homeassistant.components.application_credentials import ( - AuthImplementation, - AuthorizationServer, - ClientCredential, -) -from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_entry_oauth2_flow - -from .const import DOMAIN - -OAUTH_SERVERS = { - "oauth2_microsoft": AuthorizationServer( - authorize_url="https://login.microsoftonline.com/common/oauth2/v2.0/authorize", - token_url="https://login.microsoftonline.com/common/oauth2/v2.0/token", - ), - "oauth2_google": AuthorizationServer( - authorize_url="https://accounts.google.com/o/oauth2/v2/auth", - token_url="https://oauth2.googleapis.com/token", - ), -} - - -async def async_get_auth_implementation( - hass: HomeAssistant, - auth_domain: str, - credential: ClientCredential, -) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: - """Return auth implementation based on provider selected in config flow.""" - # Config flow stores the selected provider before triggering OAuth - provider = hass.data[DOMAIN]["oauth_provider"] - server = OAUTH_SERVERS[provider] - - return AuthImplementation( - hass, - auth_domain, - credential, - server, - ) diff --git a/custom_components/mail_and_packages/binary_sensor.py b/custom_components/mail_and_packages/binary_sensor.py deleted file mode 100644 index 8d8657c..0000000 --- a/custom_components/mail_and_packages/binary_sensor.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Binary sensors for Mail and Packages.""" - -import logging - -from homeassistant.components.binary_sensor import ( - BinarySensorEntity, -) -from homeassistant.const import CONF_HOST, CONF_RESOURCES -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) - -from . import MailAndPackagesConfigEntry -from .const import BINARY_SENSORS, DOMAIN, VERSION -from .entity import MailandPackagesBinarySensorEntityDescription - -_LOGGER = logging.getLogger(__name__) - - -async def async_setup_entry(hass, entry: MailAndPackagesConfigEntry, async_add_devices): - """Initialize binary_sensor platform.""" - coordinator = entry.runtime_data.coordinator - resources = coordinator.config.get(CONF_RESOURCES, []) - - binary_sensors = [ - PackagesBinarySensor(value, coordinator, entry) - for value in BINARY_SENSORS.values() - if not value.selectable or value.key in resources - ] - async_add_devices(binary_sensors, False) - - -class PackagesBinarySensor(CoordinatorEntity, BinarySensorEntity): - """Implementation of an Mail and Packages binary sensor.""" - - def __init__( - self, - sensor_description: MailandPackagesBinarySensorEntityDescription, - coordinator: DataUpdateCoordinator, - config: MailAndPackagesConfigEntry, - ) -> None: - """Initialize the sensor.""" - super().__init__(coordinator) - self.coordinator = coordinator - self._config = config - self.entity_description = sensor_description - self._name = sensor_description.name - self._type = sensor_description.key - self._unique_id = config.entry_id - self._host = config.data[CONF_HOST] - - self._attr_name = f"{self._name}" - self._attr_unique_id = ( - f"binary_sensor_{self._host}_{self._type}_{self._unique_id}" - ) - - @property - def device_info(self) -> dict: - """Return device information about the mailbox.""" - return { - "connections": {(DOMAIN, self._unique_id)}, - "name": self._host, - "manufacturer": "IMAP E-Mail", - "sw_version": VERSION, - } - - @property - def should_poll(self) -> bool: - """No need to poll. Coordinator notifies entity of updates.""" - return False - - @property - def available(self) -> bool: - """Return if entity is available.""" - return self.coordinator.data is not None - - @property - def is_on(self) -> bool: - """Return True if the image is updated.""" - if self.coordinator.data is None: - return False - if self._type in self.coordinator.data: - _LOGGER.debug( - "binary_sensor: %s value: %s", - self._type, - self.coordinator.data[self._type], - ) - return bool(self.coordinator.data[self._type]) - return False diff --git a/custom_components/mail_and_packages/camera.py b/custom_components/mail_and_packages/camera.py deleted file mode 100644 index de19263..0000000 --- a/custom_components/mail_and_packages/camera.py +++ /dev/null @@ -1,678 +0,0 @@ -"""Camera that loads a picture from a local file.""" - -from __future__ import annotations - -import logging -import os -from pathlib import Path -from typing import Any - -import anyio -import voluptuous as vol -from homeassistant.components.camera import Camera -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST -from homeassistant.core import ServiceCall -from homeassistant.helpers.update_coordinator import CoordinatorEntity - -from . import MailAndPackagesConfigEntry, const -from .const import ( - ATTR_IMAGE_PATH, - ATTR_USPS_IMAGE, - CAMERA_DATA, - CONF_CUSTOM_IMG, - CONF_CUSTOM_IMG_FILE, - CONF_DURATION, - CONF_POST_DE_CUSTOM_IMG, - CONF_POST_DE_CUSTOM_IMG_FILE, - DOMAIN, - SENSOR_NAME, - VERSION, -) -from .utils.image import cleanup_images, generate_delivery_gif, resize_images - -SERVICE_UPDATE_IMAGE = "update_image" -_LOGGER = logging.getLogger(__name__) - - -async def async_setup_entry( - hass, - config: MailAndPackagesConfigEntry, - async_add_entities, -): - """Set up the Camera that works with local files.""" - coordinator = config.runtime_data.coordinator - camera = [] - - for variable in CAMERA_DATA: - temp_cam = MailCam(hass, variable, config, coordinator) - camera.append(temp_cam) - config.runtime_data.cameras.append(temp_cam) - - async def _update_image(service: ServiceCall) -> None: - """Refresh camera image.""" - _LOGGER.debug("Updating image: %s", service) - cameras = config.runtime_data.cameras - entity_id = None - - if ATTR_ENTITY_ID in service.data: - entity_id = service.data[ATTR_ENTITY_ID] - - # Update all cameras if no entity_id - if entity_id is None: - for cam in cameras: - await cam.update_file_path() - - else: - for cam in cameras: - if cam.entity_id in entity_id: - await cam.update_file_path() - return True - - hass.services.async_register( - DOMAIN, - SERVICE_UPDATE_IMAGE, - _update_image, - schema=vol.Schema( - { - vol.Optional(ATTR_ENTITY_ID): vol.Coerce(str), - }, - ), - ) - - async_add_entities(camera) - - -class MailCam(CoordinatorEntity, Camera): - """Representation of a local file camera.""" - - @property - def config_data(self) -> dict[str, Any]: - """Return merged data and options.""" - return {**self.config.data, **self.config.options} - - def __init__( - self, - hass, - name: str, - config: ConfigEntry, - coordinator, - ) -> None: - """Initialize Local File Camera component.""" - CoordinatorEntity.__init__(self, coordinator) - Camera.__init__(self) - - self.hass = hass - self.config = config - self._name = CAMERA_DATA[name][SENSOR_NAME] - self._type = name - self._host = self.config_data.get(CONF_HOST) - self._unique_id = config.entry_id - - # Derive config keys and default image from camera type name - # Remove "_camera" suffix to get base name (e.g., "usps_camera" -> "usps") - base_name = self._type.removesuffix("_camera") - - # USPS and Post DE use mail_none.gif, others use no_deliveries_*.jpg - if base_name in ("usps", "post_de"): - if base_name == "usps": - custom_img_key = CONF_CUSTOM_IMG - custom_img_file_key = CONF_CUSTOM_IMG_FILE - else: - custom_img_key = CONF_POST_DE_CUSTOM_IMG - custom_img_file_key = CONF_POST_DE_CUSTOM_IMG_FILE - default_image = "mail_none.gif" - else: - # Derive config key names dynamically (e.g., "amazon" -> CONF_AMAZON_CUSTOM_IMG) - custom_img_key = getattr( - const, - f"CONF_{base_name.upper()}_CUSTOM_IMG", - None, - ) - custom_img_file_key = getattr( - const, - f"CONF_{base_name.upper()}_CUSTOM_IMG_FILE", - None, - ) - default_image = f"no_deliveries_{base_name}.jpg" - - # Set custom image paths - self._no_mail = None - if custom_img_key and self.config_data.get(custom_img_key): - self._no_mail = self.config_data.get(custom_img_file_key) - _LOGGER.debug( - "%s camera - custom image enabled: %s", - self._type, - self._no_mail, - ) - - # Set initial file path based on camera type and custom settings - if custom_img_key and self.config_data.get(custom_img_key): - self._file_path = self.config_data.get(custom_img_file_key) - _LOGGER.debug( - "%s camera - initial file path set to: %s", - self._type, - self._file_path, - ) - else: - self._file_path = f"{Path(__file__).parent}/{default_image}" - - # Canonical bundled placeholder for this camera. Always available - # regardless of later _file_path changes, so a missing delivery image - # can fall back to it instead of returning None (which makes the HA - # camera proxy serve HTTP 500). - self._default_image_path = f"{Path(__file__).parent}/{default_image}" - - self._cached_image_path: str | None = None - self._cached_image_bytes: bytes | None = None - self._last_delivery_images: list[str] | None = None - self._is_generic: bool = True - - async def async_camera_image( - self, - width: int | None = None, - height: int | None = None, - ) -> bytes | None: - """Return image response.""" - if ( - self._file_path == self._cached_image_path - and self._cached_image_bytes is not None - ): - return self._cached_image_bytes - - _LOGGER.debug( - "Camera %s reading image from: %s", - self._name, - self._file_path, - ) - - def _read_file(path: str) -> bytes: - with Path(path).open("rb") as f: - data = f.read() - if not data: - # A 0-byte image (e.g. a failed extraction that wrote an empty - # file) is as unservable as a missing one — raise so it routes - # through the same placeholder fallback below. - raise FileNotFoundError(f"empty image file: {path}") - return data - - try: - image_bytes = await self.hass.async_add_executor_job( - _read_file, self._file_path - ) - except FileNotFoundError: - _LOGGER.debug( - "Could not read camera %s image from file: %s; " - "falling back to bundled placeholder %s", - self._name, - self._file_path, - self._default_image_path, - ) - # Fall back to the bundled placeholder so the camera proxy never - # serves HTTP 500 when a delivery image is missing on disk. Skip the - # fallback when the primary path already IS the placeholder (true for - # non-custom cameras, where _file_path and _default_image_path are - # both built from the same default image): re-reading the same - # missing file would only fail again, so return None instead. - if self._file_path != self._default_image_path: - try: - image_bytes = await self.hass.async_add_executor_job( - _read_file, self._default_image_path - ) - except FileNotFoundError: - _LOGGER.warning( - "Camera %s placeholder image also missing: %s", - self._name, - self._default_image_path, - ) - return None - else: - # Cache against the placeholder path so repeated reads are - # cheap; update_file_path() invalidates the cache when the - # primary path changes. - self._cached_image_path = self._default_image_path - self._cached_image_bytes = image_bytes - return image_bytes - return None - else: - self._cached_image_path = self._file_path - self._cached_image_bytes = image_bytes - return image_bytes - - def check_file_path_access(self, file_path: str) -> None: - """Check that filepath given is readable.""" - if not os.access(file_path, os.R_OK): - _LOGGER.debug( - "Could not read camera %s image from file: %s", - self._name, - file_path, - ) - - async def update_file_path(self) -> None: - """Update the file_path.""" - _LOGGER.debug("Camera Update: %s", self._type) - _LOGGER.debug("Custom No Mail: %s", self._no_mail) - - if not self.coordinator.last_update_success: - _LOGGER.debug("Update to update camera image. Unavailable.") - return - - if self.coordinator.data is None: - _LOGGER.debug("Unable to update camera image, no data.") - return - - if self._type == "usps_camera": - self._update_usps_camera() - elif self._type == "generic_camera": - await self._update_generic_camera() - else: - await self._update_standard_camera() - - # Invalidate the cache so the next frontend request re-reads the image from disk. - # This is necessary because for some shippers (like USPS), the image file path - # stays the same but the underlying image content on disk changes. - self._cached_image_path = None - self._cached_image_bytes = None - - self.check_file_path_access(self._file_path) - self.schedule_update_ha_state() - - def _update_usps_camera(self) -> None: - """Update file path for USPS camera.""" - self._file_path = f"{Path(__file__).parent}/mail_none.gif" - self._is_generic = True - required_keys = {ATTR_USPS_IMAGE, ATTR_IMAGE_PATH} - if required_keys.issubset(self.coordinator.data): - image = self.coordinator.data[ATTR_USPS_IMAGE] - path = self.coordinator.data[ATTR_IMAGE_PATH] - self._file_path = self.hass.config.path(path, image) - self._is_generic = not self.coordinator.data.get("usps_update", False) - _LOGGER.debug( - "usps_camera camera - file path set to: %s", - self._file_path, - ) - elif self._no_mail: - self._file_path = self._no_mail - - async def _update_generic_camera(self) -> None: - """Update file path for Generic camera.""" - if self._no_mail: - self._file_path = self._no_mail - self._is_generic = True - _LOGGER.debug("Generic camera - using custom no mail: %s", self._file_path) - return - - delivery_images = self._collect_generic_delivery_images() - - if ( - self._last_delivery_images is not None - and delivery_images == self._last_delivery_images - ): - _LOGGER.debug( - "Generic camera - delivery images unchanged, skipping GIF regeneration" - ) - return - - self._last_delivery_images = delivery_images - - if not delivery_images: - self._file_path = f"{Path(__file__).parent}/no_deliveries_generic.jpg" - self._is_generic = True - _LOGGER.debug( - "Generic camera - no deliveries found, using default: %s", - self._file_path, - ) - return - - image_path = self.coordinator.data.get(ATTR_IMAGE_PATH, "") - full_storage_path = Path(self.hass.config.path(image_path)) - gif_path = str(full_storage_path / "generic_deliveries.gif") - - resized_images = await self.hass.async_add_executor_job( - resize_images, delivery_images, 800, 600 - ) - - duration = self.config_data.get(CONF_DURATION, 5) * 1000 - gif_created = await self.hass.async_add_executor_job( - generate_delivery_gif, - resized_images, - gif_path, - duration, - ) - - if gif_created: - self._file_path = gif_path - self._is_generic = False - _LOGGER.debug( - "Generic camera - created animated GIF with %d delivery images at %s", - len(delivery_images), - gif_path, - ) - else: - _LOGGER.warning( - "Failed to create animated GIF, using first delivery image", - ) - self._file_path = delivery_images[0] - self._is_generic = False - - for img in resized_images: - if await anyio.Path(img).exists(): - await self.hass.async_add_executor_job( - cleanup_images, str(Path(img).parent) + "/", Path(img).name - ) - - def _collect_generic_delivery_images(self) -> list[str]: - """Collect delivery images for the generic camera.""" - delivery_images = [] - enabled_resources = self.config_data.get("resources", []) - - for camera_type in CAMERA_DATA: - # Skip generic, USPS, and Post DE cameras - if camera_type in ("generic_camera", "usps_camera", "post_de_camera"): - continue - - base_name = camera_type.removesuffix("_camera") - delivered_key = f"{base_name}_delivered" - - # Check if this shipper's delivery sensor is enabled - if delivered_key not in enabled_resources: - _LOGGER.debug( - "Generic camera - skipping %s (sensor %s not enabled)", - base_name, - delivered_key, - ) - continue - - # Set image attributes - image_attr_name = f"ATTR_{base_name.upper()}_IMAGE" - image_attr = getattr(const, image_attr_name, None) - path_suffix = f"{base_name}/" - no_mail_check = "no_deliveries" - - required_keys = {image_attr, ATTR_IMAGE_PATH} - if not required_keys.issubset(self.coordinator.data): - continue - - image = self.coordinator.data[image_attr] - path = f"{self.coordinator.data[ATTR_IMAGE_PATH]}{path_suffix}" - delivery_file_path = self.hass.config.path(path, image) - - is_no_mail = image.startswith( - no_mail_check, - ) or self._is_custom_no_mail_image(base_name, delivery_file_path) - - delivery_count_key = f"{base_name}_delivered" - has_current_deliveries = ( - delivery_count_key in self.coordinator.data - and self.coordinator.data[delivery_count_key] > 0 - ) - - if ( - not is_no_mail - and Path(delivery_file_path).exists() - and has_current_deliveries - ): - delivery_images.append(delivery_file_path) - elif is_no_mail: - _LOGGER.debug( - "Generic camera - filtered out %s no-mail image: %s", - base_name, - image, - ) - elif not has_current_deliveries: - _LOGGER.debug( - "Generic camera - filtered out %s (no current deliveries, count=%s): %s", - base_name, - self.coordinator.data.get(delivery_count_key, 0), - image, - ) - - return delivery_images - - async def _update_standard_camera(self) -> None: - """Update file path for standard cameras (Amazon, UPS, etc).""" - base_name = self._type.removesuffix("_camera") - if base_name == "post_de": - self._file_path = f"{Path(__file__).parent}/mail_none.gif" - else: - self._file_path = f"{Path(__file__).parent}/no_deliveries_{base_name}.jpg" - self._is_generic = True - - if self._no_mail: - self._file_path = self._no_mail - _LOGGER.debug( - "%s camera - using custom no mail: %s", - self._type, - self._file_path, - ) - return - - image_attr_name = f"ATTR_{base_name.upper()}_IMAGE" - image_attr = getattr(const, image_attr_name, None) - - if not image_attr: - return - - required_keys = {image_attr, ATTR_IMAGE_PATH} - if not required_keys.issubset(self.coordinator.data): - return - - image = self.coordinator.data[image_attr] - image_path = self.coordinator.data[ATTR_IMAGE_PATH].rstrip("/") + "/" - path = f"{image_path}{base_name}/" - coordinator_file_path = self.hass.config.path(path, image) - - _LOGGER.debug( - "=== %s CAMERA UPDATE === coordinator %s = '%s'", - self._type, - image_attr, - image, - ) - - # Log all image-related keys in coordinator for this camera - all_image_keys = { - k: self.coordinator.data.get(k, "NOT SET") - for k in self.coordinator.data - if "image" in k.lower() - } - _LOGGER.debug( - "%s camera - All image keys in coordinator: %s", - self._type, - all_image_keys, - ) - - if await anyio.Path(coordinator_file_path).exists() and os.access( - coordinator_file_path, - os.R_OK, - ): - self._file_path = coordinator_file_path - _LOGGER.debug( - "%s camera - found coordinator file: %s", - self._type, - self._file_path, - ) - else: - await self._find_alternative_image(coordinator_file_path, image) - - # Coordinator hashes the delivery image against the placeholder to detect - # whether a real email image was saved; use that result rather than guessing - # from file existence (the placeholder may have been copied to the same path). - self._is_generic = not self.coordinator.data.get(f"{base_name}_update", False) - - async def _find_alternative_image( - self, - coordinator_file_path: str, - expected_image: str, - ) -> None: - """Attempt to find an alternative image in the directory.""" - path_dir = Path(coordinator_file_path).parent - _LOGGER.debug( - "%s camera - coordinator file not found: %s", - self._type, - coordinator_file_path, - ) - - # Define a helper to run blocking I/O in the executor - def _scan_images(): - if not path_dir.exists(): - _LOGGER.debug( - "%s camera - directory does not exist: %s", - self._type, - path_dir, - ) - return None - - try: - found_images = [] - for file_path in path_dir.iterdir(): - if file_path.name.lower().endswith( - (".jpg", ".jpeg", ".png", ".gif"), - ): - if file_path.exists() and os.access(file_path, os.R_OK): - if "no_deliveries" not in file_path.name: - found_images.append( - (str(file_path), file_path.stat().st_mtime), - ) - except OSError as err: - _LOGGER.debug( - "%s camera - error listing directory %s: %s", - self._type, - path_dir, - err, - ) - return None - else: - return found_images - - # Execute the scan in a background thread - image_files = await self.hass.async_add_executor_job(_scan_images) - - if image_files: - image_files.sort(key=lambda x: x[1], reverse=True) - self._file_path = image_files[0][0] - _LOGGER.debug( - "%s camera - found alternative image file (most recent): %s", - self._type, - self._file_path, - ) - - def _is_custom_no_mail_image(self, base_name: str, file_path: str) -> bool: - """Check if the given file path is a custom 'no mail' image for the specified camera. - - Args: - base_name: The base name of the camera (e.g., 'amazon', 'ups', 'walmart', 'usps') - file_path: The full file path to check - - Returns: - True if this is a custom 'no mail' image, False otherwise - - """ - # Handle USPS camera differently (uses CONF_CUSTOM_IMG instead of CONF_USPS_CUSTOM_IMG) - if base_name == "usps": - custom_img_key = "CONF_CUSTOM_IMG" - custom_img_file_key = "CONF_CUSTOM_IMG_FILE" - else: - # Handle other cameras (Amazon, UPS, Walmart) - custom_img_key = f"CONF_{base_name.upper()}_CUSTOM_IMG" - custom_img_file_key = f"CONF_{base_name.upper()}_CUSTOM_IMG_FILE" - - custom_img_conf = getattr(const, custom_img_key, None) - custom_img_file_conf = getattr(const, custom_img_file_key, None) - - if ( - custom_img_conf - and custom_img_file_conf - and self.config_data.get(custom_img_conf) - ): - custom_file_path = self.config_data.get(custom_img_file_conf) - if custom_file_path and Path(custom_file_path).exists(): - # Check if the file path matches the custom "no mail" image - return Path(file_path).resolve() == Path(custom_file_path).resolve() - - return False - - def _get_sensor_name_for_camera(self, camera_type: str) -> str: - """Get the sensor name that corresponds to a camera type. - - Args: - camera_type: The camera type (e.g., 'amazon_camera', 'ups_camera', etc.) - - Returns: - The corresponding sensor name, or None if no mapping exists - - """ - # Remove "_camera" suffix to get base name - base_name = camera_type.removesuffix("_camera") - - # Special cases for mail scans cameras - if base_name == "usps": - return "usps_mail" - if base_name == "post_de": - return "post_de_mail" - - # For other cameras, use the pattern: {base_name}_delivered - return f"{base_name}_delivered" - - async def async_added_to_hass(self) -> None: - """Run when entity is added to hass.""" - await super().async_added_to_hass() - if self.coordinator.data is not None: - await self.update_file_path() - - async def async_on_demand_update(self): - """Update state.""" - self.async_schedule_update_ha_state(True) - - @property - def device_info(self) -> dict: - """Return device information about the mailbox.""" - return { - "connections": {(DOMAIN, self._unique_id)}, - "name": self._host, - "manufacturer": "IMAP E-Mail", - "sw_version": VERSION, - } - - @property - def unique_id(self) -> str: - """Return a unique, Home Assistant friendly identifier for this entity.""" - return f"camera_{self._host}_{self._type}_{self._unique_id}" - - @property - def name(self): - """Return the name of this camera.""" - return self._name - - @property - def extra_state_attributes(self): - """Return the camera state attributes.""" - return { - "file_path": self._file_path, - "is_generic": self._is_generic, - } - - @property - def should_poll(self) -> bool: - """Return True if entity has to be polled for state. - - False if entity pushes its state to HA. - """ - return False - - @property - def available(self) -> bool: - """Return if entity is available.""" - return self.coordinator.data is not None - - def _handle_coordinator_update(self) -> None: - """Handle updated data from the coordinator.""" - _LOGGER.debug( - "%s camera - coordinator update received, updating file path", - self._type, - ) - self.hass.async_create_task(self._async_handle_coordinator_update()) - - async def _async_handle_coordinator_update(self) -> None: - """Update file path then write state so the frontend gets the correct image URL.""" - await self.update_file_path() - self.async_write_ha_state() diff --git a/custom_components/mail_and_packages/config_flow.py b/custom_components/mail_and_packages/config_flow.py deleted file mode 100644 index c020381..0000000 --- a/custom_components/mail_and_packages/config_flow.py +++ /dev/null @@ -1,1537 +0,0 @@ -"""Adds config flow for Mail and Packages.""" - -import contextlib -import logging -import ssl -from collections.abc import Mapping -from pathlib import Path -from typing import Any - -import homeassistant.helpers.config_validation as cv -import voluptuous as vol -from aioimaplib import AioImapException -from homeassistant import config_entries -from homeassistant.config_entries import ConfigFlowResult -from homeassistant.const import ( - CONF_HOST, - CONF_PASSWORD, - CONF_PORT, - CONF_RESOURCES, - CONF_USERNAME, -) -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import config_entry_oauth2_flow, selector - -from .const import ( - AUTH_TYPE_OAUTH_GOOGLE, - AUTH_TYPE_OAUTH_MICROSOFT, - AUTH_TYPE_PASSWORD, - CONF_ALLOW_EXTERNAL, - CONF_ALLOW_FORWARDED_EMAILS, - CONF_AMAZON_CUSTOM_IMG, - CONF_AMAZON_CUSTOM_IMG_FILE, - CONF_AMAZON_DAYS, - CONF_AMAZON_DOMAIN, - CONF_AMAZON_FWDS, - CONF_AUTH_TYPE, - CONF_CUSTOM_DAYS, - CONF_CUSTOM_IMG, - CONF_CUSTOM_IMG_FILE, - CONF_DURATION, - CONF_FEDEX_CUSTOM_IMG, - CONF_FEDEX_CUSTOM_IMG_FILE, - CONF_FOLDER, - CONF_FORWARDED_EMAILS, - CONF_FORWARDING_HEADER, - CONF_GENERATE_GRID, - CONF_GENERATE_MP4, - CONF_GENERIC_CUSTOM_IMG, - CONF_GENERIC_CUSTOM_IMG_FILE, - CONF_IMAGE_SECURITY, - CONF_IMAP_SECURITY, - CONF_IMAP_TIMEOUT, - CONF_PATH, - CONF_POST_DE_CUSTOM_IMG, - CONF_POST_DE_CUSTOM_IMG_FILE, - CONF_SCAN_INTERVAL, - CONF_STORAGE, - CONF_UPS_CUSTOM_IMG, - CONF_UPS_CUSTOM_IMG_FILE, - CONF_USPS_PLACEHOLDER, - CONF_VERIFY_SSL, - CONF_WALMART_CUSTOM_IMG, - CONF_WALMART_CUSTOM_IMG_FILE, - CONFIG_VER, - DEFAULT_ALLOW_EXTERNAL, - DEFAULT_ALLOW_FORWARDED_EMAILS, - DEFAULT_AMAZON_CUSTOM_IMG, - DEFAULT_AMAZON_CUSTOM_IMG_FILE, - DEFAULT_AMAZON_DAYS, - DEFAULT_AMAZON_DOMAIN, - DEFAULT_AMAZON_FWDS, - DEFAULT_CUSTOM_DAYS, - DEFAULT_CUSTOM_IMG, - DEFAULT_CUSTOM_IMG_FILE, - DEFAULT_FEDEX_CUSTOM_IMG, - DEFAULT_FEDEX_CUSTOM_IMG_FILE, - DEFAULT_FOLDER, - DEFAULT_FORWARDED_EMAILS, - DEFAULT_FORWARDING_HEADER, - DEFAULT_GENERIC_CUSTOM_IMG, - DEFAULT_GENERIC_CUSTOM_IMG_FILE, - DEFAULT_GIF_DURATION, - DEFAULT_IMAGE_SECURITY, - DEFAULT_IMAP_TIMEOUT, - DEFAULT_PATH, - DEFAULT_PORT, - DEFAULT_POST_DE_CUSTOM_IMG, - DEFAULT_POST_DE_CUSTOM_IMG_FILE, - DEFAULT_SCAN_INTERVAL, - DEFAULT_STORAGE, - DEFAULT_UPS_CUSTOM_IMG, - DEFAULT_UPS_CUSTOM_IMG_FILE, - DEFAULT_USPS_PLACEHOLDER, - DEFAULT_WALMART_CUSTOM_IMG, - DEFAULT_WALMART_CUSTOM_IMG_FILE, - DOMAIN, - OAUTH_IMAP_DEFAULTS, - OAUTH_SCOPES, -) -from .helpers import get_resources -from .utils.email import generate_service_email_domains, validate_email_address -from .utils.image import _check_ffmpeg -from .utils.imap import InvalidAuth, decode_imap_utf7, login, logout - -ERROR_MAILBOX_FAIL = "Problem getting mailbox listing using 'INBOX' message" -IMAP_SECURITY = ["none", "SSL"] -AMAZON_SENSORS = ["amazon_packages", "amazon_delivered", "amazon_exception"] -_LOGGER = logging.getLogger(__name__) -AMAZON_EMAIL_ERROR = ( - "Amazon domain found in email: %s, this may cause errors when searching emails." -) -FORWARDED_EMAIL_ERROR = "A service domain was found in email: %s, this may cause errors when searching emails." # pylint: disable=line-too-long - - -async def _check_amazon_forwards(forwards: str, domain: str) -> tuple: - """Validate and format amazon forward emails for user input. - - Returns tuple: dict of errors, list of email addresses - """ - emails = forwards.split(",") - errors = [] - - # Validate each email address - for email in emails: - email = email.strip() - - if "@" in email: - # Check for amazon domains - if f"@{domain}" in email: - _LOGGER.error( - AMAZON_EMAIL_ERROR, - email, - ) - - # No forwards - elif forwards in ["", "(none)", '""']: - forwards = [] - - else: - _LOGGER.error("Missing '@' in email address: %s", email) - errors.append("invalid_email_format") - - if len(errors) == 0: - errors.append("ok") - - return errors, forwards - - -async def _check_forwarded_emails(user_input: dict[str, Any]) -> list[str]: - """Validate forwarded email addresses provided by the user. - - Use Voluptuous to make sure that none of the forwarded email addresses use domains - that match any of the Mail service domains, as this was known to cause issues when - searching for Amazon emails. - - Args: - user_input (dict[str, Any]): The user input dictionary. - - Returns: - list[str]: A list of error codes (e.g. "missing_forwarded_emails") or "ok" if the - email addresses are valid - - """ - forwarded_emails = user_input[CONF_FORWARDED_EMAILS] - - _LOGGER.debug("checking forwarded emails: '%s'", forwarded_emails) - - # No forwards - if not forwarded_emails: - _LOGGER.error( - "Allowed forwarded emails but no forwards or '(none)' were entered.", - ) - return ["missing_forwarded_emails"] - if forwarded_emails == "(none)": - return ["ok"] - - errors = [] - - service_email_domains = generate_service_email_domains( - user_input.get(CONF_AMAZON_FWDS, []), - ) - - emails = [email.strip() for email in forwarded_emails.split(",")] - for email in emails: - _LOGGER.debug("validating email address %s", email) - if not validate_email_address(email): - _LOGGER.error("%s does not look like a valid email address", email) - errors.append("invalid_email_format") - continue - - domain = email.split("@")[1] - if domain in service_email_domains: - _LOGGER.error( - FORWARDED_EMAIL_ERROR, - email, - ) - if len(errors) == 0: - return ["ok"] - - return errors - - -def _validate_path_input( - user_input: dict, errors: dict, hass: HomeAssistant | None = None -) -> None: - """Validate path and file inputs.""" - # List of (Toggle Key, File Key, Error Key) - file_checks = [ - (CONF_CUSTOM_IMG, CONF_CUSTOM_IMG_FILE, CONF_CUSTOM_IMG_FILE), - ( - CONF_AMAZON_CUSTOM_IMG, - CONF_AMAZON_CUSTOM_IMG_FILE, - CONF_AMAZON_CUSTOM_IMG_FILE, - ), - (CONF_UPS_CUSTOM_IMG, CONF_UPS_CUSTOM_IMG_FILE, CONF_UPS_CUSTOM_IMG_FILE), - ( - CONF_WALMART_CUSTOM_IMG, - CONF_WALMART_CUSTOM_IMG_FILE, - CONF_WALMART_CUSTOM_IMG_FILE, - ), - ( - CONF_FEDEX_CUSTOM_IMG, - CONF_FEDEX_CUSTOM_IMG_FILE, - CONF_FEDEX_CUSTOM_IMG_FILE, - ), - ( - CONF_GENERIC_CUSTOM_IMG, - CONF_GENERIC_CUSTOM_IMG_FILE, - CONF_GENERIC_CUSTOM_IMG_FILE, - ), - ( - CONF_POST_DE_CUSTOM_IMG, - CONF_POST_DE_CUSTOM_IMG_FILE, - CONF_POST_DE_CUSTOM_IMG_FILE, - ), - ] - - for toggle, file_key, error_key in file_checks: - if user_input.get(toggle) and file_key in user_input: - path = user_input[file_key] - if hass: - path = hass.config.path(path) - if not Path(path).is_file(): - errors[error_key] = "file_not_found" - - if CONF_STORAGE in user_input: - path = user_input[CONF_STORAGE] - if hass: - path = hass.config.path(path) - if not Path(path).exists(): - errors[CONF_STORAGE] = "path_not_found" - - -async def _validate_amazon_fwds(user_input: dict, errors: dict) -> None: - """Validate amazon forwarding email addresses in user_input.""" - if CONF_AMAZON_FWDS not in user_input: - return - if not isinstance(user_input[CONF_AMAZON_FWDS], str): - return - status, amazon_list = await _check_amazon_forwards( - user_input[CONF_AMAZON_FWDS], - user_input[CONF_AMAZON_DOMAIN], - ) - user_input[CONF_AMAZON_FWDS] = amazon_list - if status[0] != "ok": - errors[CONF_AMAZON_FWDS] = status[0] - - -async def _validate_forwarded_emails(user_input: dict, errors: dict) -> None: - """Validate forwarded email address list in user_input.""" - if CONF_FORWARDED_EMAILS not in user_input: - return - if not isinstance(user_input[CONF_FORWARDED_EMAILS], str): - return - status = await _check_forwarded_emails(user_input) - if status[0] == "ok" and user_input[CONF_FORWARDED_EMAILS] == "(none)": - # the user changed their mind, remove the flag and config entry - user_input[CONF_ALLOW_FORWARDED_EMAILS] = False - del user_input[CONF_FORWARDED_EMAILS] - elif status[0] == "ok": - user_input[CONF_FORWARDED_EMAILS] = [ - e.strip() for e in user_input[CONF_FORWARDED_EMAILS].split(",") if e.strip() - ] - else: - errors[CONF_FORWARDED_EMAILS] = status[0] - - -async def _validate_user_input( - user_input: dict, hass: HomeAssistant | None = None -) -> tuple: - """Validate user input from config flow. - - Returns tuple with error messages and modified user_input - """ - errors = {} - - await _validate_amazon_fwds(user_input, errors) - - # Check for forwarding header mode first — it takes precedence over address list - forwarding_header = user_input.get(CONF_FORWARDING_HEADER, "") - if isinstance(forwarding_header, str): - forwarding_header = forwarding_header.strip() - if forwarding_header and forwarding_header not in ("(none)", ""): - # Header mode: store the header name, clear address list if present - user_input[CONF_FORWARDING_HEADER] = forwarding_header - user_input.pop(CONF_FORWARDED_EMAILS, None) - else: - # No header provided — clear the key and fall through to address list validation - user_input.pop(CONF_FORWARDING_HEADER, None) - await _validate_forwarded_emails(user_input, errors) - - # Check for ffmpeg if option enabled - if user_input[CONF_GENERATE_MP4]: - if not await _check_ffmpeg(): - errors[CONF_GENERATE_MP4] = "ffmpeg_not_found" - - # Validate file paths - _validate_path_input(user_input, errors, hass) - - # Normalize CONF_FOLDER: if it has exactly 1 folder, store as string - if CONF_FOLDER in user_input: - folder_val = user_input[CONF_FOLDER] - if isinstance(folder_val, (list, set, tuple)): - folder_list = [f for f in folder_val if isinstance(f, str) and f] - if not folder_list: - user_input[CONF_FOLDER] = "INBOX" - elif len(folder_list) == 1: - user_input[CONF_FOLDER] = folder_list[0] - else: - user_input[CONF_FOLDER] = folder_list - elif not isinstance(folder_val, str) or not folder_val: - user_input[CONF_FOLDER] = "INBOX" - - return errors, user_input - - -async def _get_mailboxes( - hass: HomeAssistant, - host: str, - port: int, - user: str, - pwd: str, - security: str, - verify: bool, - oauth_token: str | None = None, -) -> list: - """Get list of mailbox folders from mail server.""" - _LOGGER.debug("Getting mailboxes, login...") - try: - account = await login( - hass, - host, - port, - user, - pwd, - security, - verify, - oauth_token=oauth_token, - ) - - except (TimeoutError, AioImapException, ConnectionRefusedError, InvalidAuth) as err: - _LOGGER.error( - "Unable to connect: %s (IMAP server %s:%s as %s, security: %s, oauth: %s, class: %s)", - err if str(err) else "Authentication failed or timed out", - host, - port, - user, - security, - oauth_token is not None, - type(err).__name__, - ) - return [] - - _LOGGER.debug("Attempting to get mailbox list...") - try: - result = await account.list('""', '"*"') - status = result.result - folderlist = result.lines - _LOGGER.debug("Get mailbox status: %s folder list: %s", status, folderlist) - mailboxes = [] - if status != "OK" or not isinstance(folderlist, list): - _LOGGER.error("Error listing mailboxes ... using default") - mailboxes.append(DEFAULT_FOLDER) - else: - mailboxes = await _parse_folder_list(folderlist) - finally: - await logout(account) - - return mailboxes - - -async def _parse_folder_list(folderlist: list) -> list: - """Parse folder list from IMAP server response.""" - mailboxes = [] - with contextlib.suppress(IndexError): - mailboxes.extend( - decode_imap_utf7(i.decode().split(' "/" ')[1].strip('"')) - for i in folderlist - ) - - with contextlib.suppress(IndexError): - mailboxes.extend( - decode_imap_utf7(i.decode().split(' "." ')[1].strip('"')) - for i in folderlist - ) - - if len(mailboxes) == 0: - _LOGGER.error("Problem reading mailbox folders, using default.") - mailboxes.append(DEFAULT_FOLDER) - - return mailboxes - - -def _get_schema_auth(user_input: list, default_dict: list) -> Any: - """Get the first schema for auth type.""" - if user_input is None: - user_input = {} - - def _get_default(key: str, fallback_default: Any = None) -> None: - return user_input.get(key, default_dict.get(key, fallback_default)) - - return vol.Schema( - { - vol.Required( - CONF_AUTH_TYPE, - default=_get_default(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD), - ): selector.SelectSelector( - selector.SelectSelectorConfig( - options=[ - selector.SelectOptionDict( - value=AUTH_TYPE_PASSWORD, - label="Password", - ), - selector.SelectOptionDict( - value=AUTH_TYPE_OAUTH_MICROSOFT, - label="OAuth2 - Microsoft (Outlook/Exchange)", - ), - selector.SelectOptionDict( - value=AUTH_TYPE_OAUTH_GOOGLE, - label="OAuth2 - Google (Gmail)", - ), - ], - mode=selector.SelectSelectorMode.DROPDOWN, - translation_key="auth_type", - ), - ), - }, - ) - - -def _get_schema_imap(user_input: list, default_dict: list) -> Any: - """Get the secondary schema for IMAP configuration.""" - if user_input is None: - user_input = {} - - def _get_default(key: str, fallback_default: Any = None) -> None: - return user_input.get(key, default_dict.get(key, fallback_default)) - - # Determine if password field should be shown based on chosen auth_type - auth_type = _get_default(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD) - schema = { - vol.Required(CONF_HOST, default=_get_default(CONF_HOST)): cv.string, - vol.Required(CONF_PORT, default=_get_default(CONF_PORT, 993)): cv.port, - vol.Required(CONF_USERNAME, default=_get_default(CONF_USERNAME)): cv.string, - } - - if auth_type == AUTH_TYPE_PASSWORD: - schema[vol.Required(CONF_PASSWORD, default=_get_default(CONF_PASSWORD, ""))] = ( - cv.string - ) - - schema.update( - { - vol.Required( - CONF_IMAP_SECURITY, - default=_get_default(CONF_IMAP_SECURITY), - ): vol.In(IMAP_SECURITY), - vol.Optional( - CONF_VERIFY_SSL, - default=_get_default(CONF_VERIFY_SSL, False), - ): cv.boolean, - }, - ) - - return vol.Schema(schema) - - -class multi_folder_select(cv.multi_select): - """Multi select validator that allows a single string and converts it to a list, stripping quotes.""" - - def __call__(self, value: Any) -> list[Any]: - """Validate and format the folder selection value.""" - if isinstance(value, str): - value = [value.strip('"')] - elif isinstance(value, (list, tuple, set)): - value = [v.strip('"') if isinstance(v, str) else v for v in value] - return super().__call__(value) - - -async def _get_schema_step_2( - data: list, - user_input: list, - default_dict: list, - hass: HomeAssistant, - entry: config_entries.ConfigEntry | None = None, -) -> 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)) - - oauth_token = None - if entry: - try: - implementation = ( - await config_entry_oauth2_flow.async_get_config_entry_implementation( - hass, - entry, - ) - ) - session = config_entry_oauth2_flow.OAuth2Session( - hass, - entry, - implementation, - ) - await session.async_ensure_token_valid() - oauth_token = session.token.get("access_token") - except Exception as err: # noqa: BLE001 - _LOGGER.error("Error refreshing OAuth token: %s", err) - - if not oauth_token: - if "token" in data and isinstance(data["token"], dict): - oauth_token = data["token"].get("access_token") - else: - oauth_token = data.get("access_token") - - mailboxes = await _get_mailboxes( - hass, - data[CONF_HOST], - data[CONF_PORT], - data[CONF_USERNAME], - data.get(CONF_PASSWORD, ""), - data[CONF_IMAP_SECURITY], - data[CONF_VERIFY_SSL], - oauth_token, - ) - - default_folder = _get_default(CONF_FOLDER) - if isinstance(default_folder, str): - default_folder = [default_folder.strip('"')] - elif isinstance(default_folder, (list, tuple, set)): - default_folder = [ - f.strip('"') for f in default_folder if isinstance(f, str) and f - ] - else: - default_folder = [] - - # Clean up legacy folders that don't exist anymore/prevent validation failures - default_folder = [f for f in default_folder if f in mailboxes] - - # Pre-select "INBOX" if default_folder is empty and "INBOX" is available - if not default_folder and "INBOX" in mailboxes: - default_folder = ["INBOX"] - - return vol.Schema( - { - vol.Required(CONF_FOLDER, default=default_folder): multi_folder_select( - {m: m for m in mailboxes} - ), - vol.Required( - CONF_RESOURCES, - default=_get_default(CONF_RESOURCES), - ): cv.multi_select(get_resources()), - vol.Optional( - CONF_SCAN_INTERVAL, - default=_get_default(CONF_SCAN_INTERVAL), - ): vol.All(vol.Coerce(int), vol.Range(min=5)), - vol.Optional( - CONF_CUSTOM_DAYS, - default=_get_default(CONF_CUSTOM_DAYS, DEFAULT_CUSTOM_DAYS), - ): vol.All(vol.Coerce(int), vol.Range(min=1)), - vol.Optional( - CONF_IMAP_TIMEOUT, - default=_get_default(CONF_IMAP_TIMEOUT), - ): vol.All(vol.Coerce(int), vol.Range(min=10)), - vol.Optional( - CONF_DURATION, - default=_get_default(CONF_DURATION), - ): vol.Coerce(int), - vol.Optional( - CONF_ALLOW_FORWARDED_EMAILS, - default=_get_default(CONF_ALLOW_FORWARDED_EMAILS, False), - ): cv.boolean, - vol.Optional( - CONF_GENERATE_GRID, - default=_get_default(CONF_GENERATE_GRID, False), - ): cv.boolean, - vol.Optional( - CONF_GENERATE_MP4, - default=_get_default(CONF_GENERATE_MP4, False), - ): cv.boolean, - vol.Optional( - CONF_USPS_PLACEHOLDER, - default=_get_default(CONF_USPS_PLACEHOLDER, DEFAULT_USPS_PLACEHOLDER), - ): cv.boolean, - vol.Optional( - CONF_ALLOW_EXTERNAL, - default=_get_default(CONF_ALLOW_EXTERNAL, False), - ): cv.boolean, - vol.Optional( - CONF_CUSTOM_IMG, - default=_get_default(CONF_CUSTOM_IMG, False), - ): cv.boolean, - vol.Optional( - CONF_AMAZON_CUSTOM_IMG, - default=_get_default(CONF_AMAZON_CUSTOM_IMG, False), - ): cv.boolean, - vol.Optional( - CONF_UPS_CUSTOM_IMG, - default=_get_default(CONF_UPS_CUSTOM_IMG, False), - ): cv.boolean, - vol.Optional( - CONF_WALMART_CUSTOM_IMG, - default=_get_default(CONF_WALMART_CUSTOM_IMG, False), - ): cv.boolean, - vol.Optional( - CONF_FEDEX_CUSTOM_IMG, - default=_get_default(CONF_FEDEX_CUSTOM_IMG, False), - ): cv.boolean, - vol.Optional( - CONF_GENERIC_CUSTOM_IMG, - default=_get_default(CONF_GENERIC_CUSTOM_IMG, False), - ): cv.boolean, - vol.Optional( - CONF_POST_DE_CUSTOM_IMG, - default=_get_default(CONF_POST_DE_CUSTOM_IMG, False), - ): cv.boolean, - }, - ) - - -def _get_schema_step_3(user_input: dict, default_dict: dict) -> 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) -> str: - """Get default value for key.""" - return user_input.get(key, default_dict.get(key, fallback_default)) - - schema = {} - - # Only show custom image file field if custom image is enabled - if user_input.get(CONF_CUSTOM_IMG): - schema[ - vol.Optional( - CONF_CUSTOM_IMG_FILE, - default=_get_default(CONF_CUSTOM_IMG_FILE, DEFAULT_CUSTOM_IMG_FILE), - ) - ] = cv.string - - # Only show Amazon custom image file field if Amazon custom image is enabled - if user_input.get(CONF_AMAZON_CUSTOM_IMG): - schema[ - vol.Optional( - CONF_AMAZON_CUSTOM_IMG_FILE, - default=_get_default( - CONF_AMAZON_CUSTOM_IMG_FILE, - DEFAULT_AMAZON_CUSTOM_IMG_FILE, - ), - ) - ] = cv.string - - # Only show UPS custom image file field if UPS custom image is enabled - if user_input.get(CONF_UPS_CUSTOM_IMG): - schema[ - vol.Optional( - CONF_UPS_CUSTOM_IMG_FILE, - default=_get_default( - CONF_UPS_CUSTOM_IMG_FILE, - DEFAULT_UPS_CUSTOM_IMG_FILE, - ), - ) - ] = cv.string - - # Only show Walmart custom image file field if Walmart custom image is enabled - if user_input.get(CONF_WALMART_CUSTOM_IMG): - schema[ - vol.Optional( - CONF_WALMART_CUSTOM_IMG_FILE, - default=_get_default( - CONF_WALMART_CUSTOM_IMG_FILE, - DEFAULT_WALMART_CUSTOM_IMG_FILE, - ), - ) - ] = cv.string - - # Only show FedEx custom image file field if FedEx custom image is enabled - if user_input.get(CONF_FEDEX_CUSTOM_IMG): - schema[ - vol.Optional( - CONF_FEDEX_CUSTOM_IMG_FILE, - default=_get_default( - CONF_FEDEX_CUSTOM_IMG_FILE, - DEFAULT_FEDEX_CUSTOM_IMG_FILE, - ), - ) - ] = cv.string - - # Only show Generic custom image file field if Generic custom image is enabled - if user_input.get(CONF_GENERIC_CUSTOM_IMG): - schema[ - vol.Optional( - CONF_GENERIC_CUSTOM_IMG_FILE, - default=_get_default( - CONF_GENERIC_CUSTOM_IMG_FILE, - DEFAULT_GENERIC_CUSTOM_IMG_FILE, - ), - ) - ] = cv.string - - # Only show Post DE custom image file field if Post DE custom image is enabled - if user_input.get(CONF_POST_DE_CUSTOM_IMG): - schema[ - vol.Optional( - CONF_POST_DE_CUSTOM_IMG_FILE, - default=_get_default( - CONF_POST_DE_CUSTOM_IMG_FILE, - DEFAULT_POST_DE_CUSTOM_IMG_FILE, - ), - ) - ] = cv.string - - return vol.Schema(schema) - - -def _get_schema_step_amazon( - user_input: list, - default_dict: list, - forwarding_header: str = "", -) -> 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)) - - schema_dict: dict = { - vol.Required( - CONF_AMAZON_DOMAIN, - default=_get_default(CONF_AMAZON_DOMAIN), - ): cv.string, - } - - if not forwarding_header or forwarding_header == "(none)": - schema_dict[ - vol.Optional( - CONF_AMAZON_FWDS, - default=_get_default(CONF_AMAZON_FWDS), - ) - ] = cv.string - - schema_dict[ - vol.Optional(CONF_AMAZON_DAYS, default=_get_default(CONF_AMAZON_DAYS)) - ] = int - - return vol.Schema(schema_dict) - - -def _get_schema_step_forwarded_emails( - user_input: list, - default_dict: list, -) -> vol.Schema: - """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) -> list: - """Get default value for key.""" - value = user_input.get(key, default_dict.get(key, fallback_default)) - if isinstance(value, list): - value = ", ".join(value) - return value - - return vol.Schema( - { - vol.Optional( - CONF_FORWARDING_HEADER, - default=_get_default(CONF_FORWARDING_HEADER, DEFAULT_FORWARDING_HEADER), - ): cv.string, - vol.Optional( - CONF_FORWARDED_EMAILS, - default=_get_default(CONF_FORWARDED_EMAILS, DEFAULT_FORWARDED_EMAILS), - ): cv.string, - }, - ) - - -def _get_schema_step_storage(user_input: dict, default_dict: dict) -> 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_STORAGE, - default=_get_default(CONF_STORAGE, DEFAULT_STORAGE), - ): cv.string, - }, - ) - - -async def _validate_login( - hass: HomeAssistant, - user_input: dict[str, Any], -) -> dict[str, str]: - """Validate login credentials.""" - errors = {} - _LOGGER.debug("Testing login...") - - auth_type = user_input.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD) - - # Skip login validation for OAuth2 — auth happens via the OAuth flow - if auth_type != AUTH_TYPE_PASSWORD: - return errors - - imap_client = None - try: - imap_client = await login( - hass, - host=user_input[CONF_HOST], - port=user_input[CONF_PORT], - user=user_input[CONF_USERNAME], - pwd=user_input[CONF_PASSWORD], - security=user_input[CONF_IMAP_SECURITY], - verify=user_input[CONF_VERIFY_SSL], - ) - result, data = await imap_client.select() - - except InvalidAuth: - errors[CONF_USERNAME] = errors[CONF_PASSWORD] = "invalid_auth" - except ssl.SSLError: - errors["base"] = "ssl_error" - except (TimeoutError, AioImapException, ConnectionRefusedError) as err: - _LOGGER.error( - "Unable to connect: %s (IMAP server %s:%s as %s, security: %s, oauth: False, class: %s)", - err if str(err) else "Authentication failed or timed out", - user_input[CONF_HOST], - user_input[CONF_PORT], - user_input[CONF_USERNAME], - user_input[CONF_IMAP_SECURITY], - type(err).__name__, - ) - errors["base"] = "cannot_connect" - else: - if result != "OK": - errors["base"] = "missing_inbox" - finally: - if imap_client is not None: - await logout(imap_client) - - return errors - - -@config_entries.HANDLERS.register(DOMAIN) -class MailAndPackagesFlowHandler( - config_entry_oauth2_flow.AbstractOAuth2FlowHandler, - domain=DOMAIN, -): - """Config flow for Mail and Packages.""" - - VERSION = CONFIG_VER - CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL - DOMAIN = DOMAIN - - @staticmethod - @callback - def async_get_options_flow( - config_entry: config_entries.ConfigEntry, - ) -> config_entries.OptionsFlow: - """Get the options flow for this handler.""" - return MailAndPackagesOptionsFlow(config_entry) - - @property - def logger(self) -> logging.Logger: - """Return logger.""" - return _LOGGER - - @property - def extra_authorize_data(self) -> dict: - """Extra data that needs to be appended to the authorize url.""" - auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD) - scopes = OAUTH_SCOPES.get(auth_type, "") - data = {"scope": scopes} - if auth_type == AUTH_TYPE_OAUTH_GOOGLE: - data.update( - { - "access_type": "offline", - "prompt": "consent", - } - ) - elif auth_type == AUTH_TYPE_OAUTH_MICROSOFT: - data.update( - { - "prompt": "select_account", - } - ) - return data - - def __init__(self): - """Initialize.""" - super().__init__() - self._entry = None - self._data = {} - self._errors = {} - - async def async_step_user(self, user_input=None): - """Handle a flow initialized by the user.""" - self._errors = {} - - if user_input is not None: - self._data.update(user_input) - return await self.async_step_imap_config() - - return await self._show_auth_form(user_input) - - async def async_step_reauth( - self, entry_data: Mapping[str, Any] - ) -> ConfigFlowResult: - """Handle re-authentication.""" - self._entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) - self._data.update(entry_data) - return await self.async_step_reauth_confirm() - - async def async_step_reauth_confirm( - self, user_input: dict[str, Any] | None = None - ) -> ConfigFlowResult: - """Confirm re-authentication.""" - self._errors = {} - auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD) - - if user_input is not None: - self._data.update(user_input) - if auth_type != AUTH_TYPE_PASSWORD: - return await self.async_step_pick_implementation() - - self._errors = await _validate_login(self.hass, self._data) - if not self._errors: - return self.async_update_reload_and_abort( - self._entry, - data=self._data, - ) - - if auth_type != AUTH_TYPE_PASSWORD: - return self.async_show_form(step_id="reauth_confirm") - - schema = vol.Schema( - { - vol.Required(CONF_USERNAME, default=self._data.get(CONF_USERNAME)): str, - vol.Required(CONF_PASSWORD): str, - } - ) - return self.async_show_form( - step_id="reauth_confirm", data_schema=schema, errors=self._errors - ) - - async def async_step_imap_config(self, user_input=None): - """Handle IMAP config step after auth selection.""" - self._errors = {} - - if user_input is not None: - self._data.update(user_input) - auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD) - - if auth_type != AUTH_TYPE_PASSWORD: - # OAuth2 flow: store provider and auto-fill IMAP defaults - self._data[CONF_VERIFY_SSL] = True - self.hass.data.setdefault(DOMAIN, {}) - self.hass.data[DOMAIN]["oauth_provider"] = auth_type - return await self.async_step_pick_implementation() - - # Password flow: validate login - self._errors = await _validate_login( - self.hass, - self._data, - ) - if self._errors == {}: - return await self.async_step_config_2() - - return await self._show_imap_form(user_input) - - return await self._show_imap_form(user_input) - - async def async_oauth_create_entry( - self, - data: dict, - ) -> config_entries.ConfigFlowResult: - """Handle OAuth2 completion — store token and continue to step 2.""" - self._data.update(data) - if self._entry: - # Reconfigure or Reauth flow: update entry and reload - self.hass.config_entries.async_update_entry( - self._entry, - data=self._data, - ) - await self.hass.config_entries.async_reload(self._entry.entry_id) - if self.context.get("source") == config_entries.SOURCE_REAUTH: - return self.async_abort(reason="reauth_successful") - _LOGGER.debug("%s reconfigured.", DOMAIN) - return self.async_abort(reason="reconfigure_successful") - return await self.async_step_config_2() - - async def _show_auth_form(self, user_input): - """Show the authentication form.""" - defaults = {CONF_AUTH_TYPE: AUTH_TYPE_PASSWORD} - return self.async_show_form( - step_id="user", - data_schema=_get_schema_auth(user_input, defaults), - errors=self._errors, - ) - - async def _show_imap_form(self, user_input): - """Show the configuration form to edit IMAP configuration data.""" - auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD) - defaults = OAUTH_IMAP_DEFAULTS.get( - auth_type, - { - CONF_PORT: DEFAULT_PORT, - CONF_IMAP_SECURITY: "SSL", - CONF_VERIFY_SSL: False, - }, - ) - defaults[CONF_AUTH_TYPE] = auth_type - - return self.async_show_form( - step_id="imap_config", - data_schema=_get_schema_imap(user_input, defaults), - errors=self._errors, - ) - - async def async_step_config_2(self, user_input=None): - """Configure form step 2.""" - self._errors = {} - if user_input is not None: - self._errors, user_input = await _validate_user_input(user_input, self.hass) - self._data.update(user_input) - _LOGGER.debug("RESOURCES: %s", self._data[CONF_RESOURCES]) - if len(self._errors) == 0: - if self._data[CONF_ALLOW_FORWARDED_EMAILS]: - return await self.async_step_config_forwarded_emails() - if any( - sensor in self._data[CONF_RESOURCES] for sensor in AMAZON_SENSORS - ): - return await self.async_step_config_amazon() - has_custom_image = ( - self._data.get(CONF_CUSTOM_IMG) - or self._data.get(CONF_AMAZON_CUSTOM_IMG) - or self._data.get(CONF_UPS_CUSTOM_IMG) - or self._data.get(CONF_WALMART_CUSTOM_IMG) - or self._data.get(CONF_FEDEX_CUSTOM_IMG) - or self._data.get(CONF_GENERIC_CUSTOM_IMG) - or self._data.get(CONF_POST_DE_CUSTOM_IMG) - ) - if has_custom_image: - return await self.async_step_config_3() - - return self.async_create_entry( - title=f"Mail and Packages ({self._data[CONF_HOST]})", - data=self._data, - ) - return await self._show_config_2(user_input) - - return await self._show_config_2(user_input) - - async def _show_config_2(self, user_input): - """Step 2 setup.""" - # Defaults - defaults = { - CONF_FOLDER: DEFAULT_FOLDER, - CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL, - CONF_CUSTOM_DAYS: DEFAULT_CUSTOM_DAYS, - CONF_PATH: self.hass.config.path(DEFAULT_PATH), - CONF_DURATION: DEFAULT_GIF_DURATION, - CONF_IMAGE_SECURITY: DEFAULT_IMAGE_SECURITY, - CONF_IMAP_TIMEOUT: DEFAULT_IMAP_TIMEOUT, - CONF_GENERATE_GRID: False, - CONF_GENERATE_MP4: False, - CONF_USPS_PLACEHOLDER: DEFAULT_USPS_PLACEHOLDER, - CONF_ALLOW_EXTERNAL: DEFAULT_ALLOW_EXTERNAL, - CONF_CUSTOM_IMG: DEFAULT_CUSTOM_IMG, - CONF_AMAZON_CUSTOM_IMG: DEFAULT_AMAZON_CUSTOM_IMG, - CONF_UPS_CUSTOM_IMG: DEFAULT_UPS_CUSTOM_IMG, - CONF_WALMART_CUSTOM_IMG: DEFAULT_WALMART_CUSTOM_IMG, - CONF_FEDEX_CUSTOM_IMG: DEFAULT_FEDEX_CUSTOM_IMG, - CONF_GENERIC_CUSTOM_IMG: DEFAULT_GENERIC_CUSTOM_IMG, - CONF_POST_DE_CUSTOM_IMG: DEFAULT_POST_DE_CUSTOM_IMG, - CONF_ALLOW_FORWARDED_EMAILS: DEFAULT_ALLOW_FORWARDED_EMAILS, - } - - return self.async_show_form( - step_id="config_2", - data_schema=await _get_schema_step_2( - self._data, - user_input, - defaults, - self.hass, - getattr(self, "_entry", None), - ), - errors=self._errors, - ) - - async def async_step_config_3(self, user_input=None): - """Configure form step 2.""" - self._errors = {} - if user_input is not None: - self._data.update(user_input) - self._errors, user_input = await _validate_user_input(self._data, self.hass) - if len(self._errors) == 0: - return await self.async_step_config_storage() - return await self._show_config_3(user_input) - - return await self._show_config_3(user_input) - - async def _show_config_3(self, user_input=None): # pylint: disable=unused-argument - """Step 3 setup.""" - # Defaults - defaults = { - CONF_CUSTOM_IMG_FILE: DEFAULT_CUSTOM_IMG_FILE, - CONF_AMAZON_CUSTOM_IMG_FILE: DEFAULT_AMAZON_CUSTOM_IMG_FILE, - CONF_UPS_CUSTOM_IMG_FILE: DEFAULT_UPS_CUSTOM_IMG_FILE, - CONF_WALMART_CUSTOM_IMG_FILE: DEFAULT_WALMART_CUSTOM_IMG_FILE, - CONF_FEDEX_CUSTOM_IMG_FILE: DEFAULT_FEDEX_CUSTOM_IMG_FILE, - CONF_GENERIC_CUSTOM_IMG_FILE: DEFAULT_GENERIC_CUSTOM_IMG_FILE, - CONF_POST_DE_CUSTOM_IMG_FILE: DEFAULT_POST_DE_CUSTOM_IMG_FILE, - } - - return self.async_show_form( - step_id="config_3", - data_schema=_get_schema_step_3(self._data, defaults), - 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, self.hass) - if len(self._errors) == 0: - if ( - self._data.get(CONF_CUSTOM_IMG) - or self._data.get(CONF_AMAZON_CUSTOM_IMG) - or self._data.get(CONF_UPS_CUSTOM_IMG) - or self._data.get(CONF_WALMART_CUSTOM_IMG) - or self._data.get(CONF_GENERIC_CUSTOM_IMG) - or self._data.get(CONF_POST_DE_CUSTOM_IMG) - ): - return await self.async_step_config_3() - return await self.async_step_config_storage() - - 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_DOMAIN: DEFAULT_AMAZON_DOMAIN, - 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, - forwarding_header=self._data.get(CONF_FORWARDING_HEADER, ""), - ), - errors=self._errors, - ) - - async def async_step_config_forwarded_emails(self, user_input=None): - """Configure form step forwarded emails.""" - self._errors = {} - if user_input is not None: - self._data.update(user_input) - self._errors, user_input = await _validate_user_input(self._data, self.hass) - 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 await self.async_step_config_storage() - - return await self._show_config_forwarded_emails(user_input) - - return await self._show_config_forwarded_emails(user_input) - - async def _show_config_forwarded_emails(self, user_input): - """Configure forwarded emails setup.""" - # Defaults - defaults = { - CONF_FORWARDED_EMAILS: DEFAULT_FORWARDED_EMAILS, - } - - return self.async_show_form( - step_id="config_forwarded_emails", - data_schema=_get_schema_step_forwarded_emails(user_input, defaults), - errors=self._errors, - ) - - async def async_step_config_storage(self, user_input=None): - """Configure form step storage.""" - self._errors = {} - if user_input is not None: - self._data.update(user_input) - self._errors, user_input = await _validate_user_input(self._data, self.hass) - if len(self._errors) == 0: - return self.async_create_entry( - title=f"Mail and Packages ({self._data[CONF_HOST]})", - data=self._data, - ) - return await self._show_config_storage(user_input) - - return await self._show_config_storage(user_input) - - async def _show_config_storage(self, user_input): - """Step 3 setup.""" - # Defaults - defaults = { - CONF_STORAGE: DEFAULT_STORAGE, - } - - return self.async_show_form( - step_id="config_storage", - data_schema=_get_schema_step_storage(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"]) - assert self._entry - self._data = dict(self._entry.data) - self._errors = {} - - if user_input is not None: - self._data.update(user_input) - return await self.async_step_reconfig_imap() - - return await self._show_reconfig_auth_form(user_input) - - async def async_step_reconfig_imap(self, user_input=None): - """Handle IMAP step for reconfigure.""" - self._errors = {} - - if user_input is not None: - self._data.update(user_input) - auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD) - - if auth_type != AUTH_TYPE_PASSWORD: - if ( - self._entry - and self._entry.data.get(CONF_AUTH_TYPE) == auth_type - and self._entry.data.get(CONF_HOST) == self._data.get(CONF_HOST) - and self._entry.data.get(CONF_USERNAME) - == self._data.get(CONF_USERNAME) - and "token" in self._data - ): - self.hass.config_entries.async_update_entry( - self._entry, - data=self._data, - ) - await self.hass.config_entries.async_reload(self._entry.entry_id) - _LOGGER.debug("%s reconfigured (oauth skip).", DOMAIN) - return self.async_abort(reason="reconfigure_successful") - - self._data[CONF_VERIFY_SSL] = True - self.hass.data.setdefault(DOMAIN, {}) - self.hass.data[DOMAIN]["oauth_provider"] = auth_type - return await self.async_step_pick_implementation() - - self._errors = await _validate_login(self.hass, self._data) - if self._errors == {}: - self.hass.config_entries.async_update_entry( - self._entry, - data=self._data, - ) - await self.hass.config_entries.async_reload(self._entry.entry_id) - _LOGGER.debug("%s reconfigured.", DOMAIN) - return self.async_abort(reason="reconfigure_successful") - - return await self._show_reconfig_imap_form(user_input) - - return await self._show_reconfig_imap_form(user_input) - - async def _show_reconfig_auth_form(self, user_input): - """Show the auth form for reconfigure.""" - defaults = { - CONF_AUTH_TYPE: self._entry.data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD), - } - return self.async_show_form( - step_id="reconfigure", - data_schema=_get_schema_auth(user_input, defaults), - errors=self._errors, - ) - - async def _show_reconfig_imap_form(self, user_input): - """Show the configuration form to edit IMAP data.""" - auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD) - defaults = OAUTH_IMAP_DEFAULTS.get( - auth_type, - { - CONF_PORT: self._entry.data.get(CONF_PORT, DEFAULT_PORT), - CONF_IMAP_SECURITY: self._entry.data.get(CONF_IMAP_SECURITY, "SSL"), - CONF_VERIFY_SSL: self._entry.data.get(CONF_VERIFY_SSL, False), - }, - ) - - defaults[CONF_HOST] = self._entry.data.get(CONF_HOST, defaults.get(CONF_HOST)) - defaults[CONF_USERNAME] = self._entry.data.get( - CONF_USERNAME, - defaults.get(CONF_USERNAME), - ) - defaults[CONF_PASSWORD] = self._entry.data.get( - CONF_PASSWORD, - defaults.get(CONF_PASSWORD), - ) - defaults[CONF_AUTH_TYPE] = auth_type - - return self.async_show_form( - step_id="reconfig_imap", - data_schema=_get_schema_imap(user_input, defaults), - errors=self._errors, - ) - - -class MailAndPackagesOptionsFlow(config_entries.OptionsFlow): - """Options flow for Mail and Packages.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize options flow.""" - self._entry = config_entry - self._data = {**config_entry.data, **config_entry.options} - self._errors = {} - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> config_entries.ConfigFlowResult: - """Manage the options.""" - self._errors = {} - if user_input is not None: - self._data.update(user_input) - if self._data.get("generate_mp4", False): - if not await _check_ffmpeg(): - self._errors["generate_mp4"] = "ffmpeg_not_found" - if len(self._errors) == 0: - if self._data.get(CONF_ALLOW_FORWARDED_EMAILS, False): - return await self.async_step_options_forwarded_emails() - if any( - sensor in self._data[CONF_RESOURCES] for sensor in AMAZON_SENSORS - ): - return await self.async_step_options_amazon() - if self._data.get(CONF_CUSTOM_IMG, False): - return await self.async_step_options_3() - return await self.async_step_options_storage() - - return await self._show_options_2(user_input) - - return await self._show_options_2(user_input) - - async def _show_options_2(self, user_input): - """Step 2 of options.""" - if self._data.get(CONF_AMAZON_FWDS) == []: - self._data[CONF_AMAZON_FWDS] = "(none)" - - return self.async_show_form( - step_id="init", - data_schema=await _get_schema_step_2( - self._data, user_input, self._data, self.hass, self._entry - ), - errors=self._errors, - ) - - async def async_step_options_forwarded_emails(self, user_input=None): - """Configure forwarded emails.""" - self._errors = {} - if user_input is not None: - if user_input.get(CONF_FORWARDED_EMAILS) == "(none)": - user_input[CONF_FORWARDED_EMAILS] = [] - 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_options_amazon() - if self._data.get(CONF_CUSTOM_IMG, False): - return await self.async_step_options_3() - return await self.async_step_options_storage() - return await self._show_options_forwarded_emails(user_input) - - async def _show_options_forwarded_emails(self, user_input=None): - """Step forwarded emails.""" - if self._data.get(CONF_FORWARDED_EMAILS, []) == []: - self._data[CONF_FORWARDED_EMAILS] = "(none)" - - return self.async_show_form( - step_id="options_forwarded_emails", - data_schema=_get_schema_step_forwarded_emails(user_input, self._data), - errors=self._errors, - ) - - async def async_step_options_amazon(self, user_input=None): - """Configure amazon options.""" - self._errors = {} - if user_input is not None: - if user_input.get(CONF_AMAZON_FWDS) == "(none)": - user_input[CONF_AMAZON_FWDS] = [] - self._data.update(user_input) - self._errors, user_input = await _validate_user_input(self._data, self.hass) - if len(self._errors) == 0: - if self._data.get(CONF_CUSTOM_IMG, False): - return await self.async_step_options_3() - return await self.async_step_options_storage() - return await self._show_options_amazon(user_input) - return await self._show_options_amazon(user_input) - - async def _show_options_amazon(self, user_input): - """Step Amazon setup.""" - if self._data.get(CONF_AMAZON_FWDS) == []: - self._data[CONF_AMAZON_FWDS] = "(none)" - - return self.async_show_form( - step_id="options_amazon", - data_schema=_get_schema_step_amazon( - user_input, - self._data, - forwarding_header=self._data.get(CONF_FORWARDING_HEADER, ""), - ), - errors=self._errors, - ) - - async def async_step_options_3(self, user_input=None): - """Configure custom image files.""" - self._errors = {} - if user_input is not None: - self._data.update(user_input) - self._errors, user_input = await _validate_user_input(self._data, self.hass) - if len(self._errors) == 0: - return await self.async_step_options_storage() - return await self._show_options_3(user_input) - return await self._show_options_3(user_input) - - async def _show_options_3(self, user_input): - """Step 3 setup.""" - return self.async_show_form( - step_id="options_3", - data_schema=_get_schema_step_3(self._data, self._data), - errors=self._errors, - ) - - async def async_step_options_storage(self, user_input=None): - """Configure storage options.""" - self._errors = {} - if user_input is not None: - self._data.update(user_input) - self._errors, user_input = await _validate_user_input(self._data, self.hass) - if len(self._errors) == 0: - # Remove config flow only fields to not pollute options - options_keys = { - CONF_FOLDER, - CONF_SCAN_INTERVAL, - CONF_RESOURCES, - CONF_CUSTOM_IMG, - CONF_AMAZON_CUSTOM_IMG, - CONF_UPS_CUSTOM_IMG, - CONF_WALMART_CUSTOM_IMG, - CONF_FEDEX_CUSTOM_IMG, - CONF_GENERIC_CUSTOM_IMG, - CONF_POST_DE_CUSTOM_IMG, - CONF_CUSTOM_IMG_FILE, - CONF_AMAZON_CUSTOM_IMG_FILE, - CONF_UPS_CUSTOM_IMG_FILE, - CONF_WALMART_CUSTOM_IMG_FILE, - CONF_FEDEX_CUSTOM_IMG_FILE, - CONF_GENERIC_CUSTOM_IMG_FILE, - CONF_POST_DE_CUSTOM_IMG_FILE, - CONF_ALLOW_FORWARDED_EMAILS, - CONF_FORWARDED_EMAILS, - CONF_FORWARDING_HEADER, - CONF_AMAZON_FWDS, - CONF_AMAZON_DOMAIN, - CONF_AMAZON_DAYS, - CONF_STORAGE, - "generate_mp4", - "generate_grid", - "gif_duration", - "custom_days", - "allow_external", - "image_security", - "imap_timeout", - "image_name", - "image_path", - "usps_placeholder", - } - for key in list(self._data.keys()): - if key not in options_keys: - self._data.pop(key, None) - - return self.async_create_entry(title="", data=self._data) - - return await self._show_options_storage(user_input) - - return await self._show_options_storage(user_input) - - async def _show_options_storage(self, user_input): - """Step storage setup.""" - return self.async_show_form( - step_id="options_storage", - data_schema=_get_schema_step_storage(user_input, self._data), - errors=self._errors, - ) diff --git a/custom_components/mail_and_packages/const.py b/custom_components/mail_and_packages/const.py deleted file mode 100644 index 690250f..0000000 --- a/custom_components/mail_and_packages/const.py +++ /dev/null @@ -1,1983 +0,0 @@ -"""Constants for Mail and Packages.""" - -from __future__ import annotations - -from typing import Final - -from homeassistant.components.binary_sensor import BinarySensorDeviceClass -from homeassistant.components.sensor import SensorDeviceClass, SensorEntityDescription -from homeassistant.helpers.entity import EntityCategory - -from .entity import MailandPackagesBinarySensorEntityDescription - -DOMAIN = "mail_and_packages" -DOMAIN_DATA = f"{DOMAIN}_data" -VERSION = "0.5.18" -ISSUE_URL = "http://github.com/moralmunky/Home-Assistant-Mail-And-Packages" -PLATFORM = "sensor" -PLATFORMS = ["binary_sensor", "camera", "sensor"] -DATA = "data" -COORDINATOR = "coordinator_mail" -OVERLAY = ["overlay.png", "vignette.png", "white.png"] -SERVICE_UPDATE_FILE_PATH = "update_file_path" -CAMERA = "cameras" -CONFIG_VER = 20 - -# Attributes -ATTR_AMAZON_IMAGE = "amazon_image" -ATTR_COUNT = "count" -ATTR_CODE = "code" -ATTR_GRID_IMAGE_NAME = "grid_image" -ATTR_ORDER = "order" -ATTR_TRACKING = "tracking" -ATTR_TRACKING_NUM = "tracking_#" -ATTR_IMAGE = "image" -ATTR_IMAGE_PATH = "image_path" -ATTR_SERVER = "server" -ATTR_IMAGE_NAME = "image_name" -ATTR_EMAIL = "email" -ATTR_SUBJECT = "subject" -ATTR_BODY = "body" -ATTR_BODY_COUNT = "body_count" -ATTR_PATTERN = "pattern" -ATTR_USPS_MAIL = "usps_mail" -ATTR_UPS_IMAGE = "ups_image" -ATTR_WALMART_IMAGE = "walmart_image" -ATTR_FEDEX_IMAGE = "fedex_image" -ATTR_GENERIC_IMAGE = "generic_image" -ATTR_USPS_IMAGE = "usps_image" -ATTR_POST_DE_IMAGE = "post_de_image" - -# Configuration Properties -CONF_ALLOW_EXTERNAL = "allow_external" -CONF_CAMERA_NAME = "camera_name" -CONF_CUSTOM_IMG = "custom_img" -CONF_CUSTOM_IMG_FILE = "custom_img_file" -CONF_AMAZON_CUSTOM_IMG = "amazon_custom_img" -CONF_AMAZON_CUSTOM_IMG_FILE = "amazon_custom_img_file" -CONF_UPS_CUSTOM_IMG = "ups_custom_img" -CONF_UPS_CUSTOM_IMG_FILE = "ups_custom_img_file" -CONF_WALMART_CUSTOM_IMG = "walmart_custom_img" -CONF_WALMART_CUSTOM_IMG_FILE = "walmart_custom_img_file" -CONF_FEDEX_CUSTOM_IMG = "fedex_custom_img" -CONF_FEDEX_CUSTOM_IMG_FILE = "fedex_custom_img_file" -CONF_GENERIC_CUSTOM_IMG = "generic_custom_img" -CONF_GENERIC_CUSTOM_IMG_FILE = "generic_custom_img_file" -CONF_POST_DE_CUSTOM_IMG = "post_de_custom_img" -CONF_POST_DE_CUSTOM_IMG_FILE = "post_de_custom_img_file" -CONF_STORAGE = "storage" -CONF_FOLDER = "folder" -CONF_PATH = "image_path" -CONF_DURATION = "gif_duration" -CONF_SCAN_INTERVAL = "scan_interval" -CONF_IMAGE_SECURITY = "image_security" -CONF_IMAP_TIMEOUT = "imap_timeout" -CONF_GENERATE_GRID = "generate_grid" -CONF_GENERATE_MP4 = "generate_mp4" -CONF_AMAZON_FWDS = "amazon_fwds" -CONF_AMAZON_DAYS = "amazon_days" -CONF_VERIFY_SSL = "verify_ssl" -CONF_IMAP_SECURITY = "imap_security" -CONF_AMAZON_DOMAIN = "amazon_domain" -CONF_ALLOW_FORWARDED_EMAILS = "allow_forwarded_emails" -CONF_FORWARDED_EMAILS = "forwarded_emails" -CONF_FORWARDING_HEADER = "forwarding_header" -CONF_CUSTOM_DAYS = "custom_days" -CONF_USPS_PLACEHOLDER = "usps_placeholder" - -# Defaults -DEFAULT_CAMERA_NAME = "Mail USPS Camera" -DEFAULT_NAME = "Mail And Packages" -DEFAULT_PORT = "993" -DEFAULT_FOLDER = "INBOX" -DEFAULT_PATH = "custom_components/mail_and_packages/images/" -DEFAULT_IMAGE_SECURITY = True -DEFAULT_IMAP_TIMEOUT = 60 -DEFAULT_GIF_DURATION = 5 -DEFAULT_SCAN_INTERVAL = 30 -DEFAULT_CUSTOM_DAYS = 3 -MAX_TRACKING_AGE_DAYS = 14 -DEFAULT_GIF_FILE_NAME = "mail_today.gif" -DEFAULT_AMAZON_FWDS = "(none)" -DEFAULT_ALLOW_EXTERNAL = False -DEFAULT_CUSTOM_IMG = False -DEFAULT_CUSTOM_IMG_FILE = "custom_components/mail_and_packages/mail_none.gif" -DEFAULT_AMAZON_CUSTOM_IMG = False -DEFAULT_AMAZON_CUSTOM_IMG_FILE = ( - "custom_components/mail_and_packages/no_deliveries_amazon.jpg" -) -DEFAULT_UPS_CUSTOM_IMG = False -DEFAULT_UPS_CUSTOM_IMG_FILE = ( - "custom_components/mail_and_packages/no_deliveries_ups.jpg" -) -DEFAULT_WALMART_CUSTOM_IMG = False -DEFAULT_WALMART_CUSTOM_IMG_FILE = ( - "custom_components/mail_and_packages/no_deliveries_walmart.jpg" -) -DEFAULT_FEDEX_CUSTOM_IMG = False -DEFAULT_FEDEX_CUSTOM_IMG_FILE = ( - "custom_components/mail_and_packages/no_deliveries_fedex.jpg" -) -DEFAULT_GENERIC_CUSTOM_IMG = False -DEFAULT_GENERIC_CUSTOM_IMG_FILE = ( - "custom_components/mail_and_packages/no_deliveries_generic.jpg" -) -DEFAULT_POST_DE_CUSTOM_IMG = False -DEFAULT_POST_DE_CUSTOM_IMG_FILE = "custom_components/mail_and_packages/mail_none.gif" -DEFAULT_AMAZON_DAYS = 3 -DEFAULT_AMAZON_DOMAIN = "amazon.com" -DEFAULT_STORAGE = "custom_components/mail_and_packages/images/" - -DEFAULT_ALLOW_FORWARDED_EMAILS = False -DEFAULT_FORWARDED_EMAILS = "(none)" -DEFAULT_FORWARDING_HEADER = "(none)" -DEFAULT_USPS_PLACEHOLDER = True - -# Amazon -AMAZON_DOMAINS = [ - "amazon.com", - "amazon.ca", - "amazon.co.uk", - "amazon.in", - "amazon.de", - "amazon.it", - "amazon.com.au", - "amazon.pl", - "amazon.es", - "amazon.fr", - "amazon.ae", - "amazon.nl", - "amazon.se", -] -AMAZON_DELIVERED_SUBJECT = [ - "Delivered: ", - "Your Amazon order has arrived!", - "Consegna effettuata:", - "Dostarczono:", - "Geliefert:", - "Livré", - "Entregado:", - "Bezorgd:", - "Livraison : Votre", - "Zugestellt:", -] -AMAZON_SHIPMENT_TRACKING = [ - "auto-confirm", - "shipment-tracking", - "order-update", - "conferma-spedizione", - "confirmar-envio", - "versandbestaetigung", - "confirmation-commande", - "verzending-volgen", - "update-bestelling", -] -AMAZON_SHIPMENT_SUBJECT = [ - "Shipped:", - "Enviado:", - "Out for delivery:", - "Spedito:", - "Versandt:", - "In Zustellung:", -] -AMAZON_ORDERED_SUBJECT = ["Ordered:", "Pedido efetuado:"] -AMAZON_EMAIL = [ - "order-update@", - "update-bestelling@", - "versandbestaetigung@", - "verzending-volgen@", - "auto-bevestiging@", -] -AMAZON_PACKAGES = "amazon_packages" -AMAZON_ORDER = "amazon_order" -AMAZON_DELIVERED = "amazon_delivered" -AMAZON_IMG_LIST = [ - "us-prod-temp.s3.amazonaws.com", - "gb-prod-temp.s3.eu-west-1.amazonaws.com", -] -AMAZON_IMG_PATTERN = ( - "(https://)([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-;]*[\\w@?^=%&/~+#-;])?" -) -AMAZON_HUB = "amazon_hub" -AMAZON_HUB_CODE = "amazon_hub_code" -AMAZON_HUB_EMAIL = [ - "thehub@amazon.com", - "order-update@amazon.com", - "amazonlockers@amazon.com", - "versandbestaetigung@amazon.de", -] -AMAZON_HUB_SUBJECT = ["ready for pickup from Amazon Hub Locker"] -AMAZON_HUB_SUBJECT_SEARCH = "(a package to pick up)(.*)(\\d{6})" -AMAZON_HUB_BODY = "(Your pickup code is )(\\d{6})" -AMAZON_TIME_PATTERN = [ - "will arrive:", - "estimated delivery date is:", - "guaranteed delivery date is:", - "Arriving:", - "Arriverà:", - "arriving:", - "Arriving ", - "Dostawa:", - "Entrega:", - "A chegar:", - "Arrivée :", - "Chega ", - "Verwachte bezorgdatum:", - "Votre date de livraison prévue est :", - "In arrivo", - "Zustellung:", -] -AMAZON_TIME_PATTERN_END = [ - "Previously expected:", - "This contains", - "Track your", - "Per tracciare il tuo pacco", - "View or manage order", - "Acompanhar", - "Seguimiento", - "Verfolge deine(n) Artikel", - "Lieferung verfolgen", - "Ihr Paket verfolgen", - "Suivre", - "Volg je pakket", - "Je pakket volgen", -] -AMAZON_TIME_PATTERN_REGEX = [ - "Arriving (\\w+ \\d+) - (\\w+ \\d+)", - "Arriving (\\w+ \\d+)", - "Arriving (\\w+ ?\\d*)", - "Arriving (\\w+)", - "Zustellung:? (\\w+ \\d+) - (\\w+ \\d+)", - "Zustellung:? (\\w+ \\d+)", - "Zustellung:? (\\w+ ?\\d*)", - "Zustellung:? (\\w+)", - "Arriverà (\\w+ \\d+) - (\\w+ \\d+)", - "Arriverà (\\w+ \\d+)", - "Arriverà (\\w+ \\d*)", - "Arrivée (\\w+ \\d+) - (\\w+ \\d+)", - "Arrivée (\\w+ \\d+)", - "Arrivée (\\w+ \\d*)", - "Chega ((\\w+(-\\w+)?))", - "Wordt bezorgd op (\\w+ \\d+ \\w+)", - "Wordt bezorgd op (\\w+ \\d+)", - "Wordt (\\w+) bezorgd", - "In arrivo (\\w+ \\d+) - (\\w+ \\d+)", - "In arrivo (\\w+ \\d+)", - "In arrivo (\\w+ \\d*)", - "In arrivo (\\w+)", -] -AMAZON_EXCEPTION_SUBJECT = "Delivery update:" -AMAZON_EXCEPTION_BODY = "running late" -AMAZON_EXCEPTION = "amazon_exception" -AMAZON_EXCEPTION_ORDER = "amazon_exception_order" -AMAZON_PATTERN = "[0-9]{3}-[0-9]{7}-[0-9]{7}" -AMAZON_LANGS = [ - "it_IT", - "it_IT.UTF-8", - "pl_PL", - "pl_PL.UTF-8", - "de_DE", - "de_DE.UTF-8", - "es_ES", - "es_ES.UTF-8", - "pt_PT", - "pt_PT.UTF-8", - "pt_BR", - "pt_BR.UTF-8", - "fr_CA", - "fr_CA.UTF-8", - "", -] -AMAZON_OTP = "amazon_otp" -AMAZON_OTP_CODE = "amazon_otp_code" -AMAZON_OTP_REGEX = "(\n)(\\d{6})(\n)" -AMAZON_OTP_SUBJECT = "A one-time password is required for your Amazon delivery" - -AMAZON_DELIEVERED_BY_OTHERS_SEARCH_TEXT = ["AMAZON"] - -# Sensor Data -SENSOR_DATA = { - # USPS - "usps_delivered": { - "email": ["auto-reply@usps.com", "auto-reply@tracking.usps.com"], - "subject": ["Item Delivered"], - }, - "usps_delivering": { - "email": ["auto-reply@usps.com", "auto-reply@tracking.usps.com"], - "subject": ["Expected Delivery on", "Out for Delivery"], - "body": ["Your item is out for delivery"], - }, - "usps_exception": { - "email": ["auto-reply@usps.com", "auto-reply@tracking.usps.com"], - "subject": ["Delivery Exception"], - }, - "usps_packages": { - "email": ["auto-reply@usps.com", "auto-reply@tracking.usps.com"], - "subject": ["Expected Delivery by"], - }, - "usps_tracking": {"pattern": ["9[2345]\\d{15,26}"]}, - "usps_mail": { - "email": [ - "USPSInformedDelivery@usps.gov", - "USPSInformeddelivery@email.informeddelivery.usps.com", - "USPSInformeddelivery@informeddelivery.usps.com", - "USPS Informed Delivery", - ], - "subject": ["Your Daily Digest"], - }, - "usps_mail_delivered": { - "email": [ - "USPSInformedDelivery@usps.gov", - "USPSInformeddelivery@email.informeddelivery.usps.com", - "USPSInformeddelivery@informeddelivery.usps.com", - "USPS Informed Delivery", - ], - "subject": ["Your Mail Was Delivered"], - }, - # UPS - "ups_delivered": { - "email": ["mcinfo@ups.com", "pkginfo@ups.com"], - "subject": [ - "Your UPS Package was delivered", - "Your UPS Packages were delivered", - "Your UPS Parcel was delivered", - "Your UPS Parcels were delivered", - "Votre colis UPS a été livré", - "Paket wurde zugestellt", - ], - }, - "ups_delivering": { - "email": ["mcinfo@ups.com", "pkginfo@ups.com"], - "subject": [ - "UPS Update: Package Scheduled for Delivery Today", - "UPS Update: Follow Your Delivery on a Live Map", - "UPS Pre-Arrival: Your Driver is Arriving Soon! Follow on a Live Map", - "UPS Update: Parcel Scheduled for Delivery Today", - "Mise à jour UPS : Livraison du colis prévue demain", - "Mise à jour UPS : Livraison du colis prévue aujourd'hui", - ], - }, - "ups_exception": { - "email": ["mcinfo@ups.com"], - "subject": ["UPS Update: New Scheduled Delivery Date"], - }, - "ups_packages": { - "email": ["mcinfo@ups.com", "pkginfo@ups.com"], - "subject": ["UPS Ship Notification"], - }, - "ups_tracking": {"pattern": ["1Z?[0-9A-Z]{16}"]}, - # FedEx - "fedex_delivered": { - "email": [ - "TrackingUpdates@fedex.com", - "fedexcanada@fedex.com", - "noreply@fedex.com", - ], - "subject": [ - "Your package has been delivered", - "Your packages have been delivered", - "Your shipment was delivered", - ], - }, - "fedex_delivering": { - "email": [ - "TrackingUpdates@fedex.com", - "fedexcanada@fedex.com", - "noreply@fedex.com", - ], - "subject": [ - "Delivery scheduled for today", - "Your package is scheduled for delivery today", - "Your package is now out for delivery", - "Your shipment is out for delivery today", - "out for delivery today", - "Ihre Sendung wird voraussichtlich heute zugestellt", - ], - }, - "fedex_packages": { - "email": [ - "TrackingUpdates@fedex.com", - "fedexcanada@fedex.com", - "noreply@fedex.com", - ], - "subject": ["Your shipment is on the way"], - }, - "fedex_exception": { - "email": [ - "TrackingUpdates@fedex.com", - "fedexcanada@fedex.com", - "noreply@fedex.com", - ], - # Subject confirmed against a real FedEx delivery-exception - # notification (From: trackingupdates@fedex.com, Subject: - # "FedEx Delivery Exception"). IMAP SUBJECT search is a - # case-insensitive substring match, mirroring usps_exception's - # "Delivery Exception" fragment. - "subject": ["FedEx Delivery Exception"], - }, - "fedex_tracking": {"pattern": ["\\d{12,20}"]}, - # Canada Post - "capost_delivered": { - "email": [ - "donotreply@canadapost.postescanada.ca", - "donotreply-nepasrepondre@notifications.canadapost-postescanada.ca", - ], - "subject": [ - "Delivery Notification", - ], - }, - "capost_delivering": { - "email": [ - "donotreply-nepasrepondre@notifications.canadapost-postescanada.ca", - ], - "subject": [ - "Your parcel is out for delivery", - ], - }, - "capost_packages": {}, - "capost_tracking": {"pattern": ["\\d{16}"]}, - "capost_mail": { - "email": ["donotreply-nepasrepondre@communications.canadapost-postescanada.ca"], - "subject": ["You have mail on the way"], - "body": ["\\sYou have (\\d) piece|pieces of mail\\s"], - "body_count": True, - }, - # DHL - "dhl_delivered": { - "email": [ - "donotreply_odd@dhl.com", - "NoReply.ODD@dhl.com", - "noreply@dhl.de", - "no-reply@dhl.de", - "pl.no.reply@dhl.com", - "support@dhl.com", - "noreply@dhlecommerce.nl", - "noreply@dhl.nl", - ], - "subject": [ - "DHL On Demand Delivery", - "Powiadomienie o przesyłce", - "wurde zugestellt", - "DHL Shipment Notification", - "liegt am gewünschten Ablageort", - "Ihre Sendung liegt im Briefkasten", - "Zustellung an Ablageort", - "Ablageort", - "Sendung zugestellt", - "Paket wurde zugestellt", - "Ihre AliExpress Sendung liegt im Briefkasten", - "succesvol bezorgd", - "is bezorgd", - ], - "body": [ - "has been delivered", - "została doręczona", - "ist angekommen", - 'Notification for shipment event group "Delivered', - " - Delivered - ", - "liegt im Briefkasten", - "zugestellt", - "Zustellung", - "wurde zugestellt", - "succesvol bezorgd", - "is bezorgd", - "pakket is afgeleverd", - ], - }, - "dhl_delivering": { - "email": [ - "donotreply_odd@dhl.com", - "NoReply.ODD@dhl.com", - "noreply@dhl.de", - "no-reply@dhl.de", - "pl.no.reply@dhl.com", - "support@dhl.com", - "noreply@dhlecommerce.nl", - "noreply@dhl.nl", - ], - "subject": [ - "DHL On Demand Delivery", - "Paket kommt heute", - "kommt heute", - "wird gleich zugestellt", - "Powiadomienie o przesyłce", - "DHL Shipment Notification", - "ist unterwegs", - "Jetzt Live verfolgen", - "vanavond voor de deur", - "vandaag voor de deur", - "pakket onderweg", - "bezorging vandaag", - "staan vandaag voor de deur", - "staan vanavond voor de deur", - "komen we bij je langs", - ], - "body": [ - "scheduled for delivery TODAY", - "zostanie dziś do Państwa doręczona", - "wird Ihnen heute", - "wird Ihnen voraussichtlich", - "heute zwischen", - " - Shipment is out with courier for delivery - ", - "Shipment is scheduled for delivery", - "voraussichtlich innerhalb", - "staan vandaag voor de deur", - "staan vanavond voor de deur", - "wordt vandaag bezorgd", - "bezorger onderweg", - "komen we bij je langs", - ], - }, - "dhl_packages": {}, - "dhl_tracking": { - "pattern": [ - "(?:JJD\\d{18}|JVGL\\d{20}|MDP[A-Z0-9]{5,15}|00\\d{18}|(? dict: - """Process emails and update sensors.""" - # Initialize defaults and image paths - data = self._initialize_data() - config = await self._setup_image_config(hass, config) - - # Connect to IMAP - account = await self._get_imap_connection(config) - try: - cache = EmailCache(account) - now = datetime.datetime.now() - today = now.strftime("%d-%b-%Y") - today_iso = now.date().isoformat() - days = config.get(CONF_CUSTOM_DAYS, DEFAULT_CUSTOM_DAYS) - since_date = (now - datetime.timedelta(days=days)).strftime("%d-%b-%Y") - - # Process logic - shipper_data = await self._update_shippers( - account, config, today, since_date, cache - ) - tracking_details = shipper_data.pop("_tracking_details", {}) - data.update(shipper_data) - self._apply_tracking_state(data, tracking_details, today_iso) - - # Aggregate global transit and delivered sensors - self._aggregate_package_counts(data) - finally: - await logout(account) - - # Post-process external images - if config.get(CONF_ALLOW_EXTERNAL): - try: - await hass.async_add_executor_job(copy_images, hass, config) - except (OSError, ValueError) as err: - _LOGGER.error("Problem creating: %s", err) - - return data - - def _initialize_data(self) -> dict: - """Initialize core data structure with default values.""" - data = { - "mail_updated": datetime.datetime.now(datetime.UTC).isoformat(), - "amazon_delivered_by_others": 0, - } - resources = self.config.get(CONF_RESOURCES, []) - for sensor in resources: - if sensor not in data: - data[sensor] = 0 - return data - - async def _setup_image_config(self, hass: HomeAssistant, config: dict) -> dict: - """Configure image paths and filenames for all shippers.""" - image_path = default_image_path(hass, config) - config["image_path"] = image_path - - shipper_images = { - "amazon_image": (True, False, False, False), - "ups_image": (False, True, False, False), - "walmart_image": (False, False, True, False), - "fedex_image": (False, False, False, True), - "usps_image": (False, False, False, False), - "post_de_image": (False, False, False, False), - } - - for key, params in shipper_images.items(): - config[key] = await hass.async_add_executor_job( - image_file_name, hass, config, *params - ) - return config - - async def _get_imap_connection(self, config: dict) -> IMAP4_SSL: - """Establish and return an authenticated IMAP connection.""" - try: - account = await login( - self.hass, - config.get(CONF_HOST), - config.get(CONF_PORT), - config.get(CONF_USERNAME), - config.get(CONF_PASSWORD), - config.get(CONF_IMAP_SECURITY), - config.get(CONF_VERIFY_SSL), - config.get("oauth_token"), - timeout=self.timeout, - ) - except InvalidAuth as err: - _LOGGER.error("Authentication failed: %s", err) - # Create a repairs issue for authentication failure - ir.async_create_issue( - self.hass, - DOMAIN, - "auth_failed", - is_fixable=True, - severity=ir.IssueSeverity.ERROR, - translation_key="auth_failed", - data={"entry_id": self.config_entry.entry_id} - if self.config_entry - else None, - ) - raise ConfigEntryAuthFailed from err - except Exception as err: - _LOGGER.error("Error logging into IMAP: %s", err) - raise UpdateFailed(f"Login failed: {err}") from err - # Login succeeded, delete the issue if it exists - issue_registry = ir.async_get(self.hass) - if (DOMAIN, "auth_failed") in issue_registry.issues: - ir.async_delete_issue(self.hass, DOMAIN, "auth_failed") - - folders = config.get(CONF_FOLDER) - if isinstance(folders, str): - folders = [folders] - elif isinstance(folders, (list, tuple, set)): - folders = [f for f in folders if isinstance(f, str) and f] - else: - folders = [] - if not folders: - folders = ["INBOX"] - account._folders = folders # noqa: SLF001 - account._current_folder = None # noqa: SLF001 - - if folders: - try: - folder_ok = await selectfolder(account, folders[0]) - except Exception as err: - await logout(account) - raise UpdateFailed(f"Folder selection failed: {err}") from err - - if not folder_ok: - _LOGGER.error("Error selecting folder: %s", folders[0]) - await logout(account) - raise UpdateFailed(f"Folder selection failed: {folders[0]}") - - return account - - async def _update_shippers( - self, - account: IMAP4_SSL, - config: dict, - today: str, - since_date: str, - cache: EmailCache, - ) -> dict: - """Group and process sensors by shipper.""" - data = {} - resources = config.get(CONF_RESOURCES, []) - sensors_by_shipper = {} - - for sensor in resources: - shipper = get_shipper_for_sensor(self.hass, config, sensor) - if shipper: - sensors_by_shipper.setdefault(shipper.name, []).append( - (shipper, sensor) - ) - - for shipper_name, shipper_group in sensors_by_shipper.items(): - shipper_instance = shipper_group[0][0] - sensors = [s[1] for s in shipper_group] - - shipper_start = monotonic() - success = False - try: - results = await shipper_instance.process_batch( - account, today, sensors, cache, since_date=since_date - ) - if isinstance(results, dict): - if "_tracking_details" in results: - data.setdefault("_tracking_details", {}).update( - results.pop("_tracking_details") - ) - data.update(results) - success = True - except Exception as err: # noqa: BLE001 - _LOGGER.error("Error processing shipper %s: %s", shipper_name, err) - finally: - _LOGGER.debug( - "Shipper %s %s in %.1fs", - shipper_name, - "processed" if success else "failed", - monotonic() - shipper_start, - ) - - return data - - def _apply_tracking_state( - self, - data: dict, - tracking_details: dict[str, list[str]], - today_iso: str, - ) -> None: - """Update in-transit tracking state and override sensor counts.""" - prefixes: set[str] = set(self._in_transit_tracking.keys()) - for sensor_key in tracking_details: - prefix = "_".join(sensor_key.split("_")[:-1]) - if prefix: - prefixes.add(prefix) - - for prefix in prefixes: - if prefix in self._in_transit_tracking and not ( - f"{prefix}_delivering" in tracking_details - or f"{prefix}_exception" in tracking_details - or f"{prefix}_delivered" in tracking_details - ): - _LOGGER.debug( - "Prefix '%s' has no tracking_details entries — " - "may be a removed carrier; tracking will persist until TTL expiry", - prefix, - ) - - delivering = list(tracking_details.get(f"{prefix}_delivering", [])) - delivering += list(tracking_details.get(f"{prefix}_exception", [])) - delivered = list(tracking_details.get(f"{prefix}_delivered", [])) - - self._update_tracking_for_prefix( - prefix, delivering, delivered, today_iso, MAX_TRACKING_AGE_DAYS - ) - - in_transit = self._in_transit_tracking.get(prefix, {}) - # A carrier that reported DELIVERING/EXCEPTION tracking details - # this scan must have its count overridden even when the - # in-transit map ends up EMPTY: when every tracked package has a - # delivered notification, the raw IMAP count (which cannot dedup - # prior-day deliveries) would otherwise leak through as the - # sensor value. Batch-level dedup already zeroes the count for - # shippers that emit tracking details, so this is defense in - # depth at the state-machine layer. Delivered-only details must - # NOT trigger the override: a carrier whose delivering emails - # yielded no extractable tracking numbers has a legitimate - # email-based count that tracking-level dedup cannot verify — - # and carriers with no tracking details at all keep their - # email-count value untouched. - has_details = any( - f"{prefix}_{suffix}" in tracking_details - for suffix in ("delivering", "exception") - ) - if in_transit or has_details: - if not in_transit and data.get(f"{prefix}_delivering"): - _LOGGER.debug( - "Prefix '%s': no tracked packages remain in transit — " - "overriding delivering count %s -> 0", - prefix, - data.get(f"{prefix}_delivering"), - ) - data[f"{prefix}_tracking"] = list(in_transit.keys()) - data[f"{prefix}_delivering"] = len(in_transit) - if in_transit: - delivered_count = data.get(f"{prefix}_delivered", 0) - data[f"{prefix}_packages"] = len(in_transit) + ( - delivered_count if isinstance(delivered_count, int) else 0 - ) - - def _update_tracking_for_prefix( - self, - prefix: str, - delivering: list[str], - delivered: list[str], - today_iso: str, - ttl_days: int, - ) -> None: - """Add/expire delivering tracking numbers and remove delivered ones.""" - if prefix not in self._in_transit_tracking: - self._in_transit_tracking[prefix] = {} - - in_transit = self._in_transit_tracking[prefix] - - # Add new delivering tracking numbers (record first-seen date) - for tid in delivering: - if tid and tid not in in_transit: - in_transit[tid] = today_iso - - # Remove delivered tracking numbers - for tid in delivered: - in_transit.pop(tid, None) - - # Expire entries older than TTL - cutoff = ( - datetime.date.fromisoformat(today_iso) - datetime.timedelta(days=ttl_days) - ).isoformat() - expired = [tid for tid, seen in in_transit.items() if seen < cutoff] - for tid in expired: - del in_transit[tid] - - def _aggregate_package_counts(self, data: dict) -> None: - """Aggregate global transit and delivered counts from all shippers.""" - # Only update if sensors were requested in initialize_data - if "zpackages_transit" in data: - data["zpackages_transit"] = self._sum_transit_counts(data) - if "zpackages_delivered" in data: - data["zpackages_delivered"] = self._sum_delivered_counts(data) - - def _sum_delivered_counts(self, data: dict) -> int: - """Sum delivered packages from all shippers.""" - delivered = 0 - exclude_keys = ( - "zpackages_delivered", - "amazon_delivered_by_others", - "usps_mail_delivered", - ) - - for key, value in data.items(): - if ( - isinstance(value, int) - and value > 0 - and key.endswith("_delivered") - and key not in exclude_keys - ): - delivered += value - return delivered - - def _sum_transit_counts(self, data: dict) -> int: - """Sum transit and exception packages from all shippers.""" - transit = 0 - shippers_counted = set() - - # Amazon is special as it uses amazon_packages for total arriving - if data.get("amazon_packages", 0) > 0: - transit += data["amazon_packages"] - shippers_counted.add("amazon") - - for key, value in data.items(): - if not isinstance(value, int) or value <= 0: - continue - - # Add exceptions for all shippers - if key.endswith("_exception") and key != "zpackages_exception": - transit += value - continue - - # Match shipper prefix - shipper = next((s for s in const.SHIPPERS if key.startswith(s)), None) - if not shipper or shipper in shippers_counted: - continue - - # Priority: _delivering (preferred generic state) or _packages - if key.endswith(("_delivering", "_packages")): - transit += value - shippers_counted.add(shipper) - - return transit - - async def _binary_sensor_update(self): # noqa: C901 - """Update binary sensor states.""" - # USPS uses ATTR_USPS_IMAGE instead of the old ATTR_IMAGE_NAME - _LOGGER.debug("Data: %s", self._data) - image = self._data.get(ATTR_USPS_IMAGE) - if image: - path = default_image_path(self.hass, self.config) - usps_image = f"{path}/{image}" - usps_none = f"{Path(__file__).parent}/mail_none.gif" - usps_check = await anyio.Path(usps_image).exists() - _LOGGER.debug("USPS Check: %s", usps_check) - if usps_check: - # Optimized: Use _get_file_hash_if_changed - image_hash = await self._get_file_hash_if_changed(usps_image) - none_hash = await self._get_file_hash_if_changed(usps_none) - - _LOGGER.debug("USPS Image hash: %s", image_hash) - _LOGGER.debug("USPS None hash: %s", none_hash) - - if image_hash != none_hash: - self._data["usps_update"] = True - else: - self._data["usps_update"] = False - - # Handle generic delivery cameras (Amazon, UPS, Walmart, FedEx, Generic) with unified logic - # Derive camera list dynamically from CAMERA_DATA, excluding usps_camera and generic_camera - delivery_cameras = [ - camera_type.replace("_camera", "") - for camera_type in const.CAMERA_DATA - if camera_type not in ("usps_camera", "generic_camera") - ] - - for base_name in delivery_cameras: - # Derive attribute and config keys dynamically - image_attr_name = f"ATTR_{base_name.upper()}_IMAGE" - image_attr = getattr(const, image_attr_name, None) - if not image_attr: - continue - - custom_img_key = getattr( - const, - f"CONF_{base_name.upper()}_CUSTOM_IMG", - None, - ) - custom_img_file_key = getattr( - const, - f"CONF_{base_name.upper()}_CUSTOM_IMG_FILE", - None, - ) - update_key = f"{base_name}_update" - - image = self._data.get(image_attr) - _LOGGER.debug("%s image from data: %s", base_name.title(), image) - if image: - # Normalize path to avoid double slashes - image_path = ( - default_image_path(self.hass, self.config).rstrip("/") + "/" - ) - path = f"{image_path}{base_name}/" - # Use absolute path for file existence check - delivery_image_relative = f"{path}{image}" - delivery_image = self.hass.config.path(delivery_image_relative) - _LOGGER.debug( - "Full %s image path: %s", - base_name.title(), - delivery_image, - ) - - if custom_img_key and self.config.get(custom_img_key): - none_image = self.config.get(custom_img_file_key) - elif base_name == "post_de": - none_image = f"{Path(__file__).parent}/mail_none.gif" - else: - none_image = ( - f"{Path(__file__).parent}/no_deliveries_{base_name}.jpg" - ) - - image_check = await anyio.Path(delivery_image).exists() - _LOGGER.debug("%s Check: %s", base_name.title(), image_check) - if image_check: - # Optimized: Use _get_file_hash_if_changed - image_hash = await self._get_file_hash_if_changed(delivery_image) - none_hash = await self._get_file_hash_if_changed(none_image) - - _LOGGER.debug("%s Image hash: %s", base_name.title(), image_hash) - _LOGGER.debug("%s None hash: %s", base_name.title(), none_hash) - - if image_hash != none_hash: - self._data[update_key] = True - else: - self._data[update_key] = False diff --git a/custom_components/mail_and_packages/diagnostics.py b/custom_components/mail_and_packages/diagnostics.py deleted file mode 100644 index 8ead48d..0000000 --- a/custom_components/mail_and_packages/diagnostics.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Provide diagnostics for Mail and Packages.""" - -from __future__ import annotations - -import logging -from typing import Any - -from homeassistant.components.diagnostics import async_redact_data -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME -from homeassistant.core import HomeAssistant -from homeassistant.helpers.device_registry import DeviceEntry - -from . import MailAndPackagesConfigEntry -from .const import CONF_AMAZON_FWDS, CONF_FORWARDED_EMAILS - -_LOGGER = logging.getLogger(__name__) -REDACT_KEYS = {CONF_PASSWORD, CONF_USERNAME, CONF_AMAZON_FWDS, CONF_FORWARDED_EMAILS} - - -async def async_get_config_entry_diagnostics( - hass: HomeAssistant, - config_entry: MailAndPackagesConfigEntry, # pylint: disable=unused-argument -) -> dict[str, Any]: - """Return diagnostics for a config entry.""" - diag: dict[str, Any] = {} - diag["config"] = config_entry.as_dict() - return async_redact_data(diag, REDACT_KEYS) - - -async def async_get_device_diagnostics( - hass: HomeAssistant, - config_entry: MailAndPackagesConfigEntry, - device: DeviceEntry, # pylint: disable=unused-argument -) -> dict[str, Any]: - """Return diagnostics for a device.""" - coordinator = config_entry.runtime_data.coordinator - - dynamic_keys = { - variable - for variable in coordinator.data - if "tracking" in variable or "order" in variable - } - redact_keys = REDACT_KEYS | dynamic_keys - - _LOGGER.debug("Redacted keys: %s", redact_keys) - - return async_redact_data(coordinator.data, redact_keys) diff --git a/custom_components/mail_and_packages/entity.py b/custom_components/mail_and_packages/entity.py deleted file mode 100644 index 3e3bf5c..0000000 --- a/custom_components/mail_and_packages/entity.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Support for Mail and Packages entities.""" - -from __future__ import annotations - -from dataclasses import dataclass - -from homeassistant.components.binary_sensor import BinarySensorEntityDescription - - -@dataclass -class MailandPackagesBinarySensorEntityDescription(BinarySensorEntityDescription): - """Class describing Mail and Packages binary sensor entities.""" - - selectable: bool = False diff --git a/custom_components/mail_and_packages/helpers.py b/custom_components/mail_and_packages/helpers.py deleted file mode 100644 index 55317a3..0000000 --- a/custom_components/mail_and_packages/helpers.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Helper functions for Mail and Packages.""" - -from __future__ import annotations - -import datetime -import logging -import os -from pathlib import Path -from shutil import copyfile -from typing import Any - -from aioimaplib import IMAP4_SSL -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant - -from .const import ( - AMAZON_DELIVERED_SUBJECT, - AMAZON_EXCEPTION_SUBJECT, - AMAZON_HUB_SUBJECT, - AMAZON_OTP_SUBJECT, - ATTR_COUNT, - ATTR_TRACKING, - BINARY_SENSORS, - CONF_PATH, - SENSOR_TYPES, -) -from .shippers import SHIPPER_REGISTRY -from .shippers.usps import USPSShipper -from .utils.image import ( - default_image_path, -) - -_LOGGER = logging.getLogger(__name__) - -# Legacy constants for tests - -amazon_exception = AMAZON_EXCEPTION_SUBJECT -amazon_hub = AMAZON_HUB_SUBJECT -amazon_otp = AMAZON_OTP_SUBJECT -amazon_search_legacy = AMAZON_DELIVERED_SUBJECT -image_file_name = "mail_today.gif" - - -async def get_count( - account: IMAP4_SSL, - sensor_type: str, - body_count: bool, - image_path: str, - hass: HomeAssistant, -) -> dict[str, Any]: - """Legacy get_count wrapper for tests.""" - shipper_class = SHIPPER_REGISTRY.get(sensor_type) - if not shipper_class: - _LOGGER.error("No shipper found for %s", sensor_type) - return {ATTR_COUNT: 0, ATTR_TRACKING: []} - - shipper = shipper_class(hass, {CONF_PATH: image_path}) - today = datetime.datetime.now().strftime("%d-%b-%Y") - return await shipper.process(account, today, sensor_type) - - -async def get_mails( - account: IMAP4_SSL, - address_list: list[str], - hass: HomeAssistant, - config: dict[str, Any], -) -> int: - """Legacy get_mails wrapper for tests.""" - shipper = USPSShipper(hass, config) - today = datetime.datetime.now().strftime("%d-%b-%Y") - result = await shipper.process(account, today, "usps_mail") - return result.get(ATTR_COUNT, 0) - - -async def fetch( - hass: HomeAssistant, - config: dict[str, Any], - account: IMAP4_SSL, - sensor: str, -) -> int: - """Legacy fetch wrapper for tests.""" - shipper_class = SHIPPER_REGISTRY.get(sensor) - if not shipper_class: - return 0 - - shipper = shipper_class(hass, config) - today = datetime.datetime.now().strftime("%d-%b-%Y") - data = await shipper.process(account, today, sensor) - return data.get(ATTR_COUNT, 0) - - -async def get_items( - hass: HomeAssistant, - config: dict[str, Any], - account: IMAP4_SSL, - sensor: str, -) -> dict[str, Any]: - """Legacy get_items wrapper for tests.""" - shipper_class = SHIPPER_REGISTRY.get(sensor) - if not shipper_class: - return {ATTR_COUNT: 0, ATTR_TRACKING: []} - - shipper = shipper_class(hass, config) - today = datetime.datetime.now().strftime("%d-%b-%Y") - return await shipper.process(account, today, sensor) - - -def get_resources(hass: HomeAssistant | None = None) -> dict: - """Return resources from const.""" - resources = {k: v.name for k, v in SENSOR_TYPES.items()} - resources.update({k: v.name for k, v in BINARY_SENSORS.items() if v.selectable}) - return resources - - -def copy_images(hass: HomeAssistant, config: ConfigEntry) -> None: - """Copy processed images to www directory.""" - image_path = Path(hass.config.path(default_image_path(hass, config))) - www_path = Path(hass.config.path()) / "www" / "mail_and_packages" - - if not www_path.is_dir(): - www_path.mkdir(parents=True, exist_ok=True) - - for root, _, files in os.walk(image_path): - for file in files: - if file.endswith((".gif", ".jpg", ".png")): - src = Path(root) / file - dest = www_path / file - try: - copyfile(str(src), str(dest)) - except OSError as err: - _LOGGER.error("Error copying image: %s", err) diff --git a/custom_components/mail_and_packages/image-no-mailpieces700.jpg b/custom_components/mail_and_packages/image-no-mailpieces700.jpg deleted file mode 100644 index 794ee8cf2fb2c98832f790c5af20e763abc4bbae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44385 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�p6pDo-TcsYYTmWnN}UsOm)mZTmc1Jj$#Nl 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.0", - "dateparser", - "aioimaplib" - ], - "version": "0.5.18" -} diff --git a/custom_components/mail_and_packages/no_deliveries_amazon.jpg b/custom_components/mail_and_packages/no_deliveries_amazon.jpg deleted file mode 100644 index a445a39a81ae10c3755c3714659b711ae7885b6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35660 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 diff --git a/custom_components/mail_and_packages/no_deliveries_generic.jpg b/custom_components/mail_and_packages/no_deliveries_generic.jpg deleted file mode 100644 index 800184e5588f750e80d76c9b1c9b68ab22e7fc8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27660 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 DYr$)m diff --git a/custom_components/mail_and_packages/no_deliveries_ups.jpg b/custom_components/mail_and_packages/no_deliveries_ups.jpg deleted file mode 100644 index a37a3b6080136e1ce6c054e2706ba15405ae7c30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34834 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 DbRq9> diff --git a/custom_components/mail_and_packages/no_deliveries_walmart.jpg b/custom_components/mail_and_packages/no_deliveries_walmart.jpg deleted file mode 100644 index 83228c93898d327d091cca4965c5b23ae554f46d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47455 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 IFMrMbFKPxv-~a#s diff --git a/custom_components/mail_and_packages/overlay.png b/custom_components/mail_and_packages/overlay.png deleted file mode 100644 index 6a6c9b1d5b3edfdd30427871739340698f8f185b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8764 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 bcuT%1h2*70kj4XF^08EvUMW_}L&E None: - """Initialize.""" - self.entry_id = entry_id - - async def async_step_init( - self, user_input: dict[str, str] | None = None - ) -> FlowResult: - """Handle the first step of a repair flow.""" - return await self.async_step_confirm(user_input) - - async def async_step_confirm( - self, user_input: dict[str, str] | None = None - ) -> FlowResult: - """Handle confirm step.""" - if user_input is not None: - if self.entry_id: - entry = self.hass.config_entries.async_get_entry(self.entry_id) - else: - entries = self.hass.config_entries.async_entries(DOMAIN) - entry = entries[0] if entries else None - - if entry: - entry.async_start_reauth(self.hass) - return self.async_create_entry(title="", data={}) - - return self.async_show_form( - step_id="confirm", - data_schema=vol.Schema({}), - ) - - -async def async_create_fix_flow( - hass: HomeAssistant, - issue_id: str, - data: dict[str, Any] | None, -) -> RepairsFlow: - """Create a flow to fix a specific issue.""" - if issue_id == "auth_failed": - entry_id = data.get("entry_id") if data else None - return AuthRepairFlow(entry_id) - raise ValueError(f"Unknown issue {issue_id}") diff --git a/custom_components/mail_and_packages/sensor.py b/custom_components/mail_and_packages/sensor.py deleted file mode 100644 index 2803813..0000000 --- a/custom_components/mail_and_packages/sensor.py +++ /dev/null @@ -1,272 +0,0 @@ -"""Based on @skalavala work. - -https://blog.kalavala.net/usps/homeassistant/mqtt/2018/01/12/usps.html -Configuration code contribution from @firstof9 https://github.com/firstof9/ -""" - -import datetime -import logging -from typing import Any - -from homeassistant.components.sensor import SensorEntity, SensorEntityDescription -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_HOST, CONF_RESOURCES -from homeassistant.core import HomeAssistant -from homeassistant.helpers.network import NoURLAvailableError, get_url -from homeassistant.helpers.update_coordinator import CoordinatorEntity - -from . import MailAndPackagesConfigEntry -from .const import ( - AMAZON_EXCEPTION, - AMAZON_EXCEPTION_ORDER, - AMAZON_HUB, - AMAZON_HUB_CODE, - AMAZON_ORDER, - AMAZON_OTP, - AMAZON_OTP_CODE, - ATTR_CODE, - ATTR_GRID_IMAGE_NAME, - ATTR_IMAGE, - ATTR_IMAGE_NAME, - ATTR_IMAGE_PATH, - ATTR_ORDER, - ATTR_TRACKING_NUM, - ATTR_USPS_IMAGE, - CONF_PATH, - DOMAIN, - IMAGE_SENSORS, - SENSOR_TYPES, - VERSION, -) - -_LOGGER = logging.getLogger(__name__) - -DELIVERED_SUFFIXES = {"delivered"} - - -async def async_setup_entry( - hass, - entry: MailAndPackagesConfigEntry, - async_add_entities, -): - """Set up the sensor entities.""" - coordinator = entry.runtime_data.coordinator - resources = coordinator.config.get(CONF_RESOURCES, []) - - sensors = [ - PackagesSensor(entry, SENSOR_TYPES[variable], coordinator) - for variable in resources - if variable in SENSOR_TYPES - ] - - sensors.extend( - ImagePathSensors(hass, entry, value, coordinator) - for value in IMAGE_SENSORS.values() - ) - - async_add_entities(sensors, False) - - -class PackagesSensor(CoordinatorEntity, SensorEntity): - """Representation of a sensor.""" - - def __init__( - self, - config: ConfigEntry, - sensor_description: SensorEntityDescription, - coordinator: Any, - ): - """Initialize the sensor.""" - super().__init__(coordinator) - self.entity_description = sensor_description - self.coordinator = coordinator - self._config = config - self._name = sensor_description.name - self.type = sensor_description.key - self._host = config.data[CONF_HOST] - self._unique_id = self._config.entry_id - self.data = self.coordinator.data - parts = self.type.split("_") - if len(parts) > 1: - prefix = "_".join(parts[:-1]) - if parts[-1] in DELIVERED_SUFFIXES: - self._tracking_key = f"{prefix}_delivered_tracking" - else: - self._tracking_key = f"{prefix}_tracking" - else: - self._tracking_key = f"{self.type}_tracking" - - @property - def device_info(self) -> dict: - """Return device information about the mailbox.""" - return { - "connections": {(DOMAIN, self._unique_id)}, - "name": self._host, - "manufacturer": "IMAP E-Mail", - "sw_version": VERSION, - } - - @property - def unique_id(self) -> str: - """Return a unique, Home Assistant friendly identifier for this entity.""" - return f"sensor_{self._host}_{self.type}_{self._unique_id}" - - @property - def name(self) -> str: - """Return the name of the sensor.""" - return self._name - - @property - def native_value(self) -> Any: - """Return the state of the sensor.""" - if self.coordinator.data is None: - return None - value = self.coordinator.data.get(self.type) - - if self.type == "mail_updated": - # Safely handle string vs datetime to prevent ValueError - if isinstance(value, str): - try: - value = datetime.datetime.fromisoformat(value) - except ValueError: - value = datetime.datetime.now(datetime.UTC) - elif value is None: - value = datetime.datetime.now(datetime.UTC) - return value - - @property - def should_poll(self) -> bool: - """No need to poll. Coordinator notifies entity of updates.""" - return False - - @property - def available(self) -> bool: - """Return if entity is available.""" - return self.coordinator.data is not None - - @property - def extra_state_attributes(self) -> str | None: - """Return device specific state attributes.""" - attr = {} - data = self.coordinator.data - if data is None: - return attr - - if any( - sensor in self.type - for sensor in ["_delivering", "_delivered", "_packages", "_exception"] - ): - if tracking := data.get(self._tracking_key): - attr[ATTR_TRACKING_NUM] = tracking - - if "Amazon" in self._name: - self._add_amazon_attributes(attr, data) - elif self._name == "Mail USPS Mail": - if image_name := data.get(ATTR_IMAGE_NAME): - attr[ATTR_IMAGE] = image_name - - return attr - - def _add_amazon_attributes(self, attr: dict, data: dict) -> None: - """Add Amazon specific attributes to the sensor.""" - if self.type == AMAZON_EXCEPTION: - if order := data.get(AMAZON_EXCEPTION_ORDER, data.get(ATTR_ORDER)): - attr[ATTR_ORDER] = order - elif order := data.get(AMAZON_ORDER): - attr[ATTR_ORDER] = order - elif self.type == AMAZON_HUB: - if code := data.get(AMAZON_HUB_CODE, data.get(ATTR_CODE)): - attr[ATTR_CODE] = code - elif self.type == AMAZON_OTP: - if code := data.get(AMAZON_OTP_CODE, data.get(ATTR_CODE)): - attr[ATTR_CODE] = code - - -class ImagePathSensors(CoordinatorEntity, SensorEntity): - """Representation of a sensor.""" - - def __init__( - self, - hass: HomeAssistant, - config: ConfigEntry, - sensor_description: SensorEntityDescription, - coordinator: Any, - ): - """Initialize the sensor.""" - super().__init__(coordinator) - self.entity_description = sensor_description - self.hass = hass - self.coordinator = coordinator - self._config = config - self._name = sensor_description.name - self.type = sensor_description.key - self._host = config.data[CONF_HOST] - self._unique_id = self._config.entry_id - - @property - def device_info(self) -> dict: - """Return device information about the mailbox.""" - return { - "connections": {(DOMAIN, self._unique_id)}, - "name": self._host, - "manufacturer": "IMAP E-Mail", - "sw_version": VERSION, - } - - @property - def unique_id(self) -> str: - """Return a unique, Home Assistant friendly identifier for this entity.""" - return f"sensor_{self._host}_{self.type}_{self._unique_id}" - - @property - def name(self) -> str: - """Return the name of the sensor.""" - return self._name - - @property - def native_value(self) -> str | None: - """Return the state of the sensor.""" - if self.coordinator.data is None: - return None - - image = "" - the_path = None - - image = self.coordinator.data.get(ATTR_USPS_IMAGE) - - grid_image = self.coordinator.data.get(ATTR_GRID_IMAGE_NAME) - - path = self.coordinator.data.get( - ATTR_IMAGE_PATH, - self.coordinator.config.get(CONF_PATH), - ) - - if self.type == "usps_mail_image_system_path" and image: - _LOGGER.debug("Updating system image path to: %s", path) - the_path = self.hass.config.path(path, image) - elif self.type == "usps_mail_grid_image_path" and grid_image: - _LOGGER.debug("Updating grid image path to: %s", path) - the_path = self.hass.config.path(path, grid_image) - elif self.type == "usps_mail_image_url" and image: - url = self._get_base_url() - if url: - the_path = f"{url.rstrip('/')}/local/mail_and_packages/{image}" - return the_path - - def _get_base_url(self) -> str | None: - """Return the best available base URL for building image links.""" - try: - return get_url(self.hass, prefer_external=True) - except NoURLAvailableError: - _LOGGER.debug("No URL available for image link.") - return None - - @property - def should_poll(self) -> bool: - """No need to poll. Coordinator notifies entity of updates.""" - return False - - @property - def available(self) -> bool: - """Return if entity is available.""" - return self.coordinator.data is not None diff --git a/custom_components/mail_and_packages/services.yaml b/custom_components/mail_and_packages/services.yaml deleted file mode 100644 index 7878411..0000000 --- a/custom_components/mail_and_packages/services.yaml +++ /dev/null @@ -1,13 +0,0 @@ -update_image: - name: Refresh the Mail Camera(s) - description: Refreshes the Mail Camera specified or all of them at once. Leave blank to refresh them all. - fields: - entity_id: - name: Camera Entity - description: The camera entity to refresh. - example: camera.mail_usps_camera - required: false - selector: - entity: - domain: camera - integration: mail_and_packages diff --git a/custom_components/mail_and_packages/shippers/__init__.py b/custom_components/mail_and_packages/shippers/__init__.py deleted file mode 100644 index a8ab9f4..0000000 --- a/custom_components/mail_and_packages/shippers/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Shippers for Mail and Packages.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from .amazon import AmazonShipper -from .generic import GenericShipper -from .post_de import PostDEShipper -from .usps import USPSShipper - -if TYPE_CHECKING: - from homeassistant.core import HomeAssistant - - from .base import Shipper - -SHIPPER_REGISTRY = { - "amazon": AmazonShipper, - "generic": GenericShipper, - "post_de": PostDEShipper, - "usps": USPSShipper, -} - - -def get_shipper_for_sensor( - hass: HomeAssistant, - config: dict, - sensor_type: str, -) -> Shipper | None: - """Return the appropriate shipper for the given sensor type.""" - # Check specialized shippers first - for name, shipper_class in SHIPPER_REGISTRY.items(): - if name == "generic": - continue - if shipper_class.handles_sensor(sensor_type): - return shipper_class(hass, config) - - # Fallback to generic - if GenericShipper.handles_sensor(sensor_type): - return GenericShipper(hass, config) - - return None diff --git a/custom_components/mail_and_packages/shippers/amazon.py b/custom_components/mail_and_packages/shippers/amazon.py deleted file mode 100644 index 28c954a..0000000 --- a/custom_components/mail_and_packages/shippers/amazon.py +++ /dev/null @@ -1,633 +0,0 @@ -"""Base Shipper class.""" - -from __future__ import annotations - -import datetime -import email -import logging -import re -from pathlib import Path -from shutil import copyfile -from typing import Any - -import anyio -import dateparser -import homeassistant.helpers.config_validation as cv -from aioimaplib import IMAP4_SSL - -from custom_components.mail_and_packages import const -from custom_components.mail_and_packages.const import ( - AMAZON_DELIVERED, - AMAZON_DELIVERED_SUBJECT, - AMAZON_EXCEPTION, - AMAZON_EXCEPTION_BODY, - AMAZON_EXCEPTION_ORDER, - AMAZON_EXCEPTION_SUBJECT, - AMAZON_HUB, - AMAZON_HUB_BODY, - AMAZON_HUB_CODE, - AMAZON_HUB_SUBJECT, - AMAZON_HUB_SUBJECT_SEARCH, - AMAZON_ORDER, - AMAZON_ORDERED_SUBJECT, - AMAZON_OTP, - AMAZON_OTP_CODE, - AMAZON_OTP_REGEX, - AMAZON_OTP_SUBJECT, - AMAZON_PACKAGES, - AMAZON_SHIPMENT_SUBJECT, - ATTR_COUNT, - CONF_AMAZON_DAYS, - CONF_AMAZON_DOMAIN, - CONF_AMAZON_FWDS, - CONF_DURATION, - CONF_FORWARDING_HEADER, - DEFAULT_AMAZON_DAYS, -) -from custom_components.mail_and_packages.utils.amazon import ( - _extract_hub_code, - amazon_email_addresses, - download_amazon_img, - extract_order_numbers, - filter_amazon_strings, - get_decoded_subject, - get_email_body, - parse_amazon_arrival_date, - search_amazon_emails, -) -from custom_components.mail_and_packages.utils.cache import EmailCache -from custom_components.mail_and_packages.utils.date import get_today -from custom_components.mail_and_packages.utils.image import ( - cleanup_images, - generate_delivery_gif, - random_filename, - resize_images, -) -from custom_components.mail_and_packages.utils.imap import ( - email_fetch, - email_search, -) - -from .base import Shipper - -_LOGGER = logging.getLogger(__name__) - - -class AmazonShipper(Shipper): - """Amazon shipper implementation.""" - - @property - def name(self) -> str: - """Return shipper name.""" - return "amazon" - - @classmethod - def handles_sensor(cls, sensor_type: str) -> bool: - """Return True if this shipper handles the given sensor type.""" - return sensor_type.startswith("amazon_") or sensor_type == AMAZON_PACKAGES - - async def process( - self, - account: IMAP4_SSL, - date: str, - sensor_type: str, - cache: EmailCache | None = None, - ) -> dict[str, Any]: - """Process Amazon-specific emails.""" - forwarding_header = self.config.get(CONF_FORWARDING_HEADER, "") - if forwarding_header and forwarding_header != "(none)": - # Header mode: use native Amazon addresses; fwds not needed - fwds = None - else: - forwarding_header = "" - fwds = cv.ensure_list_csv(self.config.get(CONF_AMAZON_FWDS)) - days = self.config.get(CONF_AMAZON_DAYS, DEFAULT_AMAZON_DAYS) - domain = self.config.get(CONF_AMAZON_DOMAIN) - - if sensor_type == AMAZON_PACKAGES: - count = await self._parse_amazon_emails( - account, "count", fwds, days, domain, cache, forwarding_header - ) - orders = await self._parse_amazon_emails( - account, "order", fwds, days, domain, cache, forwarding_header - ) - return { - AMAZON_PACKAGES: count, - AMAZON_ORDER: orders, - } - - if sensor_type == AMAZON_ORDER: - result = await self._parse_amazon_emails( - account, "order", fwds, days, domain, cache, forwarding_header - ) - return {AMAZON_ORDER: result} - - if sensor_type == AMAZON_HUB: - return await self._amazon_hub(account, fwds, cache, forwarding_header) - - if sensor_type == AMAZON_OTP: - result = await self._amazon_otp(account, fwds, cache, forwarding_header) - return {sensor_type: result} - - if sensor_type == AMAZON_EXCEPTION: - return await self._amazon_exception( - account, fwds, domain, cache, forwarding_header - ) - - if sensor_type == AMAZON_DELIVERED: - image_path = self.config.get("image_path") - image_name = self.config.get("amazon_image") - result = await self._amazon_search( - account, - image_path, - image_name, - domain, - fwds, - cache, - forwarding_header, - ) - return { - sensor_type: result, - const.ATTR_AMAZON_IMAGE: image_name, - const.ATTR_IMAGE_PATH: image_path, - } - - return {ATTR_COUNT: 0} - - async def process_batch( - self, - account: IMAP4_SSL, - date: str, - sensors: list[str], - cache: EmailCache, - since_date: str | None = None, - ) -> dict[str, Any]: - """Process multiple Amazon sensors in batch.""" - res = {} - for sensor in sensors: - sensor_res = await self.process(account, date, sensor, cache) - res.update(sensor_res) - # Replicate coordinator dictionary logic - if sensor not in sensor_res: - if ATTR_COUNT in sensor_res: - res[sensor] = sensor_res[ATTR_COUNT] - return res - - # Internal helper methods (migrated from helpers.py) - - async def _parse_amazon_emails( - self, - account: IMAP4_SSL, - param: str, - fwds: list[str] | None = None, - days: int = DEFAULT_AMAZON_DAYS, - domain: str | None = None, - cache: EmailCache | None = None, - forwarding_header: str = "", - ) -> list[str] | int: - """Parse Amazon emails for delivery date and order number.""" - today_date = get_today() - address_list = amazon_email_addresses(fwds, domain) - unique_emails = await search_amazon_emails( - account, address_list, days, domain, cache, forwarding_header - ) - order_pattern = re.compile(r"[0-9]{3}-[0-9]{7}-[0-9]{7}") - - context = { - "today": today_date, - "packages_arriving_today": {}, - "delivered_packages": {}, - "amazon_delivered": [], - "deliveries_today": [], - "all_shipped_orders": set(), - "order_pattern": order_pattern, - } - - for email_id in unique_emails: - await self._process_amazon_email(account, email_id, context, cache) - - final_count = self._calculate_final_count(context) - - if param == "count": - return final_count - - return [ - order_id - for order_id in context["all_shipped_orders"] - if context["packages_arriving_today"].get(order_id, 0) - > context["delivered_packages"].get(order_id, 0) - or ( - context["packages_arriving_today"].get(order_id, 0) == 0 - and context["delivered_packages"].get(order_id, 0) == 0 - ) - ] - - async def _process_amazon_email( - self, - account: IMAP4_SSL, - email_id: bytes | str, - ctx: dict, - cache: EmailCache | None = None, - ): - """Process a single Amazon email.""" - fetch_id = email_id.decode() if isinstance(email_id, bytes) else email_id - if cache: - data = (await cache.fetch(fetch_id, "(RFC822)"))[1] - else: - data = (await email_fetch(account, fetch_id, "(RFC822)"))[1] - - for response_part in data: - if not isinstance(response_part, (bytes, bytearray)): - continue - - msg = email.message_from_bytes(response_part) - email_date = await self._parse_email_date(msg) - email_subject = get_decoded_subject(msg) - - if any(s.lower() in email_subject.lower() for s in AMAZON_ORDERED_SUBJECT): - continue - - email_msg = get_email_body(msg) - if any( - s.lower() in email_subject.lower() for s in AMAZON_DELIVERED_SUBJECT - ): - self._handle_delivered_email(email_subject, email_msg, ctx) - continue - - await self._handle_shipping_email(email_subject, email_msg, email_date, ctx) - - async def _parse_email_date( - self, - msg: email.message.Message, - ) -> datetime.date | None: - """Parse the date from an email message.""" - date_str = msg.get("Date") - if not date_str: - return None - parsed = await self.hass.async_add_executor_job(dateparser.parse, date_str) - return parsed.date() if parsed else None - - def _handle_delivered_email(self, subject: str, body: str | None, ctx: dict): - """Handle an Amazon 'delivered' email.""" - orders = extract_order_numbers(subject, ctx["order_pattern"]) - if not orders and body: - orders = extract_order_numbers(body, ctx["order_pattern"]) - for o in orders: - ctx["delivered_packages"][o] = ctx["delivered_packages"].get(o, 0) + 1 - if o not in ctx["amazon_delivered"]: - ctx["amazon_delivered"].append(o) - - async def _handle_shipping_email( - self, - subject: str, - body: str | None, - date: datetime.date | None, - ctx: dict, - ): - """Handle an Amazon 'shipping' or 'arriving' email.""" - order_id = self._extract_first_order_id(subject, body, ctx["order_pattern"]) - if order_id: - ctx["all_shipped_orders"].add(order_id) - - if body: - parsed_arrival = await parse_amazon_arrival_date(self.hass, body, date) - if parsed_arrival == ctx["today"]: - if order_id: - ctx["packages_arriving_today"][order_id] = ( - ctx["packages_arriving_today"].get(order_id, 0) + 1 - ) - else: - ctx["deliveries_today"].append("Amazon Order") - - def _extract_first_order_id( - self, - subject: str, - body: str | None, - pattern: re.Pattern, - ) -> str | None: - """Extract the first order number found in subject or body.""" - orders = extract_order_numbers(subject, pattern) - if orders: - return orders[0] - if body: - orders = extract_order_numbers(body, pattern) - if orders: - return orders[0] - return None - - def _calculate_final_count(self, ctx: dict) -> int: - """Calculate the final count of packages arriving today.""" - deliveries_today = [ - item - for item in ctx["deliveries_today"] - if item not in ctx["amazon_delivered"] - ] - final_count = 0 - for order_id, arriving_count in ctx["packages_arriving_today"].items(): - delivered_count = ctx["delivered_packages"].get(order_id, 0) - final_count += max(0, arriving_count - delivered_count) - return final_count + len(deliveries_today) - - async def _amazon_search( - self, - account: IMAP4_SSL, - image_path: str, - amazon_image_name: str, - amazon_domain: str, - fwds: list[str] | None = None, - cache: EmailCache | None = None, - forwarding_header: str = "", - ) -> int: - """Find Amazon Delivered email and handle images.""" - _LOGGER.debug("=== AMAZON DELIVERED SEARCH START ===") - subjects = AMAZON_DELIVERED_SUBJECT - today = get_today().strftime("%d-%b-%Y") - count = 0 - all_image_urls = [] - - await self.hass.async_add_executor_job( - cleanup_images, - f"{image_path or ''}amazon/", - ) - - address_list = amazon_email_addresses(fwds, amazon_domain) - _LOGGER.debug("Amazon email search addresses: %s", address_list) - if amazon_domain: - subjects = filter_amazon_strings(subjects, amazon_domain) - - (server_response, data) = await email_search( - account=account, - address=address_list, - date=today, - subject=subjects, - header=forwarding_header, - ) - if server_response == "OK" and data[0]: - for email_id in data[0].split(): - fetch_id = ( - email_id.decode() if isinstance(email_id, bytes) else email_id - ) - if cache: - msg_data = (await cache.fetch(fetch_id, "(RFC822)"))[1] - else: - msg_data = (await email_fetch(account, fetch_id, "(RFC822)"))[1] - - is_delivered, urls = self._is_amazon_delivered(msg_data, subjects) - if is_delivered: - count += 1 - for url in urls: - if url not in all_image_urls: - all_image_urls.append(url) - - await self._process_amazon_images( - all_image_urls, image_path, amazon_image_name, count - ) - - return count - - def _is_amazon_delivered( - self, msg_data: list, subjects: list[str] - ) -> tuple[bool, list[str]]: - """Verify if email is a delivered notification and return image URLs.""" - for response_part in msg_data: - if not isinstance(response_part, (bytes, bytearray)): - continue - msg = email.message_from_bytes(response_part) - subject = get_decoded_subject(msg) - if not subject: - continue - - # Check if subject contains any delivered keyword (case-insensitive) - has_delivered = any(s.lower() in subject.lower() for s in subjects) - # Check if subject contains ordered or shipped keywords (case-insensitive) - has_ordered = any( - s.lower() in subject.lower() for s in AMAZON_ORDERED_SUBJECT - ) - has_shipped = any( - s.lower() in subject.lower() for s in AMAZON_SHIPMENT_SUBJECT - ) - - if has_delivered and not has_ordered and not has_shipped: - urls = self._extract_amazon_image_urls(msg) - return True, urls - return False, [] - - def _extract_amazon_image_urls(self, msg: email.message.Message) -> list[str]: - """Extract image URLs from Amazon email body.""" - urls = [] - pattern = re.compile(rf"{const.AMAZON_IMG_PATTERN}") - for part in msg.walk(): - if part.get_content_type() != "text/html": - continue - part_payload = part.get_payload(decode=True) - if part_payload: - part_content = part_payload.decode("utf-8", "ignore") - found = pattern.findall(part_content) - for url in found: - if url[1] not in const.AMAZON_IMG_LIST: - continue - full_url = url[0] + url[1] + url[2] - if full_url not in urls: - urls.append(full_url) - return urls - - async def _process_amazon_images( - self, - image_urls: list[str], - image_base_path: str, - image_name: str, - email_count: int, - ) -> None: - """Process and save Amazon delivery images.""" - if not image_base_path or not image_name: - return - - amazon_path = Path(image_base_path) / "amazon" - image_files = await self._download_all_images(image_urls, image_base_path) - - if len(image_files) > 1: - await self._create_amazon_gif(image_files, amazon_path, image_name) - elif len(image_files) == 1: - await self._save_single_amazon_image( - image_files[0], amazon_path, image_name - ) - else: - await self._copy_amazon_placeholder(amazon_path, image_name) - - async def _download_all_images(self, urls: list[str], base_path: str) -> list[str]: - """Download all image URLs to temporary files.""" - image_files = [] - amazon_path = Path(base_path) / "amazon" - for url in urls: - temp_filename = random_filename() - await download_amazon_img(url, base_path, temp_filename, self.hass) - full_temp_path = amazon_path / temp_filename - if await anyio.Path(full_temp_path).exists(): - image_files.append(str(full_temp_path)) - return image_files - - async def _create_amazon_gif( - self, image_files: list[str], amazon_path: Path, image_name: str - ) -> None: - """Create animated GIF from multiple images.""" - _LOGGER.debug("Combining %d Amazon images into GIF", len(image_files)) - resized_images = await self.hass.async_add_executor_job( - resize_images, image_files, 724, 320 - ) - gif_path = str(amazon_path / image_name) - duration = self.config.get(CONF_DURATION, 5) * 1000 - await self.hass.async_add_executor_job( - generate_delivery_gif, resized_images, gif_path, duration - ) - # Cleanup - for img in image_files + resized_images: - if await anyio.Path(img).exists(): - await self.hass.async_add_executor_job( - cleanup_images, str(Path(img).parent) + "/", Path(img).name - ) - - async def _save_single_amazon_image( - self, image_file: str, amazon_path: Path, image_name: str - ) -> None: - """Save a single image by renaming it to the final name.""" - final_path = amazon_path / image_name - if await anyio.Path(final_path).exists(): - await anyio.Path(final_path).unlink() - await self.hass.async_add_executor_job(Path(image_file).rename, final_path) - _LOGGER.debug("Single Amazon image saved: %s", image_name) - - async def _copy_amazon_placeholder( - self, amazon_path: Path, image_name: str - ) -> None: - """Copy the Amazon no-delivery placeholder.""" - nomail = f"{Path(__file__).parent.parent}/no_deliveries_amazon.jpg" - _LOGGER.debug("No Amazon images found in emails, using placeholder") - try: - await self.hass.async_add_executor_job( - copyfile, nomail, str(amazon_path / image_name) - ) - except OSError as err: - _LOGGER.error("Error attempting to copy image: %s", err) - - async def _amazon_hub( - self, - account: IMAP4_SSL, - fwds: list[str] | None = None, - cache: EmailCache | None = None, - forwarding_header: str = "", - ) -> dict[str, Any]: - """Find Amazon Hub code.""" - _LOGGER.debug("=== AMAZON HUB SEARCH START ===") - count = 0 - code = [] - processed_ids = [] - today = get_today().strftime("%d-%b-%Y") - address_list = amazon_email_addresses(fwds, "amazon.com") - for search_subject in AMAZON_HUB_SUBJECT: - (server_response, data) = await email_search( - account, - address_list, - today, - search_subject, - body=AMAZON_HUB_BODY, - header=forwarding_header, - ) - if server_response == "OK" and data[0] is not None: - for num in data[0].split(): - if num in processed_ids: - continue - processed_ids.append(num) - if cache: - msg_parts = (await cache.fetch(num, "(RFC822)"))[1] - else: - msg_parts = (await email_fetch(account, num, "(RFC822)"))[1] - for response_part in msg_parts: - if isinstance(response_part, (bytes, bytearray)): - msg = email.message_from_bytes(response_part) - actual_subject = get_decoded_subject(msg) - body = get_email_body(msg) - if hub_code := _extract_hub_code( - body, - AMAZON_HUB_BODY, - actual_subject, - AMAZON_HUB_SUBJECT_SEARCH, - ): - count += 1 - if hub_code not in code: - code.append(hub_code) - return {AMAZON_HUB: count, AMAZON_HUB_CODE: code} - - async def _amazon_otp( - self, - account: IMAP4_SSL, - fwds: list[str] | None = None, - cache: EmailCache | None = None, - forwarding_header: str = "", - ) -> dict[str, Any]: - """Find Amazon OTP code.""" - code = [] - today = get_today().strftime("%d-%b-%Y") - address_list = amazon_email_addresses(fwds, "amazon.com") - (server_response, data) = await email_search( - account, - address_list, - today, - AMAZON_OTP_SUBJECT, - body=AMAZON_OTP_REGEX, - header=forwarding_header, - ) - if server_response == "OK" and data[0] is not None: - for num in data[0].split(): - if cache: - msg_parts = (await cache.fetch(num, "(RFC822)"))[1] - else: - msg_parts = (await email_fetch(account, num, "(RFC822)"))[1] - for response_part in msg_parts: - if isinstance(response_part, (bytes, bytearray)): - msg = email.message_from_bytes(response_part) - body = get_email_body(msg) - if ( - found := re.compile(AMAZON_OTP_REGEX).search(body) - ) is not None: - code.append(found.group(2)) - return {AMAZON_OTP: len(code), AMAZON_OTP_CODE: code} - - async def _amazon_exception( - self, - account: IMAP4_SSL, - fwds: list[str] | None = None, - domain: str | None = None, - cache: EmailCache | None = None, - forwarding_header: str = "", - ) -> dict[str, Any]: - """Find Amazon exception emails.""" - count = 0 - orders = [] - today = get_today().strftime("%d-%b-%Y") - address_list = amazon_email_addresses(fwds, domain) - (server_response, data) = await email_search( - account=account, - address=address_list, - date=today, - subject=AMAZON_EXCEPTION_SUBJECT, - header=forwarding_header, - ) - if server_response == "OK" and data[0] is not None: - order_pattern = re.compile(r"[0-9]{3}-[0-9]{7}-[0-9]{7}") - for num in data[0].split(): - if cache: - msg_parts = (await cache.fetch(num, "(RFC822)"))[1] - else: - msg_parts = (await email_fetch(account, num, "(RFC822)"))[1] - for response_part in msg_parts: - if isinstance(response_part, (bytes, bytearray)): - msg = email.message_from_bytes(response_part) - body = get_email_body(msg) - subject = get_decoded_subject(msg) - if AMAZON_EXCEPTION_BODY in body: - count += 1 - if found := order_pattern.findall(body): - orders.extend(found) - if found := order_pattern.findall(subject): - orders.extend(found) - return {AMAZON_EXCEPTION: count, AMAZON_EXCEPTION_ORDER: orders} diff --git a/custom_components/mail_and_packages/shippers/base.py b/custom_components/mail_and_packages/shippers/base.py deleted file mode 100644 index 36b91d0..0000000 --- a/custom_components/mail_and_packages/shippers/base.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Base Shipper class.""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Any - -from aioimaplib import IMAP4_SSL -from homeassistant.core import HomeAssistant - -from custom_components.mail_and_packages.utils.cache import EmailCache - - -class Shipper(ABC): - """Base class for shipper-specific parsing logic.""" - - def __init__(self, hass: HomeAssistant, config: dict[str, Any]) -> None: - """Initialize the shipper.""" - self.hass = hass - self.config = config - - @property - @abstractmethod - def name(self) -> str: - """Return the internal name of the shipper.""" - - @classmethod - @abstractmethod - def handles_sensor(cls, sensor_type: str) -> bool: - """Return True if this shipper handles the given sensor type.""" - - @abstractmethod - async def process( - self, - account: IMAP4_SSL, - date: str, - sensor_type: str, - ) -> dict[str, Any]: - """Process emails for this shipper on the given date for a specific sensor.""" - - @abstractmethod - async def process_batch( - self, - account: IMAP4_SSL, - date: str, - sensors: list[str], - cache: EmailCache, - since_date: str | None = None, - ) -> dict[str, Any]: - """Process multiple sensors for this shipper using batched fetching/searching. - - since_date: earliest IMAP SINCE date for _delivering/_exception/_delivered - sensors. Defaults to date (today) if not provided. - """ diff --git a/custom_components/mail_and_packages/shippers/generic.py b/custom_components/mail_and_packages/shippers/generic.py deleted file mode 100644 index 05ebe03..0000000 --- a/custom_components/mail_and_packages/shippers/generic.py +++ /dev/null @@ -1,708 +0,0 @@ -"""Generic Shipper class.""" - -from __future__ import annotations - -import email -import logging -from email.header import decode_header -from pathlib import Path -from shutil import copyfile -from typing import Any - -import anyio -from aioimaplib import IMAP4_SSL - -from custom_components.mail_and_packages.const import ( - AMAZON_DELIEVERED_BY_OTHERS_SEARCH_TEXT, - AMAZON_DELIVERED, - ATTR_BODY, - ATTR_BODY_COUNT, - ATTR_COUNT, - ATTR_EMAIL, - ATTR_PATTERN, - ATTR_SUBJECT, - ATTR_TRACKING, - CAMERA_DATA, - CAMERA_EXTRACTION_CONFIG, - CONF_FORWARDING_HEADER, - SENSOR_DATA, -) -from custom_components.mail_and_packages.utils.cache import EmailCache -from custom_components.mail_and_packages.utils.email import find_text, find_text_matches -from custom_components.mail_and_packages.utils.imap import ( - email_fetch, - email_fetch_headers, - email_search, -) -from custom_components.mail_and_packages.utils.shipper import ( - generic_delivery_image_extraction, - get_tracking, -) - -from .base import Shipper - -_LOGGER = logging.getLogger(__name__) - - -class GenericShipper(Shipper): - """Generic Shipper class for UPS, FedEx, Walmart, etc.""" - - @property - def name(self) -> str: - """Return the internal name of the shipper.""" - return "generic" - - @classmethod - def handles_sensor(cls, sensor_type: str) -> bool: - """Return True if this shipper handles the given sensor type.""" - return sensor_type in SENSOR_DATA - - async def process( - self, - account: IMAP4_SSL, - date: str, - sensor_type: str, - cache: EmailCache | None = None, - since_date: str | None = None, - ) -> dict[str, Any]: - """Process emails for this shipper on the given date. - - since_date: if provided, used instead of date for _delivering and - _exception sensors so that emails from previous days are included. - """ - _LOGGER.debug("Processing generic sensor: %s", sensor_type) - - if sensor_type not in SENSOR_DATA: - _LOGGER.error("Sensor %s not found in SENSOR_DATA", sensor_type) - return {ATTR_COUNT: 0} - - config = SENSOR_DATA[sensor_type] - email_addresses = config.get(ATTR_EMAIL, []) - subjects = config.get(ATTR_SUBJECT, []) - - # _packages sensors with no email/subject are computed in process_batch - # as delivering + delivered; skip IMAP search here. - if sensor_type.endswith("_packages") and not email_addresses and not subjects: - _LOGGER.debug( - "Skipping email search for %s: no email addresses configured", - sensor_type, - ) - return {ATTR_COUNT: 0, ATTR_TRACKING: []} - - forwarding_header, email_addresses = self._resolve_forwarding(email_addresses) - - # _delivering/_exception/_packages use the extended window so in-transit - # packages remain visible across the midnight boundary. - # _delivered uses today's date for the sensor count (resets at midnight) - # but also searches the extended window to obtain tracking numbers for - # deduplication — without those, a package delivered yesterday would still - # appear as "delivering" today because the delivering email is in the window - # but the delivered email is not. - is_delivered = sensor_type.endswith("_delivered") - search_date = date - if ( - since_date - and sensor_type.endswith( - ("_delivering", "_exception", "_delivered", "_packages") - ) - and sensor_type != "post_de_delivering" - ): - search_date = since_date - - result = {ATTR_COUNT: 0, ATTR_TRACKING: []} - - # Skip email search for sensors with no email addresses configured - # (e.g. *_packages sensors that are empty dicts in SENSOR_DATA) - if not email_addresses: - _LOGGER.debug( - "Skipping email search for %s: no email addresses configured", - sensor_type, - ) - return result - - image_path = self.config.get("image_path") - # Setup image extraction - shipper_cfg = await self._setup_image_extraction(sensor_type, image_path) - image_found = False - - count, found_data, image_found = await self._search_for_emails( - account, - email_addresses, - search_date, - subjects, - config, - shipper_cfg, - sensor_type, - result, - cache, - forwarding_header, - ) - - # Process tracking numbers - result[ATTR_TRACKING] = await self._process_tracking_numbers( - sensor_type, - found_data, - account, - cache, - ) - if result[ATTR_TRACKING]: - count = len(result[ATTR_TRACKING]) - - if is_delivered: - result["pre_filtered_tracking"] = result.get(ATTR_TRACKING, []) - - # For _delivered sensors, the extended-window search gives us tracking - # numbers needed for deduplication (above), but the count must reflect - # only today's deliveries so the sensor resets at midnight. - if is_delivered and since_date and search_date != date: - today_result: dict[str, Any] = {ATTR_COUNT: 0, ATTR_TRACKING: []} - today_count, today_found, _ = await self._search_for_emails( - account, - email_addresses, - date, - subjects, - config, - shipper_cfg, - sensor_type, - today_result, - cache, - forwarding_header, - ) - today_tracking = await self._process_tracking_numbers( - sensor_type, today_found, account, cache - ) - count = len(today_tracking) if today_tracking else today_count - result[ATTR_TRACKING] = today_tracking - - result[ATTR_COUNT] = count - if shipper_cfg: - image_attr = f"{shipper_cfg['name']}_image" - result[image_attr] = shipper_cfg["image_name"] - result["image_path"] = image_path - - if not image_found: - await self._copy_generic_placeholder(shipper_cfg) - - return result - - def _resolve_forwarding(self, email_addresses: list[str]) -> tuple[str, list[str]]: - """Return (forwarding_header, resolved_email_addresses). - - Header mode: uses original-sender header for matching; address list - is passed as-is so IMAP can match via HEADER substring. - Address-list mode: prepends the user's forwarded addresses so that - emails arriving through a forwarding service are also matched. - """ - forwarding_header = self.config.get(CONF_FORWARDING_HEADER, "") - if forwarding_header and forwarding_header != "(none)": - return forwarding_header, email_addresses - forwarding_header = "" - forwarded_emails = self.config.get("forwarded_emails", []) - if isinstance(forwarded_emails, str): - forwarded_emails = [ - e.strip() for e in forwarded_emails.split(",") if e.strip() - ] - if forwarded_emails: - email_addresses = forwarded_emails + email_addresses - return forwarding_header, email_addresses - - async def process_batch( - self, - account: IMAP4_SSL, - date: str, - sensors: list[str], - cache: EmailCache, - since_date: str | None = None, - ) -> dict[str, Any]: - """Process multiple generic sensors in batch.""" - batch_results, all_tracking = await self._process_individual_sensors( - account, date, sensors, cache, since_date - ) - - self._deduplicate_batch_tracking(batch_results) - self._compute_package_totals(batch_results) - - # Merge results and aggregate global tracking - res = {} - for sensor, sensor_res in batch_results: - tracking = ( - sensor_res.pop("pre_filtered_tracking", []) - if sensor.endswith("_delivered") - else sensor_res.get(ATTR_TRACKING) - ) - res.update(sensor_res) - # Expose per-sensor raw tracking for coordinator state management. - # Keyed as "_tracking_details" to distinguish from the public data dict. - if tracking and sensor.endswith( - ("_delivering", "_delivered", "_exception") - ): - res.setdefault("_tracking_details", {})[sensor] = list(tracking) - - if all_tracking: - res[ATTR_TRACKING] = list(all_tracking) - - return res - - async def _process_individual_sensors( - self, - account: IMAP4_SSL, - date: str, - sensors: list[str], - cache: EmailCache, - since_date: str | None = None, - ) -> tuple[list[tuple[str, dict[str, Any]]], set[str]]: - """Process each sensor independently and aggregate tracking.""" - batch_results = [] - all_tracking = set() - - for sensor in sensors: - sensor_res = await self.process( - account, date, sensor, cache, since_date=since_date - ) - # Replicate coordinator dictionary logic for local sensor counts - if sensor not in sensor_res and ATTR_COUNT in sensor_res: - sensor_res[sensor] = sensor_res[ATTR_COUNT] - - # Capture today-only tracking for _delivered sensors BEFORE - # _deduplicate_batch_tracking runs (which currently only modifies - # _delivering and _packages sensor results). - if sensor_res.get(ATTR_TRACKING) and sensor.endswith("_delivered"): - sensor_res[f"{sensor}_tracking"] = sensor_res[ATTR_TRACKING] - - # Record results for post-processing - batch_results.append((sensor, sensor_res)) - - # Aggregate all tracking numbers found - if sensor_res.get(ATTR_TRACKING): - all_tracking.update(sensor_res[ATTR_TRACKING]) - - return batch_results, all_tracking - - def _deduplicate_batch_tracking( - self, - batch_results: list[tuple[str, dict[str, Any]]], - ) -> None: - """Deduplicate tracking numbers across sensors based on shipper prefix.""" - shippers = {} - for sensor, sensor_res in batch_results: - # Prefix is everything before the last underscore (e.g., 'ups', 'fedex') - prefix = "_".join(sensor.split("_")[:-1]) - if prefix not in shippers: - shippers[prefix] = { - "delivered": set(), - "delivering": set(), - "update_targets": [], - "package_targets": [], - } - - tracking = set(sensor_res.get(ATTR_TRACKING, [])) - if sensor.endswith("_delivered"): - # ATTR_TRACKING on _delivered sensors holds only TODAY's - # deliveries (so the sensor resets at midnight); dedup must - # use the extended-window list or packages delivered on a - # previous day are never subtracted from _delivering. - extended = sensor_res.get("pre_filtered_tracking") - shippers[prefix]["delivered"].update( - tracking if extended is None else set(extended) - ) - elif sensor.endswith(("_delivering", "_exception")): - shippers[prefix]["delivering"].update(tracking) - shippers[prefix]["update_targets"].append((sensor, sensor_res)) - elif sensor.endswith("_packages"): - shippers[prefix]["package_targets"].append((sensor, sensor_res)) - - for data in shippers.values(): - # Remove "delivered" tracking numbers from in-transit sensors - self._apply_deduplication(data["update_targets"], data["delivered"]) - # Remove "delivering" and "delivered" tracking numbers from _packages - # so _packages only shows packages not yet out for delivery or delivered - in_pipeline = data["delivering"] | data["delivered"] - self._apply_deduplication(data["package_targets"], in_pipeline) - - def _apply_deduplication( - self, - targets: list[tuple[str, dict[str, Any]]], - delivered_ids: set[str], - ) -> None: - """Apply deduplication logic to a list of target sensors.""" - if not delivered_ids: - return - - for sensor, sensor_res in targets: - original_tracking = sensor_res.get(ATTR_TRACKING, []) - new_tracking = [ - tid for tid in original_tracking if tid not in delivered_ids - ] - - if len(new_tracking) != len(original_tracking): - sensor_res[ATTR_TRACKING] = new_tracking - sensor_res[sensor] = len(new_tracking) - if ATTR_COUNT in sensor_res: - sensor_res[ATTR_COUNT] = len(new_tracking) - - def _compute_package_totals( - self, - batch_results: list[tuple[str, dict[str, Any]]], - ) -> None: - """Compute _packages sensors with empty config as delivering + delivered. - - These sensors have no IMAP search of their own; their value is the - sum of the shipper's _delivering and _delivered counts (matching the - original pre-refactor behaviour in helpers.py). - """ - sensor_counts = { - sensor: sensor_res.get(sensor, sensor_res.get(ATTR_COUNT, 0)) - for sensor, sensor_res in batch_results - } - - for sensor, sensor_res in batch_results: - if not sensor.endswith("_packages"): - continue - config = SENSOR_DATA.get(sensor, {}) - if config.get(ATTR_EMAIL) or config.get(ATTR_SUBJECT): - continue # sensor has its own IMAP search config - prefix = sensor.replace("_packages", "") - computed = sensor_counts.get(f"{prefix}_delivering", 0) + sensor_counts.get( - f"{prefix}_delivered", 0 - ) - sensor_res[sensor] = computed - sensor_res[ATTR_COUNT] = computed - - async def _copy_generic_placeholder(self, shipper_cfg: dict[str, Any]) -> None: - """Copy the generic placeholder for the shipper.""" - shipper_name = shipper_cfg["name"] - # Try to find courier-specific placeholder - placeholder = Path(__file__).parent.parent / f"no_deliveries_{shipper_name}.jpg" - if not await anyio.Path(placeholder).exists(): - placeholder = Path(__file__).parent.parent / "mail_none.gif" - - target = ( - Path(shipper_cfg["image_path"]) / shipper_name / shipper_cfg["image_name"] - ) - _LOGGER.debug( - "No %s images found in emails, using placeholder: %s", - shipper_name, - placeholder.name, - ) - try: - await self.hass.async_add_executor_job( - copyfile, str(placeholder), str(target) - ) - except OSError as err: - _LOGGER.error("Error attempting to copy placeholder: %s", err) - - async def _search_for_emails( - self, - account: IMAP4_SSL, - email_addresses: list[str], - date: str, - subjects: list[str], - config: dict[str, Any], - shipper_cfg: dict[str, Any] | None, - sensor_type: str, - result: dict[str, Any], - cache: EmailCache | None = None, - forwarding_header: str = "", - ) -> tuple[int, list[bytes], bool]: - """Search for and process emails.""" - count = 0 - unique_email_ids = set() - found_data = [] - image_found = False - - (server_response, sdata) = await email_search( - account=account, - address=email_addresses, - date=date, - subject=subjects, - body=config.get(ATTR_BODY, ""), - header=forwarding_header, - ) - - if server_response == "OK" and sdata[0]: - raw_ids = sdata[0].split() - _LOGGER.debug( - "Found %d matching email IDs for %s: %s", - len(raw_ids), - sensor_type, - [eid.decode() if isinstance(eid, bytes) else eid for eid in raw_ids], - ) - verified_ids = await self._verify_matched_subjects( - account, raw_ids, sensor_type, subjects, cache - ) - filtered_new_ids = self._filter_unique_ids(verified_ids, unique_email_ids) - - if filtered_new_ids: - count, img_found = await self._process_matched_emails( - account, - config, - filtered_new_ids, - count, - cache, - shipper_cfg, - sensor_type, - result, - found_data, - ) - if img_found: - image_found = True - - return count, found_data, image_found - - def _decode_subject(self, header_part: bytes | bytearray) -> str | None: - """Decode MIME encoded subject from email header part.""" - msg = email.message_from_bytes(header_part) - header_val = msg.get("subject") - if not header_val: - return None - - decoded = decode_header(header_val)[0] - subject_bytes, encoding = decoded - if encoding: - try: - if isinstance(subject_bytes, bytes): - return subject_bytes.decode(encoding, "ignore").strip() - return str(subject_bytes).strip() - except (LookupError, UnicodeError): - pass - - if isinstance(subject_bytes, bytes): - return subject_bytes.decode("utf-8", "ignore").strip() - return str(subject_bytes).strip() - - async def _verify_matched_subjects( - self, - account: IMAP4_SSL, - email_ids: list[bytes], - sensor_type: str, - expected_subjects: list[str], - cache: EmailCache | None = None, - ) -> list[bytes]: - """Verify the subject of each matched email locally and log for debugging.""" - if not expected_subjects: - return email_ids - - verified_ids = [] - expected_subjects_lower = [s.lower() for s in expected_subjects] - - for eid in email_ids: - try: - if cache: - header_data = ( - await cache.fetch(eid, "(BODY[HEADER.FIELDS (SUBJECT)])") - )[1] - else: - header_data = (await email_fetch_headers(account, eid))[1] - - subject_found = False - for part in header_data: - if isinstance(part, (bytes, bytearray)): - subject = self._decode_subject(part) - if not subject: - continue - - _LOGGER.debug( - "Matched email for %s (ID %s): %s", - sensor_type, - eid.decode() if isinstance(eid, bytes) else eid, - subject, - ) - subject_lower = subject.lower() - if any( - expected in subject_lower - for expected in expected_subjects_lower - ): - subject_found = True - - if subject_found: - verified_ids.append(eid) - else: - _LOGGER.debug( - "Email ID %s rejected for %s: Subject did not match any expected subjects.", - eid.decode() if isinstance(eid, bytes) else eid, - sensor_type, - ) - except (OSError, AttributeError) as err: - _LOGGER.debug("Could not fetch subject for email %s: %s", eid, err) - - return verified_ids - - def _filter_unique_ids( - self, email_ids: list[bytes], unique_email_ids: set - ) -> list[bytes]: - """Filter out already processed email IDs.""" - new_ids = [] - for eid in email_ids: - eid_str = eid.decode() if isinstance(eid, bytes) else str(eid) - if eid_str not in unique_email_ids: - unique_email_ids.add(eid_str) - new_ids.append(eid) - return new_ids - - async def _process_matched_emails( - self, - account: IMAP4_SSL, - config: dict[str, Any], - new_ids: list[bytes], - current_count: int, - cache: EmailCache | None, - shipper_cfg: dict[str, Any] | None, - sensor_type: str, - result: dict[str, Any], - found_data: list[bytes], - ) -> tuple[int, bool]: - """Process a batch of matched unique emails.""" - image_found = False - count, matched_ids = await self._process_emails_by_type( - account, config, new_ids, current_count, cache - ) - if matched_ids: - found_data.append(b" ".join(matched_ids)) - - if shipper_cfg: - if await self._extract_images_for_shipper( - account, matched_ids, shipper_cfg, cache - ): - image_found = True - - if sensor_type.endswith("_delivered") and sensor_type != AMAZON_DELIVERED: - await self._check_amazon_mentions(account, matched_ids, result, cache) - - return count, image_found - - async def _process_tracking_numbers( - self, - sensor_type: str, - found_data: list, - account: IMAP4_SSL, - cache: EmailCache | None = None, - ) -> list: - """Process tracking numbers for the sensor.""" - tracking_key = f"{'_'.join(sensor_type.split('_')[:-1])}_tracking" - if ( - tracking_key not in SENSOR_DATA - or ATTR_PATTERN not in SENSOR_DATA[tracking_key] - ): - return [] - - pattern = SENSOR_DATA[tracking_key][ATTR_PATTERN][0] - tracking_nums = [] - for sdata in found_data: - tracking_nums.extend( - await get_tracking(sdata.decode(), account, pattern, cache) - ) - - return list(dict.fromkeys(tracking_nums)) - - async def _setup_image_extraction( - self, - sensor_type: str, - image_path: str, - ) -> dict | None: - """Set up image extraction configuration.""" - if not sensor_type.endswith("_delivered"): - return None - - shipper_name = sensor_type.replace("_delivered", "") - camera_key = f"{shipper_name}_camera" - if camera_key not in CAMERA_DATA or camera_key in ( - "usps_camera", - "generic_camera", - ): - return None - - extraction_config = CAMERA_EXTRACTION_CONFIG.get(shipper_name, {}) - absolute_image_path = image_path.rstrip("/") + "/" - - def _create_dir(): - path = Path(absolute_image_path) / shipper_name - if not path.exists(): - path.mkdir(parents=True, exist_ok=True) - - await self.hass.async_add_executor_job(_create_dir) - - return { - "name": shipper_name, - "image_path": absolute_image_path, - "image_name": self.config.get(f"{shipper_name}_image") - or f"{shipper_name}_delivery.jpg", - "image_type": extraction_config.get("image_type", "jpeg"), - "cid_name": extraction_config.get("cid_name"), - "pattern": extraction_config.get("attachment_filename_pattern"), - } - - async def _process_emails_by_type( - self, - account: IMAP4_SSL, - config: dict, - ids: list, - current_count: int, - cache: EmailCache | None = None, - ) -> tuple[int, list]: - """Process emails based on body search or just count.""" - if ATTR_BODY in config: - body_count = config.get(ATTR_BODY_COUNT, False) - mock_data = (b" ".join(ids),) - count, matched_ids = await find_text_matches( - mock_data, - account, - config[ATTR_BODY], - body_count, - cache, - ) - return current_count + count, matched_ids - return current_count + len(ids), list(ids) - - async def _extract_images_for_shipper( - self, - account: IMAP4_SSL, - ids: list, - s_config: dict, - cache: EmailCache | None = None, - ) -> bool: - """Extract delivery images from emails.""" - image_found = False - for eid in ids: - if cache: - msg_parts = (await cache.fetch(eid, "(RFC822)"))[1] - else: - msg_parts = (await email_fetch(account, eid, "(RFC822)"))[1] - for response_part in msg_parts: - if isinstance(response_part, (bytes, bytearray)): - # The extraction does blocking file I/O (the image - # write) and CPU-heavy email parsing — run the whole - # sync function off the event loop. - if await self.hass.async_add_executor_job( - generic_delivery_image_extraction, - response_part, - s_config["image_path"], - s_config["image_name"], - s_config["name"], - s_config["image_type"], - s_config["cid_name"], - s_config["pattern"], - ): - _LOGGER.debug("Extracted image for %s", s_config["name"]) - image_found = True - return image_found - - async def _check_amazon_mentions( - self, - account: IMAP4_SSL, - ids: list, - result: dict, - cache: EmailCache | None = None, - ): - """Check for Amazon mentions in emails.""" - mock_data = (b" ".join(ids),) - amazon_mentions = await find_text( - mock_data, - account, - AMAZON_DELIEVERED_BY_OTHERS_SEARCH_TEXT, - False, - cache, - ) - if amazon_mentions > 0: - result["amazon_delivered_by_others"] = ( - result.get("amazon_delivered_by_others", 0) + amazon_mentions - ) diff --git a/custom_components/mail_and_packages/shippers/post_de.py b/custom_components/mail_and_packages/shippers/post_de.py deleted file mode 100644 index 983037e..0000000 --- a/custom_components/mail_and_packages/shippers/post_de.py +++ /dev/null @@ -1,372 +0,0 @@ -"""Post DE Shipper class.""" - -from __future__ import annotations - -import email -import io -import logging -import shutil -from pathlib import Path -from typing import Any - -import anyio -from aioimaplib import IMAP4_SSL -from PIL import Image, UnidentifiedImageError - -from custom_components.mail_and_packages.const import ( - ATTR_IMAGE_PATH, - CONF_CUSTOM_IMG_FILE, - CONF_DURATION, - CONF_FORWARDING_HEADER, - CONF_GENERATE_GRID, - CONF_GENERATE_MP4, - CONF_POST_DE_CUSTOM_IMG_FILE, - DEFAULT_CUSTOM_IMG_FILE, - SENSOR_DATA, -) -from custom_components.mail_and_packages.utils.cache import EmailCache -from custom_components.mail_and_packages.utils.date import get_formatted_date -from custom_components.mail_and_packages.utils.image import ( - _generate_mp4, - cleanup_images, - generate_delivery_gif, - generate_grid_img, - random_filename, - resize_images, -) -from custom_components.mail_and_packages.utils.imap import email_fetch, email_search - -from .base import Shipper - -_LOGGER = logging.getLogger(__name__) - - -class PostDEShipper(Shipper): - """Post DE Briefankündigung shipper.""" - - @property - def name(self) -> str: - """Return shipper name.""" - return "post_de" - - @classmethod - def handles_sensor(cls, sensor_type: str) -> bool: - """Return True if this shipper handles the given sensor type.""" - return sensor_type == "post_de_mail" - - async def process( - self, - account: IMAP4_SSL, - date: str, - sensor_type: str, - cache: EmailCache | None = None, - ) -> dict[str, Any]: - """Process Post DE Briefankündigung emails.""" - if sensor_type != "post_de_mail": - return {sensor_type: 0} - - config = self._get_config() - image_count = 0 - images = [] - images_delete = [] - - (server_response, data) = await self._search_emails(account) - - # Bail out on error - if server_response != "OK" or data[0] is None: - return {sensor_type: image_count} - - # Setup image directory - post_de_dir = Path(config["image_output_path"]) / "post_de" - if not await self._setup_image_directory(str(post_de_dir)): - return {sensor_type: image_count} - - _LOGGER.debug("Post DE Briefankündigung email found processing...") - for num in data[0].split(): - (image_count, images) = await self._process_post_de_email( - account, - num, - str(post_de_dir), - image_count, - images, - cache, - ) - - image_count = len(images) - - if image_count > 0: - await self._generate_mail_image( - images, - str(post_de_dir), - config["image_name"], - config["gif_duration"], - images_delete, - ) - elif image_count == 0: - await self._copy_nomail_image( - str(post_de_dir), - config["image_name"], - config["custom_img"], - ) - - if config["gen_mp4"]: - await self._generate_mp4_video( - str(post_de_dir), - config["image_name"], - ) - if config["gen_grid"]: - await self._generate_grid_image( - str(post_de_dir), - config["image_name"], - image_count, - ) - - return { - sensor_type: image_count, - "post_de_image": config["image_name"], - ATTR_IMAGE_PATH: config["image_output_path"], - "post_de_grid_image_name": config["image_name"].replace( - ".gif", "_grid.png" - ), - } - - async def process_batch( - self, - account: IMAP4_SSL, - date: str, - sensors: list[str], - cache: EmailCache, - since_date: str | None = None, - ) -> dict[str, Any]: - """Process multiple Post DE sensors in batch.""" - res = {} - for sensor in sensors: - res.update(await self.process(account, date, sensor, cache)) - - # Replicate coordinator dict structure - if sensor not in res: - res[sensor] = res.get(sensor, 0) - - return res - - async def _generate_mp4_video(self, path: str, name: str): - """Generate MP4 video from images.""" - await self.hass.async_add_executor_job(_generate_mp4, path + "/", name) - - async def _generate_grid_image(self, path: str, name: str, count: int): - """Generate grid image from images.""" - await self.hass.async_add_executor_job( - generate_grid_img, path + "/", name, count - ) - - async def _generate_mail_image( - self, - images: list, - path: str, - name: str, - duration: int, - delete_list: list, - ): - """Generate animated GIF from mail images.""" - try: - _LOGGER.debug("Resizing Post DE images to 724x320...") - all_images = await self.hass.async_add_executor_job( - resize_images, - images, - 724, - 320, - ) - delete_list.extend(all_images) - - _LOGGER.debug("Generating animated GIF for Post DE") - gif_path = str(Path(path) / name) - await self.hass.async_add_executor_job( - generate_delivery_gif, - all_images, - gif_path, - duration * 1000, - ) - _LOGGER.debug("Post DE mail image generated.") - except (OSError, ValueError) as err: - _LOGGER.error("Error attempting to generate Post DE image: %s", err) - - for image in delete_list: - await self.hass.async_add_executor_job( - cleanup_images, - f"{Path(image).parent}/", - Path(image).name, - ) - - async def _copy_nomail_image(self, path: str, name: str, custom_img: str | None): - """Copy the 'no mail' placeholder image.""" - - def _prepare(): - if not Path(path).exists(): - Path(path).mkdir(parents=True, exist_ok=True) - target = Path(path) / name - if target.is_file(): - cleanup_images(path + "/", name) - src = custom_img or str(Path(__file__).parent.parent / "mail_none.gif") - if not Path(src).is_absolute(): - src = self.hass.config.path(src) - shutil.copyfile(src, str(target)) - - _LOGGER.debug("No Post DE mail found.") - try: - await self.hass.async_add_executor_job(_prepare) - except OSError as err: - _LOGGER.error("Error attempting to copy Post DE image: %s", err) - - def _get_config(self) -> dict: - """Get Post DE specific configuration.""" - - image_path = self.config.get("image_path") - return { - "image_output_path": image_path, - "gif_duration": self.config.get(CONF_DURATION), - "image_name": self.config.get("post_de_image") or "post_de_deliveries.gif", - "gen_mp4": self.config.get(CONF_GENERATE_MP4), - "custom_img": self.config.get(CONF_POST_DE_CUSTOM_IMG_FILE) - or self.config.get(CONF_CUSTOM_IMG_FILE) - or DEFAULT_CUSTOM_IMG_FILE, - "gen_grid": self.config.get(CONF_GENERATE_GRID), - } - - async def _search_emails(self, account: IMAP4_SSL) -> tuple: - """Search for Post DE Briefankündigung emails.""" - _LOGGER.debug("Attempting to find Post DE Briefankündigung mail") - _LOGGER.debug("Post DE search date: %s", get_formatted_date()) - - config = SENSOR_DATA["post_de_mail"] - email_addresses = config.get("email", []) - subjects = config.get("subject", []) - - forwarding_header = self.config.get(CONF_FORWARDING_HEADER, "") - if forwarding_header and forwarding_header != "(none)": - pass - else: - forwarding_header = "" - forwarded_emails = self.config.get("forwarded_emails", []) - if isinstance(forwarded_emails, str): - forwarded_emails = [ - e.strip() for e in forwarded_emails.split(",") if e.strip() - ] - if forwarded_emails: - email_addresses = forwarded_emails + email_addresses - - return await email_search( - account=account, - address=email_addresses, - date=get_formatted_date(), - subject=subjects, - header=forwarding_header, - ) - - async def _setup_image_directory(self, path: str) -> bool: - """Ensure image directory exists and is prepared.""" - if not await anyio.Path(path).is_dir(): - try: - await anyio.Path(path).mkdir(parents=True, exist_ok=True) - except OSError as err: - _LOGGER.error("Error creating directory: %s", err) - return False - - # Clean up - await self.hass.async_add_executor_job(cleanup_images, path + "/") - return True - - async def _process_post_de_email( # noqa: C901 - self, - account: IMAP4_SSL, - num: str, - image_output_path: str, - image_count: int, - images: list, - cache: EmailCache | None = None, - ) -> tuple[int, list]: - """Process a single Post DE email and extract envelope scans. - - Expected email payload format is HTML containing inline tags - referencing base64-encoded PNG/JPEG images. - """ - if cache: - msg_parts = (await cache.fetch(num, "(RFC822)"))[1] - else: - msg_parts = (await email_fetch(account, num, "(RFC822)"))[1] - _LOGGER.debug("Processing Post DE email number: %s", num) - for response_part in msg_parts: - if isinstance(response_part, (bytes, bytearray)): - msg = email.message_from_bytes(response_part) - for part in msg.walk(): - if part.get_content_type() in ("image/png", "image/jpeg"): - payload = part.get_payload(decode=True) - if not payload: - continue - - # Check image dimensions to skip logos/icons - def _check_and_save( - img_bytes: bytes, - out_path: str, - content_type: str, - ) -> str | None: - try: - img = Image.open(io.BytesIO(img_bytes)) - if img.format is None: - _LOGGER.debug( - "Post DE image format is unidentified (None)" - ) - return None - - # Validate format against expected content type - if content_type == "image/png" and img.format != "PNG": - _LOGGER.debug( - "Post DE image format mismatch: expected PNG, got %s", - img.format, - ) - return None - if ( - content_type == "image/jpeg" - and img.format != "JPEG" - ): - _LOGGER.debug( - "Post DE image format mismatch: expected JPEG, got %s", - img.format, - ) - return None - - width, height = img.size - if width > 150 and height > 100: - ext = ( - ".png" - if content_type == "image/png" - else ".jpg" - ) - filename = random_filename(ext=ext) - target = Path(out_path) / filename - with target.open("wb") as f: - f.write(img_bytes) - return str(target) - except UnidentifiedImageError as err: - _LOGGER.warning( - "Unidentified image found in Post DE email: %s", err - ) - except (OSError, ValueError, TypeError) as err: - _LOGGER.debug( - "Error checking/saving Post DE image: %s", err - ) - return None - - saved_path = await self.hass.async_add_executor_job( - _check_and_save, - payload, - image_output_path, - part.get_content_type(), - ) - if saved_path: - images.append(saved_path) - image_count += 1 - _LOGGER.debug( - "Extracted Post DE mail image: %s", saved_path - ) - - return image_count, images diff --git a/custom_components/mail_and_packages/shippers/usps.py b/custom_components/mail_and_packages/shippers/usps.py deleted file mode 100644 index bcd9b0d..0000000 --- a/custom_components/mail_and_packages/shippers/usps.py +++ /dev/null @@ -1,448 +0,0 @@ -"""USPS Shipper class.""" - -from __future__ import annotations - -import base64 -import email -import logging -import re -import shutil -from pathlib import Path -from typing import Any - -import anyio -from aioimaplib import IMAP4_SSL -from bs4 import BeautifulSoup - -from custom_components.mail_and_packages.const import ( - ATTR_COUNT, - ATTR_EMAIL, - ATTR_GRID_IMAGE_NAME, - ATTR_IMAGE_PATH, - ATTR_SUBJECT, - ATTR_USPS_IMAGE, - ATTR_USPS_MAIL, - CONF_CUSTOM_IMG_FILE, - CONF_DURATION, - CONF_FORWARDING_HEADER, - CONF_GENERATE_GRID, - CONF_GENERATE_MP4, - CONF_USPS_PLACEHOLDER, - DEFAULT_CUSTOM_IMG_FILE, - SENSOR_DATA, -) -from custom_components.mail_and_packages.utils.cache import EmailCache -from custom_components.mail_and_packages.utils.date import get_formatted_date -from custom_components.mail_and_packages.utils.image import ( - _generate_mp4, - cleanup_images, - copy_overlays, - generate_delivery_gif, - generate_grid_img, - io_save_file, - random_filename, - resize_images, -) -from custom_components.mail_and_packages.utils.imap import email_fetch, email_search - -from .base import Shipper - -_LOGGER = logging.getLogger(__name__) - - -class USPSShipper(Shipper): - """USPS Informed Delivery shipper.""" - - @property - def name(self) -> str: - """Return shipper name.""" - return "usps" - - @classmethod - def handles_sensor(cls, sensor_type: str) -> bool: - """Return True if this shipper handles the given sensor type.""" - return sensor_type == "usps_mail" - - async def process( - self, - account: IMAP4_SSL, - date: str, - sensor_type: str, - cache: EmailCache | None = None, - ) -> dict[str, Any]: - """Process USPS Informed Delivery emails.""" - config = self._get_usps_config() - image_count = 0 - images = [] - images_delete = [] - - (server_response, data) = await self._search_informed_delivery(account) - - # Bail out on error - if server_response != "OK" or data[0] is None: - return {ATTR_COUNT: image_count} - - # Setup image directory and overlays - if not await self._setup_image_directory(config["image_output_path"]): - return {ATTR_COUNT: image_count} - - all_msg_content = "" - if server_response == "OK": - _LOGGER.debug("Informed Delivery email found processing...") - for num in data[0].split(): - (image_count, images, email_content) = await self._process_usps_email( - account, - num, - config["image_output_path"], - image_count, - images, - cache, - ) - all_msg_content += email_content - - # Process images - images = await self._process_usps_images(all_msg_content, images) - image_count = len(images) - - # Generate filtered list for GIF/MP4/Grid - gif_images = images.copy() - if not config.get("usps_placeholder", True): - placeholder_str = str( - Path(__file__).parent.parent / "image-no-mailpieces700.jpg" - ) - if placeholder_str in gif_images: - gif_images.remove(placeholder_str) - - # Generate camera media - await self._create_camera_media(gif_images, config, images_delete) - - return { - ATTR_COUNT: image_count, - ATTR_USPS_IMAGE: config["image_name"], - ATTR_IMAGE_PATH: config["image_output_path"], - ATTR_GRID_IMAGE_NAME: config["image_name"].replace(".gif", "_grid.png"), - } - - async def process_batch( - self, - account: IMAP4_SSL, - date: str, - sensors: list[str], - cache: EmailCache, - since_date: str | None = None, - ) -> dict[str, Any]: - """Process multiple USPS sensors in batch.""" - res = {} - for sensor in sensors: - res.update(await self.process(account, date, sensor, cache)) - - # Replicatecoordinator dict structure - if sensor not in res: - if ATTR_COUNT in res: - res[sensor] = res[ATTR_COUNT] - # Don't pop ATTR_COUNT because other things might need it, actually - # coordinator used to pop it via explicit assignment - - return res - - async def _create_camera_media( - self, - gif_images: list, - config: dict, - images_delete: list, - ): - """Create camera media (GIF, MP4, and Grid).""" - if len(gif_images) > 0: - await self._generate_mail_image( - gif_images, - config["image_output_path"], - config["image_name"], - config["gif_duration"], - images_delete, - ) - else: - await self._copy_nomail_image( - config["image_output_path"], - config["image_name"], - config["custom_img"], - ) - - if config["gen_mp4"]: - await self._generate_mp4_video( - config["image_output_path"], - config["image_name"], - ) - if config["gen_grid"]: - await self._generate_grid_image( - config["image_output_path"], - config["image_name"], - len(gif_images), - ) - - async def _generate_mp4_video(self, path: str, name: str): - """Generate MP4 video from images.""" - await self.hass.async_add_executor_job(_generate_mp4, path, name) - - async def _generate_grid_image(self, path: str, name: str, count: int): - """Generate grid image from images.""" - await self.hass.async_add_executor_job(generate_grid_img, path, name, count) - - async def _process_usps_images(self, content: str, images: list) -> list: - """Process USPS images (placeholder and filtering).""" - # Old USPS format: plain-text email body contained the filename as a reference. - # New format is handled in _extract_usps_images on properly decoded HTML. - if re.compile(r"\bimage-no-mailpieces?700\.jpg\b").search(content) is not None: - placeholder = Path(__file__).parent.parent / "image-no-mailpieces700.jpg" - placeholder_str = str(placeholder) - if placeholder.exists() and placeholder_str not in images: - images.append(placeholder_str) - _LOGGER.debug( - "Placeholder image found using: image-no-mailpieces700.jpg.", - ) - - # Announcement images removal - return self._remove_announcement_images(images) - - def _remove_announcement_images(self, images: list) -> list: - """Remove announcement images.""" - return [ - el - for el in images - if not any( - ignore in el - for ignore in ["mailerProvidedImage", "ra_0", "Mail Attachment.txt"] - ) - ] - - async def _generate_mail_image( - self, - images: list, - path: str, - name: str, - duration: int, - delete_list: list, - ): - """Generate animated GIF from mail images.""" - _LOGGER.debug("Resizing images to 724x320...") - all_images = await self.hass.async_add_executor_job( - resize_images, - images, - 724, - 320, - ) - delete_list.extend(all_images) - - try: - _LOGGER.debug("Generating animated GIF") - gif_path = str(Path(path) / name) - await self.hass.async_add_executor_job( - generate_delivery_gif, - all_images, - gif_path, - duration * 1000, - ) - _LOGGER.debug("Mail image generated.") - except (OSError, ValueError) as err: - _LOGGER.error("Error attempting to generate image: %s", err) - - for image in delete_list: - await self.hass.async_add_executor_job( - cleanup_images, - f"{Path(image).parent}/", - Path(image).name, - ) - - async def _copy_nomail_image(self, path: str, name: str, custom_img: str | None): - """Copy the 'no mail' placeholder image.""" - - def _prepare(): - if not Path(path).exists(): - Path(path).mkdir(parents=True, exist_ok=True) - target = Path(path) / name - if target.is_file(): - cleanup_images(path, name) - src = custom_img or str(Path(__file__).parent.parent / "mail_none.gif") - if not Path(src).is_absolute(): - src = self.hass.config.path(src) - shutil.copyfile(src, str(target)) - - _LOGGER.debug("No mail found.") - try: - await self.hass.async_add_executor_job(_prepare) - except OSError as err: - _LOGGER.error("Error attempting to copy image: %s", err) - - def _get_usps_config(self) -> dict: - """Get USPS specific configuration.""" - return { - "image_output_path": self.config.get("image_path"), - "gif_duration": self.config.get(CONF_DURATION), - "image_name": self.config.get("usps_image"), - "gen_mp4": self.config.get(CONF_GENERATE_MP4), - "custom_img": self.config.get(CONF_CUSTOM_IMG_FILE) - or DEFAULT_CUSTOM_IMG_FILE, - "gen_grid": self.config.get(CONF_GENERATE_GRID), - "usps_placeholder": self.config.get(CONF_USPS_PLACEHOLDER, True), - } - - async def _search_informed_delivery(self, account: IMAP4_SSL) -> tuple: - """Search for USPS Informed Delivery emails.""" - _LOGGER.debug("Attempting to find Informed Delivery mail") - _LOGGER.debug("Informed delivery search date: %s", get_formatted_date()) - - forwarding_header = self.config.get(CONF_FORWARDING_HEADER, "") - if forwarding_header and forwarding_header != "(none)": - email_addresses = SENSOR_DATA[ATTR_USPS_MAIL][ATTR_EMAIL] - else: - forwarding_header = "" - forwarded_emails = self.config.get("forwarded_emails", []) - if isinstance(forwarded_emails, str): - forwarded_emails = [ - e.strip() for e in forwarded_emails.split(",") if e.strip() - ] - if forwarded_emails: - email_addresses = ( - forwarded_emails + SENSOR_DATA[ATTR_USPS_MAIL][ATTR_EMAIL] - ) - else: - email_addresses = SENSOR_DATA[ATTR_USPS_MAIL][ATTR_EMAIL] - - return await email_search( - account=account, - address=email_addresses, - date=get_formatted_date(), - subject=SENSOR_DATA[ATTR_USPS_MAIL][ATTR_SUBJECT][0], - header=forwarding_header, - ) - - async def _setup_image_directory(self, path: str) -> bool: - """Ensure image directory exists and is prepared.""" - if not await anyio.Path(path).is_dir(): - try: - await anyio.Path(path).mkdir(parents=True, exist_ok=True) - except OSError as err: - _LOGGER.error("Error creating directory: %s", err) - return False - - # Clean up and setup overlays - await self.hass.async_add_executor_job(cleanup_images, path) - await self.hass.async_add_executor_job(copy_overlays, path) - return True - - async def _process_usps_email( - self, - account: IMAP4_SSL, - num: str, - image_output_path: str, - image_count: int, - images: list, - cache: EmailCache | None = None, - ) -> tuple[int, list, str]: - """Process a single USPS Informed Delivery email.""" - if cache: - msg_parts = (await cache.fetch(num, "(RFC822)"))[1] - else: - msg_parts = (await email_fetch(account, num, "(RFC822)"))[1] - _LOGGER.debug("Processing email number: %s", num) - all_content = "" - for response_part in msg_parts: - if isinstance(response_part, (bytes, bytearray)): - all_content += str(response_part, "utf-8", errors="ignore") - msg = email.message_from_bytes(response_part) - for part in msg.walk(): - if part.get_content_type() == "text/html": - (image_count, images) = await self._extract_usps_images( - part, - image_output_path, - image_count, - images, - ) - elif part.get_content_type() == "image/jpeg": - (image_count, images) = await self._extract_jpeg_attachment( - part, - image_output_path, - image_count, - images, - ) - return image_count, images, all_content - - async def _extract_usps_images( - self, - part: email.message.Message, - image_output_path: str, - image_count: int, - images: list, - ) -> tuple[int, list]: - """Extract images from an email part (HTML/Base64).""" - payload = part.get_payload(decode=True) - content = ( - payload.decode("utf-8", "ignore") - if isinstance(payload, (bytes, bytearray)) - else str(payload) - ) - - # New USPS format: unscanned mailpieces use a div with a specific id. - # Check here on properly decoded HTML — raw RFC822 content is - # quoted-printable encoded and soft line breaks could split the string. - if "mailpiece-with-no-image-id" in content: - placeholder = Path(__file__).parent.parent / "image-no-mailpieces700.jpg" - placeholder_str = str(placeholder) - if placeholder.exists() and placeholder_str not in images: - images.append(placeholder_str) - image_count += 1 - _LOGGER.debug( - "Placeholder image found using: image-no-mailpieces700.jpg.", - ) - - if "data:image/jpeg;base64" not in content: - return image_count, images - - soup = BeautifulSoup(content, "html.parser") - found_images = soup.find_all(id="mailpiece-image-src-id") - - for image in found_images: - filename = random_filename() - img_data = str(image["src"]).split(",")[1] - try: - target_path = Path(image_output_path) / filename - await self.hass.async_add_executor_job( - io_save_file, - target_path, - base64.b64decode(img_data), - ) - images.append(str(target_path)) - image_count += 1 - except (OSError, ValueError, TypeError) as err: - _LOGGER.error("Error extracting image: %s", err) - - return image_count, images - - async def _extract_jpeg_attachment( - self, - part: email.message.Message, - image_output_path: str, - image_count: int, - images: list, - ) -> tuple[int, list]: - """Extract image from JPEG attachment.""" - _LOGGER.debug("Extracting image from email attachment") - filename = part.get_filename() - junkmail = ["mailer", "content", "package"] - if filename is None: - return image_count, images - if any(junk in filename for junk in junkmail): - return image_count, images - - try: - target_path = Path(image_output_path) / filename - await self.hass.async_add_executor_job( - io_save_file, - target_path, - part.get_payload(decode=True), - ) - images.append(str(target_path)) - image_count += 1 - except OSError as err: - _LOGGER.critical("Error opening filepath: %s", err) - - return image_count, images diff --git a/custom_components/mail_and_packages/strings.json b/custom_components/mail_and_packages/strings.json deleted file mode 100644 index 7cf9de2..0000000 --- a/custom_components/mail_and_packages/strings.json +++ /dev/null @@ -1,281 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Only a single configuration of Mail and Packages is allowed.", - "reconfigure_successful": "Reconfigure Successful", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Unable to connect or login to the mail server. Please check the log for details.", - "invalid_path": "Please store the images in another directory.", - "ffmpeg_not_found": "Generate MP4 requires ffmpeg", - "amazon_domain": "Invalid forwarding email address.", - "file_not_found": "Image file not found", - "invalid_email_format": "Invalid email address format.", - "missing_forwarded_emails": "Missing forwarded email addresses. You may enter '(none)' to clear this setting.", - "path_not_found": "Directory not found", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "imap_config": { - "description": "Please enter the connection information of your mail server.", - "data": { - "host": "Host", - "password": "Password", - "port": "Port", - "username": "Username", - "verify_ssl": "Verify SSL Cert", - "imap_security": "IMAP Security" - }, - "title": "Mail and Packages (Step 1 of 2)" - }, - "config_2": { - "data": { - "folder": "Mail Folder", - "resources": "Sensors List", - "scan_interval": "Scanning Interval (minutes, minimum 5)", - "image_path": "Image Path", - "gif_duration": "Image Duration (seconds)", - "image_security": "Random Image Filename", - "imap_timeout": "Mailbox scan time limit (seconds, minimum 10)", - "generate_grid": "Create image grid for LLM vision models", - "generate_mp4": "Create mp4 from images", - "allow_external": "Create image for notification apps", - "usps_placeholder": "Include USPS no-image placeholder in GIF?", - "custom_img": "Use custom USPS 'no mail' image?", - "amazon_custom_img": "Use custom 'no Amazon delivery' image?", - "ups_custom_img": "Use custom 'no UPS delivery' image?", - "walmart_custom_img": "Use custom 'no Walmart delivery' image?", - "fedex_custom_img": "Use custom 'no FedEx delivery' image?", - "post_de_custom_img": "Use custom Post DE 'no mail' image?", - "allow_forwarded_emails": "Allow forwarded emails in addition to a service's default (e.g. no-reply@usps.com)" - }, - "data_description": { - "imap_timeout": "Covers the entire scan — login plus every per-carrier IMAP search — not just connecting. Raise this if large mailboxes time out; lower it to fail faster." - }, - "description": "Finish the configuration by customizing the following based on your email structure and Home Assistant installation.\n\nIf using Amazon forwarded emails please separate each address with a comma.", - "title": "Mail and Packages (Step 2 of 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Path to custom USPS image: (ie: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Path to custom Post DE image: (ie: images/my_custom_no_post_de.gif)" - }, - "description": "Enter the path and file name to your custom no delivery images below.\n\nExample: images/custom_nomail.gif", - "title": "Mail and Packages (Step 3 of 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - }, - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.", - "title": "Amazon Settings" - }, - "config_forwarded_emails": { - "data": { - "forwarded_emails": "Forwarded Email Addresses" - }, - "description": "By default, the Mail and Packages integration only searches for messages matching a service's address.\n\nIf you want to use forwarded emails, please separate each address messages will be coming from with a comma or enter '(none)' to clear this setting.", - "title": "Forwarded Email Addresses" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Host", - "password": "Password", - "port": "Port", - "username": "Username", - "imap_security": "IMAP Security", - "verify_ssl": "Verify SSL Cert" - }, - "description": "Please enter the connection information of your mail server.", - "title": "Mail and Packages (Step 1 of 2)" - }, - "reconfig_2": { - "data": { - "folder": "Mail Folder", - "scan_interval": "Scanning Interval (minutes, minimum 5)", - "image_path": "Image Path", - "gif_duration": "Image Duration (seconds)", - "image_security": "Random Image Filename", - "generate_grid": "Create image grid for LLM vision models", - "generate_mp4": "Create mp4 from images", - "resources": "Sensors List", - "imap_timeout": "Mailbox scan time limit (seconds, minimum 10)", - "allow_external": "Create image for notification apps", - "usps_placeholder": "Include USPS no-image placeholder in GIF?", - "custom_img": "Use custom USPS 'no mail' image?", - "amazon_custom_img": "Use custom 'no Amazon delivery' image?", - "ups_custom_img": "Use custom 'no UPS delivery' image?", - "walmart_custom_img": "Use custom 'no Walmart delivery' image?", - "fedex_custom_img": "Use custom 'no FedEx delivery' image?", - "post_de_custom_img": "Use custom Post DE 'no mail' image?", - "allow_forwarded_emails": "Allow forwarded emails in addition to a service's default (e.g. no-reply@usps.com)" - }, - "data_description": { - "imap_timeout": "Covers the entire scan — login plus every per-carrier IMAP search — not just connecting. Raise this if large mailboxes time out; lower it to fail faster." - }, - "description": "Finish the configuration by customizing the following based on your email structure and Home Assistant installation.\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.", - "title": "Mail and Packages (Step 2 of 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Path to custom USPS image: (i.e.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Path to custom Post DE image: (i.e.: images/my_custom_no_post_de.gif)" - }, - "description": "Enter the path and file name to your custom no delivery images below.\n\nExample: images/custom_nomail.gif", - "title": "Mail and Packages (Step 3 of 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - }, - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.", - "title": "Amazon Settings" - }, - "config_storage": { - "data": { - "storage": "Directory to store images" - }, - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "title": "Image storage location" - }, - "reconfig_storage": { - "data": { - "storage": "Directory to store images" - }, - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "title": "Image storage location" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarded_emails": "Forwarded Email Addresses" - }, - "description": "By default, the Mail and Packages integration only searches for messages matching a service's address.\n\nIf you want to use forwarded emails, please separate each address messages will be coming from with a comma or enter '(none)' to clear this setting.", - "title": "Forwarded Email Addresses" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "options": { - "step": { - "init": { - "description": "Finish the configuration by customizing the following based on your email structure and Home Assistant installation.\n\nIf using Amazon forwarded emails please separate each address with a comma.", - "data": { - "folder": "Mail Folder", - "resources": "Sensors List", - "scan_interval": "Scanning Interval (minutes, minimum 5)", - "image_path": "Image Path", - "gif_duration": "Image Duration (seconds)", - "image_security": "Random Image Filename", - "imap_timeout": "Mailbox scan time limit (seconds, minimum 10)", - "generate_grid": "Create image grid for LLM vision models", - "generate_mp4": "Create mp4 from images", - "allow_external": "Create image for notification apps", - "usps_placeholder": "Include USPS no-image placeholder in GIF?", - "custom_img": "Use custom USPS 'no mail' image?", - "amazon_custom_img": "Use custom 'no Amazon delivery' image?", - "ups_custom_img": "Use custom 'no UPS delivery' image?", - "walmart_custom_img": "Use custom 'no Walmart delivery' image?", - "fedex_custom_img": "Use custom 'no FedEx delivery' image?", - "post_de_custom_img": "Use custom Post DE 'no mail' image?", - "allow_forwarded_emails": "Allow forwarded emails in addition to a service's default (e.g. no-reply@usps.com)" - }, - "data_description": { - "imap_timeout": "Covers the entire scan — login plus every per-carrier IMAP search — not just connecting. Raise this if large mailboxes time out; lower it to fail faster." - }, - "title": "Mail and Packages Options" - }, - "options_3": { - "data": { - "custom_img_file": "Path to custom USPS image: (ie: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Path to custom Post DE image: (ie: images/my_custom_no_post_de.gif)" - }, - "description": "Enter the path and file name to your custom no delivery images below.\n\nExample: images/custom_nomail.gif", - "title": "Custom Images" - }, - "options_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - }, - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.", - "title": "Amazon Settings" - }, - "options_storage": { - "data": { - "storage": "Directory to store images" - }, - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "title": "Image storage location" - }, - "options_forwarded_emails": { - "data": { - "forwarded_emails": "Forwarded Email Addresses" - }, - "description": "By default, the Mail and Packages integration only searches for messages matching a service's address.\n\nIf you want to use forwarded emails, please separate each address messages will be coming from with a comma or enter '(none)' to clear this setting.", - "title": "Forwarded Email Addresses" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages authentication failed", - "fix_flow": { - "step": { - "confirm": { - "title": "Re-authenticate Mail Server", - "description": "Authentication to your IMAP mail server has failed. Click submit to start the re-authentication flow." - } - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/ca.json b/custom_components/mail_and_packages/translations/ca.json deleted file mode 100644 index b5c5981..0000000 --- a/custom_components/mail_and_packages/translations/ca.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Només es permet una única configuració de correu i paquets.", - "reconfigure_successful": "Reconfiguració exitosa", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "No es pot connectar o iniciar sessió al servidor de correu. Consulteu el registre per obtenir més detalls.", - "invalid_path": "Deseu les imatges en un altre directori.", - "ffmpeg_not_found": "Generar MP4 requereix ffmpeg", - "amazon_domain": "Adreça de correu electrònic de reenviament no vàlida.", - "file_not_found": "No s'ha trobat el fitxer d'imatge", - "invalid_email_format": "Format d'adreça de correu electrònic no vàlid.", - "path_not_found": "Directori no trobat", - "missing_forwarded_emails": "Falten les adreces de correu electrònic reenviades. Podeu introduir \"(none)\" per esborrar aquesta configuració.", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Carpeta de correu", - "resources": "Llista de sensors", - "scan_interval": "Interval d'exploració (minuts)", - "image_path": "Camí de la imatge", - "gif_duration": "Durada de la imatge (segons)", - "image_security": "Nom del fitxer d'imatge aleatòria", - "imap_timeout": "Temps en segons abans de l'expiració de la connexió (segons, mínim 10)", - "generate_mp4": "Crea mp4 a partir d'imatges", - "allow_external": "Crea una imatge per a aplicacions de notificacions", - "custom_img": "Utilitza la imatge personalitzada USPS 'no hi ha correu'?", - "amazon_custom_img": "Utilitza la imatge personalitzada Amazon 'no hi ha lliurament'?", - "ups_custom_img": "Utilitza la imatge personalitzada UPS 'no hi ha lliurament'?", - "walmart_custom_img": "Utilitza la imatge personalitzada Walmart 'no hi ha lliurament'?", - "fedex_custom_img": "Utilitza la imatge personalitzada FedEx 'no hi ha lliurament'?", - "generic_custom_img": "Utilitza la imatge personalitzada genèrica 'no hi ha lliurament'?", - "generate_grid": "Crea una quadrícula d'imatges per a models de visió LLM", - "allow_forwarded_emails": "Permet els correus electrònics reenviats a més del valor predeterminat d'un servei (p. ex., no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Incloure la imatge de substitució de l'USPS sense imatge al GIF?" - }, - "description": "Finalitzeu la configuració personalitzant la següent en funció de la vostra instal·lació de correu electrònic i la instal·lació d'assistència a casa. \n\n Per obtenir més informació sobre les opcions [Integració de paquets i correus] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), reviseu les opcions [configuració, plantilles , secció i automatitzacions] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) a GitHub.", - "title": "Correu i paquets (pas 2 de 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Camí a la imatge personalitzada: (és a dir: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Introduïu el camí i el nom del fitxer de la vostra imatge personalitzada de no correu a continuació.\n\nExemple: imatges/custom_nomail.gif", - "title": "Correu i paquets (Pas 3 de 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Domini d'Amazon", - "amazon_fwds": "Amazon ha reenviat les adreces de correu electrònic", - "amazon_days": "Dies per comprovar els correus electrònics d'Amazon" - }, - "description": "Introduïu el domini des d'on Amazon envia correus electrònics (és a dir: amazon.com o amazon.de)\n\nSi utilitzeu correus electrònics reenviats d'Amazon, separeu cada adreça amb una coma o introduïu (none) per esborrar aquesta configuració.\n\nNota: si heu configurat una capçalera de reenviament en el pas anterior, el camp d'adreça de reenviament no apareixerà — la capçalera gestiona la correspondència de correus electrònics d'Amazon automàticament.", - "title": "Configuració d'Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Carpeta de correu", - "scan_interval": "Interval d'exploració (minuts)", - "image_path": "Camí de la imatge", - "gif_duration": "Durada de la imatge (segons)", - "image_security": "Nom del fitxer d'imatge aleatòria", - "generate_mp4": "Crea mp4 a partir d'imatges", - "resources": "Llista de sensors", - "imap_timeout": "Temps en segons abans de l'expiració de la connexió (segons, mínim 10)", - "allow_external": "Crea una imatge per a aplicacions de notificacions", - "custom_img": "Utilitza la imatge personalitzada USPS 'no hi ha correu'?", - "amazon_custom_img": "Utilitza la imatge personalitzada Amazon 'no hi ha lliurament'?", - "ups_custom_img": "Utilitza la imatge personalitzada UPS 'no hi ha lliurament'?", - "walmart_custom_img": "Utilitza la imatge personalitzada Walmart 'no hi ha lliurament'?", - "fedex_custom_img": "Utilitza la imatge personalitzada FedEx 'no hi ha lliurament'?", - "generic_custom_img": "Utilitza la imatge personalitzada genèrica 'no hi ha lliurament'?", - "generate_grid": "Crea una quadrícula d'imatges per a models de visió LLM", - "allow_forwarded_emails": "Permet els correus electrònics reenviats a més del valor predeterminat d'un servei (p. ex., no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Incloure la imatge de substitució de l'USPS sense imatge al GIF?" - }, - "description": "Acabeu la configuració personalitzant el següent en funció de l'estructura del vostre correu electrònic i de la instal·lació de Home Assistant.\n\nPer obtenir més detalls sobre les opcions d'[integració de correu i paquets](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), consulteu la [secció de configuració, plantilles i automatitzacions](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) a GitHub.\n\nSi utilitzeu correus electrònics reenviats d'Amazon, separeu cada adreça amb una coma o introduïu (none) per esborrar aquesta configuració.", - "title": "Correu i paquets (pas 2 de 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Camí a la imatge personalitzada: (p. ex.: imatges/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Introduïu el camí i el nom del fitxer de la vostra imatge personalitzada de no correu a continuació.\n\nExemple: imatges/custom_nomail.gif", - "title": "Correu i paquets (Pas 3 de 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Domini d'Amazon", - "amazon_fwds": "Amazon ha reenviat les adreces de correu electrònic", - "amazon_days": "Dies per comprovar els correus electrònics d'Amazon" - }, - "description": "Introduïu el domini des d'on Amazon envia correus electrònics (és a dir: amazon.com o amazon.de)\n\nSi utilitzeu correus electrònics reenviats d'Amazon, separeu cada adreça amb una coma o introduïu (none) per esborrar aquesta configuració.\n\nNota: si heu configurat una capçalera de reenviament en el pas anterior, el camp d'adreça de reenviament no apareixerà — la capçalera gestiona la correspondència de correus electrònics d'Amazon automàticament.", - "title": "Configuració d'Amazon" - }, - "config_storage": { - "data": { - "storage": "Directori per emmagatzemar imatges" - }, - "description": "Introduïu el directori on voleu que es desin les vostres imatges.\nPer defecte, es completa automàticament.", - "title": "Ubicació de l'emmagatzematge d'imatges" - }, - "reconfig_storage": { - "data": { - "storage": "Directori per emmagatzemar imatges" - }, - "description": "Introduïu el directori on voleu que es desin les vostres imatges.\nPer defecte, es completa automàticament.", - "title": "Ubicació de l'emmagatzematge d'imatges" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Capçalera de reenviament (p. ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Adreces de correu electrònic reenviades" - }, - "data_description": { - "forwarding_header": "Si el vostre servei de reenviament inclou el remitent original en una capçalera, introduïu aquí el nom de la capçalera. Això té prioritat sobre la llista d'adreces a continuació. Introduïu '(none)' o deixeu-ho en blanc per utilitzar la llista d'adreces.", - "forwarded_emails": "Llista d'adreces de reenviament separades per comes des de les quals arribaran els missatges. Deixeu '(none)' si feu servir la capçalera de reenviament de dalt." - }, - "description": "Trieu com fer coincidir els correus electrònics reenviats.\n\n**Opció 1 — Capçalera de reenviament**: Si el vostre servei (p. ex. SimpleLogin) inclou el remitent original en una capçalera personalitzada, introduïu el nom d'aquesta capçalera. Mail and Packages l'utilitzarà per fer coincidir automàticament les adreces dels transportistes.\n\n**Opció 2 — Llista d'adreces**: Introduïu les adreces de reenviament que el vostre servei ha assignat a cada transportista, separades per comes.", - "title": "Configuració dels correus electrònics reenviats" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Capçalera de reenviament (p. ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Adreces de correu electrònic reenviades" - }, - "data_description": { - "forwarding_header": "Si el vostre servei de reenviament inclou el remitent original en una capçalera, introduïu aquí el nom de la capçalera. Això té prioritat sobre la llista d'adreces a continuació. Introduïu '(none)' o deixeu-ho en blanc per utilitzar la llista d'adreces.", - "forwarded_emails": "Llista d'adreces de reenviament separades per comes des de les quals arribaran els missatges. Deixeu '(none)' si feu servir la capçalera de reenviament de dalt." - }, - "description": "Trieu com fer coincidir els correus electrònics reenviats.\n\n**Opció 1 — Capçalera de reenviament**: Si el vostre servei (p. ex. SimpleLogin) inclou el remitent original en una capçalera personalitzada, introduïu el nom d'aquesta capçalera. Mail and Packages l'utilitzarà per fer coincidir automàticament les adreces dels transportistes.\n\n**Opció 2 — Llista d'adreces**: Introduïu les adreces de reenviament que el vostre servei ha assignat a cada transportista, separades per comes.", - "title": "Configuració dels correus electrònics reenviats" - }, - "imap_config": { - "description": "Introduïu la informació de connexió del vostre servidor de correu.", - "data": { - "host": "Amfitrió", - "password": "Contrasenya", - "port": "Port", - "username": "Nom d'usuari", - "verify_ssl": "Verifica el certificat SSL", - "imap_security": "Seguretat IMAP" - }, - "title": "Correu i paquets (pas 1 de 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Amfitrió", - "password": "Contrasenya", - "port": "Port", - "username": "Nom d'usuari", - "imap_security": "Seguretat IMAP", - "verify_ssl": "Verifica el certificat SSL" - }, - "description": "Introduïu la informació de connexió del vostre servidor de correu.", - "title": "Correu i paquets (pas 1 de 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "L'autenticació de Mail and Packages ha fallat", - "fix_flow": { - "step": { - "confirm": { - "title": "Tornar a autenticar el servidor de correu", - "description": "L'autenticació amb el vostre servidor de correu IMAP ha fallat. Feu clic a envia per iniciar el flux de reautenticació." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Correu i paquets (pas 2 de 2)", - "description": "Acabeu la configuració personalitzant el següent en funció de l'estructura del vostre correu electrònic i de la instal·lació de Home Assistant.\n\nPer obtenir més detalls sobre les opcions d'[integració de correu i paquets](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), consulteu la [secció de configuració, plantilles i automatitzacions](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) a GitHub.\n\nSi utilitzeu correus electrònics reenviats d'Amazon, separeu cada adreça amb una coma o introduïu (none) per esborrar aquesta configuració.", - "data": { - "folder": "Carpeta de correu", - "resources": "Llista de sensors", - "scan_interval": "Interval d'exploració (minuts)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Camí de la imatge", - "gif_duration": "Durada de la imatge (segons)", - "image_security": "Nom del fitxer d'imatge aleatòria", - "imap_timeout": "Temps en segons abans de l'expiració de la connexió (segons, mínim 10)", - "generate_mp4": "Crea mp4 a partir d'imatges", - "allow_external": "Crea una imatge per a aplicacions de notificacions", - "usps_placeholder": "Incloure la imatge de substitució de l'USPS sense imatge al GIF?", - "custom_img": "Utilitza la imatge personalitzada USPS 'no hi ha correu'?", - "amazon_custom_img": "Utilitza la imatge personalitzada Amazon 'no hi ha lliurament'?", - "ups_custom_img": "Utilitza la imatge personalitzada UPS 'no hi ha lliurament'?", - "walmart_custom_img": "Utilitza la imatge personalitzada Walmart 'no hi ha lliurament'?", - "fedex_custom_img": "Utilitza la imatge personalitzada FedEx 'no hi ha lliurament'?", - "generic_custom_img": "Utilitza la imatge personalitzada genèrica 'no hi ha lliurament'?", - "generate_grid": "Crea una quadrícula d'imatges per a models de visió LLM", - "allow_forwarded_emails": "Permet els correus electrònics reenviats a més del valor predeterminat d'un servei (p. ex., no-reply@usps.com)" - } - }, - "options_3": { - "title": "Correu i paquets (Pas 3 de 3)", - "description": "Introduïu el camí i el nom del fitxer de la vostra imatge personalitzada de no correu a continuació.\n\nExemple: imatges/custom_nomail.gif", - "data": { - "custom_img_file": "Camí a la imatge personalitzada: (p. ex.: imatges/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Configuració d'Amazon", - "description": "Introduïu el domini des d'on Amazon envia correus electrònics (és a dir: amazon.com o amazon.de)\n\nSi utilitzeu correus electrònics reenviats d'Amazon, separeu cada adreça amb una coma o introduïu (none) per esborrar aquesta configuració.\n\nNota: si heu configurat una capçalera de reenviament en el pas anterior, el camp d'adreça de reenviament no apareixerà — la capçalera gestiona la correspondència de correus electrònics d'Amazon automàticament.", - "data": { - "amazon_domain": "Domini d'Amazon", - "amazon_fwds": "Amazon ha reenviat les adreces de correu electrònic", - "amazon_days": "Dies per comprovar els correus electrònics d'Amazon" - } - }, - "options_storage": { - "title": "Ubicació de l'emmagatzematge d'imatges", - "description": "Introduïu el directori on voleu que es desin les vostres imatges.\nPer defecte, es completa automàticament.", - "data": { - "storage": "Directori per emmagatzemar imatges" - } - }, - "options_forwarded_emails": { - "title": "Configuració dels correus electrònics reenviats", - "description": "Trieu com fer coincidir els correus electrònics reenviats.\n\n**Opció 1 — Capçalera de reenviament**: Si el vostre servei (p. ex. SimpleLogin) inclou el remitent original en una capçalera personalitzada, introduïu el nom d'aquesta capçalera. Mail and Packages l'utilitzarà per fer coincidir automàticament les adreces dels transportistes.\n\n**Opció 2 — Llista d'adreces**: Introduïu les adreces de reenviament que el vostre servei ha assignat a cada transportista, separades per comes.", - "data": { - "forwarding_header": "Capçalera de reenviament (p. ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Adreces de correu electrònic reenviades" - }, - "data_description": { - "forwarding_header": "Si el vostre servei de reenviament inclou el remitent original en una capçalera, introduïu aquí el nom de la capçalera. Això té prioritat sobre la llista d'adreces a continuació. Introduïu '(none)' o deixeu-ho en blanc per utilitzar la llista d'adreces.", - "forwarded_emails": "Llista d'adreces de reenviament separades per comes des de les quals arribaran els missatges. Deixeu '(none)' si feu servir la capçalera de reenviament de dalt." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/cs.json b/custom_components/mail_and_packages/translations/cs.json deleted file mode 100644 index a0e80d6..0000000 --- a/custom_components/mail_and_packages/translations/cs.json +++ /dev/null @@ -1,299 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Je povolena pouze jedna konfigurace Mail and Packages.", - "reconfigure_successful": "Rekonfigurace úspěšná", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Nelze se připojit nebo přihlásit k poštovnímu serveru. Podrobnosti naleznete v logu.", - "invalid_path": "Prosím uložte obrázky do jiného adresáře.", - "ffmpeg_not_found": "Generování MP4 vyžaduje ffmpeg", - "amazon_domain": "Neplatná e-mailová adresa pro přeposílání.", - "file_not_found": "Soubor obrázku nebyl nalezen", - "missing_forwarded_emails": "Chybí přeposílané e-mailové adresy. Můžete zadat '(none)' pro vymazání tohoto nastavení.", - "scan_too_low": "Interval skenování je příliš nízký (minimum 5)", - "timeout_too_low": "IMAP timeout je příliš nízký (minimum 10)", - "invalid_email_format": "Invalid email address format.", - "path_not_found": "Directory not found", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Poštovní složka", - "scan_interval": "Interval skenování (minuty)", - "image_path": "Cesta obrázku", - "gif_duration": "Trvání obrázku (sekund)", - "image_security": "Jméno náhodného obrázku", - "generate_mp4": "Vytvořit mp4 z obrázku", - "resources": "Seznam senzorů", - "imap_timeout": "Čas v sekundách před vypršením časového limitu připojení (sekund, minimum 10)", - "amazon_fwds": "Amazon předal e-mailové adresy", - "allow_external": "Vytvořte obrázek pro oznamovací aplikaci", - "amazon_days": "Dny zpět ke kontrole e-mailů Amazon", - "custom_img": "Použít vlastní obrázek USPS 'bez pošty'?", - "amazon_custom_img": "Použít vlastní obrázek Amazon 'bez dodání'?", - "ups_custom_img": "Použít vlastní obrázek UPS 'bez dodání'?", - "walmart_custom_img": "Použít vlastní obrázek Walmart 'bez dodání'?", - "fedex_custom_img": "Použít vlastní obrázek FedEx 'bez dodání'?", - "generic_custom_img": "Použít vlastní obrázek obecný 'bez dodání'?", - "allow_forwarded_emails": "Povolit přeposílané e-maily kromě výchozí hodnoty služby (např. no-reply@usps.com)", - "generate_grid": "Create image grid for LLM vision models", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Zahrnout zástupný symbol USPS bez obrázku do GIF?" - }, - "description": "Dokončete konfiguraci přizpůsobením následujících položek na základě struktury e-mailu a instalace Home Assistant.\n\nPodrobnosti o [Mail and Packages integration](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) můžete zkontrolovat na [configurace, styly a automatizace](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHubu.", - "title": "Mail and Packages (Krok 2 ze 3)" - }, - "options_3": { - "data": { - "custom_img_file": "Cesta k vlastnímu obrázku: (např.: images/my_custom_no_mail.jpg)" - }, - "description": "Níže zadejte cestu a název souboru k vlastnímu obrázku bez pošty.\n\nPříklad: images/custom_nomail.gif", - "title": "Mail and Packages (Krok 3 ze 3)" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Hlavička přesměrování (např. X-SimpleLogin-Original-From)", - "forwarded_emails": "Přeposílané e-mailové adresy" - }, - "data_description": { - "forwarding_header": "Pokud vaše přeposílací služba zahrnuje původního odesílatele do záhlaví, zadejte zde název záhlaví. To má přednost před níže uvedeným seznamem adres. Zadejte '(none)' nebo nechte prázdné, chcete-li místo toho použít seznam adres.", - "forwarded_emails": "Seznam přeposílacích adres oddělených čárkami, ze kterých budou přicházet zprávy. Ponechejte '(none)', pokud používáte výše uvedenou hlavičku přesměrování." - }, - "description": "Vyberte způsob porovnávání přeposlaných e-mailů.\n\n**Možnost 1 — Hlavička přesměrování**: Pokud vaše služba (např. SimpleLogin) zahrnuje původního odesílatele do vlastního záhlaví, zadejte název tohoto záhlaví. Mail and Packages ho použije k automatickému přiřazení adres dopravců.\n\n**Možnost 2 — Seznam adres**: Zadejte přeposílací adresy, které vaše služba přidělila každému dopravci, oddělené čárkami.", - "title": "Nastavení přeposlaných e-mailů" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Hlavička přesměrování (např. X-SimpleLogin-Original-From)", - "forwarded_emails": "Přeposílané e-mailové adresy" - }, - "data_description": { - "forwarding_header": "Pokud vaše přeposílací služba zahrnuje původního odesílatele do záhlaví, zadejte zde název záhlaví. To má přednost před níže uvedeným seznamem adres. Zadejte '(none)' nebo nechte prázdné, chcete-li místo toho použít seznam adres.", - "forwarded_emails": "Seznam přeposílacích adres oddělených čárkami, ze kterých budou přicházet zprávy. Ponechejte '(none)', pokud používáte výše uvedenou hlavičku přesměrování." - }, - "description": "Vyberte způsob porovnávání přeposlaných e-mailů.\n\n**Možnost 1 — Hlavička přesměrování**: Pokud vaše služba (např. SimpleLogin) zahrnuje původního odesílatele do vlastního záhlaví, zadejte název tohoto záhlaví. Mail and Packages ho použije k automatickému přiřazení adres dopravců.\n\n**Možnost 2 — Seznam adres**: Zadejte přeposílací adresy, které vaše služba přidělila každému dopravci, oddělené čárkami.", - "title": "Nastavení přeposlaných e-mailů" - }, - "imap_config": { - "data": { - "host": "Host", - "password": "Heslo", - "port": "Port", - "username": "Uživatelské jméno", - "verify_ssl": "Verify SSL Cert", - "imap_security": "IMAP Security" - }, - "description": "Zadejte prosím informace o připojení vašeho poštovního serveru.", - "title": "Mail and Packages (Krok 1 ze 3)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "config_3": { - "data": { - "custom_img_file": "Path to custom USPS image: (ie: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Enter the path and file name to your custom no delivery images below.\n\nExample: images/custom_nomail.gif", - "title": "Mail and Packages (Step 3 of 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - }, - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.\n\nNote: if you configured a forwarding header in the previous step, the forwarding address field will not appear — the header handles Amazon email matching automatically.", - "title": "Amazon Settings" - }, - "reconfig_imap": { - "data": { - "host": "Host", - "password": "Password", - "port": "Port", - "username": "Username", - "imap_security": "IMAP Security", - "verify_ssl": "Verify SSL Cert" - }, - "description": "Please enter the connection information of your mail server.", - "title": "Mail and Packages (Step 1 of 2)" - }, - "reconfig_2": { - "data": { - "folder": "Mail Folder", - "scan_interval": "Scanning Interval (minutes, minimum 5)", - "image_path": "Image Path", - "gif_duration": "Image Duration (seconds)", - "image_security": "Random Image Filename", - "generate_grid": "Create image grid for LLM vision models", - "generate_mp4": "Create mp4 from images", - "resources": "Sensors List", - "imap_timeout": "Time in seconds before connection timeout (seconds, minimum 10)", - "allow_external": "Create image for notification apps", - "custom_img": "Use custom USPS 'no mail' image?", - "amazon_custom_img": "Use custom 'no Amazon delivery' image?", - "ups_custom_img": "Use custom 'no UPS delivery' image?", - "walmart_custom_img": "Use custom 'no Walmart delivery' image?", - "fedex_custom_img": "Use custom 'no FedEx delivery' image?", - "allow_forwarded_emails": "Allow forwarded emails in addition to a service's default (e.g. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Zahrnout zástupný symbol USPS bez obrázku do GIF?" - }, - "description": "Finish the configuration by customizing the following based on your email structure and Home Assistant installation.\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.", - "title": "Mail and Packages (Step 2 of 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Path to custom USPS image: (i.e.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Enter the path and file name to your custom no delivery images below.\n\nExample: images/custom_nomail.gif", - "title": "Mail and Packages (Step 3 of 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - }, - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.\n\nNote: if you configured a forwarding header in the previous step, the forwarding address field will not appear — the header handles Amazon email matching automatically.", - "title": "Amazon Settings" - }, - "config_storage": { - "data": { - "storage": "Directory to store images" - }, - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "title": "Image storage location" - }, - "reconfig_storage": { - "data": { - "storage": "Directory to store images" - }, - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "title": "Image storage location" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - }, - "title": "Mail and Packages" - }, - "options": { - "step": { - "init": { - "title": "Mail and Packages (Step 2 of 2)", - "description": "Finish the configuration by customizing the following based on your email structure and Home Assistant installation.\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.", - "data": { - "folder": "Mail Folder", - "resources": "Sensors List", - "scan_interval": "Scanning Interval (minutes, minimum 5)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Image Path", - "gif_duration": "Image Duration (seconds)", - "image_security": "Random Image Filename", - "imap_timeout": "Time in seconds before connection timeout (seconds, minimum 10)", - "generate_mp4": "Create mp4 from images", - "allow_external": "Create image for notification apps", - "usps_placeholder": "Zahrnout zástupný symbol USPS bez obrázku do GIF?", - "custom_img": "Use custom USPS 'no mail' image?", - "amazon_custom_img": "Use custom 'no Amazon delivery' image?", - "ups_custom_img": "Use custom 'no UPS delivery' image?", - "walmart_custom_img": "Use custom 'no Walmart delivery' image?", - "fedex_custom_img": "Use custom 'no FedEx delivery' image?", - "generic_custom_img": "Použít vlastní obrázek obecný 'bez dodání'?", - "generate_grid": "Create image grid for LLM vision models", - "allow_forwarded_emails": "Allow forwarded emails in addition to a service's default (e.g. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Mail and Packages (Step 3 of 3)", - "description": "Enter the path and file name to your custom no delivery images below.\n\nExample: images/custom_nomail.gif", - "data": { - "custom_img_file": "Path to custom USPS image: (i.e.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Amazon Settings", - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.\n\nNote: if you configured a forwarding header in the previous step, the forwarding address field will not appear — the header handles Amazon email matching automatically.", - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - } - }, - "options_storage": { - "title": "Image storage location", - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "data": { - "storage": "Directory to store images" - } - }, - "options_forwarded_emails": { - "title": "Nastavení přeposlaných e-mailů", - "description": "Vyberte způsob porovnávání přeposlaných e-mailů.\n\n**Možnost 1 — Hlavička přesměrování**: Pokud vaše služba (např. SimpleLogin) zahrnuje původního odesílatele do vlastního záhlaví, zadejte název tohoto záhlaví. Mail and Packages ho použije k automatickému přiřazení adres dopravců.\n\n**Možnost 2 — Seznam adres**: Zadejte přeposílací adresy, které vaše služba přidělila každému dopravci, oddělené čárkami.", - "data": { - "forwarding_header": "Hlavička přesměrování (např. X-SimpleLogin-Original-From)", - "forwarded_emails": "Přeposílané e-mailové adresy" - }, - "data_description": { - "forwarding_header": "Pokud vaše přeposílací služba zahrnuje původního odesílatele do záhlaví, zadejte zde název záhlaví. To má přednost před níže uvedeným seznamem adres. Zadejte '(none)' nebo nechte prázdné, chcete-li místo toho použít seznam adres.", - "forwarded_emails": "Seznam přeposílacích adres oddělených čárkami, ze kterých budou přicházet zprávy. Ponechejte '(none)', pokud používáte výše uvedenou hlavičku přesměrování." - } - } - } - }, - "title": "Mail and Packages", - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Ověření Mail and Packages selhalo", - "fix_flow": { - "step": { - "confirm": { - "title": "Znovu ověřit poštovní server", - "description": "Ověření k vašemu poštovnímu serveru IMAP selhalo. Kliknutím na odeslat spustíte proces opětovného ověření." - } - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/de.json b/custom_components/mail_and_packages/translations/de.json deleted file mode 100644 index 8f80771..0000000 --- a/custom_components/mail_and_packages/translations/de.json +++ /dev/null @@ -1,293 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Es ist nur eine einzige Konfiguration von Mail und Paketen zulässig.", - "reconfigure_successful": "Neukonfiguration erfolgreich", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Es kann keine Verbindung zum Mailserver hergestellt oder angemeldet werden. Bitte überprüfen Sie das Protokoll für Details.", - "invalid_path": "Bitte speichern Sie die Bilder in einem anderen Verzeichnis.", - "ffmpeg_not_found": "MP4 erstellen erfordert ffmpeg", - "amazon_domain": "Ungültige Weiterleitungs-E-Mail-Adresse.", - "file_not_found": "Bilddatei nicht gefunden", - "invalid_email_format": "Ungültiges E-Mail-Adressformat.", - "missing_forwarded_emails": "Fehlende weitergeleitete E-Mail-Adressen. Sie können '(none)' eingeben, um diese Einstellung zu löschen.", - "path_not_found": "Verzeichnis nicht gefunden", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "E-Mail-Ordner", - "resources": "Sensoren Liste", - "scan_interval": "Scanintervall (Minuten)", - "image_path": "Bildpfad", - "gif_duration": "Bilddauer (Sekunden)", - "image_security": "Zufälliger Bilddateiname", - "imap_timeout": "Zeit in Sekunden bis zum Timeout der Verbindung (Sekunden, mindestens 10)", - "generate_mp4": "Erstellen Sie mp4 aus Bildern", - "allow_external": "Bild für Benachrichtigungs-Apps erstellen", - "custom_img": "Benutzerdefiniertes USPS 'keine Post' Bild verwenden?", - "amazon_custom_img": "Benutzerdefiniertes Amazon 'keine Lieferung' Bild verwenden?", - "ups_custom_img": "Benutzerdefiniertes UPS 'keine Lieferung' Bild verwenden?", - "walmart_custom_img": "Benutzerdefiniertes Walmart 'keine Lieferung' Bild verwenden?", - "fedex_custom_img": "Benutzerdefiniertes FedEx 'keine Lieferung' Bild verwenden?", - "generic_custom_img": "Benutzerdefiniertes generisches 'keine Lieferung' Bild verwenden?", - "post_de_custom_img": "Benutzerdefiniertes Post DE 'keine Post' Bild verwenden?", - "generate_grid": "Erstellen Sie ein Bildraster für LLM-Vision-Modelle", - "allow_forwarded_emails": "Weitergeleitete E-Mails zusätzlich zu den Standardwerten eines Dienstes zulassen (z. B. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "USPS-Platzhalter ohne Bild im GIF einschließen?" - }, - "description": "Beenden Sie die Konfiguration, indem Sie Folgendes basierend auf Ihrer E-Mail-Struktur und der Installation von Home Assistant anpassen. \n\n Weitere Informationen zu den Optionen [Mail- und Paketintegration] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) finden Sie in den [Konfiguration, Vorlagen und Abschnitt Automatisierungen] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) auf GitHub.", - "title": "Briefe und Pakete (Schritt 2 von 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Pfad zum benutzerdefinierten Bild: (z. B.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Pfad zum benutzerdefinierten Post DE-Bild: (z. B.: images/my_custom_no_post_de.gif)" - }, - "description": "Geben Sie unten den Pfad und den Dateinamen zu Ihrem benutzerdefinierten Keine-Mail-Bild ein.\n\nBeispiel: images/custom_nomail.gif", - "title": "Post und Pakete (Schritt 3 von 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon-Domain", - "amazon_fwds": "Amazon hat E-Mail-Adressen weitergeleitet", - "amazon_days": "Tage zurück, um nach Amazon-E-Mails zu suchen" - }, - "description": "Bitte geben Sie die Domain ein, von der Amazon E-Mails sendet (z.B.: amazon.com oder amazon.de)\n\nWenn Sie weitergeleitete E-Mails von Amazon verwenden, trennen Sie bitte jede Adresse mit einem Komma oder geben Sie (keine) ein, um diese Einstellung zu löschen.\n\nHinweis: Wenn Sie im vorherigen Schritt einen Weiterleitungs-Header konfiguriert haben, wird das Feld für die Weiterleitungsadresse nicht angezeigt — der Header übernimmt die Zuordnung von Amazon-E-Mails automatisch.", - "title": "Amazon Einstellungen" - }, - "reconfig_2": { - "data": { - "folder": "E-Mail-Ordner", - "scan_interval": "Scanintervall (Minuten)", - "image_path": "Bildpfad", - "gif_duration": "Bilddauer (Sekunden)", - "image_security": "Zufälliger Bilddateiname", - "generate_mp4": "Erstellen Sie mp4 aus Bildern", - "resources": "Sensoren Liste", - "imap_timeout": "Zeit in Sekunden bis zum Timeout der Verbindung (Sekunden, mindestens 10)", - "allow_external": "Bild für Benachrichtigungs-Apps erstellen", - "custom_img": "Benutzerdefiniertes USPS 'keine Post' Bild verwenden?", - "amazon_custom_img": "Benutzerdefiniertes Amazon 'keine Lieferung' Bild verwenden?", - "ups_custom_img": "Benutzerdefiniertes UPS 'keine Lieferung' Bild verwenden?", - "walmart_custom_img": "Benutzerdefiniertes Walmart 'keine Lieferung' Bild verwenden?", - "fedex_custom_img": "Benutzerdefiniertes FedEx 'keine Lieferung' Bild verwenden?", - "generic_custom_img": "Benutzerdefiniertes generisches 'keine Lieferung' Bild verwenden?", - "post_de_custom_img": "Benutzerdefiniertes Post DE 'keine Post' Bild verwenden?", - "generate_grid": "Erstellen Sie ein Bildraster für LLM-Vision-Modelle", - "allow_forwarded_emails": "Weitergeleitete E-Mails zusätzlich zu den Standardwerten eines Dienstes zulassen (z. B. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "USPS-Platzhalter ohne Bild im GIF einschließen?" - }, - "description": "Schließen Sie die Konfiguration ab, indem Sie das Folgende an Ihre E-Mail-Struktur und Home Assistant-Installation anpassen.\n\nWeitere Informationen zu den [Mail und Packages Integration](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) Optionen finden Sie im [Konfigurations-, Vorlagen- und Automatisierungsabschnitt](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) auf GitHub.\n\nWenn Sie weitergeleitete E-Mails von Amazon verwenden, trennen Sie bitte jede Adresse durch ein Komma oder geben Sie (keine) ein, um diese Einstellung zu löschen.", - "title": "Briefe und Pakete (Schritt 2 von 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Pfad zum benutzerdefinierten Bild: (z.B.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Pfad zum benutzerdefinierten Post DE-Bild: (z. B.: images/my_custom_no_post_de.gif)" - }, - "description": "Geben Sie unten den Pfad und den Dateinamen zu Ihrem benutzerdefinierten Keine-Mail-Bild ein.\n\nBeispiel: images/custom_nomail.gif", - "title": "Post und Pakete (Schritt 3 von 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon-Domain", - "amazon_fwds": "Amazon hat E-Mail-Adressen weitergeleitet", - "amazon_days": "Tage zurück, um nach Amazon-E-Mails zu suchen" - }, - "description": "Bitte geben Sie die Domain ein, von der Amazon E-Mails sendet (z.B.: amazon.com oder amazon.de)\n\nWenn Sie weitergeleitete E-Mails von Amazon verwenden, trennen Sie bitte jede Adresse mit einem Komma oder geben Sie (keine) ein, um diese Einstellung zu löschen.\n\nHinweis: Wenn Sie im vorherigen Schritt einen Weiterleitungs-Header konfiguriert haben, wird das Feld für die Weiterleitungsadresse nicht angezeigt — der Header übernimmt die Zuordnung von Amazon-E-Mails automatisch.", - "title": "Amazon Einstellungen" - }, - "config_storage": { - "data": { - "storage": "Verzeichnis zum Speichern von Bildern" - }, - "description": "Bitte geben Sie das Verzeichnis ein, in dem Ihre Bilder gespeichert werden sollen.\nStandardmäßig ist es automatisch ausgefüllt.", - "title": "Speicherort für Bilder" - }, - "reconfig_storage": { - "data": { - "storage": "Verzeichnis zum Speichern von Bildern" - }, - "description": "Bitte geben Sie das Verzeichnis ein, in dem Ihre Bilder gespeichert werden sollen.\nStandardmäßig ist es automatisch ausgefüllt.", - "title": "Speicherort für Bilder" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Weiterleitungs-Header (z.B. X-SimpleLogin-Original-From)", - "forwarded_emails": "Weitergeleitete E-Mail-Adressen" - }, - "data_description": { - "forwarding_header": "Wenn Ihr Weiterleitungsdienst den ursprünglichen Absender in einem Header enthält, geben Sie hier den Header-Namen ein. Dies hat Vorrang vor der Adressliste unten. Geben Sie '(none)' ein oder lassen Sie das Feld leer, um stattdessen die Adressliste zu verwenden.", - "forwarded_emails": "Kommagetrennte Liste der Weiterleitungsadressen, von denen Nachrichten eingehen. Lassen Sie '(none)' stehen, wenn Sie den Weiterleitungs-Header oben verwenden." - }, - "description": "Wählen Sie, wie weitergeleitete E-Mails abgeglichen werden sollen.\n\n**Option 1 — Weiterleitungs-Header**: Wenn Ihr Dienst (z.B. SimpleLogin) den ursprünglichen Absender in einem benutzerdefinierten Header enthält, geben Sie diesen Header-Namen ein. Mail and Packages verwendet ihn, um Zustell-Adressen automatisch abzugleichen.\n\n**Option 2 — Adressliste**: Geben Sie die Weiterleitungsadressen ein, die Ihr Dienst jedem Zusteller zugewiesen hat, getrennt durch Kommas.", - "title": "Einstellungen für weitergeleitete E-Mails" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Weiterleitungs-Header (z.B. X-SimpleLogin-Original-From)", - "forwarded_emails": "Weitergeleitete E-Mail-Adressen" - }, - "data_description": { - "forwarding_header": "Wenn Ihr Weiterleitungsdienst den ursprünglichen Absender in einem Header enthält, geben Sie hier den Header-Namen ein. Dies hat Vorrang vor der Adressliste unten. Geben Sie '(none)' ein oder lassen Sie das Feld leer, um stattdessen die Adressliste zu verwenden.", - "forwarded_emails": "Kommagetrennte Liste der Weiterleitungsadressen, von denen Nachrichten eingehen. Lassen Sie '(none)' stehen, wenn Sie den Weiterleitungs-Header oben verwenden." - }, - "description": "Wählen Sie, wie weitergeleitete E-Mails abgeglichen werden sollen.\n\n**Option 1 — Weiterleitungs-Header**: Wenn Ihr Dienst (z.B. SimpleLogin) den ursprünglichen Absender in einem benutzerdefinierten Header enthält, geben Sie diesen Header-Namen ein. Mail and Packages verwendet ihn, um Zustell-Adressen automatisch abzugleichen.\n\n**Option 2 — Adressliste**: Geben Sie die Weiterleitungsadressen ein, die Ihr Dienst jedem Zusteller zugewiesen hat, getrennt durch Kommas.", - "title": "Einstellungen für weitergeleitete E-Mails" - }, - "imap_config": { - "description": "Bitte geben Sie die Verbindungsinformationen Ihres Mail-Servers ein.", - "data": { - "host": "Host", - "password": "Passwort", - "port": "Port", - "username": "Nutzername", - "verify_ssl": "SSL-Zertifikat überprüfen", - "imap_security": "IMAP-Sicherheit" - }, - "title": "Briefe und Pakete (Schritt 1 von 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Host", - "password": "Passwort", - "port": "Port", - "username": "Nutzername", - "imap_security": "IMAP-Sicherheit", - "verify_ssl": "SSL-Zertifikat überprüfen" - }, - "description": "Bitte geben Sie die Verbindungsinformationen Ihres Mail-Servers ein.", - "title": "Briefe und Pakete (Schritt 1 von 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages Authentifizierung fehlgeschlagen", - "fix_flow": { - "step": { - "confirm": { - "title": "Mailserver neu authentifizieren", - "description": "Die Authentifizierung bei Ihrem IMAP-Mailserver ist fehlgeschlagen. Klicken Sie auf Senden, um den Reauthentifizierungs-Flow zu starten." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Briefe und Pakete (Schritt 2 von 2)", - "description": "Schließen Sie die Konfiguration ab, indem Sie das Folgende an Ihre E-Mail-Struktur und Home Assistant-Installation anpassen.\n\nWeitere Informationen zu den [Mail und Packages Integration](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) Optionen finden Sie im [Konfigurations-, Vorlagen- und Automatisierungsabschnitt](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) auf GitHub.\n\nWenn Sie weitergeleitete E-Mails von Amazon verwenden, trennen Sie bitte jede Adresse durch ein Komma oder geben Sie (keine) ein, um diese Einstellung zu löschen.", - "data": { - "folder": "E-Mail-Ordner", - "resources": "Sensoren Liste", - "scan_interval": "Scanintervall (Minuten)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Bildpfad", - "gif_duration": "Bilddauer (Sekunden)", - "image_security": "Zufälliger Bilddateiname", - "imap_timeout": "Zeit in Sekunden bis zum Timeout der Verbindung (Sekunden, mindestens 10)", - "generate_mp4": "Erstellen Sie mp4 aus Bildern", - "allow_external": "Bild für Benachrichtigungs-Apps erstellen", - "usps_placeholder": "USPS-Platzhalter ohne Bild im GIF einschließen?", - "custom_img": "Benutzerdefiniertes USPS 'keine Post' Bild verwenden?", - "amazon_custom_img": "Benutzerdefiniertes Amazon 'keine Lieferung' Bild verwenden?", - "ups_custom_img": "Benutzerdefiniertes UPS 'keine Lieferung' Bild verwenden?", - "walmart_custom_img": "Benutzerdefiniertes Walmart 'keine Lieferung' Bild verwenden?", - "fedex_custom_img": "Benutzerdefiniertes FedEx 'keine Lieferung' Bild verwenden?", - "generic_custom_img": "Benutzerdefiniertes generisches 'keine Lieferung' Bild verwenden?", - "post_de_custom_img": "Benutzerdefiniertes Post DE 'keine Post' Bild verwenden?", - "generate_grid": "Erstellen Sie ein Bildraster für LLM-Vision-Modelle", - "allow_forwarded_emails": "Weitergeleitete E-Mails zusätzlich zu den Standardwerten eines Dienstes zulassen (z. B. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Post und Pakete (Schritt 3 von 3)", - "description": "Geben Sie unten den Pfad und den Dateinamen zu Ihrem benutzerdefinierten Keine-Mail-Bild ein.\n\nBeispiel: images/custom_nomail.gif", - "data": { - "custom_img_file": "Pfad zum benutzerdefinierten Bild: (z.B.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Pfad zum benutzerdefinierten Post DE-Bild: (z. B.: images/my_custom_no_post_de.gif)" - } - }, - "options_amazon": { - "title": "Amazon Einstellungen", - "description": "Bitte geben Sie die Domain ein, von der Amazon E-Mails sendet (z.B.: amazon.com oder amazon.de)\n\nWenn Sie weitergeleitete E-Mails von Amazon verwenden, trennen Sie bitte jede Adresse mit einem Komma oder geben Sie (keine) ein, um diese Einstellung zu löschen.\n\nHinweis: Wenn Sie im vorherigen Schritt einen Weiterleitungs-Header konfiguriert haben, wird das Feld für die Weiterleitungsadresse nicht angezeigt — der Header übernimmt die Zuordnung von Amazon-E-Mails automatisch.", - "data": { - "amazon_domain": "Amazon-Domain", - "amazon_fwds": "Amazon hat E-Mail-Adressen weitergeleitet", - "amazon_days": "Tage zurück, um nach Amazon-E-Mails zu suchen" - } - }, - "options_storage": { - "title": "Speicherort für Bilder", - "description": "Bitte geben Sie das Verzeichnis ein, in dem Ihre Bilder gespeichert werden sollen.\nStandardmäßig ist es automatisch ausgefüllt.", - "data": { - "storage": "Verzeichnis zum Speichern von Bildern" - } - }, - "options_forwarded_emails": { - "title": "Einstellungen für weitergeleitete E-Mails", - "description": "Wählen Sie, wie weitergeleitete E-Mails abgeglichen werden sollen.\n\n**Option 1 — Weiterleitungs-Header**: Wenn Ihr Dienst (z.B. SimpleLogin) den ursprünglichen Absender in einem benutzerdefinierten Header enthält, geben Sie diesen Header-Namen ein. Mail and Packages verwendet ihn, um Zustell-Adressen automatisch abzugleichen.\n\n**Option 2 — Adressliste**: Geben Sie die Weiterleitungsadressen ein, die Ihr Dienst jedem Zusteller zugewiesen hat, getrennt durch Kommas.", - "data": { - "forwarding_header": "Weiterleitungs-Header (z.B. X-SimpleLogin-Original-From)", - "forwarded_emails": "Weitergeleitete E-Mail-Adressen" - }, - "data_description": { - "forwarding_header": "Wenn Ihr Weiterleitungsdienst den ursprünglichen Absender in einem Header enthält, geben Sie hier den Header-Namen ein. Dies hat Vorrang vor der Adressliste unten. Geben Sie '(none)' ein oder lassen Sie das Feld leer, um stattdessen die Adressliste zu verwenden.", - "forwarded_emails": "Kommagetrennte Liste der Weiterleitungsadressen, von denen Nachrichten eingehen. Lassen Sie '(none)' stehen, wenn Sie den Weiterleitungs-Header oben verwenden." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/en.json b/custom_components/mail_and_packages/translations/en.json deleted file mode 100644 index ee5ebaa..0000000 --- a/custom_components/mail_and_packages/translations/en.json +++ /dev/null @@ -1,302 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Only a single configuration of Mail and Packages is allowed.", - "reconfigure_successful": "Reconfigure Successful", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Unable to connect or login to the mail server. Please check the log for details.", - "invalid_path": "Please store the images in another directory.", - "ffmpeg_not_found": "Generate MP4 requires ffmpeg", - "amazon_domain": "Invalid forwarding email address.", - "file_not_found": "Image file not found", - "invalid_email_format": "Invalid email address format.", - "missing_forwarded_emails": "Missing forwarded email addresses. You may enter '(none)' to clear this setting.", - "path_not_found": "Directory not found", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "imap_config": { - "description": "Please enter the connection information of your mail server.", - "data": { - "host": "Host", - "password": "Password", - "port": "Port", - "username": "Username", - "verify_ssl": "Verify SSL Cert", - "imap_security": "IMAP Security" - }, - "title": "Mail and Packages (Step 1 of 2)" - }, - "config_2": { - "data": { - "folder": "Mail Folder", - "resources": "Sensors List", - "scan_interval": "Scanning Interval (minutes, minimum 5)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Image Path", - "gif_duration": "Image Duration (seconds)", - "image_security": "Random Image Filename", - "imap_timeout": "Mailbox scan time limit (seconds, minimum 10)", - "generate_mp4": "Create mp4 from images", - "allow_external": "Create image for notification apps", - "usps_placeholder": "Include USPS no-image placeholder in GIF?", - "custom_img": "Use custom USPS 'no mail' image?", - "amazon_custom_img": "Use custom 'no Amazon delivery' image?", - "ups_custom_img": "Use custom 'no UPS delivery' image?", - "walmart_custom_img": "Use custom 'no Walmart delivery' image?", - "fedex_custom_img": "Use custom 'no FedEx delivery' image?", - "generic_custom_img": "Use custom 'no Generic delivery' image?", - "post_de_custom_img": "Use custom Post DE 'no mail' image?", - "generate_grid": "Create image grid for LLM vision models", - "allow_forwarded_emails": "Allow forwarded emails in addition to a service's default (e.g. no-reply@usps.com)" - }, - "data_description": { - "imap_timeout": "Covers the entire scan — login plus every per-carrier IMAP search — not just connecting. Raise this if large mailboxes time out; lower it to fail faster." - }, - "description": "Finish the configuration by customizing the following based on your email structure and Home Assistant installation.\n\nIf using Amazon forwarded emails please separate each address with a comma.", - "title": "Mail and Packages (Step 2 of 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Path to custom image: (ie: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Path to custom Post DE image: (ie: images/my_custom_no_post_de.gif)" - }, - "description": "Enter the path and file name to your custom no mail image below.\n\nExample: images/custom_nomail.gif", - "title": "Mail and Packages (Step 3 of 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - }, - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.\n\nNote: if you configured a forwarding header in the previous step, the forwarding address field will not appear — the header handles Amazon email matching automatically.", - "title": "Amazon Settings" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Host", - "password": "Password", - "port": "Port", - "username": "Username", - "imap_security": "IMAP Security", - "verify_ssl": "Verify SSL Cert" - }, - "description": "Please enter the connection information of your mail server.", - "title": "Mail and Packages (Step 1 of 2)" - }, - "reconfig_2": { - "data": { - "folder": "Mail Folder", - "scan_interval": "Scanning Interval (minutes, minimum 5)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Image Path", - "gif_duration": "Image Duration (seconds)", - "image_security": "Random Image Filename", - "generate_mp4": "Create mp4 from images", - "resources": "Sensors List", - "imap_timeout": "Mailbox scan time limit (seconds, minimum 10)", - "allow_external": "Create image for notification apps", - "usps_placeholder": "Include USPS no-image placeholder in GIF?", - "custom_img": "Use custom USPS 'no mail' image?", - "amazon_custom_img": "Use custom 'no Amazon delivery' image?", - "ups_custom_img": "Use custom 'no UPS delivery' image?", - "walmart_custom_img": "Use custom 'no Walmart delivery' image?", - "fedex_custom_img": "Use custom 'no FedEx delivery' image?", - "generic_custom_img": "Use custom 'no Generic delivery' image?", - "post_de_custom_img": "Use custom Post DE 'no mail' image?", - "generate_grid": "Create image grid for LLM vision models", - "allow_forwarded_emails": "Allow forwarded emails in addition to a service's default (e.g. no-reply@usps.com)" - }, - "data_description": { - "imap_timeout": "Covers the entire scan — login plus every per-carrier IMAP search — not just connecting. Raise this if large mailboxes time out; lower it to fail faster." - }, - "description": "Finish the configuration by customizing the following based on your email structure and Home Assistant installation.\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.", - "title": "Mail and Packages (Step 2 of 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Path to custom image: (i.e.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Path to custom Post DE image: (i.e.: images/my_custom_no_post_de.gif)" - }, - "description": "Enter the path and file name to your custom no mail image below.\n\nExample: images/custom_nomail.gif", - "title": "Mail and Packages (Step 3 of 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - }, - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.\n\nNote: if you configured a forwarding header in the previous step, the forwarding address field will not appear — the header handles Amazon email matching automatically.", - "title": "Amazon Settings" - }, - "config_storage": { - "data": { - "storage": "Directory to store images" - }, - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "title": "Image storage location" - }, - "reconfig_storage": { - "data": { - "storage": "Directory to store images" - }, - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "title": "Image storage location" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Forwarding Header (e.g. X-SimpleLogin-Original-From)", - "forwarded_emails": "Forwarded Email Addresses" - }, - "data_description": { - "forwarding_header": "If your forwarding service includes the original sender in a header, enter the header name here. This takes precedence over the address list below. Enter '(none)' or leave blank to use the address list instead.", - "forwarded_emails": "Comma-separated list of forwarded addresses messages will arrive from. Leave as '(none)' if using the forwarding header above." - }, - "description": "Choose how to match forwarded emails.\n\n**Option 1 — Forwarding header**: If your service (e.g. SimpleLogin) includes the original sender in a custom header, enter that header name. Mail and Packages will use it to match carrier addresses automatically.\n\n**Option 2 — Address list**: Enter the forwarding addresses your service assigned to each carrier, separated by commas.", - "title": "Forwarded Email Settings" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Forwarding Header (e.g. X-SimpleLogin-Original-From)", - "forwarded_emails": "Forwarded Email Addresses" - }, - "data_description": { - "forwarding_header": "If your forwarding service includes the original sender in a header, enter the header name here. This takes precedence over the address list below. Enter '(none)' or leave blank to use the address list instead.", - "forwarded_emails": "Comma-separated list of forwarded addresses messages will arrive from. Leave as '(none)' if using the forwarding header above." - }, - "description": "Choose how to match forwarded emails.\n\n**Option 1 — Forwarding header**: If your service (e.g. SimpleLogin) includes the original sender in a custom header, enter that header name. Mail and Packages will use it to match carrier addresses automatically.\n\n**Option 2 — Address list**: Enter the forwarding addresses your service assigned to each carrier, separated by commas.", - "title": "Forwarded Email Settings" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "options": { - "step": { - "init": { - "title": "Mail and Packages (Step 2 of 2)", - "description": "Finish the configuration by customizing the following based on your email structure and Home Assistant installation.\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.", - "data": { - "folder": "Mail Folder", - "resources": "Sensors List", - "scan_interval": "Scanning Interval (minutes, minimum 5)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Image Path", - "gif_duration": "Image Duration (seconds)", - "image_security": "Random Image Filename", - "imap_timeout": "Mailbox scan time limit (seconds, minimum 10)", - "generate_mp4": "Create mp4 from images", - "allow_external": "Create image for notification apps", - "usps_placeholder": "Include USPS no-image placeholder in GIF?", - "custom_img": "Use custom USPS 'no mail' image?", - "amazon_custom_img": "Use custom 'no Amazon delivery' image?", - "ups_custom_img": "Use custom 'no UPS delivery' image?", - "walmart_custom_img": "Use custom 'no Walmart delivery' image?", - "fedex_custom_img": "Use custom 'no FedEx delivery' image?", - "generic_custom_img": "Use custom 'no Generic delivery' image?", - "post_de_custom_img": "Use custom Post DE 'no mail' image?", - "generate_grid": "Create image grid for LLM vision models", - "allow_forwarded_emails": "Allow forwarded emails in addition to a service's default (e.g. no-reply@usps.com)" - }, - "data_description": { - "imap_timeout": "Covers the entire scan — login plus every per-carrier IMAP search — not just connecting. Raise this if large mailboxes time out; lower it to fail faster." - } - }, - "options_3": { - "title": "Mail and Packages (Step 3 of 3)", - "description": "Enter the path and file name to your custom no mail image below.\n\nExample: images/custom_nomail.gif", - "data": { - "custom_img_file": "Path to custom image: (i.e.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)", - "post_de_custom_img_file": "Path to custom Post DE image: (i.e.: images/my_custom_no_post_de.gif)" - } - }, - "options_amazon": { - "title": "Amazon Settings", - "description": "Please enter the domain Amazon sends email's from (ie: amazon.com or amazon.de)\n\nIf using Amazon forwarded emails please separate each address with a comma or enter (none) to clear this setting.\n\nNote: if you configured a forwarding header in the previous step, the forwarding address field will not appear — the header handles Amazon email matching automatically.", - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon forwarded email addresses", - "amazon_days": "Days back to check for Amazon emails" - } - }, - "options_storage": { - "title": "Image storage location", - "description": "Please enter the directory you'd like your images to be stored in.\nThe default is auto populated.", - "data": { - "storage": "Directory to store images" - } - }, - "options_forwarded_emails": { - "title": "Forwarded Email Settings", - "description": "Choose how to match forwarded emails.\n\n**Option 1 — Forwarding header**: If your service (e.g. SimpleLogin) includes the original sender in a custom header, enter that header name. Mail and Packages will use it to match carrier addresses automatically.\n\n**Option 2 — Address list**: Enter the forwarding addresses your service assigned to each carrier, separated by commas.", - "data": { - "forwarding_header": "Forwarding Header (e.g. X-SimpleLogin-Original-From)", - "forwarded_emails": "Forwarded Email Addresses" - }, - "data_description": { - "forwarding_header": "If your forwarding service includes the original sender in a header, enter the header name here. This takes precedence over the address list below. Enter '(none)' or leave blank to use the address list instead.", - "forwarded_emails": "Comma-separated list of forwarded addresses messages will arrive from. Leave as '(none)' if using the forwarding header above." - } - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages authentication failed", - "fix_flow": { - "step": { - "confirm": { - "title": "Re-authenticate Mail Server", - "description": "Authentication to your IMAP mail server has failed. Click submit to start the re-authentication flow." - } - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/es.json b/custom_components/mail_and_packages/translations/es.json deleted file mode 100644 index d3f28a4..0000000 --- a/custom_components/mail_and_packages/translations/es.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Solo se permite una única configuración de correo y paquetes.", - "reconfigure_successful": "Reconfiguración Exitosa", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "No se puede conectar o iniciar sesión en el servidor de correo. Por favor, consulte el registro para más detalles.", - "invalid_path": "Guarde las imágenes en otro directorio.", - "ffmpeg_not_found": "Generar MP4 requiere ffmpeg", - "amazon_domain": "Dirección de correo electrónico de reenvío no válida.", - "file_not_found": "Archivo de imagen no encontrado", - "invalid_email_format": "Formato de dirección de correo electrónico no válido.", - "missing_forwarded_emails": "Faltan direcciones de correo electrónico reenviadas. Puede introducir '(none)' para borrar esta configuración.", - "path_not_found": "Directorio no encontrado", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Carpeta de correo", - "resources": "Lista de Sensores", - "scan_interval": "Intervalo de escaneo (minutos)", - "image_path": "Ruta de la imagen", - "gif_duration": "Duración de la imagen (segundos)", - "image_security": "Nombre de archivo de imagen aleatoria", - "imap_timeout": "Tiempo en segundos antes del tiempo de espera de la conexión (segundos, mínimo 10)", - "generate_mp4": "Crea mp4 a partir de imágenes", - "allow_external": "Crea imagen para aplicaciones de notificación", - "custom_img": "¿Usar imagen personalizada de USPS 'sin correo'?", - "amazon_custom_img": "¿Usar imagen personalizada de Amazon 'sin entrega'?", - "ups_custom_img": "¿Usar imagen personalizada de UPS 'sin entrega'?", - "walmart_custom_img": "¿Usar imagen personalizada de Walmart 'sin entrega'?", - "fedex_custom_img": "¿Usar imagen personalizada de FedEx 'sin entrega'?", - "generic_custom_img": "¿Usar imagen personalizada genérica 'sin entrega'?", - "generate_grid": "Crear cuadrícula de imágenes para modelos de visión LLM", - "allow_forwarded_emails": "Permitir correos electrónicos reenviados además del valor predeterminado de un servicio (p. ej., no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "¿Incluir marcador de posición sin imagen de USPS en el GIF?" - }, - "description": "Termine la configuración personalizando lo siguiente según su estructura de correo electrónico y la instalación de Home Assistant. \n\n Para obtener detalles sobre las opciones [Integración de correo y paquetes] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) revise las [configuración, plantillas , y la sección de automatizaciones] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) en GitHub.", - "title": "Correo y paquetes (Paso 2 de 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Ruta a la imagen personalizada: (es decir: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Ingrese la ruta y el nombre del archivo de su imagen personalizada de no correo a continuación.\n\nEjemplo: images/custom_nomail.gif", - "title": "Correo y Paquetes (Paso 3 de 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Dominio de Amazon", - "amazon_fwds": "Amazon reenvió direcciones de correo electrónico", - "amazon_days": "Días atrás para verificar los correos electrónicos de Amazon" - }, - "description": "Por favor, introduzca el dominio desde el cual Amazon envía correos electrónicos (es decir: amazon.com o amazon.de)\n\nSi utiliza correos electrónicos reenviados por Amazon, separe cada dirección con una coma o introduzca (ninguno) para borrar esta configuración.\n\nNota: si configuró un encabezado de reenvío en el paso anterior, el campo de dirección de reenvío no aparecerá — el encabezado gestiona la coincidencia de correos electrónicos de Amazon automáticamente.", - "title": "Configuración de Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Carpeta de correo", - "scan_interval": "Intervalo de escaneo (minutos)", - "image_path": "Ruta de la imagen", - "gif_duration": "Duración de la imagen (segundos)", - "image_security": "Nombre de archivo de imagen aleatoria", - "generate_mp4": "Crea mp4 a partir de imágenes", - "resources": "Lista de Sensores", - "imap_timeout": "Tiempo en segundos antes del tiempo de espera de la conexión (segundos, mínimo 10)", - "allow_external": "Crea imagen para aplicaciones de notificación", - "custom_img": "¿Usar imagen personalizada de USPS 'sin correo'?", - "amazon_custom_img": "¿Usar imagen personalizada de Amazon 'sin entrega'?", - "ups_custom_img": "¿Usar imagen personalizada de UPS 'sin entrega'?", - "walmart_custom_img": "¿Usar imagen personalizada de Walmart 'sin entrega'?", - "fedex_custom_img": "¿Usar imagen personalizada de FedEx 'sin entrega'?", - "generic_custom_img": "¿Usar imagen personalizada genérica 'sin entrega'?", - "generate_grid": "Crear cuadrícula de imágenes para modelos de visión LLM", - "allow_forwarded_emails": "Permitir correos electrónicos reenviados además del valor predeterminado de un servicio (p. ej., no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "¿Incluir marcador de posición sin imagen de USPS en el GIF?" - }, - "description": "Finalice la configuración personalizando lo siguiente en función de la estructura de su correo electrónico y la instalación de Home Assistant.\n\nPara obtener detalles sobre las opciones de [integración de correo y paquetes](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), revise la [sección de configuración, plantillas y automatizaciones](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) en GitHub.\n\nSi utiliza correos electrónicos reenviados de Amazon, separe cada dirección con una coma o ingrese (ninguno) para borrar esta configuración.", - "title": "Correo y paquetes (Paso 2 de 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Ruta a la imagen personalizada: (es decir: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Ingrese la ruta y el nombre del archivo de su imagen personalizada de no correo a continuación.\n\nEjemplo: images/custom_nomail.gif", - "title": "Correo y Paquetes (Paso 3 de 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Dominio de Amazon", - "amazon_fwds": "Amazon reenvió direcciones de correo electrónico", - "amazon_days": "Días atrás para verificar los correos electrónicos de Amazon" - }, - "description": "Por favor, introduzca el dominio desde el cual Amazon envía correos electrónicos (es decir: amazon.com o amazon.de)\n\nSi utiliza correos electrónicos reenviados por Amazon, separe cada dirección con una coma o introduzca (ninguno) para borrar esta configuración.\n\nNota: si configuró un encabezado de reenvío en el paso anterior, el campo de dirección de reenvío no aparecerá — el encabezado gestiona la coincidencia de correos electrónicos de Amazon automáticamente.", - "title": "Configuración de Amazon" - }, - "config_storage": { - "data": { - "storage": "Directorio para almacenar imágenes" - }, - "description": "Por favor, introduzca el directorio en el que le gustaría almacenar sus imágenes. \nEl valor predeterminado se rellena automáticamente.", - "title": "Ubicación de almacenamiento de imágenes" - }, - "reconfig_storage": { - "data": { - "storage": "Directorio para almacenar imágenes" - }, - "description": "Por favor, introduzca el directorio en el que le gustaría almacenar sus imágenes. \nEl valor predeterminado se rellena automáticamente.", - "title": "Ubicación de almacenamiento de imágenes" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Cabecera de reenvío (p. ej. X-SimpleLogin-Original-From)", - "forwarded_emails": "Direcciones de correo electrónico reenviadas" - }, - "data_description": { - "forwarding_header": "Si su servicio de reenvío incluye el remitente original en una cabecera, ingrese el nombre de la cabecera aquí. Esto tiene prioridad sobre la lista de direcciones a continuación. Ingrese '(none)' o deje en blanco para usar la lista de direcciones en su lugar.", - "forwarded_emails": "Lista separada por comas de las direcciones de reenvío de las que llegarán los mensajes. Deje '(none)' si usa la cabecera de reenvío de arriba." - }, - "description": "Elija cómo hacer coincidir los correos electrónicos reenviados.\n\n**Opción 1 — Cabecera de reenvío**: Si su servicio (p. ej. SimpleLogin) incluye el remitente original en una cabecera personalizada, ingrese ese nombre de cabecera. Mail and Packages lo usará para hacer coincidir las direcciones de los transportistas automáticamente.\n\n**Opción 2 — Lista de direcciones**: Ingrese las direcciones de reenvío que su servicio asignó a cada transportista, separadas por comas.", - "title": "Configuración de correos electrónicos reenviados" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Cabecera de reenvío (p. ej. X-SimpleLogin-Original-From)", - "forwarded_emails": "Direcciones de correo electrónico reenviadas" - }, - "data_description": { - "forwarding_header": "Si su servicio de reenvío incluye el remitente original en una cabecera, ingrese el nombre de la cabecera aquí. Esto tiene prioridad sobre la lista de direcciones a continuación. Ingrese '(none)' o deje en blanco para usar la lista de direcciones en su lugar.", - "forwarded_emails": "Lista separada por comas de las direcciones de reenvío de las que llegarán los mensajes. Deje '(none)' si usa la cabecera de reenvío de arriba." - }, - "description": "Elija cómo hacer coincidir los correos electrónicos reenviados.\n\n**Opción 1 — Cabecera de reenvío**: Si su servicio (p. ej. SimpleLogin) incluye el remitente original en una cabecera personalizada, ingrese ese nombre de cabecera. Mail and Packages lo usará para hacer coincidir las direcciones de los transportistas automáticamente.\n\n**Opción 2 — Lista de direcciones**: Ingrese las direcciones de reenvío que su servicio asignó a cada transportista, separadas por comas.", - "title": "Configuración de correos electrónicos reenviados" - }, - "imap_config": { - "description": "Por favor, ingrese la información de conexión de su servidor de correo.", - "data": { - "host": "Anfitrión", - "password": "Contraseña", - "port": "Puerto", - "username": "Nombre de usuario", - "verify_ssl": "Verificar Certificado SSL", - "imap_security": "Seguridad IMAP" - }, - "title": "Correo y paquetes (Paso 1 de 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Anfitrión", - "password": "Contraseña", - "port": "Puerto", - "username": "Nombre de usuario", - "imap_security": "Seguridad IMAP", - "verify_ssl": "Verificar Certificado SSL" - }, - "description": "Por favor, ingrese la información de conexión de su servidor de correo.", - "title": "Correo y paquetes (Paso 1 de 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Error de autenticación de Mail and Packages", - "fix_flow": { - "step": { - "confirm": { - "title": "Volver a autenticar el servidor de correo", - "description": "Error al autenticar en su servidor de correo IMAP. Haga clic en enviar para iniciar el flujo de reautenticación." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Correo y paquetes (Paso 2 de 2)", - "description": "Finalice la configuración personalizando lo siguiente en función de la estructura de su correo electrónico y la instalación de Home Assistant.\n\nPara obtener detalles sobre las opciones de [integración de correo y paquetes](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), revise la [sección de configuración, plantillas y automatizaciones](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) en GitHub.\n\nSi utiliza correos electrónicos reenviados de Amazon, separe cada dirección con una coma o ingrese (ninguno) para borrar esta configuración.", - "data": { - "folder": "Carpeta de correo", - "resources": "Lista de Sensores", - "scan_interval": "Intervalo de escaneo (minutos)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Ruta de la imagen", - "gif_duration": "Duración de la imagen (segundos)", - "image_security": "Nombre de archivo de imagen aleatoria", - "imap_timeout": "Tiempo en segundos antes del tiempo de espera de la conexión (segundos, mínimo 10)", - "generate_mp4": "Crea mp4 a partir de imágenes", - "allow_external": "Crea imagen para aplicaciones de notificación", - "usps_placeholder": "¿Incluir marcador de posición sin imagen de USPS en el GIF?", - "custom_img": "¿Usar imagen personalizada de USPS 'sin correo'?", - "amazon_custom_img": "¿Usar imagen personalizada de Amazon 'sin entrega'?", - "ups_custom_img": "¿Usar imagen personalizada de UPS 'sin entrega'?", - "walmart_custom_img": "¿Usar imagen personalizada de Walmart 'sin entrega'?", - "fedex_custom_img": "¿Usar imagen personalizada de FedEx 'sin entrega'?", - "generic_custom_img": "¿Usar imagen personalizada genérica 'sin entrega'?", - "generate_grid": "Crear cuadrícula de imágenes para modelos de visión LLM", - "allow_forwarded_emails": "Permitir correos electrónicos reenviados además del valor predeterminado de un servicio (p. ej., no-reply@usps.com)" - } - }, - "options_3": { - "title": "Correo y Paquetes (Paso 3 de 3)", - "description": "Ingrese la ruta y el nombre del archivo de su imagen personalizada de no correo a continuación.\n\nEjemplo: images/custom_nomail.gif", - "data": { - "custom_img_file": "Ruta a la imagen personalizada: (es decir: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Configuración de Amazon", - "description": "Por favor, introduzca el dominio desde el cual Amazon envía correos electrónicos (es decir: amazon.com o amazon.de)\n\nSi utiliza correos electrónicos reenviados por Amazon, separe cada dirección con una coma o introduzca (ninguno) para borrar esta configuración.\n\nNota: si configuró un encabezado de reenvío en el paso anterior, el campo de dirección de reenvío no aparecerá — el encabezado gestiona la coincidencia de correos electrónicos de Amazon automáticamente.", - "data": { - "amazon_domain": "Dominio de Amazon", - "amazon_fwds": "Amazon reenvió direcciones de correo electrónico", - "amazon_days": "Días atrás para verificar los correos electrónicos de Amazon" - } - }, - "options_storage": { - "title": "Ubicación de almacenamiento de imágenes", - "description": "Por favor, introduzca el directorio en el que le gustaría almacenar sus imágenes. \nEl valor predeterminado se rellena automáticamente.", - "data": { - "storage": "Directorio para almacenar imágenes" - } - }, - "options_forwarded_emails": { - "title": "Configuración de correos electrónicos reenviados", - "description": "Elija cómo hacer coincidir los correos electrónicos reenviados.\n\n**Opción 1 — Cabecera de reenvío**: Si su servicio (p. ej. SimpleLogin) incluye el remitente original en una cabecera personalizada, ingrese ese nombre de cabecera. Mail and Packages lo usará para hacer coincidir las direcciones de los transportistas automáticamente.\n\n**Opción 2 — Lista de direcciones**: Ingrese las direcciones de reenvío que su servicio asignó a cada transportista, separadas por comas.", - "data": { - "forwarding_header": "Cabecera de reenvío (p. ej. X-SimpleLogin-Original-From)", - "forwarded_emails": "Direcciones de correo electrónico reenviadas" - }, - "data_description": { - "forwarding_header": "Si su servicio de reenvío incluye el remitente original en una cabecera, ingrese el nombre de la cabecera aquí. Esto tiene prioridad sobre la lista de direcciones a continuación. Ingrese '(none)' o deje en blanco para usar la lista de direcciones en su lugar.", - "forwarded_emails": "Lista separada por comas de las direcciones de reenvío de las que llegarán los mensajes. Deje '(none)' si usa la cabecera de reenvío de arriba." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/es_419.json b/custom_components/mail_and_packages/translations/es_419.json deleted file mode 100644 index d5809b6..0000000 --- a/custom_components/mail_and_packages/translations/es_419.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Solo se permite una única configuración de correo y paquetes.", - "reconfigure_successful": "Reconfiguración Exitosa", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "No se puede conectar o iniciar sesión en el servidor de correo. Por favor, consulte el registro para más detalles.", - "invalid_path": "Guarde las imágenes en otro directorio.", - "ffmpeg_not_found": "Generar MP4 requiere ffmpeg", - "amazon_domain": "Dirección de correo electrónico de reenvío no válida.", - "file_not_found": "Archivo de imagen no encontrado", - "invalid_email_format": "Formato de dirección de correo electrónico inválido.", - "missing_forwarded_emails": "Faltan direcciones de correo electrónico reenviadas. Puede introducir '(none)' para borrar esta configuración.", - "path_not_found": "Directorio no encontrado", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Carpeta de correo", - "resources": "Lista de Sensores", - "scan_interval": "Intervalo de escaneo (minutos)", - "image_path": "Ruta de la imagen", - "gif_duration": "Duración de la imagen (segundos)", - "image_security": "Nombre de archivo de imagen aleatoria", - "imap_timeout": "Tiempo en segundos antes del tiempo de espera de la conexión (segundos, mínimo 10)", - "generate_mp4": "Crea mp4 a partir de imágenes", - "allow_external": "Crea imagen para aplicaciones de notificación", - "custom_img": "¿Usar imagen personalizada de USPS 'sin correo'?", - "amazon_custom_img": "¿Usar imagen personalizada de Amazon 'sin entrega'?", - "ups_custom_img": "¿Usar imagen personalizada de UPS 'sin entrega'?", - "walmart_custom_img": "¿Usar imagen personalizada de Walmart 'sin entrega'?", - "fedex_custom_img": "¿Usar imagen personalizada de FedEx 'sin entrega'?", - "generic_custom_img": "¿Usar imagen personalizada genérica 'sin entrega'?", - "generate_grid": "Crear cuadrícula de imágenes para modelos de visión LLM", - "allow_forwarded_emails": "Permitir correos electrónicos reenviados además del valor predeterminado de un servicio (p. ej., no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "¿Incluir marcador de posición sin imagen de USPS en el GIF?" - }, - "description": "Termine la configuración personalizando lo siguiente según su estructura de correo electrónico y la instalación de Home Assistant. \n\n Para obtener detalles sobre las opciones [Integración de correo y paquetes] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) revise las [configuración, plantillas , y la sección de automatizaciones] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) en GitHub.", - "title": "Correo y paquetes (Paso 2 de 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Ruta a la imagen personalizada: (es decir: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Ingrese la ruta y el nombre del archivo de su imagen personalizada de no correo a continuación.\n\nEjemplo: images/custom_nomail.gif", - "title": "Correo y paquetes (Paso 3 de 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Dominio de Amazon", - "amazon_fwds": "Amazon reenvió direcciones de correo electrónico", - "amazon_days": "Días atrás para verificar los correos electrónicos de Amazon" - }, - "description": "Por favor, ingrese el dominio desde el cual Amazon envía correos electrónicos (es decir: amazon.com o amazon.de)\n\nSi utiliza correos electrónicos reenviados por Amazon, separe cada dirección con una coma o ingrese (ninguno) para borrar esta configuración.\n\nNota: si configuró un encabezado de reenvío en el paso anterior, el campo de dirección de reenvío no aparecerá — el encabezado gestiona la coincidencia de correos electrónicos de Amazon automáticamente.", - "title": "Configuración de Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Carpeta de correo", - "scan_interval": "Intervalo de escaneo (minutos)", - "image_path": "Ruta de la imagen", - "gif_duration": "Duración de la imagen (segundos)", - "image_security": "Nombre de archivo de imagen aleatoria", - "generate_mp4": "Crea mp4 a partir de imágenes", - "resources": "Lista de Sensores", - "imap_timeout": "Tiempo en segundos antes del tiempo de espera de la conexión (segundos, mínimo 10)", - "allow_external": "Crea imagen para aplicaciones de notificación", - "custom_img": "¿Usar imagen personalizada de USPS 'sin correo'?", - "amazon_custom_img": "¿Usar imagen personalizada de Amazon 'sin entrega'?", - "ups_custom_img": "¿Usar imagen personalizada de UPS 'sin entrega'?", - "walmart_custom_img": "¿Usar imagen personalizada de Walmart 'sin entrega'?", - "fedex_custom_img": "¿Usar imagen personalizada de FedEx 'sin entrega'?", - "generic_custom_img": "¿Usar imagen personalizada genérica 'sin entrega'?", - "generate_grid": "Crear cuadrícula de imágenes para modelos de visión LLM", - "allow_forwarded_emails": "Permitir correos electrónicos reenviados además del valor predeterminado de un servicio (p. ej., no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "¿Incluir marcador de posición sin imagen de USPS en el GIF?" - }, - "description": "Finalice la configuración personalizando lo siguiente en función de la estructura de su correo electrónico y la instalación de Home Assistant.\n\nPara obtener detalles sobre las opciones de [integración de correo y paquetes](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), revise la [sección de configuración, plantillas y automatizaciones](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) en GitHub.\n\nSi utiliza correos electrónicos reenviados de Amazon, separe cada dirección con una coma o ingrese (ninguno) para borrar esta configuración.", - "title": "Correo y paquetes (Paso 2 de 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Ruta a la imagen personalizada: (es decir: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Ingrese la ruta y el nombre del archivo de su imagen personalizada de no correo a continuación.\n\nEjemplo: images/custom_nomail.gif", - "title": "Correo y paquetes (Paso 3 de 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Dominio de Amazon", - "amazon_fwds": "Amazon reenvió direcciones de correo electrónico", - "amazon_days": "Días atrás para verificar los correos electrónicos de Amazon" - }, - "description": "Por favor, ingrese el dominio desde el cual Amazon envía correos electrónicos (es decir: amazon.com o amazon.de)\n\nSi utiliza correos electrónicos reenviados por Amazon, separe cada dirección con una coma o ingrese (ninguno) para borrar esta configuración.\n\nNota: si configuró un encabezado de reenvío en el paso anterior, el campo de dirección de reenvío no aparecerá — el encabezado gestiona la coincidencia de correos electrónicos de Amazon automáticamente.", - "title": "Configuración de Amazon" - }, - "config_storage": { - "data": { - "storage": "Directorio para almacenar imágenes" - }, - "description": "Por favor, ingrese el directorio en el que le gustaría almacenar sus imágenes.\nEl predeterminado se llena automáticamente.", - "title": "Ubicación de almacenamiento de imágenes" - }, - "reconfig_storage": { - "data": { - "storage": "Directorio para almacenar imágenes" - }, - "description": "Por favor, ingrese el directorio en el que le gustaría almacenar sus imágenes.\nEl predeterminado se llena automáticamente.", - "title": "Ubicación de almacenamiento de imágenes" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Cabecera de reenvío (p. ej. X-SimpleLogin-Original-From)", - "forwarded_emails": "Direcciones de correo electrónico reenviadas" - }, - "data_description": { - "forwarding_header": "Si su servicio de reenvío incluye el remitente original en una cabecera, ingrese el nombre de la cabecera aquí. Esto tiene prioridad sobre la lista de direcciones a continuación. Ingrese '(none)' o deje en blanco para usar la lista de direcciones en su lugar.", - "forwarded_emails": "Lista separada por comas de las direcciones de reenvío de las que llegarán los mensajes. Deje '(none)' si usa la cabecera de reenvío de arriba." - }, - "description": "Elija cómo hacer coincidir los correos electrónicos reenviados.\n\n**Opción 1 — Cabecera de reenvío**: Si su servicio (p. ej. SimpleLogin) incluye el remitente original en una cabecera personalizada, ingrese ese nombre de cabecera. Mail and Packages lo usará para hacer coincidir las direcciones de los transportistas automáticamente.\n\n**Opción 2 — Lista de direcciones**: Ingrese las direcciones de reenvío que su servicio asignó a cada transportista, separadas por comas.", - "title": "Configuración de correos electrónicos reenviados" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Cabecera de reenvío (p. ej. X-SimpleLogin-Original-From)", - "forwarded_emails": "Direcciones de correo electrónico reenviadas" - }, - "data_description": { - "forwarding_header": "Si su servicio de reenvío incluye el remitente original en una cabecera, ingrese el nombre de la cabecera aquí. Esto tiene prioridad sobre la lista de direcciones a continuación. Ingrese '(none)' o deje en blanco para usar la lista de direcciones en su lugar.", - "forwarded_emails": "Lista separada por comas de las direcciones de reenvío de las que llegarán los mensajes. Deje '(none)' si usa la cabecera de reenvío de arriba." - }, - "description": "Elija cómo hacer coincidir los correos electrónicos reenviados.\n\n**Opción 1 — Cabecera de reenvío**: Si su servicio (p. ej. SimpleLogin) incluye el remitente original en una cabecera personalizada, ingrese ese nombre de cabecera. Mail and Packages lo usará para hacer coincidir las direcciones de los transportistas automáticamente.\n\n**Opción 2 — Lista de direcciones**: Ingrese las direcciones de reenvío que su servicio asignó a cada transportista, separadas por comas.", - "title": "Configuración de correos electrónicos reenviados" - }, - "imap_config": { - "description": "Por favor, ingrese la información de conexión de su servidor de correo.", - "data": { - "host": "Anfitrión", - "password": "Contraseña", - "port": "Puerto", - "username": "Nombre de usuario", - "verify_ssl": "Verificar Certificado SSL", - "imap_security": "Seguridad IMAP" - }, - "title": "Correo y paquetes (Paso 1 de 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Anfitrión", - "password": "Contraseña", - "port": "Puerto", - "username": "Nombre de usuario", - "imap_security": "Seguridad IMAP", - "verify_ssl": "Verificar Certificado SSL" - }, - "description": "Por favor, ingrese la información de conexión de su servidor de correo.", - "title": "Correo y paquetes (Paso 1 de 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Error de autenticación de Mail and Packages", - "fix_flow": { - "step": { - "confirm": { - "title": "Volver a autenticar el servidor de correo", - "description": "Error al autenticar en su servidor de correo IMAP. Haga clic en enviar para iniciar el flujo de reautenticación." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Correo y paquetes (Paso 2 de 2)", - "description": "Finalice la configuración personalizando lo siguiente en función de la estructura de su correo electrónico y la instalación de Home Assistant.\n\nPara obtener detalles sobre las opciones de [integración de correo y paquetes](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), revise la [sección de configuración, plantillas y automatizaciones](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) en GitHub.\n\nSi utiliza correos electrónicos reenviados de Amazon, separe cada dirección con una coma o ingrese (ninguno) para borrar esta configuración.", - "data": { - "folder": "Carpeta de correo", - "resources": "Lista de Sensores", - "scan_interval": "Intervalo de escaneo (minutos)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Ruta de la imagen", - "gif_duration": "Duración de la imagen (segundos)", - "image_security": "Nombre de archivo de imagen aleatoria", - "imap_timeout": "Tiempo en segundos antes del tiempo de espera de la conexión (segundos, mínimo 10)", - "generate_mp4": "Crea mp4 a partir de imágenes", - "allow_external": "Crea imagen para aplicaciones de notificación", - "usps_placeholder": "¿Incluir marcador de posición sin imagen de USPS en el GIF?", - "custom_img": "¿Usar imagen personalizada de USPS 'sin correo'?", - "amazon_custom_img": "¿Usar imagen personalizada de Amazon 'sin entrega'?", - "ups_custom_img": "¿Usar imagen personalizada de UPS 'sin entrega'?", - "walmart_custom_img": "¿Usar imagen personalizada de Walmart 'sin entrega'?", - "fedex_custom_img": "¿Usar imagen personalizada de FedEx 'sin entrega'?", - "generic_custom_img": "¿Usar imagen personalizada genérica 'sin entrega'?", - "generate_grid": "Crear cuadrícula de imágenes para modelos de visión LLM", - "allow_forwarded_emails": "Permitir correos electrónicos reenviados además del valor predeterminado de un servicio (p. ej., no-reply@usps.com)" - } - }, - "options_3": { - "title": "Correo y paquetes (Paso 3 de 3)", - "description": "Ingrese la ruta y el nombre del archivo de su imagen personalizada de no correo a continuación.\n\nEjemplo: images/custom_nomail.gif", - "data": { - "custom_img_file": "Ruta a la imagen personalizada: (es decir: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Configuración de Amazon", - "description": "Por favor, ingrese el dominio desde el cual Amazon envía correos electrónicos (es decir: amazon.com o amazon.de)\n\nSi utiliza correos electrónicos reenviados por Amazon, separe cada dirección con una coma o ingrese (ninguno) para borrar esta configuración.\n\nNota: si configuró un encabezado de reenvío en el paso anterior, el campo de dirección de reenvío no aparecerá — el encabezado gestiona la coincidencia de correos electrónicos de Amazon automáticamente.", - "data": { - "amazon_domain": "Dominio de Amazon", - "amazon_fwds": "Amazon reenvió direcciones de correo electrónico", - "amazon_days": "Días atrás para verificar los correos electrónicos de Amazon" - } - }, - "options_storage": { - "title": "Ubicación de almacenamiento de imágenes", - "description": "Por favor, ingrese el directorio en el que le gustaría almacenar sus imágenes.\nEl predeterminado se llena automáticamente.", - "data": { - "storage": "Directorio para almacenar imágenes" - } - }, - "options_forwarded_emails": { - "title": "Configuración de correos electrónicos reenviados", - "description": "Elija cómo hacer coincidir los correos electrónicos reenviados.\n\n**Opción 1 — Cabecera de reenvío**: Si su servicio (p. ej. SimpleLogin) incluye el remitente original en una cabecera personalizada, ingrese ese nombre de cabecera. Mail and Packages lo usará para hacer coincidir las direcciones de los transportistas automáticamente.\n\n**Opción 2 — Lista de direcciones**: Ingrese las direcciones de reenvío que su servicio asignó a cada transportista, separadas por comas.", - "data": { - "forwarding_header": "Cabecera de reenvío (p. ej. X-SimpleLogin-Original-From)", - "forwarded_emails": "Direcciones de correo electrónico reenviadas" - }, - "data_description": { - "forwarding_header": "Si su servicio de reenvío incluye el remitente original en una cabecera, ingrese el nombre de la cabecera aquí. Esto tiene prioridad sobre la lista de direcciones a continuación. Ingrese '(none)' o deje en blanco para usar la lista de direcciones en su lugar.", - "forwarded_emails": "Lista separada por comas de las direcciones de reenvío de las que llegarán los mensajes. Deje '(none)' si usa la cabecera de reenvío de arriba." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/fi.json b/custom_components/mail_and_packages/translations/fi.json deleted file mode 100644 index adc7825..0000000 --- a/custom_components/mail_and_packages/translations/fi.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Vain yksi posti- ja pakettien kokoonpano on sallittu.", - "reconfigure_successful": "Uudelleenmääritys onnistui", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Ei voida muodostaa yhteyttä tai kirjautua sisään postipalvelimeen. Tarkista lokista yksityiskohdat.", - "invalid_path": "Tallenna kuvat toiseen hakemistoon.", - "ffmpeg_not_found": "MP4:n luominen vaatii ffmpeg:n", - "amazon_domain": "Virheellinen edelleenlähetettävä sähköpostiosoite.", - "file_not_found": "Kuvatiedostoa ei löydy", - "invalid_email_format": "Virheellinen sähköpostiosoitteen muoto.", - "missing_forwarded_emails": "Puuttuvat edelleenlähetetyt sähköpostiosoitteet. Voit syöttää '(none)' tyhjentääksesi tämän asetuksen.", - "path_not_found": "Hakemistoa ei löydy", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Mail-kansio", - "resources": "Anturiluettelo", - "scan_interval": "Skannausväli (minuutit)", - "image_path": "Kuvan polku", - "gif_duration": "Kuvan kesto (sekunteina)", - "image_security": "Satunnainen kuvatiedostonimi", - "imap_timeout": "Aika sekunneissa ennen yhteyden aikakatkaisua (sekuntia, vähintään 10)", - "generate_mp4": "Luo mp4 kuvista", - "allow_external": "Luo kuva ilmoitussovelluksille", - "custom_img": "Käytä mukautettua USPS 'ei postia' kuvaa?", - "amazon_custom_img": "Käytä mukautettua Amazon 'ei toimituksia' kuvaa?", - "ups_custom_img": "Käytä mukautettua UPS 'ei toimituksia' kuvaa?", - "walmart_custom_img": "Käytä mukautettua Walmart 'ei toimituksia' kuvaa?", - "fedex_custom_img": "Käytä mukautettua FedEx 'ei toimituksia' kuvaa?", - "generic_custom_img": "Käytä mukautettua yleistä 'ei toimituksia' kuvaa?", - "generate_grid": "Luo kuvaverkko LLM-näkömalleille", - "allow_forwarded_emails": "Salli edelleenlähetetyt sähköpostit palvelun oletusarvon lisäksi (esim. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Sisällytä USPS:n ei-kuvaa -paikkamerkki GIF-tiedostoon?" - }, - "description": "Viimeistele kokoonpano mukauttamalla seuraava sähköpostirakenteen ja Home Assistant -asennuksen perusteella. \n\n Lisätietoja [Posti ja paketit-integroinnista] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) -asetuksista on [kokoonpano, mallit , ja automaatiot-osio] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) GitHubissa.", - "title": "Posti ja paketit (vaihe 2/2)" - }, - "config_3": { - "data": { - "custom_img_file": "Polku mukautettuun kuvaan: (esim: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Syötä alla olevaan kenttään polku ja tiedostonimi mukautetulle ei-postia -kuvallesi.\n\nEsimerkki: kuvat/mukautettu_eipostia.gif", - "title": "Posti ja paketit (vaihe 3/3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazonin verkkotunnus", - "amazon_fwds": "Amazon välitti sähköpostiosoitteet", - "amazon_days": "Päiviä, joiden aikana tarkistetaan Amazonin sähköpostit" - }, - "description": "Anna domaini, josta Amazon lähettää sähköposteja (esim: amazon.com tai amazon.de)\n\nJos käytät Amazonin välittämiä sähköposteja, erottele jokainen osoite pilkulla tai kirjoita (none) tyhjentääksesi tämän asetuksen.\n\nHuomio: jos määritit edelleenlähetyksen otsikon edellisessä vaiheessa, edelleenlähetysosoite-kenttä ei näy — otsikko käsittelee Amazon-sähköpostien täsmäytyksen automaattisesti.", - "title": "Amazonin asetukset" - }, - "reconfig_2": { - "data": { - "folder": "Mail-kansio", - "scan_interval": "Skannausväli (minuutit)", - "image_path": "Kuvan polku", - "gif_duration": "Kuvan kesto (sekunteina)", - "image_security": "Satunnainen kuvatiedostonimi", - "generate_mp4": "Luo mp4 kuvista", - "resources": "Anturiluettelo", - "imap_timeout": "Aika sekunneissa ennen yhteyden aikakatkaisua (sekuntia, vähintään 10)", - "allow_external": "Luo kuva ilmoitussovelluksille", - "custom_img": "Käytä mukautettua USPS 'ei postia' kuvaa?", - "amazon_custom_img": "Käytä mukautettua Amazon 'ei toimituksia' kuvaa?", - "ups_custom_img": "Käytä mukautettua UPS 'ei toimituksia' kuvaa?", - "walmart_custom_img": "Käytä mukautettua Walmart 'ei toimituksia' kuvaa?", - "fedex_custom_img": "Käytä mukautettua FedEx 'ei toimituksia' kuvaa?", - "generic_custom_img": "Käytä mukautettua yleistä 'ei toimituksia' kuvaa?", - "generate_grid": "Luo kuvaverkko LLM-näkömalleille", - "allow_forwarded_emails": "Salli edelleenlähetetyt sähköpostit palvelun oletusarvon lisäksi (esim. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Sisällytä USPS:n ei-kuvaa -paikkamerkki GIF-tiedostoon?" - }, - "description": "Suorita määritys loppuun mukauttamalla seuraavat sähköpostirakenteeseesi ja Home Assistant -asennukseesi perustuen.\n\nLisätietoja [Mail and Packages -integraation](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) vaihtoehdoista löydät GitHubista [määritys-, mallit- ja automaatiot-osiossa](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration).\n\nJos käytät Amazonin välittämiä sähköposteja, erottele jokainen osoite pilkulla tai syötä (none) tyhjentääksesi tämän asetuksen.", - "title": "Posti ja paketit (vaihe 2/2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Polku mukautettuun kuvaan: (esim.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Syötä alla olevaan kenttään polku ja tiedostonimi mukautetulle ei-postia -kuvallesi.\n\nEsimerkki: kuvat/mukautettu_eipostia.gif", - "title": "Posti ja paketit (vaihe 3/3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazonin verkkotunnus", - "amazon_fwds": "Amazon välitti sähköpostiosoitteet", - "amazon_days": "Päiviä, joiden aikana tarkistetaan Amazonin sähköpostit" - }, - "description": "Anna domaini, josta Amazon lähettää sähköposteja (esim: amazon.com tai amazon.de)\n\nJos käytät Amazonin välittämiä sähköposteja, erottele jokainen osoite pilkulla tai kirjoita (none) tyhjentääksesi tämän asetuksen.\n\nHuomio: jos määritit edelleenlähetyksen otsikon edellisessä vaiheessa, edelleenlähetysosoite-kenttä ei näy — otsikko käsittelee Amazon-sähköpostien täsmäytyksen automaattisesti.", - "title": "Amazonin asetukset" - }, - "config_storage": { - "data": { - "storage": "Hakemisto kuvien tallentamiseen" - }, - "description": "Anna hakemisto, johon haluat kuviesi tallentuvan.\nOletusarvo täytetään automaattisesti.", - "title": "Kuvien tallennuspaikka" - }, - "reconfig_storage": { - "data": { - "storage": "Hakemisto kuvien tallentamiseen" - }, - "description": "Anna hakemisto, johon haluat kuviesi tallentuvan.\nOletusarvo täytetään automaattisesti.", - "title": "Kuvien tallennuspaikka" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Välitysotsake (esim. X-SimpleLogin-Original-From)", - "forwarded_emails": "Edelleenlähetetyt sähköpostiosoitteet" - }, - "data_description": { - "forwarding_header": "Jos välityspalvelusi sisältää alkuperäisen lähettäjän otsikossa, syötä otsikon nimi tähän. Tämä ohittaa alla olevan osoitelistan. Syötä '(none)' tai jätä tyhjäksi käyttääksesi osoitelistaa.", - "forwarded_emails": "Pilkuilla eroteltu lista välitysosoitteista, joista viestit saapuvat. Jätä '(none)', jos käytät yllä olevaa välitysotsiketta." - }, - "description": "Valitse, miten välitetyt sähköpostit yhdistetään.\n\n**Vaihtoehto 1 — Välitysotsake**: Jos palvelusi (esim. SimpleLogin) sisältää alkuperäisen lähettäjän mukautetussa otsakkeessa, syötä kyseinen otsakkeen nimi. Mail and Packages käyttää sitä kuriiriosoitteiden automaattiseen yhdistämiseen.\n\n**Vaihtoehto 2 — Osoitelista**: Syötä välitysosoitteet, jotka palvelusi on määrittänyt kullekin kuriirille, pilkuilla eroteltuna.", - "title": "Välitettyjen sähköpostien asetukset" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Välitysotsake (esim. X-SimpleLogin-Original-From)", - "forwarded_emails": "Edelleenlähetetyt sähköpostiosoitteet" - }, - "data_description": { - "forwarding_header": "Jos välityspalvelusi sisältää alkuperäisen lähettäjän otsikossa, syötä otsikon nimi tähän. Tämä ohittaa alla olevan osoitelistan. Syötä '(none)' tai jätä tyhjäksi käyttääksesi osoitelistaa.", - "forwarded_emails": "Pilkuilla eroteltu lista välitysosoitteista, joista viestit saapuvat. Jätä '(none)', jos käytät yllä olevaa välitysotsiketta." - }, - "description": "Valitse, miten välitetyt sähköpostit yhdistetään.\n\n**Vaihtoehto 1 — Välitysotsake**: Jos palvelusi (esim. SimpleLogin) sisältää alkuperäisen lähettäjän mukautetussa otsakkeessa, syötä kyseinen otsakkeen nimi. Mail and Packages käyttää sitä kuriiriosoitteiden automaattiseen yhdistämiseen.\n\n**Vaihtoehto 2 — Osoitelista**: Syötä välitysosoitteet, jotka palvelusi on määrittänyt kullekin kuriirille, pilkuilla eroteltuna.", - "title": "Välitettyjen sähköpostien asetukset" - }, - "imap_config": { - "description": "Anna postipalvelimesi yhteystiedot.", - "data": { - "host": "isäntä", - "password": "Salasana", - "port": "portti", - "username": "Käyttäjätunnus", - "verify_ssl": "Varmenna SSL-sertifikaatti", - "imap_security": "IMAP-tietoturva" - }, - "title": "Posti ja paketit (vaihe 1/2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "isäntä", - "password": "Salasana", - "port": "portti", - "username": "Käyttäjätunnus", - "imap_security": "IMAP-tietoturva", - "verify_ssl": "Varmenna SSL-sertifikaatti" - }, - "description": "Anna postipalvelimesi yhteystiedot.", - "title": "Posti ja paketit (vaihe 1/2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages -autentikointi epäonnistui", - "fix_flow": { - "step": { - "confirm": { - "title": "Tunnistaudu uudelleen sähköpostipalvelimeen", - "description": "Autentikointi IMAP-sähköpostipalvelimeesi epäonnistui. Napsauta Lähetä aloittaaksesi uudelleentunnistautumisen." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Posti ja paketit (vaihe 2/2)", - "description": "Suorita määritys loppuun mukauttamalla seuraavat sähköpostirakenteeseesi ja Home Assistant -asennukseesi perustuen.\n\nLisätietoja [Mail and Packages -integraation](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) vaihtoehdoista löydät GitHubista [määritys-, mallit- ja automaatiot-osiossa](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration).\n\nJos käytät Amazonin välittämiä sähköposteja, erottele jokainen osoite pilkulla tai syötä (none) tyhjentääksesi tämän asetuksen.", - "data": { - "folder": "Mail-kansio", - "resources": "Anturiluettelo", - "scan_interval": "Skannausväli (minuutit)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Kuvan polku", - "gif_duration": "Kuvan kesto (sekunteina)", - "image_security": "Satunnainen kuvatiedostonimi", - "imap_timeout": "Aika sekunneissa ennen yhteyden aikakatkaisua (sekuntia, vähintään 10)", - "generate_mp4": "Luo mp4 kuvista", - "allow_external": "Luo kuva ilmoitussovelluksille", - "usps_placeholder": "Sisällytä USPS:n ei-kuvaa -paikkamerkki GIF-tiedostoon?", - "custom_img": "Käytä mukautettua USPS 'ei postia' kuvaa?", - "amazon_custom_img": "Käytä mukautettua Amazon 'ei toimituksia' kuvaa?", - "ups_custom_img": "Käytä mukautettua UPS 'ei toimituksia' kuvaa?", - "walmart_custom_img": "Käytä mukautettua Walmart 'ei toimituksia' kuvaa?", - "fedex_custom_img": "Käytä mukautettua FedEx 'ei toimituksia' kuvaa?", - "generic_custom_img": "Käytä mukautettua yleistä 'ei toimituksia' kuvaa?", - "generate_grid": "Luo kuvaverkko LLM-näkömalleille", - "allow_forwarded_emails": "Salli edelleenlähetetyt sähköpostit palvelun oletusarvon lisäksi (esim. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Posti ja paketit (vaihe 3/3)", - "description": "Syötä alla olevaan kenttään polku ja tiedostonimi mukautetulle ei-postia -kuvallesi.\n\nEsimerkki: kuvat/mukautettu_eipostia.gif", - "data": { - "custom_img_file": "Polku mukautettuun kuvaan: (esim.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Amazonin asetukset", - "description": "Anna domaini, josta Amazon lähettää sähköposteja (esim: amazon.com tai amazon.de)\n\nJos käytät Amazonin välittämiä sähköposteja, erottele jokainen osoite pilkulla tai kirjoita (none) tyhjentääksesi tämän asetuksen.\n\nHuomio: jos määritit edelleenlähetyksen otsikon edellisessä vaiheessa, edelleenlähetysosoite-kenttä ei näy — otsikko käsittelee Amazon-sähköpostien täsmäytyksen automaattisesti.", - "data": { - "amazon_domain": "Amazonin verkkotunnus", - "amazon_fwds": "Amazon välitti sähköpostiosoitteet", - "amazon_days": "Päiviä, joiden aikana tarkistetaan Amazonin sähköpostit" - } - }, - "options_storage": { - "title": "Kuvien tallennuspaikka", - "description": "Anna hakemisto, johon haluat kuviesi tallentuvan.\nOletusarvo täytetään automaattisesti.", - "data": { - "storage": "Hakemisto kuvien tallentamiseen" - } - }, - "options_forwarded_emails": { - "title": "Välitettyjen sähköpostien asetukset", - "description": "Valitse, miten välitetyt sähköpostit yhdistetään.\n\n**Vaihtoehto 1 — Välitysotsake**: Jos palvelusi (esim. SimpleLogin) sisältää alkuperäisen lähettäjän mukautetussa otsakkeessa, syötä kyseinen otsakkeen nimi. Mail and Packages käyttää sitä kuriiriosoitteiden automaattiseen yhdistämiseen.\n\n**Vaihtoehto 2 — Osoitelista**: Syötä välitysosoitteet, jotka palvelusi on määrittänyt kullekin kuriirille, pilkuilla eroteltuna.", - "data": { - "forwarding_header": "Välitysotsake (esim. X-SimpleLogin-Original-From)", - "forwarded_emails": "Edelleenlähetetyt sähköpostiosoitteet" - }, - "data_description": { - "forwarding_header": "Jos välityspalvelusi sisältää alkuperäisen lähettäjän otsikossa, syötä otsikon nimi tähän. Tämä ohittaa alla olevan osoitelistan. Syötä '(none)' tai jätä tyhjäksi käyttääksesi osoitelistaa.", - "forwarded_emails": "Pilkuilla eroteltu lista välitysosoitteista, joista viestit saapuvat. Jätä '(none)', jos käytät yllä olevaa välitysotsiketta." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/fr.json b/custom_components/mail_and_packages/translations/fr.json deleted file mode 100644 index 10dcf04..0000000 --- a/custom_components/mail_and_packages/translations/fr.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Une seule configuration de Mail et Packages est autorisée.", - "reconfigure_successful": "Reconfiguration réussie", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Impossible de se connecter ou de se connecter au serveur de messagerie. Veuillez consulter le journal pour plus de détails.", - "invalid_path": "Veuillez stocker les images dans un autre répertoire.", - "ffmpeg_not_found": "Générer MP4 nécessite ffmpeg", - "amazon_domain": "Adresse e-mail de transfert invalide.", - "file_not_found": "Fichier image non trouvé", - "invalid_email_format": "Format d'adresse e-mail invalide.", - "missing_forwarded_emails": "Adresses e-mail de transfert manquantes. Vous pouvez entrer '(none)' pour effacer ce paramètre.", - "path_not_found": "Répertoire non trouvé", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Dossier de messagerie", - "resources": "Liste des capteurs", - "scan_interval": "Intervalle de numérisation (minutes)", - "image_path": "Chemin de l'image", - "gif_duration": "Durée de l'image (secondes)", - "image_security": "Nom de fichier d'image aléatoire", - "imap_timeout": "Temps en secondes avant la fin de la connexion (secondes, minimum 10)", - "generate_mp4": "Créer mp4 à partir d'images", - "allow_external": "Créer une image pour les applications de notification", - "custom_img": "Utiliser une image personnalisée USPS 'pas de courrier' ?", - "amazon_custom_img": "Utiliser une image personnalisée Amazon 'pas de livraison' ?", - "ups_custom_img": "Utiliser une image personnalisée UPS 'pas de livraison' ?", - "walmart_custom_img": "Utiliser une image personnalisée Walmart 'pas de livraison' ?", - "fedex_custom_img": "Utiliser une image personnalisée FedEx 'pas de livraison' ?", - "generic_custom_img": "Utiliser une image personnalisée générique 'pas de livraison' ?", - "generate_grid": "Créer une grille d'images pour les modèles de vision LLM", - "allow_forwarded_emails": "Autoriser les e-mails transférés en plus de la valeur par défaut d'un service (par ex. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Inclure l'image d'emplacement sans image USPS dans le GIF ?" - }, - "description": "Terminez la configuration en personnalisant les éléments suivants en fonction de votre structure de messagerie et de l'installation de Home Assistant. \n\n Pour plus de détails sur les [Intégration de messagerie et de packages] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), passez en revue les [configuration, modèles et section automatisations] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) sur GitHub.", - "title": "Courrier et colis (étape 2 sur 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Chemin vers l'image personnalisée : (par exemple : images/mon_image_personnalisée_pas_de_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Entrez le chemin et le nom de fichier de votre image personnalisée de non-réception de courrier ci-dessous.\n\nExemple : images/custom_nomail.gif", - "title": "Courrier et colis (Étape 3 sur 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Domaine Amazon", - "amazon_fwds": "Amazon a transmis des adresses e-mail", - "amazon_days": "Jours en arrière pour vérifier les emails Amazon" - }, - "description": "Veuillez entrer le domaine à partir duquel Amazon envoie des emails (par exemple : amazon.com ou amazon.de)\n\nSi vous utilisez des emails transférés par Amazon, veuillez séparer chaque adresse par une virgule ou entrer (aucun) pour effacer ce paramètre.\n\nRemarque : si vous avez configuré un en-tête de transfert dans l'étape précédente, le champ d'adresse de transfert n'apparaîtra pas — l'en-tête gère automatiquement la correspondance des e-mails Amazon.", - "title": "Paramètres Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Dossier de messagerie", - "scan_interval": "Intervalle de numérisation (minutes)", - "image_path": "Chemin de l'image", - "gif_duration": "Durée de l'image (secondes)", - "image_security": "Nom de fichier d'image aléatoire", - "generate_mp4": "Créer mp4 à partir d'images", - "resources": "Liste des capteurs", - "imap_timeout": "Temps en secondes avant la fin de la connexion (secondes, minimum 10)", - "allow_external": "Créer une image pour les applications de notification", - "custom_img": "Utiliser une image personnalisée USPS 'pas de courrier' ?", - "amazon_custom_img": "Utiliser une image personnalisée Amazon 'pas de livraison' ?", - "ups_custom_img": "Utiliser une image personnalisée UPS 'pas de livraison' ?", - "walmart_custom_img": "Utiliser une image personnalisée Walmart 'pas de livraison' ?", - "fedex_custom_img": "Utiliser une image personnalisée FedEx 'pas de livraison' ?", - "generic_custom_img": "Utiliser une image personnalisée générique 'pas de livraison' ?", - "generate_grid": "Créer une grille d'images pour les modèles de vision LLM", - "allow_forwarded_emails": "Autoriser les e-mails transférés en plus de la valeur par défaut d'un service (par ex. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Inclure l'image d'emplacement sans image USPS dans le GIF ?" - }, - "description": "Terminez la configuration en personnalisant ce qui suit en fonction de la structure de votre courrier électronique et de l'installation de Home Assistant.\n\nPour plus de détails sur les options d'[intégration de courrier et de colis](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), consultez la [section configuration, modèles et automatisations](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) sur GitHub.\n\nSi vous utilisez des courriels transférés par Amazon, veuillez séparer chaque adresse par une virgule ou entrer (aucun) pour effacer ce paramètre.", - "title": "Courrier et colis (étape 2 sur 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Chemin vers l'image personnalisée : (par exemple : images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Entrez le chemin et le nom de fichier de votre image personnalisée de non-réception de courrier ci-dessous.\n\nExemple : images/custom_nomail.gif", - "title": "Courrier et colis (Étape 3 sur 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Domaine Amazon", - "amazon_fwds": "Amazon a transmis des adresses e-mail", - "amazon_days": "Jours en arrière pour vérifier les emails Amazon" - }, - "description": "Veuillez entrer le domaine à partir duquel Amazon envoie des emails (par exemple : amazon.com ou amazon.de)\n\nSi vous utilisez des emails transférés par Amazon, veuillez séparer chaque adresse par une virgule ou entrer (aucun) pour effacer ce paramètre.\n\nRemarque : si vous avez configuré un en-tête de transfert dans l'étape précédente, le champ d'adresse de transfert n'apparaîtra pas — l'en-tête gère automatiquement la correspondance des e-mails Amazon.", - "title": "Paramètres Amazon" - }, - "config_storage": { - "data": { - "storage": "Répertoire pour stocker les images" - }, - "description": "Veuillez entrer le répertoire dans lequel vous souhaitez que vos images soient stockées.\nLa valeur par défaut est automatiquement remplie.", - "title": "Emplacement de stockage des images" - }, - "reconfig_storage": { - "data": { - "storage": "Répertoire pour stocker les images" - }, - "description": "Veuillez entrer le répertoire dans lequel vous souhaitez que vos images soient stockées.\nLa valeur par défaut est automatiquement remplie.", - "title": "Emplacement de stockage des images" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "En-tête de transfert (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Adresses e-mail transférées" - }, - "data_description": { - "forwarding_header": "Si votre service de transfert inclut l'expéditeur original dans un en-tête, entrez le nom de l'en-tête ici. Cela a la priorité sur la liste d'adresses ci-dessous. Entrez '(none)' ou laissez vide pour utiliser la liste d'adresses à la place.", - "forwarded_emails": "Liste séparée par des virgules des adresses de transfert dont proviendront les messages. Laissez '(none)' si vous utilisez l'en-tête de transfert ci-dessus." - }, - "description": "Choisissez comment faire correspondre les e-mails transférés.\n\n**Option 1 — En-tête de transfert** : Si votre service (ex. SimpleLogin) inclut l'expéditeur original dans un en-tête personnalisé, entrez ce nom d'en-tête. Mail and Packages l'utilisera pour faire correspondre automatiquement les adresses des transporteurs.\n\n**Option 2 — Liste d'adresses** : Entrez les adresses de transfert que votre service a attribuées à chaque transporteur, séparées par des virgules.", - "title": "Paramètres des e-mails transférés" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "En-tête de transfert (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Adresses e-mail transférées" - }, - "data_description": { - "forwarding_header": "Si votre service de transfert inclut l'expéditeur original dans un en-tête, entrez le nom de l'en-tête ici. Cela a la priorité sur la liste d'adresses ci-dessous. Entrez '(none)' ou laissez vide pour utiliser la liste d'adresses à la place.", - "forwarded_emails": "Liste séparée par des virgules des adresses de transfert dont proviendront les messages. Laissez '(none)' si vous utilisez l'en-tête de transfert ci-dessus." - }, - "description": "Choisissez comment faire correspondre les e-mails transférés.\n\n**Option 1 — En-tête de transfert** : Si votre service (ex. SimpleLogin) inclut l'expéditeur original dans un en-tête personnalisé, entrez ce nom d'en-tête. Mail and Packages l'utilisera pour faire correspondre automatiquement les adresses des transporteurs.\n\n**Option 2 — Liste d'adresses** : Entrez les adresses de transfert que votre service a attribuées à chaque transporteur, séparées par des virgules.", - "title": "Paramètres des e-mails transférés" - }, - "imap_config": { - "description": "Veuillez entrer les informations de connexion de votre serveur de messagerie.", - "data": { - "host": "Hôte", - "password": "Mot de passe", - "port": "Port", - "username": "Nom d'utilisateur", - "verify_ssl": "Vérifier le certificat SSL", - "imap_security": "Sécurité IMAP" - }, - "title": "Courrier et colis (étape 1 sur 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Hôte", - "password": "Mot de passe", - "port": "Port", - "username": "Nom d'utilisateur", - "imap_security": "Sécurité IMAP", - "verify_ssl": "Vérifier le certificat SSL" - }, - "description": "Veuillez entrer les informations de connexion de votre serveur de messagerie.", - "title": "Courrier et colis (étape 1 sur 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Échec de l'authentification de Mail and Packages", - "fix_flow": { - "step": { - "confirm": { - "title": "Ré-authentifier le serveur de messagerie", - "description": "L'authentification sur votre serveur de messagerie IMAP a échoué. Cliquez sur soumettre pour démarrer le flux de ré-authentification." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Courrier et colis (étape 2 sur 2)", - "description": "Terminez la configuration en personnalisant ce qui suit en fonction de la structure de votre courrier électronique et de l'installation de Home Assistant.\n\nPour plus de détails sur les options d'[intégration de courrier et de colis](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), consultez la [section configuration, modèles et automatisations](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) sur GitHub.\n\nSi vous utilisez des courriels transférés par Amazon, veuillez séparer chaque adresse par une virgule ou entrer (aucun) pour effacer ce paramètre.", - "data": { - "folder": "Dossier de messagerie", - "resources": "Liste des capteurs", - "scan_interval": "Intervalle de numérisation (minutes)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Chemin de l'image", - "gif_duration": "Durée de l'image (secondes)", - "image_security": "Nom de fichier d'image aléatoire", - "imap_timeout": "Temps en secondes avant la fin de la connexion (secondes, minimum 10)", - "generate_mp4": "Créer mp4 à partir d'images", - "allow_external": "Créer une image pour les applications de notification", - "usps_placeholder": "Inclure l'image d'emplacement sans image USPS dans le GIF ?", - "custom_img": "Utiliser une image personnalisée USPS 'pas de courrier' ?", - "amazon_custom_img": "Utiliser une image personnalisée Amazon 'pas de livraison' ?", - "ups_custom_img": "Utiliser une image personnalisée UPS 'pas de livraison' ?", - "walmart_custom_img": "Utiliser une image personnalisée Walmart 'pas de livraison' ?", - "fedex_custom_img": "Utiliser une image personnalisée FedEx 'pas de livraison' ?", - "generic_custom_img": "Utiliser une image personnalisée générique 'pas de livraison' ?", - "generate_grid": "Créer une grille d'images pour les modèles de vision LLM", - "allow_forwarded_emails": "Autoriser les e-mails transférés en plus de la valeur par défaut d'un service (par ex. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Courrier et colis (Étape 3 sur 3)", - "description": "Entrez le chemin et le nom de fichier de votre image personnalisée de non-réception de courrier ci-dessous.\n\nExemple : images/custom_nomail.gif", - "data": { - "custom_img_file": "Chemin vers l'image personnalisée : (par exemple : images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Paramètres Amazon", - "description": "Veuillez entrer le domaine à partir duquel Amazon envoie des emails (par exemple : amazon.com ou amazon.de)\n\nSi vous utilisez des emails transférés par Amazon, veuillez séparer chaque adresse par une virgule ou entrer (aucun) pour effacer ce paramètre.\n\nRemarque : si vous avez configuré un en-tête de transfert dans l'étape précédente, le champ d'adresse de transfert n'apparaîtra pas — l'en-tête gère automatiquement la correspondance des e-mails Amazon.", - "data": { - "amazon_domain": "Domaine Amazon", - "amazon_fwds": "Amazon a transmis des adresses e-mail", - "amazon_days": "Jours en arrière pour vérifier les emails Amazon" - } - }, - "options_storage": { - "title": "Emplacement de stockage des images", - "description": "Veuillez entrer le répertoire dans lequel vous souhaitez que vos images soient stockées.\nLa valeur par défaut est automatiquement remplie.", - "data": { - "storage": "Répertoire pour stocker les images" - } - }, - "options_forwarded_emails": { - "title": "Paramètres des e-mails transférés", - "description": "Choisissez comment faire correspondre les e-mails transférés.\n\n**Option 1 — En-tête de transfert** : Si votre service (ex. SimpleLogin) inclut l'expéditeur original dans un en-tête personnalisé, entrez ce nom d'en-tête. Mail and Packages l'utilisera pour faire correspondre automatiquement les adresses des transporteurs.\n\n**Option 2 — Liste d'adresses** : Entrez les adresses de transfert que votre service a attribuées à chaque transporteur, séparées par des virgules.", - "data": { - "forwarding_header": "En-tête de transfert (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Adresses e-mail transférées" - }, - "data_description": { - "forwarding_header": "Si votre service de transfert inclut l'expéditeur original dans un en-tête, entrez le nom de l'en-tête ici. Cela a la priorité sur la liste d'adresses ci-dessous. Entrez '(none)' ou laissez vide pour utiliser la liste d'adresses à la place.", - "forwarded_emails": "Liste séparée par des virgules des adresses de transfert dont proviendront les messages. Laissez '(none)' si vous utilisez l'en-tête de transfert ci-dessus." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/hu.json b/custom_components/mail_and_packages/translations/hu.json deleted file mode 100644 index a274636..0000000 --- a/custom_components/mail_and_packages/translations/hu.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Csak a Levelek és a Csomagok egyetlen konfigurációja megengedett.", - "reconfigure_successful": "Újrakonfigurálás sikeres", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Nem lehet csatlakozni vagy bejelentkezni az e-mail szerverhez. Kérjük, ellenőrizze a naplót a részletekért.", - "invalid_path": "Tárolja a képeket egy másik könyvtárban.", - "ffmpeg_not_found": "Az MP4 generálása ffmpeg-et igényel", - "amazon_domain": "Érvénytelen továbbítási e-mail cím.", - "file_not_found": "Képfájl nem található", - "invalid_email_format": "Érvénytelen e-mail cím formátum.", - "missing_forwarded_emails": "Hiányzó továbbított e-mail címek. Megadhatja a '(none)' értéket a beállítás törléséhez.", - "path_not_found": "Könyvtár nem található", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Mail mappa", - "resources": "Érzékelők listája", - "scan_interval": "Beolvasási időköz (perc)", - "image_path": "Kép elérési útja", - "gif_duration": "Kép időtartama (másodpercben)", - "image_security": "Véletlen képfájlnév", - "imap_timeout": "A kapcsolat időtúllépése előtti idő másodpercben (másodperc, minimum 10)", - "generate_mp4": "Készítsen mp4-et a képekből", - "allow_external": "Kép létrehozása értesítési alkalmazásokhoz", - "custom_img": "Használjon egyéni USPS 'nincs posta' képet?", - "amazon_custom_img": "Használjon egyéni Amazon 'nincs szállítás' képet?", - "ups_custom_img": "Használjon egyéni UPS 'nincs szállítás' képet?", - "walmart_custom_img": "Használjon egyéni Walmart 'nincs szállítás' képet?", - "fedex_custom_img": "Használjon egyéni FedEx 'nincs szállítás' képet?", - "generic_custom_img": "Használjon egyéni általános 'nincs szállítás' képet?", - "generate_grid": "Kép rács létrehozása LLM látásmodellekhez", - "allow_forwarded_emails": "Engedélyezze a továbbított e-maileket a szolgáltatás alapértelmezett értékén kívül (pl. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Tartalmazza az USPS kép nélküli helyőrzőjét a GIF-ben?" - }, - "description": "Végezze el a konfigurációt az alábbiak testreszabásával az e-mail struktúrája és a Home Assistant telepítése alapján. \n\n A [Levelek és csomagok integrációja] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) opciókkal kapcsolatban tekintse meg a [konfiguráció, sablonok , és az automatizálás szakasz] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) a GitHubon.", - "title": "Levél és csomagok (2. lépés a 2-ből)" - }, - "config_3": { - "data": { - "custom_img_file": "Egyéni kép elérési útja: (pl.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Adja meg az egyéni \"nincs levél\" kép útvonalát és fájlnevét alább.\n\nPélda: images/custom_nomail.gif", - "title": "Levelek és csomagok (3. lépés a 3-ból)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon továbbított e-mail címek", - "amazon_days": "Napok visszamenőleg az Amazon e-mailek ellenőrzéséhez" - }, - "description": "Kérjük, adja meg azt a domain-t, ahonnan az Amazon e-maileket küld (pl.: amazon.com vagy amazon.de)\n\nHa az Amazon továbbított e-maileket használ, kérjük, válassza el minden címet vesszővel, vagy írja be, hogy (nincs), hogy törölje ezt a beállítást.\n\nMegjegyzés: ha az előző lépésben beállított egy továbbítási fejlécet, a továbbítási cím mező nem jelenik meg — a fejléc automatikusan kezeli az Amazon e-mailek egyeztetését.", - "title": "Amazon beállítások" - }, - "reconfig_2": { - "data": { - "folder": "Mail mappa", - "scan_interval": "Beolvasási időköz (perc)", - "image_path": "Kép elérési útja", - "gif_duration": "Kép időtartama (másodpercben)", - "image_security": "Véletlen képfájlnév", - "generate_mp4": "Készítsen mp4-et a képekből", - "resources": "Érzékelők listája", - "imap_timeout": "A kapcsolat időtúllépése előtti idő másodpercben (másodperc, minimum 10)", - "allow_external": "Kép létrehozása értesítési alkalmazásokhoz", - "custom_img": "Használjon egyéni USPS 'nincs posta' képet?", - "amazon_custom_img": "Használjon egyéni Amazon 'nincs szállítás' képet?", - "ups_custom_img": "Használjon egyéni UPS 'nincs szállítás' képet?", - "walmart_custom_img": "Használjon egyéni Walmart 'nincs szállítás' képet?", - "fedex_custom_img": "Használjon egyéni FedEx 'nincs szállítás' képet?", - "generic_custom_img": "Használjon egyéni általános 'nincs szállítás' képet?", - "generate_grid": "Kép rács létrehozása LLM látásmodellekhez", - "allow_forwarded_emails": "Engedélyezze a továbbított e-maileket a szolgáltatás alapértelmezett értékén kívül (pl. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Tartalmazza az USPS kép nélküli helyőrzőjét a GIF-ben?" - }, - "description": "Fejezze be a konfigurációt a következők testreszabásával az e-mail struktúrája és a Home Assistant telepítése alapján.\n\nA [Mail and Packages integráció](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) lehetőségeinek részleteiről a GitHub-on található [konfiguráció, sablonok és automatizálások szekcióban](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) található információkat.\n\nHa Amazon továbbított e-maileket használ, kérjük, válassza el minden címet vesszővel, vagy írja be a (none) opciót, hogy törölje ezt a beállítást.", - "title": "Levél és csomagok (2. lépés a 2-ből)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Egyéni kép elérési útja: (pl.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Adja meg az egyéni \"nincs levél\" kép útvonalát és fájlnevét alább.\n\nPélda: images/custom_nomail.gif", - "title": "Levelek és csomagok (3. lépés a 3-ból)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon továbbított e-mail címek", - "amazon_days": "Napok visszamenőleg az Amazon e-mailek ellenőrzéséhez" - }, - "description": "Kérjük, adja meg azt a domain-t, ahonnan az Amazon e-maileket küld (pl.: amazon.com vagy amazon.de)\n\nHa az Amazon továbbított e-maileket használ, kérjük, válassza el minden címet vesszővel, vagy írja be, hogy (nincs), hogy törölje ezt a beállítást.\n\nMegjegyzés: ha az előző lépésben beállított egy továbbítási fejlécet, a továbbítási cím mező nem jelenik meg — a fejléc automatikusan kezeli az Amazon e-mailek egyeztetését.", - "title": "Amazon beállítások" - }, - "config_storage": { - "data": { - "storage": "Képek tárolására szolgáló könyvtár" - }, - "description": "Kérjük, adja meg azt a könyvtárat, ahová a képeit szeretné tárolni.\nAz alapértelmezett automatikusan kitöltődik.", - "title": "Képtárolási hely" - }, - "reconfig_storage": { - "data": { - "storage": "Képek tárolására szolgáló könyvtár" - }, - "description": "Kérjük, adja meg azt a könyvtárat, ahová a képeit szeretné tárolni.\nAz alapértelmezett automatikusan kitöltődik.", - "title": "Képtárolási hely" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Továbbítási fejléc (pl. X-SimpleLogin-Original-From)", - "forwarded_emails": "Továbbított e-mail címek" - }, - "data_description": { - "forwarding_header": "Ha a továbbítási szolgáltatása tartalmazza az eredeti feladót egy fejlécben, adja meg itt a fejléc nevét. Ez elsőbbséget élvez az alábbi címlistával szemben. Írja be a '(none)' értéket, vagy hagyja üresen a címlista használatához.", - "forwarded_emails": "Vesszővel elválasztott lista a továbbítási címekről, ahonnan az üzenetek érkeznek. Hagyja '(none)' értéken, ha a fenti továbbítási fejlécet használja." - }, - "description": "Válassza ki, hogyan egyezzen meg a továbbított e-mailekkel.\n\n**1. lehetőség — Továbbítási fejléc**: Ha a szolgáltatása (pl. SimpleLogin) tartalmazza az eredeti feladót egy egyéni fejlécben, adja meg azt a fejlécnevet. A Mail and Packages ezt fogja használni a szállítói címek automatikus egyeztetéséhez.\n\n**2. lehetőség — Címlista**: Adja meg a továbbítási címeket, amelyeket a szolgáltatása minden szállítóhoz hozzárendelt, vesszővel elválasztva.", - "title": "Továbbított e-mailek beállításai" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Továbbítási fejléc (pl. X-SimpleLogin-Original-From)", - "forwarded_emails": "Továbbított e-mail címek" - }, - "data_description": { - "forwarding_header": "Ha a továbbítási szolgáltatása tartalmazza az eredeti feladót egy fejlécben, adja meg itt a fejléc nevét. Ez elsőbbséget élvez az alábbi címlistával szemben. Írja be a '(none)' értéket, vagy hagyja üresen a címlista használatához.", - "forwarded_emails": "Vesszővel elválasztott lista a továbbítási címekről, ahonnan az üzenetek érkeznek. Hagyja '(none)' értéken, ha a fenti továbbítási fejlécet használja." - }, - "description": "Válassza ki, hogyan egyezzen meg a továbbított e-mailekkel.\n\n**1. lehetőség — Továbbítási fejléc**: Ha a szolgáltatása (pl. SimpleLogin) tartalmazza az eredeti feladót egy egyéni fejlécben, adja meg azt a fejlécnevet. A Mail and Packages ezt fogja használni a szállítói címek automatikus egyeztetéséhez.\n\n**2. lehetőség — Címlista**: Adja meg a továbbítási címeket, amelyeket a szolgáltatása minden szállítóhoz hozzárendelt, vesszővel elválasztva.", - "title": "Továbbított e-mailek beállításai" - }, - "imap_config": { - "description": "Kérjük, adja meg a levelezőszervere kapcsolati adatait.", - "data": { - "host": "Házigazda", - "password": "Jelszó", - "port": "Kikötő", - "username": "Felhasználónév", - "verify_ssl": "Ellenőrizze az SSL tanúsítványt", - "imap_security": "IMAP Biztonság" - }, - "title": "Levél és csomagok (1. lépés a 2-ből)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Házigazda", - "password": "Jelszó", - "port": "Kikötő", - "username": "Felhasználónév", - "imap_security": "IMAP Biztonság", - "verify_ssl": "Ellenőrizze az SSL tanúsítványt" - }, - "description": "Kérjük, adja meg a levelezőszervere kapcsolati adatait.", - "title": "Levél és csomagok (1. lépés a 2-ből)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages hitelesítés sikertelen", - "fix_flow": { - "step": { - "confirm": { - "title": "Levelezőszerver újrahitelesítése", - "description": "Nem sikerült a hitelesítés az IMAP levelezőszerveren. Kattintson a küldésre az újrahitelesítési folyamat elindításához." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Levél és csomagok (2. lépés a 2-ből)", - "description": "Fejezze be a konfigurációt a következők testreszabásával az e-mail struktúrája és a Home Assistant telepítése alapján.\n\nA [Mail and Packages integráció](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) lehetőségeinek részleteiről a GitHub-on található [konfiguráció, sablonok és automatizálások szekcióban](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) található információkat.\n\nHa Amazon továbbított e-maileket használ, kérjük, válassza el minden címet vesszővel, vagy írja be a (none) opciót, hogy törölje ezt a beállítást.", - "data": { - "folder": "Mail mappa", - "resources": "Érzékelők listája", - "scan_interval": "Beolvasási időköz (perc)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Kép elérési útja", - "gif_duration": "Kép időtartama (másodpercben)", - "image_security": "Véletlen képfájlnév", - "imap_timeout": "A kapcsolat időtúllépése előtti idő másodpercben (másodperc, minimum 10)", - "generate_mp4": "Készítsen mp4-et a képekből", - "allow_external": "Kép létrehozása értesítési alkalmazásokhoz", - "usps_placeholder": "Tartalmazza az USPS kép nélküli helyőrzőjét a GIF-ben?", - "custom_img": "Használjon egyéni USPS 'nincs posta' képet?", - "amazon_custom_img": "Használjon egyéni Amazon 'nincs szállítás' képet?", - "ups_custom_img": "Használjon egyéni UPS 'nincs szállítás' képet?", - "walmart_custom_img": "Használjon egyéni Walmart 'nincs szállítás' képet?", - "fedex_custom_img": "Használjon egyéni FedEx 'nincs szállítás' képet?", - "generic_custom_img": "Használjon egyéni általános 'nincs szállítás' képet?", - "generate_grid": "Kép rács létrehozása LLM látásmodellekhez", - "allow_forwarded_emails": "Engedélyezze a továbbított e-maileket a szolgáltatás alapértelmezett értékén kívül (pl. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Levelek és csomagok (3. lépés a 3-ból)", - "description": "Adja meg az egyéni \"nincs levél\" kép útvonalát és fájlnevét alább.\n\nPélda: images/custom_nomail.gif", - "data": { - "custom_img_file": "Egyéni kép elérési útja: (pl.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Amazon beállítások", - "description": "Kérjük, adja meg azt a domain-t, ahonnan az Amazon e-maileket küld (pl.: amazon.com vagy amazon.de)\n\nHa az Amazon továbbított e-maileket használ, kérjük, válassza el minden címet vesszővel, vagy írja be, hogy (nincs), hogy törölje ezt a beállítást.\n\nMegjegyzés: ha az előző lépésben beállított egy továbbítási fejlécet, a továbbítási cím mező nem jelenik meg — a fejléc automatikusan kezeli az Amazon e-mailek egyeztetését.", - "data": { - "amazon_domain": "Amazon domain", - "amazon_fwds": "Amazon továbbított e-mail címek", - "amazon_days": "Napok visszamenőleg az Amazon e-mailek ellenőrzéséhez" - } - }, - "options_storage": { - "title": "Képtárolási hely", - "description": "Kérjük, adja meg azt a könyvtárat, ahová a képeit szeretné tárolni.\nAz alapértelmezett automatikusan kitöltődik.", - "data": { - "storage": "Képek tárolására szolgáló könyvtár" - } - }, - "options_forwarded_emails": { - "title": "Továbbított e-mailek beállításai", - "description": "Válassza ki, hogyan egyezzen meg a továbbított e-mailekkel.\n\n**1. lehetőség — Továbbítási fejléc**: Ha a szolgáltatása (pl. SimpleLogin) tartalmazza az eredeti feladót egy egyéni fejlécben, adja meg azt a fejlécnevet. A Mail and Packages ezt fogja használni a szállítói címek automatikus egyeztetéséhez.\n\n**2. lehetőség — Címlista**: Adja meg a továbbítási címeket, amelyeket a szolgáltatása minden szállítóhoz hozzárendelt, vesszővel elválasztva.", - "data": { - "forwarding_header": "Továbbítási fejléc (pl. X-SimpleLogin-Original-From)", - "forwarded_emails": "Továbbított e-mail címek" - }, - "data_description": { - "forwarding_header": "Ha a továbbítási szolgáltatása tartalmazza az eredeti feladót egy fejlécben, adja meg itt a fejléc nevét. Ez elsőbbséget élvez az alábbi címlistával szemben. Írja be a '(none)' értéket, vagy hagyja üresen a címlista használatához.", - "forwarded_emails": "Vesszővel elválasztott lista a továbbítási címekről, ahonnan az üzenetek érkeznek. Hagyja '(none)' értéken, ha a fenti továbbítási fejlécet használja." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/it.json b/custom_components/mail_and_packages/translations/it.json deleted file mode 100644 index 9b48157..0000000 --- a/custom_components/mail_and_packages/translations/it.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "È consentita una sola configurazione di posta e pacchetti.", - "reconfigure_successful": "Riconfigurazione Riuscita", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Impossibile connettersi o accedere al server di posta. Si prega di controllare il registro per i dettagli.", - "invalid_path": "Conservare le immagini in un'altra directory.", - "ffmpeg_not_found": "Generare MP4 richiede ffmpeg", - "amazon_domain": "Indirizzo email di inoltro non valido.", - "file_not_found": "File immagine non trovato", - "invalid_email_format": "Formato dell'indirizzo email non valido.", - "missing_forwarded_emails": "Indirizzi email inoltrati mancanti. Puoi inserire '(none)' per cancellare questa impostazione.", - "path_not_found": "Directory non trovato", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Cartella posta", - "resources": "Elenco dei sensori", - "scan_interval": "Intervallo di scansione (minuti)", - "image_path": "Percorso immagine", - "gif_duration": "Durata immagine (secondi)", - "image_security": "Nome file immagine casuale", - "imap_timeout": "Tempo in secondi prima del timeout di connessione (secondi, minimo 10)", - "generate_mp4": "Crea mp4 dalle immagini", - "allow_external": "Crea immagine per le app di notifica", - "custom_img": "Usare l'immagine personalizzata USPS 'nessuna posta'?", - "amazon_custom_img": "Usare l'immagine personalizzata Amazon 'nessuna consegna'?", - "ups_custom_img": "Usare l'immagine personalizzata UPS 'nessuna consegna'?", - "walmart_custom_img": "Usare l'immagine personalizzata Walmart 'nessuna consegna'?", - "fedex_custom_img": "Usare l'immagine personalizzata FedEx 'nessuna consegna'?", - "generic_custom_img": "Usare l'immagine personalizzata generica 'nessuna consegna'?", - "generate_grid": "Crea una griglia di immagini per i modelli di visione LLM", - "allow_forwarded_emails": "Consenti email inoltrate oltre al valore predefinito di un servizio (ad es. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Includere il segnaposto USPS senza immagine nella GIF?" - }, - "description": "Termina la configurazione personalizzando quanto segue in base alla struttura della tua e-mail e all'installazione di Home Assistant. \n\n Per i dettagli sulle opzioni [Integrazione posta e pacchetti] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) rivedere le [configurazione, modelli e sezione automazioni] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) su GitHub.", - "title": "Posta e pacchi (passaggio 2 di 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Percorso per l'immagine personalizzata: (es: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Inserisci il percorso e il nome del file per la tua immagine personalizzata di nessuna mail qui sotto.\n\nEsempio: images/custom_nomail.gif", - "title": "Posta e Pacchetti (Passaggio 3 di 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Dominio Amazon", - "amazon_fwds": "Amazon ha inoltrato gli indirizzi email", - "amazon_days": "Giorni precedenti da controllare per le email di Amazon" - }, - "description": "Inserisci il dominio da cui Amazon invia le email (ad esempio: amazon.com o amazon.de)\n\nSe stai utilizzando email inoltrate da Amazon, separa ogni indirizzo con una virgola o inserisci (nessuno) per cancellare questa impostazione.\n\nNota: se hai configurato un'intestazione di inoltro nel passaggio precedente, il campo dell'indirizzo di inoltro non apparirà — l'intestazione gestisce automaticamente la corrispondenza delle email Amazon.", - "title": "Impostazioni Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Cartella posta", - "scan_interval": "Intervallo di scansione (minuti)", - "image_path": "Percorso immagine", - "gif_duration": "Durata immagine (secondi)", - "image_security": "Nome file immagine casuale", - "generate_mp4": "Crea mp4 dalle immagini", - "resources": "Elenco dei sensori", - "imap_timeout": "Tempo in secondi prima del timeout di connessione (secondi, minimo 10)", - "allow_external": "Crea immagine per le app di notifica", - "custom_img": "Usare l'immagine personalizzata USPS 'nessuna posta'?", - "amazon_custom_img": "Usare l'immagine personalizzata Amazon 'nessuna consegna'?", - "ups_custom_img": "Usare l'immagine personalizzata UPS 'nessuna consegna'?", - "walmart_custom_img": "Usare l'immagine personalizzata Walmart 'nessuna consegna'?", - "fedex_custom_img": "Usare l'immagine personalizzata FedEx 'nessuna consegna'?", - "generic_custom_img": "Usare l'immagine personalizzata generica 'nessuna consegna'?", - "generate_grid": "Crea una griglia di immagini per i modelli di visione LLM", - "allow_forwarded_emails": "Consenti email inoltrate oltre al valore predefinito di un servizio (ad es. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Includere il segnaposto USPS senza immagine nella GIF?" - }, - "description": "Termina la configurazione personalizzando quanto segue in base alla struttura della tua email e all'installazione di Home Assistant.\n\nPer i dettagli sulle opzioni di [integrazione Mail e Pacchetti](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) consulta la [sezione configurazione, modelli e automazioni](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) su GitHub.\n\nSe utilizzi email inoltrate da Amazon, separa ogni indirizzo con una virgola o inserisci (nessuno) per cancellare questa impostazione.", - "title": "Posta e pacchi (passaggio 2 di 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Percorso per l'immagine personalizzata: (ad es.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Inserisci il percorso e il nome del file per la tua immagine personalizzata di nessuna mail qui sotto.\n\nEsempio: images/custom_nomail.gif", - "title": "Posta e Pacchetti (Passaggio 3 di 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Dominio Amazon", - "amazon_fwds": "Amazon ha inoltrato gli indirizzi email", - "amazon_days": "Giorni precedenti da controllare per le email di Amazon" - }, - "description": "Inserisci il dominio da cui Amazon invia le email (ad esempio: amazon.com o amazon.de)\n\nSe stai utilizzando email inoltrate da Amazon, separa ogni indirizzo con una virgola o inserisci (nessuno) per cancellare questa impostazione.\n\nNota: se hai configurato un'intestazione di inoltro nel passaggio precedente, il campo dell'indirizzo di inoltro non apparirà — l'intestazione gestisce automaticamente la corrispondenza delle email Amazon.", - "title": "Impostazioni Amazon" - }, - "config_storage": { - "data": { - "storage": "Cartella per memorizzare le immagini" - }, - "description": "Inserisci la directory in cui desideri che le tue immagini vengano memorizzate.\nIl valore predefinito viene popolato automaticamente.", - "title": "Posizione di archiviazione delle immagini" - }, - "reconfig_storage": { - "data": { - "storage": "Cartella per memorizzare le immagini" - }, - "description": "Inserisci la directory in cui desideri che le tue immagini vengano memorizzate.\nIl valore predefinito viene popolato automaticamente.", - "title": "Posizione di archiviazione delle immagini" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Intestazione di inoltro (es. X-SimpleLogin-Original-From)", - "forwarded_emails": "Indirizzi email inoltrati" - }, - "data_description": { - "forwarding_header": "Se il tuo servizio di inoltro include il mittente originale in un'intestazione, inserisci qui il nome dell'intestazione. Questo ha la precedenza sull'elenco di indirizzi seguente. Inserisci '(none)' o lascia vuoto per usare l'elenco di indirizzi.", - "forwarded_emails": "Elenco separato da virgole degli indirizzi di inoltro da cui arriveranno i messaggi. Lascia '(none)' se stai usando l'intestazione di inoltro sopra." - }, - "description": "Scegli come abbinare le email inoltrate.\n\n**Opzione 1 — Intestazione di inoltro**: Se il tuo servizio (es. SimpleLogin) include il mittente originale in un'intestazione personalizzata, inserisci il nome di quell'intestazione. Mail and Packages lo utilizzerà per abbinare automaticamente gli indirizzi dei corrieri.\n\n**Opzione 2 — Elenco di indirizzi**: Inserisci gli indirizzi di inoltro che il tuo servizio ha assegnato a ciascun corriere, separati da virgole.", - "title": "Impostazioni email inoltrate" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Intestazione di inoltro (es. X-SimpleLogin-Original-From)", - "forwarded_emails": "Indirizzi email inoltrati" - }, - "data_description": { - "forwarding_header": "Se il tuo servizio di inoltro include il mittente originale in un'intestazione, inserisci qui il nome dell'intestazione. Questo ha la precedenza sull'elenco di indirizzi seguente. Inserisci '(none)' o lascia vuoto per usare l'elenco di indirizzi.", - "forwarded_emails": "Elenco separato da virgole degli indirizzi di inoltro da cui arriveranno i messaggi. Lascia '(none)' se stai usando l'intestazione di inoltro sopra." - }, - "description": "Scegli come abbinare le email inoltrate.\n\n**Opzione 1 — Intestazione di inoltro**: Se il tuo servizio (es. SimpleLogin) include il mittente originale in un'intestazione personalizzata, inserisci il nome di quell'intestazione. Mail and Packages lo utilizzerà per abbinare automaticamente gli indirizzi dei corrieri.\n\n**Opzione 2 — Elenco di indirizzi**: Inserisci gli indirizzi di inoltro che il tuo servizio ha assegnato a ciascun corriere, separati da virgole.", - "title": "Impostazioni email inoltrate" - }, - "imap_config": { - "description": "Inserisci le informazioni di connessione del tuo server di posta.", - "data": { - "host": "Ospite", - "password": "Parola d'ordine", - "port": "Porta", - "username": "Nome utente", - "verify_ssl": "Verifica Certificato SSL", - "imap_security": "Sicurezza IMAP" - }, - "title": "Posta e pacchi (passaggio 1 di 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Ospite", - "password": "Parola d'ordine", - "port": "Porta", - "username": "Nome utente", - "imap_security": "Sicurezza IMAP", - "verify_ssl": "Verifica Certificato SSL" - }, - "description": "Inserisci le informazioni di connessione del tuo server di posta.", - "title": "Posta e pacchi (passaggio 1 di 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Autenticazione Mail and Packages fallita", - "fix_flow": { - "step": { - "confirm": { - "title": "Riautentica il server di posta", - "description": "L'autenticazione al server di posta IMAP è fallita. Clicca su invia per avviare la procedura di riautenticazione." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Posta e pacchi (passaggio 2 di 2)", - "description": "Termina la configurazione personalizzando quanto segue in base alla struttura della tua email e all'installazione di Home Assistant.\n\nPer i dettagli sulle opzioni di [integrazione Mail e Pacchetti](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) consulta la [sezione configurazione, modelli e automazioni](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) su GitHub.\n\nSe utilizzi email inoltrate da Amazon, separa ogni indirizzo con una virgola o inserisci (nessuno) per cancellare questa impostazione.", - "data": { - "folder": "Cartella posta", - "resources": "Elenco dei sensori", - "scan_interval": "Intervallo di scansione (minuti)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Percorso immagine", - "gif_duration": "Durata immagine (secondi)", - "image_security": "Nome file immagine casuale", - "imap_timeout": "Tempo in secondi prima del timeout di connessione (secondi, minimo 10)", - "generate_mp4": "Crea mp4 dalle immagini", - "allow_external": "Crea immagine per le app di notifica", - "usps_placeholder": "Includere il segnaposto USPS senza immagine nella GIF?", - "custom_img": "Usare l'immagine personalizzata USPS 'nessuna posta'?", - "amazon_custom_img": "Usare l'immagine personalizzata Amazon 'nessuna consegna'?", - "ups_custom_img": "Usare l'immagine personalizzata UPS 'nessuna consegna'?", - "walmart_custom_img": "Usare l'immagine personalizzata Walmart 'nessuna consegna'?", - "fedex_custom_img": "Usare l'immagine personalizzata FedEx 'nessuna consegna'?", - "generic_custom_img": "Usare l'immagine personalizzata generica 'nessuna consegna'?", - "generate_grid": "Crea una griglia di immagini per i modelli di visione LLM", - "allow_forwarded_emails": "Consenti email inoltrate oltre al valore predefinito di un servizio (ad es. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Posta e Pacchetti (Passaggio 3 di 3)", - "description": "Inserisci il percorso e il nome del file per la tua immagine personalizzata di nessuna mail qui sotto.\n\nEsempio: images/custom_nomail.gif", - "data": { - "custom_img_file": "Percorso per l'immagine personalizzata: (ad es.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Impostazioni Amazon", - "description": "Inserisci il dominio da cui Amazon invia le email (ad esempio: amazon.com o amazon.de)\n\nSe stai utilizzando email inoltrate da Amazon, separa ogni indirizzo con una virgola o inserisci (nessuno) per cancellare questa impostazione.\n\nNota: se hai configurato un'intestazione di inoltro nel passaggio precedente, il campo dell'indirizzo di inoltro non apparirà — l'intestazione gestisce automaticamente la corrispondenza delle email Amazon.", - "data": { - "amazon_domain": "Dominio Amazon", - "amazon_fwds": "Amazon ha inoltrato gli indirizzi email", - "amazon_days": "Giorni precedenti da controllare per le email di Amazon" - } - }, - "options_storage": { - "title": "Posizione di archiviazione delle immagini", - "description": "Inserisci la directory in cui desideri che le tue immagini vengano memorizzate.\nIl valore predefinito viene popolato automaticamente.", - "data": { - "storage": "Cartella per memorizzare le immagini" - } - }, - "options_forwarded_emails": { - "title": "Impostazioni email inoltrate", - "description": "Scegli come abbinare le email inoltrate.\n\n**Opzione 1 — Intestazione di inoltro**: Se il tuo servizio (es. SimpleLogin) include il mittente originale in un'intestazione personalizzata, inserisci il nome di quell'intestazione. Mail and Packages lo utilizzerà per abbinare automaticamente gli indirizzi dei corrieri.\n\n**Opzione 2 — Elenco di indirizzi**: Inserisci gli indirizzi di inoltro che il tuo servizio ha assegnato a ciascun corriere, separati da virgole.", - "data": { - "forwarding_header": "Intestazione di inoltro (es. X-SimpleLogin-Original-From)", - "forwarded_emails": "Indirizzi email inoltrati" - }, - "data_description": { - "forwarding_header": "Se il tuo servizio di inoltro include il mittente originale in un'intestazione, inserisci qui il nome dell'intestazione. Questo ha la precedenza sull'elenco di indirizzi seguente. Inserisci '(none)' o lascia vuoto per usare l'elenco di indirizzi.", - "forwarded_emails": "Elenco separato da virgole degli indirizzi di inoltro da cui arriveranno i messaggi. Lascia '(none)' se stai usando l'intestazione di inoltro sopra." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/ko.json b/custom_components/mail_and_packages/translations/ko.json deleted file mode 100644 index 821af0d..0000000 --- a/custom_components/mail_and_packages/translations/ko.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "메일 및 패키지의 단일 구성 만 허용됩니다.", - "reconfigure_successful": "재구성 성공", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "메일 서버에 연결하거나 로그인 할 수 없습니다. 자세한 내용은 로그를 확인하십시오.", - "invalid_path": "이미지를 다른 디렉토리에 저장하십시오.", - "ffmpeg_not_found": "MP4 생성은 ffmpeg가 필요합니다", - "amazon_domain": "잘못된 전달 이메일 주소입니다.", - "file_not_found": "이미지 파일을 찾을 수 없습니다", - "invalid_email_format": "잘못된 이메일 주소 형식입니다.", - "missing_forwarded_emails": "전달된 이메일 주소가 없습니다. 이 설정을 지우려면 '(none)'을 입력할 수 있습니다.", - "path_not_found": "디렉토리를 찾을 수 없습니다", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "메일 폴더", - "resources": "센서 목록", - "scan_interval": "스캔 간격 (분)", - "image_path": "이미지 경로", - "gif_duration": "이미지 지속 시간 (초)", - "image_security": "임의의 이미지 파일 이름", - "imap_timeout": "연결 시간 초과 전 시간(초, 최소 10)", - "generate_mp4": "이미지에서 mp4 만들기", - "allow_external": "알림 앱용 이미지 생성", - "custom_img": "사용자 정의 USPS '우편 없음' 이미지를 사용하시겠습니까?", - "amazon_custom_img": "사용자 정의 Amazon '배송 없음' 이미지를 사용하시겠습니까?", - "ups_custom_img": "사용자 정의 UPS '배송 없음' 이미지를 사용하시겠습니까?", - "walmart_custom_img": "사용자 정의 Walmart '배송 없음' 이미지를 사용하시겠습니까?", - "fedex_custom_img": "사용자 정의 FedEx '배송 없음' 이미지를 사용하시겠습니까?", - "generic_custom_img": "사용자 정의 일반 '배송 없음' 이미지를 사용하시겠습니까?", - "generate_grid": "LLM 비전 모델을 위한 이미지 그리드 생성", - "allow_forwarded_emails": "서비스 기본값 외에 전달된 이메일 허용 (예: no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "GIF에 USPS 이미지 없음 자리 표시자를 포함하시겠습니까?" - }, - "description": "이메일 구조 및 Home Assistant 설치에 따라 다음을 사용자 정의하여 구성을 완료하십시오. \n\n [메일 및 패키지 통합] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) 옵션에 대한 자세한 내용은 [구성, 템플릿 및 자동화 섹션] (https://github.com/moralmunky/Home-Assistant-Mail-and-Packages/wiki/Configuration-and-Email-Settings#configuration)", - "title": "메일 및 패키지 (2/2 단계)" - }, - "config_3": { - "data": { - "custom_img_file": "사용자 정의 이미지 경로: (예: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "아래에 사용자 정의 '메일 없음' 이미지의 경로와 파일 이름을 입력하세요.\n\n예시: images/custom_nomail.gif", - "title": "우편 및 패키지 (3단계 중 3단계)" - }, - "config_amazon": { - "data": { - "amazon_domain": "아마존 도메인", - "amazon_fwds": "Amazon이 전달한 이메일 주소", - "amazon_days": "Amazon 이메일을 확인하기 위해 돌아가야 할 날짜" - }, - "description": "Amazon이 이메일을 보내는 도메인을 입력해 주세요 (예: amazon.com 또는 amazon.de)\n\nAmazon에서 전달된 이메일을 사용하는 경우 각 주소를 쉼표로 구분하거나 이 설정을 지우려면 (none)을 입력하세요.\n\n참고: 이전 단계에서 전달 헤더를 구성한 경우 전달 주소 필드가 표시되지 않습니다 — 헤더가 Amazon 이메일 매칭을 자동으로 처리합니다.", - "title": "아마존 설정" - }, - "reconfig_2": { - "data": { - "folder": "메일 폴더", - "scan_interval": "스캔 간격 (분)", - "image_path": "이미지 경로", - "gif_duration": "이미지 지속 시간 (초)", - "image_security": "임의의 이미지 파일 이름", - "generate_mp4": "이미지에서 mp4 만들기", - "resources": "센서 목록", - "imap_timeout": "연결 시간 초과 전 시간(초, 최소 10)", - "allow_external": "알림 앱용 이미지 생성", - "custom_img": "사용자 정의 USPS '우편 없음' 이미지를 사용하시겠습니까?", - "amazon_custom_img": "사용자 정의 Amazon '배송 없음' 이미지를 사용하시겠습니까?", - "ups_custom_img": "사용자 정의 UPS '배송 없음' 이미지를 사용하시겠습니까?", - "walmart_custom_img": "사용자 정의 Walmart '배송 없음' 이미지를 사용하시겠습니까?", - "fedex_custom_img": "사용자 정의 FedEx '배송 없음' 이미지를 사용하시겠습니까?", - "generic_custom_img": "사용자 정의 일반 '배송 없음' 이미지를 사용하시겠습니까?", - "generate_grid": "LLM 비전 모델을 위한 이미지 그리드 생성", - "allow_forwarded_emails": "서비스 기본값 외에 전달된 이메일 허용 (예: no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "GIF에 USPS 이미지 없음 자리 표시자를 포함하시겠습니까?" - }, - "description": "이메일 구조와 Home Assistant 설치에 따라 다음을 사용자 정의하여 구성을 완료하십시오.\n\n[Mail and Packages 통합](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) 옵션에 대한 자세한 내용은 GitHub의 [구성, 템플릿, 자동화 섹션](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration)을 참조하십시오.\n\nAmazon에서 전달된 이메일을 사용하는 경우 각 주소를 쉼표로 구분하거나 이 설정을 지우려면 (none)을 입력하십시오.", - "title": "메일 및 패키지 (2/2 단계)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "사용자 정의 이미지 경로: (예: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "아래에 사용자 정의 '메일 없음' 이미지의 경로와 파일 이름을 입력하세요.\n\n예시: images/custom_nomail.gif", - "title": "우편 및 패키지 (3단계 중 3단계)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "아마존 도메인", - "amazon_fwds": "Amazon이 전달한 이메일 주소", - "amazon_days": "Amazon 이메일을 확인하기 위해 돌아가야 할 날짜" - }, - "description": "Amazon이 이메일을 보내는 도메인을 입력해 주세요 (예: amazon.com 또는 amazon.de)\n\nAmazon에서 전달된 이메일을 사용하는 경우 각 주소를 쉼표로 구분하거나 이 설정을 지우려면 (none)을 입력하세요.\n\n참고: 이전 단계에서 전달 헤더를 구성한 경우 전달 주소 필드가 표시되지 않습니다 — 헤더가 Amazon 이메일 매칭을 자동으로 처리합니다.", - "title": "아마존 설정" - }, - "config_storage": { - "data": { - "storage": "이미지를 저장할 디렉토리" - }, - "description": "이미지를 저장할 디렉토리를 입력해 주세요.\n기본값은 자동으로 채워집니다.", - "title": "이미지 저장 위치" - }, - "reconfig_storage": { - "data": { - "storage": "이미지를 저장할 디렉토리" - }, - "description": "이미지를 저장할 디렉토리를 입력해 주세요.\n기본값은 자동으로 채워집니다.", - "title": "이미지 저장 위치" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "전달 헤더 (예: X-SimpleLogin-Original-From)", - "forwarded_emails": "전달된 이메일 주소" - }, - "data_description": { - "forwarding_header": "전달 서비스가 헤더에 원래 발신자를 포함하는 경우, 여기에 헤더 이름을 입력하세요. 이는 아래 주소 목록보다 우선합니다. 주소 목록을 사용하려면 '(none)'을 입력하거나 비워두세요.", - "forwarded_emails": "메시지가 도착할 전달 주소의 쉼표로 구분된 목록입니다. 위의 전달 헤더를 사용하는 경우 '(none)'으로 두세요." - }, - "description": "전달된 이메일을 일치시키는 방법을 선택하세요.\n\n**옵션 1 — 전달 헤더**: 서비스(예: SimpleLogin)가 사용자 지정 헤더에 원래 발신자를 포함하는 경우, 해당 헤더 이름을 입력하세요. Mail and Packages가 이를 사용하여 배송사 주소를 자동으로 일치시킵니다.\n\n**옵션 2 — 주소 목록**: 서비스가 각 배송사에 할당한 전달 주소를 쉼표로 구분하여 입력하세요.", - "title": "전달 이메일 설정" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "전달 헤더 (예: X-SimpleLogin-Original-From)", - "forwarded_emails": "전달된 이메일 주소" - }, - "data_description": { - "forwarding_header": "전달 서비스가 헤더에 원래 발신자를 포함하는 경우, 여기에 헤더 이름을 입력하세요. 이는 아래 주소 목록보다 우선합니다. 주소 목록을 사용하려면 '(none)'을 입력하거나 비워두세요.", - "forwarded_emails": "메시지가 도착할 전달 주소의 쉼표로 구분된 목록입니다. 위의 전달 헤더를 사용하는 경우 '(none)'으로 두세요." - }, - "description": "전달된 이메일을 일치시키는 방법을 선택하세요.\n\n**옵션 1 — 전달 헤더**: 서비스(예: SimpleLogin)가 사용자 지정 헤더에 원래 발신자를 포함하는 경우, 해당 헤더 이름을 입력하세요. Mail and Packages가 이를 사용하여 배송사 주소를 자동으로 일치시킵니다.\n\n**옵션 2 — 주소 목록**: 서비스가 각 배송사에 할당한 전달 주소를 쉼표로 구분하여 입력하세요.", - "title": "전달 이메일 설정" - }, - "imap_config": { - "description": "메일 서버의 연결 정보를 입력해 주세요.", - "data": { - "host": "주최자", - "password": "암호", - "port": "포트", - "username": "사용자 이름", - "verify_ssl": "SSL 인증서 확인", - "imap_security": "IMAP 보안" - }, - "title": "메일 및 패키지 (1/2 단계)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "주최자", - "password": "암호", - "port": "포트", - "username": "사용자 이름", - "imap_security": "IMAP 보안", - "verify_ssl": "SSL 인증서 확인" - }, - "description": "메일 서버의 연결 정보를 입력해 주세요.", - "title": "메일 및 패키지 (1/2 단계)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages 인증 실패", - "fix_flow": { - "step": { - "confirm": { - "title": "메일 서버 재인증", - "description": "IMAP 메일 서버 인증에 실패했습니다. 제출을 클릭하여 재인증 흐름을 시작하십시오." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "메일 및 패키지 (2/2 단계)", - "description": "이메일 구조와 Home Assistant 설치에 따라 다음을 사용자 정의하여 구성을 완료하십시오.\n\n[Mail and Packages 통합](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) 옵션에 대한 자세한 내용은 GitHub의 [구성, 템플릿, 자동화 섹션](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration)을 참조하십시오.\n\nAmazon에서 전달된 이메일을 사용하는 경우 각 주소를 쉼표로 구분하거나 이 설정을 지우려면 (none)을 입력하십시오.", - "data": { - "folder": "메일 폴더", - "resources": "센서 목록", - "scan_interval": "스캔 간격 (분)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "이미지 경로", - "gif_duration": "이미지 지속 시간 (초)", - "image_security": "임의의 이미지 파일 이름", - "imap_timeout": "연결 시간 초과 전 시간(초, 최소 10)", - "generate_mp4": "이미지에서 mp4 만들기", - "allow_external": "알림 앱용 이미지 생성", - "usps_placeholder": "GIF에 USPS 이미지 없음 자리 표시자를 포함하시겠습니까?", - "custom_img": "사용자 정의 USPS '우편 없음' 이미지를 사용하시겠습니까?", - "amazon_custom_img": "사용자 정의 Amazon '배송 없음' 이미지를 사용하시겠습니까?", - "ups_custom_img": "사용자 정의 UPS '배송 없음' 이미지를 사용하시겠습니까?", - "walmart_custom_img": "사용자 정의 Walmart '배송 없음' 이미지를 사용하시겠습니까?", - "fedex_custom_img": "사용자 정의 FedEx '배송 없음' 이미지를 사용하시겠습니까?", - "generic_custom_img": "사용자 정의 일반 '배송 없음' 이미지를 사용하시겠습니까?", - "generate_grid": "LLM 비전 모델을 위한 이미지 그리드 생성", - "allow_forwarded_emails": "서비스 기본값 외에 전달된 이메일 허용 (예: no-reply@usps.com)" - } - }, - "options_3": { - "title": "우편 및 패키지 (3단계 중 3단계)", - "description": "아래에 사용자 정의 '메일 없음' 이미지의 경로와 파일 이름을 입력하세요.\n\n예시: images/custom_nomail.gif", - "data": { - "custom_img_file": "사용자 정의 이미지 경로: (예: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "아마존 설정", - "description": "Amazon이 이메일을 보내는 도메인을 입력해 주세요 (예: amazon.com 또는 amazon.de)\n\nAmazon에서 전달된 이메일을 사용하는 경우 각 주소를 쉼표로 구분하거나 이 설정을 지우려면 (none)을 입력하세요.\n\n참고: 이전 단계에서 전달 헤더를 구성한 경우 전달 주소 필드가 표시되지 않습니다 — 헤더가 Amazon 이메일 매칭을 자동으로 처리합니다.", - "data": { - "amazon_domain": "아마존 도메인", - "amazon_fwds": "Amazon이 전달한 이메일 주소", - "amazon_days": "Amazon 이메일을 확인하기 위해 돌아가야 할 날짜" - } - }, - "options_storage": { - "title": "이미지 저장 위치", - "description": "이미지를 저장할 디렉토리를 입력해 주세요.\n기본값은 자동으로 채워집니다.", - "data": { - "storage": "이미지를 저장할 디렉토리" - } - }, - "options_forwarded_emails": { - "title": "전달 이메일 설정", - "description": "전달된 이메일을 일치시키는 방법을 선택하세요.\n\n**옵션 1 — 전달 헤더**: 서비스(예: SimpleLogin)가 사용자 지정 헤더에 원래 발신자를 포함하는 경우, 해당 헤더 이름을 입력하세요. Mail and Packages가 이를 사용하여 배송사 주소를 자동으로 일치시킵니다.\n\n**옵션 2 — 주소 목록**: 서비스가 각 배송사에 할당한 전달 주소를 쉼표로 구분하여 입력하세요.", - "data": { - "forwarding_header": "전달 헤더 (예: X-SimpleLogin-Original-From)", - "forwarded_emails": "전달된 이메일 주소" - }, - "data_description": { - "forwarding_header": "전달 서비스가 헤더에 원래 발신자를 포함하는 경우, 여기에 헤더 이름을 입력하세요. 이는 아래 주소 목록보다 우선합니다. 주소 목록을 사용하려면 '(none)'을 입력하거나 비워두세요.", - "forwarded_emails": "메시지가 도착할 전달 주소의 쉼표로 구분된 목록입니다. 위의 전달 헤더를 사용하는 경우 '(none)'으로 두세요." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/nl.json b/custom_components/mail_and_packages/translations/nl.json deleted file mode 100644 index eec6cfc..0000000 --- a/custom_components/mail_and_packages/translations/nl.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Slechts een enkele configuratie van Mail en pakketten is toegestaan.", - "reconfigure_successful": "Herconfiguratie succesvol", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Kan geen verbinding maken met of inloggen op de mailserver. Controleer het logboek voor details.", - "invalid_path": "Bewaar de afbeeldingen in een andere map.", - "ffmpeg_not_found": "MP4 genereren vereist ffmpeg", - "amazon_domain": "Ongeldig doorstuur e-mailadres.", - "file_not_found": "Afbeeldingsbestand niet gevonden", - "invalid_email_format": "Ongeldig e-mailadres formaat.", - "missing_forwarded_emails": "Ontbrekende doorgestuurde e-mailadressen. U kunt '(none)' invoeren om deze instelling te wissen.", - "path_not_found": "Map niet gevonden", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "E-mailmap", - "resources": "Lijst van sensoren", - "scan_interval": "Scaninterval (minuten)", - "image_path": "Afbeeldingspad", - "gif_duration": "Beeldduur (seconden)", - "image_security": "Bestandsnaam willekeurige afbeelding", - "imap_timeout": "Tijd in seconden voordat de verbinding verloopt (seconden, minimaal 10)", - "generate_mp4": "Maak mp4 van afbeeldingen", - "allow_external": "Maak afbeelding voor meldingsapps", - "custom_img": "Gebruik aangepaste USPS 'geen post' afbeelding?", - "amazon_custom_img": "Gebruik aangepaste Amazon 'geen levering' afbeelding?", - "ups_custom_img": "Gebruik aangepaste UPS 'geen levering' afbeelding?", - "walmart_custom_img": "Gebruik aangepaste Walmart 'geen levering' afbeelding?", - "fedex_custom_img": "Gebruik aangepaste FedEx 'geen levering' afbeelding?", - "generic_custom_img": "Gebruik aangepaste generieke 'geen levering' afbeelding?", - "generate_grid": "Maak een afbeeldingsraster voor LLM-visiemodellen", - "allow_forwarded_emails": "Doorgestuurde e-mails toestaan naast de standaardwaarde van een service (bijv. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "USPS tijdelijke aanduiding zonder afbeelding opnemen in GIF?" - }, - "description": "Voltooi de configuratie door het volgende aan te passen op basis van uw e-mailstructuur en Home Assistant-installatie. \n\n Voor meer informatie over de [E-mail en pakketten integratie] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) opties bekijk de [configuratie, sjablonen , en automatisering sectie] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) op GitHub.", - "title": "Mail en pakketten (stap 2 van 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Pad naar aangepaste afbeelding: (bijv: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Voer hieronder het pad en de bestandsnaam in voor uw aangepaste afbeelding voor geen mail.\n\nVoorbeeld: images/custom_nomail.gif", - "title": "Mail en Pakketten (Stap 3 van 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon domein", - "amazon_fwds": "Amazon stuurde e-mailadressen door", - "amazon_days": "Dagen terug om te controleren op Amazon e-mails" - }, - "description": "Voer het domein in waarvan Amazon e-mails verstuurt (bijv: amazon.com of amazon.de)\n\nAls u doorgestuurde e-mails van Amazon gebruikt, scheid dan elk adres met een komma of voer (geen) in om deze instelling te wissen.\n\nOpmerking: als u in de vorige stap een doorstuurheader hebt geconfigureerd, verschijnt het doorstuuradresveld niet — de header verwerkt de Amazon e-mailkoppeling automatisch.", - "title": "Amazon Instellingen" - }, - "reconfig_2": { - "data": { - "folder": "E-mailmap", - "scan_interval": "Scaninterval (minuten)", - "image_path": "Afbeeldingspad", - "gif_duration": "Beeldduur (seconden)", - "image_security": "Bestandsnaam willekeurige afbeelding", - "generate_mp4": "Maak mp4 van afbeeldingen", - "resources": "Lijst van sensoren", - "imap_timeout": "Tijd in seconden voordat de verbinding verloopt (seconden, minimaal 10)", - "allow_external": "Maak afbeelding voor meldingsapps", - "custom_img": "Gebruik aangepaste USPS 'geen post' afbeelding?", - "amazon_custom_img": "Gebruik aangepaste Amazon 'geen levering' afbeelding?", - "ups_custom_img": "Gebruik aangepaste UPS 'geen levering' afbeelding?", - "walmart_custom_img": "Gebruik aangepaste Walmart 'geen levering' afbeelding?", - "fedex_custom_img": "Gebruik aangepaste FedEx 'geen levering' afbeelding?", - "generic_custom_img": "Gebruik aangepaste generieke 'geen levering' afbeelding?", - "generate_grid": "Maak een afbeeldingsraster voor LLM-visiemodellen", - "allow_forwarded_emails": "Doorgestuurde e-mails toestaan naast de standaardwaarde van een service (bijv. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "USPS tijdelijke aanduiding zonder afbeelding opnemen in GIF?" - }, - "description": "Rond de configuratie af door het volgende aan te passen op basis van uw e-mailstructuur en Home Assistant-installatie.\n\nVoor details over de [Mail en Packages integratie](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) opties, bekijk de [configuratie, sjablonen en automatiseringen sectie](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) op GitHub.\n\nAls u doorgestuurde e-mails van Amazon gebruikt, scheid dan elk adres met een komma of voer (geen) in om deze instelling te wissen.", - "title": "Mail en pakketten (stap 2 van 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Pad naar aangepaste afbeelding: (bijv.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Voer hieronder het pad en de bestandsnaam in voor uw aangepaste afbeelding voor geen mail.\n\nVoorbeeld: images/custom_nomail.gif", - "title": "Mail en Pakketten (Stap 3 van 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon domein", - "amazon_fwds": "Amazon stuurde e-mailadressen door", - "amazon_days": "Dagen terug om te controleren op Amazon e-mails" - }, - "description": "Voer het domein in waarvan Amazon e-mails verstuurt (bijv: amazon.com of amazon.de)\n\nAls u doorgestuurde e-mails van Amazon gebruikt, scheid dan elk adres met een komma of voer (geen) in om deze instelling te wissen.\n\nOpmerking: als u in de vorige stap een doorstuurheader hebt geconfigureerd, verschijnt het doorstuuradresveld niet — de header verwerkt de Amazon e-mailkoppeling automatisch.", - "title": "Amazon Instellingen" - }, - "config_storage": { - "data": { - "storage": "Map om afbeeldingen op te slaan" - }, - "description": "Voer de map in waar u uw afbeeldingen wilt opslaan.\nDe standaard is automatisch ingevuld.", - "title": "Locatie voor afbeeldingsopslag" - }, - "reconfig_storage": { - "data": { - "storage": "Map om afbeeldingen op te slaan" - }, - "description": "Voer de map in waar u uw afbeeldingen wilt opslaan.\nDe standaard is automatisch ingevuld.", - "title": "Locatie voor afbeeldingsopslag" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Doorstuurkoptekst (bijv. X-SimpleLogin-Original-From)", - "forwarded_emails": "Doorgestuurde e-mailadressen" - }, - "data_description": { - "forwarding_header": "Als uw doorstuurdienst de oorspronkelijke afzender in een koptekst opneemt, voer dan hier de naam van de koptekst in. Dit heeft voorrang op de onderstaande adreslijst. Voer '(none)' in of laat leeg om de adreslijst te gebruiken.", - "forwarded_emails": "Kommagescheiden lijst van doorstuuradressen waarvan berichten zullen aankomen. Laat '(none)' staan als u de bovenstaande doorstuurkoptekst gebruikt." - }, - "description": "Kies hoe doorgestuurde e-mails worden gekoppeld.\n\n**Optie 1 — Doorstuurkoptekst**: Als uw dienst (bijv. SimpleLogin) de oorspronkelijke afzender in een aangepaste koptekst opneemt, voer dan die koptekstnaam in. Mail and Packages zal die gebruiken om vervoerdersadressen automatisch te koppelen.\n\n**Optie 2 — Adreslijst**: Voer de doorstuuradressen in die uw dienst aan elke vervoerder heeft toegewezen, gescheiden door komma's.", - "title": "Instellingen doorgestuurde e-mails" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Doorstuurkoptekst (bijv. X-SimpleLogin-Original-From)", - "forwarded_emails": "Doorgestuurde e-mailadressen" - }, - "data_description": { - "forwarding_header": "Als uw doorstuurdienst de oorspronkelijke afzender in een koptekst opneemt, voer dan hier de naam van de koptekst in. Dit heeft voorrang op de onderstaande adreslijst. Voer '(none)' in of laat leeg om de adreslijst te gebruiken.", - "forwarded_emails": "Kommagescheiden lijst van doorstuuradressen waarvan berichten zullen aankomen. Laat '(none)' staan als u de bovenstaande doorstuurkoptekst gebruikt." - }, - "description": "Kies hoe doorgestuurde e-mails worden gekoppeld.\n\n**Optie 1 — Doorstuurkoptekst**: Als uw dienst (bijv. SimpleLogin) de oorspronkelijke afzender in een aangepaste koptekst opneemt, voer dan die koptekstnaam in. Mail and Packages zal die gebruiken om vervoerdersadressen automatisch te koppelen.\n\n**Optie 2 — Adreslijst**: Voer de doorstuuradressen in die uw dienst aan elke vervoerder heeft toegewezen, gescheiden door komma's.", - "title": "Instellingen doorgestuurde e-mails" - }, - "imap_config": { - "description": "Vul de verbindingsinformatie van uw mailserver in.", - "data": { - "host": "Gastheer", - "password": "Wachtwoord", - "port": "Haven", - "username": "Gebruikersnaam", - "verify_ssl": "SSL Certificaat Verifiëren", - "imap_security": "IMAP-beveiliging" - }, - "title": "Mail en pakketten (stap 1 van 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Gastheer", - "password": "Wachtwoord", - "port": "Haven", - "username": "Gebruikersnaam", - "imap_security": "IMAP-beveiliging", - "verify_ssl": "SSL Certificaat Verifiëren" - }, - "description": "Vul de verbindingsinformatie van uw mailserver in.", - "title": "Mail en pakketten (stap 1 van 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages authenticatie mislukt", - "fix_flow": { - "step": { - "confirm": { - "title": "E-mailserver opnieuw verifiëren", - "description": "Authenticatie bij uw IMAP-mailserver is mislukt. Klik op verzenden om de herauthenticatiestroom te starten." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Mail en pakketten (stap 2 van 2)", - "description": "Rond de configuratie af door het volgende aan te passen op basis van uw e-mailstructuur en Home Assistant-installatie.\n\nVoor details over de [Mail en Packages integratie](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) opties, bekijk de [configuratie, sjablonen en automatiseringen sectie](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) op GitHub.\n\nAls u doorgestuurde e-mails van Amazon gebruikt, scheid dan elk adres met een komma of voer (geen) in om deze instelling te wissen.", - "data": { - "folder": "E-mailmap", - "resources": "Lijst van sensoren", - "scan_interval": "Scaninterval (minuten)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Afbeeldingspad", - "gif_duration": "Beeldduur (seconden)", - "image_security": "Bestandsnaam willekeurige afbeelding", - "imap_timeout": "Tijd in seconden voordat de verbinding verloopt (seconden, minimaal 10)", - "generate_mp4": "Maak mp4 van afbeeldingen", - "allow_external": "Maak afbeelding voor meldingsapps", - "usps_placeholder": "USPS tijdelijke aanduiding zonder afbeelding opnemen in GIF?", - "custom_img": "Gebruik aangepaste USPS 'geen post' afbeelding?", - "amazon_custom_img": "Gebruik aangepaste Amazon 'geen levering' afbeelding?", - "ups_custom_img": "Gebruik aangepaste UPS 'geen levering' afbeelding?", - "walmart_custom_img": "Gebruik aangepaste Walmart 'geen levering' afbeelding?", - "fedex_custom_img": "Gebruik aangepaste FedEx 'geen levering' afbeelding?", - "generic_custom_img": "Gebruik aangepaste generieke 'geen levering' afbeelding?", - "generate_grid": "Maak een afbeeldingsraster voor LLM-visiemodellen", - "allow_forwarded_emails": "Doorgestuurde e-mails toestaan naast de standaardwaarde van een service (bijv. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Mail en Pakketten (Stap 3 van 3)", - "description": "Voer hieronder het pad en de bestandsnaam in voor uw aangepaste afbeelding voor geen mail.\n\nVoorbeeld: images/custom_nomail.gif", - "data": { - "custom_img_file": "Pad naar aangepaste afbeelding: (bijv.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Amazon Instellingen", - "description": "Voer het domein in waarvan Amazon e-mails verstuurt (bijv: amazon.com of amazon.de)\n\nAls u doorgestuurde e-mails van Amazon gebruikt, scheid dan elk adres met een komma of voer (geen) in om deze instelling te wissen.\n\nOpmerking: als u in de vorige stap een doorstuurheader hebt geconfigureerd, verschijnt het doorstuuradresveld niet — de header verwerkt de Amazon e-mailkoppeling automatisch.", - "data": { - "amazon_domain": "Amazon domein", - "amazon_fwds": "Amazon stuurde e-mailadressen door", - "amazon_days": "Dagen terug om te controleren op Amazon e-mails" - } - }, - "options_storage": { - "title": "Locatie voor afbeeldingsopslag", - "description": "Voer de map in waar u uw afbeeldingen wilt opslaan.\nDe standaard is automatisch ingevuld.", - "data": { - "storage": "Map om afbeeldingen op te slaan" - } - }, - "options_forwarded_emails": { - "title": "Instellingen doorgestuurde e-mails", - "description": "Kies hoe doorgestuurde e-mails worden gekoppeld.\n\n**Optie 1 — Doorstuurkoptekst**: Als uw dienst (bijv. SimpleLogin) de oorspronkelijke afzender in een aangepaste koptekst opneemt, voer dan die koptekstnaam in. Mail and Packages zal die gebruiken om vervoerdersadressen automatisch te koppelen.\n\n**Optie 2 — Adreslijst**: Voer de doorstuuradressen in die uw dienst aan elke vervoerder heeft toegewezen, gescheiden door komma's.", - "data": { - "forwarding_header": "Doorstuurkoptekst (bijv. X-SimpleLogin-Original-From)", - "forwarded_emails": "Doorgestuurde e-mailadressen" - }, - "data_description": { - "forwarding_header": "Als uw doorstuurdienst de oorspronkelijke afzender in een koptekst opneemt, voer dan hier de naam van de koptekst in. Dit heeft voorrang op de onderstaande adreslijst. Voer '(none)' in of laat leeg om de adreslijst te gebruiken.", - "forwarded_emails": "Kommagescheiden lijst van doorstuuradressen waarvan berichten zullen aankomen. Laat '(none)' staan als u de bovenstaande doorstuurkoptekst gebruikt." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/no.json b/custom_components/mail_and_packages/translations/no.json deleted file mode 100644 index 29dbd7b..0000000 --- a/custom_components/mail_and_packages/translations/no.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Bare en enkelt konfigurasjon av e-post og pakker er tillatt.", - "reconfigure_successful": "Omkonfigurering vellykket", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Kan ikke koble til eller logge inn på postserveren. Vennligst sjekk loggen for detaljer.", - "invalid_path": "Lagre bildene i en annen katalog.", - "ffmpeg_not_found": "Generer MP4 krever ffmpeg", - "amazon_domain": "Ugyldig videresendings e-postadresse.", - "file_not_found": "Bildefil ikke funnet", - "invalid_email_format": "Ugyldig e-postadresseformat.", - "missing_forwarded_emails": "Manglende videresendte e-postadresser. Du kan skrive inn '(none)' for å tømme denne innstillingen.", - "path_not_found": "Mappen ble ikke funnet", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "E-postmappe", - "resources": "Sensorliste", - "scan_interval": "Skanneintervall (minutter)", - "image_path": "Bildevei", - "gif_duration": "Bildets varighet (sekunder)", - "image_security": "Tilfeldig bilde filnavn", - "imap_timeout": "Tid i sekunder før tilkoblingen tidsavbrudd (sekunder, minimum 10)", - "generate_mp4": "Lag mp4 fra bilder", - "allow_external": "Lag bilde for varslingsapper", - "custom_img": "Bruk tilpasset USPS 'ingen post' bilde?", - "amazon_custom_img": "Bruk tilpasset Amazon 'ingen levering' bilde?", - "ups_custom_img": "Bruk tilpasset UPS 'ingen levering' bilde?", - "walmart_custom_img": "Bruk tilpasset Walmart 'ingen levering' bilde?", - "fedex_custom_img": "Bruk tilpasset FedEx 'ingen levering' bilde?", - "generic_custom_img": "Bruk tilpasset generisk 'ingen levering' bilde?", - "generate_grid": "Opprett bildegitter for LLM-visjonsmodeller", - "allow_forwarded_emails": "Tillat videresendte e-poster i tillegg til en tjenestes standardverdi (f.eks. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Inkluder USPS plassholder uten bilde i GIF?" - }, - "description": "Fullfør konfigurasjonen ved å tilpasse følgende basert på e-poststrukturen og Home Assistant-installasjonen. \n\n For detaljer om alternativene [Mail and Packages] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), les gjennom [konfigurasjon, maler , og automatiseringsdel] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) på GitHub.", - "title": "E-post og pakker (trinn 2 av 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Sti til tilpasset bilde: (f.eks: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Skriv inn stien og filnavnet til ditt tilpassede ingen post-bilde nedenfor.\n\nEksempel: bilder/tilpasset_ingenpost.gif", - "title": "Post og pakker (Trinn 3 av 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon-domene", - "amazon_fwds": "Amazon videresendte e-postadresser", - "amazon_days": "Dager tilbake for å sjekke Amazon-e-poster" - }, - "description": "Vennligst skriv inn domenet amazon sender e-poster fra (f.eks: amazon.com eller amazon.de)\n\nHvis du bruker Amazon videresendte e-poster, vennligst skill hver adresse med et komma, eller skriv inn (ingen) for å tømme denne innstillingen.\n\nMerk: hvis du konfigurerte et videresendingshode i forrige trinn, vil feltet for videresendingsadresse ikke vises — hodet håndterer Amazon e-postkoblingen automatisk.", - "title": "Amazon-innstillinger" - }, - "reconfig_2": { - "data": { - "folder": "E-postmappe", - "scan_interval": "Skanneintervall (minutter)", - "image_path": "Bildevei", - "gif_duration": "Bildets varighet (sekunder)", - "image_security": "Tilfeldig bilde filnavn", - "generate_mp4": "Lag mp4 fra bilder", - "resources": "Sensorliste", - "imap_timeout": "Tid i sekunder før tilkoblingen tidsavbrudd (sekunder, minimum 10)", - "allow_external": "Lag bilde for varslingsapper", - "custom_img": "Bruk tilpasset USPS 'ingen post' bilde?", - "amazon_custom_img": "Bruk tilpasset Amazon 'ingen levering' bilde?", - "ups_custom_img": "Bruk tilpasset UPS 'ingen levering' bilde?", - "walmart_custom_img": "Bruk tilpasset Walmart 'ingen levering' bilde?", - "fedex_custom_img": "Bruk tilpasset FedEx 'ingen levering' bilde?", - "generic_custom_img": "Bruk tilpasset generisk 'ingen levering' bilde?", - "generate_grid": "Opprett bildegitter for LLM-visjonsmodeller", - "allow_forwarded_emails": "Tillat videresendte e-poster i tillegg til en tjenestes standardverdi (f.eks. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Inkluder USPS plassholder uten bilde i GIF?" - }, - "description": "Fullfør konfigurasjonen ved å tilpasse følgende basert på din e-poststruktur og Home Assistant-installasjon.\n\nFor detaljer om [Mail and Packages-integrasjonen](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) alternativene, se gjennom [konfigurasjon, maler og automatiseringsseksjonen](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) på GitHub.\n\nHvis du bruker Amazon videresendte e-poster, vennligst skill hver adresse med et komma eller skriv inn (ingen) for å tømme denne innstillingen.", - "title": "E-post og pakker (trinn 2 av 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Sti til tilpasset bilde: (dvs.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Skriv inn stien og filnavnet til ditt tilpassede ingen post-bilde nedenfor.\n\nEksempel: bilder/tilpasset_ingenpost.gif", - "title": "Post og pakker (Trinn 3 av 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon-domene", - "amazon_fwds": "Amazon videresendte e-postadresser", - "amazon_days": "Dager tilbake for å sjekke Amazon-e-poster" - }, - "description": "Vennligst skriv inn domenet amazon sender e-poster fra (f.eks: amazon.com eller amazon.de)\n\nHvis du bruker Amazon videresendte e-poster, vennligst skill hver adresse med et komma, eller skriv inn (ingen) for å tømme denne innstillingen.\n\nMerk: hvis du konfigurerte et videresendingshode i forrige trinn, vil feltet for videresendingsadresse ikke vises — hodet håndterer Amazon e-postkoblingen automatisk.", - "title": "Amazon-innstillinger" - }, - "config_storage": { - "data": { - "storage": "Katalog for å lagre bilder" - }, - "description": "Vennligst oppgi mappen du vil at bildene dine skal lagres i.\nStandard er automatisk fylt ut.", - "title": "Bildelagringssted" - }, - "reconfig_storage": { - "data": { - "storage": "Katalog for å lagre bilder" - }, - "description": "Vennligst oppgi mappen du vil at bildene dine skal lagres i.\nStandard er automatisk fylt ut.", - "title": "Bildelagringssted" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Videresendingshode (f.eks. X-SimpleLogin-Original-From)", - "forwarded_emails": "Videresendte e-postadresser" - }, - "data_description": { - "forwarding_header": "Hvis videresendingstjenesten din inkluderer den opprinnelige avsenderen i en topptekst, skriv inn topptekstnavnet her. Dette har forrang over adresselisten nedenfor. Skriv inn '(none)' eller la det stå tomt for å bruke adresselisten i stedet.", - "forwarded_emails": "Kommaseparert liste over videresendingsadresser meldinger vil komme fra. La '(none)' stå hvis du bruker videresendingshodet ovenfor." - }, - "description": "Velg hvordan videresendte e-poster skal matches.\n\n**Alternativ 1 — Videresendingshode**: Hvis tjenesten din (f.eks. SimpleLogin) inkluderer den opprinnelige avsenderen i et tilpasset hode, skriv inn det hodet. Mail and Packages vil bruke det til å matche fraktadresser automatisk.\n\n**Alternativ 2 — Adresseliste**: Skriv inn videresendingsadressene tjenesten din har tildelt hver fraktfører, atskilt med komma.", - "title": "Innstillinger for videresendte e-poster" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Videresendingshode (f.eks. X-SimpleLogin-Original-From)", - "forwarded_emails": "Videresendte e-postadresser" - }, - "data_description": { - "forwarding_header": "Hvis videresendingstjenesten din inkluderer den opprinnelige avsenderen i en topptekst, skriv inn topptekstnavnet her. Dette har forrang over adresselisten nedenfor. Skriv inn '(none)' eller la det stå tomt for å bruke adresselisten i stedet.", - "forwarded_emails": "Kommaseparert liste over videresendingsadresser meldinger vil komme fra. La '(none)' stå hvis du bruker videresendingshodet ovenfor." - }, - "description": "Velg hvordan videresendte e-poster skal matches.\n\n**Alternativ 1 — Videresendingshode**: Hvis tjenesten din (f.eks. SimpleLogin) inkluderer den opprinnelige avsenderen i et tilpasset hode, skriv inn det hodet. Mail and Packages vil bruke det til å matche fraktadresser automatisk.\n\n**Alternativ 2 — Adresseliste**: Skriv inn videresendingsadressene tjenesten din har tildelt hver fraktfører, atskilt med komma.", - "title": "Innstillinger for videresendte e-poster" - }, - "imap_config": { - "description": "Vennligst oppgi tilkoblingsinformasjonen til din e-postserver.", - "data": { - "host": "Vert", - "password": "Passord", - "port": "Havn", - "username": "Brukernavn", - "verify_ssl": "Bekreft SSL-sertifikat", - "imap_security": "IMAP-sikkerhet" - }, - "title": "E-post og pakker (trinn 1 av 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Vert", - "password": "Passord", - "port": "Havn", - "username": "Brukernavn", - "imap_security": "IMAP-sikkerhet", - "verify_ssl": "Bekreft SSL-sertifikat" - }, - "description": "Vennligst oppgi tilkoblingsinformasjonen til din e-postserver.", - "title": "E-post og pakker (trinn 1 av 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Autentisering for Mail and Packages mislyktes", - "fix_flow": { - "step": { - "confirm": { - "title": "Autentiser e-postserver på nytt", - "description": "Autentisering mot din IMAP-e-postserver mislyktes. Klikk på send for å starte reautentiseringsflyten." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "E-post og pakker (trinn 2 av 2)", - "description": "Fullfør konfigurasjonen ved å tilpasse følgende basert på din e-poststruktur og Home Assistant-installasjon.\n\nFor detaljer om [Mail and Packages-integrasjonen](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) alternativene, se gjennom [konfigurasjon, maler og automatiseringsseksjonen](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) på GitHub.\n\nHvis du bruker Amazon videresendte e-poster, vennligst skill hver adresse med et komma eller skriv inn (ingen) for å tømme denne innstillingen.", - "data": { - "folder": "E-postmappe", - "resources": "Sensorliste", - "scan_interval": "Skanneintervall (minutter)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Bildevei", - "gif_duration": "Bildets varighet (sekunder)", - "image_security": "Tilfeldig bilde filnavn", - "imap_timeout": "Tid i sekunder før tilkoblingen tidsavbrudd (sekunder, minimum 10)", - "generate_mp4": "Lag mp4 fra bilder", - "allow_external": "Lag bilde for varslingsapper", - "usps_placeholder": "Inkluder USPS plassholder uten bilde i GIF?", - "custom_img": "Bruk tilpasset USPS 'ingen post' bilde?", - "amazon_custom_img": "Bruk tilpasset Amazon 'ingen levering' bilde?", - "ups_custom_img": "Bruk tilpasset UPS 'ingen levering' bilde?", - "walmart_custom_img": "Bruk tilpasset Walmart 'ingen levering' bilde?", - "fedex_custom_img": "Bruk tilpasset FedEx 'ingen levering' bilde?", - "generic_custom_img": "Bruk tilpasset generisk 'ingen levering' bilde?", - "generate_grid": "Opprett bildegitter for LLM-visjonsmodeller", - "allow_forwarded_emails": "Tillat videresendte e-poster i tillegg til en tjenestes standardverdi (f.eks. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Post og pakker (Trinn 3 av 3)", - "description": "Skriv inn stien og filnavnet til ditt tilpassede ingen post-bilde nedenfor.\n\nEksempel: bilder/tilpasset_ingenpost.gif", - "data": { - "custom_img_file": "Sti til tilpasset bilde: (dvs.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Amazon-innstillinger", - "description": "Vennligst skriv inn domenet amazon sender e-poster fra (f.eks: amazon.com eller amazon.de)\n\nHvis du bruker Amazon videresendte e-poster, vennligst skill hver adresse med et komma, eller skriv inn (ingen) for å tømme denne innstillingen.\n\nMerk: hvis du konfigurerte et videresendingshode i forrige trinn, vil feltet for videresendingsadresse ikke vises — hodet håndterer Amazon e-postkoblingen automatisk.", - "data": { - "amazon_domain": "Amazon-domene", - "amazon_fwds": "Amazon videresendte e-postadresser", - "amazon_days": "Dager tilbake for å sjekke Amazon-e-poster" - } - }, - "options_storage": { - "title": "Bildelagringssted", - "description": "Vennligst oppgi mappen du vil at bildene dine skal lagres i.\nStandard er automatisk fylt ut.", - "data": { - "storage": "Katalog for å lagre bilder" - } - }, - "options_forwarded_emails": { - "title": "Innstillinger for videresendte e-poster", - "description": "Velg hvordan videresendte e-poster skal matches.\n\n**Alternativ 1 — Videresendingshode**: Hvis tjenesten din (f.eks. SimpleLogin) inkluderer den opprinnelige avsenderen i et tilpasset hode, skriv inn det hodet. Mail and Packages vil bruke det til å matche fraktadresser automatisk.\n\n**Alternativ 2 — Adresseliste**: Skriv inn videresendingsadressene tjenesten din har tildelt hver fraktfører, atskilt med komma.", - "data": { - "forwarding_header": "Videresendingshode (f.eks. X-SimpleLogin-Original-From)", - "forwarded_emails": "Videresendte e-postadresser" - }, - "data_description": { - "forwarding_header": "Hvis videresendingstjenesten din inkluderer den opprinnelige avsenderen i en topptekst, skriv inn topptekstnavnet her. Dette har forrang over adresselisten nedenfor. Skriv inn '(none)' eller la det stå tomt for å bruke adresselisten i stedet.", - "forwarded_emails": "Kommaseparert liste over videresendingsadresser meldinger vil komme fra. La '(none)' stå hvis du bruker videresendingshodet ovenfor." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/pl.json b/custom_components/mail_and_packages/translations/pl.json deleted file mode 100644 index fcb0dfc..0000000 --- a/custom_components/mail_and_packages/translations/pl.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Dozwolona jest tylko jedna konfiguracja poczty i pakietów.", - "reconfigure_successful": "Pomyślnie przekonfigurowano", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Nie można połączyć się lub zalogować do serwera pocztowego. Sprawdź szczegóły w dzienniku.", - "invalid_path": "Proszę przechowywać obrazy w innym katalogu.", - "ffmpeg_not_found": "Generowanie MP4 wymaga ffmpeg", - "amazon_domain": "Nieprawidłowy adres e-mail do przekierowania.", - "file_not_found": "Nie znaleziono pliku obrazu", - "invalid_email_format": "Nieprawidłowy format adresu e-mail.", - "missing_forwarded_emails": "Brak przekierowanych adresów e-mail. Możesz wprowadzić '(none)', aby wyczyścić to ustawienie.", - "path_not_found": "Katalog nie został znaleziony", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Folder poczty", - "resources": "Lista czujników", - "scan_interval": "Interwał skanowania (minuty)", - "image_path": "Ścieżka obrazu", - "gif_duration": "Czas trwania obrazu (sekundy)", - "image_security": "Losowa nazwa pliku obrazu", - "imap_timeout": "Czas w sekundach przed wygaśnięciem połączenia (sekundy, minimum 10)", - "generate_mp4": "Utwórz mp4 z obrazów", - "allow_external": "Utwórz obraz dla aplikacji powiadomień", - "custom_img": "Użyć niestandardowego obrazu USPS 'brak poczty'?", - "amazon_custom_img": "Użyć niestandardowego obrazu Amazon 'brak dostawy'?", - "ups_custom_img": "Użyć niestandardowego obrazu UPS 'brak dostawy'?", - "walmart_custom_img": "Użyć niestandardowego obrazu Walmart 'brak dostawy'?", - "fedex_custom_img": "Użyć niestandardowego obrazu FedEx 'brak dostawy'?", - "generic_custom_img": "Użyć niestandardowego obrazu ogólnego 'brak dostawy'?", - "generate_grid": "Utwórz siatkę obrazów dla modeli wizji LLM", - "allow_forwarded_emails": "Zezwalaj na przekierowane e-maile oprócz wartości domyślnej usługi (np. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Dołączyć zarezerwowane miejsce dla USPS bez obrazu do pliku GIF?" - }, - "description": "Zakończ konfigurację, dostosowując następujące elementy w oparciu o strukturę poczty e-mail i instalację Home Assistant. \n\n Aby uzyskać szczegółowe informacje na temat opcji [Integracja poczty i pakietów] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) sprawdź opcje [konfiguracja, szablony i sekcja automatyzacji] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHub.", - "title": "Poczta i paczki (krok 2 z 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Ścieżka do niestandardowego obrazu: (np.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Wprowadź ścieżkę i nazwę pliku do swojego niestandardowego obrazu bez maila poniżej.\n\nPrzykład: images/custom_nomail.gif", - "title": "Poczta i paczki (Krok 3 z 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Domena Amazon", - "amazon_fwds": "Amazon przekazał adresy e-mail", - "amazon_days": "Dni do sprawdzenia dla wiadomości e-mail od Amazon" - }, - "description": "Wprowadź domenę, z której Amazon wysyła e-maile (np. amazon.com lub amazon.de)\n\nJeśli korzystasz z przekierowanych e-maili od Amazon, oddziel każdy adres przecinkiem lub wpisz (brak), aby wyczyścić to ustawienie.\n\nUwaga: jeśli w poprzednim kroku skonfigurowałeś nagłówek przekierowania, pole adresu przekierowania nie pojawi się — nagłówek automatycznie obsługuje dopasowywanie e-maili Amazon.", - "title": "Ustawienia Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Folder poczty", - "scan_interval": "Interwał skanowania (minuty)", - "image_path": "Ścieżka obrazu", - "gif_duration": "Czas trwania obrazu (sekundy)", - "image_security": "Losowa nazwa pliku obrazu", - "generate_mp4": "Utwórz mp4 z obrazów", - "resources": "Lista czujników", - "imap_timeout": "Czas w sekundach przed wygaśnięciem połączenia (sekundy, minimum 10)", - "allow_external": "Utwórz obraz dla aplikacji powiadomień", - "custom_img": "Użyć niestandardowego obrazu USPS 'brak poczty'?", - "amazon_custom_img": "Użyć niestandardowego obrazu Amazon 'brak dostawy'?", - "ups_custom_img": "Użyć niestandardowego obrazu UPS 'brak dostawy'?", - "walmart_custom_img": "Użyć niestandardowego obrazu Walmart 'brak dostawy'?", - "fedex_custom_img": "Użyć niestandardowego obrazu FedEx 'brak dostawy'?", - "generic_custom_img": "Użyć niestandardowego obrazu ogólnego 'brak dostawy'?", - "generate_grid": "Utwórz siatkę obrazów dla modeli wizji LLM", - "allow_forwarded_emails": "Zezwalaj na przekierowane e-maile oprócz wartości domyślnej usługi (np. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Dołączyć zarezerwowane miejsce dla USPS bez obrazu do pliku GIF?" - }, - "description": "Zakończ konfigurację, dostosowując następujące elementy do struktury swojego e-maila i instalacji Home Assistant.\n\nAby uzyskać szczegółowe informacje na temat opcji [integracji Mail and Packages](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), zapoznaj się z [sekcją konfiguracji, szablonów i automatyzacji](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHubie.\n\nJeśli korzystasz z przekierowanych e-maili Amazon, oddziel każdy adres przecinkiem lub wprowadź (brak), aby wyczyścić to ustawienie.", - "title": "Poczta i paczki (krok 2 z 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Ścieżka do niestandardowego obrazu: (np.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Wprowadź ścieżkę i nazwę pliku do swojego niestandardowego obrazu bez maila poniżej.\n\nPrzykład: images/custom_nomail.gif", - "title": "Poczta i paczki (Krok 3 z 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Domena Amazon", - "amazon_fwds": "Amazon przekazał adresy e-mail", - "amazon_days": "Dni do sprawdzenia dla wiadomości e-mail od Amazon" - }, - "description": "Wprowadź domenę, z której Amazon wysyła e-maile (np. amazon.com lub amazon.de)\n\nJeśli korzystasz z przekierowanych e-maili od Amazon, oddziel każdy adres przecinkiem lub wpisz (brak), aby wyczyścić to ustawienie.\n\nUwaga: jeśli w poprzednim kroku skonfigurowałeś nagłówek przekierowania, pole adresu przekierowania nie pojawi się — nagłówek automatycznie obsługuje dopasowywanie e-maili Amazon.", - "title": "Ustawienia Amazon" - }, - "config_storage": { - "data": { - "storage": "Katalog do przechowywania obrazów" - }, - "description": "Wprowadź katalog, w którym chciałbyś przechowywać swoje obrazy.\nDomyślnie jest on automatycznie wypełniany.", - "title": "Lokalizacja przechowywania obrazów" - }, - "reconfig_storage": { - "data": { - "storage": "Katalog do przechowywania obrazów" - }, - "description": "Wprowadź katalog, w którym chciałbyś przechowywać swoje obrazy.\nDomyślnie jest on automatycznie wypełniany.", - "title": "Lokalizacja przechowywania obrazów" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Nagłówek przekierowania (np. X-SimpleLogin-Original-From)", - "forwarded_emails": "Przekierowane adresy e-mail" - }, - "data_description": { - "forwarding_header": "Jeśli Twoja usługa przekierowania zawiera oryginalnego nadawcę w nagłówku, wprowadź tutaj nazwę nagłówka. Ma to pierwszeństwo przed poniższą listą adresów. Wprowadź '(none)' lub pozostaw puste, aby zamiast tego użyć listy adresów.", - "forwarded_emails": "Lista oddzielona przecinkami adresów przekierowania, z których będą przychodziły wiadomości. Pozostaw '(none)', jeśli używasz powyższego nagłówka przekierowania." - }, - "description": "Wybierz sposób dopasowywania przekierowanych wiadomości e-mail.\n\n**Opcja 1 — Nagłówek przekierowania**: Jeśli Twoja usługa (np. SimpleLogin) zawiera oryginalnego nadawcę w niestandardowym nagłówku, wprowadź nazwę tego nagłówka. Mail and Packages użyje go do automatycznego dopasowywania adresów przewoźników.\n\n**Opcja 2 — Lista adresów**: Wprowadź adresy przekierowania przypisane przez usługę do każdego przewoźnika, oddzielone przecinkami.", - "title": "Ustawienia przekierowanych wiadomości e-mail" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Nagłówek przekierowania (np. X-SimpleLogin-Original-From)", - "forwarded_emails": "Przekierowane adresy e-mail" - }, - "data_description": { - "forwarding_header": "Jeśli Twoja usługa przekierowania zawiera oryginalnego nadawcę w nagłówku, wprowadź tutaj nazwę nagłówka. Ma to pierwszeństwo przed poniższą listą adresów. Wprowadź '(none)' lub pozostaw puste, aby zamiast tego użyć listy adresów.", - "forwarded_emails": "Lista oddzielona przecinkami adresów przekierowania, z których będą przychodziły wiadomości. Pozostaw '(none)', jeśli używasz powyższego nagłówka przekierowania." - }, - "description": "Wybierz sposób dopasowywania przekierowanych wiadomości e-mail.\n\n**Opcja 1 — Nagłówek przekierowania**: Jeśli Twoja usługa (np. SimpleLogin) zawiera oryginalnego nadawcę w niestandardowym nagłówku, wprowadź nazwę tego nagłówka. Mail and Packages użyje go do automatycznego dopasowywania adresów przewoźników.\n\n**Opcja 2 — Lista adresów**: Wprowadź adresy przekierowania przypisane przez usługę do każdego przewoźnika, oddzielone przecinkami.", - "title": "Ustawienia przekierowanych wiadomości e-mail" - }, - "imap_config": { - "description": "Proszę wprowadzić informacje o połączeniu z serwerem pocztowym.", - "data": { - "host": "Gospodarz", - "password": "Hasło", - "port": "Port", - "username": "Nazwa Użytkownika", - "verify_ssl": "Sprawdź certyfikat SSL", - "imap_security": "Bezpieczeństwo IMAP" - }, - "title": "Poczta i paczki (krok 1 z 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Gospodarz", - "password": "Hasło", - "port": "Port", - "username": "Nazwa Użytkownika", - "imap_security": "Bezpieczeństwo IMAP", - "verify_ssl": "Sprawdź certyfikat SSL" - }, - "description": "Proszę wprowadzić informacje o połączeniu z serwerem pocztowym.", - "title": "Poczta i paczki (krok 1 z 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Błąd uwierzytelniania Mail and Packages", - "fix_flow": { - "step": { - "confirm": { - "title": "Ponownie uwierzytelnij serwer pocztowy", - "description": "Uwierzytelnienie na serwerze pocztowym IMAP nie powiodło się. Kliknij prześlij, aby rozpocząć proces ponownego uwierzytelniania." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Poczta i paczki (krok 2 z 2)", - "description": "Zakończ konfigurację, dostosowując następujące elementy do struktury swojego e-maila i instalacji Home Assistant.\n\nAby uzyskać szczegółowe informacje na temat opcji [integracji Mail and Packages](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), zapoznaj się z [sekcją konfiguracji, szablonów i automatyzacji](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHubie.\n\nJeśli korzystasz z przekierowanych e-maili Amazon, oddziel każdy adres przecinkiem lub wprowadź (brak), aby wyczyścić to ustawienie.", - "data": { - "folder": "Folder poczty", - "resources": "Lista czujników", - "scan_interval": "Interwał skanowania (minuty)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Ścieżka obrazu", - "gif_duration": "Czas trwania obrazu (sekundy)", - "image_security": "Losowa nazwa pliku obrazu", - "imap_timeout": "Czas w sekundach przed wygaśnięciem połączenia (sekundy, minimum 10)", - "generate_mp4": "Utwórz mp4 z obrazów", - "allow_external": "Utwórz obraz dla aplikacji powiadomień", - "usps_placeholder": "Dołączyć zarezerwowane miejsce dla USPS bez obrazu do pliku GIF?", - "custom_img": "Użyć niestandardowego obrazu USPS 'brak poczty'?", - "amazon_custom_img": "Użyć niestandardowego obrazu Amazon 'brak dostawy'?", - "ups_custom_img": "Użyć niestandardowego obrazu UPS 'brak dostawy'?", - "walmart_custom_img": "Użyć niestandardowego obrazu Walmart 'brak dostawy'?", - "fedex_custom_img": "Użyć niestandardowego obrazu FedEx 'brak dostawy'?", - "generic_custom_img": "Użyć niestandardowego obrazu ogólnego 'brak dostawy'?", - "generate_grid": "Utwórz siatkę obrazów dla modeli wizji LLM", - "allow_forwarded_emails": "Zezwalaj na przekierowane e-maile oprócz wartości domyślnej usługi (np. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Poczta i paczki (Krok 3 z 3)", - "description": "Wprowadź ścieżkę i nazwę pliku do swojego niestandardowego obrazu bez maila poniżej.\n\nPrzykład: images/custom_nomail.gif", - "data": { - "custom_img_file": "Ścieżka do niestandardowego obrazu: (np.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Ustawienia Amazon", - "description": "Wprowadź domenę, z której Amazon wysyła e-maile (np. amazon.com lub amazon.de)\n\nJeśli korzystasz z przekierowanych e-maili od Amazon, oddziel każdy adres przecinkiem lub wpisz (brak), aby wyczyścić to ustawienie.\n\nUwaga: jeśli w poprzednim kroku skonfigurowałeś nagłówek przekierowania, pole adresu przekierowania nie pojawi się — nagłówek automatycznie obsługuje dopasowywanie e-maili Amazon.", - "data": { - "amazon_domain": "Domena Amazon", - "amazon_fwds": "Amazon przekazał adresy e-mail", - "amazon_days": "Dni do sprawdzenia dla wiadomości e-mail od Amazon" - } - }, - "options_storage": { - "title": "Lokalizacja przechowywania obrazów", - "description": "Wprowadź katalog, w którym chciałbyś przechowywać swoje obrazy.\nDomyślnie jest on automatycznie wypełniany.", - "data": { - "storage": "Katalog do przechowywania obrazów" - } - }, - "options_forwarded_emails": { - "title": "Ustawienia przekierowanych wiadomości e-mail", - "description": "Wybierz sposób dopasowywania przekierowanych wiadomości e-mail.\n\n**Opcja 1 — Nagłówek przekierowania**: Jeśli Twoja usługa (np. SimpleLogin) zawiera oryginalnego nadawcę w niestandardowym nagłówku, wprowadź nazwę tego nagłówka. Mail and Packages użyje go do automatycznego dopasowywania adresów przewoźników.\n\n**Opcja 2 — Lista adresów**: Wprowadź adresy przekierowania przypisane przez usługę do każdego przewoźnika, oddzielone przecinkami.", - "data": { - "forwarding_header": "Nagłówek przekierowania (np. X-SimpleLogin-Original-From)", - "forwarded_emails": "Przekierowane adresy e-mail" - }, - "data_description": { - "forwarding_header": "Jeśli Twoja usługa przekierowania zawiera oryginalnego nadawcę w nagłówku, wprowadź tutaj nazwę nagłówka. Ma to pierwszeństwo przed poniższą listą adresów. Wprowadź '(none)' lub pozostaw puste, aby zamiast tego użyć listy adresów.", - "forwarded_emails": "Lista oddzielona przecinkami adresów przekierowania, z których będą przychodziły wiadomości. Pozostaw '(none)', jeśli używasz powyższego nagłówka przekierowania." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/pt.json b/custom_components/mail_and_packages/translations/pt.json deleted file mode 100644 index 60add51..0000000 --- a/custom_components/mail_and_packages/translations/pt.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Somente uma única configuração de correio e pacotes é permitida.", - "reconfigure_successful": "Reconfiguração bem-sucedida", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Não foi possível conectar ou fazer login no servidor de email. Por favor, verifique o log para obter detalhes.", - "invalid_path": "Por favor, guarde as imagens em outro diretório.", - "ffmpeg_not_found": "Gerar MP4 requer ffmpeg", - "amazon_domain": "Endereço de encaminhamento de email inválido.", - "file_not_found": "Arquivo de imagem não encontrado", - "invalid_email_format": "Formato de endereço de email inválido.", - "missing_forwarded_emails": "Endereços de email encaminhados em falta. Pode introduzir '(none)' para limpar esta configuração.", - "path_not_found": "Diretório não encontrado", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Pasta de Correio", - "resources": "Lista de Sensores", - "scan_interval": "Intervalo de digitalização (minutos)", - "image_path": "Caminho da imagem", - "gif_duration": "Duração da imagem (segundos)", - "image_security": "Nome da imagem aleatória", - "imap_timeout": "Tempo em segundos antes do tempo limite de conexão (segundos, mínimo 10)", - "generate_mp4": "Crie mp4 a partir de imagens", - "allow_external": "Crie imagem para aplicativos de notificação", - "custom_img": "Usar imagem personalizada USPS 'sem correio'?", - "amazon_custom_img": "Usar imagem personalizada Amazon 'sem entrega'?", - "ups_custom_img": "Usar imagem personalizada UPS 'sem entrega'?", - "walmart_custom_img": "Usar imagem personalizada Walmart 'sem entrega'?", - "fedex_custom_img": "Usar imagem personalizada FedEx 'sem entrega'?", - "generic_custom_img": "Usar imagem personalizada genérica 'sem entrega'?", - "generate_grid": "Criar grade de imagens para modelos de visão LLM", - "allow_forwarded_emails": "Permitir emails encaminhados além do padrão de um serviço (ex: no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Incluir marcador de posição sem imagem USPS no GIF?" - }, - "description": "Conclua a configuração, personalizando o seguinte com base na sua estrutura de email e instalação do Home Assistant. \n\n Para obter detalhes sobre as opções [integração de Mail e Pacotes] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), revise as opções de [configuração, modelos e seção de automações] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) no GitHub.", - "title": "Correio e pacotes (Etapa 2 de 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Caminho para imagem personalizada: (ex: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Insira o caminho e o nome do arquivo para a sua imagem personalizada de sem correio abaixo.\n\nExemplo: images/custom_nomail.gif", - "title": "Correio e Pacotes (Etapa 3 de 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Domínio da Amazon", - "amazon_fwds": "A Amazon encaminhou endereços de email", - "amazon_days": "Dias atrás para verificar emails da Amazon" - }, - "description": "Por favor, insira o domínio de onde a Amazon envia e-mails (por exemplo: amazon.com ou amazon.de)\n\nSe estiver usando e-mails encaminhados pela Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.\n\nNota: se configurou um cabeçalho de reencaminhamento no passo anterior, o campo de endereço de reencaminhamento não aparecerá — o cabeçalho trata automaticamente da correspondência de e-mails da Amazon.", - "title": "Configurações da Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Pasta de Correio", - "scan_interval": "Intervalo de digitalização (minutos)", - "image_path": "Caminho da imagem", - "gif_duration": "Duração da imagem (segundos)", - "image_security": "Nome da imagem aleatória", - "generate_mp4": "Crie mp4 a partir de imagens", - "resources": "Lista de Sensores", - "imap_timeout": "Tempo em segundos antes do tempo limite de conexão (segundos, mínimo 10)", - "allow_external": "Crie imagem para aplicativos de notificação", - "custom_img": "Usar imagem personalizada USPS 'sem correio'?", - "amazon_custom_img": "Usar imagem personalizada Amazon 'sem entrega'?", - "ups_custom_img": "Usar imagem personalizada UPS 'sem entrega'?", - "walmart_custom_img": "Usar imagem personalizada Walmart 'sem entrega'?", - "fedex_custom_img": "Usar imagem personalizada FedEx 'sem entrega'?", - "generic_custom_img": "Usar imagem personalizada genérica 'sem entrega'?", - "generate_grid": "Criar grade de imagens para modelos de visão LLM", - "allow_forwarded_emails": "Permitir emails encaminhados além do padrão de um serviço (ex: no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Incluir marcador de posição sem imagem USPS no GIF?" - }, - "description": "Conclua a configuração personalizando o seguinte com base na estrutura do seu email e na instalação do Home Assistant.\n\nPara detalhes sobre as opções de [integração de Correio e Pacotes](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), consulte a [seção de configuração, modelos e automações](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) no GitHub.\n\nSe estiver usando emails encaminhados da Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.", - "title": "Correio e pacotes (Etapa 2 de 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Caminho para imagem personalizada: (ex.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Insira o caminho e o nome do arquivo para a sua imagem personalizada de sem correio abaixo.\n\nExemplo: images/custom_nomail.gif", - "title": "Correio e Pacotes (Etapa 3 de 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Domínio da Amazon", - "amazon_fwds": "A Amazon encaminhou endereços de email", - "amazon_days": "Dias atrás para verificar emails da Amazon" - }, - "description": "Por favor, insira o domínio de onde a Amazon envia e-mails (por exemplo: amazon.com ou amazon.de)\n\nSe estiver usando e-mails encaminhados pela Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.\n\nNota: se configurou um cabeçalho de reencaminhamento no passo anterior, o campo de endereço de reencaminhamento não aparecerá — o cabeçalho trata automaticamente da correspondência de e-mails da Amazon.", - "title": "Configurações da Amazon" - }, - "config_storage": { - "data": { - "storage": "Diretório para armazenar imagens" - }, - "description": "Por favor, insira o diretório em que você gostaria que suas imagens fossem armazenadas.\nO padrão é preenchido automaticamente.", - "title": "Local de armazenamento de imagens" - }, - "reconfig_storage": { - "data": { - "storage": "Diretório para armazenar imagens" - }, - "description": "Por favor, insira o diretório em que você gostaria que suas imagens fossem armazenadas.\nO padrão é preenchido automaticamente.", - "title": "Local de armazenamento de imagens" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Cabeçalho de reencaminhamento (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Endereços de email encaminhados" - }, - "data_description": { - "forwarding_header": "Se o seu serviço de reencaminhamento inclui o remetente original num cabeçalho, introduza aqui o nome do cabeçalho. Isto tem prioridade sobre a lista de endereços abaixo. Introduza '(none)' ou deixe em branco para usar a lista de endereços.", - "forwarded_emails": "Lista separada por vírgulas dos endereços de reencaminhamento de onde chegarão as mensagens. Deixe '(none)' se estiver a usar o cabeçalho de reencaminhamento acima." - }, - "description": "Escolha como fazer a correspondência dos e-mails reencaminhados.\n\n**Opção 1 — Cabeçalho de reencaminhamento**: Se o seu serviço (ex. SimpleLogin) inclui o remetente original num cabeçalho personalizado, introduza esse nome de cabeçalho. O Mail and Packages irá utilizá-lo para fazer a correspondência automática dos endereços das transportadoras.\n\n**Opção 2 — Lista de endereços**: Introduza os endereços de reencaminhamento que o seu serviço atribuiu a cada transportadora, separados por vírgulas.", - "title": "Definições de e-mails reencaminhados" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Cabeçalho de reencaminhamento (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Endereços de email encaminhados" - }, - "data_description": { - "forwarding_header": "Se o seu serviço de reencaminhamento inclui o remetente original num cabeçalho, introduza aqui o nome do cabeçalho. Isto tem prioridade sobre a lista de endereços abaixo. Introduza '(none)' ou deixe em branco para usar a lista de endereços.", - "forwarded_emails": "Lista separada por vírgulas dos endereços de reencaminhamento de onde chegarão as mensagens. Deixe '(none)' se estiver a usar o cabeçalho de reencaminhamento acima." - }, - "description": "Escolha como fazer a correspondência dos e-mails reencaminhados.\n\n**Opção 1 — Cabeçalho de reencaminhamento**: Se o seu serviço (ex. SimpleLogin) inclui o remetente original num cabeçalho personalizado, introduza esse nome de cabeçalho. O Mail and Packages irá utilizá-lo para fazer a correspondência automática dos endereços das transportadoras.\n\n**Opção 2 — Lista de endereços**: Introduza os endereços de reencaminhamento que o seu serviço atribuiu a cada transportadora, separados por vírgulas.", - "title": "Definições de e-mails reencaminhados" - }, - "imap_config": { - "description": "Por favor, insira as informações de conexão do seu servidor de email.", - "data": { - "host": "Hospedeiro", - "password": "Senha", - "port": "Porta", - "username": "Nome do usuário", - "verify_ssl": "Verificar Certificado SSL", - "imap_security": "Segurança IMAP" - }, - "title": "Correio e pacotes (Etapa 1 de 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Hospedeiro", - "password": "Senha", - "port": "Porta", - "username": "Nome do usuário", - "imap_security": "Segurança IMAP", - "verify_ssl": "Verificar Certificado SSL" - }, - "description": "Por favor, insira as informações de conexão do seu servidor de email.", - "title": "Correio e pacotes (Etapa 1 de 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Falha na autenticação de Mail and Packages", - "fix_flow": { - "step": { - "confirm": { - "title": "Reautenticar o Servidor de E-mail", - "description": "A autenticação no seu servidor de e-mail IMAP falhou. Clique em enviar para iniciar o fluxo de reautenticação." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Correio e pacotes (Etapa 2 de 2)", - "description": "Conclua a configuração personalizando o seguinte com base na estrutura do seu email e na instalação do Home Assistant.\n\nPara detalhes sobre as opções de [integração de Correio e Pacotes](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), consulte a [seção de configuração, modelos e automações](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) no GitHub.\n\nSe estiver usando emails encaminhados da Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.", - "data": { - "folder": "Pasta de Correio", - "resources": "Lista de Sensores", - "scan_interval": "Intervalo de digitalização (minutos)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Caminho da imagem", - "gif_duration": "Duração da imagem (segundos)", - "image_security": "Nome da imagem aleatória", - "imap_timeout": "Tempo em segundos antes do tempo limite de conexão (segundos, mínimo 10)", - "generate_mp4": "Crie mp4 a partir de imagens", - "allow_external": "Crie imagem para aplicativos de notificação", - "usps_placeholder": "Incluir marcador de posição sem imagem USPS no GIF?", - "custom_img": "Usar imagem personalizada USPS 'sem correio'?", - "amazon_custom_img": "Usar imagem personalizada Amazon 'sem entrega'?", - "ups_custom_img": "Usar imagem personalizada UPS 'sem entrega'?", - "walmart_custom_img": "Usar imagem personalizada Walmart 'sem entrega'?", - "fedex_custom_img": "Usar imagem personalizada FedEx 'sem entrega'?", - "generic_custom_img": "Usar imagem personalizada genérica 'sem entrega'?", - "generate_grid": "Criar grade de imagens para modelos de visão LLM", - "allow_forwarded_emails": "Permitir emails encaminhados além do padrão de um serviço (ex: no-reply@usps.com)" - } - }, - "options_3": { - "title": "Correio e Pacotes (Etapa 3 de 3)", - "description": "Insira o caminho e o nome do arquivo para a sua imagem personalizada de sem correio abaixo.\n\nExemplo: images/custom_nomail.gif", - "data": { - "custom_img_file": "Caminho para imagem personalizada: (ex.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Configurações da Amazon", - "description": "Por favor, insira o domínio de onde a Amazon envia e-mails (por exemplo: amazon.com ou amazon.de)\n\nSe estiver usando e-mails encaminhados pela Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.\n\nNota: se configurou um cabeçalho de reencaminhamento no passo anterior, o campo de endereço de reencaminhamento não aparecerá — o cabeçalho trata automaticamente da correspondência de e-mails da Amazon.", - "data": { - "amazon_domain": "Domínio da Amazon", - "amazon_fwds": "A Amazon encaminhou endereços de email", - "amazon_days": "Dias atrás para verificar emails da Amazon" - } - }, - "options_storage": { - "title": "Local de armazenamento de imagens", - "description": "Por favor, insira o diretório em que você gostaria que suas imagens fossem armazenadas.\nO padrão é preenchido automaticamente.", - "data": { - "storage": "Diretório para armazenar imagens" - } - }, - "options_forwarded_emails": { - "title": "Definições de e-mails reencaminhados", - "description": "Escolha como fazer a correspondência dos e-mails reencaminhados.\n\n**Opção 1 — Cabeçalho de reencaminhamento**: Se o seu serviço (ex. SimpleLogin) inclui o remetente original num cabeçalho personalizado, introduza esse nome de cabeçalho. O Mail and Packages irá utilizá-lo para fazer a correspondência automática dos endereços das transportadoras.\n\n**Opção 2 — Lista de endereços**: Introduza os endereços de reencaminhamento que o seu serviço atribuiu a cada transportadora, separados por vírgulas.", - "data": { - "forwarding_header": "Cabeçalho de reencaminhamento (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Endereços de email encaminhados" - }, - "data_description": { - "forwarding_header": "Se o seu serviço de reencaminhamento inclui o remetente original num cabeçalho, introduza aqui o nome do cabeçalho. Isto tem prioridade sobre a lista de endereços abaixo. Introduza '(none)' ou deixe em branco para usar a lista de endereços.", - "forwarded_emails": "Lista separada por vírgulas dos endereços de reencaminhamento de onde chegarão as mensagens. Deixe '(none)' se estiver a usar o cabeçalho de reencaminhamento acima." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/pt_BR.json b/custom_components/mail_and_packages/translations/pt_BR.json deleted file mode 100644 index bb8c8fa..0000000 --- a/custom_components/mail_and_packages/translations/pt_BR.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Somente uma única configuração de correio e pacotes é permitida.", - "reconfigure_successful": "Reconfiguração bem-sucedida", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Não foi possível conectar ou fazer login no servidor de email. Por favor, verifique o log para obter detalhes.", - "invalid_path": "Por favor, guarde as imagens em outro diretório.", - "ffmpeg_not_found": "Gerar MP4 requer ffmpeg", - "amazon_domain": "Endereço de encaminhamento de email inválido.", - "file_not_found": "Arquivo de imagem não encontrado", - "invalid_email_format": "Formato de endereço de email inválido.", - "missing_forwarded_emails": "Endereços de email encaminhados em falta. Você pode inserir '(none)' para limpar esta configuração.", - "path_not_found": "Diretório não encontrado", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Pasta de Correio", - "resources": "Lista de Sensores", - "scan_interval": "Intervalo de digitalização (minutos)", - "image_path": "Caminho da imagem", - "gif_duration": "Duração da imagem (segundos)", - "image_security": "Nome da imagem aleatória", - "imap_timeout": "Tempo em segundos antes do tempo limite de conexão (segundos, mínimo 10)", - "generate_mp4": "Crie mp4 a partir de imagens", - "allow_external": "Crie imagem para aplicativos de notificação", - "custom_img": "Usar imagem personalizada USPS 'sem correio'?", - "amazon_custom_img": "Usar imagem personalizada Amazon 'sem entrega'?", - "ups_custom_img": "Usar imagem personalizada UPS 'sem entrega'?", - "walmart_custom_img": "Usar imagem personalizada Walmart 'sem entrega'?", - "fedex_custom_img": "Usar imagem personalizada FedEx 'sem entrega'?", - "generic_custom_img": "Usar imagem personalizada genérica 'sem entrega'?", - "generate_grid": "Criar grade de imagens para modelos de visão LLM", - "allow_forwarded_emails": "Permitir e-mails encaminhados além do padrão de um serviço (ex: no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Incluir marcador de posição sem imagem USPS no GIF?" - }, - "description": "Conclua a configuração, personalizando o seguinte com base na sua estrutura de email e instalação do Home Assistant. \n\n Para obter detalhes sobre as opções [integração de Mail e Pacotes] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), revise as opções de [configuração, modelos e seção de automações] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) no GitHub.", - "title": "Correio e pacotes (Etapa 2 de 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Caminho para imagem personalizada: (ex: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Insira o caminho e o nome do arquivo para a sua imagem personalizada de sem correio abaixo.\n\nExemplo: images/custom_nomail.gif", - "title": "Correio e Pacotes (Etapa 3 de 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Domínio da Amazon", - "amazon_fwds": "A Amazon encaminhou endereços de email", - "amazon_days": "Dias atrás para verificar os emails da Amazon" - }, - "description": "Por favor, insira o domínio do qual a Amazon envia e-mails (por exemplo: amazon.com ou amazon.de)\n\nSe estiver usando e-mails encaminhados pela Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.\n\nNota: se você configurou um cabeçalho de encaminhamento na etapa anterior, o campo de endereço de encaminhamento não aparecerá — o cabeçalho trata automaticamente da correspondência de e-mails da Amazon.", - "title": "Configurações da Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Pasta de Correio", - "scan_interval": "Intervalo de digitalização (minutos)", - "image_path": "Caminho da imagem", - "gif_duration": "Duração da imagem (segundos)", - "image_security": "Nome da imagem aleatória", - "generate_mp4": "Crie mp4 a partir de imagens", - "resources": "Lista de Sensores", - "imap_timeout": "Tempo em segundos antes do tempo limite de conexão (segundos, mínimo 10)", - "allow_external": "Crie imagem para aplicativos de notificação", - "custom_img": "Usar imagem personalizada USPS 'sem correio'?", - "amazon_custom_img": "Usar imagem personalizada Amazon 'sem entrega'?", - "ups_custom_img": "Usar imagem personalizada UPS 'sem entrega'?", - "walmart_custom_img": "Usar imagem personalizada Walmart 'sem entrega'?", - "fedex_custom_img": "Usar imagem personalizada FedEx 'sem entrega'?", - "generic_custom_img": "Usar imagem personalizada genérica 'sem entrega'?", - "generate_grid": "Criar grade de imagens para modelos de visão LLM", - "allow_forwarded_emails": "Permitir e-mails encaminhados além do padrão de um serviço (ex: no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Incluir marcador de posição sem imagem USPS no GIF?" - }, - "description": "Conclua a configuração personalizando o seguinte com base na estrutura do seu email e na instalação do Home Assistant.\n\nPara detalhes sobre as opções de [integração de Mail e Pacotes](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), consulte a [seção de configuração, modelos e automações](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) no GitHub.\n\nSe estiver usando emails encaminhados da Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.", - "title": "Correio e pacotes (Etapa 2 de 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Caminho para imagem personalizada: (ex.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Insira o caminho e o nome do arquivo para a sua imagem personalizada de sem correio abaixo.\n\nExemplo: images/custom_nomail.gif", - "title": "Correio e Pacotes (Etapa 3 de 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Domínio da Amazon", - "amazon_fwds": "A Amazon encaminhou endereços de email", - "amazon_days": "Dias atrás para verificar os emails da Amazon" - }, - "description": "Por favor, insira o domínio do qual a Amazon envia e-mails (por exemplo: amazon.com ou amazon.de)\n\nSe estiver usando e-mails encaminhados pela Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.\n\nNota: se você configurou um cabeçalho de encaminhamento na etapa anterior, o campo de endereço de encaminhamento não aparecerá — o cabeçalho trata automaticamente da correspondência de e-mails da Amazon.", - "title": "Configurações da Amazon" - }, - "config_storage": { - "data": { - "storage": "Diretório para armazenar imagens" - }, - "description": "Por favor, insira o diretório em que você gostaria que suas imagens fossem armazenadas.\nO padrão é preenchido automaticamente.", - "title": "Local de armazenamento de imagens" - }, - "reconfig_storage": { - "data": { - "storage": "Diretório para armazenar imagens" - }, - "description": "Por favor, insira o diretório em que você gostaria que suas imagens fossem armazenadas.\nO padrão é preenchido automaticamente.", - "title": "Local de armazenamento de imagens" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Cabeçalho de encaminhamento (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Endereços de e-mail encaminhados" - }, - "data_description": { - "forwarding_header": "Se o seu serviço de encaminhamento inclui o remetente original em um cabeçalho, insira o nome do cabeçalho aqui. Isso tem precedência sobre a lista de endereços abaixo. Insira '(none)' ou deixe em branco para usar a lista de endereços.", - "forwarded_emails": "Lista separada por vírgulas dos endereços de encaminhamento dos quais as mensagens chegarão. Deixe '(none)' se estiver usando o cabeçalho de encaminhamento acima." - }, - "description": "Escolha como corresponder os e-mails encaminhados.\n\n**Opção 1 — Cabeçalho de encaminhamento**: Se o seu serviço (ex. SimpleLogin) inclui o remetente original em um cabeçalho personalizado, insira esse nome de cabeçalho. O Mail and Packages o usará para corresponder os endereços das transportadoras automaticamente.\n\n**Opção 2 — Lista de endereços**: Insira os endereços de encaminhamento que seu serviço atribuiu a cada transportadora, separados por vírgulas.", - "title": "Configurações de e-mails encaminhados" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Cabeçalho de encaminhamento (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Endereços de e-mail encaminhados" - }, - "data_description": { - "forwarding_header": "Se o seu serviço de encaminhamento inclui o remetente original em um cabeçalho, insira o nome do cabeçalho aqui. Isso tem precedência sobre a lista de endereços abaixo. Insira '(none)' ou deixe em branco para usar a lista de endereços.", - "forwarded_emails": "Lista separada por vírgulas dos endereços de encaminhamento dos quais as mensagens chegarão. Deixe '(none)' se estiver usando o cabeçalho de encaminhamento acima." - }, - "description": "Escolha como corresponder os e-mails encaminhados.\n\n**Opção 1 — Cabeçalho de encaminhamento**: Se o seu serviço (ex. SimpleLogin) inclui o remetente original em um cabeçalho personalizado, insira esse nome de cabeçalho. O Mail and Packages o usará para corresponder os endereços das transportadoras automaticamente.\n\n**Opção 2 — Lista de endereços**: Insira os endereços de encaminhamento que seu serviço atribuiu a cada transportadora, separados por vírgulas.", - "title": "Configurações de e-mails encaminhados" - }, - "imap_config": { - "description": "Por favor, insira as informações de conexão do seu servidor de e-mail.", - "data": { - "host": "Hospedeiro", - "password": "Senha", - "port": "Porta", - "username": "Nome do usuário", - "verify_ssl": "Verificar Certificado SSL", - "imap_security": "Segurança IMAP" - }, - "title": "Correio e pacotes (Etapa 1 de 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Hospedeiro", - "password": "Senha", - "port": "Porta", - "username": "Nome do usuário", - "imap_security": "Segurança IMAP", - "verify_ssl": "Verificar Certificado SSL" - }, - "description": "Por favor, insira as informações de conexão do seu servidor de e-mail.", - "title": "Correio e pacotes (Etapa 1 de 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Falha na autenticação de Mail and Packages", - "fix_flow": { - "step": { - "confirm": { - "title": "Reautenticar o Servidor de E-mail", - "description": "A autenticação no seu servidor de e-mail IMAP falhou. Clique em enviar para iniciar o fluxo de reautenticação." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Correio e pacotes (Etapa 2 de 2)", - "description": "Conclua a configuração personalizando o seguinte com base na estrutura do seu email e na instalação do Home Assistant.\n\nPara detalhes sobre as opções de [integração de Mail e Pacotes](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration), consulte a [seção de configuração, modelos e automações](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) no GitHub.\n\nSe estiver usando emails encaminhados da Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.", - "data": { - "folder": "Pasta de Correio", - "resources": "Lista de Sensores", - "scan_interval": "Intervalo de digitalização (minutos)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Caminho da imagem", - "gif_duration": "Duração da imagem (segundos)", - "image_security": "Nome da imagem aleatória", - "imap_timeout": "Tempo em segundos antes do tempo limite de conexão (segundos, mínimo 10)", - "generate_mp4": "Crie mp4 a partir de imagens", - "allow_external": "Crie imagem para aplicativos de notificação", - "usps_placeholder": "Incluir marcador de posição sem imagem USPS no GIF?", - "custom_img": "Usar imagem personalizada USPS 'sem correio'?", - "amazon_custom_img": "Usar imagem personalizada Amazon 'sem entrega'?", - "ups_custom_img": "Usar imagem personalizada UPS 'sem entrega'?", - "walmart_custom_img": "Usar imagem personalizada Walmart 'sem entrega'?", - "fedex_custom_img": "Usar imagem personalizada FedEx 'sem entrega'?", - "generic_custom_img": "Usar imagem personalizada genérica 'sem entrega'?", - "generate_grid": "Criar grade de imagens para modelos de visão LLM", - "allow_forwarded_emails": "Permitir e-mails encaminhados além do padrão de um serviço (ex: no-reply@usps.com)" - } - }, - "options_3": { - "title": "Correio e Pacotes (Etapa 3 de 3)", - "description": "Insira o caminho e o nome do arquivo para a sua imagem personalizada de sem correio abaixo.\n\nExemplo: images/custom_nomail.gif", - "data": { - "custom_img_file": "Caminho para imagem personalizada: (ex.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Configurações da Amazon", - "description": "Por favor, insira o domínio do qual a Amazon envia e-mails (por exemplo: amazon.com ou amazon.de)\n\nSe estiver usando e-mails encaminhados pela Amazon, separe cada endereço com uma vírgula ou insira (nenhum) para limpar essa configuração.\n\nNota: se você configurou um cabeçalho de encaminhamento na etapa anterior, o campo de endereço de encaminhamento não aparecerá — o cabeçalho trata automaticamente da correspondência de e-mails da Amazon.", - "data": { - "amazon_domain": "Domínio da Amazon", - "amazon_fwds": "A Amazon encaminhou endereços de email", - "amazon_days": "Dias atrás para verificar os emails da Amazon" - } - }, - "options_storage": { - "title": "Local de armazenamento de imagens", - "description": "Por favor, insira o diretório em que você gostaria que suas imagens fossem armazenadas.\nO padrão é preenchido automaticamente.", - "data": { - "storage": "Diretório para armazenar imagens" - } - }, - "options_forwarded_emails": { - "title": "Configurações de e-mails encaminhados", - "description": "Escolha como corresponder os e-mails encaminhados.\n\n**Opção 1 — Cabeçalho de encaminhamento**: Se o seu serviço (ex. SimpleLogin) inclui o remetente original em um cabeçalho personalizado, insira esse nome de cabeçalho. O Mail and Packages o usará para corresponder os endereços das transportadoras automaticamente.\n\n**Opção 2 — Lista de endereços**: Insira os endereços de encaminhamento que seu serviço atribuiu a cada transportadora, separados por vírgulas.", - "data": { - "forwarding_header": "Cabeçalho de encaminhamento (ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Endereços de e-mail encaminhados" - }, - "data_description": { - "forwarding_header": "Se o seu serviço de encaminhamento inclui o remetente original em um cabeçalho, insira o nome do cabeçalho aqui. Isso tem precedência sobre a lista de endereços abaixo. Insira '(none)' ou deixe em branco para usar a lista de endereços.", - "forwarded_emails": "Lista separada por vírgulas dos endereços de encaminhamento dos quais as mensagens chegarão. Deixe '(none)' se estiver usando o cabeçalho de encaminhamento acima." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/ru.json b/custom_components/mail_and_packages/translations/ru.json deleted file mode 100644 index edb121b..0000000 --- a/custom_components/mail_and_packages/translations/ru.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Разрешена только одна конфигурация Почты и Пакетов.", - "reconfigure_successful": "Перенастройка успешно завершена", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Невозможно подключиться или войти на почтовый сервер. Пожалуйста, проверьте журнал для деталей.", - "invalid_path": "Пожалуйста, храните изображения в другом каталоге.", - "ffmpeg_not_found": "Для создания MP4 требуется ffmpeg", - "amazon_domain": "Недействительный адрес электронной почты для переадресации.", - "file_not_found": "Файл изображения не найден", - "invalid_email_format": "Неверный формат адреса электронной почты.", - "missing_forwarded_emails": "Отсутствуют переадресованные адреса электронной почты. Вы можете ввести '(none)', чтобы очистить этот параметр.", - "path_not_found": "Директория не найдена", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Папка почты", - "resources": "Список датчиков", - "scan_interval": "Интервал сканирования (минуты)", - "image_path": "Путь к изображению", - "gif_duration": "Длительность изображения (секунды)", - "image_security": "Случайное изображение Имя файла", - "imap_timeout": "Время в секундах до тайм-аута соединения (секунды, минимум 10)", - "generate_mp4": "Создать mp4 из изображений", - "allow_external": "Создать изображение для приложений уведомлений", - "custom_img": "Использовать пользовательское изображение USPS 'нет почты'?", - "amazon_custom_img": "Использовать пользовательское изображение Amazon 'нет доставки'?", - "ups_custom_img": "Использовать пользовательское изображение UPS 'нет доставки'?", - "walmart_custom_img": "Использовать пользовательское изображение Walmart 'нет доставки'?", - "fedex_custom_img": "Использовать пользовательское изображение FedEx 'нет доставки'?", - "generic_custom_img": "Использовать пользовательское изображение общее 'нет доставки'?", - "generate_grid": "Создать сетку изображений для моделей зрения LLM", - "allow_forwarded_emails": "Разрешить переадресованные электронные письма в дополнение к значениям по умолчанию службы (например, no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Включить заполнитель без изображения USPS в GIF?" - }, - "description": "Завершите настройку, настроив следующие параметры в зависимости от структуры электронной почты и установки Home Assistant. \n\n Подробнее о параметрах [Интеграция с почтой и пакетами] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) см. В разделе [конфигурация, шаблоны и раздел автоматизации] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) на GitHub.", - "title": "Почта и пакеты (шаг 2 из 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Путь к пользовательскому изображению: (например: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Введите путь и имя файла к вашему пользовательскому изображению без почты ниже.\n\nПример: images/custom_nomail.gif", - "title": "Почта и посылки (Шаг 3 из 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Домен Amazon", - "amazon_fwds": "Amazon переслал адреса электронной почты", - "amazon_days": "Дни для проверки электронных писем от Amazon" - }, - "description": "Пожалуйста, введите домен, с которого Amazon отправляет электронные письма (например: amazon.com или amazon.de)\n\nЕсли вы используете переадресованные электронные письма от Amazon, разделите каждый адрес запятой или введите (none), чтобы очистить эту настройку.\n\nПримечание: если вы настроили заголовок переадресации на предыдущем шаге, поле адреса переадресации не появится — заголовок автоматически обрабатывает сопоставление писем Amazon.", - "title": "Настройки Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Папка почты", - "scan_interval": "Интервал сканирования (минуты)", - "image_path": "Путь к изображению", - "gif_duration": "Длительность изображения (секунды)", - "image_security": "Случайное изображение Имя файла", - "generate_mp4": "Создать mp4 из изображений", - "resources": "Список датчиков", - "imap_timeout": "Время в секундах до тайм-аута соединения (секунды, минимум 10)", - "allow_external": "Создать изображение для приложений уведомлений", - "custom_img": "Использовать пользовательское изображение USPS 'нет почты'?", - "amazon_custom_img": "Использовать пользовательское изображение Amazon 'нет доставки'?", - "ups_custom_img": "Использовать пользовательское изображение UPS 'нет доставки'?", - "walmart_custom_img": "Использовать пользовательское изображение Walmart 'нет доставки'?", - "fedex_custom_img": "Использовать пользовательское изображение FedEx 'нет доставки'?", - "generic_custom_img": "Использовать пользовательское изображение общее 'нет доставки'?", - "generate_grid": "Создать сетку изображений для моделей зрения LLM", - "allow_forwarded_emails": "Разрешить переадресованные электронные письма в дополнение к значениям по умолчанию службы (например, no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Включить заполнитель без изображения USPS в GIF?" - }, - "description": "Завершите настройку, настроив следующее в соответствии со структурой вашей электронной почты и установкой Home Assistant.\n\nДля получения подробной информации о вариантах [интеграции Почта и Пакеты](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) ознакомьтесь с [разделом конфигурации, шаблонов и автоматизации](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) на GitHub.\n\nЕсли вы используете переадресованные электронные письма Amazon, разделите каждый адрес запятой или введите (none), чтобы очистить эту настройку.", - "title": "Почта и пакеты (шаг 2 из 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Путь к пользовательскому изображению: (например: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Введите путь и имя файла к вашему пользовательскому изображению без почты ниже.\n\nПример: images/custom_nomail.gif", - "title": "Почта и посылки (Шаг 3 из 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Домен Amazon", - "amazon_fwds": "Amazon переслал адреса электронной почты", - "amazon_days": "Дни для проверки электронных писем от Amazon" - }, - "description": "Пожалуйста, введите домен, с которого Amazon отправляет электронные письма (например: amazon.com или amazon.de)\n\nЕсли вы используете переадресованные электронные письма от Amazon, разделите каждый адрес запятой или введите (none), чтобы очистить эту настройку.\n\nПримечание: если вы настроили заголовок переадресации на предыдущем шаге, поле адреса переадресации не появится — заголовок автоматически обрабатывает сопоставление писем Amazon.", - "title": "Настройки Amazon" - }, - "config_storage": { - "data": { - "storage": "Каталог для хранения изображений" - }, - "description": "Пожалуйста, введите директорию, в которой вы хотите хранить свои изображения. \nПо умолчанию она заполняется автоматически.", - "title": "Место хранения изображения" - }, - "reconfig_storage": { - "data": { - "storage": "Каталог для хранения изображений" - }, - "description": "Пожалуйста, введите директорию, в которой вы хотите хранить свои изображения. \nПо умолчанию она заполняется автоматически.", - "title": "Место хранения изображения" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Заголовок переадресации (например, X-SimpleLogin-Original-From)", - "forwarded_emails": "Переадресованные адреса электронной почты" - }, - "data_description": { - "forwarding_header": "Если ваш сервис переадресации включает оригинального отправителя в заголовок, введите здесь название заголовка. Это имеет приоритет над списком адресов ниже. Введите '(none)' или оставьте пустым, чтобы использовать список адресов.", - "forwarded_emails": "Список адресов переадресации, разделённых запятыми, с которых будут приходить сообщения. Оставьте '(none)', если используете заголовок переадресации выше." - }, - "description": "Выберите способ сопоставления переадресованных писем.\n\n**Вариант 1 — Заголовок переадресации**: Если ваш сервис (например, SimpleLogin) включает оригинального отправителя в пользовательский заголовок, введите это название заголовка. Mail and Packages будет использовать его для автоматического сопоставления адресов перевозчиков.\n\n**Вариант 2 — Список адресов**: Введите адреса переадресации, которые ваш сервис назначил каждому перевозчику, через запятую.", - "title": "Настройки переадресованных писем" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Заголовок переадресации (например, X-SimpleLogin-Original-From)", - "forwarded_emails": "Переадресованные адреса электронной почты" - }, - "data_description": { - "forwarding_header": "Если ваш сервис переадресации включает оригинального отправителя в заголовок, введите здесь название заголовка. Это имеет приоритет над списком адресов ниже. Введите '(none)' или оставьте пустым, чтобы использовать список адресов.", - "forwarded_emails": "Список адресов переадресации, разделённых запятыми, с которых будут приходить сообщения. Оставьте '(none)', если используете заголовок переадресации выше." - }, - "description": "Выберите способ сопоставления переадресованных писем.\n\n**Вариант 1 — Заголовок переадресации**: Если ваш сервис (например, SimpleLogin) включает оригинального отправителя в пользовательский заголовок, введите это название заголовка. Mail and Packages будет использовать его для автоматического сопоставления адресов перевозчиков.\n\n**Вариант 2 — Список адресов**: Введите адреса переадресации, которые ваш сервис назначил каждому перевозчику, через запятую.", - "title": "Настройки переадресованных писем" - }, - "imap_config": { - "description": "Пожалуйста, введите информацию о подключении к вашему почтовому серверу.", - "data": { - "host": "хозяин", - "password": "пароль", - "port": "порт", - "username": "имя пользователя", - "verify_ssl": "Проверить SSL-сертификат", - "imap_security": "Безопасность IMAP" - }, - "title": "Почта и Пакеты (Шаг 1 из 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "хозяин", - "password": "пароль", - "port": "порт", - "username": "имя пользователя", - "imap_security": "Безопасность IMAP", - "verify_ssl": "Проверить SSL-сертификат" - }, - "description": "Пожалуйста, введите информацию о подключении к вашему почтовому серверу.", - "title": "Почта и Пакеты (Шаг 1 из 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Ошибка авторизации Mail and Packages", - "fix_flow": { - "step": { - "confirm": { - "title": "Повторная авторизация почтового сервера", - "description": "Не удалось авторизоваться на вашем почтовом сервере IMAP. Нажмите «Отправить», чтобы начать процесс повторной авторизации." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Почта и пакеты (шаг 2 из 2)", - "description": "Завершите настройку, настроив следующее в соответствии со структурой вашей электронной почты и установкой Home Assistant.\n\nДля получения подробной информации о вариантах [интеграции Почта и Пакеты](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) ознакомьтесь с [разделом конфигурации, шаблонов и автоматизации](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) на GitHub.\n\nЕсли вы используете переадресованные электронные письма Amazon, разделите каждый адрес запятой или введите (none), чтобы очистить эту настройку.", - "data": { - "folder": "Папка почты", - "resources": "Список датчиков", - "scan_interval": "Интервал сканирования (минуты)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Путь к изображению", - "gif_duration": "Длительность изображения (секунды)", - "image_security": "Случайное изображение Имя файла", - "imap_timeout": "Время в секундах до тайм-аута соединения (секунды, минимум 10)", - "generate_mp4": "Создать mp4 из изображений", - "allow_external": "Создать изображение для приложений уведомлений", - "usps_placeholder": "Включить заполнитель без изображения USPS в GIF?", - "custom_img": "Использовать пользовательское изображение USPS 'нет почты'?", - "amazon_custom_img": "Использовать пользовательское изображение Amazon 'нет доставки'?", - "ups_custom_img": "Использовать пользовательское изображение UPS 'нет доставки'?", - "walmart_custom_img": "Использовать пользовательское изображение Walmart 'нет доставки'?", - "fedex_custom_img": "Использовать пользовательское изображение FedEx 'нет доставки'?", - "generic_custom_img": "Использовать пользовательское изображение общее 'нет доставки'?", - "generate_grid": "Создать сетку изображений для моделей зрения LLM", - "allow_forwarded_emails": "Разрешить переадресованные электронные письма в дополнение к значениям по умолчанию службы (например, no-reply@usps.com)" - } - }, - "options_3": { - "title": "Почта и посылки (Шаг 3 из 3)", - "description": "Введите путь и имя файла к вашему пользовательскому изображению без почты ниже.\n\nПример: images/custom_nomail.gif", - "data": { - "custom_img_file": "Путь к пользовательскому изображению: (например: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Настройки Amazon", - "description": "Пожалуйста, введите домен, с которого Amazon отправляет электронные письма (например: amazon.com или amazon.de)\n\nЕсли вы используете переадресованные электронные письма от Amazon, разделите каждый адрес запятой или введите (none), чтобы очистить эту настройку.\n\nПримечание: если вы настроили заголовок переадресации на предыдущем шаге, поле адреса переадресации не появится — заголовок автоматически обрабатывает сопоставление писем Amazon.", - "data": { - "amazon_domain": "Домен Amazon", - "amazon_fwds": "Amazon переслал адреса электронной почты", - "amazon_days": "Дни для проверки электронных писем от Amazon" - } - }, - "options_storage": { - "title": "Место хранения изображения", - "description": "Пожалуйста, введите директорию, в которой вы хотите хранить свои изображения. \nПо умолчанию она заполняется автоматически.", - "data": { - "storage": "Каталог для хранения изображений" - } - }, - "options_forwarded_emails": { - "title": "Настройки переадресованных писем", - "description": "Выберите способ сопоставления переадресованных писем.\n\n**Вариант 1 — Заголовок переадресации**: Если ваш сервис (например, SimpleLogin) включает оригинального отправителя в пользовательский заголовок, введите это название заголовка. Mail and Packages будет использовать его для автоматического сопоставления адресов перевозчиков.\n\n**Вариант 2 — Список адресов**: Введите адреса переадресации, которые ваш сервис назначил каждому перевозчику, через запятую.", - "data": { - "forwarding_header": "Заголовок переадресации (например, X-SimpleLogin-Original-From)", - "forwarded_emails": "Переадресованные адреса электронной почты" - }, - "data_description": { - "forwarding_header": "Если ваш сервис переадресации включает оригинального отправителя в заголовок, введите здесь название заголовка. Это имеет приоритет над списком адресов ниже. Введите '(none)' или оставьте пустым, чтобы использовать список адресов.", - "forwarded_emails": "Список адресов переадресации, разделённых запятыми, с которых будут приходить сообщения. Оставьте '(none)', если используете заголовок переадресации выше." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/sk.json b/custom_components/mail_and_packages/translations/sk.json deleted file mode 100644 index f400880..0000000 --- a/custom_components/mail_and_packages/translations/sk.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Je povolená iba jedna konfigurácia pošty a balíkov.", - "reconfigure_successful": "Rekonfigurácia úspešná", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Nedá sa pripojiť alebo prihlásiť na poštový server. Podrobnosti nájdete v protokole.", - "invalid_path": "Uložte obrázky do iného adresára.", - "ffmpeg_not_found": "Vytvorenie MP4 vyžaduje ffmpeg", - "amazon_domain": "Neplatná adresa pre presmerovanie e-mailov.", - "file_not_found": "Obrázokový súbor nebol nájdený", - "invalid_email_format": "Neplatný formát e-mailovej adresy.", - "missing_forwarded_emails": "Chýbajú preposielané e-mailové adresy. Môžete zadať '(none)' na vymazanie tohto nastavenia.", - "path_not_found": "Adresár nebol nájdený", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Priečinok pošty", - "resources": "Zoznam senzorov", - "scan_interval": "Interval skenovania (minúty)", - "image_path": "Cesta obrázka", - "gif_duration": "Trvanie obrázka (sekundy)", - "image_security": "Náhodný názov súboru obrázka", - "imap_timeout": "Čas v sekundách pred časovým limitom pripojenia (sekundy, minimálne 10)", - "generate_mp4": "Vytvorte mp4 z obrázkov", - "allow_external": "Vytvorte obrázok pre aplikácie upozornení", - "custom_img": "Použiť vlastný obrázok USPS 'bez pošty'?", - "amazon_custom_img": "Použiť vlastný obrázok Amazon 'bez dodávky'?", - "ups_custom_img": "Použiť vlastný obrázok UPS 'bez dodávky'?", - "walmart_custom_img": "Použiť vlastný obrázok Walmart 'bez dodávky'?", - "fedex_custom_img": "Použiť vlastný obrázok FedEx 'bez dodávky'?", - "generic_custom_img": "Použiť vlastný obrázok všeobecný 'bez dodávky'?", - "generate_grid": "Vytvoriť mriežku obrázkov pre modely videnia LLM", - "allow_forwarded_emails": "Povoliť preposielané e-maily okrem predvolenej hodnoty služby (napr. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Zahrnúť zástupný symbol USPS bez obrázka do GIF?" - }, - "description": "Dokončite konfiguráciu prispôsobením nasledujúcich položiek na základe štruktúry e-mailu a inštalácie Home Assistant.\n\nPodrobnosti nájdete na [Pošta a balíky integrácia](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) možnosti si pozrite v časti [konfigurácia, šablóny a automatizácie](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHube.", - "title": "Pošta a balíky (krok 2 z 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Cesta k vlastnému obrázku: (napr.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Zadajte cestu a názov súboru k vášmu vlastnému obrázku bez pošty nižšie.\n\nPríklad: images/custom_nomail.gif", - "title": "Pošta a balíky (Krok 3 z 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Doména Amazon", - "amazon_fwds": "Amazon preposlal emailové adresy", - "amazon_days": "Dni späť na kontrolu e-mailov od Amazonu" - }, - "description": "Prosím, zadajte doménu, z ktorej Amazon posiela e-maily (napríklad: amazon.com alebo amazon.de)\n\nAk používate preposlané e-maily od Amazonu, oddelte každú adresu čiarkou alebo zadajte (žiadne) na vymazanie tohto nastavenia.\n\nPoznámka: ak ste v predchádzajúcom kroku nakonfigurovali hlavičku presmerovania, pole s adresou presmerovania sa nezobrazí — hlavička automaticky spracúva zhodu e-mailov Amazon.", - "title": "Nastavenia Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Priečinok pošty", - "scan_interval": "Interval skenovania (minúty)", - "image_path": "Cesta obrázka", - "gif_duration": "Trvanie obrázka (sekundy)", - "image_security": "Náhodný názov súboru obrázka", - "generate_mp4": "Vytvorte mp4 z obrázkov", - "resources": "Zoznam senzorov", - "imap_timeout": "Čas v sekundách pred časovým limitom pripojenia (sekundy, minimálne 10)", - "allow_external": "Vytvorte obrázok pre aplikácie upozornení", - "custom_img": "Použiť vlastný obrázok USPS 'bez pošty'?", - "amazon_custom_img": "Použiť vlastný obrázok Amazon 'bez dodávky'?", - "ups_custom_img": "Použiť vlastný obrázok UPS 'bez dodávky'?", - "walmart_custom_img": "Použiť vlastný obrázok Walmart 'bez dodávky'?", - "fedex_custom_img": "Použiť vlastný obrázok FedEx 'bez dodávky'?", - "generic_custom_img": "Použiť vlastný obrázok všeobecný 'bez dodávky'?", - "generate_grid": "Vytvoriť mriežku obrázkov pre modely videnia LLM", - "allow_forwarded_emails": "Povoliť preposielané e-maily okrem predvolenej hodnoty služby (napr. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Zahrnúť zástupný symbol USPS bez obrázka do GIF?" - }, - "description": "Dokončite konfiguráciu prispôsobením nasledujúceho na základe štruktúry vášho e-mailu a inštalácie Home Assistant.\n\nPre podrobnosti o možnostiach [integrácie Mail a Packages](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) si prečítajte [sekciu o konfigurácii, šablónach a automatizáciách](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHub.\n\nAk používate preposlané e-maily od Amazonu, oddelte každú adresu čiarkou alebo zadajte (none) na vymazanie tohto nastavenia.", - "title": "Pošta a balíky (krok 2 z 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Cesta k vlastnému obrázku: (napr.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Zadajte cestu a názov súboru k vášmu vlastnému obrázku bez pošty nižšie.\n\nPríklad: images/custom_nomail.gif", - "title": "Pošta a balíky (Krok 3 z 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Doména Amazon", - "amazon_fwds": "Amazon preposlal emailové adresy", - "amazon_days": "Dni späť na kontrolu e-mailov od Amazonu" - }, - "description": "Prosím, zadajte doménu, z ktorej Amazon posiela e-maily (napríklad: amazon.com alebo amazon.de)\n\nAk používate preposlané e-maily od Amazonu, oddelte každú adresu čiarkou alebo zadajte (žiadne) na vymazanie tohto nastavenia.\n\nPoznámka: ak ste v predchádzajúcom kroku nakonfigurovali hlavičku presmerovania, pole s adresou presmerovania sa nezobrazí — hlavička automaticky spracúva zhodu e-mailov Amazon.", - "title": "Nastavenia Amazon" - }, - "config_storage": { - "data": { - "storage": "Adresár na ukladanie obrázkov" - }, - "description": "Prosím, zadajte adresár, v ktorom chcete ukladať svoje obrázky.\nPredvolená hodnota je automaticky vyplnená.", - "title": "Miesto ukladania obrázkov" - }, - "reconfig_storage": { - "data": { - "storage": "Adresár na ukladanie obrázkov" - }, - "description": "Prosím, zadajte adresár, v ktorom chcete ukladať svoje obrázky.\nPredvolená hodnota je automaticky vyplnená.", - "title": "Miesto ukladania obrázkov" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Hlavička presmerovania (napr. X-SimpleLogin-Original-From)", - "forwarded_emails": "Preposielané e-mailové adresy" - }, - "data_description": { - "forwarding_header": "Ak vaša služba presmerovania zahŕňa pôvodného odosielateľa v hlavičke, zadajte tu názov hlavičky. Toto má prednosť pred zoznamom adries nižšie. Zadajte '(none)' alebo nechajte prázdne, aby ste namiesto toho použili zoznam adries.", - "forwarded_emails": "Zoznam presmerovacích adries oddelených čiarkami, z ktorých budú prichádzať správy. Ponechajte '(none)', ak používate vyššie uvedenú hlavičku presmerovania." - }, - "description": "Vyberte spôsob porovnávania presmerovaných e-mailov.\n\n**Možnosť 1 — Hlavička presmerovania**: Ak vaša služba (napr. SimpleLogin) zahŕňa pôvodného odosielateľa do vlastnej hlavičky, zadajte názov tej hlavičky. Mail and Packages ho použije na automatické priradenie adries dopravcov.\n\n**Možnosť 2 — Zoznam adries**: Zadajte presmerovavacie adresy, ktoré vaša služba priradila každému dopravcovi, oddelené čiarkami.", - "title": "Nastavenia presmerovaných e-mailov" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Hlavička presmerovania (napr. X-SimpleLogin-Original-From)", - "forwarded_emails": "Preposielané e-mailové adresy" - }, - "data_description": { - "forwarding_header": "Ak vaša služba presmerovania zahŕňa pôvodného odosielateľa v hlavičke, zadajte tu názov hlavičky. Toto má prednosť pred zoznamom adries nižšie. Zadajte '(none)' alebo nechajte prázdne, aby ste namiesto toho použili zoznam adries.", - "forwarded_emails": "Zoznam presmerovacích adries oddelených čiarkami, z ktorých budú prichádzať správy. Ponechajte '(none)', ak používate vyššie uvedenú hlavičku presmerovania." - }, - "description": "Vyberte spôsob porovnávania presmerovaných e-mailov.\n\n**Možnosť 1 — Hlavička presmerovania**: Ak vaša služba (napr. SimpleLogin) zahŕňa pôvodného odosielateľa do vlastnej hlavičky, zadajte názov tej hlavičky. Mail and Packages ho použije na automatické priradenie adries dopravcov.\n\n**Možnosť 2 — Zoznam adries**: Zadajte presmerovavacie adresy, ktoré vaša služba priradila každému dopravcovi, oddelené čiarkami.", - "title": "Nastavenia presmerovaných e-mailov" - }, - "imap_config": { - "description": "Prosím, zadajte informácie o pripojení k vášmu mailovému serveru.", - "data": { - "host": "Host", - "password": "Heslo", - "port": "Port", - "username": "Užívateľské meno", - "verify_ssl": "Overiť SSL certifikát", - "imap_security": "Bezpečnosť IMAP" - }, - "title": "Pošta a balíky (1. krok z 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Host", - "password": "Heslo", - "port": "Port", - "username": "Užívateľské meno", - "imap_security": "Bezpečnosť IMAP", - "verify_ssl": "Overiť SSL certifikát" - }, - "description": "Prosím, zadajte informácie o pripojení k vášmu mailovému serveru.", - "title": "Pošta a balíky (1. krok z 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Overenie Mail and Packages zlyhalo", - "fix_flow": { - "step": { - "confirm": { - "title": "Znovu overiť poštový server", - "description": "Overenie k vášmu poštovému serveru IMAP zlyhalo. Kliknutím na odoslať spustíte proces opätovného overenia." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Pošta a balíky (krok 2 z 2)", - "description": "Dokončite konfiguráciu prispôsobením nasledujúceho na základe štruktúry vášho e-mailu a inštalácie Home Assistant.\n\nPre podrobnosti o možnostiach [integrácie Mail a Packages](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) si prečítajte [sekciu o konfigurácii, šablónach a automatizáciách](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHub.\n\nAk používate preposlané e-maily od Amazonu, oddelte každú adresu čiarkou alebo zadajte (none) na vymazanie tohto nastavenia.", - "data": { - "folder": "Priečinok pošty", - "resources": "Zoznam senzorov", - "scan_interval": "Interval skenovania (minúty)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Cesta obrázka", - "gif_duration": "Trvanie obrázka (sekundy)", - "image_security": "Náhodný názov súboru obrázka", - "imap_timeout": "Čas v sekundách pred časovým limitom pripojenia (sekundy, minimálne 10)", - "generate_mp4": "Vytvorte mp4 z obrázkov", - "allow_external": "Vytvorte obrázok pre aplikácie upozornení", - "usps_placeholder": "Zahrnúť zástupný symbol USPS bez obrázka do GIF?", - "custom_img": "Použiť vlastný obrázok USPS 'bez pošty'?", - "amazon_custom_img": "Použiť vlastný obrázok Amazon 'bez dodávky'?", - "ups_custom_img": "Použiť vlastný obrázok UPS 'bez dodávky'?", - "walmart_custom_img": "Použiť vlastný obrázok Walmart 'bez dodávky'?", - "fedex_custom_img": "Použiť vlastný obrázok FedEx 'bez dodávky'?", - "generic_custom_img": "Použiť vlastný obrázok všeobecný 'bez dodávky'?", - "generate_grid": "Vytvoriť mriežku obrázkov pre modely videnia LLM", - "allow_forwarded_emails": "Povoliť preposielané e-maily okrem predvolenej hodnoty služby (napr. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Pošta a balíky (Krok 3 z 3)", - "description": "Zadajte cestu a názov súboru k vášmu vlastnému obrázku bez pošty nižšie.\n\nPríklad: images/custom_nomail.gif", - "data": { - "custom_img_file": "Cesta k vlastnému obrázku: (napr.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Nastavenia Amazon", - "description": "Prosím, zadajte doménu, z ktorej Amazon posiela e-maily (napríklad: amazon.com alebo amazon.de)\n\nAk používate preposlané e-maily od Amazonu, oddelte každú adresu čiarkou alebo zadajte (žiadne) na vymazanie tohto nastavenia.\n\nPoznámka: ak ste v predchádzajúcom kroku nakonfigurovali hlavičku presmerovania, pole s adresou presmerovania sa nezobrazí — hlavička automaticky spracúva zhodu e-mailov Amazon.", - "data": { - "amazon_domain": "Doména Amazon", - "amazon_fwds": "Amazon preposlal emailové adresy", - "amazon_days": "Dni späť na kontrolu e-mailov od Amazonu" - } - }, - "options_storage": { - "title": "Miesto ukladania obrázkov", - "description": "Prosím, zadajte adresár, v ktorom chcete ukladať svoje obrázky.\nPredvolená hodnota je automaticky vyplnená.", - "data": { - "storage": "Adresár na ukladanie obrázkov" - } - }, - "options_forwarded_emails": { - "title": "Nastavenia presmerovaných e-mailov", - "description": "Vyberte spôsob porovnávania presmerovaných e-mailov.\n\n**Možnosť 1 — Hlavička presmerovania**: Ak vaša služba (napr. SimpleLogin) zahŕňa pôvodného odosielateľa do vlastnej hlavičky, zadajte názov tej hlavičky. Mail and Packages ho použije na automatické priradenie adries dopravcov.\n\n**Možnosť 2 — Zoznam adries**: Zadajte presmerovavacie adresy, ktoré vaša služba priradila každému dopravcovi, oddelené čiarkami.", - "data": { - "forwarding_header": "Hlavička presmerovania (napr. X-SimpleLogin-Original-From)", - "forwarded_emails": "Preposielané e-mailové adresy" - }, - "data_description": { - "forwarding_header": "Ak vaša služba presmerovania zahŕňa pôvodného odosielateľa v hlavičke, zadajte tu názov hlavičky. Toto má prednosť pred zoznamom adries nižšie. Zadajte '(none)' alebo nechajte prázdne, aby ste namiesto toho použili zoznam adries.", - "forwarded_emails": "Zoznam presmerovacích adries oddelených čiarkami, z ktorých budú prichádzať správy. Ponechajte '(none)', ak používate vyššie uvedenú hlavičku presmerovania." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/sl.json b/custom_components/mail_and_packages/translations/sl.json deleted file mode 100644 index 3ef802d..0000000 --- a/custom_components/mail_and_packages/translations/sl.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Dovoljena je samo ena konfiguracija pošte in paketov.", - "reconfigure_successful": "Prekonfiguracija uspešna", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Poštnega strežnika ni mogoče povezati ali prijaviti. Za podrobnosti preverite dnevnik.", - "invalid_path": "Shranite slike v drugem imeniku.", - "ffmpeg_not_found": "Ustvarjanje MP4 zahteva ffmpeg", - "amazon_domain": "Neveljaven naslov za preusmerjanje e-pošte.", - "file_not_found": "Slikovna datoteka ni najdena", - "invalid_email_format": "Neveljaven format e-poštnega naslova.", - "missing_forwarded_emails": "Manjkajo posredovani e-poštni naslovi. Vnesete lahko '(none)' za izbris te nastavitve.", - "path_not_found": "Mapa ni najdena", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "Mapa mape", - "resources": "Seznam senzorjev", - "scan_interval": "Interval optičnega branja (minute)", - "image_path": "Pot slike", - "gif_duration": "Trajanje slike (sekunde)", - "image_security": "Naključno ime datoteke", - "imap_timeout": "Čas v sekundah pred prekinitvijo povezave (sekunde, najmanj 10)", - "generate_mp4": "Ustvari mp4 iz slik", - "allow_external": "Ustvari sliko za aplikacije za obvestila", - "custom_img": "Uporabite prilagojeno sliko USPS 'brez pošte'?", - "amazon_custom_img": "Uporabite prilagojeno sliko Amazon 'brez dostave'?", - "ups_custom_img": "Uporabite prilagojeno sliko UPS 'brez dostave'?", - "walmart_custom_img": "Uporabite prilagojeno sliko Walmart 'brez dostave'?", - "fedex_custom_img": "Uporabite prilagojeno sliko FedEx 'brez dostave'?", - "generic_custom_img": "Uporabite prilagojeno splošno sliko 'brez dostave'?", - "generate_grid": "Ustvari mrežo slik za vizualne modele LLM", - "allow_forwarded_emails": "Dovoli posredovane e-poštne sporočila poleg privzete vrednosti storitve (npr. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Ali naj se v GIF vključi nadomestni znak USPS brez slike?" - }, - "description": "Končajte konfiguracijo s prilagoditvijo naslednjih na podlagi strukture e-pošte in namestitve Home Assistant. \n\n Za podrobnosti o možnostih [Integracija pošte in paketov] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) preglejte [konfiguracijo, predloge in oddelku za avtomatizacije] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHubu.", - "title": "Pošta in paketi (2. korak od 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Pot do prilagojene slike: (npr.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "V spodnje polje vnesite pot in ime datoteke do vaše prilagojene slike brez pošte.\n\nPrimer: images/custom_nomail.gif", - "title": "Pošta in paketi (Korak 3 od 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon domena", - "amazon_fwds": "Amazon je posredoval e-poštne naslove", - "amazon_days": "Dnevi nazaj za preverjanje Amazonovih e-poštnih sporočil" - }, - "description": "Prosimo, vnesite domeno, s katere Amazon pošilja e-pošto (npr.: amazon.com ali amazon.de)\n\nČe uporabljate preusmerjena e-poštna sporočila Amazon, ločite vsak naslov z vejico ali vnesite (noben) za izbris te nastavitve.\n\nOpomba: če ste v prejšnjem koraku konfigurirali glavo posredovanja, polje z naslovom posredovanja ne bo prikazano — glava samodejno obravnava ujemanje Amazonovih e-poštnih sporočil.", - "title": "Nastavitve Amazon" - }, - "reconfig_2": { - "data": { - "folder": "Mapa mape", - "scan_interval": "Interval optičnega branja (minute)", - "image_path": "Pot slike", - "gif_duration": "Trajanje slike (sekunde)", - "image_security": "Naključno ime datoteke", - "generate_mp4": "Ustvari mp4 iz slik", - "resources": "Seznam senzorjev", - "imap_timeout": "Čas v sekundah pred prekinitvijo povezave (sekunde, najmanj 10)", - "allow_external": "Ustvari sliko za aplikacije za obvestila", - "custom_img": "Uporabite prilagojeno sliko USPS 'brez pošte'?", - "amazon_custom_img": "Uporabite prilagojeno sliko Amazon 'brez dostave'?", - "ups_custom_img": "Uporabite prilagojeno sliko UPS 'brez dostave'?", - "walmart_custom_img": "Uporabite prilagojeno sliko Walmart 'brez dostave'?", - "fedex_custom_img": "Uporabite prilagojeno sliko FedEx 'brez dostave'?", - "generic_custom_img": "Uporabite prilagojeno splošno sliko 'brez dostave'?", - "generate_grid": "Ustvari mrežo slik za vizualne modele LLM", - "allow_forwarded_emails": "Dovoli posredovane e-poštne sporočila poleg privzete vrednosti storitve (npr. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Ali naj se v GIF vključi nadomestni znak USPS brez slike?" - }, - "description": "Konfiguracijo dokončajte z prilagajanjem naslednjega glede na strukturo vašega e-poštnega sporočila in namestitev Home Assistant.\n\nZa podrobnosti o možnostih [integracije Mail and Packages](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) si oglejte [razdelek o konfiguraciji, predlogah in avtomatizacijah](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHubu.\n\nČe uporabljate preusmerjena e-poštna sporočila Amazon, ločite vsak naslov z vejico ali vnesite (noben) za izbris te nastavitve.", - "title": "Pošta in paketi (2. korak od 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Pot do prilagojene slike: (npr.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "V spodnje polje vnesite pot in ime datoteke do vaše prilagojene slike brez pošte.\n\nPrimer: images/custom_nomail.gif", - "title": "Pošta in paketi (korak 3 od 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon domena", - "amazon_fwds": "Amazon je posredoval e-poštne naslove", - "amazon_days": "Dnevi nazaj za preverjanje Amazonovih e-poštnih sporočil" - }, - "description": "Prosimo, vnesite domeno, s katere Amazon pošilja e-pošto (npr.: amazon.com ali amazon.de)\n\nČe uporabljate preusmerjena e-poštna sporočila Amazon, ločite vsak naslov z vejico ali vnesite (noben) za izbris te nastavitve.\n\nOpomba: če ste v prejšnjem koraku konfigurirali glavo posredovanja, polje z naslovom posredovanja ne bo prikazano — glava samodejno obravnava ujemanje Amazonovih e-poštnih sporočil.", - "title": "Nastavitve Amazon" - }, - "config_storage": { - "data": { - "storage": "Mapa za shranjevanje slik" - }, - "description": "Prosimo, vnesite imenik, v katerem želite shraniti svoje slike.\nPrivzeto je samodejno izpolnjeno.", - "title": "Lokacija shranjevanja slik" - }, - "reconfig_storage": { - "data": { - "storage": "Mapa za shranjevanje slik" - }, - "description": "Prosimo, vnesite imenik, v katerem želite shraniti svoje slike.\nPrivzeto je samodejno izpolnjeno.", - "title": "Lokacija shranjevanja slik" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Glava za posredovanje (npr. X-SimpleLogin-Original-From)", - "forwarded_emails": "Posredovani e-poštni naslovi" - }, - "data_description": { - "forwarding_header": "Če vaša storitev posredovanja vključuje izvirnega pošiljatelja v glavi, vnesite tukaj ime glave. To ima prednost pred spodnjim seznamom naslovov. Vnesite '(none)' ali pustite prazno, da namesto tega uporabite seznam naslovov.", - "forwarded_emails": "Z vejicami ločen seznam naslovov za posredovanje, od katerih bodo prihajala sporočila. Pustite '(none)', če uporabljate zgornjo glavo za posredovanje." - }, - "description": "Izberite, kako ujemati posredovana e-poštna sporočila.\n\n**Možnost 1 — Glava za posredovanje**: Če vaša storitev (npr. SimpleLogin) vključuje izvirnega pošiljatelja v glavo po meri, vnesite ime te glave. Mail and Packages jo bo uporabil za samodejno ujemanje naslovov prevoznikov.\n\n**Možnost 2 — Seznam naslovov**: Vnesite naslove za posredovanje, ki jih je vaša storitev dodelila vsakemu prevozniku, ločene z vejicami.", - "title": "Nastavitve posredovane e-pošte" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Glava za posredovanje (npr. X-SimpleLogin-Original-From)", - "forwarded_emails": "Posredovani e-poštni naslovi" - }, - "data_description": { - "forwarding_header": "Če vaša storitev posredovanja vključuje izvirnega pošiljatelja v glavi, vnesite tukaj ime glave. To ima prednost pred spodnjim seznamom naslovov. Vnesite '(none)' ali pustite prazno, da namesto tega uporabite seznam naslovov.", - "forwarded_emails": "Z vejicami ločen seznam naslovov za posredovanje, od katerih bodo prihajala sporočila. Pustite '(none)', če uporabljate zgornjo glavo za posredovanje." - }, - "description": "Izberite, kako ujemati posredovana e-poštna sporočila.\n\n**Možnost 1 — Glava za posredovanje**: Če vaša storitev (npr. SimpleLogin) vključuje izvirnega pošiljatelja v glavo po meri, vnesite ime te glave. Mail and Packages jo bo uporabil za samodejno ujemanje naslovov prevoznikov.\n\n**Možnost 2 — Seznam naslovov**: Vnesite naslove za posredovanje, ki jih je vaša storitev dodelila vsakemu prevozniku, ločene z vejicami.", - "title": "Nastavitve posredovane e-pošte" - }, - "imap_config": { - "description": "Prosimo, vnesite podatke o povezavi vašega poštnega strežnika.", - "data": { - "host": "Gostitelj", - "password": "Geslo", - "port": "Pristanišče", - "username": "Uporabniško ime", - "verify_ssl": "Preveri SSL certifikat", - "imap_security": "Varnost IMAP" - }, - "title": "Pošta in paketi (1. korak od 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Gostitelj", - "password": "Geslo", - "port": "Pristanišče", - "username": "Uporabniško ime", - "imap_security": "Varnost IMAP", - "verify_ssl": "Preveri SSL certifikat" - }, - "description": "Prosimo, vnesite podatke o povezavi vašega poštnega strežnika.", - "title": "Pošta in paketi (1. korak od 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Preverjanje pristnosti za Mail and Packages ni uspelo", - "fix_flow": { - "step": { - "confirm": { - "title": "Ponovno preveri poštni strežnik", - "description": "Preverjanje pristnosti z vašim poštnim strežnikom IMAP ni uspelo. Kliknite Pošlji, če želite začeti potek ponovnega preverjanja pristnosti." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Pošta in paketi (2. korak od 2)", - "description": "Konfiguracijo dokončajte z prilagajanjem naslednjega glede na strukturo vašega e-poštnega sporočila in namestitev Home Assistant.\n\nZa podrobnosti o možnostih [integracije Mail and Packages](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) si oglejte [razdelek o konfiguraciji, predlogah in avtomatizacijah](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) na GitHubu.\n\nČe uporabljate preusmerjena e-poštna sporočila Amazon, ločite vsak naslov z vejico ali vnesite (noben) za izbris te nastavitve.", - "data": { - "folder": "Mapa mape", - "resources": "Seznam senzorjev", - "scan_interval": "Interval optičnega branja (minute)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Pot slike", - "gif_duration": "Trajanje slike (sekunde)", - "image_security": "Naključno ime datoteke", - "imap_timeout": "Čas v sekundah pred prekinitvijo povezave (sekunde, najmanj 10)", - "generate_mp4": "Ustvari mp4 iz slik", - "allow_external": "Ustvari sliko za aplikacije za obvestila", - "usps_placeholder": "Ali naj se v GIF vključi nadomestni znak USPS brez slike?", - "custom_img": "Uporabite prilagojeno sliko USPS 'brez pošte'?", - "amazon_custom_img": "Uporabite prilagojeno sliko Amazon 'brez dostave'?", - "ups_custom_img": "Uporabite prilagojeno sliko UPS 'brez dostave'?", - "walmart_custom_img": "Uporabite prilagojeno sliko Walmart 'brez dostave'?", - "fedex_custom_img": "Uporabite prilagojeno sliko FedEx 'brez dostave'?", - "generic_custom_img": "Uporabite prilagojeno splošno sliko 'brez dostave'?", - "generate_grid": "Ustvari mrežo slik za vizualne modele LLM", - "allow_forwarded_emails": "Dovoli posredovane e-poštne sporočila poleg privzete vrednosti storitve (npr. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Pošta in paketi (korak 3 od 3)", - "description": "V spodnje polje vnesite pot in ime datoteke do vaše prilagojene slike brez pošte.\n\nPrimer: images/custom_nomail.gif", - "data": { - "custom_img_file": "Pot do prilagojene slike: (npr.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Nastavitve Amazon", - "description": "Prosimo, vnesite domeno, s katere Amazon pošilja e-pošto (npr.: amazon.com ali amazon.de)\n\nČe uporabljate preusmerjena e-poštna sporočila Amazon, ločite vsak naslov z vejico ali vnesite (noben) za izbris te nastavitve.\n\nOpomba: če ste v prejšnjem koraku konfigurirali glavo posredovanja, polje z naslovom posredovanja ne bo prikazano — glava samodejno obravnava ujemanje Amazonovih e-poštnih sporočil.", - "data": { - "amazon_domain": "Amazon domena", - "amazon_fwds": "Amazon je posredoval e-poštne naslove", - "amazon_days": "Dnevi nazaj za preverjanje Amazonovih e-poštnih sporočil" - } - }, - "options_storage": { - "title": "Lokacija shranjevanja slik", - "description": "Prosimo, vnesite imenik, v katerem želite shraniti svoje slike.\nPrivzeto je samodejno izpolnjeno.", - "data": { - "storage": "Mapa za shranjevanje slik" - } - }, - "options_forwarded_emails": { - "title": "Nastavitve posredovane e-pošte", - "description": "Izberite, kako ujemati posredovana e-poštna sporočila.\n\n**Možnost 1 — Glava za posredovanje**: Če vaša storitev (npr. SimpleLogin) vključuje izvirnega pošiljatelja v glavo po meri, vnesite ime te glave. Mail and Packages jo bo uporabil za samodejno ujemanje naslovov prevoznikov.\n\n**Možnost 2 — Seznam naslovov**: Vnesite naslove za posredovanje, ki jih je vaša storitev dodelila vsakemu prevozniku, ločene z vejicami.", - "data": { - "forwarding_header": "Glava za posredovanje (npr. X-SimpleLogin-Original-From)", - "forwarded_emails": "Posredovani e-poštni naslovi" - }, - "data_description": { - "forwarding_header": "Če vaša storitev posredovanja vključuje izvirnega pošiljatelja v glavi, vnesite tukaj ime glave. To ima prednost pred spodnjim seznamom naslovov. Vnesite '(none)' ali pustite prazno, da namesto tega uporabite seznam naslovov.", - "forwarded_emails": "Z vejicami ločen seznam naslovov za posredovanje, od katerih bodo prihajala sporočila. Pustite '(none)', če uporabljate zgornjo glavo za posredovanje." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/sv.json b/custom_components/mail_and_packages/translations/sv.json deleted file mode 100644 index c6fbfc9..0000000 --- a/custom_components/mail_and_packages/translations/sv.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "Endast en enda konfiguration av post och paket är tillåten.", - "reconfigure_successful": "Omkonfigurering lyckades", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "Det går inte att ansluta eller logga in på e-postservern. Kontrollera loggen för mer information.", - "invalid_path": "Förvara bilderna i en annan katalog.", - "ffmpeg_not_found": "Generera MP4 kräver ffmpeg", - "amazon_domain": "Ogiltig vidarebefordrings e-postadress.", - "file_not_found": "Bildfilen hittades inte", - "invalid_email_format": "Ogiltigt format för e-postadress.", - "missing_forwarded_emails": "Saknade vidarebefordrade e-postadresser. Du kan ange '(none)' för att rensa denna inställning.", - "path_not_found": "Katalogen hittades inte", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "E-postmapp", - "resources": "Sensorlista", - "scan_interval": "Skanningsintervall (minuter)", - "image_path": "Bildväg", - "gif_duration": "Bildens längd (sekunder)", - "image_security": "Slumpmässig bildfilnamn", - "imap_timeout": "Tid i sekunder innan anslutningen kopplas från (sekunder, minst 10)", - "generate_mp4": "Skapa mp4 från bilder", - "allow_external": "Skapa bild för notifikationsappar", - "custom_img": "Använd anpassad USPS 'inget post' bild?", - "amazon_custom_img": "Använd anpassad Amazon 'ingen leverans' bild?", - "ups_custom_img": "Använd anpassad UPS 'ingen leverans' bild?", - "walmart_custom_img": "Använd anpassad Walmart 'ingen leverans' bild?", - "fedex_custom_img": "Använd anpassad FedEx 'ingen leverans' bild?", - "generic_custom_img": "Använd anpassad generisk 'ingen leverans' bild?", - "generate_grid": "Skapa bildrutnät för LLM-visionsmodeller", - "allow_forwarded_emails": "Tillåt vidarebefordrade e-postmeddelanden utöver en tjänsts standardvärde (t.ex. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Inkludera USPS platshållare utan bild i GIF?" - }, - "description": "Avsluta konfigurationen genom att anpassa följande baserat på din e-poststruktur och installation av hemassistent. \n\n Mer information om alternativen [Mail and Packages] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) läser [konfiguration, mallar , och automatiseringsavsnitt] (https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) på GitHub.", - "title": "Mail och paket (steg 2 av 2)" - }, - "config_3": { - "data": { - "custom_img_file": "Sökväg till anpassad bild: (t.ex: bilder/min_anpassade_ingen_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "Ange sökvägen och filnamnet till din anpassade ingen post-bild nedan.\n\nExempel: bilder/anpassad_nomail.gif", - "title": "Post och paket (Steg 3 av 3)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon domän", - "amazon_fwds": "Amazon vidarebefordrade e-postadresser", - "amazon_days": "Dagar tillbaka för att kontrollera Amazon-e-postmeddelanden" - }, - "description": "Vänligen ange domänen som Amazon skickar e-postmeddelanden från (t.ex: amazon.com eller amazon.de)\n\nOm du använder Amazon vidarebefordrade e-postmeddelanden, vänligen separera varje adress med ett kommatecken eller ange (ingen) för att rensa denna inställning.\n\nObs: om du konfigurerade ett vidarebefordringshuvud i föregående steg kommer fältet för vidarebefordringsadress inte att visas — huvudet hanterar Amazon e-postkopplingen automatiskt.", - "title": "Amazon-inställningar" - }, - "reconfig_2": { - "data": { - "folder": "E-postmapp", - "scan_interval": "Skanningsintervall (minuter)", - "image_path": "Bildväg", - "gif_duration": "Bildens längd (sekunder)", - "image_security": "Slumpmässig bildfilnamn", - "generate_mp4": "Skapa mp4 från bilder", - "resources": "Sensorlista", - "imap_timeout": "Tid i sekunder innan anslutningen kopplas från (sekunder, minst 10)", - "allow_external": "Skapa bild för notifikationsappar", - "custom_img": "Använd anpassad USPS 'inget post' bild?", - "amazon_custom_img": "Använd anpassad Amazon 'ingen leverans' bild?", - "ups_custom_img": "Använd anpassad UPS 'ingen leverans' bild?", - "walmart_custom_img": "Använd anpassad Walmart 'ingen leverans' bild?", - "fedex_custom_img": "Använd anpassad FedEx 'ingen leverans' bild?", - "generic_custom_img": "Använd anpassad generisk 'ingen leverans' bild?", - "generate_grid": "Skapa bildrutnät för LLM-visionsmodeller", - "allow_forwarded_emails": "Tillåt vidarebefordrade e-postmeddelanden utöver en tjänsts standardvärde (t.ex. no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "Inkludera USPS platshållare utan bild i GIF?" - }, - "description": "Slutför konfigurationen genom att anpassa följande baserat på din e-poststruktur och Home Assistant-installation.\n\nFör detaljer om [Mail and Packages integration](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) alternativen granska [konfiguration, mallar och automatiseringssektionen](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) på GitHub.\n\nOm du använder vidarebefordrade e-postmeddelanden från Amazon, separera varje adress med ett kommatecken eller ange (ingen) för att rensa denna inställning.", - "title": "Mail och paket (steg 2 av 2)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Sökväg till anpassad bild: (t.ex.: bilder/min_anpassade_ingen_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "Ange sökvägen och filnamnet till din anpassade ingen post-bild nedan.\n\nExempel: bilder/anpassad_nomail.gif", - "title": "Post och paket (Steg 3 av 3)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon domän", - "amazon_fwds": "Amazon vidarebefordrade e-postadresser", - "amazon_days": "Dagar tillbaka för att kontrollera Amazon-e-postmeddelanden" - }, - "description": "Vänligen ange domänen som Amazon skickar e-postmeddelanden från (t.ex: amazon.com eller amazon.de)\n\nOm du använder Amazon vidarebefordrade e-postmeddelanden, vänligen separera varje adress med ett kommatecken eller ange (ingen) för att rensa denna inställning.\n\nObs: om du konfigurerade ett vidarebefordringshuvud i föregående steg kommer fältet för vidarebefordringsadress inte att visas — huvudet hanterar Amazon e-postkopplingen automatiskt.", - "title": "Amazon-inställningar" - }, - "config_storage": { - "data": { - "storage": "Katalog för att lagra bilder" - }, - "description": "Vänligen ange den katalog du vill att dina bilder ska lagras i.\nStandardinställningen är automatiskt ifylld.", - "title": "Bildlagringsplats" - }, - "reconfig_storage": { - "data": { - "storage": "Katalog för att lagra bilder" - }, - "description": "Vänligen ange den katalog du vill att dina bilder ska lagras i.\nStandardinställningen är automatiskt ifylld.", - "title": "Bildlagringsplats" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "Vidarebefordringshuvud (t.ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Vidarebefordrade e-postadresser" - }, - "data_description": { - "forwarding_header": "Om din vidarebefordringstjänst inkluderar den ursprungliga avsändaren i ett huvud, ange huvud-namnet här. Detta har företräde framför adresslistan nedan. Ange '(none)' eller lämna tomt för att använda adresslistan istället.", - "forwarded_emails": "Kommaseparerad lista över vidarebefordringsadresser som meddelanden kommer att anlända från. Lämna '(none)' om du använder vidarebefordringshuvudet ovan." - }, - "description": "Välj hur vidarebefordrade e-postmeddelanden ska matchas.\n\n**Alternativ 1 — Vidarebefordringshuvud**: Om din tjänst (t.ex. SimpleLogin) inkluderar den ursprungliga avsändaren i ett anpassat huvud, ange det huvud-namnet. Mail and Packages kommer att använda det för att automatiskt matcha transportörernas adresser.\n\n**Alternativ 2 — Adresslista**: Ange de vidarebefordringsadresser som din tjänst har tilldelat varje transportör, separerade med kommatecken.", - "title": "Inställningar för vidarebefordrade e-postmeddelanden" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "Vidarebefordringshuvud (t.ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Vidarebefordrade e-postadresser" - }, - "data_description": { - "forwarding_header": "Om din vidarebefordringstjänst inkluderar den ursprungliga avsändaren i ett huvud, ange huvud-namnet här. Detta har företräde framför adresslistan nedan. Ange '(none)' eller lämna tomt för att använda adresslistan istället.", - "forwarded_emails": "Kommaseparerad lista över vidarebefordringsadresser som meddelanden kommer att anlända från. Lämna '(none)' om du använder vidarebefordringshuvudet ovan." - }, - "description": "Välj hur vidarebefordrade e-postmeddelanden ska matchas.\n\n**Alternativ 1 — Vidarebefordringshuvud**: Om din tjänst (t.ex. SimpleLogin) inkluderar den ursprungliga avsändaren i ett anpassat huvud, ange det huvud-namnet. Mail and Packages kommer att använda det för att automatiskt matcha transportörernas adresser.\n\n**Alternativ 2 — Adresslista**: Ange de vidarebefordringsadresser som din tjänst har tilldelat varje transportör, separerade med kommatecken.", - "title": "Inställningar för vidarebefordrade e-postmeddelanden" - }, - "imap_config": { - "description": "Vänligen ange anslutningsinformationen för din e-postserver.", - "data": { - "host": "Värd", - "password": "Lösenord", - "port": "Hamn", - "username": "Användarnamn", - "verify_ssl": "Verifiera SSL-certifikat", - "imap_security": "IMAP-säkerhet" - }, - "title": "Mail och paket (steg 1 av 2)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "Värd", - "password": "Lösenord", - "port": "Hamn", - "username": "Användarnamn", - "imap_security": "IMAP-säkerhet", - "verify_ssl": "Verifiera SSL-certifikat" - }, - "description": "Vänligen ange anslutningsinformationen för din e-postserver.", - "title": "Mail och paket (steg 1 av 2)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages-autentisering misslyckades", - "fix_flow": { - "step": { - "confirm": { - "title": "Återautentisera e-postserver", - "description": "Autentisering mot din IMAP-e-postserver misslyckades. Klicka på skicka för att starta återautentiseringsflödet." - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Mail och paket (steg 2 av 2)", - "description": "Slutför konfigurationen genom att anpassa följande baserat på din e-poststruktur och Home Assistant-installation.\n\nFör detaljer om [Mail and Packages integration](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) alternativen granska [konfiguration, mallar och automatiseringssektionen](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration) på GitHub.\n\nOm du använder vidarebefordrade e-postmeddelanden från Amazon, separera varje adress med ett kommatecken eller ange (ingen) för att rensa denna inställning.", - "data": { - "folder": "E-postmapp", - "resources": "Sensorlista", - "scan_interval": "Skanningsintervall (minuter)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "Bildväg", - "gif_duration": "Bildens längd (sekunder)", - "image_security": "Slumpmässig bildfilnamn", - "imap_timeout": "Tid i sekunder innan anslutningen kopplas från (sekunder, minst 10)", - "generate_mp4": "Skapa mp4 från bilder", - "allow_external": "Skapa bild för notifikationsappar", - "usps_placeholder": "Inkludera USPS platshållare utan bild i GIF?", - "custom_img": "Använd anpassad USPS 'inget post' bild?", - "amazon_custom_img": "Använd anpassad Amazon 'ingen leverans' bild?", - "ups_custom_img": "Använd anpassad UPS 'ingen leverans' bild?", - "walmart_custom_img": "Använd anpassad Walmart 'ingen leverans' bild?", - "fedex_custom_img": "Använd anpassad FedEx 'ingen leverans' bild?", - "generic_custom_img": "Använd anpassad generisk 'ingen leverans' bild?", - "generate_grid": "Skapa bildrutnät för LLM-visionsmodeller", - "allow_forwarded_emails": "Tillåt vidarebefordrade e-postmeddelanden utöver en tjänsts standardvärde (t.ex. no-reply@usps.com)" - } - }, - "options_3": { - "title": "Post och paket (Steg 3 av 3)", - "description": "Ange sökvägen och filnamnet till din anpassade ingen post-bild nedan.\n\nExempel: bilder/anpassad_nomail.gif", - "data": { - "custom_img_file": "Sökväg till anpassad bild: (t.ex.: bilder/min_anpassade_ingen_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Amazon-inställningar", - "description": "Vänligen ange domänen som Amazon skickar e-postmeddelanden från (t.ex: amazon.com eller amazon.de)\n\nOm du använder Amazon vidarebefordrade e-postmeddelanden, vänligen separera varje adress med ett kommatecken eller ange (ingen) för att rensa denna inställning.\n\nObs: om du konfigurerade ett vidarebefordringshuvud i föregående steg kommer fältet för vidarebefordringsadress inte att visas — huvudet hanterar Amazon e-postkopplingen automatiskt.", - "data": { - "amazon_domain": "Amazon domän", - "amazon_fwds": "Amazon vidarebefordrade e-postadresser", - "amazon_days": "Dagar tillbaka för att kontrollera Amazon-e-postmeddelanden" - } - }, - "options_storage": { - "title": "Bildlagringsplats", - "description": "Vänligen ange den katalog du vill att dina bilder ska lagras i.\nStandardinställningen är automatiskt ifylld.", - "data": { - "storage": "Katalog för att lagra bilder" - } - }, - "options_forwarded_emails": { - "title": "Inställningar för vidarebefordrade e-postmeddelanden", - "description": "Välj hur vidarebefordrade e-postmeddelanden ska matchas.\n\n**Alternativ 1 — Vidarebefordringshuvud**: Om din tjänst (t.ex. SimpleLogin) inkluderar den ursprungliga avsändaren i ett anpassat huvud, ange det huvud-namnet. Mail and Packages kommer att använda det för att automatiskt matcha transportörernas adresser.\n\n**Alternativ 2 — Adresslista**: Ange de vidarebefordringsadresser som din tjänst har tilldelat varje transportör, separerade med kommatecken.", - "data": { - "forwarding_header": "Vidarebefordringshuvud (t.ex. X-SimpleLogin-Original-From)", - "forwarded_emails": "Vidarebefordrade e-postadresser" - }, - "data_description": { - "forwarding_header": "Om din vidarebefordringstjänst inkluderar den ursprungliga avsändaren i ett huvud, ange huvud-namnet här. Detta har företräde framför adresslistan nedan. Ange '(none)' eller lämna tomt för att använda adresslistan istället.", - "forwarded_emails": "Kommaseparerad lista över vidarebefordringsadresser som meddelanden kommer att anlända från. Lämna '(none)' om du använder vidarebefordringshuvudet ovan." - } - } - } - } -} diff --git a/custom_components/mail_and_packages/translations/zh_Hant_HK.json b/custom_components/mail_and_packages/translations/zh_Hant_HK.json deleted file mode 100644 index 7fe246f..0000000 --- a/custom_components/mail_and_packages/translations/zh_Hant_HK.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "config": { - "abort": { - "single_instance_allowed": "僅允許郵件和軟件包的單個配置。", - "reconfigure_successful": "重新配置成功", - "oauth_unauthorized": "OAuth unauthorized: access was denied or the authentication was cancelled." - }, - "error": { - "cannot_connect": "無法連接或登錄到郵件服務器。請檢查日誌以獲取詳細信息。", - "invalid_path": "請將圖像存儲在另一個目錄中。", - "ffmpeg_not_found": "生成MP4需要ffmpeg", - "amazon_domain": "無效的轉發電郵地址。", - "file_not_found": "找不到圖像檔案", - "invalid_email_format": "無效的電郵地址格式。", - "missing_forwarded_emails": "缺少轉發的電郵地址。您可以輸入 '(none)' 來清除此設定。", - "path_not_found": "找不到目錄", - "invalid_auth": "Invalid authentication details. Please check your username and password.", - "ssl_error": "An SSL error occurred. Change SSL cipher list and try again." - }, - "step": { - "config_2": { - "data": { - "folder": "郵件文件夾", - "resources": "感應器列表", - "scan_interval": "掃描間隔(分鐘)", - "image_path": "圖像路徑", - "gif_duration": "圖像持續時間(秒)", - "image_security": "隨機圖像文件名", - "imap_timeout": "連接超時前的時間(秒,最少10秒)", - "generate_mp4": "從圖像創建mp4", - "allow_external": "為通知應用程式創建圖像", - "custom_img": "使用自訂的 USPS「無郵件」圖像?", - "amazon_custom_img": "使用自訂的 Amazon「無送貨」圖像?", - "ups_custom_img": "使用自訂的 UPS「無送貨」圖像?", - "walmart_custom_img": "使用自訂的 Walmart「無送貨」圖像?", - "fedex_custom_img": "使用自訂的 FedEx「無送貨」圖像?", - "generic_custom_img": "使用自訂的通用「無送貨」圖像?", - "generate_grid": "為LLM視覺模型創建圖像網格", - "allow_forwarded_emails": "允許轉發的電郵,除了服務的預設值(例如 no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "在 GIF 中包含 USPS 無圖像預留位置?" - }, - "description": "通過根據您的電子郵件結構和Home Assistant安裝自定義以下內容來完成配置。 \n\n有關[郵件和軟件包集成](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration)選項的詳細信息,請查看[配置,模板和自動化部分](GitHub上的https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration)。", - "title": "郵件和包裹(第2步,共2步)" - }, - "config_3": { - "data": { - "custom_img_file": "自訂圖片的路徑:(例如:images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (ie: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (ie: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (ie: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (ie: images/my_custom_no_fedex.jpg)" - }, - "description": "在下方輸入您的自訂無郵件圖像的路徑和檔案名稱。\n\n例如:images/custom_nomail.gif", - "title": "郵件和包裹(第3步,共3步)" - }, - "config_amazon": { - "data": { - "amazon_domain": "Amazon 域名", - "amazon_fwds": "Amazon 轉發的電郵地址", - "amazon_days": "檢查Amazon電郵的天數" - }, - "description": "請輸入亞馬遜發送電子郵件的域名(例如:amazon.com或amazon.de)\n\n如果使用亞馬遜轉發的電子郵件,請用逗號分隔每個地址,或輸入(無)以清除此設定。\n\n注意:如果您在上一步中設定了轉寄標頭,轉寄地址欄位將不會顯示 — 標頭會自動處理 Amazon 電郵的配對。", - "title": "Amazon 設定" - }, - "reconfig_2": { - "data": { - "folder": "郵件文件夾", - "scan_interval": "掃描間隔(分鐘)", - "image_path": "圖像路徑", - "gif_duration": "圖像持續時間(秒)", - "image_security": "隨機圖像文件名", - "generate_mp4": "從圖像創建mp4", - "resources": "感應器列表", - "imap_timeout": "連接超時前的時間(秒,最少10秒)", - "allow_external": "為通知應用程式創建圖像", - "custom_img": "使用自訂的 USPS「無郵件」圖像?", - "amazon_custom_img": "使用自訂的 Amazon「無送貨」圖像?", - "ups_custom_img": "使用自訂的 UPS「無送貨」圖像?", - "walmart_custom_img": "使用自訂的 Walmart「無送貨」圖像?", - "fedex_custom_img": "使用自訂的 FedEx「無送貨」圖像?", - "generic_custom_img": "使用自訂的通用「無送貨」圖像?", - "generate_grid": "為LLM視覺模型創建圖像網格", - "allow_forwarded_emails": "允許轉發的電郵,除了服務的預設值(例如 no-reply@usps.com)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "usps_placeholder": "在 GIF 中包含 USPS 無圖像預留位置?" - }, - "description": "根據您的電郵結構和Home Assistant安裝來完成配置。\n\n有關[郵件和包裹整合](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration)選項的詳情,請查閱GitHub上的[配置、模板和自動化部分](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration)。\n\n如果使用Amazon轉發的電郵,請用逗號分隔每個地址,或輸入(none)以清除此設定。", - "title": "郵件和包裹(第2步,共2步)" - }, - "reconfig_3": { - "data": { - "custom_img_file": "Path to custom image: (i.e.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - }, - "description": "在下方輸入您的自訂無郵件圖像的路徑和檔案名稱。\n\n例如:images/custom_nomail.gif", - "title": "郵件和包裹(第3步,共3步)" - }, - "reconfig_amazon": { - "data": { - "amazon_domain": "Amazon 域名", - "amazon_fwds": "Amazon 轉發的電郵地址", - "amazon_days": "檢查Amazon電郵的天數" - }, - "description": "請輸入亞馬遜發送電子郵件的域名(例如:amazon.com或amazon.de)\n\n如果使用亞馬遜轉發的電子郵件,請用逗號分隔每個地址,或輸入(無)以清除此設定。\n\n注意:如果您在上一步中設定了轉寄標頭,轉寄地址欄位將不會顯示 — 標頭會自動處理 Amazon 電郵的配對。", - "title": "Amazon 設定" - }, - "config_storage": { - "data": { - "storage": "儲存圖片的目錄" - }, - "description": "請輸入您希望存儲圖像的目錄。\n預設值會自動填充。", - "title": "圖像儲存位置" - }, - "reconfig_storage": { - "data": { - "storage": "儲存圖片的目錄" - }, - "description": "請輸入您希望存儲圖像的目錄。\n預設值會自動填充。", - "title": "圖像儲存位置" - }, - "config_forwarded_emails": { - "data": { - "forwarding_header": "轉寄標頭 (例如 X-SimpleLogin-Original-From)", - "forwarded_emails": "轉發的電郵地址" - }, - "data_description": { - "forwarding_header": "如果您的轉寄服務在標頭中包含原始寄件者,請在此輸入標頭名稱。這優先於下方的地址清單。輸入「(none)」或留空以改用地址清單。", - "forwarded_emails": "以逗號分隔的轉寄地址清單,訊息將從這些地址發送。如果使用上方的轉寄標頭,請保留「(none)」。" - }, - "description": "選擇如何匹配轉寄的電子郵件。\n\n**選項 1 — 轉寄標頭**:如果您的服務(例如 SimpleLogin)在自訂標頭中包含原始寄件者,請輸入該標頭名稱。Mail and Packages 將使用它自動匹配快遞公司地址。\n\n**選項 2 — 地址清單**:輸入您的服務為每個快遞公司分配的轉寄地址,以逗號分隔。", - "title": "轉寄電子郵件設定" - }, - "reconfig_forwarded_emails": { - "data": { - "forwarding_header": "轉寄標頭 (例如 X-SimpleLogin-Original-From)", - "forwarded_emails": "轉發的電郵地址" - }, - "data_description": { - "forwarding_header": "如果您的轉寄服務在標頭中包含原始寄件者,請在此輸入標頭名稱。這優先於下方的地址清單。輸入「(none)」或留空以改用地址清單。", - "forwarded_emails": "以逗號分隔的轉寄地址清單,訊息將從這些地址發送。如果使用上方的轉寄標頭,請保留「(none)」。" - }, - "description": "選擇如何匹配轉寄的電子郵件。\n\n**選項 1 — 轉寄標頭**:如果您的服務(例如 SimpleLogin)在自訂標頭中包含原始寄件者,請輸入該標頭名稱。Mail and Packages 將使用它自動匹配快遞公司地址。\n\n**選項 2 — 地址清單**:輸入您的服務為每個快遞公司分配的轉寄地址,以逗號分隔。", - "title": "轉寄電子郵件設定" - }, - "imap_config": { - "description": "請輸入您的郵件伺服器的連接資訊。", - "data": { - "host": "主辦", - "password": "密碼", - "port": "港口", - "username": "用戶名", - "verify_ssl": "驗證 SSL 證書", - "imap_security": "IMAP 安全性" - }, - "title": "郵件和包裹(第1步,共2步)" - }, - "user": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reconfig_imap": { - "data": { - "host": "主辦", - "password": "密碼", - "port": "港口", - "username": "用戶名", - "imap_security": "IMAP 安全性", - "verify_ssl": "驗證 SSL 證書" - }, - "description": "請輸入您的郵件伺服器的連接資訊。", - "title": "郵件和包裹(第1步,共2步)" - }, - "reconfigure": { - "description": "Select the authentication method for your mail server.", - "data": { - "auth_type": "Authentication Type" - }, - "title": "Authentication Method" - }, - "reauth_confirm": { - "description": "The authentication for your mail server has expired or is invalid. Please re-authenticate to continue.", - "data": { - "username": "Username", - "password": "Password" - }, - "title": "Re-authenticate Mail Server" - } - } - }, - "selector": { - "auth_type": { - "options": { - "password": "Password", - "oauth2_microsoft": "OAuth2 - Microsoft (Outlook/Exchange)", - "oauth2_google": "OAuth2 - Google (Gmail)" - } - } - }, - "issues": { - "auth_failed": { - "title": "Mail and Packages 認證失敗", - "fix_flow": { - "step": { - "confirm": { - "title": "重新認證郵件伺服器", - "description": "認證 IMAP 郵件伺服器失敗。請按提交以啟動重新認證流程。" - } - } - } - } - }, - "options": { - "step": { - "init": { - "title": "郵件和包裹(第2步,共2步)", - "description": "根據您的電郵結構和Home Assistant安裝來完成配置。\n\n有關[郵件和包裹整合](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration)選項的詳情,請查閱GitHub上的[配置、模板和自動化部分](https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings#configuration)。\n\n如果使用Amazon轉發的電郵,請用逗號分隔每個地址,或輸入(none)以清除此設定。", - "data": { - "folder": "郵件文件夾", - "resources": "感應器列表", - "scan_interval": "掃描間隔(分鐘)", - "custom_days": "Days back to check for package emails (minimum 1, default 3)", - "image_path": "圖像路徑", - "gif_duration": "圖像持續時間(秒)", - "image_security": "隨機圖像文件名", - "imap_timeout": "連接超時前的時間(秒,最少10秒)", - "generate_mp4": "從圖像創建mp4", - "allow_external": "為通知應用程式創建圖像", - "usps_placeholder": "在 GIF 中包含 USPS 無圖像預留位置?", - "custom_img": "使用自訂的 USPS「無郵件」圖像?", - "amazon_custom_img": "使用自訂的 Amazon「無送貨」圖像?", - "ups_custom_img": "使用自訂的 UPS「無送貨」圖像?", - "walmart_custom_img": "使用自訂的 Walmart「無送貨」圖像?", - "fedex_custom_img": "使用自訂的 FedEx「無送貨」圖像?", - "generic_custom_img": "使用自訂的通用「無送貨」圖像?", - "generate_grid": "為LLM視覺模型創建圖像網格", - "allow_forwarded_emails": "允許轉發的電郵,除了服務的預設值(例如 no-reply@usps.com)" - } - }, - "options_3": { - "title": "郵件和包裹(第3步,共3步)", - "description": "在下方輸入您的自訂無郵件圖像的路徑和檔案名稱。\n\n例如:images/custom_nomail.gif", - "data": { - "custom_img_file": "Path to custom image: (i.e.: images/my_custom_no_mail.jpg)", - "amazon_custom_img_file": "Path to custom Amazon image: (i.e.: images/my_custom_no_amazon.jpg)", - "ups_custom_img_file": "Path to custom UPS image: (i.e.: images/my_custom_no_ups.jpg)", - "walmart_custom_img_file": "Path to custom Walmart image: (i.e.: images/my_custom_no_walmart.jpg)", - "fedex_custom_img_file": "Path to custom FedEx image: (i.e.: images/my_custom_no_fedex.jpg)" - } - }, - "options_amazon": { - "title": "Amazon 設定", - "description": "請輸入亞馬遜發送電子郵件的域名(例如:amazon.com或amazon.de)\n\n如果使用亞馬遜轉發的電子郵件,請用逗號分隔每個地址,或輸入(無)以清除此設定。\n\n注意:如果您在上一步中設定了轉寄標頭,轉寄地址欄位將不會顯示 — 標頭會自動處理 Amazon 電郵的配對。", - "data": { - "amazon_domain": "Amazon 域名", - "amazon_fwds": "Amazon 轉發的電郵地址", - "amazon_days": "檢查Amazon電郵的天數" - } - }, - "options_storage": { - "title": "圖像儲存位置", - "description": "請輸入您希望存儲圖像的目錄。\n預設值會自動填充。", - "data": { - "storage": "儲存圖片的目錄" - } - }, - "options_forwarded_emails": { - "title": "轉寄電子郵件設定", - "description": "選擇如何匹配轉寄的電子郵件。\n\n**選項 1 — 轉寄標頭**:如果您的服務(例如 SimpleLogin)在自訂標頭中包含原始寄件者,請輸入該標頭名稱。Mail and Packages 將使用它自動匹配快遞公司地址。\n\n**選項 2 — 地址清單**:輸入您的服務為每個快遞公司分配的轉寄地址,以逗號分隔。", - "data": { - "forwarding_header": "轉寄標頭 (例如 X-SimpleLogin-Original-From)", - "forwarded_emails": "轉發的電郵地址" - }, - "data_description": { - "forwarding_header": "如果您的轉寄服務在標頭中包含原始寄件者,請在此輸入標頭名稱。這優先於下方的地址清單。輸入「(none)」或留空以改用地址清單。", - "forwarded_emails": "以逗號分隔的轉寄地址清單,訊息將從這些地址發送。如果使用上方的轉寄標頭,請保留「(none)」。" - } - } - } - } -} diff --git a/custom_components/mail_and_packages/utils/__init__.py b/custom_components/mail_and_packages/utils/__init__.py deleted file mode 100644 index 3d48004..0000000 --- a/custom_components/mail_and_packages/utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Utility functions for Mail and Packages.""" diff --git a/custom_components/mail_and_packages/utils/amazon.py b/custom_components/mail_and_packages/utils/amazon.py deleted file mode 100644 index 83db281..0000000 --- a/custom_components/mail_and_packages/utils/amazon.py +++ /dev/null @@ -1,408 +0,0 @@ -"""Amazon specific utilities.""" - -from __future__ import annotations - -import datetime -import email -import logging -import re -from email.header import decode_header -from functools import partial -from pathlib import Path -from typing import Any - -import aiohttp -import dateparser -from aioimaplib import IMAP4_SSL - -from custom_components.mail_and_packages.const import ( - AMAZON_DELIVERED_SUBJECT, - AMAZON_DOMAINS, - AMAZON_EMAIL, - AMAZON_IMG_LIST, - AMAZON_IMG_PATTERN, - AMAZON_ORDERED_SUBJECT, - AMAZON_SHIPMENT_SUBJECT, - AMAZON_SHIPMENT_TRACKING, - AMAZON_TIME_PATTERN, - AMAZON_TIME_PATTERN_END, - AMAZON_TIME_PATTERN_REGEX, - DEFAULT_AMAZON_DAYS, -) -from custom_components.mail_and_packages.utils.cache import EmailCache -from custom_components.mail_and_packages.utils.date import get_today -from custom_components.mail_and_packages.utils.image import io_save_file -from custom_components.mail_and_packages.utils.imap import ( - email_fetch, - email_search, -) - -_LOGGER = logging.getLogger(__name__) - -_MAX_IMAGE_SIZE = 10 * 1024 * 1024 # 10 MB - -DOMAIN_LANG_MAP = { - "amazon.de": [ - "versandbestaetigung", - "Geliefert:", - "Zugestellt:", - "Versandt:", - "In Zustellung:", - "Zustellung:", - ], - "amazon.it": [ - "conferma-spedizione", - "Consegna effettuata:", - "Arriverà", - "Spedito:", - ], - "amazon.nl": [ - "update-bestelling", - "verzending-volgen", - "auto-bevestiging", - "Bezorgd:", - ], - "amazon.fr": ["confirmation-commande", "Livré", "Livraison : Votre", "Arrivée :"], - "amazon.ca": ["confirmation-commande", "Livré", "Livraison : Votre", "Arrivée :"], - "amazon.es": [ - "confirmar-envio", - "Entregado:", - "Enviado:", - "Pedido efetuado:", - "Chega ", - ], - "amazon.pl": ["Dostarczono:"], -} - - -def filter_amazon_strings(strings: list[str], domain: str) -> list[str]: - """Filter list of strings based on the domain language.""" - all_mapped_strings = [] - for lang_list in DOMAIN_LANG_MAP.values(): - all_mapped_strings.extend(lang_list) - - def is_mapped(s: str) -> bool: - return any(m in s for m in all_mapped_strings) - - base_strings = [s for s in strings if not is_mapped(s)] - - domain_strings = [] - if domain in DOMAIN_LANG_MAP: - mapped_for_domain = DOMAIN_LANG_MAP[domain] - domain_strings = [s for s in strings if any(m in s for m in mapped_for_domain)] - - # Only amazon.ca and unmapped domains (like .com, .co.uk) should use base English strings - if domain in DOMAIN_LANG_MAP and domain != "amazon.ca": - return list(dict.fromkeys(domain_strings)) - - return list(dict.fromkeys(base_strings + domain_strings)) - - -def get_decoded_subject(msg: email.message.Message) -> str: - """Decode email subject.""" - header_val = msg["subject"] - if header_val: - decoded_parts = [] - for subject_bytes, encoding in decode_header(header_val): - if isinstance(subject_bytes, bytes): - if encoding: - try: - decoded_parts.append(subject_bytes.decode(encoding, "ignore")) - continue - except (LookupError, UnicodeError): - pass - decoded_parts.append(subject_bytes.decode("utf-8", "ignore")) - else: - decoded_parts.append(str(subject_bytes)) - return "".join(decoded_parts) - - body = get_email_body(msg) - if not body: - return "" - title_match = re.search( - r"]*>([^<]+)", body, re.IGNORECASE | re.DOTALL - ) - if not title_match: - return "" - return title_match.group(1).strip() - - -def get_email_body(msg: email.message.Message) -> str: - """Extract and decode the email body safely.""" - try: - if msg.is_multipart(): - # Standard practice is to look for text/plain then text/html - for part in msg.walk(): - if part.get_content_type() == "text/plain": - return part.get_payload(decode=True).decode("utf-8", "ignore") - # If no text/plain, fall back to first part - payload = msg.get_payload(0) - if isinstance(payload, email.message.Message): - return payload.get_payload(decode=True).decode("utf-8", "ignore") - return str(payload) - - return msg.get_payload(decode=True).decode("utf-8", "ignore") - except (ValueError, TypeError, IndexError, AttributeError) as err: - _LOGGER.debug("Problem decoding email message: %s", err) - return "" - - -def extract_order_numbers(text: str, pattern: re.Pattern | str) -> list[str]: - """Extract order numbers from text.""" - if isinstance(pattern, str): - pattern = re.compile(pattern) - return pattern.findall(text) - - -async def parse_amazon_arrival_date( - hass: Any, - email_msg: str, - email_date: datetime.date, -) -> datetime.date | None: - """Determine arrival date from email.""" - today_date = get_today() - - # Try using regex for more precise extraction of the arrival date string - if date_str := amazon_date_regex(email_msg): - base_datetime = datetime.datetime.combine( - email_date or today_date, - datetime.time(), - ) - - # 1. Try parsing without PREFER_DATES_FROM: future to handle relative terms (any language) - dateobj = await hass.async_add_executor_job( - partial( - dateparser.parse, - date_str, - settings={ - "RELATIVE_BASE": base_datetime, - "RETURN_AS_TIMEZONE_AWARE": False, - }, - ), - ) - if dateobj: - parsed_date = dateobj.date() - base_date = email_date or today_date - # Only accept matches that resolve to email_date (today) or email_date + 1 day (tomorrow) - if ( - parsed_date == base_date - or parsed_date == base_date + datetime.timedelta(days=1) - ): - return parsed_date - - # 2. Fall back to parsing with PREFER_DATES_FROM: future for absolute dates - dateobj = await hass.async_add_executor_job( - partial( - dateparser.parse, - date_str, - settings={ - "PREFER_DATES_FROM": "future", - "RELATIVE_BASE": base_datetime, - "RETURN_AS_TIMEZONE_AWARE": False, - }, - ), - ) - if dateobj: - return dateobj.date() - - # Fallback to chunk-based parsing - for search in AMAZON_TIME_PATTERN: - if search not in email_msg: - continue - - start = email_msg.find(search) + len(search) - chunk = email_msg[start : start + 50] - - dateobj = await hass.async_add_executor_job( - partial( - dateparser.parse, - chunk, - settings={ - "PREFER_DATES_FROM": "future", - "RELATIVE_BASE": datetime.datetime.combine( - email_date or today_date, - datetime.time(), - ), - "RETURN_AS_TIMEZONE_AWARE": False, - }, - ), - ) - if dateobj: - return dateobj.date() - return None - - -def amazon_email_addresses( - fwds: list[str] | str | None = None, - domain: str | None = None, -) -> list[str]: - """Generate Amazon email addresses.""" - if isinstance(fwds, str): - fwds = [fwds] - elif not isinstance(fwds, (list, tuple)): - fwds = None - - if domain is None: - domain = "amazon.com" - - # Use both AMAZON_EMAIL and AMAZON_SHIPMENT_TRACKING for prefixes - prefixes = list(AMAZON_EMAIL) - for p in AMAZON_SHIPMENT_TRACKING: - if f"{p}@" not in prefixes: - prefixes.append(f"{p}@") - - prefixes = filter_amazon_strings(prefixes, domain) - - value = [f"{e}{domain}" for e in prefixes] - if fwds: - for fwd in fwds: - if "@" in fwd: - value.append(fwd) - elif any(f in fwd for f in AMAZON_DOMAINS): - value.extend(f"{e}{fwd}" for e in prefixes) - return value - - -async def search_amazon_emails( - account: IMAP4_SSL, - address_list: list[str], - days: int, - domain: str | None = None, - cache: EmailCache | None = None, - forwarding_header: str = "", -) -> list[bytes]: - """Search for Amazon emails.""" - if not isinstance(days, int): - try: - days = int(days) - except (ValueError, TypeError): - days = DEFAULT_AMAZON_DAYS - - past_date = get_today() - datetime.timedelta(days=days) - tfmt = past_date.strftime("%d-%b-%Y") - amazon_subjects = ( - AMAZON_DELIVERED_SUBJECT + AMAZON_SHIPMENT_SUBJECT + AMAZON_ORDERED_SUBJECT - ) - if domain: - amazon_subjects = filter_amazon_strings(amazon_subjects, domain) - - (server_response, sdata) = await email_search( - account=account, - address=address_list, - date=tfmt, - subject=amazon_subjects, - header=forwarding_header, - ) - - if server_response != "OK" or not sdata[0]: - return [] - - return sdata[0].split() - - -async def download_amazon_img( - img_url: str, - img_path: str, - img_name: str, - hass: Any, -) -> None: - """Download image from url.""" - img_path = Path(img_path) / "amazon" - filepath = img_path / img_name - timeout = aiohttp.ClientTimeout(total=30) - async with aiohttp.ClientSession(timeout=timeout) as session: - try: - async with session.get(img_url.replace("&", "&")) as resp: - if resp.status != 200: - return - content_type = resp.headers.get("content-type", "") - if "image" not in content_type: - return - content_length = int(resp.headers.get("content-length", 0)) - if content_length > _MAX_IMAGE_SIZE: - _LOGGER.warning( - "Amazon image too large to download (%d bytes), skipping", - content_length, - ) - return - data = await resp.read() - if len(data) > _MAX_IMAGE_SIZE: - _LOGGER.warning( - "Amazon image exceeds size limit after download, discarding" - ) - return - await hass.async_add_executor_job(io_save_file, filepath, data) - except aiohttp.ClientError as err: - _LOGGER.error("Problem downloading file: %s", err) - - -async def get_amazon_image_urls( - sdata: Any, - account: IMAP4_SSL, - cache: EmailCache | None = None, -) -> list[str]: - """Find all Amazon delivery image URLs.""" - mail_list = sdata.split() - pattern = re.compile(rf"{AMAZON_IMG_PATTERN}") - urls = [] - for i in mail_list: - if cache: - data = (await cache.fetch(i, "(RFC822)"))[1] - else: - data = (await email_fetch(account, i, "(RFC822)"))[1] - for response_part in data: - if isinstance(response_part, (bytes, bytearray)): - msg = email.message_from_bytes(response_part) - for part in msg.walk(): - if part.get_content_type() != "text/html": - continue - part_payload = part.get_payload(decode=True) - part_content = part_payload.decode("utf-8", "ignore") - found = pattern.findall(part_content) - for url in found: - if url[1] not in AMAZON_IMG_LIST: - continue - full_url = url[0] + url[1] + url[2] - if full_url not in urls: - urls.append(full_url) - return urls - - -def _extract_hub_code( - body: str, - hub_pattern: str, - subject: str, - subject_pattern: str, -) -> str: - """Extract Amazon Hub code from email body or subject.""" - # Check subject first - if (found := re.compile(subject_pattern).search(subject)) is not None: - return found.group(3) - - # Check body - if (found := re.compile(hub_pattern).search(body)) is not None: - return found.group(2) - return "" - - -def amazon_date_search(email_msg: str, patterns: list[str] | None = None) -> int: - """Search for a date pattern in an email message and return its index.""" - if patterns is None: - patterns = AMAZON_TIME_PATTERN_END - - for pattern in patterns: - if (index := email_msg.find(pattern)) != -1: - return index - return -1 - - -def amazon_date_regex(email_msg: str, patterns: list[str] | None = None) -> str | None: - """Search for a date pattern using regex and return the first capture group.""" - if patterns is None: - patterns = AMAZON_TIME_PATTERN_REGEX - - for pattern in patterns: - if (found := re.compile(pattern, re.IGNORECASE).search(email_msg)) is not None: - if found.groups(): - return found.group(1) - return None diff --git a/custom_components/mail_and_packages/utils/cache.py b/custom_components/mail_and_packages/utils/cache.py deleted file mode 100644 index 1381959..0000000 --- a/custom_components/mail_and_packages/utils/cache.py +++ /dev/null @@ -1,104 +0,0 @@ -"""Email caching utility for Mail and Packages.""" - -import logging - -from aioimaplib import IMAP4_SSL - -from .imap import email_fetch, email_fetch_batch, email_fetch_headers, email_fetch_text - -_LOGGER = logging.getLogger(__name__) - - -class EmailCache: - """Cache for IMAP email fetches to avoid duplicate downloads.""" - - def __init__(self, account: IMAP4_SSL) -> None: - """Initialize the cache.""" - self.account = account - self._cache_rfc822: dict[str, tuple] = {} - self._cache_text: dict[str, tuple] = {} - self._cache_headers: dict[str, tuple] = {} - - async def fetch(self, email_id: str | bytes, parts: str = "(RFC822)") -> tuple: # noqa: C901 - """Fetch email content or return from cache.""" - eid_str = email_id.decode() if isinstance(email_id, bytes) else str(email_id) - - if parts in ("(RFC822)", "BODY[]"): - if eid_str in self._cache_rfc822: - return self._cache_rfc822[eid_str] - res = await email_fetch(self.account, eid_str, parts) - if res[0] == "OK": - self._cache_rfc822[eid_str] = res - return res - - if "HEADER" in parts: - if eid_str in self._cache_headers: - return self._cache_headers[eid_str] - # fallback to RFC822 cache if we already have it - if eid_str in self._cache_rfc822: - return self._cache_rfc822[eid_str] - res = await email_fetch_headers(self.account, eid_str) - if res[0] == "OK": - self._cache_headers[eid_str] = res - return res - - if "TEXT" in parts or parts == "(BODY[1])": - if eid_str in self._cache_text: - return self._cache_text[eid_str] - if eid_str in self._cache_rfc822: - return self._cache_rfc822[eid_str] - res = await email_fetch_text(self.account, eid_str, parts) - if res[0] == "OK": - self._cache_text[eid_str] = res - return res - - # Unknown part, bypass cache - return await email_fetch(self.account, eid_str, parts) - - async def fetch_batch( - self, email_ids: list[str | bytes], parts: str = "(RFC822)" - ) -> tuple: - """Fetch multiple emails in a batch, retrieving from cache if applicable.""" - # For simplicity, if we don't have all of them, fetch the missing ones in batch - missing_ids = [] - eid_strs = [e.decode() if isinstance(e, bytes) else str(e) for e in email_ids] - - # Determine which ones we already have - for eid_str in eid_strs: - if parts in ("(RFC822)", "BODY[]"): - if eid_str not in self._cache_rfc822: - missing_ids.append(eid_str) - elif "HEADER" in parts: - if ( - eid_str not in self._cache_headers - and eid_str not in self._cache_rfc822 - ): - missing_ids.append(eid_str) - elif "TEXT" in parts or parts == "(BODY[1])": - if ( - eid_str not in self._cache_text - and eid_str not in self._cache_rfc822 - ): - missing_ids.append(eid_str) - else: - missing_ids.append(eid_str) - - # Batch fetch only what is missing - if missing_ids: - # Just use fetch() internally for now to populate individual items since fetch_batch - # returns combined lines which might be tricky to parse. - # Wait, email_fetch_batch might be tricky to cache without parsing the response to separate emails. - # So actually we will just loop `fetch` for batch if we want it cleanly cached, or we don't cache - # `fetch_batch` if we aren't parsing it. - # Actually `fetch_batch` isn't needed if we just do individual fetches? No, batch is faster. - # But parsing batch response to stick into cache is annoying. - pass - - # For this version, let's keep fetch_batch simple and bypass cache. - return await email_fetch_batch(self.account, email_ids, parts) - - def clear(self) -> None: - """Clear the cache.""" - self._cache_rfc822.clear() - self._cache_text.clear() - self._cache_headers.clear() diff --git a/custom_components/mail_and_packages/utils/date.py b/custom_components/mail_and_packages/utils/date.py deleted file mode 100644 index 8446891..0000000 --- a/custom_components/mail_and_packages/utils/date.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Date and time helpers for Mail and Packages.""" - -import datetime - - -def get_today() -> datetime.date: - """Get today's date using system local timezone (Home Assistant's timezone). - - Returns date object using the system's local timezone. - """ - return datetime.date.today() - - -def get_formatted_date() -> str: - """Return today in specific format. - - Returns current timestamp as string - """ - return get_today().strftime("%d-%b-%Y") - - -async def update_time() -> datetime.datetime: - """Get update time. - - Returns current timestamp as datetime object. - """ - return datetime.datetime.now(datetime.UTC) diff --git a/custom_components/mail_and_packages/utils/email.py b/custom_components/mail_and_packages/utils/email.py deleted file mode 100644 index eb41f73..0000000 --- a/custom_components/mail_and_packages/utils/email.py +++ /dev/null @@ -1,201 +0,0 @@ -"""Email parsing and validation utilities for Mail and Packages.""" - -import email -import logging -import re -from typing import Any - -from aioimaplib import IMAP4_SSL -from voluptuous import Email, MultipleInvalid, Schema - -from custom_components.mail_and_packages.const import SENSOR_DATA -from custom_components.mail_and_packages.utils.cache import EmailCache - -from .imap import email_fetch - -_LOGGER = logging.getLogger(__name__) - - -def validate_email_address(email_address: str) -> bool: - """Validate the format of an email address. - - Args: - email_address (str): The email address to validate. - - Returns: - bool: `True` if the email address is valid, `False` otherwise. - - """ - try: - schema = Schema(Email()) # pylint: disable=no-value-for-parameter - schema(email_address) - except MultipleInvalid: - _LOGGER.error("'%s' does not look like a valid email address", email_address) - return False - - _LOGGER.debug("%s is a valid email address", email_address) - - return True - - -def generate_service_email_domains(amazon_fwds: list) -> set[str]: - """Generate a set of service email domains from amazon domains and SENSOR_DATA. - - Returns: - set[str]: A set of unique email domains. - - """ - domains = {fwd.split("@")[1] for fwd in amazon_fwds if "@" in fwd} - for sensor in SENSOR_DATA.values(): - for address in sensor.get("email", []): - if "@" not in address: - continue - domains.add(address.split("@")[1]) - return domains - - -def _match_patterns( - text: str, - patterns: list[re.Pattern], - body_count: bool, -) -> tuple[int, int | None]: - """Apply patterns to text and return occurrence count and extracted value. - - Returns: - tuple[int, int | None]: (count_of_matches, extracted_value) - - """ - local_count = 0 - extracted_value = None - - for pattern in patterns: - if body_count: - if (found := pattern.search(text)) and len(found.groups()) > 0: - _LOGGER.debug( - "Found (%s) in email result: %s", - pattern.pattern, - found.groups(), - ) - extracted_value = int(found.group(1)) - elif (found := pattern.findall(text)) and len(found) > 0: - _LOGGER.debug( - "Found (%s) in email %s times.", - pattern.pattern, - len(found), - ) - local_count += len(found) - - return local_count, extracted_value - - -async def _scan_email_for_text( - account: type[IMAP4_SSL], - email_id: str, - patterns: list[re.Pattern], - body_count: bool, - cache: EmailCache | None = None, -) -> tuple[int, int | None]: - """Scan a single email for terms. - - Returns: - tuple[int, int | None]: (total_matches, last_extracted_value) - - """ - total_matches = 0 - last_value = None - - if cache: - data = (await cache.fetch(email_id, "(RFC822)"))[1] - else: - data = (await email_fetch(account, email_id, "(RFC822)"))[1] - for response_part in data: - if not isinstance(response_part, (bytes, bytearray)): - continue - - msg = email.message_from_bytes(response_part) - - for part in msg.walk(): - if part.get_content_type() not in ["text/html", "text/plain"]: - continue - - email_msg = part.get_payload(decode=True) - try: - email_msg = email_msg.decode("utf-8", "ignore") - except (AttributeError, UnicodeError): - continue - - matches, value = _match_patterns(email_msg, patterns, body_count) - total_matches += matches - if value is not None: - last_value = value - - return total_matches, last_value - - -async def find_text( - sdata: Any, - account: type[IMAP4_SSL], - search_terms: list, - body_count: bool, - cache: EmailCache | None = None, -) -> int: - """Filter for specific words in email.""" - _LOGGER.debug("Searching for (%s) in (%s) emails", search_terms, len(sdata)) - mail_list = sdata[0].split() - count = 0 - - # Pre-compile regex patterns once - patterns = [re.compile(rf"{term}") for term in search_terms] - - for i in mail_list: - matches, value = await _scan_email_for_text( - account, i, patterns, body_count, cache - ) - - if body_count: - # If extracting a value, "last found value wins" (updates count) - if value is not None: - count = value - else: - # If counting occurrences, accumulate - count += matches - - return count - - -async def find_text_matches( - sdata: Any, - account: type[IMAP4_SSL], - search_terms: list, - body_count: bool, - cache: EmailCache | None = None, -) -> tuple[int, list[bytes]]: - """Filter for specific words in email and return matches and matching IDs.""" - _LOGGER.debug("Searching for (%s) in (%s) emails", search_terms, len(sdata)) - mail_list = sdata[0].split() - count = 0 - matching_ids = [] - - # Pre-compile regex patterns once - patterns = [re.compile(rf"{term}") for term in search_terms] - - for i in mail_list: - matches, value = await _scan_email_for_text( - account, i, patterns, body_count, cache - ) - - matched = False - if body_count: - # If extracting a value, "last found value wins" (updates count) - if value is not None: - count = value - matched = True - # If counting occurrences, accumulate - elif matches > 0: - count += matches - matched = True - - if matched: - matching_ids.append(i) - - return count, matching_ids diff --git a/custom_components/mail_and_packages/utils/image.py b/custom_components/mail_and_packages/utils/image.py deleted file mode 100644 index 54e3c3d..0000000 --- a/custom_components/mail_and_packages/utils/image.py +++ /dev/null @@ -1,489 +0,0 @@ -"""Image processing and management utilities for Mail and Packages.""" - -import contextlib -import datetime -import hashlib -import logging -import os -import subprocess # nosec -import uuid -from pathlib import Path -from shutil import copyfile, which - -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from PIL import Image, ImageOps - -from custom_components.mail_and_packages.const import ( - CONF_AMAZON_CUSTOM_IMG, - CONF_AMAZON_CUSTOM_IMG_FILE, - CONF_CUSTOM_IMG, - CONF_CUSTOM_IMG_FILE, - CONF_FEDEX_CUSTOM_IMG, - CONF_FEDEX_CUSTOM_IMG_FILE, - CONF_STORAGE, - CONF_UPS_CUSTOM_IMG, - CONF_UPS_CUSTOM_IMG_FILE, - CONF_WALMART_CUSTOM_IMG, - CONF_WALMART_CUSTOM_IMG_FILE, - DEFAULT_AMAZON_CUSTOM_IMG_FILE, - DEFAULT_CUSTOM_IMG_FILE, - DEFAULT_FEDEX_CUSTOM_IMG_FILE, - DEFAULT_UPS_CUSTOM_IMG_FILE, - DEFAULT_WALMART_CUSTOM_IMG_FILE, - OVERLAY, -) - -from .date import get_formatted_date - -_LOGGER = logging.getLogger(__name__) - - -async def _check_ffmpeg() -> bool: - """Check if ffmpeg is installed. - - Returns boolean - """ - return which("ffmpeg") - - -def default_image_path( - hass: HomeAssistant, # pylint: disable=unused-argument - config_entry: ConfigEntry, -) -> str: - """Return value of the default image path. - - Returns the default path based on logic - """ - storage = None - try: - storage = config_entry.options.get(CONF_STORAGE) or config_entry.data.get( - CONF_STORAGE - ) - except AttributeError: - with contextlib.suppress(AttributeError): - storage = config_entry.get(CONF_STORAGE) - - if storage: - return storage - return "custom_components/mail_and_packages/images/" - - -def hash_file(filename: str) -> str: - """Return the SHA-1 hash of the file passed into it. - - Returns hash of file as string - """ - # make a hash object - the_hash = hashlib.sha1() # nosec - - # open file for reading in binary mode - with Path(filename).open("rb") as file: - # loop till the end of the file - chunk = 0 - while chunk != b"": - # read only 1024 bytes at a time - chunk = file.read(1024) - the_hash.update(chunk) - - # return the hex representation of digest - return the_hash.hexdigest() - - -def _delete_file(file_path: Path, context: str) -> bool: - """Delete a single file with error handling.""" - try: - if file_path.exists(): - file_path.unlink() - _LOGGER.debug("%s - Successfully removed: %s", context, file_path) - return True - _LOGGER.debug("%s - File does not exist: %s", context, file_path) - except OSError as err: - _LOGGER.error("Error attempting to remove image in %s: %s", context, err) - return False - - -def _cleanup_directory(path: str) -> None: - """Handle directory-wide cleanup.""" - if not Path(path).is_dir(): - _LOGGER.debug("cleanup_images - Directory does not exist: %s", path) - return - - try: - files_before = [x.name for x in Path(path).iterdir()] - _LOGGER.debug( - "cleanup_images - Files in directory BEFORE cleanup: %s", - files_before, - ) - for file in files_before: - if file.endswith((".gif", ".mp4", ".jpg", ".png")): - full_path = Path(path) / file - _delete_file(full_path, "cleanup_images") - - files_after = [] - if Path(path).is_dir(): - files_after = [f.name for f in Path(path).iterdir()] - - _LOGGER.debug( - "cleanup_images - Files in directory AFTER cleanup: %s", - files_after, - ) - except FileNotFoundError: - _LOGGER.debug("cleanup_images - Directory removed during cleanup: %s", path) - except OSError as err: - _LOGGER.error("Error listing directory for cleanup: %s", err) - - -def cleanup_images(path: str, image: str | None = None) -> None: - """Clean up image storage directory. - - Only suppose to delete .gif, .mp4, and .jpg files - """ - _LOGGER.debug("=== cleanup_images CALLED === path: %s, image: %s", path, image) - - if isinstance(path, tuple): - path, image = path - - if image is not None: - full_path = Path(path) / image - _delete_file(full_path, "cleanup_images") - return - - _cleanup_directory(path) - - -def copy_overlays(path: str) -> None: - """Copy overlay images to image output path.""" - overlays = OVERLAY - check = all(item.name in overlays for item in Path(path).iterdir()) - - # Copy files if they are missing - if not check: - for file in overlays: - _LOGGER.debug("Copying file to: %s", path + file) - copyfile( - Path(__file__).parent.parent / file, - path + file, - ) - - -def resize_images(images: list, width: int, height: int) -> list: - """Resize images.""" - all_images = [] - for image_path in images: - try: - img_path = Path(image_path) - with img_path.open("rb") as fd_img: - img = Image.open(fd_img) - img.thumbnail((width, height), resample=Image.Resampling.LANCZOS) - img = ImageOps.pad( - img, - (width, height), - method=Image.Resampling.LANCZOS, - ) - img = img.crop((0, 0, width, height)) - new_image_path = img_path.parent / f"{img_path.stem}_resized.gif" - img.save(new_image_path, "GIF") - all_images.append(str(new_image_path)) - - except (OSError, ValueError) as err: - _LOGGER.error("Error processing image %s: %s", image_path, err) - continue - - return all_images - - -def random_filename(ext: str = ".jpg") -> str: - """Generate random filename.""" - return f"{uuid.uuid4()!s}{ext}" - - -def io_save_file(path: str | Path, data: bytes) -> None: - """Write bytes to a file synchronously (for use in executor).""" - with Path(path).open("wb") as the_file: - the_file.write(data) - - -def _generate_mp4(path: str, image_file: str) -> None: - """Generate mp4 from gif. - - use a subprocess so we don't lock up the thread - command: ffmpeg -f gif -i infile.gif outfile.mp4 - """ - base_path = Path(path) - gif_image = base_path / image_file - mp4_file = base_path / image_file.replace(".gif", ".mp4") - - filecheck = mp4_file.is_file() - - _LOGGER.debug("Generating mp4: %s", mp4_file) - if filecheck: - # Construct path string with trailing slash to ensure cleanup_images concatenates correctly - cleanup_images(str(mp4_file.parent) + "/", mp4_file.name) - _LOGGER.debug("Removing old mp4: %s", mp4_file) - - try: - cmd = [ - "ffmpeg", - "-y", - "-i", - str(gif_image), - "-pix_fmt", - "yuv420p", - str(mp4_file), - ] - subprocess.run( - cmd, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - check=True, - ) - except subprocess.CalledProcessError as err: - _LOGGER.error("FFmpeg failed to generate MP4: %s", err) - - -def generate_grid_img(path: str, image_file: str, count: int) -> None: - """Generate png grid from gif. - - use a subprocess so we don't lock up the thread - command: ffmpeg -f gif -i infile.gif outfile.mp4 - """ - count = max(count, 1) - if count % 2 == 0: - length = int(count / 2) - else: - length = int(count / 2) + count % 2 - - gif_image = Path(path + image_file) - png_file = image_file.replace(".gif", "_grid.png") - png_image = Path(path).joinpath(png_file) - - filecheck = png_image.is_file() - - _LOGGER.debug("Generating png image grid %s from %s", png_image, gif_image) - if filecheck: - # cleanup_images expects a tuple or string path, so we use string parts here - # or we could update cleanup_images to handle Path objects natively later. - cleanup_images(str(png_image.parent) + "/", png_image.name) - _LOGGER.debug("Removing old png grid: %s", png_image) - - try: - subprocess.run( - [ - "ffmpeg", - "-i", - str(gif_image), - "-r", - "0.20", - "-filter_complex", - f"tile=2x{length}:padding=10:color=black", - str(png_image), - ], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - check=True, - ) - except subprocess.CalledProcessError as err: - _LOGGER.error("FFmpeg failed to generate grid image: %s", err) - - -def generate_delivery_gif( - delivery_images: list, - gif_path: str, - duration: int = 3000, -) -> bool: - """Generate an animated GIF from delivery images. - - Args: - delivery_images: List of image file paths - gif_path: Path where the GIF should be saved - duration: Duration for each frame in milliseconds (default: 3000) - - Returns: - bool: True if GIF was created successfully, False otherwise - - """ - try: - # Open all images - corrected_images = [] - for img_path in delivery_images: - img = Image.open(img_path) - img = ImageOps.exif_transpose(img) # auto-rotates according to EXIF - corrected_images.append(img) - - # Create animated GIF - corrected_images[0].save( - gif_path, - format="GIF", - append_images=corrected_images[1:], - save_all=True, - duration=duration, - loop=0, # Infinite loop - ) - - _LOGGER.debug( - "Generated animated GIF with %d delivery images at %s", - len(delivery_images), - gif_path, - ) - - except (OSError, ValueError, Image.UnidentifiedImageError) as e: - _LOGGER.error("Error creating animated GIF: %s", e) - return False - else: - return True - - -def _get_courier_info( - hass: HomeAssistant, - config: ConfigEntry, - amazon: bool, - ups: bool, - walmart: bool, - fedex: bool, -) -> tuple[str, str]: - """Determine the path and default image for the active courier.""" - configs = [ - ( - amazon, - CONF_AMAZON_CUSTOM_IMG, - CONF_AMAZON_CUSTOM_IMG_FILE, - DEFAULT_AMAZON_CUSTOM_IMG_FILE, - "no_deliveries_amazon.jpg", - "amazon", - ), - ( - ups, - CONF_UPS_CUSTOM_IMG, - CONF_UPS_CUSTOM_IMG_FILE, - DEFAULT_UPS_CUSTOM_IMG_FILE, - "no_deliveries_ups.jpg", - "ups", - ), - ( - walmart, - CONF_WALMART_CUSTOM_IMG, - CONF_WALMART_CUSTOM_IMG_FILE, - DEFAULT_WALMART_CUSTOM_IMG_FILE, - "no_deliveries_walmart.jpg", - "walmart", - ), - ( - fedex, - CONF_FEDEX_CUSTOM_IMG, - CONF_FEDEX_CUSTOM_IMG_FILE, - DEFAULT_FEDEX_CUSTOM_IMG_FILE, - "no_deliveries_fedex.jpg", - "fedex", - ), - ] - - base_path = hass.config.path(default_image_path(hass, config)) - - for ( - active, - img_conf, - file_conf, - default_file_conf, - local_default, - sub_dir, - ) in configs: - if active: - _LOGGER.debug("Processing %s image file name", sub_dir.title()) - if config.get(img_conf): - mail_none = config.get(file_conf) or default_file_conf - _LOGGER.debug("Using custom %s image: %s", sub_dir.title(), mail_none) - else: - mail_none = str(Path(__file__).parent.parent / local_default) - _LOGGER.debug("Using default %s image: %s", sub_dir.title(), mail_none) - return f"{base_path}{sub_dir}", mail_none - - # Standard mail case - path = base_path.rstrip("/") - if config.get(CONF_CUSTOM_IMG): - mail_none = config.get(CONF_CUSTOM_IMG_FILE) or DEFAULT_CUSTOM_IMG_FILE - else: - mail_none = str(Path(__file__).parent.parent / "mail_none.gif") - return path, mail_none - - -def _get_image_name_from_directory( - path: str, mail_none: str, sha1: str, ext: str -) -> str: - """Check existing images and return a filename.""" - image_name = os.path.split(mail_none)[1] - today = get_formatted_date() - - try: - for file_path in Path(path).iterdir(): - if not file_path.is_file(): - continue - filename = file_path.name - if filename == "generic_deliveries.gif": - continue - is_image_file = filename.endswith(".gif") or ( - filename.endswith(".jpg") and ext == ".jpg" - ) - if is_image_file: - try: - created = datetime.datetime.fromtimestamp( - file_path.stat().st_ctime, - ).strftime("%d-%b-%Y") - # If it's the correct hash OR created today, we can reuse it - if sha1 == hash_file(str(file_path)) or today == created: - image_name = filename - break - image_name = f"{uuid.uuid4()!s}{ext}" - except OSError as err: - _LOGGER.error("Problem accessing file %s: %s", filename, err) - except OSError as err: - _LOGGER.error("Error accessing directory %s: %s", path, err) - - if image_name in mail_none: - image_name = f"{uuid.uuid4()!s}{ext}" - _LOGGER.debug("=== image_file_name GENERATED NEW UUID: %s ===", image_name) - else: - _LOGGER.debug("=== image_file_name USING EXISTING: %s ===", image_name) - - return image_name - - -def image_file_name( - hass: HomeAssistant, - config: ConfigEntry, - amazon: bool = False, - ups: bool = False, - walmart: bool = False, - fedex: bool = False, -) -> str: - """Determine if filename is to be changed or not. - - Returns filename - """ - _LOGGER.debug( - "=== image_file_name CALLED === - amazon: %s, ups: %s, walmart: %s, fedex: %s", - amazon, - ups, - walmart, - fedex, - ) - - path, mail_none = _get_courier_info(hass, config, amazon, ups, walmart, fedex) - image_name = os.path.split(mail_none)[1] - - # Path check - try: - Path(path).mkdir(parents=True, exist_ok=True) - except OSError as err: - _LOGGER.error("Error creating directory: %s", err) - return image_name - - # SHA1 file hash check - try: - sha1 = hash_file(mail_none) - except OSError as err: - _LOGGER.error("Problem accessing file: %s, error returned: %s", mail_none, err) - return image_name - - ext = ".jpg" if ups or walmart or fedex else ".gif" - return _get_image_name_from_directory(path, mail_none, sha1, ext) diff --git a/custom_components/mail_and_packages/utils/imap.py b/custom_components/mail_and_packages/utils/imap.py deleted file mode 100644 index 3d7e241..0000000 --- a/custom_components/mail_and_packages/utils/imap.py +++ /dev/null @@ -1,769 +0,0 @@ -"""IMAP connection and search utilities for Mail and Packages.""" - -import asyncio -import binascii -import logging -import re -import unicodedata -from urllib.parse import quote, unquote - -import aioimaplib -from aioimaplib import ( - AUTH, - IMAP4, - IMAP4_SSL, - NONAUTH, - SELECTED, - AioImapException, - Cmd, - Command, - Exec, -) -from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError -from homeassistant.util import ssl - -from custom_components.mail_and_packages.const import DEFAULT_IMAP_TIMEOUT - -_LOGGER = logging.getLogger(__name__) - -# Register ESEARCH command if not already present in aioimaplib -if "ESEARCH" not in aioimaplib.Commands: - aioimaplib.Commands["ESEARCH"] = Cmd("ESEARCH", (AUTH, SELECTED), Exec.is_async) - - -def encode_imap_utf7(s: str) -> str: - """Encode a string into IMAP modified UTF-7.""" - res = [] - unicode_buffer = [] - - def flush_unicode(): - if unicode_buffer: - u_str = "".join(unicode_buffer) - encoded_bytes = u_str.encode("utf-16be") - b64 = ( - binascii.b2a_base64(encoded_bytes) - .decode("ascii") - .rstrip("\n=") - .replace("/", ",") - ) - res.append(f"&{b64}-") - unicode_buffer.clear() - - for char in s: - ord_c = ord(char) - if 0x20 <= ord_c <= 0x7E: - if char == "&": - flush_unicode() - res.append("&-") - else: - if unicode_buffer: - flush_unicode() - res.append(char) - else: - unicode_buffer.append(char) - - flush_unicode() - return "".join(res) - - -def decode_imap_utf7(s: str) -> str: - """Decode a string from IMAP modified UTF-7.""" - res = [] - i = 0 - n = len(s) - while i < n: - char = s[i] - if char == "&": - end = s.find("-", i + 1) - if end == -1: - res.append("&") - i += 1 - elif end == i + 1: - res.append("&") - i += 2 - else: - b64_part = s[i + 1 : end] - b64_part = b64_part.replace(",", "/") - pad = len(b64_part) % 4 - if pad: - b64_part += "=" * (4 - pad) - try: - decoded_bytes = binascii.a2b_base64(b64_part) - res.append(decoded_bytes.decode("utf-16be")) - except (binascii.Error, UnicodeDecodeError, ValueError): - res.append(s[i : end + 1]) - i = end + 1 - else: - res.append(char) - i += 1 - - return "".join(res) - - -_ATOM_SPECIALS = frozenset('(){%*"\\] ') - - -def _is_imap_atom(s: str) -> bool: - """Check if the string is a valid IMAP atom.""" - return bool(s) and all(0x20 < ord(c) < 0x7F and c not in _ATOM_SPECIALS for c in s) - - -def quote_folder(folder: str) -> str: - """Ensure folder name is properly quoted for IMAP commands.""" - if folder.startswith('"') and folder.endswith('"'): - return folder - return folder if _is_imap_atom(folder) else f'"{folder}"' - - -def encode_folder_ref(folder: str) -> str: - """Percent-encode a folder name for use in a composite ``folder/uid`` ID. - - Multi-folder searches tag each UID with its source folder as - ``folder/uid``. Those composite IDs are space-joined and re-split on - whitespace at several call sites, and split on ``/`` to recover the - folder — so the folder component must contain neither whitespace nor - ``/``. A folder named ``# - Projects`` would otherwise shatter into - ``#``, ``-``, ``Projects/55`` when the joined ID list is ``.split()``. - ``quote(..., safe="")`` escapes both (and ``%`` itself, keeping the - round-trip lossless for any folder name). - """ - return quote(folder, safe="") - - -def decode_folder_ref(folder: str) -> str: - """Decode the percent-encoded folder component of a composite ID. - - Takes the already-split folder component (everything before the final - ``/`` of a ``folder/uid`` ID), not the full composite ID. - """ - return unquote(folder) - - -class InvalidAuth(HomeAssistantError): - """Raise exception for invalid credentials.""" - - -async def login( - hass: HomeAssistant, - host: str, - port: int, - user: str, - pwd: str, - security: str, - verify: bool = True, - oauth_token: str | None = None, - timeout: float = DEFAULT_IMAP_TIMEOUT, -) -> IMAP4_SSL | IMAP4: - """Login to IMAP server asynchronously. - - Supports both password and OAuth2 (XOAUTH2) authentication. - If oauth_token is provided, uses XOAUTH2 SASL mechanism. - Otherwise falls back to standard LOGIN command. - """ - ssl_context = ( - ssl.client_context(ssl.SSLCipherList.PYTHON_DEFAULT) - if verify - else ssl.create_no_verify_ssl_context() - ) - if security == "SSL": - account = IMAP4_SSL( - host=host, port=port, ssl_context=ssl_context, timeout=timeout - ) - else: - account = IMAP4(host=host, port=port, timeout=timeout) - - await account.wait_hello_from_server() - - if account.protocol.state == NONAUTH: - try: - if oauth_token: - try: - res = await asyncio.wait_for( - account.xoauth2(user, oauth_token), - timeout=min(timeout, 15.0), - ) - if account.protocol.state not in {AUTH, SELECTED}: - _LOGGER.error( - "OAuth login failed. Result: %s, Lines: %s", - getattr(res, "result", None), - getattr(res, "lines", None), - ) - except TimeoutError as err: - _LOGGER.error( - "OAuth authentication timed out for %s. This typically indicates invalid OAuth credentials, missing 'https://mail.google.com/' scope, or a mismatched email address.", - user, - ) - raise InvalidAuth( - "OAuth authentication timed out or failed" - ) from err - else: - await account.login(user, pwd) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error logging in to IMAP Server: %s", err) - raise InvalidAuth from err - - if account.protocol.state not in {AUTH, SELECTED}: - _LOGGER.error( - "Error logging in to IMAP Server. State: %s", account.protocol.state - ) - raise InvalidAuth - return account - - -async def selectfolder(account: IMAP4_SSL, folder: str) -> bool: - """Select folder inside the mailbox asynchronously.""" - if getattr(account, "_current_folder", None) == folder: - return True - - encoded_folder = encode_imap_utf7(folder) - quoted_folder = quote_folder(encoded_folder) - - try: - await account.select(quoted_folder) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error selecting folder %s: %s", folder, err) - return False - else: - account._current_folder = folder # noqa: SLF001 - return True - - -def clean_search_string(val: str) -> str: - """Clean search string for IMAP search compatibility. - - Normalizes Unicode characters to NFKD decomposed form, strips non-ASCII - characters to ensure compatibility with US-ASCII only IMAP servers, - and removes any double quotes to prevent syntax corruption. - """ - if not val: - return "" - normalized = unicodedata.normalize("NFKD", val) - cleaned = normalized.encode("ascii", "ignore").decode("ascii") - return cleaned.replace('"', "") - - -def build_search( # noqa: C901 - address: list, - date: str, - subject: str | list[str] = "", - body: str | list[str] = "", - header: str = "", - is_yahoo: bool = False, -) -> tuple: - """Build IMAP search query. - - Return tuple of utf8 flag and search query. - Non-ASCII characters are stripped from subject to ensure compatibility - with servers that only support US-ASCII charset (e.g. Microsoft Exchange). - IMAP SUBJECT performs substring matching, so stripping non-ASCII chars - still matches the original subject (e.g. 'Livr' matches 'Livré'). - - When `header` is provided, each address is matched as either a forwarded - email (via HEADER substring match) OR a direct email (via FROM), so the - same config works for carriers that are forwarded through a service like - SimpleLogin AND carriers whose emails arrive directly in the mailbox. - IMAP HEADER does substring matching, so "mcinfo@ups.com" will match a - header value of "UPS ". - """ - the_date = f"SINCE {date}" - - if not address: - raise ValueError("address list must not be empty") - - # Build the address/header clause - if header: - # Each address matches via header (forwarded) OR FROM (direct), so - # users with mixed setups (some carriers forwarded, others direct) - # don't need separate configurations. - parts = [f'OR HEADER "{header}" "{a}" FROM "{a}"' for a in address] - if len(parts) == 1: - addr_clause = f"({parts[0]})" if is_yahoo else parts[0] - else: - or_prefix = " ".join(["OR"] * (len(parts) - 1)) - addr_clause = ( - f"({or_prefix} {' '.join(parts)})" - if is_yahoo - else f"{or_prefix} {' '.join(parts)}" - ) - elif len(address) == 1: - addr_clause = f'FROM "{address[0]}"' - else: - joined = '" FROM "'.join(address) - or_prefix = " ".join(["OR"] * (len(address) - 1)) - addr_clause = ( - f'({or_prefix} FROM "{joined}")' - if is_yahoo - else f'{or_prefix} FROM "{joined}"' - ) - - # Handle multiple subjects - subject_part = "" - if subject: - subjects = [subject] if isinstance(subject, str) else subject - safe_subjects = [clean_search_string(s) for s in subjects] - safe_subjects = [s for s in safe_subjects if s] - - if len(safe_subjects) == 1: - subject_part = f'SUBJECT "{safe_subjects[0]}"' - elif len(safe_subjects) > 1: - subject_prefix = " ".join(["OR"] * (len(safe_subjects) - 1)) - subject_joined = '" SUBJECT "'.join(safe_subjects) - subject_part = ( - f'({subject_prefix} SUBJECT "{subject_joined}")' - if is_yahoo - else f'{subject_prefix} SUBJECT "{subject_joined}"' - ) - - # Handle multiple bodies - body_part = "" - if body: - bodies = [body] if isinstance(body, str) else body - safe_bodies = [clean_search_string(b) for b in bodies] - safe_bodies = [b for b in safe_bodies if b] - - if len(safe_bodies) == 1: - body_part = f'BODY "{safe_bodies[0]}"' - elif len(safe_bodies) > 1: - body_prefix = " ".join(["OR"] * (len(safe_bodies) - 1)) - body_joined = '" BODY "'.join(safe_bodies) - body_part = ( - f'({body_prefix} BODY "{body_joined}")' - if is_yahoo - else f'{body_prefix} BODY "{body_joined}"' - ) - - if is_yahoo: - if subject_part or body_part: - search_criteria = f"{subject_part} {body_part}".strip() - imap_search = f"({addr_clause} {search_criteria} {the_date})" - else: - imap_search = f"({addr_clause} {the_date})" - elif subject_part or body_part: - search_criteria = f"{subject_part} {body_part}".strip() - imap_search = f"{addr_clause} {search_criteria} {the_date}" - else: - imap_search = f"{addr_clause} {the_date}" - - _LOGGER.debug("DEBUG imap_search: %s", imap_search) - - return (False, imap_search) - - -def parse_search_response(lines: list[bytes]) -> list[bytes]: - """Parse IMAP SEARCH response lines and return list of UID/ID bytes. - - Handles both standard server responses (prefixed with b"SEARCH") - and mocked test inputs (which often contain raw UIDs directly). - Filters out the SEARCH keyword, tagged OK/status responses, - and any non-numeric tokens. - """ - uids = [] - for line in lines: - if not line: - continue - parts = line.split() - if not parts: - continue - - if parts[0] == b"SEARCH": - # Check if this is a search result line, e.g. b"SEARCH 1001 1002" - # (as opposed to b"SEARCH completed") - if len(parts) > 1 and parts[1].isdigit(): - uids.extend(parts[1:]) - # Check if this line is just a list of numeric UIDs (mock/test compatibility) - # and ignore status/existence responses like b"23 EXISTS" - elif all(p.isdigit() for p in parts): - uids.extend(parts) - - return uids - - -def _parse_esearch_line(line_bytes: bytes) -> list[bytes]: - """Parse a single ESEARCH line and return list of formatted UID bytes: b'folder/uid'.""" - line_str = line_bytes.decode("utf-8", "ignore") - - # Extract the correlator inside parentheses - start_paren = line_str.find("(") - end_paren = line_str.find(")", start_paren) if start_paren != -1 else -1 - if start_paren == -1 or end_paren == -1: - return [] - - correlator = line_str[start_paren + 1 : end_paren] - - # Extract mailbox name (could be quoted or unquoted) - mailbox_match = re.search(r'MAILBOX\s+"([^"]+)"', correlator) - if not mailbox_match: - mailbox_match = re.search(r"MAILBOX\s+(\S+)", correlator) - if not mailbox_match: - return [] - mailbox = mailbox_match.group(1) - mailbox = decode_imap_utf7(mailbox) - - # Extract the sequence set after 'UID ALL' anywhere in the line - seq_match = re.search(r"UID\s+ALL\s+(\S+)", line_str) - if not seq_match: - return [] - seq_set = seq_match.group(1) - - uids = [] - for part in seq_set.split(","): - part = part.strip() - if not part: - continue - if ":" in part: - try: - start_str, end_str = part.split(":", 1) - start, end = int(start_str), int(end_str) - if start <= end: - uids.extend(str(x) for x in range(start, end + 1)) - else: - uids.extend(str(x) for x in range(end, start + 1)) - except ValueError: - pass - else: - uids.append(part) - return [f"{encode_folder_ref(mailbox)}/{uid}".encode() for uid in uids] - - -async def _execute_single_search(account: IMAP4_SSL, search_query: str) -> list[bytes]: # noqa: C901 - """Execute search query. If single folder, use standard search. If multiple, use hybrid ESEARCH/fallback.""" - folders = getattr(account, "_folders", ["INBOX"]) - - if len(folders) <= 1: - res = await account.search(search_query, charset=None) - if res.result == "OK" and res.lines: - return parse_search_response(res.lines) - return [] - - all_uids = [] - - # Check for MULTISEARCH capability safely (handling mock/AsyncMock in tests) - is_multisearch = False - if hasattr(account, "has_capability"): - try: - res = account.has_capability("MULTISEARCH") - if asyncio.iscoroutine(res): - res.close() - is_multisearch = False - else: - is_multisearch = bool(res) - except Exception: # noqa: BLE001 - pass - - if is_multisearch: - # ESEARCH IN ("folder1" "folder2") query - encode and quote folders - folder_list = " ".join([quote_folder(encode_imap_utf7(f)) for f in folders]) - args = ("IN", f"({folder_list})", search_query) - try: - timeout = getattr(account, "timeout", None) - if not isinstance(timeout, (int, float)): - timeout = None - res = await account.protocol.execute( - Command( - "ESEARCH", - account.protocol.new_tag(), - *args, - loop=account.protocol.loop, - timeout=timeout, - ) - ) - if res.result == "OK": - for line in res.lines: - if line: - all_uids.extend(_parse_esearch_line(line)) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error executing ESEARCH: %s", err) - else: - # Sequential select and search fallback - no limits on configured folders - for folder in folders: - select_ok = await selectfolder(account, folder) - if not select_ok: - continue - try: - res = await account.uid_search(search_query, charset=None) - if res.result == "OK" and res.lines: - parsed = parse_search_response(res.lines) - all_uids.extend( - f"{encode_folder_ref(folder)}/{uid.decode()}".encode() - for uid in parsed - ) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error searching folder %s: %s", folder, err) - - return all_uids - - -async def email_search( # noqa: C901 - account: IMAP4_SSL, - address: list, - date: str, - subject: str | list[str] = "", - body: str | list[str] = "", - header: str = "", -) -> tuple: - """Search emails with from/header, subject, and date asynchronously. - - Always uses charset=None to avoid sending CHARSET in the IMAP SEARCH - command, ensuring compatibility with servers like Microsoft Exchange - that only support US-ASCII. - - When `header` is provided, searches via HEADER criterion instead of FROM, - matching the original sender in forwarding-service headers. - - If multiple subjects are provided, they are searched in batches of 10 - to keep the search query length safe. - """ - folders = getattr(account, "_folders", ["INBOX"]) - is_yahoo = False - if hasattr(account, "host") and isinstance(account.host, str): - host_lower = account.host.lower() - is_yahoo = "yahoo" in host_lower or "aol" in host_lower - - # If there are more than 2 body patterns, do not search them server-side - # to prevent slow query execution and timeouts on standard IMAP servers. - # Instead, we let the shipper's client-side text filtering handle it. - body_search = body - if body: - bodies = [body] if isinstance(body, str) else body - if len(bodies) > 2: - body_search = "" - - if len(folders) <= 1: - if not isinstance(subject, list) or len(subject) <= 10: - _unused, search = build_search( - address, date, subject, body_search, header, is_yahoo=is_yahoo - ) - try: - res = await account.search(search, charset=None) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error searching emails: %s", err) - return ("BAD", str(err)) - else: - parsed = parse_search_response(res.lines) - return (res.result, [b" ".join(parsed)]) - - # Batch subjects in groups of 10 - all_matched_ids = [] - for i in range(0, len(subject), 10): - batch = subject[i : i + 10] - _unused, search = build_search( - address, date, batch, body_search, header, is_yahoo=is_yahoo - ) - try: - res = await account.search(search, charset=None) - if res.result == "OK" and res.lines: - parsed = parse_search_response(res.lines) - all_matched_ids.extend(parsed) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error searching emails batch: %s", err) - - # Deduplicate and return in same format as individual search - unique_ids = list(dict.fromkeys(all_matched_ids)) - return ("OK", [b" ".join(unique_ids)]) - - # Multi-folder search logic - if not isinstance(subject, list) or len(subject) <= 10: - _unused, search = build_search( - address, date, subject, body_search, header, is_yahoo=is_yahoo - ) - try: - uids = await _execute_single_search(account, search) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error searching emails: %s", err) - return ("BAD", str(err)) - return ("OK", [b" ".join(uids)]) - - # Batch subjects in groups of 10 - all_matched_ids = [] - for i in range(0, len(subject), 10): - batch = subject[i : i + 10] - _unused, search = build_search( - address, date, batch, body_search, header, is_yahoo=is_yahoo - ) - try: - uids = await _execute_single_search(account, search) - all_matched_ids.extend(uids) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error searching emails batch: %s", err) - - # Deduplicate and return in same format as individual search - unique_ids = list(dict.fromkeys(all_matched_ids)) - return ("OK", [b" ".join(unique_ids)]) - - -async def email_fetch(account: IMAP4_SSL, num, parts: str = "(RFC822)") -> tuple: - """Download specified email for parsing asynchronously.""" - if account.host == "imap.mail.me.com": - parts = "BODY[]" - - num_str = num.decode() if isinstance(num, bytes) else str(num) - if "/" in num_str: - folder, num_str = num_str.rsplit("/", 1) - await selectfolder(account, decode_folder_ref(folder)) - try: - res = await account.uid("FETCH", num_str, parts) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error fetching email %s: %s", num_str, err) - return ("BAD", str(err)) - else: - return (res.result, res.lines) - - try: - res = await account.fetch(num_str, parts) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error fetching email %s: %s", num_str, err) - return ("BAD", str(err)) - else: - return (res.result, res.lines) - - -async def email_fetch_headers(account: IMAP4_SSL, num) -> tuple: - """Download only the subject header of an email asynchronously.""" - num_str = num.decode() if isinstance(num, bytes) else str(num) - if "/" in num_str: - folder, num_str = num_str.rsplit("/", 1) - await selectfolder(account, decode_folder_ref(folder)) - try: - res = await account.uid("FETCH", num_str, "(BODY[HEADER.FIELDS (SUBJECT)])") - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error fetching email headers %s: %s", num_str, err) - return ("BAD", str(err)) - else: - return (res.result, res.lines) - - try: - res = await account.fetch(num_str, "(BODY[HEADER.FIELDS (SUBJECT)])") - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error fetching email headers %s: %s", num_str, err) - return ("BAD", str(err)) - else: - return (res.result, res.lines) - - -async def email_fetch_text(account: IMAP4_SSL, num, parts: str = "(BODY[1])") -> tuple: - """Download the specific part of the email body asynchronously.""" - if account.host == "imap.mail.me.com": - parts = "BODY[]" - - num_str = num.decode() if isinstance(num, bytes) else str(num) - if "/" in num_str: - folder, num_str = num_str.rsplit("/", 1) - await selectfolder(account, decode_folder_ref(folder)) - try: - res = await account.uid("FETCH", num_str, parts) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error fetching email text %s: %s", num_str, err) - return ("BAD", str(err)) - else: - return (res.result, res.lines) - - try: - res = await account.fetch(num_str, parts) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error fetching email text %s: %s", num_str, err) - return ("BAD", str(err)) - else: - return (res.result, res.lines) - - -async def email_fetch_batch( # noqa: C901 - account: IMAP4_SSL, nums: list[str | bytes], parts: str = "(RFC822)" -) -> tuple: - """Download specified emails for parsing asynchronously in a batch.""" - if not nums: - return ("OK", []) - - if account.host == "imap.mail.me.com": - parts = "BODY[]" - - # Check if any ID contains a folder prefix - has_folder_prefix = False - for num in nums: - num_str = num.decode() if isinstance(num, bytes) else str(num) - if "/" in num_str: - has_folder_prefix = True - break - - if not has_folder_prefix: - num_strs = [ - num.decode() if isinstance(num, bytes) else str(num) for num in nums - ] - num_list_str = ",".join(num_strs) - try: - res = await account.fetch(num_list_str, parts) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error fetching emails batch %s: %s", num_list_str, err) - return ("BAD", str(err)) - else: - return (res.result, res.lines) - - # Group nums by their folder prefix - folder_to_nums = {} - for num in nums: - num_str = num.decode() if isinstance(num, bytes) else str(num) - if "/" in num_str: - folder, actual_num = num_str.rsplit("/", 1) - folder = decode_folder_ref(folder) - else: - folder, actual_num = None, num_str - folder_to_nums.setdefault(folder, []).append(actual_num) - - all_results = [] - overall_result = "OK" - - for folder, folder_nums in folder_to_nums.items(): - if folder is not None: - await selectfolder(account, folder) - - num_list_str = ",".join(folder_nums) - try: - res = await account.uid("FETCH", num_list_str, parts) - if res.result != "OK": - overall_result = res.result - all_results.extend(res.lines) - except TimeoutError: - raise - except (AioImapException, OSError) as err: - _LOGGER.error("Error fetching emails batch %s: %s", num_list_str, err) - return ("BAD", str(err)) - - return (overall_result, all_results) - - -async def logout(account: IMAP4_SSL | IMAP4) -> None: - """Logout from IMAP server asynchronously.""" - try: - await account.logout() - except (TimeoutError, AioImapException, OSError, asyncio.CancelledError) as err: - _LOGGER.debug("Error logging out of IMAP Server: %s", err) diff --git a/custom_components/mail_and_packages/utils/shipper.py b/custom_components/mail_and_packages/utils/shipper.py deleted file mode 100644 index 756a3c1..0000000 --- a/custom_components/mail_and_packages/utils/shipper.py +++ /dev/null @@ -1,310 +0,0 @@ -"""Shipper utility functions for Mail and Packages.""" - -from __future__ import annotations - -import base64 -import email -import logging -import re -from pathlib import Path -from typing import Any - -from aioimaplib import IMAP4_SSL - -from custom_components.mail_and_packages.utils.cache import EmailCache - -from .imap import email_fetch - -_LOGGER = logging.getLogger(__name__) - - -async def get_tracking( - sdata: Any, - account: type[IMAP4_SSL], - the_format: str | None = None, - cache: EmailCache | None = None, -) -> list: - """Parse tracking numbers from email. - - Returns list of tracking numbers - """ - tracking = [] - pattern = re.compile(rf"{the_format}") - mail_list = sdata.split() - _LOGGER.debug("Searching for tracking numbers in %s messages...", len(mail_list)) - - for i in mail_list: - if cache: - data = (await cache.fetch(i, "(RFC822)"))[1] - else: - data = (await email_fetch(account, i, "(RFC822)"))[1] - - for response_part in data: - if not isinstance(response_part, (bytes, bytearray)): - continue - - msg = email.message_from_bytes(response_part) - - # 1. Search subject - if found := _find_tracking_in_subject(msg, pattern): - if found not in tracking: - tracking.append(found) - continue - - # 2. Search body - if the_format == "1Z?[0-9A-Z]{16}": - found = _find_ups_tracking_in_raw(response_part, pattern) - else: - found = _find_tracking_in_body(msg, pattern, the_format) - - if found and found not in tracking: - tracking.append(found) - - return tracking - - -def _find_tracking_in_subject( - msg: email.message.Message, - pattern: re.Pattern, -) -> str | None: - """Find tracking number in email subject.""" - email_subject = msg["subject"] - if email_subject: - email_subject = str(email_subject) - if (found := pattern.findall(email_subject)) and len(found) > 0: - _LOGGER.debug("Found tracking number in email subject: %s", found[0]) - return found[0] - return None - - -def _find_ups_tracking_in_raw( - response_part: bytes | bytearray, - pattern: re.Pattern, -) -> str | None: - """UPS specific tracking search in raw email bytes.""" - try: - email_content = str(response_part, "utf-8", errors="ignore") - if (found := pattern.findall(email_content)) and len(found) > 0: - _LOGGER.debug("Found tracking number in email: %s", found[0]) - return found[0] - except (TypeError, UnicodeError) as err: - _LOGGER.debug("Error processing email content: %s", err) - return None - - -def _find_tracking_in_body( - msg: email.message.Message, - pattern: re.Pattern, - the_format: str, -) -> str | None: - """Search for tracking number in email body parts.""" - for part in msg.walk(): - if part.get_content_type() not in ["text/html", "text/plain"]: - continue - email_msg = part.get_payload(decode=True) - email_msg = email_msg.decode("utf-8", "ignore") - if (found := pattern.findall(email_msg)) and len(found) > 0: - tracking_num = found[0] - # DHL is special - if " " in the_format: - tracking_num = tracking_num.split(" ")[1] - - _LOGGER.debug("Found tracking number in email body: %s", tracking_num) - return tracking_num - return None - - -def save_image_data_to_disk( - shipper_name: str, path: str, image_data: bytes | None -) -> bool: - """Write image bytes to disk and verify.""" - if not image_data: - # Extraction can hand us zero bytes (e.g. a bare/empty base64 data URI - # in the email HTML). Writing that produces a 0-byte "photo" the - # camera then serves as a broken image — report failure instead so - # the caller falls through to the next extraction pass. - _LOGGER.debug( - "%s - No image data extracted; not writing %s", - shipper_name, - path, - ) - return False - try: - # Ensure directory exists - directory = Path(path).parent - if not directory.is_dir(): - _LOGGER.debug("%s - Creating directory: %s", shipper_name, directory) - try: - directory.mkdir(parents=True, exist_ok=True) - except OSError as err: - _LOGGER.error("Error creating directory: %s", err) - return False - - _LOGGER.debug( - "%s - Writing %d bytes to file: %s", - shipper_name, - len(image_data), - path, - ) - with Path(path).open("wb") as the_file: - the_file.write(image_data) - - except OSError as err: - _LOGGER.error( - "Error saving %s delivery photo to %s: %s", - shipper_name, - path, - err, - ) - return False - else: - if Path(path).exists(): - return True - - _LOGGER.error( - "%s - ERROR: File write reported success but file doesn't exist: %s", - shipper_name, - path, - ) - return False - - -def generic_delivery_image_extraction( - sdata: Any, - image_path: str, - image_name: str, - shipper_name: str, - image_type: str, - cid_name: str | None = None, - attachment_filename_pattern: str | None = None, -) -> bool: - """Extract delivery photos from email.""" - _LOGGER.debug("Attempting to extract %s delivery photo", shipper_name) - - msg = ( - email.message_from_bytes(sdata) - if isinstance(sdata, (bytes, bytearray)) - else email.message_from_string(sdata) - ) - - normalized_image_path = image_path.rstrip("/") + "/" - shipper_path = f"{normalized_image_path}{shipper_name}/" - full_path = shipper_path + image_name - - # Pass 1: CID search - if cid_name and ( - found := _extract_from_cid(msg, cid_name, shipper_name, full_path, image_type) - ): - return found - - # Pass 2: HTML/Base64 search - if found := _extract_from_html(msg, cid_name, shipper_name, full_path, image_type): - return found - - # Pass 3: Attachment search - return _extract_from_attachments( - msg, - attachment_filename_pattern, - shipper_name, - full_path, - image_type, - ) - - -def _extract_from_cid( - msg: email.message.Message, - cid_name: str, - shipper_name: str, - full_path: str, - image_type: str, -) -> bool: - """Pass 1: Look for CID embedded images.""" - content_type = f"image/{image_type}" - for part in msg.walk(): - if part.get_content_type() == content_type: - content_id = part.get("Content-ID") - if content_id and content_id.strip("<>") == cid_name: - return save_image_data_to_disk( - shipper_name, - full_path, - part.get_payload(decode=True), - ) - return False - - -def _extract_from_html( - msg: email.message.Message, - cid_name: str | None, - shipper_name: str, - full_path: str, - image_type: str, -) -> bool: - """Pass 2: Look for HTML content with CID references or base64.""" - base64_pattern = rf"data:image/{image_type};base64,((?:[A-Za-z0-9+/]{{4}})*(?:[A-Za-z0-9+/]{{2}}==|[A-Za-z0-9+/]{{3}}=)?)" - - for part in msg.walk(): - if part.get_content_type() != "text/html": - continue - - payload = part.get_payload(decode=True) - content = ( - payload.decode("utf-8", "ignore") - if isinstance(payload, bytes) - else str(payload) - ) - - # Base64 check. Skip empty matches: a bare "data:image/...;base64," - # URI (seen in real FedEx delivered emails) matches zero characters, - # and taking it would discard a real photo later in the same part. - matches = [m for m in re.findall(base64_pattern, content) if m] - if matches: - try: - base64_data = matches[0].replace(" ", "").replace("=3D", "=") - return save_image_data_to_disk( - shipper_name, - full_path, - base64.b64decode(base64_data), - ) - except (OSError, ValueError, TypeError) as err: - _LOGGER.error( - "Error saving %s delivery photo from base64: %s", - shipper_name, - err, - ) - return False - - return False - - -def _extract_from_attachments( - msg: email.message.Message, - pattern: str | None, - shipper_name: str, - full_path: str, - image_type: str, -) -> bool: - """Pass 3: Look for attachments.""" - content_type = f"image/{image_type}" - for part in msg.walk(): - if part.get_content_type() == content_type: - filename = part.get_filename() - if not filename: - continue - if pattern and pattern.lower() not in filename.lower(): - continue - - try: - return save_image_data_to_disk( - shipper_name, - full_path, - part.get_payload(decode=True), - ) - except (OSError, ValueError, TypeError) as err: - _LOGGER.error( - "Error saving %s delivery photo to %s: %s", - shipper_name, - full_path, - err, - ) - return False - return False diff --git a/custom_components/mail_and_packages/vignette.png b/custom_components/mail_and_packages/vignette.png deleted file mode 100644 index a5365ce05adeee6a3cc873db0c566ba979774846..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38527 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ΠklJ4rA-Y+UH3d8RD*{&s~bR@AT!UM=o%9 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 XHtp2D6#oC=Kl`j~EptCVc;f#6hRkuo diff --git a/custom_components/mail_and_packages/white.png b/custom_components/mail_and_packages/white.png deleted file mode 100644 index a12ccffbcb5a56f87278b7dac5f20ab0f8539517..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmeAS@N?(olHy`uVBq!ia0y~yV4A_ez-Yn93=}!aE%*~iF%}28J29*~C-V}>VM%xN zb!1@J*w6hZkrl{i4)6(a{r~^}@*~nGfLzW3kH}&M2EM}}%y>M1MG8>R(9^{+q~g}w qgNBSi-k}9w{d0Nw6@X0afbS|pS^@*(xs}c8AO)VTelF{r5}E*RI48^i