Axis - config flow use new helper functions (#31286)

* Make use of new config flow helpers
Simplify Axis entry config to work with config flow helpers

* Keep old device data for rollback purposes
This commit is contained in:
Robert Svensson 2020-01-30 22:20:30 +01:00 committed by GitHub
parent cd1aa46404
commit 56657fa859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 183 additions and 130 deletions

View File

@ -1,15 +1,23 @@
"""Support for Axis devices.""" """Support for Axis devices."""
import logging
from homeassistant.const import ( from homeassistant.const import (
CONF_DEVICE, CONF_DEVICE,
CONF_HOST,
CONF_MAC, CONF_MAC,
CONF_PASSWORD,
CONF_PORT,
CONF_TRIGGER_TIME, CONF_TRIGGER_TIME,
CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
) )
from .const import CONF_CAMERA, CONF_EVENTS, DEFAULT_TRIGGER_TIME, DOMAIN from .const import CONF_CAMERA, CONF_EVENTS, DEFAULT_TRIGGER_TIME, DOMAIN
from .device import AxisNetworkDevice, get_device from .device import AxisNetworkDevice, get_device
LOGGER = logging.getLogger(__name__)
async def async_setup(hass, config): async def async_setup(hass, config):
"""Old way to set up Axis devices.""" """Old way to set up Axis devices."""
@ -35,7 +43,7 @@ async def async_setup_entry(hass, config_entry):
config_entry, unique_id=device.api.vapix.params.system_serialnumber config_entry, unique_id=device.api.vapix.params.system_serialnumber
) )
hass.data[DOMAIN][device.serial] = device hass.data[DOMAIN][config_entry.unique_id] = device
await device.async_update_device_registry() await device.async_update_device_registry()
@ -52,7 +60,13 @@ async def async_unload_entry(hass, config_entry):
async def async_populate_options(hass, config_entry): async def async_populate_options(hass, config_entry):
"""Populate default options for device.""" """Populate default options for device."""
device = await get_device(hass, config_entry.data[CONF_DEVICE]) device = await get_device(
hass,
host=config_entry.data[CONF_HOST],
port=config_entry.data[CONF_PORT],
username=config_entry.data[CONF_USERNAME],
password=config_entry.data[CONF_PASSWORD],
)
supported_formats = device.vapix.params.image_format supported_formats = device.vapix.params.image_format
camera = bool(supported_formats) camera = bool(supported_formats)
@ -64,3 +78,18 @@ async def async_populate_options(hass, config_entry):
} }
hass.config_entries.async_update_entry(config_entry, options=options) hass.config_entries.async_update_entry(config_entry, options=options)
async def async_migrate_entry(hass, config_entry):
"""Migrate old entry."""
LOGGER.debug("Migrating from version %s", config_entry.version)
# Flatten configuration but keep old data if user rollbacks HASS
if config_entry.version == 1:
config_entry.data = {**config_entry.data, **config_entry.data[CONF_DEVICE]}
config_entry.version = 2
LOGGER.info("Migration to version %s successful", config_entry.version)
return True

View File

@ -9,7 +9,6 @@ from homeassistant.components.mjpeg.camera import (
) )
from homeassistant.const import ( from homeassistant.const import (
CONF_AUTHENTICATION, CONF_AUTHENTICATION,
CONF_DEVICE,
CONF_HOST, CONF_HOST,
CONF_NAME, CONF_NAME,
CONF_PASSWORD, CONF_PASSWORD,
@ -35,15 +34,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
config = { config = {
CONF_NAME: config_entry.data[CONF_NAME], CONF_NAME: config_entry.data[CONF_NAME],
CONF_USERNAME: config_entry.data[CONF_DEVICE][CONF_USERNAME], CONF_USERNAME: config_entry.data[CONF_USERNAME],
CONF_PASSWORD: config_entry.data[CONF_DEVICE][CONF_PASSWORD], CONF_PASSWORD: config_entry.data[CONF_PASSWORD],
CONF_MJPEG_URL: AXIS_VIDEO.format( CONF_MJPEG_URL: AXIS_VIDEO.format(
config_entry.data[CONF_DEVICE][CONF_HOST], config_entry.data[CONF_HOST], config_entry.data[CONF_PORT],
config_entry.data[CONF_DEVICE][CONF_PORT],
), ),
CONF_STILL_IMAGE_URL: AXIS_IMAGE.format( CONF_STILL_IMAGE_URL: AXIS_IMAGE.format(
config_entry.data[CONF_DEVICE][CONF_HOST], config_entry.data[CONF_HOST], config_entry.data[CONF_PORT],
config_entry.data[CONF_DEVICE][CONF_PORT],
), ),
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION, CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
} }
@ -76,14 +73,14 @@ class AxisCamera(AxisEntityBase, MjpegCamera):
async def stream_source(self): async def stream_source(self):
"""Return the stream source.""" """Return the stream source."""
return AXIS_STREAM.format( return AXIS_STREAM.format(
self.device.config_entry.data[CONF_DEVICE][CONF_USERNAME], self.device.config_entry.data[CONF_USERNAME],
self.device.config_entry.data[CONF_DEVICE][CONF_PASSWORD], self.device.config_entry.data[CONF_PASSWORD],
self.device.host, self.device.host,
) )
def _new_address(self): def _new_address(self):
"""Set new device address for video stream.""" """Set new device address for video stream."""
port = self.device.config_entry.data[CONF_DEVICE][CONF_PORT] port = self.device.config_entry.data[CONF_PORT]
self._mjpeg_url = AXIS_VIDEO.format(self.device.host, port) self._mjpeg_url = AXIS_VIDEO.format(self.device.host, port)
self._still_image_url = AXIS_IMAGE.format(self.device.host, port) self._still_image_url = AXIS_IMAGE.format(self.device.host, port)

View File

@ -4,7 +4,6 @@ import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import ( from homeassistant.const import (
CONF_DEVICE,
CONF_HOST, CONF_HOST,
CONF_MAC, CONF_MAC,
CONF_NAME, CONF_NAME,
@ -33,16 +32,12 @@ DEFAULT_PORT = 80
class AxisFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): class AxisFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a Axis config flow.""" """Handle a Axis config flow."""
VERSION = 1 VERSION = 2
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
def __init__(self): def __init__(self):
"""Initialize the Axis config flow.""" """Initialize the Axis config flow."""
self.device_config = {} self.device_config = {}
self.model = None
self.name = None
self.serial_number = None
self.discovery_schema = {} self.discovery_schema = {}
self.import_schema = {} self.import_schema = {}
@ -55,24 +50,32 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
if user_input is not None: if user_input is not None:
try: try:
device = await get_device(
self.hass,
host=user_input[CONF_HOST],
port=user_input[CONF_PORT],
username=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
)
serial_number = device.vapix.params.system_serialnumber
await self.async_set_unique_id(serial_number)
self._abort_if_unique_id_configured(
updates={
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
}
)
self.device_config = { self.device_config = {
CONF_HOST: user_input[CONF_HOST], CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT], CONF_PORT: user_input[CONF_PORT],
CONF_USERNAME: user_input[CONF_USERNAME], CONF_USERNAME: user_input[CONF_USERNAME],
CONF_PASSWORD: user_input[CONF_PASSWORD], CONF_PASSWORD: user_input[CONF_PASSWORD],
CONF_MAC: serial_number,
CONF_MODEL: device.vapix.params.prodnbr,
} }
device = await get_device(self.hass, self.device_config)
self.serial_number = device.vapix.params.system_serialnumber
config_entry = await self.async_set_unique_id(self.serial_number)
if config_entry:
return self._update_entry(
config_entry,
host=user_input[CONF_HOST],
port=user_input[CONF_PORT],
)
self.model = device.vapix.params.prodnbr
return await self._create_entry() return await self._create_entry()
@ -101,41 +104,23 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
Generate a name to be used as a prefix for device entities. Generate a name to be used as a prefix for device entities.
""" """
model = self.device_config[CONF_MODEL]
same_model = [ same_model = [
entry.data[CONF_NAME] entry.data[CONF_NAME]
for entry in self.hass.config_entries.async_entries(DOMAIN) for entry in self.hass.config_entries.async_entries(DOMAIN)
if entry.data[CONF_MODEL] == self.model if entry.data[CONF_MODEL] == model
] ]
self.name = f"{self.model}" name = model
for idx in range(len(same_model) + 1): for idx in range(len(same_model) + 1):
self.name = f"{self.model} {idx}" name = f"{model} {idx}"
if self.name not in same_model: if name not in same_model:
break break
data = { self.device_config[CONF_NAME] = name
CONF_DEVICE: self.device_config,
CONF_NAME: self.name,
CONF_MAC: self.serial_number,
CONF_MODEL: self.model,
}
title = f"{self.model} - {self.serial_number}" title = f"{model} - {self.device_config[CONF_MAC]}"
return self.async_create_entry(title=title, data=data) return self.async_create_entry(title=title, data=self.device_config)
def _update_entry(self, entry, host, port):
"""Update existing entry."""
if (
entry.data[CONF_DEVICE][CONF_HOST] == host
and entry.data[CONF_DEVICE][CONF_PORT] == port
):
return self.async_abort(reason="already_configured")
entry.data[CONF_DEVICE][CONF_HOST] = host
entry.data[CONF_DEVICE][CONF_PORT] = port
self.hass.config_entries.async_update_entry(entry)
return self.async_abort(reason="updated_configuration")
async def async_step_zeroconf(self, discovery_info): async def async_step_zeroconf(self, discovery_info):
"""Prepare configuration for a discovered Axis device.""" """Prepare configuration for a discovered Axis device."""
@ -147,18 +132,19 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
if discovery_info[CONF_HOST].startswith("169.254"): if discovery_info[CONF_HOST].startswith("169.254"):
return self.async_abort(reason="link_local_address") return self.async_abort(reason="link_local_address")
config_entry = await self.async_set_unique_id(serial_number) await self.async_set_unique_id(serial_number)
if config_entry:
return self._update_entry( self._abort_if_unique_id_configured(
config_entry, updates={
host=discovery_info[CONF_HOST], CONF_HOST: discovery_info[CONF_HOST],
port=discovery_info[CONF_PORT], CONF_PORT: discovery_info[CONF_PORT],
) }
)
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["title_placeholders"] = { self.context["title_placeholders"] = {
"name": discovery_info["hostname"][:-7], CONF_NAME: discovery_info["hostname"][:-7],
"host": discovery_info[CONF_HOST], CONF_HOST: discovery_info[CONF_HOST],
} }
self.discovery_schema = { self.discovery_schema = {

View File

@ -7,9 +7,7 @@ import axis
from axis.streammanager import SIGNAL_PLAYING from axis.streammanager import SIGNAL_PLAYING
from homeassistant.const import ( from homeassistant.const import (
CONF_DEVICE,
CONF_HOST, CONF_HOST,
CONF_MAC,
CONF_NAME, CONF_NAME,
CONF_PASSWORD, CONF_PASSWORD,
CONF_PORT, CONF_PORT,
@ -42,7 +40,7 @@ class AxisNetworkDevice:
@property @property
def host(self): def host(self):
"""Return the host of this device.""" """Return the host of this device."""
return self.config_entry.data[CONF_DEVICE][CONF_HOST] return self.config_entry.data[CONF_HOST]
@property @property
def model(self): def model(self):
@ -75,7 +73,13 @@ class AxisNetworkDevice:
async def async_setup(self): async def async_setup(self):
"""Set up the device.""" """Set up the device."""
try: try:
self.api = await get_device(self.hass, self.config_entry.data[CONF_DEVICE]) self.api = await get_device(
self.hass,
host=self.config_entry.data[CONF_HOST],
port=self.config_entry.data[CONF_PORT],
username=self.config_entry.data[CONF_USERNAME],
password=self.config_entry.data[CONF_PASSWORD],
)
except CannotConnect: except CannotConnect:
raise ConfigEntryNotReady raise ConfigEntryNotReady
@ -126,7 +130,7 @@ class AxisNetworkDevice:
This is a static method because a class method (bound method), This is a static method because a class method (bound method),
can not be used with weak references. can not be used with weak references.
""" """
device = hass.data[DOMAIN][entry.data[CONF_MAC]] device = hass.data[DOMAIN][entry.unique_id]
device.api.config.host = device.host device.api.config.host = device.host
async_dispatcher_send(hass, device.event_new_address) async_dispatcher_send(hass, device.event_new_address)
@ -197,15 +201,15 @@ class AxisNetworkDevice:
return True return True
async def get_device(hass, config): async def get_device(hass, host, port, username, password):
"""Create a Axis device.""" """Create a Axis device."""
device = axis.AxisDevice( device = axis.AxisDevice(
loop=hass.loop, loop=hass.loop,
host=config[CONF_HOST], host=host,
username=config[CONF_USERNAME], port=port,
password=config[CONF_PASSWORD], username=username,
port=config[CONF_PORT], password=password,
web_proto="http", web_proto="http",
) )
@ -224,13 +228,11 @@ async def get_device(hass, config):
return device return device
except axis.Unauthorized: except axis.Unauthorized:
LOGGER.warning( LOGGER.warning("Connected to device at %s but not registered.", host)
"Connected to device at %s but not registered.", config[CONF_HOST]
)
raise AuthenticationRequired raise AuthenticationRequired
except (asyncio.TimeoutError, axis.RequestError): except (asyncio.TimeoutError, axis.RequestError):
LOGGER.error("Error connecting to the Axis device at %s", config[CONF_HOST]) LOGGER.error("Error connecting to the Axis device at %s", host)
raise CannotConnect raise CannotConnect
except axis.AxisException: except axis.AxisException:

View File

@ -23,8 +23,7 @@
"already_configured": "Device is already configured", "already_configured": "Device is already configured",
"bad_config_file": "Bad data from config file", "bad_config_file": "Bad data from config file",
"link_local_address": "Link local addresses are not supported", "link_local_address": "Link local addresses are not supported",
"not_axis_device": "Discovered device not an Axis device", "not_axis_device": "Discovered device not an Axis device"
"updated_configuration": "Updated device configuration with new host address"
} }
} }
} }

View File

@ -4,7 +4,7 @@ from unittest.mock import Mock, patch
from homeassistant.components import axis from homeassistant.components import axis
from homeassistant.components.axis import config_flow from homeassistant.components.axis import config_flow
from .test_device import MAC, setup_axis_integration from .test_device import MAC, MODEL, NAME, setup_axis_integration
from tests.common import MockConfigEntry, mock_coro from tests.common import MockConfigEntry, mock_coro
@ -54,12 +54,10 @@ async def test_flow_manual_configuration(hass):
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
assert result["title"] == f"prodnbr - {MAC}" assert result["title"] == f"prodnbr - {MAC}"
assert result["data"] == { assert result["data"] == {
axis.CONF_DEVICE: { config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_HOST: "1.2.3.4", config_flow.CONF_USERNAME: "user",
config_flow.CONF_USERNAME: "user", config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PASSWORD: "pass", config_flow.CONF_PORT: 80,
config_flow.CONF_PORT: 80,
},
config_flow.CONF_MAC: MAC, config_flow.CONF_MAC: MAC,
config_flow.CONF_MODEL: "prodnbr", config_flow.CONF_MODEL: "prodnbr",
config_flow.CONF_NAME: "prodnbr 0", config_flow.CONF_NAME: "prodnbr 0",
@ -95,11 +93,8 @@ async def test_manual_configuration_update_configuration(hass):
) )
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "updated_configuration" assert result["reason"] == "already_configured"
assert ( assert device.config_entry.data[config_flow.CONF_HOST] == "2.3.4.5"
device.config_entry.data[config_flow.CONF_DEVICE][config_flow.CONF_HOST]
== "2.3.4.5"
)
async def test_flow_fails_already_configured(hass): async def test_flow_fails_already_configured(hass):
@ -223,12 +218,10 @@ async def test_flow_create_entry_multiple_existing_entries_of_same_model(hass):
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
assert result["title"] == f"prodnbr - {MAC}" assert result["title"] == f"prodnbr - {MAC}"
assert result["data"] == { assert result["data"] == {
axis.CONF_DEVICE: { config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_HOST: "1.2.3.4", config_flow.CONF_USERNAME: "user",
config_flow.CONF_USERNAME: "user", config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PASSWORD: "pass", config_flow.CONF_PORT: 80,
config_flow.CONF_PORT: 80,
},
config_flow.CONF_MAC: MAC, config_flow.CONF_MAC: MAC,
config_flow.CONF_MODEL: "prodnbr", config_flow.CONF_MODEL: "prodnbr",
config_flow.CONF_NAME: "prodnbr 2", config_flow.CONF_NAME: "prodnbr 2",
@ -271,12 +264,10 @@ async def test_zeroconf_flow(hass):
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
assert result["title"] == f"prodnbr - {MAC}" assert result["title"] == f"prodnbr - {MAC}"
assert result["data"] == { assert result["data"] == {
axis.CONF_DEVICE: { config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_HOST: "1.2.3.4", config_flow.CONF_USERNAME: "user",
config_flow.CONF_USERNAME: "user", config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PASSWORD: "pass", config_flow.CONF_PORT: 80,
config_flow.CONF_PORT: 80,
},
config_flow.CONF_MAC: MAC, config_flow.CONF_MAC: MAC,
config_flow.CONF_MODEL: "prodnbr", config_flow.CONF_MODEL: "prodnbr",
config_flow.CONF_NAME: "prodnbr 0", config_flow.CONF_NAME: "prodnbr 0",
@ -310,6 +301,15 @@ async def test_zeroconf_flow_updated_configuration(hass):
"""Test that zeroconf update configuration with new parameters.""" """Test that zeroconf update configuration with new parameters."""
device = await setup_axis_integration(hass) device = await setup_axis_integration(hass)
assert device.host == "1.2.3.4" assert device.host == "1.2.3.4"
assert device.config_entry.data == {
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_PORT: 80,
config_flow.CONF_USERNAME: "username",
config_flow.CONF_PASSWORD: "password",
config_flow.CONF_MAC: MAC,
config_flow.CONF_MODEL: MODEL,
config_flow.CONF_NAME: NAME,
}
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, config_flow.DOMAIN,
@ -323,11 +323,16 @@ async def test_zeroconf_flow_updated_configuration(hass):
) )
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "updated_configuration" assert result["reason"] == "already_configured"
assert device.host == "2.3.4.5" assert device.config_entry.data == {
assert ( config_flow.CONF_HOST: "2.3.4.5",
device.config_entry.data[config_flow.CONF_DEVICE][config_flow.CONF_PORT] == 8080 config_flow.CONF_PORT: 8080,
) config_flow.CONF_USERNAME: "username",
config_flow.CONF_PASSWORD: "password",
config_flow.CONF_MAC: MAC,
config_flow.CONF_MODEL: MODEL,
config_flow.CONF_NAME: NAME,
}
async def test_zeroconf_flow_ignore_non_axis_device(hass): async def test_zeroconf_flow_ignore_non_axis_device(hass):

View File

@ -14,18 +14,14 @@ MAC = "00408C12345"
MODEL = "model" MODEL = "model"
NAME = "name" NAME = "name"
DEVICE_DATA = { ENTRY_OPTIONS = {axis.CONF_CAMERA: True, axis.CONF_EVENTS: True}
axis.device.CONF_HOST: "1.2.3.4",
axis.device.CONF_USERNAME: "username",
axis.device.CONF_PASSWORD: "password",
axis.device.CONF_PORT: 80,
}
ENTRY_OPTIONS = {axis.device.CONF_CAMERA: True, axis.device.CONF_EVENTS: True}
ENTRY_CONFIG = { ENTRY_CONFIG = {
axis.device.CONF_DEVICE: DEVICE_DATA, axis.CONF_HOST: "1.2.3.4",
axis.device.CONF_MAC: MAC, axis.CONF_USERNAME: "username",
axis.CONF_PASSWORD: "password",
axis.CONF_PORT: 80,
axis.CONF_MAC: MAC,
axis.device.CONF_MODEL: MODEL, axis.device.CONF_MODEL: MODEL,
axis.device.CONF_NAME: NAME, axis.device.CONF_NAME: NAME,
} }
@ -76,6 +72,7 @@ async def setup_axis_integration(
connection_class=config_entries.CONN_CLASS_LOCAL_PUSH, connection_class=config_entries.CONN_CLASS_LOCAL_PUSH,
options=deepcopy(options), options=deepcopy(options),
entry_id="1", entry_id="1",
version=2,
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
@ -116,10 +113,10 @@ async def test_device_setup(hass):
assert forward_entry_setup.mock_calls[1][1] == (entry, "binary_sensor") assert forward_entry_setup.mock_calls[1][1] == (entry, "binary_sensor")
assert forward_entry_setup.mock_calls[2][1] == (entry, "switch") assert forward_entry_setup.mock_calls[2][1] == (entry, "switch")
assert device.host == DEVICE_DATA[axis.device.CONF_HOST] assert device.host == ENTRY_CONFIG[axis.CONF_HOST]
assert device.model == ENTRY_CONFIG[axis.device.CONF_MODEL] assert device.model == ENTRY_CONFIG[axis.device.CONF_MODEL]
assert device.name == ENTRY_CONFIG[axis.device.CONF_NAME] assert device.name == ENTRY_CONFIG[axis.device.CONF_NAME]
assert device.serial == ENTRY_CONFIG[axis.device.CONF_MAC] assert device.serial == ENTRY_CONFIG[axis.CONF_MAC]
async def test_update_address(hass): async def test_update_address(hass):
@ -204,7 +201,7 @@ async def test_get_device_fails(hass):
with patch( with patch(
"axis.param_cgi.Params.update_brand", side_effect=axislib.Unauthorized "axis.param_cgi.Params.update_brand", side_effect=axislib.Unauthorized
), pytest.raises(axis.errors.AuthenticationRequired): ), pytest.raises(axis.errors.AuthenticationRequired):
await axis.device.get_device(hass, DEVICE_DATA) await axis.device.get_device(hass, host="", port="", username="", password="")
async def test_get_device_device_unavailable(hass): async def test_get_device_device_unavailable(hass):
@ -212,7 +209,7 @@ async def test_get_device_device_unavailable(hass):
with patch( with patch(
"axis.param_cgi.Params.update_brand", side_effect=axislib.RequestError "axis.param_cgi.Params.update_brand", side_effect=axislib.RequestError
), pytest.raises(axis.errors.CannotConnect): ), pytest.raises(axis.errors.CannotConnect):
await axis.device.get_device(hass, DEVICE_DATA) await axis.device.get_device(hass, host="", port="", username="", password="")
async def test_get_device_unknown_error(hass): async def test_get_device_unknown_error(hass):
@ -220,4 +217,4 @@ async def test_get_device_unknown_error(hass):
with patch( with patch(
"axis.param_cgi.Params.update_brand", side_effect=axislib.AxisException "axis.param_cgi.Params.update_brand", side_effect=axislib.AxisException
), pytest.raises(axis.errors.AuthenticationRequired): ), pytest.raises(axis.errors.AuthenticationRequired):
await axis.device.get_device(hass, DEVICE_DATA) await axis.device.get_device(hass, host="", port="", username="", password="")

View File

@ -4,7 +4,7 @@ from unittest.mock import Mock, patch
from homeassistant.components import axis from homeassistant.components import axis
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from .test_device import MAC, setup_axis_integration from .test_device import ENTRY_CONFIG, MAC, setup_axis_integration
from tests.common import MockConfigEntry, mock_coro from tests.common import MockConfigEntry, mock_coro
@ -16,7 +16,7 @@ async def test_setup_device_already_configured(hass):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
axis.DOMAIN, axis.DOMAIN,
{axis.DOMAIN: {"device_name": {axis.config_flow.CONF_HOST: "1.2.3.4"}}}, {axis.DOMAIN: {"device_name": {axis.CONF_HOST: "1.2.3.4"}}},
) )
assert not mock_config_entries.flow.mock_calls assert not mock_config_entries.flow.mock_calls
@ -38,7 +38,7 @@ async def test_setup_entry(hass):
async def test_setup_entry_fails(hass): async def test_setup_entry_fails(hass):
"""Test successful setup of entry.""" """Test successful setup of entry."""
entry = MockConfigEntry( entry = MockConfigEntry(
domain=axis.DOMAIN, data={axis.device.CONF_MAC: "0123"}, options=True domain=axis.DOMAIN, data={axis.CONF_MAC: "0123"}, options=True
) )
mock_device = Mock() mock_device = Mock()
@ -63,7 +63,7 @@ async def test_unload_entry(hass):
async def test_populate_options(hass): async def test_populate_options(hass):
"""Test successful populate options.""" """Test successful populate options."""
entry = MockConfigEntry(domain=axis.DOMAIN, data={"device": {}}) entry = MockConfigEntry(domain=axis.DOMAIN, data=ENTRY_CONFIG)
entry.add_to_hass(hass) entry.add_to_hass(hass)
with patch.object(axis, "get_device", return_value=mock_coro(Mock())): with patch.object(axis, "get_device", return_value=mock_coro(Mock())):
@ -75,3 +75,41 @@ async def test_populate_options(hass):
axis.CONF_EVENTS: True, axis.CONF_EVENTS: True,
axis.CONF_TRIGGER_TIME: axis.DEFAULT_TRIGGER_TIME, axis.CONF_TRIGGER_TIME: axis.DEFAULT_TRIGGER_TIME,
} }
async def test_migrate_entry(hass):
"""Test successful migration of entry data."""
legacy_config = {
axis.CONF_DEVICE: {
axis.CONF_HOST: "1.2.3.4",
axis.CONF_USERNAME: "username",
axis.CONF_PASSWORD: "password",
axis.CONF_PORT: 80,
},
axis.CONF_MAC: "mac",
axis.device.CONF_MODEL: "model",
axis.device.CONF_NAME: "name",
}
entry = MockConfigEntry(domain=axis.DOMAIN, data=legacy_config)
assert entry.data == legacy_config
assert entry.version == 1
await axis.async_migrate_entry(hass, entry)
assert entry.data == {
axis.CONF_DEVICE: {
axis.CONF_HOST: "1.2.3.4",
axis.CONF_USERNAME: "username",
axis.CONF_PASSWORD: "password",
axis.CONF_PORT: 80,
},
axis.CONF_HOST: "1.2.3.4",
axis.CONF_USERNAME: "username",
axis.CONF_PASSWORD: "password",
axis.CONF_PORT: 80,
axis.CONF_MAC: "mac",
axis.device.CONF_MODEL: "model",
axis.device.CONF_NAME: "name",
}
assert entry.version == 2