Support configuring Axis to use HTTPS (#113271)

This commit is contained in:
Robert Svensson 2024-03-15 18:58:49 +01:00 committed by GitHub
parent be7c4295dc
commit 5b5ff92a05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 45 additions and 17 deletions

View File

@ -25,6 +25,7 @@ from homeassistant.const import (
CONF_NAME, CONF_NAME,
CONF_PASSWORD, CONF_PASSWORD,
CONF_PORT, CONF_PORT,
CONF_PROTOCOL,
CONF_USERNAME, CONF_USERNAME,
) )
from homeassistant.core import callback from homeassistant.core import callback
@ -42,7 +43,9 @@ from .errors import AuthenticationRequired, CannotConnect
from .hub import AxisHub, get_axis_api from .hub import AxisHub, get_axis_api
AXIS_OUI = {"00:40:8c", "ac:cc:8e", "b8:a4:4f"} AXIS_OUI = {"00:40:8c", "ac:cc:8e", "b8:a4:4f"}
DEFAULT_PORT = 80 DEFAULT_PORT = 443
DEFAULT_PROTOCOL = "https"
PROTOCOL_CHOICES = ["https", "http"]
class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN): class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
@ -74,11 +77,19 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
try: try:
api = await get_axis_api(self.hass, MappingProxyType(user_input)) api = await get_axis_api(self.hass, MappingProxyType(user_input))
except AuthenticationRequired:
errors["base"] = "invalid_auth"
except CannotConnect:
errors["base"] = "cannot_connect"
else:
serial = api.vapix.serial_number serial = api.vapix.serial_number
await self.async_set_unique_id(format_mac(serial)) await self.async_set_unique_id(format_mac(serial))
self._abort_if_unique_id_configured( self._abort_if_unique_id_configured(
updates={ updates={
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
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],
@ -87,6 +98,7 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
) )
self.config = { self.config = {
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
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],
@ -96,13 +108,8 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
return await self._create_entry(serial) return await self._create_entry(serial)
except AuthenticationRequired:
errors["base"] = "invalid_auth"
except CannotConnect:
errors["base"] = "cannot_connect"
data = self.discovery_schema or { data = self.discovery_schema or {
vol.Required(CONF_PROTOCOL): vol.In(PROTOCOL_CHOICES),
vol.Required(CONF_HOST): str, vol.Required(CONF_HOST): str,
vol.Required(CONF_USERNAME): str, vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str, vol.Required(CONF_PASSWORD): str,
@ -149,6 +156,9 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
} }
self.discovery_schema = { self.discovery_schema = {
vol.Required(
CONF_PROTOCOL, default=entry_data.get(CONF_PROTOCOL, DEFAULT_PROTOCOL)
): str,
vol.Required(CONF_HOST, default=entry_data[CONF_HOST]): str, vol.Required(CONF_HOST, default=entry_data[CONF_HOST]): str,
vol.Required(CONF_USERNAME, default=entry_data[CONF_USERNAME]): str, vol.Required(CONF_USERNAME, default=entry_data[CONF_USERNAME]): str,
vol.Required(CONF_PASSWORD): str, vol.Required(CONF_PASSWORD): str,
@ -166,7 +176,7 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
CONF_HOST: discovery_info.ip, CONF_HOST: discovery_info.ip,
CONF_MAC: format_mac(discovery_info.macaddress), CONF_MAC: format_mac(discovery_info.macaddress),
CONF_NAME: discovery_info.hostname, CONF_NAME: discovery_info.hostname,
CONF_PORT: DEFAULT_PORT, CONF_PORT: 80,
} }
) )
@ -210,10 +220,7 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
await self.async_set_unique_id(discovery_info[CONF_MAC]) await self.async_set_unique_id(discovery_info[CONF_MAC])
self._abort_if_unique_id_configured( self._abort_if_unique_id_configured(
updates={ updates={CONF_HOST: discovery_info[CONF_HOST]}
CONF_HOST: discovery_info[CONF_HOST],
CONF_PORT: discovery_info[CONF_PORT],
}
) )
self.context.update( self.context.update(
@ -227,10 +234,11 @@ class AxisFlowHandler(ConfigFlow, domain=AXIS_DOMAIN):
) )
self.discovery_schema = { self.discovery_schema = {
vol.Required(CONF_PROTOCOL): vol.In(PROTOCOL_CHOICES),
vol.Required(CONF_HOST, default=discovery_info[CONF_HOST]): str, vol.Required(CONF_HOST, default=discovery_info[CONF_HOST]): str,
vol.Required(CONF_USERNAME): str, vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str, vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_PORT, default=discovery_info[CONF_PORT]): int, vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
} }
return await self.async_step_user() return await self.async_step_user()

View File

@ -7,7 +7,13 @@ from typing import Any
import axis import axis
from axis.configuration import Configuration from axis.configuration import Configuration
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_PROTOCOL,
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.httpx_client import get_async_client from homeassistant.helpers.httpx_client import get_async_client
@ -29,6 +35,7 @@ async def get_axis_api(
port=config[CONF_PORT], port=config[CONF_PORT],
username=config[CONF_USERNAME], username=config[CONF_USERNAME],
password=config[CONF_PASSWORD], password=config[CONF_PASSWORD],
web_proto=config.get(CONF_PROTOCOL, "http"),
) )
) )

View File

@ -28,6 +28,7 @@ from homeassistant.const import (
CONF_NAME, CONF_NAME,
CONF_PASSWORD, CONF_PASSWORD,
CONF_PORT, CONF_PORT,
CONF_PROTOCOL,
CONF_USERNAME, CONF_USERNAME,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -65,6 +66,7 @@ async def test_flow_manual_configuration(
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_PROTOCOL: "http",
CONF_HOST: "1.2.3.4", CONF_HOST: "1.2.3.4",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -75,6 +77,7 @@ async def test_flow_manual_configuration(
assert result["type"] == FlowResultType.CREATE_ENTRY assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == f"M1065-LW - {MAC}" assert result["title"] == f"M1065-LW - {MAC}"
assert result["data"] == { assert result["data"] == {
CONF_PROTOCOL: "http",
CONF_HOST: "1.2.3.4", CONF_HOST: "1.2.3.4",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -101,6 +104,7 @@ async def test_manual_configuration_update_configuration(
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_PROTOCOL: "http",
CONF_HOST: "2.3.4.5", CONF_HOST: "2.3.4.5",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -130,6 +134,7 @@ async def test_flow_fails_faulty_credentials(hass: HomeAssistant) -> None:
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_PROTOCOL: "http",
CONF_HOST: "1.2.3.4", CONF_HOST: "1.2.3.4",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -156,6 +161,7 @@ async def test_flow_fails_cannot_connect(hass: HomeAssistant) -> None:
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_PROTOCOL: "http",
CONF_HOST: "1.2.3.4", CONF_HOST: "1.2.3.4",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -191,6 +197,7 @@ async def test_flow_create_entry_multiple_existing_entries_of_same_model(
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_PROTOCOL: "http",
CONF_HOST: "1.2.3.4", CONF_HOST: "1.2.3.4",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -201,6 +208,7 @@ async def test_flow_create_entry_multiple_existing_entries_of_same_model(
assert result["type"] == FlowResultType.CREATE_ENTRY assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == f"M1065-LW - {MAC}" assert result["title"] == f"M1065-LW - {MAC}"
assert result["data"] == { assert result["data"] == {
CONF_PROTOCOL: "http",
CONF_HOST: "1.2.3.4", CONF_HOST: "1.2.3.4",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -233,17 +241,20 @@ async def test_reauth_flow_update_configuration(
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_PROTOCOL: "https",
CONF_HOST: "2.3.4.5", CONF_HOST: "2.3.4.5",
CONF_USERNAME: "user2", CONF_USERNAME: "user2",
CONF_PASSWORD: "pass2", CONF_PASSWORD: "pass2",
CONF_PORT: 80, CONF_PORT: 443,
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert result["type"] == FlowResultType.ABORT assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"
assert mock_config_entry.data[CONF_PROTOCOL] == "https"
assert mock_config_entry.data[CONF_HOST] == "2.3.4.5" assert mock_config_entry.data[CONF_HOST] == "2.3.4.5"
assert mock_config_entry.data[CONF_PORT] == 443
assert mock_config_entry.data[CONF_USERNAME] == "user2" assert mock_config_entry.data[CONF_USERNAME] == "user2"
assert mock_config_entry.data[CONF_PASSWORD] == "pass2" assert mock_config_entry.data[CONF_PASSWORD] == "pass2"
@ -334,6 +345,7 @@ async def test_discovery_flow(
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_PROTOCOL: "http",
CONF_HOST: "1.2.3.4", CONF_HOST: "1.2.3.4",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -344,6 +356,7 @@ async def test_discovery_flow(
assert result["type"] == FlowResultType.CREATE_ENTRY assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == f"M1065-LW - {MAC}" assert result["title"] == f"M1065-LW - {MAC}"
assert result["data"] == { assert result["data"] == {
CONF_PROTOCOL: "http",
CONF_HOST: "1.2.3.4", CONF_HOST: "1.2.3.4",
CONF_USERNAME: "user", CONF_USERNAME: "user",
CONF_PASSWORD: "pass", CONF_PASSWORD: "pass",
@ -430,7 +443,7 @@ async def test_discovered_device_already_configured(
"presentationURL": "http://2.3.4.5:8080/", "presentationURL": "http://2.3.4.5:8080/",
}, },
), ),
8080, 80,
), ),
( (
SOURCE_ZEROCONF, SOURCE_ZEROCONF,
@ -443,7 +456,7 @@ async def test_discovered_device_already_configured(
properties={"macaddress": MAC}, properties={"macaddress": MAC},
type="mock_type", type="mock_type",
), ),
8080, 80,
), ),
], ],
) )