Huum - Introduce coordinator to support multiple platforms (#148889)

Co-authored-by: Josef Zweck <josef@zweck.dev>
This commit is contained in:
Vincent Wolsink 2025-07-17 11:43:26 +02:00 committed by GitHub
parent d72fb021c1
commit 9373bb287c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 403 additions and 140 deletions

4
CODEOWNERS generated
View File

@ -684,8 +684,8 @@ build.json @home-assistant/supervisor
/tests/components/husqvarna_automower/ @Thomas55555 /tests/components/husqvarna_automower/ @Thomas55555
/homeassistant/components/husqvarna_automower_ble/ @alistair23 /homeassistant/components/husqvarna_automower_ble/ @alistair23
/tests/components/husqvarna_automower_ble/ @alistair23 /tests/components/husqvarna_automower_ble/ @alistair23
/homeassistant/components/huum/ @frwickst /homeassistant/components/huum/ @frwickst @vincentwolsink
/tests/components/huum/ @frwickst /tests/components/huum/ @frwickst @vincentwolsink
/homeassistant/components/hvv_departures/ @vigonotion /homeassistant/components/hvv_departures/ @vigonotion
/tests/components/hvv_departures/ @vigonotion /tests/components/hvv_departures/ @vigonotion
/homeassistant/components/hydrawise/ @dknowles2 @thomaskistler @ptcryan /homeassistant/components/hydrawise/ @dknowles2 @thomaskistler @ptcryan

View File

@ -2,46 +2,28 @@
from __future__ import annotations from __future__ import annotations
import logging
from huum.exceptions import Forbidden, NotAuthenticated
from huum.huum import Huum
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN, PLATFORMS from .const import PLATFORMS
from .coordinator import HuumConfigEntry, HuumDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, config_entry: HuumConfigEntry) -> bool:
"""Set up Huum from a config entry.""" """Set up Huum from a config entry."""
username = entry.data[CONF_USERNAME] coordinator = HuumDataUpdateCoordinator(
password = entry.data[CONF_PASSWORD] hass=hass,
config_entry=config_entry,
)
huum = Huum(username, password, session=async_get_clientsession(hass)) await coordinator.async_config_entry_first_refresh()
config_entry.runtime_data = coordinator
try: await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
await huum.status()
except (Forbidden, NotAuthenticated) as err:
_LOGGER.error("Could not log in to Huum with given credentials")
raise ConfigEntryNotReady(
"Could not log in to Huum with given credentials"
) from err
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = huum
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(
hass: HomeAssistant, config_entry: HuumConfigEntry
) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -7,38 +7,35 @@ from typing import Any
from huum.const import SaunaStatus from huum.const import SaunaStatus
from huum.exceptions import SafetyException from huum.exceptions import SafetyException
from huum.huum import Huum
from huum.schemas import HuumStatusResponse
from homeassistant.components.climate import ( from homeassistant.components.climate import (
ClimateEntity, ClimateEntity,
ClimateEntityFeature, ClimateEntityFeature,
HVACMode, HVACMode,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN from .const import DOMAIN
from .coordinator import HuumConfigEntry, HuumDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: HuumConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the Huum sauna with config flow.""" """Set up the Huum sauna with config flow."""
huum_handler = hass.data.setdefault(DOMAIN, {})[entry.entry_id] async_add_entities([HuumDevice(entry.runtime_data)])
async_add_entities([HuumDevice(huum_handler, entry.entry_id)], True)
class HuumDevice(ClimateEntity): class HuumDevice(CoordinatorEntity[HuumDataUpdateCoordinator], ClimateEntity):
"""Representation of a heater.""" """Representation of a heater."""
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF] _attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
@ -54,24 +51,22 @@ class HuumDevice(ClimateEntity):
_attr_has_entity_name = True _attr_has_entity_name = True
_attr_name = None _attr_name = None
_target_temperature: int | None = None def __init__(self, coordinator: HuumDataUpdateCoordinator) -> None:
_status: HuumStatusResponse | None = None
def __init__(self, huum_handler: Huum, unique_id: str) -> None:
"""Initialize the heater.""" """Initialize the heater."""
self._attr_unique_id = unique_id super().__init__(coordinator)
self._attr_unique_id = coordinator.config_entry.entry_id
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, unique_id)}, identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
name="Huum sauna", name="Huum sauna",
manufacturer="Huum", manufacturer="Huum",
model="UKU WiFi",
) )
self._huum_handler = huum_handler
@property @property
def hvac_mode(self) -> HVACMode: def hvac_mode(self) -> HVACMode:
"""Return hvac operation ie. heat, cool mode.""" """Return hvac operation ie. heat, cool mode."""
if self._status and self._status.status == SaunaStatus.ONLINE_HEATING: if self.coordinator.data.status == SaunaStatus.ONLINE_HEATING:
return HVACMode.HEAT return HVACMode.HEAT
return HVACMode.OFF return HVACMode.OFF
@ -85,41 +80,33 @@ class HuumDevice(ClimateEntity):
@property @property
def current_temperature(self) -> int | None: def current_temperature(self) -> int | None:
"""Return the current temperature.""" """Return the current temperature."""
if (status := self._status) is not None: return self.coordinator.data.temperature
return status.temperature
return None
@property @property
def target_temperature(self) -> int: def target_temperature(self) -> int:
"""Return the temperature we try to reach.""" """Return the temperature we try to reach."""
return self._target_temperature or int(self.min_temp) return self.coordinator.data.target_temperature or int(self.min_temp)
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set hvac mode.""" """Set hvac mode."""
if hvac_mode == HVACMode.HEAT: if hvac_mode == HVACMode.HEAT:
await self._turn_on(self.target_temperature) await self._turn_on(self.target_temperature)
elif hvac_mode == HVACMode.OFF: elif hvac_mode == HVACMode.OFF:
await self._huum_handler.turn_off() await self.coordinator.huum.turn_off()
await self.coordinator.async_refresh()
async def async_set_temperature(self, **kwargs: Any) -> None: async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature.""" """Set new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE) temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature is None: if temperature is None or self.hvac_mode != HVACMode.HEAT:
return return
self._target_temperature = temperature
if self.hvac_mode == HVACMode.HEAT: await self._turn_on(temperature)
await self._turn_on(temperature) await self.coordinator.async_refresh()
async def async_update(self) -> None:
"""Get the latest status data."""
self._status = await self._huum_handler.status()
if self._target_temperature is None or self.hvac_mode == HVACMode.HEAT:
self._target_temperature = self._status.target_temperature
async def _turn_on(self, temperature: int) -> None: async def _turn_on(self, temperature: int) -> None:
try: try:
await self._huum_handler.turn_on(temperature) await self.coordinator.huum.turn_on(temperature)
except (ValueError, SafetyException) as err: except (ValueError, SafetyException) as err:
_LOGGER.error(str(err)) _LOGGER.error(str(err))
raise HomeAssistantError(f"Unable to turn on sauna: {err}") from err raise HomeAssistantError(f"Unable to turn on sauna: {err}") from err

View File

@ -37,12 +37,12 @@ class HuumConfigFlow(ConfigFlow, domain=DOMAIN):
errors = {} errors = {}
if user_input is not None: if user_input is not None:
try: try:
huum_handler = Huum( huum = Huum(
user_input[CONF_USERNAME], user_input[CONF_USERNAME],
user_input[CONF_PASSWORD], user_input[CONF_PASSWORD],
session=async_get_clientsession(self.hass), session=async_get_clientsession(self.hass),
) )
await huum_handler.status() await huum.status()
except (Forbidden, NotAuthenticated): except (Forbidden, NotAuthenticated):
# Most likely Forbidden as that is what is returned from `.status()` with bad creds # Most likely Forbidden as that is what is returned from `.status()` with bad creds
_LOGGER.error("Could not log in to Huum with given credentials") _LOGGER.error("Could not log in to Huum with given credentials")

View File

@ -0,0 +1,60 @@
"""DataUpdateCoordinator for Huum."""
from __future__ import annotations
from datetime import timedelta
import logging
from huum.exceptions import Forbidden, NotAuthenticated
from huum.huum import Huum
from huum.schemas import HuumStatusResponse
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN
type HuumConfigEntry = ConfigEntry[HuumDataUpdateCoordinator]
_LOGGER = logging.getLogger(__name__)
UPDATE_INTERVAL = timedelta(seconds=30)
class HuumDataUpdateCoordinator(DataUpdateCoordinator[HuumStatusResponse]):
"""Class to manage fetching data from the API."""
config_entry: HuumConfigEntry
def __init__(
self,
hass: HomeAssistant,
config_entry: HuumConfigEntry,
) -> None:
"""Initialize."""
super().__init__(
hass=hass,
logger=_LOGGER,
name=DOMAIN,
update_interval=UPDATE_INTERVAL,
config_entry=config_entry,
)
self.huum = Huum(
config_entry.data[CONF_USERNAME],
config_entry.data[CONF_PASSWORD],
session=async_get_clientsession(hass),
)
async def _async_update_data(self) -> HuumStatusResponse:
"""Get the latest status data."""
try:
return await self.huum.status()
except (Forbidden, NotAuthenticated) as err:
_LOGGER.error("Could not log in to Huum with given credentials")
raise UpdateFailed(
"Could not log in to Huum with given credentials"
) from err

View File

@ -1,7 +1,7 @@
{ {
"domain": "huum", "domain": "huum",
"name": "Huum", "name": "Huum",
"codeowners": ["@frwickst"], "codeowners": ["@frwickst", "@vincentwolsink"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/huum", "documentation": "https://www.home-assistant.io/integrations/huum",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",

View File

@ -1 +1,18 @@
"""Tests for the huum integration.""" """Tests for the huum integration."""
from unittest.mock import patch
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_with_selected_platforms(
hass: HomeAssistant, entry: MockConfigEntry, platforms: list[Platform]
) -> None:
"""Set up the Huum integration with the selected platforms."""
entry.add_to_hass(hass)
with patch("homeassistant.components.huum.PLATFORMS", platforms):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()

View File

@ -0,0 +1,72 @@
"""Configuration for Huum tests."""
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from huum.const import SaunaStatus
import pytest
from homeassistant.components.huum.const import DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from tests.common import MockConfigEntry
@pytest.fixture
def mock_huum() -> Generator[AsyncMock]:
"""Mock data from the API."""
huum = AsyncMock()
with (
patch(
"homeassistant.components.huum.config_flow.Huum.status",
return_value=huum,
),
patch(
"homeassistant.components.huum.coordinator.Huum.status",
return_value=huum,
),
patch(
"homeassistant.components.huum.coordinator.Huum.turn_on",
return_value=huum,
) as turn_on,
):
huum.status = SaunaStatus.ONLINE_NOT_HEATING
huum.door_closed = True
huum.temperature = 30
huum.sauna_name = 123456
huum.target_temperature = 80
huum.light = 1
huum.humidity = 5
huum.sauna_config.child_lock = "OFF"
huum.sauna_config.max_heating_time = 3
huum.sauna_config.min_heating_time = 0
huum.sauna_config.max_temp = 110
huum.sauna_config.min_temp = 40
huum.sauna_config.max_timer = 0
huum.sauna_config.min_timer = 0
huum.turn_on = turn_on
yield huum
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Mock setting up a config entry."""
with patch(
"homeassistant.components.huum.async_setup_entry", return_value=True
) as setup_entry_mock:
yield setup_entry_mock
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Mock a config entry."""
return MockConfigEntry(
domain=DOMAIN,
data={
CONF_USERNAME: "huum@sauna.org",
CONF_PASSWORD: "ukuuku",
},
unique_id="123456",
entry_id="AABBCC112233",
)

View File

@ -0,0 +1,68 @@
# serializer version: 1
# name: test_climate_entity[climate.huum_sauna-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.HEAT: 'heat'>,
<HVACMode.OFF: 'off'>,
]),
'max_temp': 110,
'min_temp': 40,
'target_temp_step': 1,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.huum_sauna',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': 'mdi:radiator-off',
'original_name': None,
'platform': 'huum',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': <ClimateEntityFeature: 385>,
'translation_key': None,
'unique_id': 'AABBCC112233',
'unit_of_measurement': None,
})
# ---
# name: test_climate_entity[climate.huum_sauna-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 30,
'friendly_name': 'Huum sauna',
'hvac_modes': list([
<HVACMode.HEAT: 'heat'>,
<HVACMode.OFF: 'off'>,
]),
'icon': 'mdi:radiator-off',
'max_temp': 110,
'min_temp': 40,
'supported_features': <ClimateEntityFeature: 385>,
'target_temp_step': 1,
'temperature': 80,
}),
'context': <ANY>,
'entity_id': 'climate.huum_sauna',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -0,0 +1,78 @@
"""Tests for the Huum climate entity."""
from unittest.mock import AsyncMock
from huum.const import SaunaStatus
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.climate import (
ATTR_HVAC_MODE,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_TEMPERATURE,
HVACMode,
)
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_with_selected_platforms
from tests.common import MockConfigEntry, snapshot_platform
ENTITY_ID = "climate.huum_sauna"
async def test_climate_entity(
hass: HomeAssistant,
mock_huum: AsyncMock,
mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Test the initial parameters."""
await setup_with_selected_platforms(hass, mock_config_entry, [Platform.CLIMATE])
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_set_hvac_mode(
hass: HomeAssistant,
mock_huum: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test setting HVAC mode."""
await setup_with_selected_platforms(hass, mock_config_entry, [Platform.CLIMATE])
mock_huum.status = SaunaStatus.ONLINE_HEATING
await hass.services.async_call(
Platform.CLIMATE,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.HEAT},
blocking=True,
)
state = hass.states.get(ENTITY_ID)
assert state.state == HVACMode.HEAT
mock_huum.turn_on.assert_called_once()
async def test_set_temperature(
hass: HomeAssistant,
mock_huum: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test setting the temperature."""
await setup_with_selected_platforms(hass, mock_config_entry, [Platform.CLIMATE])
mock_huum.status = SaunaStatus.ONLINE_HEATING
await hass.services.async_call(
Platform.CLIMATE,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: ENTITY_ID,
ATTR_TEMPERATURE: 60,
},
blocking=True,
)
mock_huum.turn_on.assert_called_once_with(60)

View File

@ -1,6 +1,6 @@
"""Test the huum config flow.""" """Test the huum config flow."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
from huum.exceptions import Forbidden from huum.exceptions import Forbidden
import pytest import pytest
@ -13,11 +13,13 @@ from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
TEST_USERNAME = "test-username" TEST_USERNAME = "huum@sauna.org"
TEST_PASSWORD = "test-password" TEST_PASSWORD = "ukuuku"
async def test_form(hass: HomeAssistant) -> None: async def test_form(
hass: HomeAssistant, mock_huum: AsyncMock, mock_setup_entry: AsyncMock
) -> None:
"""Test we get the form.""" """Test we get the form."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -26,24 +28,14 @@ async def test_form(hass: HomeAssistant) -> None:
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["errors"] == {} assert result["errors"] == {}
with ( result2 = await hass.config_entries.flow.async_configure(
patch( result["flow_id"],
"homeassistant.components.huum.config_flow.Huum.status", {
return_value=True, CONF_USERNAME: TEST_USERNAME,
), CONF_PASSWORD: TEST_PASSWORD,
patch( },
"homeassistant.components.huum.async_setup_entry", )
return_value=True, await hass.async_block_till_done()
) as mock_setup_entry,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == TEST_USERNAME assert result2["title"] == TEST_USERNAME
@ -54,42 +46,28 @@ async def test_form(hass: HomeAssistant) -> None:
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
async def test_signup_flow_already_set_up(hass: HomeAssistant) -> None: async def test_signup_flow_already_set_up(
hass: HomeAssistant,
mock_huum: AsyncMock,
mock_setup_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that we handle already existing entities with same id.""" """Test that we handle already existing entities with same id."""
mock_config_entry = MockConfigEntry(
title="Huum Sauna",
domain=DOMAIN,
unique_id=TEST_USERNAME,
data={
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
mock_config_entry.add_to_hass(hass) mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with ( result2 = await hass.config_entries.flow.async_configure(
patch( result["flow_id"],
"homeassistant.components.huum.config_flow.Huum.status", {
return_value=True, CONF_USERNAME: TEST_USERNAME,
), CONF_PASSWORD: TEST_PASSWORD,
patch( },
"homeassistant.components.huum.async_setup_entry", )
return_value=True, await hass.async_block_till_done()
), assert result2["type"] is FlowResultType.ABORT
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.ABORT
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -103,7 +81,11 @@ async def test_signup_flow_already_set_up(hass: HomeAssistant) -> None:
], ],
) )
async def test_huum_errors( async def test_huum_errors(
hass: HomeAssistant, raises: Exception, error_base: str hass: HomeAssistant,
mock_huum: AsyncMock,
mock_setup_entry: AsyncMock,
raises: Exception,
error_base: str,
) -> None: ) -> None:
"""Test we handle cannot connect error.""" """Test we handle cannot connect error."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -125,21 +107,11 @@ async def test_huum_errors(
assert result2["type"] is FlowResultType.FORM assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": error_base} assert result2["errors"] == {"base": error_base}
with ( result2 = await hass.config_entries.flow.async_configure(
patch( result["flow_id"],
"homeassistant.components.huum.config_flow.Huum.status", {
return_value=True, CONF_USERNAME: TEST_USERNAME,
), CONF_PASSWORD: TEST_PASSWORD,
patch( },
"homeassistant.components.huum.async_setup_entry", )
return_value=True, assert result2["type"] is FlowResultType.CREATE_ENTRY
),
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
assert result2["type"] is FlowResultType.CREATE_ENTRY

View File

@ -0,0 +1,27 @@
"""Tests for the Huum __init__."""
from unittest.mock import AsyncMock
from homeassistant.components.huum.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from . import setup_with_selected_platforms
from tests.common import MockConfigEntry
async def test_loading_and_unloading_config_entry(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_huum: AsyncMock
) -> None:
"""Test loading and unloading a config entry."""
await setup_with_selected_platforms(hass, mock_config_entry, [Platform.CLIMATE])
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert mock_config_entry.state is ConfigEntryState.LOADED
assert await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED