Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
37 changes: 34 additions & 3 deletions homeassistant/components/intellifire/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from intellifire4py import UnifiedFireplace
from intellifire4py.cloud_interface import IntelliFireCloudInterface
from intellifire4py.const import IntelliFireApiMode
from intellifire4py.model import IntelliFireCommonFireplaceData

from homeassistant.const import (
Expand Down Expand Up @@ -55,8 +56,8 @@ def _construct_common_data(
serial=entry.data[CONF_SERIAL],
api_key=entry.data[CONF_API_KEY],
ip_address=entry.data[CONF_IP_ADDRESS],
read_mode=entry.options[CONF_READ_MODE],
control_mode=entry.options[CONF_CONTROL_MODE],
read_mode=IntelliFireApiMode(entry.options[CONF_READ_MODE]),
control_mode=IntelliFireApiMode(entry.options[CONF_CONTROL_MODE]),
)


Expand Down Expand Up @@ -117,7 +118,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: IntellifireConfigEntry)
try:
fireplace: UnifiedFireplace = (
await UnifiedFireplace.build_fireplace_from_common(
_construct_common_data(entry)
_construct_common_data(entry),
polling_enabled=False,
)
)
LOGGER.debug("Waiting for Fireplace to Initialize")
Expand All @@ -139,9 +141,38 @@ async def async_setup_entry(hass: HomeAssistant, entry: IntellifireConfigEntry)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

entry.async_on_unload(entry.add_update_listener(async_update_options))

return True


async def async_update_options(
hass: HomeAssistant, entry: IntellifireConfigEntry
) -> None:
"""Handle options update."""
coordinator: IntellifireDataUpdateCoordinator = entry.runtime_data

new_read_mode = IntelliFireApiMode(entry.options[CONF_READ_MODE])
new_control_mode = IntelliFireApiMode(entry.options[CONF_CONTROL_MODE])

current_read_mode = coordinator.get_read_mode()
current_control_mode = coordinator.get_control_mode()

# Only update modes that actually changed
if new_read_mode != current_read_mode:
LOGGER.debug("Updating read mode: %s -> %s", current_read_mode, new_read_mode)
await coordinator.set_read_mode(new_read_mode)

if new_control_mode != current_control_mode:
LOGGER.debug(
"Updating control mode: %s -> %s", current_control_mode, new_control_mode
)
await coordinator.set_control_mode(new_control_mode)

# Refresh data with new mode settings
await coordinator.async_request_refresh()


async def _async_wait_for_initialization(
fireplace: UnifiedFireplace, timeout=STARTUP_TIMEOUT
):
Expand Down
92 changes: 91 additions & 1 deletion homeassistant/components/intellifire/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,26 @@
from intellifire4py.model import IntelliFireCommonFireplaceData
import voluptuous as vol

from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import (
SOURCE_REAUTH,
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import (
CONF_API_KEY,
CONF_HOST,
CONF_IP_ADDRESS,
CONF_PASSWORD,
CONF_USERNAME,
)
from homeassistant.core import callback
from homeassistant.helpers import selector
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo

from .const import (
API_MODE_CLOUD,
API_MODE_LOCAL,
CONF_AUTH_COOKIE,
CONF_CONTROL_MODE,
Expand Down Expand Up @@ -260,3 +269,84 @@ async def async_step_dhcp(
return self.async_abort(reason="not_intellifire_device")

return await self.async_step_cloud_api()

@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
"""Create the options flow."""
return IntelliFireOptionsFlowHandler()


class IntelliFireOptionsFlowHandler(OptionsFlow):
"""Options flow for IntelliFire component."""

async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage the options."""
errors: dict[str, str] = {}

if user_input is not None:
# Validate connectivity for requested modes
coordinator = self.config_entry.runtime_data
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_disabled"
if (
user_input[CONF_READ_MODE] == API_MODE_CLOUD
and not fireplace.cloud_connectivity
):
errors[CONF_READ_MODE] = "cloud_disabled"
if (
user_input[CONF_CONTROL_MODE] == API_MODE_LOCAL
and not fireplace.local_connectivity
):
errors[CONF_CONTROL_MODE] = "local_disabled"
if (
user_input[CONF_CONTROL_MODE] == API_MODE_CLOUD
and not fireplace.cloud_connectivity
):
errors[CONF_CONTROL_MODE] = "cloud_disabled"

if not errors:
return self.async_create_entry(title="", data=user_input)

existing_read = self.config_entry.options.get(CONF_READ_MODE, API_MODE_LOCAL)
existing_control = self.config_entry.options.get(
CONF_CONTROL_MODE, API_MODE_LOCAL
)

cloud_local_options = selector.SelectSelectorConfig(
options=[
selector.SelectOptionDict(value=API_MODE_LOCAL, label="Local"),
selector.SelectOptionDict(value=API_MODE_CLOUD, label="Cloud"),
]
)

return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Required(
CONF_READ_MODE,
default=user_input.get(CONF_READ_MODE, existing_read)
if user_input
else existing_read,
): selector.SelectSelector(cloud_local_options),
vol.Required(
CONF_CONTROL_MODE,
default=user_input.get(CONF_CONTROL_MODE, existing_control)
if user_input
else existing_control,
): selector.SelectSelector(cloud_local_options),
}
),
errors=errors,
)
18 changes: 18 additions & 0 deletions homeassistant/components/intellifire/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from datetime import timedelta

from intellifire4py import UnifiedFireplace
from intellifire4py.const import IntelliFireApiMode
from intellifire4py.control import IntelliFireController
from intellifire4py.model import IntelliFirePollData
from intellifire4py.read import IntelliFireDataProvider
Expand Down Expand Up @@ -51,7 +52,24 @@ def control_api(self) -> IntelliFireController:
"""Return the control API."""
return self.fireplace.control_api

def get_read_mode(self) -> IntelliFireApiMode:
"""Return the current read mode."""
return self.fireplace.read_mode

async def set_read_mode(self, mode: IntelliFireApiMode) -> None:
"""Set the read mode between cloud/local."""
await self.fireplace.set_read_mode(mode)

def get_control_mode(self) -> IntelliFireApiMode:
"""Return the current control mode."""
return self.fireplace.control_mode

async def set_control_mode(self, mode: IntelliFireApiMode) -> None:
"""Set the control mode between cloud/local."""
await self.fireplace.set_control_mode(mode)

async def _async_update_data(self) -> IntelliFirePollData:
await self.fireplace.perform_poll()
return self.fireplace.data

@property
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/intellifire/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["intellifire4py"],
"requirements": ["intellifire4py==4.3.1"]
"requirements": ["intellifire4py==4.4.0"]
}
12 changes: 12 additions & 0 deletions homeassistant/components/intellifire/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ def _uptime_to_timestamp(


INTELLIFIRE_SENSORS: tuple[IntellifireSensorEntityDescription, ...] = (
IntellifireSensorEntityDescription(
key="read_mode",
translation_key="read_mode",
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda coordinator: coordinator.get_read_mode().value,
),
IntellifireSensorEntityDescription(
key="control_mode",
translation_key="control_mode",
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda coordinator: coordinator.get_control_mode().value,
),
IntellifireSensorEntityDescription(
key="flame_height",
translation_key="flame_height",
Expand Down
24 changes: 24 additions & 0 deletions homeassistant/components/intellifire/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
"connection_quality": {
"name": "Connection quality"
},
"control_mode": {
"name": "Control mode"
},
"downtime": {
"name": "Downtime"
},
Expand All @@ -115,6 +118,9 @@
"ipv4_address": {
"name": "IP address"
},
"read_mode": {
"name": "Read mode"
},
"target_temp": {
"name": "Target temperature"
},
Expand All @@ -133,5 +139,23 @@
"name": "Pilot light"
}
}
},
"options": {
"error": {
"cloud_disabled": "Cloud connectivity is not available",
"local_disabled": "Local connectivity is not available"
},
"step": {
"init": {
"data": {
"cloud_control": "Control mode",
"cloud_read": "Read mode"
},
"data_description": {
"cloud_control": "Select endpoint to use for controlling device",
"cloud_read": "Select endpoint to use for reading data"
}
}
}
}
}
2 changes: 1 addition & 1 deletion requirements_all.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion requirements_test_all.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions tests/components/intellifire/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ def mock_fp(mock_common_data_local) -> Generator[AsyncMock]:
mock_instance.set_read_mode = AsyncMock()
mock_instance.set_control_mode = AsyncMock()

mock_instance.perform_poll = AsyncMock()

mock_instance.async_validate_connectivity = AsyncMock(
return_value=(True, False)
)
Expand Down
Loading