mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 00:37:53 +00:00
Add HTTPS support for Fully Kiosk (#89592)
* Add HTTPS support for Fully Kiosk with optional certificate verification. * All pytests passing. * Better readability for url parameter of DeviceInfo * All pytests passing with latest fixes from upstream * Removing fully_kiosk/translations * Rebasing * Added extra error detail when the integration config flow fails --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
39c81cb4b1
commit
7ef47da27d
@ -12,7 +12,13 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.dhcp import DhcpServiceInfo
|
||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_MAC,
|
||||
CONF_PASSWORD,
|
||||
CONF_SSL,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
@ -31,13 +37,19 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
self._discovered_device_info: dict[str, Any] = {}
|
||||
|
||||
async def _create_entry(
|
||||
self, host: str, user_input: dict[str, Any], errors: dict[str, str]
|
||||
self,
|
||||
host: str,
|
||||
user_input: dict[str, Any],
|
||||
errors: dict[str, str],
|
||||
description_placeholders: dict[str, str] | Any = None,
|
||||
) -> FlowResult | None:
|
||||
fully = FullyKiosk(
|
||||
async_get_clientsession(self.hass),
|
||||
host,
|
||||
DEFAULT_PORT,
|
||||
user_input[CONF_PASSWORD],
|
||||
use_ssl=user_input[CONF_SSL],
|
||||
verify_ssl=user_input[CONF_VERIFY_SSL],
|
||||
)
|
||||
|
||||
try:
|
||||
@ -50,10 +62,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
) as error:
|
||||
LOGGER.debug(error.args, exc_info=True)
|
||||
errors["base"] = "cannot_connect"
|
||||
description_placeholders["error_detail"] = str(error.args)
|
||||
return None
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
LOGGER.exception("Unexpected exception: %s", error)
|
||||
errors["base"] = "unknown"
|
||||
description_placeholders["error_detail"] = str(error.args)
|
||||
return None
|
||||
|
||||
await self.async_set_unique_id(device_info["deviceID"], raise_on_progress=False)
|
||||
@ -64,6 +78,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
CONF_HOST: host,
|
||||
CONF_PASSWORD: user_input[CONF_PASSWORD],
|
||||
CONF_MAC: format_mac(device_info["Mac"]),
|
||||
CONF_SSL: user_input[CONF_SSL],
|
||||
CONF_VERIFY_SSL: user_input[CONF_VERIFY_SSL],
|
||||
},
|
||||
)
|
||||
|
||||
@ -72,8 +88,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step."""
|
||||
errors: dict[str, str] = {}
|
||||
placeholders: dict[str, str] = {}
|
||||
if user_input is not None:
|
||||
result = await self._create_entry(user_input[CONF_HOST], user_input, errors)
|
||||
result = await self._create_entry(
|
||||
user_input[CONF_HOST], user_input, errors, placeholders
|
||||
)
|
||||
if result:
|
||||
return result
|
||||
|
||||
@ -83,8 +102,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
{
|
||||
vol.Required(CONF_HOST): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Optional(CONF_SSL, default=False): bool,
|
||||
vol.Optional(CONF_VERIFY_SSL, default=False): bool,
|
||||
}
|
||||
),
|
||||
description_placeholders=placeholders,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
@ -127,6 +149,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Optional(CONF_SSL, default=False): bool,
|
||||
vol.Optional(CONF_VERIFY_SSL, default=False): bool,
|
||||
}
|
||||
),
|
||||
description_placeholders=placeholders,
|
||||
|
@ -6,7 +6,7 @@ from fullykiosk import FullyKiosk
|
||||
from fullykiosk.exceptions import FullyKioskError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_SSL, CONF_VERIFY_SSL
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
@ -24,6 +24,8 @@ class FullyKioskDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
entry.data[CONF_HOST],
|
||||
DEFAULT_PORT,
|
||||
entry.data[CONF_PASSWORD],
|
||||
use_ssl=entry.data[CONF_SSL],
|
||||
verify_ssl=entry.data[CONF_VERIFY_SSL],
|
||||
)
|
||||
super().__init__(
|
||||
hass,
|
||||
@ -31,6 +33,7 @@ class FullyKioskDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
name=entry.data[CONF_HOST],
|
||||
update_interval=UPDATE_INTERVAL,
|
||||
)
|
||||
self.use_ssl = entry.data[CONF_SSL]
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Any]:
|
||||
"""Update data via library."""
|
||||
|
@ -1,6 +1,8 @@
|
||||
"""Base entity for the Fully Kiosk Browser integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from yarl import URL
|
||||
|
||||
from homeassistant.const import ATTR_CONNECTIONS
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
@ -30,13 +32,20 @@ class FullyKioskEntity(CoordinatorEntity[FullyKioskDataUpdateCoordinator], Entit
|
||||
def __init__(self, coordinator: FullyKioskDataUpdateCoordinator) -> None:
|
||||
"""Initialize the Fully Kiosk Browser entity."""
|
||||
super().__init__(coordinator=coordinator)
|
||||
|
||||
url = URL.build(
|
||||
scheme="https" if coordinator.use_ssl else "http",
|
||||
host=coordinator.data["ip4"],
|
||||
port=2323,
|
||||
)
|
||||
|
||||
device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, coordinator.data["deviceID"])},
|
||||
name=coordinator.data["deviceName"],
|
||||
manufacturer=coordinator.data["deviceManufacturer"],
|
||||
model=coordinator.data["deviceModel"],
|
||||
sw_version=coordinator.data["appVersionName"],
|
||||
configuration_url=f"http://{coordinator.data['ip4']}:2323",
|
||||
configuration_url=str(url),
|
||||
)
|
||||
if "Mac" in coordinator.data and valid_global_mac_address(
|
||||
coordinator.data["Mac"]
|
||||
|
@ -10,13 +10,15 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"ssl": "[%key:common::config_flow::data::ssl%]",
|
||||
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
"cannot_connect": "Cannot connect. Details: {error_detail}",
|
||||
"unknown": "Unknown. Details: {error_detail}"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
||||
|
@ -8,7 +8,13 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.fully_kiosk.const import DOMAIN
|
||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_MAC,
|
||||
CONF_PASSWORD,
|
||||
CONF_SSL,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
@ -24,6 +30,8 @@ def mock_config_entry() -> MockConfigEntry:
|
||||
CONF_HOST: "127.0.0.1",
|
||||
CONF_PASSWORD: "mocked-password",
|
||||
CONF_MAC: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
},
|
||||
unique_id="12345",
|
||||
)
|
||||
|
@ -10,7 +10,13 @@ import pytest
|
||||
from homeassistant.components.dhcp import DhcpServiceInfo
|
||||
from homeassistant.components.fully_kiosk.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_DHCP, SOURCE_MQTT, SOURCE_USER
|
||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_MAC,
|
||||
CONF_PASSWORD,
|
||||
CONF_SSL,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
|
||||
@ -35,6 +41,8 @@ async def test_user_flow(
|
||||
{
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
},
|
||||
)
|
||||
|
||||
@ -44,6 +52,8 @@ async def test_user_flow(
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_MAC: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
}
|
||||
assert "result" in result2
|
||||
assert result2["result"].unique_id == "12345"
|
||||
@ -76,7 +86,13 @@ async def test_errors(
|
||||
|
||||
mock_fully_kiosk_config_flow.getDeviceInfo.side_effect = side_effect
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
flow_id, user_input={CONF_HOST: "1.1.1.1", CONF_PASSWORD: "test-password"}
|
||||
flow_id,
|
||||
user_input={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result2.get("type") == FlowResultType.FORM
|
||||
@ -88,7 +104,13 @@ async def test_errors(
|
||||
|
||||
mock_fully_kiosk_config_flow.getDeviceInfo.side_effect = None
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
flow_id, user_input={CONF_HOST: "1.1.1.1", CONF_PASSWORD: "test-password"}
|
||||
flow_id,
|
||||
user_input={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_SSL: True,
|
||||
CONF_VERIFY_SSL: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result3.get("type") == FlowResultType.CREATE_ENTRY
|
||||
@ -97,6 +119,8 @@ async def test_errors(
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_MAC: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_SSL: True,
|
||||
CONF_VERIFY_SSL: False,
|
||||
}
|
||||
assert "result" in result3
|
||||
assert result3["result"].unique_id == "12345"
|
||||
@ -124,6 +148,8 @@ async def test_duplicate_updates_existing_entry(
|
||||
{
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_SSL: True,
|
||||
CONF_VERIFY_SSL: True,
|
||||
},
|
||||
)
|
||||
|
||||
@ -133,6 +159,8 @@ async def test_duplicate_updates_existing_entry(
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_MAC: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_SSL: True,
|
||||
CONF_VERIFY_SSL: True,
|
||||
}
|
||||
|
||||
assert len(mock_fully_kiosk_config_flow.getDeviceInfo.mock_calls) == 1
|
||||
@ -161,6 +189,8 @@ async def test_dhcp_discovery_updates_entry(
|
||||
CONF_HOST: "127.0.0.2",
|
||||
CONF_PASSWORD: "mocked-password",
|
||||
CONF_MAC: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
}
|
||||
|
||||
|
||||
@ -212,6 +242,8 @@ async def test_mqtt_discovery_flow(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
},
|
||||
)
|
||||
|
||||
@ -222,6 +254,8 @@ async def test_mqtt_discovery_flow(
|
||||
CONF_HOST: "192.168.1.234",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_MAC: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
}
|
||||
assert "result" in confirmResult
|
||||
assert confirmResult["result"].unique_id == "12345"
|
||||
|
@ -9,7 +9,13 @@ import pytest
|
||||
from homeassistant.components.fully_kiosk.const import DOMAIN
|
||||
from homeassistant.components.fully_kiosk.entity import valid_global_mac_address
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_MAC,
|
||||
CONF_PASSWORD,
|
||||
CONF_SSL,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
@ -92,6 +98,8 @@ async def test_multiple_kiosk_with_empty_mac(
|
||||
CONF_HOST: "127.0.0.1",
|
||||
CONF_PASSWORD: "mocked-password",
|
||||
CONF_MAC: "",
|
||||
CONF_SSL: False,
|
||||
CONF_VERIFY_SSL: False,
|
||||
},
|
||||
unique_id="111111",
|
||||
)
|
||||
@ -105,6 +113,8 @@ async def test_multiple_kiosk_with_empty_mac(
|
||||
CONF_HOST: "127.0.0.2",
|
||||
CONF_PASSWORD: "mocked-password",
|
||||
CONF_MAC: "",
|
||||
CONF_SSL: True,
|
||||
CONF_VERIFY_SSL: False,
|
||||
},
|
||||
unique_id="22222",
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user