IntelliFire Config API Token Config Update (#68134)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
73a368c242
commit
4d09078114
11 changed files with 338 additions and 69 deletions
|
@ -1,11 +1,14 @@
|
|||
"""The IntelliFire integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from intellifire4py import IntellifireAsync
|
||||
from aiohttp import ClientConnectionError
|
||||
from intellifire4py import IntellifireAsync, IntellifireControlAsync
|
||||
from intellifire4py.exceptions import LoginException
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, Platform
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .coordinator import IntellifireDataUpdateCoordinator
|
||||
|
@ -17,17 +20,35 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
"""Set up IntelliFire from a config entry."""
|
||||
LOGGER.debug("Setting up config entry: %s", entry.unique_id)
|
||||
|
||||
# Define the API Object
|
||||
api_object = IntellifireAsync(entry.data[CONF_HOST])
|
||||
if CONF_USERNAME not in entry.data:
|
||||
LOGGER.debug("Old config entry format detected: %s", entry.unique_id)
|
||||
raise ConfigEntryAuthFailed
|
||||
|
||||
# Define the API Objects
|
||||
read_object = IntellifireAsync(entry.data[CONF_HOST])
|
||||
ift_control = IntellifireControlAsync(
|
||||
fireplace_ip=entry.data[CONF_HOST],
|
||||
)
|
||||
try:
|
||||
await ift_control.login(
|
||||
username=entry.data[CONF_USERNAME],
|
||||
password=entry.data[CONF_PASSWORD],
|
||||
)
|
||||
except (ConnectionError, ClientConnectionError) as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
except LoginException as err:
|
||||
raise ConfigEntryAuthFailed(err) from err
|
||||
|
||||
finally:
|
||||
await ift_control.close()
|
||||
|
||||
# Define the update coordinator
|
||||
coordinator = IntellifireDataUpdateCoordinator(
|
||||
hass=hass,
|
||||
api=api_object,
|
||||
hass=hass, read_api=read_object, control_api=ift_control
|
||||
)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
|
|
@ -164,4 +164,4 @@ class IntellifireBinarySensor(IntellifireEntity, BinarySensorEntity):
|
|||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Use this to get the correct value."""
|
||||
return self.entity_description.value_fn(self.coordinator.api.data)
|
||||
return self.entity_description.value_fn(self.coordinator.read_api.data)
|
||||
|
|
|
@ -5,12 +5,17 @@ from dataclasses import dataclass
|
|||
from typing import Any
|
||||
|
||||
from aiohttp import ClientConnectionError
|
||||
from intellifire4py import AsyncUDPFireplaceFinder, IntellifireAsync
|
||||
from intellifire4py import (
|
||||
AsyncUDPFireplaceFinder,
|
||||
IntellifireAsync,
|
||||
IntellifireControlAsync,
|
||||
)
|
||||
from intellifire4py.exceptions import LoginException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.dhcp import DhcpServiceInfo
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
@ -48,9 +53,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
def __init__(self):
|
||||
"""Initialize the Config Flow Handler."""
|
||||
self._config_context = {}
|
||||
self._host: str = ""
|
||||
self._serial: str = ""
|
||||
self._not_configured_hosts: list[DiscoveredHostInfo] = []
|
||||
self._discovered_host: DiscoveredHostInfo
|
||||
self._reauth_needed: DiscoveredHostInfo
|
||||
|
||||
async def _find_fireplaces(self):
|
||||
"""Perform UDP discovery."""
|
||||
|
@ -71,31 +78,102 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
LOGGER.debug("Configured Hosts: %s", configured_hosts)
|
||||
LOGGER.debug("Not Configured Hosts: %s", self._not_configured_hosts)
|
||||
|
||||
async def _async_validate_and_create_entry(self, host: str) -> FlowResult:
|
||||
"""Validate and create the entry."""
|
||||
self._async_abort_entries_match({CONF_HOST: host})
|
||||
serial = await validate_host_input(host)
|
||||
await self.async_set_unique_id(serial, raise_on_progress=False)
|
||||
self._abort_if_unique_id_configured(updates={CONF_HOST: host})
|
||||
return self.async_create_entry(
|
||||
title=f"Fireplace {serial}",
|
||||
data={CONF_HOST: host},
|
||||
async def validate_api_access_and_create_or_update(
|
||||
self, *, host: str, username: str, password: str, serial: str
|
||||
):
|
||||
"""Validate username/password against api."""
|
||||
ift_control = IntellifireControlAsync(fireplace_ip=host)
|
||||
|
||||
LOGGER.debug("Attempting login to iftapi with: %s", username)
|
||||
# This can throw an error which will be handled above
|
||||
try:
|
||||
await ift_control.login(username=username, password=password)
|
||||
await ift_control.get_username()
|
||||
finally:
|
||||
await ift_control.close()
|
||||
|
||||
data = {CONF_HOST: host, CONF_PASSWORD: password, CONF_USERNAME: username}
|
||||
|
||||
# Update or Create
|
||||
existing_entry = await self.async_set_unique_id(serial)
|
||||
if existing_entry:
|
||||
self.hass.config_entries.async_update_entry(existing_entry, data=data)
|
||||
await self.hass.config_entries.async_reload(existing_entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
return self.async_create_entry(title=f"Fireplace {serial}", data=data)
|
||||
|
||||
async def async_step_api_config(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Configure API access."""
|
||||
|
||||
errors = {}
|
||||
control_schema = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
}
|
||||
)
|
||||
|
||||
if user_input is not None:
|
||||
|
||||
control_schema = vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_USERNAME, default=user_input.get(CONF_USERNAME, "")
|
||||
): str,
|
||||
vol.Required(
|
||||
CONF_PASSWORD, default=user_input.get(CONF_PASSWORD, "")
|
||||
): str,
|
||||
}
|
||||
)
|
||||
|
||||
if user_input[CONF_USERNAME] != "":
|
||||
try:
|
||||
return await self.validate_api_access_and_create_or_update(
|
||||
host=self._host,
|
||||
username=user_input[CONF_USERNAME],
|
||||
password=user_input[CONF_PASSWORD],
|
||||
serial=self._serial,
|
||||
)
|
||||
|
||||
except (ConnectionError, ClientConnectionError):
|
||||
errors["base"] = "iftapi_connect"
|
||||
LOGGER.info("ERROR: iftapi_connect")
|
||||
except LoginException:
|
||||
errors["base"] = "api_error"
|
||||
LOGGER.info("ERROR: api_error")
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="api_config", errors=errors, data_schema=control_schema
|
||||
)
|
||||
|
||||
async def _async_validate_ip_and_continue(self, host: str) -> FlowResult:
|
||||
"""Validate local config and continue."""
|
||||
self._async_abort_entries_match({CONF_HOST: host})
|
||||
self._serial = await validate_host_input(host)
|
||||
await self.async_set_unique_id(self._serial, raise_on_progress=False)
|
||||
self._abort_if_unique_id_configured(updates={CONF_HOST: host})
|
||||
# Store current data and jump to next stage
|
||||
self._host = host
|
||||
|
||||
return await self.async_step_api_config()
|
||||
|
||||
async def async_step_manual_device_entry(self, user_input=None):
|
||||
"""Handle manual input of local IP configuration."""
|
||||
LOGGER.debug("STEP: manual_device_entry")
|
||||
errors = {}
|
||||
host = user_input.get(CONF_HOST) if user_input else None
|
||||
self._host = user_input.get(CONF_HOST) if user_input else None
|
||||
if user_input is not None:
|
||||
try:
|
||||
return await self._async_validate_and_create_entry(host)
|
||||
return await self._async_validate_ip_and_continue(self._host)
|
||||
except (ConnectionError, ClientConnectionError):
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="manual_device_entry",
|
||||
errors=errors,
|
||||
data_schema=vol.Schema({vol.Required(CONF_HOST, default=host): str}),
|
||||
data_schema=vol.Schema({vol.Required(CONF_HOST, default=self._host): str}),
|
||||
)
|
||||
|
||||
async def async_step_pick_device(
|
||||
|
@ -103,15 +181,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
) -> FlowResult:
|
||||
"""Pick which device to configure."""
|
||||
errors = {}
|
||||
LOGGER.debug("STEP: pick_device")
|
||||
|
||||
if user_input is not None:
|
||||
if user_input[CONF_HOST] == MANUAL_ENTRY_STRING:
|
||||
return await self.async_step_manual_device_entry()
|
||||
|
||||
try:
|
||||
return await self._async_validate_and_create_entry(
|
||||
user_input[CONF_HOST]
|
||||
)
|
||||
return await self._async_validate_ip_and_continue(user_input[CONF_HOST])
|
||||
except (ConnectionError, ClientConnectionError):
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
|
@ -135,30 +212,44 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
# Launch fireplaces discovery
|
||||
await self._find_fireplaces()
|
||||
|
||||
LOGGER.debug("STEP: user")
|
||||
if self._not_configured_hosts:
|
||||
LOGGER.debug("Running Step: pick_device")
|
||||
return await self.async_step_pick_device()
|
||||
LOGGER.debug("Running Step: manual_device_entry")
|
||||
return await self.async_step_manual_device_entry()
|
||||
|
||||
async def async_step_reauth(self, user_input=None):
|
||||
"""Perform reauth upon an API authentication error."""
|
||||
LOGGER.debug("STEP: reauth")
|
||||
entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
|
||||
|
||||
# populate the expected vars
|
||||
self._serial = entry.unique_id
|
||||
self._host = entry.data[CONF_HOST]
|
||||
|
||||
placeholders = {CONF_HOST: self._host, "serial": self._serial}
|
||||
self.context["title_placeholders"] = placeholders
|
||||
return await self.async_step_api_config()
|
||||
|
||||
async def async_step_dhcp(self, discovery_info: DhcpServiceInfo) -> FlowResult:
|
||||
"""Handle DHCP Discovery."""
|
||||
|
||||
LOGGER.debug("STEP: dhcp")
|
||||
# Run validation logic on ip
|
||||
host = discovery_info.ip
|
||||
|
||||
self._async_abort_entries_match({CONF_HOST: host})
|
||||
try:
|
||||
serial = await validate_host_input(host)
|
||||
self._serial = await validate_host_input(host)
|
||||
except (ConnectionError, ClientConnectionError):
|
||||
return self.async_abort(reason="not_intellifire_device")
|
||||
|
||||
await self.async_set_unique_id(serial)
|
||||
await self.async_set_unique_id(self._serial)
|
||||
self._abort_if_unique_id_configured(updates={CONF_HOST: host})
|
||||
self._discovered_host = DiscoveredHostInfo(ip=host, serial=serial)
|
||||
self._discovered_host = DiscoveredHostInfo(ip=host, serial=self._serial)
|
||||
|
||||
placeholders = {CONF_HOST: host, "serial": serial}
|
||||
placeholders = {CONF_HOST: host, "serial": self._serial}
|
||||
self.context["title_placeholders"] = placeholders
|
||||
self._set_confirm_only()
|
||||
|
||||
|
@ -167,6 +258,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
async def async_step_dhcp_confirm(self, user_input=None):
|
||||
"""Attempt to confirm."""
|
||||
|
||||
LOGGER.debug("STEP: dhcp_confirm")
|
||||
# Add the hosts one by one
|
||||
host = self._discovered_host.ip
|
||||
serial = self._discovered_host.serial
|
||||
|
|
|
@ -6,3 +6,5 @@ import logging
|
|||
DOMAIN = "intellifire"
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
CONF_SERIAL = "serial"
|
||||
|
|
|
@ -5,7 +5,11 @@ from datetime import timedelta
|
|||
|
||||
from aiohttp import ClientConnectionError
|
||||
from async_timeout import timeout
|
||||
from intellifire4py import IntellifireAsync, IntellifirePollData
|
||||
from intellifire4py import (
|
||||
IntellifireAsync,
|
||||
IntellifireControlAsync,
|
||||
IntellifirePollData,
|
||||
)
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
|
@ -17,7 +21,12 @@ from .const import DOMAIN, LOGGER
|
|||
class IntellifireDataUpdateCoordinator(DataUpdateCoordinator[IntellifirePollData]):
|
||||
"""Class to manage the polling of the fireplace API."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, api: IntellifireAsync) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
read_api: IntellifireAsync,
|
||||
control_api: IntellifireControlAsync,
|
||||
) -> None:
|
||||
"""Initialize the Coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
|
@ -25,21 +34,27 @@ class IntellifireDataUpdateCoordinator(DataUpdateCoordinator[IntellifirePollData
|
|||
name=DOMAIN,
|
||||
update_interval=timedelta(seconds=15),
|
||||
)
|
||||
self._api = api
|
||||
self._read_api = read_api
|
||||
self._control_api = control_api
|
||||
|
||||
async def _async_update_data(self) -> IntellifirePollData:
|
||||
LOGGER.debug("Calling update loop on IntelliFire")
|
||||
async with timeout(100):
|
||||
try:
|
||||
await self._api.poll()
|
||||
await self._read_api.poll()
|
||||
except (ConnectionError, ClientConnectionError) as exception:
|
||||
raise UpdateFailed from exception
|
||||
return self._api.data
|
||||
return self._read_api.data
|
||||
|
||||
@property
|
||||
def api(self) -> IntellifireAsync:
|
||||
"""Return the API pointer."""
|
||||
return self._api
|
||||
def read_api(self) -> IntellifireAsync:
|
||||
"""Return the Status API pointer."""
|
||||
return self._read_api
|
||||
|
||||
@property
|
||||
def control_api(self) -> IntellifireControlAsync:
|
||||
"""Return the control API."""
|
||||
return self._control_api
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
|
@ -48,6 +63,7 @@ class IntellifireDataUpdateCoordinator(DataUpdateCoordinator[IntellifirePollData
|
|||
manufacturer="Hearth and Home",
|
||||
model="IFT-WFM",
|
||||
name="IntelliFire Fireplace",
|
||||
identifiers={("IntelliFire", f"{self.api.data.serial}]")},
|
||||
sw_version=self.api.data.fw_ver_str,
|
||||
identifiers={("IntelliFire", f"{self.read_api.data.serial}]")},
|
||||
sw_version=self.read_api.data.fw_ver_str,
|
||||
configuration_url=f"http://{self.read_api.ip}/poll",
|
||||
)
|
||||
|
|
|
@ -22,6 +22,6 @@ class IntellifireEntity(CoordinatorEntity[IntellifireDataUpdateCoordinator]):
|
|||
self.entity_description = description
|
||||
# Set the Display name the User will see
|
||||
self._attr_name = f"Fireplace {description.name}"
|
||||
self._attr_unique_id = f"{description.key}_{coordinator.api.data.serial}"
|
||||
self._attr_unique_id = f"{description.key}_{coordinator.read_api.data.serial}"
|
||||
# Configure the Device Info
|
||||
self._attr_device_info = self.coordinator.device_info
|
||||
|
|
|
@ -150,4 +150,4 @@ class IntellifireSensor(IntellifireEntity, SensorEntity):
|
|||
@property
|
||||
def native_value(self) -> int | str | datetime | None:
|
||||
"""Return the state."""
|
||||
return self.entity_description.value_fn(self.coordinator.api.data)
|
||||
return self.entity_description.value_fn(self.coordinator.read_api.data)
|
||||
|
|
|
@ -5,23 +5,34 @@
|
|||
"manual_device_entry": {
|
||||
"description": "Local Configuration",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
"host": "[%key:common::config_flow::data::host%] (IP Address)"
|
||||
}
|
||||
},
|
||||
"api_config": {
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
},
|
||||
"dhcp_confirm": {
|
||||
"description": "Do you want to setup {host}\nSerial: {serial}?"
|
||||
},
|
||||
"pick_device": {
|
||||
"title": "Device Selection",
|
||||
"description": "The following IntelliFire devices were discovered. Please select which you wish to configure.",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"api_error": "Login failed",
|
||||
"iftapi_connect": "Error conecting to iftapi.net"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"not_intellifire_device": "Not an IntelliFire Device."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,26 +2,39 @@
|
|||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured",
|
||||
"not_intellifire_device": "Not an IntelliFire Device."
|
||||
"not_intellifire_device": "Not an IntelliFire Device.",
|
||||
"reauth_successful": "Re-authentication was successful"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect"
|
||||
"api_error": "Login failed",
|
||||
"cannot_connect": "Could not connect to a fireplace endpoint at url: http://{host}/poll\nVerify IP address and try again",
|
||||
"iftapi_connect": "Error conecting to iftapi.net"
|
||||
},
|
||||
"flow_title": "{serial} ({host})",
|
||||
"step": {
|
||||
"api_config": {
|
||||
"data": {
|
||||
"password": "Password",
|
||||
"username": "Username (Email)"
|
||||
},
|
||||
"description": "IntelliFire will need to reach out to [iftapi.net](https://iftapi.net/webaccess/login.html) in order to obtain an API key. Once it has obtained this API key, the rest of its interactions will occur completely within the local network. If the API key were to expire it would again need to reach out to https://iftapi.net/webaccess/login.html\n\nUsername and Password are the same information used in your IntelliFire Android/iOS application. ",
|
||||
"title": "IntelliFire - API Configuration"
|
||||
},
|
||||
"dhcp_confirm": {
|
||||
"description": "Do you want to setup {host}\nSerial: {serial}?"
|
||||
},
|
||||
"manual_device_entry": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
"host": "Host (IP Address)"
|
||||
},
|
||||
"description": "Local Configuration"
|
||||
},
|
||||
"pick_device": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
}
|
||||
},
|
||||
"description": "The following IntelliFire devices were discovered. Please select which you wish to configure.",
|
||||
"title": "Device Selection"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||
|
||||
from aiohttp.client_reqrep import ConnectionKey
|
||||
import pytest
|
||||
|
||||
|
||||
|
@ -49,3 +50,10 @@ def mock_intellifire_config_flow() -> Generator[None, MagicMock, None]:
|
|||
intellifire = intellifire_mock.return_value
|
||||
intellifire.data = data_mock
|
||||
yield intellifire
|
||||
|
||||
|
||||
def mock_api_connection_error() -> ConnectionError:
|
||||
"""Return a fake a ConnectionError for iftapi.net."""
|
||||
ret = ConnectionError()
|
||||
ret.args = [ConnectionKey("iftapi.net", 443, False, None, None, None, None)]
|
||||
return ret
|
||||
|
|
|
@ -1,17 +1,29 @@
|
|||
"""Test the IntelliFire config flow."""
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from intellifire4py.exceptions import LoginException
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import dhcp
|
||||
from homeassistant.components.intellifire.config_flow import MANUAL_ENTRY_STRING
|
||||
from homeassistant.components.intellifire.const import DOMAIN
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
RESULT_TYPE_CREATE_ENTRY,
|
||||
RESULT_TYPE_FORM,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.intellifire.conftest import mock_api_connection_error
|
||||
|
||||
|
||||
@patch.multiple(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireControlAsync",
|
||||
login=AsyncMock(),
|
||||
get_username=AsyncMock(return_value="intellifire"),
|
||||
)
|
||||
async def test_no_discovery(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
|
@ -36,12 +48,31 @@ async def test_no_discovery(
|
|||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "Fireplace 12345"
|
||||
assert result2["data"] == {CONF_HOST: "1.1.1.1"}
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["step_id"] == "api_config"
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: "test", CONF_PASSWORD: "AROONIE"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result3["title"] == "Fireplace 12345"
|
||||
assert result3["data"] == {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_USERNAME: "test",
|
||||
CONF_PASSWORD: "AROONIE",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@patch.multiple(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireControlAsync",
|
||||
login=AsyncMock(side_effect=mock_api_connection_error()),
|
||||
get_username=AsyncMock(return_value="intellifire"),
|
||||
)
|
||||
async def test_single_discovery(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
|
@ -56,16 +87,48 @@ async def test_single_discovery(
|
|||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_HOST: "192.168.1.69"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
print("Result:", result)
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: "test", CONF_PASSWORD: "AROONIE"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result3["type"] == RESULT_TYPE_FORM
|
||||
assert result3["errors"] == {"base": "iftapi_connect"}
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "Fireplace 12345"
|
||||
assert result2["data"] == {CONF_HOST: "192.168.1.69"}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
@patch.multiple(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireControlAsync",
|
||||
login=AsyncMock(side_effect=LoginException()),
|
||||
)
|
||||
async def test_single_discovery_loign_error(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_intellifire_config_flow: MagicMock,
|
||||
) -> None:
|
||||
"""Test single fireplace UDP discovery."""
|
||||
with patch(
|
||||
"homeassistant.components.intellifire.config_flow.AsyncUDPFireplaceFinder.search_fireplace",
|
||||
return_value=["192.168.1.69"],
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_HOST: "192.168.1.69"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: "test", CONF_PASSWORD: "AROONIE"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result3["type"] == RESULT_TYPE_FORM
|
||||
assert result3["errors"] == {"base": "api_error"}
|
||||
|
||||
|
||||
async def test_manual_entry(
|
||||
|
@ -73,7 +136,7 @@ async def test_manual_entry(
|
|||
mock_setup_entry: AsyncMock,
|
||||
mock_intellifire_config_flow: MagicMock,
|
||||
) -> None:
|
||||
"""Test for multiple firepalce discovery - involing a pick_device step."""
|
||||
"""Test for multiple Fireplace discovery - involving a pick_device step."""
|
||||
with patch(
|
||||
"homeassistant.components.intellifire.config_flow.AsyncUDPFireplaceFinder.search_fireplace",
|
||||
return_value=["192.168.1.69", "192.168.1.33", "192.168.169"],
|
||||
|
@ -107,15 +170,12 @@ async def test_multi_discovery(
|
|||
)
|
||||
|
||||
assert result["step_id"] == "pick_device"
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_HOST: "192.168.1.33"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result["step_id"] == "pick_device"
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_multi_discovery_cannot_connect(
|
||||
hass: HomeAssistant,
|
||||
|
@ -200,10 +260,56 @@ async def test_picker_already_discovered(
|
|||
CONF_HOST: "192.168.1.4",
|
||||
},
|
||||
)
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "Fireplace 12345"
|
||||
assert result2["data"] == {CONF_HOST: "192.168.1.4"}
|
||||
assert len(mock_setup_entry.mock_calls) == 2
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert len(mock_setup_entry.mock_calls) == 0
|
||||
|
||||
|
||||
@patch.multiple(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireControlAsync",
|
||||
login=AsyncMock(),
|
||||
get_username=AsyncMock(return_value="intellifire"),
|
||||
)
|
||||
async def test_reauth_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_intellifire_config_flow: MagicMock,
|
||||
) -> None:
|
||||
"""Test the reauth flow."""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
"host": "192.168.1.3",
|
||||
},
|
||||
title="Fireplace 1234",
|
||||
version=1,
|
||||
unique_id="4444",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": "reauth",
|
||||
"unique_id": entry.unique_id,
|
||||
"entry_id": entry.entry_id,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_config"
|
||||
|
||||
with patch(
|
||||
"homeassistant.config_entries.ConfigFlow.async_set_unique_id",
|
||||
return_value=entry,
|
||||
):
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: "test", CONF_PASSWORD: "AROONIE"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result3["type"] == RESULT_TYPE_ABORT
|
||||
|
||||
|
||||
async def test_dhcp_discovery_intellifire_device(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue