Fulfill IQS rule runtime-data in ViCare integration (#133633)

This commit is contained in:
Christopher Fenner 2024-12-23 00:06:01 +01:00 committed by GitHub
parent 353f085474
commit 67f0de441b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 121 additions and 131 deletions

View File

@ -2,11 +2,9 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping
from contextlib import suppress from contextlib import suppress
import logging import logging
import os import os
from typing import Any
from PyViCare.PyViCare import PyViCare from PyViCare.PyViCare import PyViCare
from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig
@ -16,8 +14,6 @@ from PyViCare.PyViCareUtils import (
) )
from homeassistant.components.climate import DOMAIN as DOMAIN_CLIMATE from homeassistant.components.climate import DOMAIN as DOMAIN_CLIMATE
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
@ -25,31 +21,28 @@ from homeassistant.helpers.storage import STORAGE_DIR
from .const import ( from .const import (
DEFAULT_CACHE_DURATION, DEFAULT_CACHE_DURATION,
DEVICE_LIST,
DOMAIN, DOMAIN,
PLATFORMS, PLATFORMS,
UNSUPPORTED_DEVICES, UNSUPPORTED_DEVICES,
VICARE_TOKEN_FILENAME,
) )
from .types import ViCareDevice from .types import ViCareConfigEntry, ViCareData, ViCareDevice
from .utils import get_device, get_device_serial from .utils import get_device, get_device_serial, login
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_TOKEN_FILENAME = "vicare_token.save"
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ViCareConfigEntry) -> bool:
"""Set up from config entry.""" """Set up from config entry."""
_LOGGER.debug("Setting up ViCare component") _LOGGER.debug("Setting up ViCare component")
hass.data[DOMAIN] = {}
hass.data[DOMAIN][entry.entry_id] = {}
try: try:
await hass.async_add_executor_job(setup_vicare_api, hass, entry) entry.runtime_data = await hass.async_add_executor_job(
setup_vicare_api, hass, entry
)
except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError) as err: except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError) as err:
raise ConfigEntryAuthFailed("Authentication failed") from err raise ConfigEntryAuthFailed("Authentication failed") from err
for device in hass.data[DOMAIN][entry.entry_id][DEVICE_LIST]: for device in entry.runtime_data.devices:
# Migration can be removed in 2025.4.0 # Migration can be removed in 2025.4.0
await async_migrate_devices_and_entities(hass, entry, device) await async_migrate_devices_and_entities(hass, entry, device)
@ -58,28 +51,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True return True
def vicare_login( def setup_vicare_api(hass: HomeAssistant, entry: ViCareConfigEntry) -> PyViCare:
hass: HomeAssistant,
entry_data: Mapping[str, Any],
cache_duration=DEFAULT_CACHE_DURATION,
) -> PyViCare:
"""Login via PyVicare API."""
vicare_api = PyViCare()
vicare_api.setCacheDuration(cache_duration)
vicare_api.initWithCredentials(
entry_data[CONF_USERNAME],
entry_data[CONF_PASSWORD],
entry_data[CONF_CLIENT_ID],
hass.config.path(STORAGE_DIR, _TOKEN_FILENAME),
)
return vicare_api
def setup_vicare_api(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Set up PyVicare API.""" """Set up PyVicare API."""
vicare_api = vicare_login(hass, entry.data) client = login(hass, entry.data)
device_config_list = get_supported_devices(vicare_api.devices) device_config_list = get_supported_devices(client.devices)
# increase cache duration to fit rate limit to number of devices
if (number_of_devices := len(device_config_list)) > 1: if (number_of_devices := len(device_config_list)) > 1:
cache_duration = DEFAULT_CACHE_DURATION * number_of_devices cache_duration = DEFAULT_CACHE_DURATION * number_of_devices
_LOGGER.debug( _LOGGER.debug(
@ -87,36 +65,35 @@ def setup_vicare_api(hass: HomeAssistant, entry: ConfigEntry) -> None:
number_of_devices, number_of_devices,
cache_duration, cache_duration,
) )
vicare_api = vicare_login(hass, entry.data, cache_duration) client = login(hass, entry.data, cache_duration)
device_config_list = get_supported_devices(vicare_api.devices) device_config_list = get_supported_devices(client.devices)
for device in device_config_list: for device in device_config_list:
_LOGGER.debug( _LOGGER.debug(
"Found device: %s (online: %s)", device.getModel(), str(device.isOnline()) "Found device: %s (online: %s)", device.getModel(), str(device.isOnline())
) )
hass.data[DOMAIN][entry.entry_id][DEVICE_LIST] = [ devices = [
ViCareDevice(config=device_config, api=get_device(entry, device_config)) ViCareDevice(config=device_config, api=get_device(entry, device_config))
for device_config in device_config_list for device_config in device_config_list
] ]
return ViCareData(client=client, devices=devices)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ViCareConfigEntry) -> bool:
"""Unload ViCare config entry.""" """Unload ViCare config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
with suppress(FileNotFoundError): with suppress(FileNotFoundError):
await hass.async_add_executor_job( await hass.async_add_executor_job(
os.remove, hass.config.path(STORAGE_DIR, _TOKEN_FILENAME) os.remove, hass.config.path(STORAGE_DIR, VICARE_TOKEN_FILENAME)
) )
return unload_ok return unload_ok
async def async_migrate_devices_and_entities( async def async_migrate_devices_and_entities(
hass: HomeAssistant, entry: ConfigEntry, device: ViCareDevice hass: HomeAssistant, entry: ViCareConfigEntry, device: ViCareDevice
) -> None: ) -> None:
"""Migrate old entry.""" """Migrate old entry."""
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)

View File

@ -24,13 +24,11 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription, BinarySensorEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DEVICE_LIST, DOMAIN
from .entity import ViCareEntity from .entity import ViCareEntity
from .types import ViCareDevice, ViCareRequiredKeysMixin from .types import ViCareConfigEntry, ViCareDevice, ViCareRequiredKeysMixin
from .utils import ( from .utils import (
get_burners, get_burners,
get_circuits, get_circuits,
@ -152,16 +150,14 @@ def _build_entities(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ViCareConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Create the ViCare binary sensor devices.""" """Create the ViCare binary sensor devices."""
device_list = hass.data[DOMAIN][config_entry.entry_id][DEVICE_LIST]
async_add_entities( async_add_entities(
await hass.async_add_executor_job( await hass.async_add_executor_job(
_build_entities, _build_entities,
device_list, config_entry.runtime_data.devices,
) )
) )

View File

@ -16,14 +16,12 @@ from PyViCare.PyViCareUtils import (
import requests import requests
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DEVICE_LIST, DOMAIN
from .entity import ViCareEntity from .entity import ViCareEntity
from .types import ViCareDevice, ViCareRequiredKeysMixinWithSet from .types import ViCareConfigEntry, ViCareDevice, ViCareRequiredKeysMixinWithSet
from .utils import get_device_serial, is_supported from .utils import get_device_serial, is_supported
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -67,16 +65,14 @@ def _build_entities(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ViCareConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Create the ViCare button entities.""" """Create the ViCare button entities."""
device_list = hass.data[DOMAIN][config_entry.entry_id][DEVICE_LIST]
async_add_entities( async_add_entities(
await hass.async_add_executor_job( await hass.async_add_executor_job(
_build_entities, _build_entities,
device_list, config_entry.runtime_data.devices,
) )
) )

View File

@ -24,7 +24,6 @@ from homeassistant.components.climate import (
HVACAction, HVACAction,
HVACMode, HVACMode,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_TEMPERATURE, ATTR_TEMPERATURE,
PRECISION_TENTHS, PRECISION_TENTHS,
@ -37,9 +36,9 @@ from homeassistant.helpers import entity_platform
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DEVICE_LIST, DOMAIN from .const import DOMAIN
from .entity import ViCareEntity from .entity import ViCareEntity
from .types import HeatingProgram, ViCareDevice from .types import HeatingProgram, ViCareConfigEntry, ViCareDevice
from .utils import get_burners, get_circuits, get_compressors, get_device_serial from .utils import get_burners, get_circuits, get_compressors, get_device_serial
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -99,25 +98,22 @@ def _build_entities(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ViCareConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the ViCare climate platform.""" """Set up the ViCare climate platform."""
platform = entity_platform.async_get_current_platform() platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service( platform.async_register_entity_service(
SERVICE_SET_VICARE_MODE, SERVICE_SET_VICARE_MODE,
{vol.Required(SERVICE_SET_VICARE_MODE_ATTR_MODE): cv.string}, {vol.Required(SERVICE_SET_VICARE_MODE_ATTR_MODE): cv.string},
"set_vicare_mode", "set_vicare_mode",
) )
device_list = hass.data[DOMAIN][config_entry.entry_id][DEVICE_LIST]
async_add_entities( async_add_entities(
await hass.async_add_executor_job( await hass.async_add_executor_job(
_build_entities, _build_entities,
device_list, config_entry.runtime_data.devices,
) )
) )

View File

@ -18,7 +18,6 @@ from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import format_mac from homeassistant.helpers.device_registry import format_mac
from . import vicare_login
from .const import ( from .const import (
CONF_HEATING_TYPE, CONF_HEATING_TYPE,
DEFAULT_HEATING_TYPE, DEFAULT_HEATING_TYPE,
@ -26,6 +25,7 @@ from .const import (
VICARE_NAME, VICARE_NAME,
HeatingType, HeatingType,
) )
from .utils import login
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -62,9 +62,7 @@ class ViCareConfigFlow(ConfigFlow, domain=DOMAIN):
if user_input is not None: if user_input is not None:
try: try:
await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(login, self.hass, user_input)
vicare_login, self.hass, user_input
)
except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError): except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError):
errors["base"] = "invalid_auth" errors["base"] = "invalid_auth"
else: else:
@ -96,7 +94,7 @@ class ViCareConfigFlow(ConfigFlow, domain=DOMAIN):
} }
try: try:
await self.hass.async_add_executor_job(vicare_login, self.hass, data) await self.hass.async_add_executor_job(login, self.hass, data)
except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError): except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError):
errors["base"] = "invalid_auth" errors["base"] = "invalid_auth"
else: else:

View File

@ -25,8 +25,8 @@ UNSUPPORTED_DEVICES = [
"E3_RoomControl_One_522", "E3_RoomControl_One_522",
] ]
DEVICE_LIST = "device_list"
VICARE_NAME = "ViCare" VICARE_NAME = "ViCare"
VICARE_TOKEN_FILENAME = "vicare_token.save"
CONF_CIRCUIT = "circuit" CONF_CIRCUIT = "circuit"
CONF_HEATING_TYPE = "heating_type" CONF_HEATING_TYPE = "heating_type"

View File

@ -6,25 +6,24 @@ import json
from typing import Any from typing import Any
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import DEVICE_LIST, DOMAIN from .types import ViCareConfigEntry
TO_REDACT = {CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME} TO_REDACT = {CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME}
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: ViCareConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""
def dump_devices() -> list[dict[str, Any]]: def dump_devices() -> list[dict[str, Any]]:
"""Dump devices.""" """Dump devices."""
return [ return [
json.loads(device.config.dump_secure()) json.loads(device.dump_secure())
for device in hass.data[DOMAIN][entry.entry_id][DEVICE_LIST] for device in entry.runtime_data.client.devices
] ]
return { return {

View File

@ -19,7 +19,6 @@ from PyViCare.PyViCareVentilationDevice import (
from requests.exceptions import ConnectionError as RequestConnectionError from requests.exceptions import ConnectionError as RequestConnectionError
from homeassistant.components.fan import FanEntity, FanEntityFeature from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.percentage import ( from homeassistant.util.percentage import (
@ -27,9 +26,8 @@ from homeassistant.util.percentage import (
percentage_to_ordered_list_item, percentage_to_ordered_list_item,
) )
from .const import DEVICE_LIST, DOMAIN
from .entity import ViCareEntity from .entity import ViCareEntity
from .types import ViCareDevice from .types import ViCareConfigEntry, ViCareDevice
from .utils import get_device_serial from .utils import get_device_serial
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -104,17 +102,14 @@ def _build_entities(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ViCareConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the ViCare fan platform.""" """Set up the ViCare fan platform."""
device_list = hass.data[DOMAIN][config_entry.entry_id][DEVICE_LIST]
async_add_entities( async_add_entities(
await hass.async_add_executor_job( await hass.async_add_executor_job(
_build_entities, _build_entities,
device_list, config_entry.runtime_data.devices,
) )
) )

View File

@ -25,14 +25,17 @@ from homeassistant.components.number import (
NumberEntity, NumberEntity,
NumberEntityDescription, NumberEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, UnitOfTemperature from homeassistant.const import EntityCategory, UnitOfTemperature
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DEVICE_LIST, DOMAIN
from .entity import ViCareEntity from .entity import ViCareEntity
from .types import HeatingProgram, ViCareDevice, ViCareRequiredKeysMixin from .types import (
HeatingProgram,
ViCareConfigEntry,
ViCareDevice,
ViCareRequiredKeysMixin,
)
from .utils import get_circuits, get_device_serial, is_supported from .utils import get_circuits, get_device_serial, is_supported
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -370,16 +373,14 @@ def _build_entities(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ViCareConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Create the ViCare number devices.""" """Create the ViCare number devices."""
device_list = hass.data[DOMAIN][config_entry.entry_id][DEVICE_LIST]
async_add_entities( async_add_entities(
await hass.async_add_executor_job( await hass.async_add_executor_job(
_build_entities, _build_entities,
device_list, config_entry.runtime_data.devices,
) )
) )

View File

@ -6,9 +6,7 @@ rules:
status: todo status: todo
comment: Uniqueness is not checked yet. comment: Uniqueness is not checked yet.
config-flow-test-coverage: done config-flow-test-coverage: done
runtime-data: runtime-data: done
status: todo
comment: runtime_data is not used yet.
test-before-setup: done test-before-setup: done
appropriate-polling: done appropriate-polling: done
entity-unique-id: done entity-unique-id: done

View File

@ -25,7 +25,6 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
PERCENTAGE, PERCENTAGE,
EntityCategory, EntityCategory,
@ -40,8 +39,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ( from .const import (
DEVICE_LIST,
DOMAIN,
VICARE_CUBIC_METER, VICARE_CUBIC_METER,
VICARE_KW, VICARE_KW,
VICARE_KWH, VICARE_KWH,
@ -50,7 +47,7 @@ from .const import (
VICARE_WH, VICARE_WH,
) )
from .entity import ViCareEntity from .entity import ViCareEntity
from .types import ViCareDevice, ViCareRequiredKeysMixin from .types import ViCareConfigEntry, ViCareDevice, ViCareRequiredKeysMixin
from .utils import ( from .utils import (
get_burners, get_burners,
get_circuits, get_circuits,
@ -968,16 +965,14 @@ def _build_entities(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ViCareConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Create the ViCare sensor devices.""" """Create the ViCare sensor devices."""
device_list = hass.data[DOMAIN][config_entry.entry_id][DEVICE_LIST]
async_add_entities( async_add_entities(
await hass.async_add_executor_job( await hass.async_add_executor_job(
_build_entities, _build_entities,
device_list, config_entry.runtime_data.devices,
), ),
# run update to have device_class set depending on unit_of_measurement # run update to have device_class set depending on unit_of_measurement
True, True,

View File

@ -6,6 +6,7 @@ from dataclasses import dataclass
import enum import enum
from typing import Any from typing import Any
from PyViCare.PyViCare import PyViCare
from PyViCare.PyViCareDevice import Device as PyViCareDevice from PyViCare.PyViCareDevice import Device as PyViCareDevice
from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig
@ -15,6 +16,7 @@ from homeassistant.components.climate import (
PRESET_HOME, PRESET_HOME,
PRESET_SLEEP, PRESET_SLEEP,
) )
from homeassistant.config_entries import ConfigEntry
class HeatingProgram(enum.StrEnum): class HeatingProgram(enum.StrEnum):
@ -80,6 +82,17 @@ class ViCareDevice:
api: PyViCareDevice api: PyViCareDevice
@dataclass(frozen=True)
class ViCareData:
"""ViCare data class."""
client: PyViCare
devices: list[ViCareDevice]
type ViCareConfigEntry = ConfigEntry[ViCareData]
@dataclass(frozen=True) @dataclass(frozen=True)
class ViCareRequiredKeysMixin: class ViCareRequiredKeysMixin:
"""Mixin for required keys.""" """Mixin for required keys."""

View File

@ -1,7 +1,12 @@
"""ViCare helpers functions.""" """ViCare helpers functions."""
import logging from __future__ import annotations
from collections.abc import Mapping
import logging
from typing import Any
from PyViCare.PyViCare import PyViCare
from PyViCare.PyViCareDevice import Device as PyViCareDevice from PyViCare.PyViCareDevice import Device as PyViCareDevice
from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig
from PyViCare.PyViCareHeatingDevice import ( from PyViCare.PyViCareHeatingDevice import (
@ -14,16 +19,41 @@ from PyViCare.PyViCareUtils import (
) )
import requests import requests
from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.storage import STORAGE_DIR
from .const import CONF_HEATING_TYPE, HEATING_TYPE_TO_CREATOR_METHOD, HeatingType from .const import (
from .types import ViCareRequiredKeysMixin CONF_HEATING_TYPE,
DEFAULT_CACHE_DURATION,
HEATING_TYPE_TO_CREATOR_METHOD,
VICARE_TOKEN_FILENAME,
HeatingType,
)
from .types import ViCareConfigEntry, ViCareRequiredKeysMixin
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def login(
hass: HomeAssistant,
entry_data: Mapping[str, Any],
cache_duration=DEFAULT_CACHE_DURATION,
) -> PyViCare:
"""Login via PyVicare API."""
vicare_api = PyViCare()
vicare_api.setCacheDuration(cache_duration)
vicare_api.initWithCredentials(
entry_data[CONF_USERNAME],
entry_data[CONF_PASSWORD],
entry_data[CONF_CLIENT_ID],
hass.config.path(STORAGE_DIR, VICARE_TOKEN_FILENAME),
)
return vicare_api
def get_device( def get_device(
entry: ConfigEntry, device_config: PyViCareDeviceConfig entry: ViCareConfigEntry, device_config: PyViCareDeviceConfig
) -> PyViCareDevice: ) -> PyViCareDevice:
"""Get device for device config.""" """Get device for device config."""
return getattr( return getattr(

View File

@ -20,14 +20,12 @@ from homeassistant.components.water_heater import (
WaterHeaterEntity, WaterHeaterEntity,
WaterHeaterEntityFeature, WaterHeaterEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DEVICE_LIST, DOMAIN
from .entity import ViCareEntity from .entity import ViCareEntity
from .types import ViCareDevice from .types import ViCareConfigEntry, ViCareDevice
from .utils import get_circuits, get_device_serial from .utils import get_circuits, get_device_serial
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -81,16 +79,14 @@ def _build_entities(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ViCareConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the ViCare water heater platform.""" """Set up the ViCare water heater platform."""
device_list = hass.data[DOMAIN][config_entry.entry_id][DEVICE_LIST]
async_add_entities( async_add_entities(
await hass.async_add_executor_job( await hass.async_add_executor_job(
_build_entities, _build_entities,
device_list, config_entry.runtime_data.devices,
) )
) )

View File

@ -84,7 +84,7 @@ async def mock_vicare_gas_boiler(
"""Return a mocked ViCare API representing a single gas boiler device.""" """Return a mocked ViCare API representing a single gas boiler device."""
fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")] fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]
with patch( with patch(
f"{MODULE}.vicare_login", f"{MODULE}.login",
return_value=MockPyViCare(fixtures), return_value=MockPyViCare(fixtures),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
@ -102,7 +102,7 @@ async def mock_vicare_room_sensors(
Fixture({"type:climateSensor"}, "vicare/RoomSensor2.json"), Fixture({"type:climateSensor"}, "vicare/RoomSensor2.json"),
] ]
with patch( with patch(
f"{MODULE}.vicare_login", f"{MODULE}.login",
return_value=MockPyViCare(fixtures), return_value=MockPyViCare(fixtures),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)

View File

@ -43,7 +43,7 @@ async def test_all_entities(
"""Test all entities.""" """Test all entities."""
fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")] fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.BINARY_SENSOR]), patch(f"{MODULE}.PLATFORMS", [Platform.BINARY_SENSOR]),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)

View File

@ -25,7 +25,7 @@ async def test_all_entities(
"""Test all entities.""" """Test all entities."""
fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")] fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.BUTTON]), patch(f"{MODULE}.PLATFORMS", [Platform.BUTTON]),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)

View File

@ -25,7 +25,7 @@ async def test_all_entities(
"""Test all entities.""" """Test all entities."""
fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")] fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.CLIMATE]), patch(f"{MODULE}.PLATFORMS", [Platform.CLIMATE]),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)

View File

@ -49,7 +49,7 @@ async def test_user_create_entry(
# test PyViCareInvalidConfigurationError # test PyViCareInvalidConfigurationError
with patch( with patch(
f"{MODULE}.config_flow.vicare_login", f"{MODULE}.config_flow.login",
side_effect=PyViCareInvalidConfigurationError( side_effect=PyViCareInvalidConfigurationError(
{"error": "foo", "error_description": "bar"} {"error": "foo", "error_description": "bar"}
), ),
@ -65,7 +65,7 @@ async def test_user_create_entry(
# test PyViCareInvalidCredentialsError # test PyViCareInvalidCredentialsError
with patch( with patch(
f"{MODULE}.config_flow.vicare_login", f"{MODULE}.config_flow.login",
side_effect=PyViCareInvalidCredentialsError, side_effect=PyViCareInvalidCredentialsError,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -79,7 +79,7 @@ async def test_user_create_entry(
# test success # test success
with patch( with patch(
f"{MODULE}.config_flow.vicare_login", f"{MODULE}.config_flow.login",
return_value=None, return_value=None,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -110,7 +110,7 @@ async def test_step_reauth(hass: HomeAssistant, mock_setup_entry: AsyncMock) ->
# test PyViCareInvalidConfigurationError # test PyViCareInvalidConfigurationError
with patch( with patch(
f"{MODULE}.config_flow.vicare_login", f"{MODULE}.config_flow.login",
side_effect=PyViCareInvalidConfigurationError( side_effect=PyViCareInvalidConfigurationError(
{"error": "foo", "error_description": "bar"} {"error": "foo", "error_description": "bar"}
), ),
@ -125,7 +125,7 @@ async def test_step_reauth(hass: HomeAssistant, mock_setup_entry: AsyncMock) ->
# test success # test success
with patch( with patch(
f"{MODULE}.config_flow.vicare_login", f"{MODULE}.config_flow.login",
return_value=None, return_value=None,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -160,7 +160,7 @@ async def test_form_dhcp(
assert result["errors"] == {} assert result["errors"] == {}
with patch( with patch(
f"{MODULE}.config_flow.vicare_login", f"{MODULE}.config_flow.login",
return_value=None, return_value=None,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(

View File

@ -25,7 +25,7 @@ async def test_all_entities(
"""Test all entities.""" """Test all entities."""
fixtures: list[Fixture] = [Fixture({"type:ventilation"}, "vicare/ViAir300F.json")] fixtures: list[Fixture] = [Fixture({"type:ventilation"}, "vicare/ViAir300F.json")]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.FAN]), patch(f"{MODULE}.PLATFORMS", [Platform.FAN]),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)

View File

@ -26,7 +26,7 @@ async def test_device_and_entity_migration(
Fixture({"type:boiler"}, "vicare/dummy-device-no-serial.json"), Fixture({"type:boiler"}, "vicare/dummy-device-no-serial.json"),
] ]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.CLIMATE]), patch(f"{MODULE}.PLATFORMS", [Platform.CLIMATE]),
): ):
mock_config_entry.add_to_hass(hass) mock_config_entry.add_to_hass(hass)

View File

@ -25,7 +25,7 @@ async def test_all_entities(
"""Test all entities.""" """Test all entities."""
fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")] fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.NUMBER]), patch(f"{MODULE}.PLATFORMS", [Platform.NUMBER]),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)

View File

@ -27,7 +27,7 @@ async def test_all_entities(
Fixture({"type:boiler"}, "vicare/Vitodens300W.json"), Fixture({"type:boiler"}, "vicare/Vitodens300W.json"),
] ]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.SENSOR]), patch(f"{MODULE}.PLATFORMS", [Platform.SENSOR]),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
@ -48,7 +48,7 @@ async def test_room_sensors(
Fixture({"type:climateSensor"}, "vicare/RoomSensor2.json"), Fixture({"type:climateSensor"}, "vicare/RoomSensor2.json"),
] ]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.SENSOR]), patch(f"{MODULE}.PLATFORMS", [Platform.SENSOR]),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)

View File

@ -25,7 +25,7 @@ async def test_all_entities(
"""Test all entities.""" """Test all entities."""
fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")] fixtures: list[Fixture] = [Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]
with ( with (
patch(f"{MODULE}.vicare_login", return_value=MockPyViCare(fixtures)), patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),
patch(f"{MODULE}.PLATFORMS", [Platform.WATER_HEATER]), patch(f"{MODULE}.PLATFORMS", [Platform.WATER_HEATER]),
): ):
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)