mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
Check webhook url is reachable in Reolink (#89585)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
parent
24d0d15f38
commit
e9925f6062
@ -54,7 +54,9 @@ class ReolinkHost:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.webhook_id: str | None = None
|
self.webhook_id: str | None = None
|
||||||
self._webhook_url: str | None = None
|
self._base_url: str = ""
|
||||||
|
self._webhook_url: str = ""
|
||||||
|
self._webhook_reachable: asyncio.Event = asyncio.Event()
|
||||||
self._lost_subscription: bool = False
|
self._lost_subscription: bool = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -138,6 +140,32 @@ class ReolinkHost:
|
|||||||
|
|
||||||
await self.subscribe()
|
await self.subscribe()
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Waiting for initial ONVIF state on webhook '%s'", self._webhook_url
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(self._webhook_reachable.wait(), timeout=15)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Did not receive initial ONVIF state on webhook '%s' after 15 seconds",
|
||||||
|
self._webhook_url,
|
||||||
|
)
|
||||||
|
ir.async_create_issue(
|
||||||
|
self._hass,
|
||||||
|
DOMAIN,
|
||||||
|
"webhook_url",
|
||||||
|
is_fixable=False,
|
||||||
|
severity=ir.IssueSeverity.WARNING,
|
||||||
|
translation_key="webhook_url",
|
||||||
|
translation_placeholders={
|
||||||
|
"name": self._api.nvr_name,
|
||||||
|
"base_url": self._base_url,
|
||||||
|
"network_link": "https://my.home-assistant.io/redirect/network/",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ir.async_delete_issue(self._hass, DOMAIN, "webhook_url")
|
||||||
|
|
||||||
if self._api.sw_version_update_required:
|
if self._api.sw_version_update_required:
|
||||||
ir.async_create_issue(
|
ir.async_create_issue(
|
||||||
self._hass,
|
self._hass,
|
||||||
@ -287,10 +315,10 @@ class ReolinkHost:
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
base_url = get_url(self._hass, prefer_external=False)
|
self._base_url = get_url(self._hass, prefer_external=False)
|
||||||
except NoURLAvailableError:
|
except NoURLAvailableError:
|
||||||
try:
|
try:
|
||||||
base_url = get_url(self._hass, prefer_external=True)
|
self._base_url = get_url(self._hass, prefer_external=True)
|
||||||
except NoURLAvailableError as err:
|
except NoURLAvailableError as err:
|
||||||
self.unregister_webhook()
|
self.unregister_webhook()
|
||||||
raise ReolinkWebhookException(
|
raise ReolinkWebhookException(
|
||||||
@ -299,9 +327,9 @@ class ReolinkHost:
|
|||||||
) from err
|
) from err
|
||||||
|
|
||||||
webhook_path = webhook.async_generate_path(event_id)
|
webhook_path = webhook.async_generate_path(event_id)
|
||||||
self._webhook_url = f"{base_url}{webhook_path}"
|
self._webhook_url = f"{self._base_url}{webhook_path}"
|
||||||
|
|
||||||
if base_url.startswith("https"):
|
if self._base_url.startswith("https"):
|
||||||
ir.async_create_issue(
|
ir.async_create_issue(
|
||||||
self._hass,
|
self._hass,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -310,7 +338,7 @@ class ReolinkHost:
|
|||||||
severity=ir.IssueSeverity.WARNING,
|
severity=ir.IssueSeverity.WARNING,
|
||||||
translation_key="https_webhook",
|
translation_key="https_webhook",
|
||||||
translation_placeholders={
|
translation_placeholders={
|
||||||
"base_url": base_url,
|
"base_url": self._base_url,
|
||||||
"network_link": "https://my.home-assistant.io/redirect/network/",
|
"network_link": "https://my.home-assistant.io/redirect/network/",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -337,6 +365,8 @@ class ReolinkHost:
|
|||||||
"""Handle incoming webhook from Reolink for inbound messages and calls."""
|
"""Handle incoming webhook from Reolink for inbound messages and calls."""
|
||||||
|
|
||||||
_LOGGER.debug("Webhook '%s' called", webhook_id)
|
_LOGGER.debug("Webhook '%s' called", webhook_id)
|
||||||
|
if not self._webhook_reachable.is_set():
|
||||||
|
self._webhook_reachable.set()
|
||||||
|
|
||||||
if not request.body_exists:
|
if not request.body_exists:
|
||||||
_LOGGER.debug("Webhook '%s' triggered without payload", webhook_id)
|
_LOGGER.debug("Webhook '%s' triggered without payload", webhook_id)
|
||||||
|
@ -41,7 +41,11 @@
|
|||||||
"issues": {
|
"issues": {
|
||||||
"https_webhook": {
|
"https_webhook": {
|
||||||
"title": "Reolink webhook URL uses HTTPS (SSL)",
|
"title": "Reolink webhook URL uses HTTPS (SSL)",
|
||||||
"description": "Reolink products can not push motion events to an HTTPS address (SSL), please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}). The current (local) address is: `{base_url}`"
|
"description": "Reolink products can not push motion events to an HTTPS address (SSL), please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}). The current (local) address is: `{base_url}`, a valid address could, for example, be `http://192.168.1.10:8123` where `192.168.1.10` is the IP of the Home Assistant device"
|
||||||
|
},
|
||||||
|
"webhook_url": {
|
||||||
|
"title": "Reolink webhook URL unreachable",
|
||||||
|
"description": "Did not receive initial ONVIF state from {name}. Most likely, the Reolink camera can not reach the current (local) Home Assistant URL `{base_url}`, please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}) that points to Home Assistant. For example `http://192.168.1.10:8123` where `192.168.1.10` is the IP of the Home Assistant device. Also, make sure the Reolink camera can reach that URL."
|
||||||
},
|
},
|
||||||
"enable_port": {
|
"enable_port": {
|
||||||
"title": "Reolink port not enabled",
|
"title": "Reolink port not enabled",
|
||||||
|
@ -39,6 +39,8 @@ def reolink_connect(mock_get_source_ip: None) -> Generator[MagicMock, None, None
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.reolink.host.webhook.async_register",
|
"homeassistant.components.reolink.host.webhook.async_register",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.reolink.host.asyncio.Event.wait", AsyncMock()
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.reolink.host.Host", autospec=True
|
"homeassistant.components.reolink.host.Host", autospec=True
|
||||||
) as host_mock_class:
|
) as host_mock_class:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Test the Reolink init."""
|
"""Test the Reolink init."""
|
||||||
|
import asyncio
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import AsyncMock, MagicMock, Mock
|
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from reolink_aio.exceptions import ReolinkError
|
from reolink_aio.exceptions import ReolinkError
|
||||||
@ -99,6 +100,7 @@ async def test_no_repair_issue(
|
|||||||
|
|
||||||
issue_registry = ir.async_get(hass)
|
issue_registry = ir.async_get(hass)
|
||||||
assert (const.DOMAIN, "https_webhook") not in issue_registry.issues
|
assert (const.DOMAIN, "https_webhook") not in issue_registry.issues
|
||||||
|
assert (const.DOMAIN, "webhook_url") not in issue_registry.issues
|
||||||
assert (const.DOMAIN, "enable_port") not in issue_registry.issues
|
assert (const.DOMAIN, "enable_port") not in issue_registry.issues
|
||||||
assert (const.DOMAIN, "firmware_update") not in issue_registry.issues
|
assert (const.DOMAIN, "firmware_update") not in issue_registry.issues
|
||||||
|
|
||||||
@ -138,6 +140,21 @@ async def test_port_repair_issue(
|
|||||||
assert (const.DOMAIN, "enable_port") in issue_registry.issues
|
assert (const.DOMAIN, "enable_port") in issue_registry.issues
|
||||||
|
|
||||||
|
|
||||||
|
async def test_webhook_repair_issue(
|
||||||
|
hass: HomeAssistant, config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test repairs issue is raised when the webhook url is unreachable."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.reolink.host.asyncio.Event.wait",
|
||||||
|
AsyncMock(side_effect=asyncio.TimeoutError()),
|
||||||
|
):
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
issue_registry = ir.async_get(hass)
|
||||||
|
assert (const.DOMAIN, "webhook_url") in issue_registry.issues
|
||||||
|
|
||||||
|
|
||||||
async def test_firmware_repair_issue(
|
async def test_firmware_repair_issue(
|
||||||
hass: HomeAssistant, config_entry: MockConfigEntry, reolink_connect: MagicMock
|
hass: HomeAssistant, config_entry: MockConfigEntry, reolink_connect: MagicMock
|
||||||
) -> None:
|
) -> None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user