Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve UniFi Protect unauth handling #75269

Merged
merged 1 commit into from
Jul 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 9 additions & 3 deletions homeassistant/components/unifiprotect/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(
self._pending_camera_ids: set[str] = set()
self._unsub_interval: CALLBACK_TYPE | None = None
self._unsub_websocket: CALLBACK_TYPE | None = None
self._auth_failures = 0

self.last_update_success = False
self.api = protect
Expand Down Expand Up @@ -117,9 +118,13 @@ async def async_refresh(self, *_: Any, force: bool = False) -> None:
try:
updates = await self.api.update(force=force)
except NotAuthorized:
await self.async_stop()
_LOGGER.exception("Reauthentication required")
self._entry.async_start_reauth(self._hass)
if self._auth_failures < 10:
_LOGGER.exception("Auth error while updating")
self._auth_failures += 1
else:
await self.async_stop()
_LOGGER.exception("Reauthentication required")
self._entry.async_start_reauth(self._hass)
self.last_update_success = False
except ClientError:
if self.last_update_success:
Expand All @@ -129,6 +134,7 @@ async def async_refresh(self, *_: Any, force: bool = False) -> None:
self._async_process_updates(self.api.bootstrap)
else:
self.last_update_success = True
self._auth_failures = 0
self._async_process_updates(updates)

@callback
Expand Down
27 changes: 21 additions & 6 deletions tests/components/unifiprotect/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
from pyunifiprotect.data import NVR, Bootstrap, Light

from homeassistant.components.unifiprotect.const import CONF_DISABLE_RTSP, DOMAIN
from homeassistant.components.unifiprotect.const import (
CONF_DISABLE_RTSP,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
)
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.setup import async_setup_component

from . import _patch_discovery
from .utils import MockUFPFixture, init_entry
from .utils import MockUFPFixture, init_entry, time_changed

from tests.common import MockConfigEntry

Expand Down Expand Up @@ -145,12 +149,23 @@ async def test_setup_failed_update(hass: HomeAssistant, ufp: MockUFPFixture):
async def test_setup_failed_update_reauth(hass: HomeAssistant, ufp: MockUFPFixture):
"""Test setup of unifiprotect entry with update that gives unauthroized error."""

ufp.api.update = AsyncMock(side_effect=NotAuthorized)

await hass.config_entries.async_setup(ufp.entry.entry_id)
await hass.async_block_till_done()
assert ufp.entry.state == ConfigEntryState.SETUP_RETRY
assert ufp.api.update.called
assert ufp.entry.state == ConfigEntryState.LOADED

# reauth should not be triggered until there are 10 auth failures in a row
# to verify it is not transient
ufp.api.update = AsyncMock(side_effect=NotAuthorized)
for _ in range(10):
await time_changed(hass, DEFAULT_SCAN_INTERVAL)
assert len(hass.config_entries.flow._progress) == 0

assert ufp.api.update.call_count == 10
assert ufp.entry.state == ConfigEntryState.LOADED

await time_changed(hass, DEFAULT_SCAN_INTERVAL)
assert ufp.api.update.call_count == 11
assert len(hass.config_entries.flow._progress) == 1


async def test_setup_failed_error(hass: HomeAssistant, ufp: MockUFPFixture):
Expand Down