From b6f16f87a786b007e744b214974189e08d4b7e73 Mon Sep 17 00:00:00 2001
From: Jeef <jeeftor@users.noreply.github.com>
Date: Wed, 29 Jun 2022 09:51:39 -0600
Subject: [PATCH] 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>
---
 .../components/intellifire/__init__.py        | 54 ++++++++++++++++---
 .../components/intellifire/config_flow.py     | 35 ++++++------
 homeassistant/components/intellifire/const.py |  2 +
 .../components/intellifire/coordinator.py     | 48 +++++++++--------
 .../components/intellifire/manifest.json      |  2 +-
 .../components/intellifire/switch.py          | 25 ++++-----
 requirements_all.txt                          |  2 +-
 requirements_test_all.txt                     |  2 +-
 tests/components/intellifire/conftest.py      |  2 +-
 .../intellifire/test_config_flow.py           | 27 ++++++----
 10 files changed, 126 insertions(+), 73 deletions(-)

diff --git a/homeassistant/components/intellifire/__init__.py b/homeassistant/components/intellifire/__init__.py
index 83c6e05f572..034e74c2aa6 100644
--- a/homeassistant/components/intellifire/__init__.py
+++ b/homeassistant/components/intellifire/__init__.py
@@ -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()
diff --git a/homeassistant/components/intellifire/config_flow.py b/homeassistant/components/intellifire/config_flow.py
index df13795a4ed..23bd92b0715 100644
--- a/homeassistant/components/intellifire/config_flow.py
+++ b/homeassistant/components/intellifire/config_flow.py
@@ -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)
diff --git a/homeassistant/components/intellifire/const.py b/homeassistant/components/intellifire/const.py
index fe715c3ce8a..2e9a2fabc06 100644
--- a/homeassistant/components/intellifire/const.py
+++ b/homeassistant/components/intellifire/const.py
@@ -5,6 +5,8 @@ import logging
 
 DOMAIN = "intellifire"
 
+CONF_USER_ID = "user_id"
+
 LOGGER = logging.getLogger(__package__)
 
 CONF_SERIAL = "serial"
diff --git a/homeassistant/components/intellifire/coordinator.py b/homeassistant/components/intellifire/coordinator.py
index 9b74bd81653..39f197285d4 100644
--- a/homeassistant/components/intellifire/coordinator.py
+++ b/homeassistant/components/intellifire/coordinator.py
@@ -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",
         )
diff --git a/homeassistant/components/intellifire/manifest.json b/homeassistant/components/intellifire/manifest.json
index 388ce0c86cb..cd1a12a36bf 100644
--- a/homeassistant/components/intellifire/manifest.json
+++ b/homeassistant/components/intellifire/manifest.json
@@ -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"],
diff --git a/homeassistant/components/intellifire/switch.py b/homeassistant/components/intellifire/switch.py
index 9c196a59fd4..ef0363696c4 100644
--- a/homeassistant/components/intellifire/switch.py
+++ b/homeassistant/components/intellifire/switch.py
@@ -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:
diff --git a/requirements_all.txt b/requirements_all.txt
index d758df0a97c..064f686dc49 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -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
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index c2cf31965f6..f579b9395d9 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -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
diff --git a/tests/components/intellifire/conftest.py b/tests/components/intellifire/conftest.py
index 3f73834226c..8940acd9d8e 100644
--- a/tests/components/intellifire/conftest.py
+++ b/tests/components/intellifire/conftest.py
@@ -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
diff --git a/tests/components/intellifire/test_config_flow.py b/tests/components/intellifire/test_config_flow.py
index 2f48e645708..06fcbea5bfa 100644
--- a/tests/components/intellifire/test_config_flow.py
+++ b/tests/components/intellifire/test_config_flow.py
@@ -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,
-- 
GitLab