mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Add Escea fireplace integration (#56039)
Co-authored-by: Teemu R. <tpr@iki.fi> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
bcc2be344a
commit
a1d5a4bc79
@ -309,6 +309,9 @@ omit =
|
||||
homeassistant/components/epson/media_player.py
|
||||
homeassistant/components/epsonworkforce/sensor.py
|
||||
homeassistant/components/eq3btsmart/climate.py
|
||||
homeassistant/components/escea/climate.py
|
||||
homeassistant/components/escea/discovery.py
|
||||
homeassistant/components/escea/__init__.py
|
||||
homeassistant/components/esphome/__init__.py
|
||||
homeassistant/components/esphome/binary_sensor.py
|
||||
homeassistant/components/esphome/button.py
|
||||
|
@ -309,6 +309,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/epson/ @pszafer
|
||||
/homeassistant/components/epsonworkforce/ @ThaStealth
|
||||
/homeassistant/components/eq3btsmart/ @rytilahti
|
||||
/homeassistant/components/escea/ @lazdavila
|
||||
/tests/components/escea/ @lazdavila
|
||||
/homeassistant/components/esphome/ @OttoWinter @jesserockz
|
||||
/tests/components/esphome/ @OttoWinter @jesserockz
|
||||
/homeassistant/components/evil_genius_labs/ @balloob
|
||||
|
22
homeassistant/components/escea/__init__.py
Normal file
22
homeassistant/components/escea/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
"""Platform for the Escea fireplace."""
|
||||
|
||||
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .discovery import async_start_discovery_service, async_stop_discovery_service
|
||||
|
||||
PLATFORMS = [CLIMATE_DOMAIN]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up from a config entry."""
|
||||
await async_start_discovery_service(hass)
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload the config entry and stop discovery process."""
|
||||
await async_stop_discovery_service(hass)
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
221
homeassistant/components/escea/climate.py
Normal file
221
homeassistant/components/escea/climate.py
Normal file
@ -0,0 +1,221 @@
|
||||
"""Support for the Escea Fireplace."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Coroutine
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from pescea import Controller
|
||||
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.components.climate.const import (
|
||||
FAN_AUTO,
|
||||
FAN_HIGH,
|
||||
FAN_LOW,
|
||||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DATA_DISCOVERY_SERVICE,
|
||||
DISPATCH_CONTROLLER_DISCONNECTED,
|
||||
DISPATCH_CONTROLLER_DISCOVERED,
|
||||
DISPATCH_CONTROLLER_RECONNECTED,
|
||||
DISPATCH_CONTROLLER_UPDATE,
|
||||
DOMAIN,
|
||||
ESCEA_FIREPLACE,
|
||||
ESCEA_MANUFACTURER,
|
||||
ICON,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
_ESCEA_FAN_TO_HA = {
|
||||
Controller.Fan.FLAME_EFFECT: FAN_LOW,
|
||||
Controller.Fan.FAN_BOOST: FAN_HIGH,
|
||||
Controller.Fan.AUTO: FAN_AUTO,
|
||||
}
|
||||
_HA_FAN_TO_ESCEA = {v: k for k, v in _ESCEA_FAN_TO_HA.items()}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Initialize an Escea Controller."""
|
||||
discovery_service = hass.data[DATA_DISCOVERY_SERVICE]
|
||||
|
||||
@callback
|
||||
def init_controller(ctrl: Controller) -> None:
|
||||
"""Register the controller device."""
|
||||
|
||||
_LOGGER.debug("Controller UID=%s discovered", ctrl.device_uid)
|
||||
|
||||
entity = ControllerEntity(ctrl)
|
||||
async_add_entities([entity])
|
||||
|
||||
# create any components not yet created
|
||||
for controller in discovery_service.controllers.values():
|
||||
init_controller(controller)
|
||||
|
||||
# connect to register any further components
|
||||
config_entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, DISPATCH_CONTROLLER_DISCOVERED, init_controller)
|
||||
)
|
||||
|
||||
|
||||
class ControllerEntity(ClimateEntity):
|
||||
"""Representation of Escea Controller."""
|
||||
|
||||
_attr_fan_modes = list(_HA_FAN_TO_ESCEA)
|
||||
_attr_has_entity_name = True
|
||||
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
|
||||
_attr_icon = ICON
|
||||
_attr_precision = PRECISION_WHOLE
|
||||
_attr_should_poll = False
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
|
||||
)
|
||||
_attr_target_temperature_step = PRECISION_WHOLE
|
||||
_attr_temperature_unit = TEMP_CELSIUS
|
||||
|
||||
def __init__(self, controller: Controller) -> None:
|
||||
"""Initialise ControllerDevice."""
|
||||
self._controller = controller
|
||||
|
||||
self._attr_min_temp = controller.min_temp
|
||||
self._attr_max_temp = controller.max_temp
|
||||
|
||||
self._attr_unique_id = controller.device_uid
|
||||
|
||||
# temporary assignment to get past mypy checker
|
||||
unique_id: str = controller.device_uid
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
manufacturer=ESCEA_MANUFACTURER,
|
||||
name=ESCEA_FIREPLACE,
|
||||
)
|
||||
|
||||
self._attr_available = True
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Call on adding to hass.
|
||||
|
||||
Registers for connect/disconnect/update events
|
||||
"""
|
||||
|
||||
@callback
|
||||
def controller_disconnected(ctrl: Controller, ex: Exception) -> None:
|
||||
"""Disconnected from controller."""
|
||||
if ctrl is not self._controller:
|
||||
return
|
||||
self.set_available(False, ex)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, DISPATCH_CONTROLLER_DISCONNECTED, controller_disconnected
|
||||
)
|
||||
)
|
||||
|
||||
@callback
|
||||
def controller_reconnected(ctrl: Controller) -> None:
|
||||
"""Reconnected to controller."""
|
||||
if ctrl is not self._controller:
|
||||
return
|
||||
self.set_available(True)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, DISPATCH_CONTROLLER_RECONNECTED, controller_reconnected
|
||||
)
|
||||
)
|
||||
|
||||
@callback
|
||||
def controller_update(ctrl: Controller) -> None:
|
||||
"""Handle controller data updates."""
|
||||
if ctrl is not self._controller:
|
||||
return
|
||||
self.async_write_ha_state()
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, DISPATCH_CONTROLLER_UPDATE, controller_update
|
||||
)
|
||||
)
|
||||
|
||||
@callback
|
||||
def set_available(self, available: bool, ex: Exception = None) -> None:
|
||||
"""Set availability for the controller."""
|
||||
if self._attr_available == available:
|
||||
return
|
||||
|
||||
if available:
|
||||
_LOGGER.debug("Reconnected controller %s ", self._controller.device_uid)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"Controller %s disconnected due to exception: %s",
|
||||
self._controller.device_uid,
|
||||
ex,
|
||||
)
|
||||
|
||||
self._attr_available = available
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
return HVACMode.HEAT if self._controller.is_on else HVACMode.OFF
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
"""Return the current temperature."""
|
||||
return self._controller.current_temp
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
"""Return the temperature we try to reach."""
|
||||
return self._controller.desired_temp
|
||||
|
||||
@property
|
||||
def fan_mode(self) -> str | None:
|
||||
"""Return the fan setting."""
|
||||
return _ESCEA_FAN_TO_HA[self._controller.fan]
|
||||
|
||||
async def wrap_and_catch(self, coro: Coroutine) -> None:
|
||||
"""Catch any connection errors and set unavailable."""
|
||||
try:
|
||||
await coro
|
||||
except ConnectionError as ex:
|
||||
self.set_available(False, ex)
|
||||
else:
|
||||
self.set_available(True)
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
temp = kwargs.get(ATTR_TEMPERATURE)
|
||||
if temp is not None:
|
||||
await self.wrap_and_catch(self._controller.set_desired_temp(temp))
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
"""Set new target fan mode."""
|
||||
await self.wrap_and_catch(self._controller.set_fan(_HA_FAN_TO_ESCEA[fan_mode]))
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set new target operation mode."""
|
||||
await self.wrap_and_catch(self._controller.set_on(hvac_mode == HVACMode.HEAT))
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn the entity on."""
|
||||
await self.wrap_and_catch(self._controller.set_on(True))
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn the entity off."""
|
||||
await self.wrap_and_catch(self._controller.set_on(False))
|
52
homeassistant/components/escea/config_flow.py
Normal file
52
homeassistant/components/escea/config_flow.py
Normal file
@ -0,0 +1,52 @@
|
||||
"""Config flow for escea."""
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
import logging
|
||||
|
||||
from async_timeout import timeout
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_entry_flow
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import (
|
||||
DISPATCH_CONTROLLER_DISCOVERED,
|
||||
DOMAIN,
|
||||
ESCEA_FIREPLACE,
|
||||
TIMEOUT_DISCOVERY,
|
||||
)
|
||||
from .discovery import async_start_discovery_service, async_stop_discovery_service
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def _async_has_devices(hass: HomeAssistant) -> bool:
|
||||
|
||||
controller_ready = asyncio.Event()
|
||||
|
||||
@callback
|
||||
def dispatch_discovered(_):
|
||||
controller_ready.set()
|
||||
|
||||
remove_handler = async_dispatcher_connect(
|
||||
hass, DISPATCH_CONTROLLER_DISCOVERED, dispatch_discovered
|
||||
)
|
||||
|
||||
discovery_service = await async_start_discovery_service(hass)
|
||||
|
||||
with suppress(asyncio.TimeoutError):
|
||||
async with timeout(TIMEOUT_DISCOVERY):
|
||||
await controller_ready.wait()
|
||||
|
||||
remove_handler()
|
||||
|
||||
if not discovery_service.controllers:
|
||||
await async_stop_discovery_service(hass)
|
||||
_LOGGER.debug("No controllers found")
|
||||
return False
|
||||
|
||||
_LOGGER.debug("Controllers %s", discovery_service.controllers)
|
||||
return True
|
||||
|
||||
|
||||
config_entry_flow.register_discovery_flow(DOMAIN, ESCEA_FIREPLACE, _async_has_devices)
|
15
homeassistant/components/escea/const.py
Normal file
15
homeassistant/components/escea/const.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""Constants used by the escea component."""
|
||||
|
||||
DOMAIN = "escea"
|
||||
ESCEA_MANUFACTURER = "Escea"
|
||||
ESCEA_FIREPLACE = "Escea Fireplace"
|
||||
ICON = "mdi:fire"
|
||||
|
||||
DATA_DISCOVERY_SERVICE = "escea_discovery"
|
||||
|
||||
DISPATCH_CONTROLLER_DISCOVERED = "escea_controller_discovered"
|
||||
DISPATCH_CONTROLLER_DISCONNECTED = "escea_controller_disconnected"
|
||||
DISPATCH_CONTROLLER_RECONNECTED = "escea_controller_reconnected"
|
||||
DISPATCH_CONTROLLER_UPDATE = "escea_controller_update"
|
||||
|
||||
TIMEOUT_DISCOVERY = 20
|
75
homeassistant/components/escea/discovery.py
Normal file
75
homeassistant/components/escea/discovery.py
Normal file
@ -0,0 +1,75 @@
|
||||
"""Internal discovery service for Escea Fireplace."""
|
||||
from __future__ import annotations
|
||||
|
||||
from pescea import (
|
||||
AbstractDiscoveryService,
|
||||
Controller,
|
||||
Listener,
|
||||
discovery_service as pescea_discovery_service,
|
||||
)
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
from .const import (
|
||||
DATA_DISCOVERY_SERVICE,
|
||||
DISPATCH_CONTROLLER_DISCONNECTED,
|
||||
DISPATCH_CONTROLLER_DISCOVERED,
|
||||
DISPATCH_CONTROLLER_RECONNECTED,
|
||||
DISPATCH_CONTROLLER_UPDATE,
|
||||
)
|
||||
|
||||
|
||||
class DiscoveryServiceListener(Listener):
|
||||
"""Discovery data and interfacing with pescea library."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialise discovery service."""
|
||||
super().__init__()
|
||||
self.hass = hass
|
||||
|
||||
# Listener interface
|
||||
def controller_discovered(self, ctrl: Controller) -> None:
|
||||
"""Handle new controller discoverery."""
|
||||
async_dispatcher_send(self.hass, DISPATCH_CONTROLLER_DISCOVERED, ctrl)
|
||||
|
||||
def controller_disconnected(self, ctrl: Controller, ex: Exception) -> None:
|
||||
"""On disconnect from controller."""
|
||||
async_dispatcher_send(self.hass, DISPATCH_CONTROLLER_DISCONNECTED, ctrl, ex)
|
||||
|
||||
def controller_reconnected(self, ctrl: Controller) -> None:
|
||||
"""On reconnect to controller."""
|
||||
async_dispatcher_send(self.hass, DISPATCH_CONTROLLER_RECONNECTED, ctrl)
|
||||
|
||||
def controller_update(self, ctrl: Controller) -> None:
|
||||
"""System update message is received from the controller."""
|
||||
async_dispatcher_send(self.hass, DISPATCH_CONTROLLER_UPDATE, ctrl)
|
||||
|
||||
|
||||
async def async_start_discovery_service(
|
||||
hass: HomeAssistant,
|
||||
) -> AbstractDiscoveryService:
|
||||
"""Set up the pescea internal discovery."""
|
||||
discovery_service = hass.data.get(DATA_DISCOVERY_SERVICE)
|
||||
if discovery_service:
|
||||
# Already started
|
||||
return discovery_service
|
||||
|
||||
# discovery local services
|
||||
listener = DiscoveryServiceListener(hass)
|
||||
discovery_service = pescea_discovery_service(listener)
|
||||
hass.data[DATA_DISCOVERY_SERVICE] = discovery_service
|
||||
|
||||
await discovery_service.start_discovery()
|
||||
|
||||
return discovery_service
|
||||
|
||||
|
||||
async def async_stop_discovery_service(hass: HomeAssistant) -> None:
|
||||
"""Stop the discovery service."""
|
||||
discovery_service = hass.data.get(DATA_DISCOVERY_SERVICE)
|
||||
if not discovery_service:
|
||||
return
|
||||
|
||||
await discovery_service.close()
|
||||
del hass.data[DATA_DISCOVERY_SERVICE]
|
12
homeassistant/components/escea/manifest.json
Normal file
12
homeassistant/components/escea/manifest.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"domain": "escea",
|
||||
"name": "Escea",
|
||||
"documentation": "https://www.home-assistant.io/integrations/escea",
|
||||
"codeowners": ["@lazdavila"],
|
||||
"requirements": ["pescea==1.0.12"],
|
||||
"config_flow": true,
|
||||
"homekit": {
|
||||
"models": ["Escea"]
|
||||
},
|
||||
"iot_class": "local_push"
|
||||
}
|
13
homeassistant/components/escea/strings.json
Normal file
13
homeassistant/components/escea/strings.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"confirm": {
|
||||
"description": "Do you want to set up an Escea fireplace?"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
|
||||
}
|
||||
}
|
||||
}
|
13
homeassistant/components/escea/translations/en.json
Normal file
13
homeassistant/components/escea/translations/en.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"no_devices_found": "No devices found on the network",
|
||||
"single_instance_allowed": "Already configured. Only a single configuration possible."
|
||||
},
|
||||
"step": {
|
||||
"confirm": {
|
||||
"description": "Do you want to set up an Escea fireplace?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -99,6 +99,7 @@ FLOWS = {
|
||||
"enphase_envoy",
|
||||
"environment_canada",
|
||||
"epson",
|
||||
"escea",
|
||||
"esphome",
|
||||
"evil_genius_labs",
|
||||
"ezviz",
|
||||
|
@ -426,6 +426,7 @@ HOMEKIT = {
|
||||
"C105X": "roku",
|
||||
"C135X": "roku",
|
||||
"EB-*": "ecobee",
|
||||
"Escea": "escea",
|
||||
"HHKBridge*": "hive",
|
||||
"Healty Home Coach": "netatmo",
|
||||
"Iota": "abode",
|
||||
|
@ -1227,6 +1227,9 @@ peco==0.0.29
|
||||
# homeassistant.components.pencom
|
||||
pencompy==0.0.3
|
||||
|
||||
# homeassistant.components.escea
|
||||
pescea==1.0.12
|
||||
|
||||
# homeassistant.components.aruba
|
||||
# homeassistant.components.cisco_ios
|
||||
# homeassistant.components.pandora
|
||||
|
@ -854,6 +854,9 @@ pdunehd==1.3.2
|
||||
# homeassistant.components.peco
|
||||
peco==0.0.29
|
||||
|
||||
# homeassistant.components.escea
|
||||
pescea==1.0.12
|
||||
|
||||
# homeassistant.components.aruba
|
||||
# homeassistant.components.cisco_ios
|
||||
# homeassistant.components.pandora
|
||||
|
1
tests/components/escea/__init__.py
Normal file
1
tests/components/escea/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Escea tests."""
|
117
tests/components/escea/test_config_flow.py
Normal file
117
tests/components/escea/test_config_flow.py
Normal file
@ -0,0 +1,117 @@
|
||||
"""Tests for Escea."""
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.escea.const import DOMAIN, ESCEA_FIREPLACE
|
||||
from homeassistant.components.escea.discovery import DiscoveryServiceListener
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_discovery_service")
|
||||
def mock_discovery_service_fixture() -> AsyncMock:
|
||||
"""Mock discovery service."""
|
||||
discovery_service = AsyncMock()
|
||||
discovery_service.controllers = {}
|
||||
return discovery_service
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_controller")
|
||||
def mock_controller_fixture() -> MagicMock:
|
||||
"""Mock controller."""
|
||||
controller = MagicMock()
|
||||
return controller
|
||||
|
||||
|
||||
def _mock_start_discovery(
|
||||
discovery_service: MagicMock, controller: MagicMock
|
||||
) -> Callable[[], Coroutine[None, None, None]]:
|
||||
"""Mock start discovery service."""
|
||||
|
||||
async def do_discovered() -> None:
|
||||
"""Call the listener callback."""
|
||||
listener: DiscoveryServiceListener = discovery_service.call_args[0][0]
|
||||
listener.controller_discovered(controller)
|
||||
|
||||
return do_discovered
|
||||
|
||||
|
||||
async def test_not_found(
|
||||
hass: HomeAssistant, mock_discovery_service: MagicMock
|
||||
) -> None:
|
||||
"""Test not finding any Escea controllers."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.escea.discovery.pescea_discovery_service"
|
||||
) as discovery_service, patch(
|
||||
"homeassistant.components.escea.config_flow.TIMEOUT_DISCOVERY", 0
|
||||
):
|
||||
discovery_service.return_value = mock_discovery_service
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
# Confirmation form
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "no_devices_found"
|
||||
assert discovery_service.return_value.close.call_count == 1
|
||||
|
||||
|
||||
async def test_found(
|
||||
hass: HomeAssistant, mock_controller: MagicMock, mock_discovery_service: AsyncMock
|
||||
) -> None:
|
||||
"""Test finding an Escea controller."""
|
||||
mock_discovery_service.controllers["test-uid"] = mock_controller
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.escea.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.escea.discovery.pescea_discovery_service"
|
||||
) as discovery_service:
|
||||
discovery_service.return_value = mock_discovery_service
|
||||
mock_discovery_service.start_discovery.side_effect = _mock_start_discovery(
|
||||
discovery_service, mock_controller
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
# Confirmation form
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert mock_setup.call_count == 1
|
||||
|
||||
|
||||
async def test_single_instance_allowed(hass: HomeAssistant) -> None:
|
||||
"""Test single instance allowed."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, title=ESCEA_FIREPLACE)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.escea.discovery.pescea_discovery_service"
|
||||
) as discovery_service:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "single_instance_allowed"
|
||||
assert discovery_service.call_count == 0
|
Loading…
x
Reference in New Issue
Block a user