mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Bump intellifire4py to 2.0.0 (#72563)
* Enable Flame/Pilot switch * Enable Flame/Pilot switch * Update homeassistant/components/intellifire/switch.py Co-authored-by: J. Nick Koston <nick@koston.org> * Update homeassistant/components/intellifire/switch.py Thats a great fix! Co-authored-by: J. Nick Koston <nick@koston.org> * write not update * fixed forced upates * removed data field * Refactor to support update to backing library * pre-push-ninja-style * moving over * fixed coverage * removed tuple junk * re-added description * Update homeassistant/components/intellifire/translations/en.json Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io> * adressing PR comments * actually store generated values * Update homeassistant/components/intellifire/__init__.py Way better option! Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
fa678d0408
commit
b6f16f87a7
@ -2,15 +2,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from aiohttp import ClientConnectionError
|
||||
from intellifire4py import IntellifireAsync, IntellifireControlAsync
|
||||
from intellifire4py import IntellifireControlAsync
|
||||
from intellifire4py.exceptions import LoginException
|
||||
from intellifire4py.intellifire import IntellifireAPICloud, IntellifireAPILocal
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .const import CONF_USER_ID, DOMAIN, LOGGER
|
||||
from .coordinator import IntellifireDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH]
|
||||
@ -24,8 +31,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
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],
|
||||
)
|
||||
@ -42,9 +47,46 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
finally:
|
||||
await ift_control.close()
|
||||
|
||||
# Extract API Key and User_ID from ift_control
|
||||
# Eventually this will migrate to using IntellifireAPICloud
|
||||
|
||||
if CONF_USER_ID not in entry.data or CONF_API_KEY not in entry.data:
|
||||
LOGGER.info(
|
||||
"Updating intellifire config entry for %s with api information",
|
||||
entry.unique_id,
|
||||
)
|
||||
cloud_api = IntellifireAPICloud()
|
||||
await cloud_api.login(
|
||||
username=entry.data[CONF_USERNAME],
|
||||
password=entry.data[CONF_PASSWORD],
|
||||
)
|
||||
api_key = cloud_api.get_fireplace_api_key()
|
||||
user_id = cloud_api.get_user_id()
|
||||
# Update data entry
|
||||
hass.config_entries.async_update_entry(
|
||||
entry,
|
||||
data={
|
||||
**entry.data,
|
||||
CONF_API_KEY: api_key,
|
||||
CONF_USER_ID: user_id,
|
||||
},
|
||||
)
|
||||
|
||||
else:
|
||||
api_key = entry.data[CONF_API_KEY]
|
||||
user_id = entry.data[CONF_USER_ID]
|
||||
|
||||
# Instantiate local control
|
||||
api = IntellifireAPILocal(
|
||||
fireplace_ip=entry.data[CONF_HOST],
|
||||
api_key=api_key,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
# Define the update coordinator
|
||||
coordinator = IntellifireDataUpdateCoordinator(
|
||||
hass=hass, read_api=read_object, control_api=ift_control
|
||||
hass=hass,
|
||||
api=api,
|
||||
)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
@ -6,20 +6,17 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import ClientConnectionError
|
||||
from intellifire4py import (
|
||||
AsyncUDPFireplaceFinder,
|
||||
IntellifireAsync,
|
||||
IntellifireControlAsync,
|
||||
)
|
||||
from intellifire4py import AsyncUDPFireplaceFinder
|
||||
from intellifire4py.exceptions import LoginException
|
||||
from intellifire4py.intellifire import IntellifireAPICloud, IntellifireAPILocal
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.dhcp import DhcpServiceInfo
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .const import CONF_USER_ID, DOMAIN, LOGGER
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str})
|
||||
|
||||
@ -39,7 +36,8 @@ async def validate_host_input(host: str) -> str:
|
||||
|
||||
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
|
||||
"""
|
||||
api = IntellifireAsync(host)
|
||||
LOGGER.debug("Instantiating IntellifireAPI with host: [%s]", host)
|
||||
api = IntellifireAPILocal(fireplace_ip=host)
|
||||
await api.poll()
|
||||
serial = api.data.serial
|
||||
LOGGER.debug("Found a fireplace: %s", serial)
|
||||
@ -83,17 +81,20 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
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}
|
||||
ift_cloud = IntellifireAPICloud()
|
||||
await ift_cloud.login(username=username, password=password)
|
||||
api_key = ift_cloud.get_fireplace_api_key()
|
||||
user_id = ift_cloud.get_user_id()
|
||||
|
||||
data = {
|
||||
CONF_HOST: host,
|
||||
CONF_PASSWORD: password,
|
||||
CONF_USERNAME: username,
|
||||
CONF_API_KEY: api_key,
|
||||
CONF_USER_ID: user_id,
|
||||
}
|
||||
|
||||
# Update or Create
|
||||
existing_entry = await self.async_set_unique_id(serial)
|
||||
|
@ -5,6 +5,8 @@ import logging
|
||||
|
||||
DOMAIN = "intellifire"
|
||||
|
||||
CONF_USER_ID = "user_id"
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
CONF_SERIAL = "serial"
|
||||
|
@ -5,11 +5,8 @@ from datetime import timedelta
|
||||
|
||||
from aiohttp import ClientConnectionError
|
||||
from async_timeout import timeout
|
||||
from intellifire4py import (
|
||||
IntellifireAsync,
|
||||
IntellifireControlAsync,
|
||||
IntellifirePollData,
|
||||
)
|
||||
from intellifire4py import IntellifirePollData
|
||||
from intellifire4py.intellifire import IntellifireAPILocal
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
@ -24,8 +21,7 @@ class IntellifireDataUpdateCoordinator(DataUpdateCoordinator[IntellifirePollData
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
read_api: IntellifireAsync,
|
||||
control_api: IntellifireControlAsync,
|
||||
api: IntellifireAPILocal,
|
||||
) -> None:
|
||||
"""Initialize the Coordinator."""
|
||||
super().__init__(
|
||||
@ -34,27 +30,37 @@ class IntellifireDataUpdateCoordinator(DataUpdateCoordinator[IntellifirePollData
|
||||
name=DOMAIN,
|
||||
update_interval=timedelta(seconds=15),
|
||||
)
|
||||
self._read_api = read_api
|
||||
self._control_api = control_api
|
||||
self._api = api
|
||||
|
||||
async def _async_update_data(self) -> IntellifirePollData:
|
||||
LOGGER.debug("Calling update loop on IntelliFire")
|
||||
async with timeout(100):
|
||||
try:
|
||||
await self._read_api.poll()
|
||||
except (ConnectionError, ClientConnectionError) as exception:
|
||||
raise UpdateFailed from exception
|
||||
return self._read_api.data
|
||||
|
||||
if not self._api.is_polling_in_background:
|
||||
LOGGER.info("Starting Intellifire Background Polling Loop")
|
||||
await self._api.start_background_polling()
|
||||
|
||||
# Don't return uninitialized poll data
|
||||
async with timeout(15):
|
||||
try:
|
||||
await self._api.poll()
|
||||
except (ConnectionError, ClientConnectionError) as exception:
|
||||
raise UpdateFailed from exception
|
||||
|
||||
LOGGER.info("Failure Count %d", self._api.failed_poll_attempts)
|
||||
if self._api.failed_poll_attempts > 10:
|
||||
LOGGER.debug("Too many polling errors - raising exception")
|
||||
raise UpdateFailed
|
||||
|
||||
return self._api.data
|
||||
|
||||
@property
|
||||
def read_api(self) -> IntellifireAsync:
|
||||
def read_api(self) -> IntellifireAPILocal:
|
||||
"""Return the Status API pointer."""
|
||||
return self._read_api
|
||||
return self._api
|
||||
|
||||
@property
|
||||
def control_api(self) -> IntellifireControlAsync:
|
||||
def control_api(self) -> IntellifireAPILocal:
|
||||
"""Return the control API."""
|
||||
return self._control_api
|
||||
return self._api
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
@ -65,5 +71,5 @@ class IntellifireDataUpdateCoordinator(DataUpdateCoordinator[IntellifirePollData
|
||||
name="IntelliFire Fireplace",
|
||||
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",
|
||||
configuration_url=f"http://{self._api.fireplace_ip}/poll",
|
||||
)
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "IntelliFire",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/intellifire",
|
||||
"requirements": ["intellifire4py==1.0.2"],
|
||||
"requirements": ["intellifire4py==2.0.0"],
|
||||
"codeowners": ["@jeeftor"],
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["intellifire4py"],
|
||||
|
@ -5,7 +5,8 @@ from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from intellifire4py import IntellifireControlAsync, IntellifirePollData
|
||||
from intellifire4py import IntellifirePollData
|
||||
from intellifire4py.intellifire import IntellifireAPILocal
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@ -21,8 +22,8 @@ from .entity import IntellifireEntity
|
||||
class IntellifireSwitchRequiredKeysMixin:
|
||||
"""Mixin for required keys."""
|
||||
|
||||
on_fn: Callable[[IntellifireControlAsync], Awaitable]
|
||||
off_fn: Callable[[IntellifireControlAsync], Awaitable]
|
||||
on_fn: Callable[[IntellifireAPILocal], Awaitable]
|
||||
off_fn: Callable[[IntellifireAPILocal], Awaitable]
|
||||
value_fn: Callable[[IntellifirePollData], bool]
|
||||
|
||||
|
||||
@ -37,24 +38,16 @@ INTELLIFIRE_SWITCHES: tuple[IntellifireSwitchEntityDescription, ...] = (
|
||||
IntellifireSwitchEntityDescription(
|
||||
key="on_off",
|
||||
name="Flame",
|
||||
on_fn=lambda control_api: control_api.flame_on(
|
||||
fireplace=control_api.default_fireplace
|
||||
),
|
||||
off_fn=lambda control_api: control_api.flame_off(
|
||||
fireplace=control_api.default_fireplace
|
||||
),
|
||||
on_fn=lambda control_api: control_api.flame_on(),
|
||||
off_fn=lambda control_api: control_api.flame_off(),
|
||||
value_fn=lambda data: data.is_on,
|
||||
),
|
||||
IntellifireSwitchEntityDescription(
|
||||
key="pilot",
|
||||
name="Pilot Light",
|
||||
icon="mdi:fire-alert",
|
||||
on_fn=lambda control_api: control_api.pilot_on(
|
||||
fireplace=control_api.default_fireplace
|
||||
),
|
||||
off_fn=lambda control_api: control_api.pilot_off(
|
||||
fireplace=control_api.default_fireplace
|
||||
),
|
||||
on_fn=lambda control_api: control_api.pilot_on(),
|
||||
off_fn=lambda control_api: control_api.pilot_off(),
|
||||
value_fn=lambda data: data.pilot_on,
|
||||
),
|
||||
)
|
||||
@ -82,10 +75,12 @@ class IntellifireSwitch(IntellifireEntity, SwitchEntity):
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the switch."""
|
||||
await self.entity_description.on_fn(self.coordinator.control_api)
|
||||
await self.async_update_ha_state(force_refresh=True)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off the switch."""
|
||||
await self.entity_description.off_fn(self.coordinator.control_api)
|
||||
await self.async_update_ha_state(force_refresh=True)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
|
@ -894,7 +894,7 @@ influxdb==5.3.1
|
||||
insteon-frontend-home-assistant==0.1.1
|
||||
|
||||
# homeassistant.components.intellifire
|
||||
intellifire4py==1.0.2
|
||||
intellifire4py==2.0.0
|
||||
|
||||
# homeassistant.components.iotawatt
|
||||
iotawattpy==0.1.0
|
||||
|
@ -637,7 +637,7 @@ influxdb==5.3.1
|
||||
insteon-frontend-home-assistant==0.1.1
|
||||
|
||||
# homeassistant.components.intellifire
|
||||
intellifire4py==1.0.2
|
||||
intellifire4py==2.0.0
|
||||
|
||||
# homeassistant.components.iotawatt
|
||||
iotawattpy==0.1.0
|
||||
|
@ -44,7 +44,7 @@ def mock_intellifire_config_flow() -> Generator[None, MagicMock, None]:
|
||||
data_mock.serial = "12345"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireAsync",
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireAPILocal",
|
||||
autospec=True,
|
||||
) as intellifire_mock:
|
||||
intellifire = intellifire_mock.return_value
|
||||
|
@ -6,8 +6,8 @@ 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, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.components.intellifire.const import CONF_USER_ID, DOMAIN
|
||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
@ -20,9 +20,10 @@ from tests.components.intellifire.conftest import mock_api_connection_error
|
||||
|
||||
|
||||
@patch.multiple(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireControlAsync",
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireAPICloud",
|
||||
login=AsyncMock(),
|
||||
get_username=AsyncMock(return_value="intellifire"),
|
||||
get_user_id=MagicMock(return_value="intellifire"),
|
||||
get_fireplace_api_key=MagicMock(return_value="key"),
|
||||
)
|
||||
async def test_no_discovery(
|
||||
hass: HomeAssistant,
|
||||
@ -64,14 +65,17 @@ async def test_no_discovery(
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_USERNAME: "test",
|
||||
CONF_PASSWORD: "AROONIE",
|
||||
CONF_API_KEY: "key",
|
||||
CONF_USER_ID: "intellifire",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@patch.multiple(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireControlAsync",
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireAPICloud",
|
||||
login=AsyncMock(side_effect=mock_api_connection_error()),
|
||||
get_username=AsyncMock(return_value="intellifire"),
|
||||
get_user_id=MagicMock(return_value="intellifire"),
|
||||
get_fireplace_api_key=MagicMock(return_value="key"),
|
||||
)
|
||||
async def test_single_discovery(
|
||||
hass: HomeAssistant,
|
||||
@ -101,8 +105,10 @@ async def test_single_discovery(
|
||||
|
||||
|
||||
@patch.multiple(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireControlAsync",
|
||||
login=AsyncMock(side_effect=LoginException()),
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireAPICloud",
|
||||
login=AsyncMock(side_effect=LoginException),
|
||||
get_user_id=MagicMock(return_value="intellifire"),
|
||||
get_fireplace_api_key=MagicMock(return_value="key"),
|
||||
)
|
||||
async def test_single_discovery_loign_error(
|
||||
hass: HomeAssistant,
|
||||
@ -265,9 +271,10 @@ async def test_picker_already_discovered(
|
||||
|
||||
|
||||
@patch.multiple(
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireControlAsync",
|
||||
"homeassistant.components.intellifire.config_flow.IntellifireAPICloud",
|
||||
login=AsyncMock(),
|
||||
get_username=AsyncMock(return_value="intellifire"),
|
||||
get_user_id=MagicMock(return_value="intellifire"),
|
||||
get_fireplace_api_key=MagicMock(return_value="key"),
|
||||
)
|
||||
async def test_reauth_flow(
|
||||
hass: HomeAssistant,
|
||||
|
Loading…
x
Reference in New Issue
Block a user