Only update unifiprotect ips from discovery when the console is offline (#73411)

pull/73419/head
J. Nick Koston 2022-06-12 17:04:17 -10:00 committed by GitHub
parent 9ae713f128
commit 9159db4b4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 5 deletions

View File

@ -8,6 +8,7 @@ from typing import Any
from aiohttp import CookieJar from aiohttp import CookieJar
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
from pyunifiprotect.data import NVR from pyunifiprotect.data import NVR
from unifi_discovery import async_console_is_alive
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
@ -22,7 +23,10 @@ from homeassistant.const import (
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.helpers.aiohttp_client import (
async_create_clientsession,
async_get_clientsession,
)
from homeassistant.helpers.typing import DiscoveryInfoType from homeassistant.helpers.typing import DiscoveryInfoType
from homeassistant.loader import async_get_integration from homeassistant.loader import async_get_integration
from homeassistant.util.network import is_ip_address from homeassistant.util.network import is_ip_address
@ -37,11 +41,17 @@ from .const import (
MIN_REQUIRED_PROTECT_V, MIN_REQUIRED_PROTECT_V,
OUTDATED_LOG_MESSAGE, OUTDATED_LOG_MESSAGE,
) )
from .data import async_last_update_was_successful
from .discovery import async_start_discovery from .discovery import async_start_discovery
from .utils import _async_resolve, _async_short_mac, _async_unifi_mac_from_hass from .utils import _async_resolve, _async_short_mac, _async_unifi_mac_from_hass
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ENTRY_FAILURE_STATES = (
config_entries.ConfigEntryState.SETUP_ERROR,
config_entries.ConfigEntryState.SETUP_RETRY,
)
async def async_local_user_documentation_url(hass: HomeAssistant) -> str: async def async_local_user_documentation_url(hass: HomeAssistant) -> str:
"""Get the documentation url for creating a local user.""" """Get the documentation url for creating a local user."""
@ -54,6 +64,25 @@ def _host_is_direct_connect(host: str) -> bool:
return host.endswith(".ui.direct") return host.endswith(".ui.direct")
async def _async_console_is_offline(
hass: HomeAssistant,
entry: config_entries.ConfigEntry,
) -> bool:
"""Check if a console is offline.
We define offline by the config entry
is in a failure/retry state or the updates
are failing and the console is unreachable
since protect may be updating.
"""
return bool(
entry.state in ENTRY_FAILURE_STATES
or not async_last_update_was_successful(hass, entry)
) and not await async_console_is_alive(
async_get_clientsession(hass, verify_ssl=False), entry.data[CONF_HOST]
)
class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a UniFi Protect config flow.""" """Handle a UniFi Protect config flow."""
@ -111,6 +140,7 @@ class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
not entry_has_direct_connect not entry_has_direct_connect
and is_ip_address(entry_host) and is_ip_address(entry_host)
and entry_host != source_ip and entry_host != source_ip
and await _async_console_is_offline(self.hass, entry)
): ):
new_host = source_ip new_host = source_ip
if new_host: if new_host:

View File

@ -26,6 +26,16 @@ from .utils import async_get_adoptable_devices_by_type, async_get_devices
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@callback
def async_last_update_was_successful(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Check if the last update was successful for a config entry."""
return bool(
DOMAIN in hass.data
and entry.entry_id in hass.data[DOMAIN]
and hass.data[DOMAIN][entry.entry_id].last_update_success
)
class ProtectData: class ProtectData:
"""Coordinate updates.""" """Coordinate updates."""

View File

@ -3,7 +3,7 @@
"name": "UniFi Protect", "name": "UniFi Protect",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/unifiprotect", "documentation": "https://www.home-assistant.io/integrations/unifiprotect",
"requirements": ["pyunifiprotect==3.9.2", "unifi-discovery==1.1.3"], "requirements": ["pyunifiprotect==3.9.2", "unifi-discovery==1.1.4"],
"dependencies": ["http"], "dependencies": ["http"],
"codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"],
"quality_scale": "platinum", "quality_scale": "platinum",

View File

@ -2355,7 +2355,7 @@ twitchAPI==2.5.2
uasiren==0.0.1 uasiren==0.0.1
# homeassistant.components.unifiprotect # homeassistant.components.unifiprotect
unifi-discovery==1.1.3 unifi-discovery==1.1.4
# homeassistant.components.unifiled # homeassistant.components.unifiled
unifiled==0.11 unifiled==0.11

View File

@ -1552,7 +1552,7 @@ twitchAPI==2.5.2
uasiren==0.0.1 uasiren==0.0.1
# homeassistant.components.unifiprotect # homeassistant.components.unifiprotect
unifi-discovery==1.1.3 unifi-discovery==1.1.4
# homeassistant.components.upb # homeassistant.components.upb
upb_lib==0.4.12 upb_lib==0.4.12

View File

@ -402,7 +402,10 @@ async def test_discovered_by_unifi_discovery_direct_connect_updated_but_not_usin
) )
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with _patch_discovery(): with _patch_discovery(), patch(
"homeassistant.components.unifiprotect.config_flow.async_console_is_alive",
return_value=False,
):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY}, context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY},
@ -415,6 +418,41 @@ async def test_discovered_by_unifi_discovery_direct_connect_updated_but_not_usin
assert mock_config.data[CONF_HOST] == "127.0.0.1" assert mock_config.data[CONF_HOST] == "127.0.0.1"
async def test_discovered_by_unifi_discovery_does_not_update_ip_when_console_is_still_online(
hass: HomeAssistant, mock_nvr: NVR
) -> None:
"""Test a discovery from unifi-discovery does not update the ip unless the console at the old ip is offline."""
mock_config = MockConfigEntry(
domain=DOMAIN,
data={
"host": "1.2.2.2",
"username": "test-username",
"password": "test-password",
"id": "UnifiProtect",
"port": 443,
"verify_ssl": False,
},
version=2,
unique_id=DEVICE_MAC_ADDRESS.replace(":", "").upper(),
)
mock_config.add_to_hass(hass)
with _patch_discovery(), patch(
"homeassistant.components.unifiprotect.config_flow.async_console_is_alive",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY},
data=UNIFI_DISCOVERY_DICT,
)
await hass.async_block_till_done()
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
assert mock_config.data[CONF_HOST] == "1.2.2.2"
async def test_discovered_host_not_updated_if_existing_is_a_hostname( async def test_discovered_host_not_updated_if_existing_is_a_hostname(
hass: HomeAssistant, mock_nvr: NVR hass: HomeAssistant, mock_nvr: NVR
) -> None: ) -> None: