Migrate unique id in Trafikverket Camera (#101937)
parent
88296c1998
commit
6b05f51413
|
@ -1,15 +1,23 @@
|
|||
"""The trafikverket_camera component."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from pytrafikverket.trafikverket_camera import TrafikverketCamera
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN, PLATFORMS
|
||||
from .const import CONF_LOCATION, DOMAIN, PLATFORMS
|
||||
from .coordinator import TVDataUpdateCoordinator
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Trafikverket Camera from a config entry."""
|
||||
|
@ -30,3 +38,37 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Migrate old entry."""
|
||||
# Change entry unique id from location to camera id
|
||||
if entry.version == 1:
|
||||
location = entry.data[CONF_LOCATION]
|
||||
api_key = entry.data[CONF_API_KEY]
|
||||
|
||||
web_session = async_get_clientsession(hass)
|
||||
camera_api = TrafikverketCamera(web_session, api_key)
|
||||
|
||||
try:
|
||||
camera_info = await camera_api.async_get_camera(location)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.error(
|
||||
"Could not migrate the config entry. No connection to the api"
|
||||
)
|
||||
return False
|
||||
|
||||
if camera_id := camera_info.camera_id:
|
||||
entry.version = 2
|
||||
_LOGGER.debug(
|
||||
"Migrate Trafikverket Camera config entry unique id to %s",
|
||||
camera_id,
|
||||
)
|
||||
hass.config_entries.async_update_entry(
|
||||
entry,
|
||||
unique_id=f"{DOMAIN}-{camera_id}",
|
||||
)
|
||||
return True
|
||||
_LOGGER.error("Could not migrate the config entry. Camera has no id")
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -25,17 +25,18 @@ from .const import CONF_LOCATION, DOMAIN
|
|||
class TVCameraConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Trafikverket Camera integration."""
|
||||
|
||||
VERSION = 1
|
||||
VERSION = 2
|
||||
|
||||
entry: config_entries.ConfigEntry | None
|
||||
|
||||
async def validate_input(
|
||||
self, sensor_api: str, location: str
|
||||
) -> tuple[dict[str, str], str | None]:
|
||||
) -> tuple[dict[str, str], str | None, str | None]:
|
||||
"""Validate input from user input."""
|
||||
errors: dict[str, str] = {}
|
||||
camera_info: CameraInfo | None = None
|
||||
camera_location: str | None = None
|
||||
camera_id: str | None = None
|
||||
|
||||
web_session = async_get_clientsession(self.hass)
|
||||
camera_api = TrafikverketCamera(web_session, sensor_api)
|
||||
|
@ -51,12 +52,13 @@ class TVCameraConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
errors["base"] = "cannot_connect"
|
||||
|
||||
if camera_info:
|
||||
camera_id = camera_info.camera_id
|
||||
if _location := camera_info.location:
|
||||
camera_location = _location
|
||||
else:
|
||||
camera_location = camera_info.camera_name
|
||||
|
||||
return (errors, camera_location)
|
||||
return (errors, camera_location, camera_id)
|
||||
|
||||
async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult:
|
||||
"""Handle re-authentication with Trafikverket."""
|
||||
|
@ -74,7 +76,7 @@ class TVCameraConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
api_key = user_input[CONF_API_KEY]
|
||||
|
||||
assert self.entry is not None
|
||||
errors, _ = await self.validate_input(
|
||||
errors, _, _ = await self.validate_input(
|
||||
api_key, self.entry.data[CONF_LOCATION]
|
||||
)
|
||||
|
||||
|
@ -109,11 +111,13 @@ class TVCameraConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
api_key = user_input[CONF_API_KEY]
|
||||
location = user_input[CONF_LOCATION]
|
||||
|
||||
errors, camera_location = await self.validate_input(api_key, location)
|
||||
errors, camera_location, camera_id = await self.validate_input(
|
||||
api_key, location
|
||||
)
|
||||
|
||||
if not errors:
|
||||
assert camera_location
|
||||
await self.async_set_unique_id(f"{DOMAIN}-{camera_location}")
|
||||
await self.async_set_unique_id(f"{DOMAIN}-{camera_id}")
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(
|
||||
title=camera_location,
|
||||
|
|
|
@ -32,7 +32,8 @@ async def load_integration_from_entry(
|
|||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="123",
|
||||
version=2,
|
||||
unique_id="trafikverket_camera-1234",
|
||||
title="Test location",
|
||||
)
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ async def test_form(hass: HomeAssistant, get_camera: CameraInfo) -> None:
|
|||
"location": "Test location",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
assert result2["result"].unique_id == "trafikverket_camera-Test location"
|
||||
assert result2["result"].unique_id == "trafikverket_camera-1234"
|
||||
|
||||
|
||||
async def test_form_no_location_data(
|
||||
|
@ -90,7 +90,7 @@ async def test_form_no_location_data(
|
|||
"location": "Test Camera",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
assert result2["result"].unique_id == "trafikverket_camera-Test Camera"
|
||||
assert result2["result"].unique_id == "trafikverket_camera-1234"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -153,6 +153,7 @@ async def test_reauth_flow(hass: HomeAssistant) -> None:
|
|||
CONF_LOCATION: "Test location",
|
||||
},
|
||||
unique_id="1234",
|
||||
version=2,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
|
@ -225,6 +226,7 @@ async def test_reauth_flow_error(
|
|||
CONF_LOCATION: "Test location",
|
||||
},
|
||||
unique_id="1234",
|
||||
version=2,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
|
|
@ -40,7 +40,8 @@ async def test_coordinator(
|
|||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="123",
|
||||
version=2,
|
||||
unique_id="trafikverket_camera-1234",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
@ -100,7 +101,8 @@ async def test_coordinator_failed_update(
|
|||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="123",
|
||||
version=2,
|
||||
unique_id="trafikverket_camera-1234",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
@ -133,7 +135,8 @@ async def test_coordinator_failed_get_image(
|
|||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="123",
|
||||
version=2,
|
||||
unique_id="trafikverket_camera-1234",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
"""Test for Trafikverket Ferry component Init."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from pytrafikverket.exceptions import UnknownError
|
||||
from pytrafikverket.trafikverket_camera import CameraInfo
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.trafikverket_camera import async_migrate_entry
|
||||
from homeassistant.components.trafikverket_camera.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import ENTRY_CONFIG
|
||||
|
||||
|
@ -31,7 +35,8 @@ async def test_setup_entry(
|
|||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="123",
|
||||
version=2,
|
||||
unique_id="trafikverket_camera-1234",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
@ -62,7 +67,8 @@ async def test_unload_entry(
|
|||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="321",
|
||||
version=2,
|
||||
unique_id="trafikverket_camera-1234",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
@ -78,3 +84,145 @@ async def test_unload_entry(
|
|||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_migrate_entry(
|
||||
hass: HomeAssistant,
|
||||
get_camera: CameraInfo,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test migrate entry to version 2."""
|
||||
aioclient_mock.get(
|
||||
"https://www.testurl.com/test_photo.jpg?type=fullsize", content=b"0123456789"
|
||||
)
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="trafikverket_camera-Test location",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.trafikverket_camera.coordinator.TrafikverketCamera.async_get_camera",
|
||||
return_value=get_camera,
|
||||
) as mock_tvt_camera:
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is config_entries.ConfigEntryState.LOADED
|
||||
assert entry.version == 2
|
||||
assert entry.unique_id == "trafikverket_camera-1234"
|
||||
assert len(mock_tvt_camera.mock_calls) == 2
|
||||
|
||||
|
||||
async def test_migrate_entry_fails_with_error(
|
||||
hass: HomeAssistant,
|
||||
get_camera: CameraInfo,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test migrate entry fails with api error."""
|
||||
aioclient_mock.get(
|
||||
"https://www.testurl.com/test_photo.jpg?type=fullsize", content=b"0123456789"
|
||||
)
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="trafikverket_camera-Test location",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.trafikverket_camera.coordinator.TrafikverketCamera.async_get_camera",
|
||||
side_effect=UnknownError,
|
||||
) as mock_tvt_camera:
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is config_entries.ConfigEntryState.MIGRATION_ERROR
|
||||
assert entry.version == 1
|
||||
assert entry.unique_id == "trafikverket_camera-Test location"
|
||||
assert len(mock_tvt_camera.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_migrate_entry_fails_no_id(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test migrate entry fails, camera returns no id."""
|
||||
aioclient_mock.get(
|
||||
"https://www.testurl.com/test_photo.jpg?type=fullsize", content=b"0123456789"
|
||||
)
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="trafikverket_camera-Test location",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
_camera = CameraInfo(
|
||||
camera_name="Test_camera",
|
||||
camera_id=None,
|
||||
active=True,
|
||||
deleted=False,
|
||||
description="Test Camera for testing",
|
||||
direction="180",
|
||||
fullsizephoto=True,
|
||||
location="Test location",
|
||||
modified=datetime(2022, 4, 4, 4, 4, 4, tzinfo=dt_util.UTC),
|
||||
phototime=datetime(2022, 4, 4, 4, 4, 4, tzinfo=dt_util.UTC),
|
||||
photourl="https://www.testurl.com/test_photo.jpg",
|
||||
status="Running",
|
||||
camera_type="Road",
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.trafikverket_camera.coordinator.TrafikverketCamera.async_get_camera",
|
||||
return_value=_camera,
|
||||
) as mock_tvt_camera:
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is config_entries.ConfigEntryState.MIGRATION_ERROR
|
||||
assert entry.version == 1
|
||||
assert entry.unique_id == "trafikverket_camera-Test location"
|
||||
assert len(mock_tvt_camera.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_no_migration_needed(
|
||||
hass: HomeAssistant,
|
||||
get_camera: CameraInfo,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test migrate entry fails, camera returns no id."""
|
||||
aioclient_mock.get(
|
||||
"https://www.testurl.com/test_photo.jpg?type=fullsize", content=b"0123456789"
|
||||
)
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
version=2,
|
||||
entry_id="1234",
|
||||
unique_id="trafikverket_camera-1234",
|
||||
title="Test location",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.trafikverket_camera.coordinator.TrafikverketCamera.async_get_camera",
|
||||
return_value=get_camera,
|
||||
):
|
||||
assert await async_migrate_entry(hass, entry) is True
|
||||
|
|
Loading…
Reference in New Issue