diff --git a/custom_components/mail_and_packages/__init__.py b/custom_components/mail_and_packages/__init__.py index 94a0701a..afa25642 100644 --- a/custom_components/mail_and_packages/__init__.py +++ b/custom_components/mail_and_packages/__init__.py @@ -4,11 +4,21 @@ import async_timeout from homeassistant.const import CONF_HOST -from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed - -from . import const -from .helpers import process_emails, update_time +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import ( + CONF_AMAZON_FWDS, + CONF_IMAGE_SECURITY, + CONF_IMAP_TIMEOUT, + CONF_PATH, + CONF_SCAN_INTERVAL, + COORDINATOR, + DOMAIN, + ISSUE_URL, + PLATFORM, + VERSION, +) +from .helpers import process_emails _LOGGER = logging.getLogger(__name__) @@ -23,17 +33,34 @@ async def async_setup_entry(hass, config_entry): """Load the saved entities.""" _LOGGER.info( "Version %s is starting, if you have any issues please report" " them here: %s", - const.VERSION, - const.ISSUE_URL, + VERSION, + ISSUE_URL, ) - hass.data.setdefault(const.DOMAIN, {}) + hass.data.setdefault(DOMAIN, {}) + updated_config = config_entry.data.copy() + + # Set default image path + if CONF_PATH not in config_entry.data.keys(): + updated_config[CONF_PATH] = "www/mail_and_packages/" + # Set image security always on + if CONF_IMAGE_SECURITY not in config_entry.data.keys(): + updated_config[CONF_IMAGE_SECURITY] = True + + # Force path update + if config_entry.data[CONF_PATH] != "www/mail_and_packages/": + updated_config = config_entry.data.copy() + updated_config[CONF_PATH] = "www/mail_and_packages/" + + if updated_config != config_entry.data: + hass.config_entries.async_update_entry(config_entry, data=updated_config) + config_entry.options = config_entry.data config = config_entry.data async def async_update_data(): """Fetch data """ - async with async_timeout.timeout(config.get(const.CONF_IMAP_TIMEOUT)): + async with async_timeout.timeout(config.get(CONF_IMAP_TIMEOUT)): return await hass.async_add_executor_job(process_emails, hass, config) coordinator = DataUpdateCoordinator( @@ -41,21 +68,19 @@ async def async_update_data(): _LOGGER, name=f"Mail and Packages ({config.get(CONF_HOST)})", update_method=async_update_data, - update_interval=timedelta( - minutes=config_entry.options.get(const.CONF_SCAN_INTERVAL) - ), + update_interval=timedelta(minutes=config_entry.options.get(CONF_SCAN_INTERVAL)), ) # Fetch initial data so we have data when entities subscribe await coordinator.async_refresh() - hass.data[const.DOMAIN][config_entry.entry_id] = { - const.COORDINATOR: coordinator, + hass.data[DOMAIN][config_entry.entry_id] = { + COORDINATOR: coordinator, } config_entry.add_update_listener(update_listener) hass.async_create_task( - hass.config_entries.async_forward_entry_setup(config_entry, const.PLATFORM) + hass.config_entries.async_forward_entry_setup(config_entry, PLATFORM) ) return True @@ -64,12 +89,8 @@ async def async_update_data(): async def async_unload_entry(hass, config_entry): """Handle removal of an entry.""" try: - await hass.config_entries.async_forward_entry_unload( - config_entry, const.PLATFORM - ) - _LOGGER.info( - "Successfully removed sensor from the %s integration", const.DOMAIN - ) + await hass.config_entries.async_forward_entry_unload(config_entry, PLATFORM) + _LOGGER.info("Successfully removed sensor from the %s integration", DOMAIN) except ValueError: pass return True @@ -78,9 +99,9 @@ async def async_unload_entry(hass, config_entry): async def update_listener(hass, config_entry): """Update listener.""" config_entry.data = config_entry.options - await hass.config_entries.async_forward_entry_unload(config_entry, const.PLATFORM) + await hass.config_entries.async_forward_entry_unload(config_entry, PLATFORM) hass.async_add_job( - hass.config_entries.async_forward_entry_setup(config_entry, const.PLATFORM) + hass.config_entries.async_forward_entry_setup(config_entry, PLATFORM) ) @@ -93,27 +114,30 @@ async def async_migrate_entry(hass, config_entry): _LOGGER.debug("Migrating from version %s", version) data = config_entry.data.copy() - if const.CONF_AMAZON_FWDS in data.keys(): - if not isinstance(data[const.CONF_AMAZON_FWDS], list): - data[const.CONF_AMAZON_FWDS] = data[const.CONF_AMAZON_FWDS].split(",") + if CONF_AMAZON_FWDS in data.keys(): + if not isinstance(data[CONF_AMAZON_FWDS], list): + data[CONF_AMAZON_FWDS] = data[CONF_AMAZON_FWDS].split(",") else: - _LOGGER.warn("Missing configuration data: %s", const.CONF_AMAZON_FWDS) - - if const.CONF_IMAP_TIMEOUT not in data.keys(): - data[const.CONF_IMAP_TIMEOUT] = const.DEFAULT_IMAP_TIMEOUT + _LOGGER.warn("Missing configuration data: %s", CONF_AMAZON_FWDS) hass.config_entries.async_update_entry(config_entry, data=data) config_entry.version = 3 + _LOGGER.debug("Migration to version %s complete", config_entry.version) - # 2 -> 3: Add missing default - elif version == 2: + if version == 2: _LOGGER.debug("Migrating from version %s", version) - data = config_entry.data.copy() + updated_config = config_entry.data.copy() - if const.CONF_IMAP_TIMEOUT not in data.keys(): - data[const.CONF_IMAP_TIMEOUT] = const.DEFAULT_IMAP_TIMEOUT - hass.config_entries.async_update_entry(config_entry, data=data) - config_entry.version = 3 + # Force path change + updated_config[CONF_PATH] = "www/mail_and_packages/" - _LOGGER.debug("Migration to version %s complete", config_entry.version) + # Always on image security + if not config_entry.data[CONF_IMAGE_SECURITY]: + updated_config[CONF_IMAGE_SECURITY] = True + + if updated_config != config_entry.data: + hass.config_entries.async_update_entry(config_entry, data=updated_config) + + config_entry.version = 3 + _LOGGER.debug("Migration to version %s complete", config_entry.version) return True diff --git a/custom_components/mail_and_packages/config_flow.py b/custom_components/mail_and_packages/config_flow.py index 65a6937b..b37d5b17 100644 --- a/custom_components/mail_and_packages/config_flow.py +++ b/custom_components/mail_and_packages/config_flow.py @@ -1,7 +1,6 @@ """Adds config flow for Mail and Packages.""" import logging -from collections import OrderedDict import homeassistant.helpers.config_validation as cv import voluptuous as vol @@ -34,7 +33,7 @@ DEFAULT_SCAN_INTERVAL, DOMAIN, ) -from .helpers import _check_ffmpeg, _test_login, _validate_path, get_resources, login +from .helpers import _check_ffmpeg, _test_login, get_resources, login _LOGGER = logging.getLogger(__name__) @@ -111,13 +110,9 @@ def _get_default(key): vol.Optional( CONF_IMAP_TIMEOUT, default=_get_default(CONF_IMAP_TIMEOUT) ): vol.Coerce(int), - vol.Optional(CONF_PATH, default=_get_default(CONF_PATH)): str, vol.Optional( CONF_DURATION, default=_get_default(CONF_DURATION) ): vol.Coerce(int), - vol.Optional( - CONF_IMAGE_SECURITY, default=_get_default(CONF_IMAGE_SECURITY) - ): bool, vol.Optional( CONF_GENERATE_MP4, default=_get_default(CONF_GENERATE_MP4) ): bool, @@ -177,25 +172,17 @@ async def async_step_config_2(self, user_input=None): if user_input is not None: user_input[CONF_AMAZON_FWDS] = user_input[CONF_AMAZON_FWDS].split(",") self._data.update(user_input) - valid = await _validate_path(user_input[CONF_PATH]) + if user_input[CONF_GENERATE_MP4]: + valid = await _check_ffmpeg() + else: + valid = True + if valid: - if user_input[CONF_GENERATE_MP4]: - valid = await _check_ffmpeg() - else: - valid = True - - if valid: - if user_input[CONF_FOLDER] is not None: - if not user_input[CONF_PATH].endswith("/"): - user_input[CONF_PATH] += "/" - self._data.update(user_input) - return self.async_create_entry( - title=self._data[CONF_HOST], data=self._data - ) - else: - self._errors["base"] = "ffmpeg_not_found" + return self.async_create_entry( + title=self._data[CONF_HOST], data=self._data + ) else: - self._errors["base"] = "invalid_path" + self._errors["base"] = "ffmpeg_not_found" return await self._show_config_2(user_input) @@ -271,25 +258,16 @@ async def async_step_options_2(self, user_input=None): if user_input is not None: user_input[CONF_AMAZON_FWDS] = user_input[CONF_AMAZON_FWDS].split(",") self._data.update(user_input) - valid = await _validate_path(user_input[CONF_PATH]) + + if user_input[CONF_GENERATE_MP4]: + valid = await _check_ffmpeg() + else: + valid = True if valid: - if user_input[CONF_GENERATE_MP4]: - valid = await _check_ffmpeg() - else: - valid = True - - if valid: - if user_input[CONF_FOLDER] is not None: - if not user_input[CONF_PATH].endswith("/"): - user_input[CONF_PATH] += "/" - self._data.update(user_input) - - return self.async_create_entry(title="", data=self._data) - else: - self._errors["base"] = "ffmpeg_not_found" + return self.async_create_entry(title="", data=self._data) else: - self._errors["base"] = "invalid_path" + self._errors["base"] = "ffmpeg_not_found" return await self._show_step_options_2(user_input) diff --git a/custom_components/mail_and_packages/const.py b/custom_components/mail_and_packages/const.py index 188668e8..7281c8da 100644 --- a/custom_components/mail_and_packages/const.py +++ b/custom_components/mail_and_packages/const.py @@ -1,6 +1,6 @@ DOMAIN = "mail_and_packages" DOMAIN_DATA = "{}_data".format(DOMAIN) -VERSION = "0.3.0-b32" +VERSION = "0.3.0-b33" ISSUE_URL = "http://github.com/moralmunky/Home-Assistant-Mail-And-Packages" PLATFORM = "sensor" DATA = "data" @@ -270,6 +270,20 @@ ], } +# Name, unit of measure, icon +IMAGE_SENSORS = { + "usps_mail_image_system_path": [ + "Mail Image System Path", + None, + "mdi:folder-multiple-image", + ], + "usps_mail_image_url": [ + "Mail Image URL", + None, + "mdi:link-variant", + ], +} + # Sensor Index SENSOR_NAME = 0 SENSOR_UNIT = 1 diff --git a/custom_components/mail_and_packages/helpers.py b/custom_components/mail_and_packages/helpers.py index 3a38cbf8..851042d3 100644 --- a/custom_components/mail_and_packages/helpers.py +++ b/custom_components/mail_and_packages/helpers.py @@ -42,14 +42,6 @@ def get_resources(): return known_available_resources -async def _validate_path(path): - """ make sure path is valid """ - if os.path.exists(path): - return True - else: - return False - - async def _check_ffmpeg(): """ check if ffmpeg is installed """ if which("ffmpeg") is not None: @@ -86,7 +78,6 @@ def process_emails(hass, config): pwd = config.get(CONF_PASSWORD) folder = config.get(const.CONF_FOLDER) resources = config.get(CONF_RESOURCES) - image_security = config.get(const.CONF_IMAGE_SECURITY) # Login to email server and select the folder account = login(host, port, user, pwd) @@ -102,11 +93,7 @@ def process_emails(hass, config): # Create image file name dict container _image = {} - if image_security: - image_name = str(uuid.uuid4()) + ".gif" - else: - image_name = const.DEFAULT_GIF_FILE_NAME - + image_name = f"{str(uuid.uuid4())}.gif" _image[const.ATTR_IMAGE_NAME] = image_name data.update(_image) @@ -120,7 +107,7 @@ def process_emails(hass, config): def fetch(hass, config, account, data, sensor): """Fetch data for a single sensor, including any sensors it depends on.""" - img_out_path = config.get(const.CONF_PATH) + img_out_path = f"{hass.config.path()}/{config.get(const.CONF_PATH)}" gif_duration = config.get(const.CONF_DURATION) generate_mp4 = config.get(const.CONF_GENERATE_MP4) amazon_fwds = config.get(const.CONF_AMAZON_FWDS) @@ -440,6 +427,7 @@ def _generate_mp4(path, image_file): cleanup_images(os.path.split(mp4_file)) _LOGGER.debug("Removing old mp4: %s", mp4_file) + # TODO: find a way to call ffmpeg the right way from HA subprocess.call( [ "ffmpeg", diff --git a/custom_components/mail_and_packages/sensor.py b/custom_components/mail_and_packages/sensor.py index 3a27d3b9..6fd5bd6e 100644 --- a/custom_components/mail_and_packages/sensor.py +++ b/custom_components/mail_and_packages/sensor.py @@ -7,7 +7,6 @@ import logging from homeassistant.const import CONF_HOST, CONF_RESOURCES -from homeassistant.core import callback from homeassistant.helpers.entity import Entity from . import const @@ -24,6 +23,9 @@ async def async_setup_entry(hass, entry, async_add_entities): for variable in resources: sensors.append(PackagesSensor(entry, variable, coordinator, unique_id)) + for variable in const.IMAGE_SENSORS: + sensors.append(ImagePathSensors(hass, entry, variable, coordinator, unique_id)) + async_add_entities(sensors, False) @@ -55,9 +57,7 @@ def name(self): @property def state(self): """Return the state of the sensor.""" - value = self.coordinator.data[self.type] - self.data = self.coordinator.data - return value + return self.coordinator.data[self.type] @property def unit_of_measurement(self): @@ -106,3 +106,85 @@ async def async_added_to_hass(self): self.async_on_remove( self.coordinator.async_add_listener(self.async_write_ha_state) ) + + +class ImagePathSensors(Entity): + """ Represntation of a sensor """ + + def __init__(self, hass, config, sensor_type, coordinator, unique_id): + """ Initialize the sensor """ + self.hass = hass + self.coordinator = coordinator + self._config = config + self._name = const.IMAGE_SENSORS[sensor_type][const.SENSOR_NAME] + self._icon = const.IMAGE_SENSORS[sensor_type][const.SENSOR_ICON] + self._unit_of_measurement = const.IMAGE_SENSORS[sensor_type][const.SENSOR_UNIT] + self.type = sensor_type + self._host = config.data[CONF_HOST] + self._unique_id = unique_id + self._image = self.coordinator.data[const.ATTR_IMAGE_NAME] + + @property + def unique_id(self): + """Return a unique, Home Assistant friendly identifier for this entity.""" + return f"{self._host}_{self._name}_{self._unique_id}" + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + if self.type == "usps_mail_image_system_path": + return f"{self.hass.config.path()}/{self._config.data[const.CONF_PATH]}" + elif self.type == "usps_mail_image_url": + if self.hass.config.external_url is None: + _LOGGER.warn("External URL not set in configuration.") + return f"{self.hass.config.internal_url}/local/mail_and_packages/{self._image}" + return ( + f"{self.hass.config.external_url}/local/mail_and_packages/{self._image}" + ) + else: + return None + + @property + def unit_of_measurement(self): + """Return the unit of measurement of this entity, if any.""" + return self._unit_of_measurement + + @property + def icon(self): + """Return the unit of measurement.""" + return self._icon + + @property + def should_poll(self): + """No need to poll. Coordinator notifies entity of updates.""" + return False + + @property + def available(self): + """Return if entity is available.""" + return self.coordinator.last_update_success + + @property + def device_state_attributes(self): + """Return device specific state attributes.""" + attr = {} + + return attr + + async def async_update(self): + """Update the entity. + + Only used by the generic entity update service. + """ + await self.coordinator.async_request_refresh() + + async def async_added_to_hass(self): + """When entity is added to hass.""" + self.async_on_remove( + self.coordinator.async_add_listener(self.async_write_ha_state) + ) diff --git a/tests/const.py b/tests/const.py index ea47ac89..d8bb0c5e 100644 --- a/tests/const.py +++ b/tests/const.py @@ -7,7 +7,7 @@ "host": None, "image_name": "mail_today.gif", "image_path": "/config/www/mail_and_packages/", - "image_security": "true", + "image_security": False, "imap_timeout": 30, "password": "suchfakemuchpassword", "port": 993, @@ -42,7 +42,7 @@ "gif_duration": 5, "host": "imap.test.email", "image_name": "mail_today.gif", - "image_path": "/config/www/mail_and_packages/", + "image_path": "www/mail_and_packages/", "image_security": True, "imap_timeout": 30, "password": "suchfakemuchpassword", @@ -86,7 +86,7 @@ "gif_duration": 5, "host": "imap.test.email", "image_name": "mail_today.gif", - "image_path": "/config/www/mail_and_packages/", + "image_path": "www/mail_and_packages/", "image_security": False, "imap_timeout": 30, "password": "suchfakemuchpassword", diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index a558f5f1..1e3607bd 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -7,11 +7,7 @@ from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.mail_and_packages.const import DOMAIN -from custom_components.mail_and_packages.helpers import ( - _check_ffmpeg, - _test_login, - _validate_path, -) +from custom_components.mail_and_packages.helpers import _check_ffmpeg, _test_login from tests.const import FAKE_CONFIG_DATA @@ -31,8 +27,6 @@ "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -66,8 +60,6 @@ "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -114,9 +106,6 @@ async def test_form( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -173,9 +162,6 @@ async def test_form_connection_error(input_1, step_id_2, hass, mock_imap): with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=False, - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -210,8 +196,6 @@ async def test_form_connection_error(input_1, step_id_2, hass, mock_imap): "folder": '"INBOX"', "generate_mp4": True, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -245,8 +229,6 @@ async def test_form_connection_error(input_1, step_id_2, hass, mock_imap): "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -288,9 +270,6 @@ async def test_form_invalid_ffmpeg( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True, - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=False, @@ -316,128 +295,6 @@ async def test_form_invalid_ffmpeg( assert result3["errors"] == {"base": "ffmpeg_not_found"} -@pytest.mark.parametrize( - "input_1,step_id_2,input_2,title,data", - [ - ( - { - "host": "imap.test.email", - "port": "993", - "username": "test@test.email", - "password": "notarealpassword", - }, - "config_2", - { - "amazon_fwds": "", - "folder": '"INBOX"', - "generate_mp4": True, - "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, - "imap_timeout": 30, - "scan_interval": 20, - "resources": [ - "amazon_packages", - "fedex_delivered", - "fedex_delivering", - "fedex_packages", - "mail_updated", - "ups_delivered", - "ups_delivering", - "ups_packages", - "usps_delivered", - "usps_delivering", - "usps_mail", - "usps_packages", - "zpackages_delivered", - "zpackages_transit", - "dhl_delivered", - "dhl_delivering", - "dhl_packages", - "amazon_delivered", - ], - }, - "imap.test.email", - { - "amazon_fwds": "", - "host": "imap.test.email", - "port": 993, - "username": "test@test.email", - "password": "notarealpassword", - "folder": '"INBOX"', - "generate_mp4": False, - "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, - "imap_timeout": 30, - "scan_interval": 20, - "resources": [ - "amazon_packages", - "fedex_delivered", - "fedex_delivering", - "fedex_packages", - "mail_updated", - "ups_delivered", - "ups_delivering", - "ups_packages", - "usps_delivered", - "usps_delivering", - "usps_mail", - "usps_packages", - "zpackages_delivered", - "zpackages_transit", - "dhl_delivered", - "dhl_delivering", - "dhl_packages", - "amazon_delivered", - ], - }, - ), - ], -) -async def test_form_invalid_path( - input_1, step_id_2, input_2, title, data, hass, mock_imap -): - """Test we get the form.""" - await setup.async_setup_component(hass, "persistent_notification", {}) - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == "form" - assert result["errors"] == {} - # assert result["title"] == title_1 - - with patch( - "custom_components.mail_and_packages.config_flow._test_login", - return_value=True, - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=False, - ), patch( - "custom_components.mail_and_packages.config_flow._check_ffmpeg", - return_value=True, - ), patch( - "custom_components.mail_and_packages.async_setup", return_value=True - ) as mock_setup, patch( - "custom_components.mail_and_packages.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], input_1 - ) - assert result2["type"] == "form" - assert result2["step_id"] == step_id_2 - - result3 = await hass.config_entries.flow.async_configure( - result["flow_id"], input_2 - ) - - assert result3["type"] == "form" - assert result3["step_id"] == step_id_2 - assert result3["errors"] == {"base": "invalid_path"} - - @pytest.mark.parametrize( "input_1,step_id_2,input_2,title,data", [ @@ -454,8 +311,6 @@ async def test_form_invalid_path( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -489,8 +344,6 @@ async def test_form_invalid_path( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -537,9 +390,6 @@ async def test_form_index_error( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -585,8 +435,6 @@ async def test_form_index_error( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -620,8 +468,6 @@ async def test_form_index_error( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -668,9 +514,6 @@ async def test_form_index_error_2( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -716,8 +559,6 @@ async def test_form_index_error_2( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -751,8 +592,6 @@ async def test_form_index_error_2( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -799,9 +638,6 @@ async def test_form_mailbox_format2( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -831,16 +667,6 @@ async def test_form_mailbox_format2( assert len(mock_setup_entry.mock_calls) == 1 -async def test_valid_path(): - result = await _validate_path(os.path.dirname(__file__)) - assert result - - -async def test_invalid_path(): - result = await _validate_path("/should/fail") - assert not result - - async def test_valid_ffmpeg(test_valid_ffmpeg): result = await _check_ffmpeg() assert result @@ -887,8 +713,6 @@ async def test_imap_login_error(mock_imap_login_error, caplog): "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -922,8 +746,6 @@ async def test_imap_login_error(mock_imap_login_error, caplog): "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -977,9 +799,6 @@ async def test_options_flow( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -1044,9 +863,6 @@ async def test_options_flow_connection_error( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=False, - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -1081,8 +897,6 @@ async def test_options_flow_connection_error( "folder": '"INBOX"', "generate_mp4": True, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -1116,8 +930,6 @@ async def test_options_flow_connection_error( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -1171,9 +983,6 @@ async def test_options_flow_invalid_ffmpeg( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=False, @@ -1198,139 +1007,6 @@ async def test_options_flow_invalid_ffmpeg( assert result3["errors"] == {"base": "ffmpeg_not_found"} -@pytest.mark.parametrize( - "input_1,step_id_2,input_2,title,data", - [ - ( - { - "host": "imap.test.email", - "port": "993", - "username": "test@test.email", - "password": "notarealpassword", - }, - "options_2", - { - "amazon_fwds": "", - "folder": '"INBOX"', - "generate_mp4": True, - "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, - "imap_timeout": 30, - "scan_interval": 20, - "resources": [ - "amazon_packages", - "fedex_delivered", - "fedex_delivering", - "fedex_packages", - "mail_updated", - "ups_delivered", - "ups_delivering", - "ups_packages", - "usps_delivered", - "usps_delivering", - "usps_mail", - "usps_packages", - "zpackages_delivered", - "zpackages_transit", - "dhl_delivered", - "dhl_delivering", - "dhl_packages", - "amazon_delivered", - ], - }, - "imap.test.email", - { - "amazon_fwds": [""], - "host": "imap.test.email", - "port": 993, - "username": "test@test.email", - "password": "notarealpassword", - "folder": '"INBOX"', - "generate_mp4": False, - "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, - "imap_timeout": 30, - "scan_interval": 20, - "resources": [ - "amazon_packages", - "fedex_delivered", - "fedex_delivering", - "fedex_packages", - "mail_updated", - "ups_delivered", - "ups_delivering", - "ups_packages", - "usps_delivered", - "usps_delivering", - "usps_mail", - "usps_packages", - "zpackages_delivered", - "zpackages_transit", - "dhl_delivered", - "dhl_delivering", - "dhl_packages", - "amazon_delivered", - ], - }, - ), - ], -) -async def test_options_flow_invalid_path( - input_1, - step_id_2, - input_2, - title, - data, - hass, - mock_imap, -): - """Test config flow options.""" - entry = MockConfigEntry( - domain=DOMAIN, - title="imap.test.email", - data=FAKE_CONFIG_DATA, - ) - - entry.add_to_hass(hass) - - await setup.async_setup_component(hass, "persistent_notification", {}) - result = await hass.config_entries.options.async_init(entry.entry_id) - - assert result["type"] == "form" - assert result["errors"] == {} - # assert result["title"] == title_1 - - with patch( - "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=False, - ), patch( - "custom_components.mail_and_packages.config_flow._check_ffmpeg", - return_value=True, - ), patch( - "custom_components.mail_and_packages.async_setup", return_value=True - ) as mock_setup, patch( - "custom_components.mail_and_packages.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - - result2 = await hass.config_entries.options.async_configure( - result["flow_id"], input_1 - ) - assert result2["type"] == "form" - assert result2["step_id"] == step_id_2 - - result3 = await hass.config_entries.options.async_configure( - result["flow_id"], input_2 - ) - - assert result3["type"] == "form" - assert result3["errors"] == {"base": "invalid_path"} - - @pytest.mark.parametrize( "input_1,step_id_2,input_2,title,data", [ @@ -1347,8 +1023,6 @@ async def test_options_flow_invalid_path( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -1382,8 +1056,6 @@ async def test_options_flow_invalid_path( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -1437,9 +1109,6 @@ async def test_options_flow_index_error( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -1481,8 +1150,6 @@ async def test_options_flow_index_error( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -1516,8 +1183,6 @@ async def test_options_flow_index_error( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -1571,9 +1236,6 @@ async def test_options_flow_index_error_2( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, @@ -1615,8 +1277,6 @@ async def test_options_flow_index_error_2( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -1650,8 +1310,6 @@ async def test_options_flow_index_error_2( "folder": '"INBOX"', "generate_mp4": False, "gif_duration": 5, - "image_path": "/config/www/mail_and_packages/", - "image_security": True, "imap_timeout": 30, "scan_interval": 20, "resources": [ @@ -1705,9 +1363,6 @@ async def test_options_flow_mailbox_format2( with patch( "custom_components.mail_and_packages.config_flow._test_login", return_value=True - ), patch( - "custom_components.mail_and_packages.config_flow._validate_path", - return_value=True, ), patch( "custom_components.mail_and_packages.config_flow._check_ffmpeg", return_value=True, diff --git a/tests/test_helpers.py b/tests/test_helpers.py index d35bbfcf..c8378a93 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -29,7 +29,7 @@ from tests.const import FAKE_CONFIG_DATA, FAKE_CONFIG_DATA_BAD, FAKE_CONFIG_DATA_NO_RND -async def test_unload_entry(hass, mock_update): +async def test_unload_entry(hass, mock_update, mock_copy_overlays): """Test unloading entities. """ entry = MockConfigEntry( domain=DOMAIN, @@ -41,7 +41,7 @@ async def test_unload_entry(hass, mock_update): assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 22 + assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 24 entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 @@ -57,6 +57,7 @@ async def test_setup_entry( mock_osmakedir, mock_listdir, mock_update_time, + mock_copy_overlays, ): """Test settting up entities. """ entry = MockConfigEntry( @@ -69,7 +70,7 @@ async def test_setup_entry( assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 26 + assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 28 entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 @@ -119,48 +120,24 @@ async def test_process_emails( entry = MockConfigEntry( domain=DOMAIN, title="imap.test.email", - data=FAKE_CONFIG_DATA_NO_RND, + data=FAKE_CONFIG_DATA, ) entry.add_to_hass(hass) assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - config = entry.data - assert config == FAKE_CONFIG_DATA_NO_RND + config = entry.data.copy() + assert config == FAKE_CONFIG_DATA result = process_emails(hass, config) - assert result == { - "amazon_delivered": 0, - "amazon_hub": 0, - "amazon_hub_code": [], - "amazon_order": [], - "amazon_packages": 0, - "capost_delivered": 0, - "capost_delivering": 0, - "capost_packages": 0, - "capost_tracking": "", - "dhl_delivered": 0, - "dhl_delivering": 0, - "dhl_packages": 0, - "dhl_tracking": [], - "fedex_delivered": 0, - "fedex_delivering": 0, - "fedex_packages": 0, - "fedex_tracking": [], - "image_name": "mail_today.gif", - "mail_updated": "Sep-23-2020 10:28 AM", - "ups_delivered": 0, - "ups_delivering": 0, - "ups_packages": 0, - "ups_tracking": [], - "usps_delivered": 0, - "usps_delivering": 0, - "usps_mail": 0, - "usps_packages": 0, - "usps_tracking": [], - "zpackages_delivered": 0, - "zpackages_transit": 0, - } + assert result["mail_updated"] == "Sep-23-2020 10:28 AM" + assert result["zpackages_delivered"] == 0 + assert result["zpackages_transit"] == 0 + assert result["amazon_delivered"] == 0 + assert result["amazon_hub"] == 0 + assert result["amazon_packages"] == 0 + assert result["amazon_order"] == [] + assert result["amazon_hub_code"] == [] async def test_process_emails_bad(hass, mock_imap_no_email):