mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Add dhcp ip update support to onvif (#91474)
* Add dhcp ip update support to onvif If we know the mac address of the camera we can update the config entry when the ip changes * fix lookup * coverage * remove unreachable * remove unreachable * remove unreachable
This commit is contained in:
parent
d16e1b4ed0
commit
7f7909e0d1
@ -13,6 +13,7 @@ from wsdiscovery.service import Service
|
||||
from zeep.exceptions import Fault
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import dhcp
|
||||
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS
|
||||
from homeassistant.components.stream import (
|
||||
CONF_RTSP_TRANSPORT,
|
||||
@ -27,6 +28,8 @@ from homeassistant.const import (
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from .const import CONF_DEVICE_ID, DEFAULT_ARGUMENTS, DEFAULT_PORT, DOMAIN, LOGGER
|
||||
from .device import get_device
|
||||
@ -101,6 +104,30 @@ class OnvifFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
data_schema=vol.Schema({vol.Required("auto", default=True): bool}),
|
||||
)
|
||||
|
||||
async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult:
|
||||
"""Handle dhcp discovery."""
|
||||
hass = self.hass
|
||||
mac = discovery_info.macaddress
|
||||
registry = dr.async_get(self.hass)
|
||||
if not (
|
||||
device := registry.async_get_device(
|
||||
identifiers=set(), connections={(dr.CONNECTION_NETWORK_MAC, mac)}
|
||||
)
|
||||
):
|
||||
return self.async_abort(reason="no_devices_found")
|
||||
for entry_id in device.config_entries:
|
||||
if (
|
||||
not (entry := hass.config_entries.async_get_entry(entry_id))
|
||||
or entry.domain != DOMAIN
|
||||
or entry.state is config_entries.ConfigEntryState.LOADED
|
||||
):
|
||||
continue
|
||||
if hass.config_entries.async_update_entry(
|
||||
entry, data=entry.data | {CONF_HOST: discovery_info.ip}
|
||||
):
|
||||
hass.async_create_task(self.hass.config_entries.async_reload(entry_id))
|
||||
return self.async_abort(reason="already_configured")
|
||||
|
||||
async def async_step_device(self, user_input=None):
|
||||
"""Handle WS-Discovery.
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
"codeowners": ["@hunterjm"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["ffmpeg"],
|
||||
"dhcp": [{ "registered_devices": true }],
|
||||
"documentation": "https://www.home-assistant.io/integrations/onvif",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["onvif", "wsdiscovery", "zeep"],
|
||||
|
@ -2,6 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"onvif_error": "Error setting up ONVIF device. Check logs for more information.",
|
||||
"no_h264": "There were no H264 streams available. Check the profile configuration on your device.",
|
||||
|
@ -339,6 +339,10 @@ DHCP: list[dict[str, str | bool]] = [
|
||||
"hostname": "kohlergen*",
|
||||
"macaddress": "00146F*",
|
||||
},
|
||||
{
|
||||
"domain": "onvif",
|
||||
"registered_devices": True,
|
||||
},
|
||||
{
|
||||
"domain": "overkiz",
|
||||
"hostname": "gateway*",
|
||||
|
@ -6,7 +6,13 @@ from zeep.exceptions import Fault
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.onvif import config_flow
|
||||
from homeassistant.components.onvif.const import CONF_SNAPSHOT_AUTH
|
||||
from homeassistant.components.onvif.models import Capabilities, DeviceInfo, Profile
|
||||
from homeassistant.components.onvif.models import (
|
||||
Capabilities,
|
||||
DeviceInfo,
|
||||
Profile,
|
||||
Resolution,
|
||||
Video,
|
||||
)
|
||||
from homeassistant.const import HTTP_DIGEST_AUTHENTICATION
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
@ -100,7 +106,7 @@ def setup_mock_device(mock_device):
|
||||
index=0,
|
||||
token="dummy",
|
||||
name="profile1",
|
||||
video=None,
|
||||
video=Video("any", Resolution(640, 480)),
|
||||
ptz=None,
|
||||
video_source_token=None,
|
||||
)
|
||||
@ -120,7 +126,7 @@ async def setup_onvif_integration(
|
||||
unique_id=MAC,
|
||||
entry_id="1",
|
||||
source=config_entries.SOURCE_USER,
|
||||
):
|
||||
) -> tuple[MockConfigEntry, MagicMock, MagicMock]:
|
||||
"""Create an ONVIF config entry."""
|
||||
if not config:
|
||||
config = {
|
||||
|
@ -2,8 +2,13 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.onvif import config_flow
|
||||
from homeassistant.components import dhcp
|
||||
from homeassistant.components.onvif import DOMAIN, config_flow
|
||||
from homeassistant.config_entries import SOURCE_DHCP
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from . import (
|
||||
HOST,
|
||||
@ -34,6 +39,16 @@ DISCOVERY = [
|
||||
"MAC": "ee:dd:cc:bb:aa",
|
||||
},
|
||||
]
|
||||
DHCP_DISCOVERY = dhcp.DhcpServiceInfo(
|
||||
hostname="any",
|
||||
ip="5.6.7.8",
|
||||
macaddress=MAC,
|
||||
)
|
||||
DHCP_DISCOVERY_SAME_IP = dhcp.DhcpServiceInfo(
|
||||
hostname="any",
|
||||
ip="1.2.3.4",
|
||||
macaddress=MAC,
|
||||
)
|
||||
|
||||
|
||||
def setup_mock_discovery(
|
||||
@ -339,3 +354,88 @@ async def test_option_flow(hass: HomeAssistant) -> None:
|
||||
config_flow.CONF_RTSP_TRANSPORT: list(config_flow.RTSP_TRANSPORTS)[1],
|
||||
config_flow.CONF_USE_WALLCLOCK_AS_TIMESTAMPS: True,
|
||||
}
|
||||
|
||||
|
||||
async def test_discovered_by_dhcp_updates_host(hass: HomeAssistant) -> None:
|
||||
"""Test dhcp updates existing host."""
|
||||
config_entry, _camera, device = await setup_onvif_integration(hass)
|
||||
device.profiles = device.async_get_profiles()
|
||||
registry = dr.async_get(hass)
|
||||
devices = dr.async_entries_for_config_entry(registry, config_entry.entry_id)
|
||||
assert len(devices) == 1
|
||||
device = devices[0]
|
||||
assert device.model == "TestModel"
|
||||
assert device.connections == {(dr.CONNECTION_NETWORK_MAC, MAC)}
|
||||
assert config_entry.data[CONF_HOST] == "1.2.3.4"
|
||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_DHCP}, data=DHCP_DISCOVERY
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
assert config_entry.data[CONF_HOST] == DHCP_DISCOVERY.ip
|
||||
|
||||
|
||||
async def test_discovered_by_dhcp_does_nothing_if_host_is_the_same(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test dhcp update does nothing if host is the same."""
|
||||
config_entry, _camera, device = await setup_onvif_integration(hass)
|
||||
device.profiles = device.async_get_profiles()
|
||||
registry = dr.async_get(hass)
|
||||
devices = dr.async_entries_for_config_entry(registry, config_entry.entry_id)
|
||||
assert len(devices) == 1
|
||||
device = devices[0]
|
||||
assert device.model == "TestModel"
|
||||
assert device.connections == {(dr.CONNECTION_NETWORK_MAC, MAC)}
|
||||
assert config_entry.data[CONF_HOST] == DHCP_DISCOVERY_SAME_IP.ip
|
||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_DHCP}, data=DHCP_DISCOVERY_SAME_IP
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
assert config_entry.data[CONF_HOST] == DHCP_DISCOVERY_SAME_IP.ip
|
||||
|
||||
|
||||
async def test_discovered_by_dhcp_does_not_update_if_already_loaded(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test dhcp does not update existing host if its already loaded."""
|
||||
config_entry, _camera, device = await setup_onvif_integration(hass)
|
||||
device.profiles = device.async_get_profiles()
|
||||
registry = dr.async_get(hass)
|
||||
devices = dr.async_entries_for_config_entry(registry, config_entry.entry_id)
|
||||
assert len(devices) == 1
|
||||
device = devices[0]
|
||||
assert device.model == "TestModel"
|
||||
assert device.connections == {(dr.CONNECTION_NETWORK_MAC, MAC)}
|
||||
assert config_entry.data[CONF_HOST] == "1.2.3.4"
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_DHCP}, data=DHCP_DISCOVERY
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
assert config_entry.data[CONF_HOST] != DHCP_DISCOVERY.ip
|
||||
|
||||
|
||||
async def test_discovered_by_dhcp_does_not_update_if_no_matching_entry(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test dhcp does not update existing host if there are no matching entries."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_DHCP}, data=DHCP_DISCOVERY
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "no_devices_found"
|
||||
|
@ -63,7 +63,10 @@ async def test_diagnostics(
|
||||
"index": 0,
|
||||
"token": "dummy",
|
||||
"name": "profile1",
|
||||
"video": None,
|
||||
"video": {
|
||||
"encoding": "any",
|
||||
"resolution": {"width": 640, "height": 480},
|
||||
},
|
||||
"ptz": None,
|
||||
"video_source_token": None,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user