Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions homeassistant/components/intellifire/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import asyncio

import aiohttp
from intellifire4py import UnifiedFireplace
from intellifire4py.cloud_interface import IntelliFireCloudInterface
from intellifire4py.const import IntelliFireApiMode
Expand Down Expand Up @@ -153,6 +154,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: IntellifireConfigEntry)
raise ConfigEntryNotReady(
"Initialization of fireplace timed out after 10 minutes"
) from err
except (aiohttp.ClientConnectionError, ConnectionError) as err:
raise ConfigEntryNotReady(
"Error communicating with fireplace during initialization"
) from err

# Construct coordinator
data_update_coordinator = IntellifireDataUpdateCoordinator(hass, entry, fireplace)
Expand Down
58 changes: 30 additions & 28 deletions homeassistant/components/intellifire/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,34 +287,36 @@ async def async_step_init(
errors: dict[str, str] = {}

if user_input is not None:
# Validate connectivity for requested modes if runtime data is available
coordinator = self.config_entry.runtime_data
if coordinator is not None:
fireplace = coordinator.fireplace

# Refresh connectivity status before validating
await fireplace.async_validate_connectivity()

if (
user_input[CONF_READ_MODE] == API_MODE_LOCAL
and not fireplace.local_connectivity
):
errors[CONF_READ_MODE] = "local_unavailable"
if (
user_input[CONF_READ_MODE] == API_MODE_CLOUD
and not fireplace.cloud_connectivity
):
errors[CONF_READ_MODE] = "cloud_unavailable"
if (
user_input[CONF_CONTROL_MODE] == API_MODE_LOCAL
and not fireplace.local_connectivity
):
errors[CONF_CONTROL_MODE] = "local_unavailable"
if (
user_input[CONF_CONTROL_MODE] == API_MODE_CLOUD
and not fireplace.cloud_connectivity
):
errors[CONF_CONTROL_MODE] = "cloud_unavailable"
if (
coordinator := getattr(self.config_entry, "runtime_data", None)
) is None:
return self.async_abort(reason="not_loaded")
Comment thread
jeeftor marked this conversation as resolved.
Outdated

fireplace = coordinator.fireplace

# Refresh connectivity status before validating
await fireplace.async_validate_connectivity()

if (
user_input[CONF_READ_MODE] == API_MODE_LOCAL
and not fireplace.local_connectivity
):
errors[CONF_READ_MODE] = "local_unavailable"
if (
user_input[CONF_READ_MODE] == API_MODE_CLOUD
and not fireplace.cloud_connectivity
):
errors[CONF_READ_MODE] = "cloud_unavailable"
if (
user_input[CONF_CONTROL_MODE] == API_MODE_LOCAL
and not fireplace.local_connectivity
):
errors[CONF_CONTROL_MODE] = "local_unavailable"
if (
user_input[CONF_CONTROL_MODE] == API_MODE_CLOUD
and not fireplace.cloud_connectivity
):
errors[CONF_CONTROL_MODE] = "cloud_unavailable"

Comment thread
jeeftor marked this conversation as resolved.
Outdated
if not errors:
return self.async_create_entry(title="", data=user_input)
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/intellifire/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@
}
},
"options": {
"abort": {
"not_loaded": "The IntelliFire integration is not loaded."
},
"error": {
"cloud_unavailable": "Cloud connectivity is not available",
"local_unavailable": "Local connectivity is not available"
Expand Down
29 changes: 29 additions & 0 deletions tests/components/intellifire/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,35 @@ async def test_options_flow(
}


async def test_options_flow_not_loaded_on_submit(
hass: HomeAssistant,
mock_config_entry_current: MockConfigEntry,
) -> None:
"""Test options flow aborts when runtime data is missing on submit."""
config_entry = MockConfigEntry(
domain=DOMAIN,
version=mock_config_entry_current.version,
minor_version=mock_config_entry_current.minor_version,
data=dict(mock_config_entry_current.data),
options=dict(mock_config_entry_current.options),
unique_id=mock_config_entry_current.unique_id,
state=config_entries.ConfigEntryState.SETUP_ERROR,
)
config_entry.add_to_hass(hass)

result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"

result = await hass.config_entries.options.async_configure(
result["flow_id"],
{CONF_READ_MODE: API_MODE_CLOUD, CONF_CONTROL_MODE: API_MODE_LOCAL},
)

assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "not_loaded"


async def test_options_flow_local_read_unavailable(
hass: HomeAssistant,
mock_config_entry_current: MockConfigEntry,
Expand Down
20 changes: 14 additions & 6 deletions tests/components/intellifire/test_init.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Test the IntelliFire config flow."""

from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, MagicMock, patch

import aiohttp
from intellifire4py.const import IntelliFireApiMode
import pytest

from homeassistant.components.intellifire import CONF_USER_ID
from homeassistant.components.intellifire.const import (
Expand Down Expand Up @@ -159,22 +161,28 @@ async def test_init_with_no_username(hass: HomeAssistant, mock_apis_single_fp) -
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR


async def test_connectivity_bad(
@pytest.mark.parametrize(
"setup_error",
[aiohttp.ClientConnectionError, ConnectionError, TimeoutError],
)
async def test_connectivity_error_during_setup_retries(
hass: HomeAssistant,
mock_config_entry_current,
mock_apis_single_fp,
mock_config_entry_current: MockConfigEntry,
mock_apis_single_fp: tuple[AsyncMock, AsyncMock, MagicMock],
setup_error: type[Exception],
) -> None:
"""Test a timeout error on the setup flow."""
"""Test a connection error during setup retries the config entry."""

with patch(
"homeassistant.components.intellifire.UnifiedFireplace.build_fireplace_from_common",
new_callable=AsyncMock,
side_effect=TimeoutError,
side_effect=setup_error,
):
mock_config_entry_current.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry_current.entry_id)

await hass.async_block_till_done()
assert mock_config_entry_current.state is ConfigEntryState.SETUP_RETRY
assert len(hass.states.async_all()) == 0


Expand Down
Loading