From 75726a26954cf2527c9fa28b9de2200232d255d5 Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sat, 7 Aug 2021 21:29:52 -0700 Subject: [PATCH] Don't block motionEye setup on NoURLAvailableError (#54225) Co-authored-by: Paulus Schoutsen --- .../components/motioneye/__init__.py | 68 +++++++++++-------- tests/components/motioneye/test_web_hooks.py | 31 +++++++++ 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/motioneye/__init__.py b/homeassistant/components/motioneye/__init__.py index 2ade7c48e1b..acafdceeb05 100644 --- a/homeassistant/components/motioneye/__init__.py +++ b/homeassistant/components/motioneye/__init__.py @@ -53,7 +53,7 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_send, ) from homeassistant.helpers.entity import DeviceInfo, EntityDescription -from homeassistant.helpers.network import get_url +from homeassistant.helpers.network import NoURLAvailableError, get_url from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, @@ -145,12 +145,21 @@ def listen_for_new_cameras( @callback -def async_generate_motioneye_webhook(hass: HomeAssistant, webhook_id: str) -> str: +def async_generate_motioneye_webhook( + hass: HomeAssistant, webhook_id: str +) -> str | None: """Generate the full local URL for a webhook_id.""" - return "{}{}".format( - get_url(hass, allow_cloud=False), - async_generate_path(webhook_id), - ) + try: + return "{}{}".format( + get_url(hass, allow_cloud=False), + async_generate_path(webhook_id), + ) + except NoURLAvailableError: + _LOGGER.warning( + "Unable to get Home Assistant URL. Have you set the internal and/or " + "external URLs in Configuration -> General?" + ) + return None @callback @@ -228,28 +237,31 @@ def _add_camera( if entry.options.get(CONF_WEBHOOK_SET, DEFAULT_WEBHOOK_SET): url = async_generate_motioneye_webhook(hass, entry.data[CONF_WEBHOOK_ID]) - if _set_webhook( - _build_url( - device, - url, - EVENT_MOTION_DETECTED, - EVENT_MOTION_DETECTED_KEYS, - ), - KEY_WEB_HOOK_NOTIFICATIONS_URL, - KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD, - KEY_WEB_HOOK_NOTIFICATIONS_ENABLED, - camera, - ) | _set_webhook( - _build_url( - device, - url, - EVENT_FILE_STORED, - EVENT_FILE_STORED_KEYS, - ), - KEY_WEB_HOOK_STORAGE_URL, - KEY_WEB_HOOK_STORAGE_HTTP_METHOD, - KEY_WEB_HOOK_STORAGE_ENABLED, - camera, + if url and ( + _set_webhook( + _build_url( + device, + url, + EVENT_MOTION_DETECTED, + EVENT_MOTION_DETECTED_KEYS, + ), + KEY_WEB_HOOK_NOTIFICATIONS_URL, + KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD, + KEY_WEB_HOOK_NOTIFICATIONS_ENABLED, + camera, + ) + | _set_webhook( + _build_url( + device, + url, + EVENT_FILE_STORED, + EVENT_FILE_STORED_KEYS, + ), + KEY_WEB_HOOK_STORAGE_URL, + KEY_WEB_HOOK_STORAGE_HTTP_METHOD, + KEY_WEB_HOOK_STORAGE_ENABLED, + camera, + ) ): hass.async_create_task(client.async_set_camera(camera_id, camera)) diff --git a/tests/components/motioneye/test_web_hooks.py b/tests/components/motioneye/test_web_hooks.py index 03b4e8bc46a..f20ef5101e9 100644 --- a/tests/components/motioneye/test_web_hooks.py +++ b/tests/components/motioneye/test_web_hooks.py @@ -32,11 +32,13 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.network import NoURLAvailableError from homeassistant.setup import async_setup_component from . import ( TEST_CAMERA, TEST_CAMERA_DEVICE_IDENTIFIER, + TEST_CAMERA_ENTITY_ID, TEST_CAMERA_ID, TEST_CAMERA_NAME, TEST_CAMERAS, @@ -251,6 +253,35 @@ async def test_setup_camera_with_correct_webhook( assert not client.async_set_camera.called +async def test_setup_camera_with_no_home_assistant_urls( + hass: HomeAssistant, + caplog: Any, +) -> None: + """Verify setup works without Home Assistant internal/external URLs.""" + + client = create_mock_motioneye_client() + config_entry = create_mock_motioneye_config_entry(hass, data={CONF_URL: TEST_URL}) + + with patch( + "homeassistant.components.motioneye.get_url", side_effect=NoURLAvailableError + ): + await setup_mock_motioneye_config_entry( + hass, + config_entry=config_entry, + client=client, + ) + + # Should log a warning ... + assert "Unable to get Home Assistant URL" in caplog.text + + # ... should not set callbacks in the camera ... + assert not client.async_set_camera.called + + # ... but camera should still be present. + entity_state = hass.states.get(TEST_CAMERA_ENTITY_ID) + assert entity_state + + async def test_good_query(hass: HomeAssistant, aiohttp_client: Any) -> None: """Test good callbacks.""" await async_setup_component(hass, "http", {"http": {}})