Add vicare config flow (#56691)
* Configuration via UI Config flow / YAML deprecation - Support discovery via MAC address - Support import of YAML config - Switch to ConfigEntry, get rid of platform setup * Fix review comments * More tests for vicare yaml import
This commit is contained in:
parent
a7382c8092
commit
38b976e6d6
15 changed files with 564 additions and 95 deletions
|
@ -1194,7 +1194,12 @@ omit =
|
||||||
homeassistant/components/vesync/light.py
|
homeassistant/components/vesync/light.py
|
||||||
homeassistant/components/vesync/switch.py
|
homeassistant/components/vesync/switch.py
|
||||||
homeassistant/components/viaggiatreno/sensor.py
|
homeassistant/components/viaggiatreno/sensor.py
|
||||||
homeassistant/components/vicare/*
|
homeassistant/components/vicare/binary_sensor.py
|
||||||
|
homeassistant/components/vicare/climate.py
|
||||||
|
homeassistant/components/vicare/const.py
|
||||||
|
homeassistant/components/vicare/__init__.py
|
||||||
|
homeassistant/components/vicare/sensor.py
|
||||||
|
homeassistant/components/vicare/water_heater.py
|
||||||
homeassistant/components/vilfo/__init__.py
|
homeassistant/components/vilfo/__init__.py
|
||||||
homeassistant/components/vilfo/sensor.py
|
homeassistant/components/vilfo/sensor.py
|
||||||
homeassistant/components/vilfo/const.py
|
homeassistant/components/vilfo/const.py
|
||||||
|
|
|
@ -9,6 +9,7 @@ from PyViCare.PyViCare import PyViCare
|
||||||
from PyViCare.PyViCareDevice import Device
|
from PyViCare.PyViCareDevice import Device
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_CLIENT_ID,
|
CONF_CLIENT_ID,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
|
@ -16,7 +17,7 @@ from homeassistant.const import (
|
||||||
CONF_SCAN_INTERVAL,
|
CONF_SCAN_INTERVAL,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.storage import STORAGE_DIR
|
from homeassistant.helpers.storage import STORAGE_DIR
|
||||||
|
|
||||||
|
@ -30,7 +31,6 @@ from .const import (
|
||||||
VICARE_API,
|
VICARE_API,
|
||||||
VICARE_CIRCUITS,
|
VICARE_CIRCUITS,
|
||||||
VICARE_DEVICE_CONFIG,
|
VICARE_DEVICE_CONFIG,
|
||||||
VICARE_NAME,
|
|
||||||
HeatingType,
|
HeatingType,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ CONFIG_SCHEMA = vol.Schema(
|
||||||
): int, # Ignored: All circuits are now supported. Will be removed when switching to Setup via UI.
|
): int, # Ignored: All circuits are now supported. Will be removed when switching to Setup via UI.
|
||||||
vol.Optional(CONF_NAME, default="ViCare"): cv.string,
|
vol.Optional(CONF_NAME, default="ViCare"): cv.string,
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_HEATING_TYPE, default=DEFAULT_HEATING_TYPE
|
CONF_HEATING_TYPE, default=DEFAULT_HEATING_TYPE.value
|
||||||
): cv.enum(HeatingType),
|
): vol.In([e.value for e in HeatingType]),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -71,44 +71,75 @@ CONFIG_SCHEMA = vol.Schema(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
async def async_setup(hass: HomeAssistant, config) -> bool:
|
||||||
"""Create the ViCare component."""
|
"""Set up the ViCare component from yaml."""
|
||||||
conf = config[DOMAIN]
|
if DOMAIN not in config:
|
||||||
params = {"token_file": hass.config.path(STORAGE_DIR, "vicare_token.save")}
|
# Setup via UI. No need to continue yaml-based setup
|
||||||
|
return True
|
||||||
|
|
||||||
params["cacheDuration"] = conf.get(CONF_SCAN_INTERVAL)
|
hass.async_create_task(
|
||||||
params["client_id"] = conf.get(CONF_CLIENT_ID)
|
hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
hass.data[DOMAIN] = {}
|
context={"source": SOURCE_IMPORT},
|
||||||
hass.data[DOMAIN][VICARE_NAME] = conf[CONF_NAME]
|
data=config[DOMAIN],
|
||||||
setup_vicare_api(hass, conf, hass.data[DOMAIN])
|
)
|
||||||
|
)
|
||||||
hass.data[DOMAIN][CONF_HEATING_TYPE] = conf[CONF_HEATING_TYPE]
|
|
||||||
|
|
||||||
for platform in PLATFORMS:
|
|
||||||
discovery.load_platform(hass, platform, DOMAIN, {}, config)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setup_vicare_api(hass, conf, entity_data):
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up PyVicare API."""
|
"""Set up from config entry."""
|
||||||
|
_LOGGER.debug("Setting up ViCare component")
|
||||||
|
|
||||||
|
hass.data[DOMAIN] = {}
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = {}
|
||||||
|
|
||||||
|
await hass.async_add_executor_job(setup_vicare_api, hass, entry)
|
||||||
|
|
||||||
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def vicare_login(hass, entry_data):
|
||||||
|
"""Login via PyVicare API."""
|
||||||
vicare_api = PyViCare()
|
vicare_api = PyViCare()
|
||||||
vicare_api.setCacheDuration(conf[CONF_SCAN_INTERVAL])
|
vicare_api.setCacheDuration(entry_data[CONF_SCAN_INTERVAL])
|
||||||
vicare_api.initWithCredentials(
|
vicare_api.initWithCredentials(
|
||||||
conf[CONF_USERNAME],
|
entry_data[CONF_USERNAME],
|
||||||
conf[CONF_PASSWORD],
|
entry_data[CONF_PASSWORD],
|
||||||
conf[CONF_CLIENT_ID],
|
entry_data[CONF_CLIENT_ID],
|
||||||
hass.config.path(STORAGE_DIR, "vicare_token.save"),
|
hass.config.path(STORAGE_DIR, "vicare_token.save"),
|
||||||
)
|
)
|
||||||
|
return vicare_api
|
||||||
|
|
||||||
|
|
||||||
|
def setup_vicare_api(hass, entry):
|
||||||
|
"""Set up PyVicare API."""
|
||||||
|
vicare_api = vicare_login(hass, entry.data)
|
||||||
|
|
||||||
device = vicare_api.devices[0]
|
|
||||||
for device in vicare_api.devices:
|
for device in vicare_api.devices:
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Found device: %s (online: %s)", device.getModel(), str(device.isOnline())
|
"Found device: %s (online: %s)", device.getModel(), str(device.isOnline())
|
||||||
)
|
)
|
||||||
entity_data[VICARE_DEVICE_CONFIG] = device
|
|
||||||
entity_data[VICARE_API] = getattr(
|
# Currently we only support a single device
|
||||||
device, HEATING_TYPE_TO_CREATOR_METHOD[conf[CONF_HEATING_TYPE]]
|
device = vicare_api.devices[0]
|
||||||
|
hass.data[DOMAIN][entry.entry_id][VICARE_DEVICE_CONFIG] = device
|
||||||
|
hass.data[DOMAIN][entry.entry_id][VICARE_API] = getattr(
|
||||||
|
device,
|
||||||
|
HEATING_TYPE_TO_CREATOR_METHOD[HeatingType(entry.data[CONF_HEATING_TYPE])],
|
||||||
)()
|
)()
|
||||||
entity_data[VICARE_CIRCUITS] = entity_data[VICARE_API].circuits
|
hass.data[DOMAIN][entry.entry_id][VICARE_CIRCUITS] = hass.data[DOMAIN][
|
||||||
|
entry.entry_id
|
||||||
|
][VICARE_API].circuits
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Unload ViCare config entry."""
|
||||||
|
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
if unload_ok:
|
||||||
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
|
return unload_ok
|
||||||
|
|
|
@ -17,9 +17,10 @@ from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
BinarySensorEntityDescription,
|
BinarySensorEntityDescription,
|
||||||
)
|
)
|
||||||
|
from homeassistant.const import CONF_NAME
|
||||||
|
|
||||||
from . import ViCareRequiredKeysMixin
|
from . import ViCareRequiredKeysMixin
|
||||||
from .const import DOMAIN, VICARE_API, VICARE_DEVICE_CONFIG, VICARE_NAME
|
from .const import DOMAIN, VICARE_API, VICARE_CIRCUITS, VICARE_DEVICE_CONFIG
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ def _build_entity(name, vicare_api, device_config, sensor):
|
||||||
|
|
||||||
|
|
||||||
async def _entities_from_descriptions(
|
async def _entities_from_descriptions(
|
||||||
hass, name, all_devices, sensor_descriptions, iterables
|
hass, name, all_devices, sensor_descriptions, iterables, config_entry
|
||||||
):
|
):
|
||||||
"""Create entities from descriptions and list of burners/circuits."""
|
"""Create entities from descriptions and list of burners/circuits."""
|
||||||
for description in sensor_descriptions:
|
for description in sensor_descriptions:
|
||||||
|
@ -96,33 +97,30 @@ async def _entities_from_descriptions(
|
||||||
_build_entity,
|
_build_entity,
|
||||||
f"{name} {description.name}{suffix}",
|
f"{name} {description.name}{suffix}",
|
||||||
current,
|
current,
|
||||||
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG],
|
||||||
description,
|
description,
|
||||||
)
|
)
|
||||||
if entity is not None:
|
if entity is not None:
|
||||||
all_devices.append(entity)
|
all_devices.append(entity)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||||
"""Create the ViCare binary sensor devices."""
|
"""Create the ViCare binary sensor devices."""
|
||||||
if discovery_info is None:
|
name = config_entry.data[CONF_NAME]
|
||||||
return
|
api = hass.data[DOMAIN][config_entry.entry_id][VICARE_API]
|
||||||
|
|
||||||
name = hass.data[DOMAIN][VICARE_NAME]
|
|
||||||
api = hass.data[DOMAIN][VICARE_API]
|
|
||||||
|
|
||||||
all_devices = []
|
all_devices = []
|
||||||
|
|
||||||
for description in CIRCUIT_SENSORS:
|
for description in CIRCUIT_SENSORS:
|
||||||
for circuit in api.circuits:
|
for circuit in hass.data[DOMAIN][config_entry.entry_id][VICARE_CIRCUITS]:
|
||||||
suffix = ""
|
suffix = ""
|
||||||
if len(api.circuits) > 1:
|
if len(hass.data[DOMAIN][config_entry.entry_id][VICARE_CIRCUITS]) > 1:
|
||||||
suffix = f" {circuit.id}"
|
suffix = f" {circuit.id}"
|
||||||
entity = await hass.async_add_executor_job(
|
entity = await hass.async_add_executor_job(
|
||||||
_build_entity,
|
_build_entity,
|
||||||
f"{name} {description.name}{suffix}",
|
f"{name} {description.name}{suffix}",
|
||||||
circuit,
|
circuit,
|
||||||
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG],
|
||||||
description,
|
description,
|
||||||
)
|
)
|
||||||
if entity is not None:
|
if entity is not None:
|
||||||
|
@ -130,19 +128,19 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await _entities_from_descriptions(
|
await _entities_from_descriptions(
|
||||||
hass, name, all_devices, BURNER_SENSORS, api.burners
|
hass, name, all_devices, BURNER_SENSORS, api.burners, config_entry
|
||||||
)
|
)
|
||||||
except PyViCareNotSupportedFeatureError:
|
except PyViCareNotSupportedFeatureError:
|
||||||
_LOGGER.info("No burners found")
|
_LOGGER.info("No burners found")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await _entities_from_descriptions(
|
await _entities_from_descriptions(
|
||||||
hass, name, all_devices, COMPRESSOR_SENSORS, api.compressors
|
hass, name, all_devices, COMPRESSOR_SENSORS, api.compressors, config_entry
|
||||||
)
|
)
|
||||||
except PyViCareNotSupportedFeatureError:
|
except PyViCareNotSupportedFeatureError:
|
||||||
_LOGGER.info("No compressors found")
|
_LOGGER.info("No compressors found")
|
||||||
|
|
||||||
async_add_entities(all_devices)
|
async_add_devices(all_devices)
|
||||||
|
|
||||||
|
|
||||||
class ViCareBinarySensor(BinarySensorEntity):
|
class ViCareBinarySensor(BinarySensorEntity):
|
||||||
|
|
|
@ -22,7 +22,12 @@ from homeassistant.components.climate.const import (
|
||||||
SUPPORT_PRESET_MODE,
|
SUPPORT_PRESET_MODE,
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
SUPPORT_TARGET_TEMPERATURE,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
from homeassistant.const import (
|
||||||
|
ATTR_TEMPERATURE,
|
||||||
|
CONF_NAME,
|
||||||
|
PRECISION_WHOLE,
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
)
|
||||||
from homeassistant.helpers import entity_platform
|
from homeassistant.helpers import entity_platform
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
|
@ -32,7 +37,6 @@ from .const import (
|
||||||
VICARE_API,
|
VICARE_API,
|
||||||
VICARE_CIRCUITS,
|
VICARE_CIRCUITS,
|
||||||
VICARE_DEVICE_CONFIG,
|
VICARE_DEVICE_CONFIG,
|
||||||
VICARE_NAME,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -99,33 +103,26 @@ def _build_entity(name, vicare_api, circuit, device_config, heating_type):
|
||||||
return ViCareClimate(name, vicare_api, device_config, circuit, heating_type)
|
return ViCareClimate(name, vicare_api, device_config, circuit, heating_type)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||||
hass, hass_config, async_add_entities, discovery_info=None
|
"""Set up the ViCare climate platform."""
|
||||||
):
|
name = config_entry.data[CONF_NAME]
|
||||||
"""Create the ViCare climate devices."""
|
|
||||||
# Legacy setup. Remove after configuration.yaml deprecation end
|
|
||||||
if discovery_info is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
name = hass.data[DOMAIN][VICARE_NAME]
|
|
||||||
all_devices = []
|
all_devices = []
|
||||||
|
|
||||||
for circuit in hass.data[DOMAIN][VICARE_CIRCUITS]:
|
for circuit in hass.data[DOMAIN][config_entry.entry_id][VICARE_CIRCUITS]:
|
||||||
suffix = ""
|
suffix = ""
|
||||||
if len(hass.data[DOMAIN][VICARE_CIRCUITS]) > 1:
|
if len(hass.data[DOMAIN][config_entry.entry_id][VICARE_CIRCUITS]) > 1:
|
||||||
suffix = f" {circuit.id}"
|
suffix = f" {circuit.id}"
|
||||||
entity = _build_entity(
|
entity = _build_entity(
|
||||||
f"{name} Heating{suffix}",
|
f"{name} Heating{suffix}",
|
||||||
hass.data[DOMAIN][VICARE_API],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_API],
|
||||||
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG],
|
||||||
circuit,
|
circuit,
|
||||||
hass.data[DOMAIN][CONF_HEATING_TYPE],
|
config_entry.data[CONF_HEATING_TYPE],
|
||||||
)
|
)
|
||||||
if entity is not None:
|
if entity is not None:
|
||||||
all_devices.append(entity)
|
all_devices.append(entity)
|
||||||
|
|
||||||
async_add_entities(all_devices)
|
|
||||||
|
|
||||||
platform = entity_platform.async_get_current_platform()
|
platform = entity_platform.async_get_current_platform()
|
||||||
|
|
||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
|
@ -134,6 +131,8 @@ async def async_setup_platform(
|
||||||
"set_vicare_mode",
|
"set_vicare_mode",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async_add_devices(all_devices)
|
||||||
|
|
||||||
|
|
||||||
class ViCareClimate(ClimateEntity):
|
class ViCareClimate(ClimateEntity):
|
||||||
"""Representation of the ViCare heating climate device."""
|
"""Representation of the ViCare heating climate device."""
|
||||||
|
|
110
homeassistant/components/vicare/config_flow.py
Normal file
110
homeassistant/components/vicare/config_flow.py
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
"""Config flow for ViCare integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from PyViCare.PyViCareUtils import PyViCareInvalidCredentialsError
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components.dhcp import MAC_ADDRESS
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_CLIENT_ID,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_SCAN_INTERVAL,
|
||||||
|
CONF_USERNAME,
|
||||||
|
)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
|
||||||
|
from . import vicare_login
|
||||||
|
from .const import (
|
||||||
|
CONF_CIRCUIT,
|
||||||
|
CONF_HEATING_TYPE,
|
||||||
|
DEFAULT_HEATING_TYPE,
|
||||||
|
DEFAULT_SCAN_INTERVAL,
|
||||||
|
DOMAIN,
|
||||||
|
HeatingType,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow for ViCare."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input: dict[str, Any] | None = None):
|
||||||
|
"""Invoke when a user initiates a flow via the user interface."""
|
||||||
|
if self._async_current_entries():
|
||||||
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
|
data_schema = {
|
||||||
|
vol.Required(CONF_USERNAME): cv.string,
|
||||||
|
vol.Required(CONF_PASSWORD): cv.string,
|
||||||
|
vol.Required(CONF_CLIENT_ID): cv.string,
|
||||||
|
vol.Required(CONF_HEATING_TYPE, default=DEFAULT_HEATING_TYPE.value): vol.In(
|
||||||
|
[e.value for e in HeatingType]
|
||||||
|
),
|
||||||
|
vol.Optional(CONF_NAME, default="ViCare"): cv.string,
|
||||||
|
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): vol.All(
|
||||||
|
vol.Coerce(int), vol.Range(min=30)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
try:
|
||||||
|
await self.hass.async_add_executor_job(
|
||||||
|
vicare_login, self.hass, user_input
|
||||||
|
)
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=user_input[CONF_NAME], data=user_input
|
||||||
|
)
|
||||||
|
except PyViCareInvalidCredentialsError as ex:
|
||||||
|
_LOGGER.debug("Could not log in to ViCare, %s", ex)
|
||||||
|
errors["base"] = "invalid_auth"
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user",
|
||||||
|
data_schema=vol.Schema(data_schema),
|
||||||
|
errors=errors,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_dhcp(self, discovery_info):
|
||||||
|
"""Invoke when a Viessmann MAC address is discovered on the network."""
|
||||||
|
formatted_mac = format_mac(discovery_info[MAC_ADDRESS])
|
||||||
|
_LOGGER.info("Found device with mac %s", formatted_mac)
|
||||||
|
|
||||||
|
await self.async_set_unique_id(formatted_mac)
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
if self._async_current_entries():
|
||||||
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
|
return await self.async_step_user()
|
||||||
|
|
||||||
|
async def async_step_import(self, import_info):
|
||||||
|
"""Handle a flow initiated by a YAML config import."""
|
||||||
|
|
||||||
|
await self.async_set_unique_id("Configuration.yaml")
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
if self._async_current_entries():
|
||||||
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
|
# Remove now unsupported config parameters
|
||||||
|
if import_info.get(CONF_CIRCUIT):
|
||||||
|
import_info.pop(CONF_CIRCUIT)
|
||||||
|
|
||||||
|
# Add former optional config if missing
|
||||||
|
if import_info.get(CONF_HEATING_TYPE) is None:
|
||||||
|
import_info[CONF_HEATING_TYPE] = DEFAULT_HEATING_TYPE.value
|
||||||
|
|
||||||
|
return self.async_create_entry(
|
||||||
|
title="Configuration.yaml",
|
||||||
|
data=import_info,
|
||||||
|
)
|
|
@ -14,7 +14,6 @@ PLATFORMS = ["climate", "sensor", "binary_sensor", "water_heater"]
|
||||||
|
|
||||||
VICARE_DEVICE_CONFIG = "device_conf"
|
VICARE_DEVICE_CONFIG = "device_conf"
|
||||||
VICARE_API = "api"
|
VICARE_API = "api"
|
||||||
VICARE_NAME = "name"
|
|
||||||
VICARE_CIRCUITS = "circuits"
|
VICARE_CIRCUITS = "circuits"
|
||||||
|
|
||||||
CONF_CIRCUIT = "circuit"
|
CONF_CIRCUIT = "circuit"
|
||||||
|
|
|
@ -4,5 +4,11 @@
|
||||||
"documentation": "https://www.home-assistant.io/integrations/vicare",
|
"documentation": "https://www.home-assistant.io/integrations/vicare",
|
||||||
"codeowners": ["@oischinger"],
|
"codeowners": ["@oischinger"],
|
||||||
"requirements": ["PyViCare==2.13.1"],
|
"requirements": ["PyViCare==2.13.1"],
|
||||||
"iot_class": "cloud_polling"
|
"iot_class": "cloud_polling",
|
||||||
|
"config_flow": true,
|
||||||
|
"dhcp": [
|
||||||
|
{
|
||||||
|
"macaddress": "B87424*"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ from homeassistant.components.sensor import (
|
||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
CONF_NAME,
|
||||||
DEVICE_CLASS_ENERGY,
|
DEVICE_CLASS_ENERGY,
|
||||||
DEVICE_CLASS_POWER,
|
DEVICE_CLASS_POWER,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
@ -36,8 +37,8 @@ from . import ViCareRequiredKeysMixin
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
VICARE_API,
|
VICARE_API,
|
||||||
|
VICARE_CIRCUITS,
|
||||||
VICARE_DEVICE_CONFIG,
|
VICARE_DEVICE_CONFIG,
|
||||||
VICARE_NAME,
|
|
||||||
VICARE_UNIT_TO_DEVICE_CLASS,
|
VICARE_UNIT_TO_DEVICE_CLASS,
|
||||||
VICARE_UNIT_TO_UNIT_OF_MEASUREMENT,
|
VICARE_UNIT_TO_UNIT_OF_MEASUREMENT,
|
||||||
)
|
)
|
||||||
|
@ -338,7 +339,7 @@ def _build_entity(name, vicare_api, device_config, sensor):
|
||||||
|
|
||||||
|
|
||||||
async def _entities_from_descriptions(
|
async def _entities_from_descriptions(
|
||||||
hass, name, all_devices, sensor_descriptions, iterables
|
hass, name, all_devices, sensor_descriptions, iterables, config_entry
|
||||||
):
|
):
|
||||||
"""Create entities from descriptions and list of burners/circuits."""
|
"""Create entities from descriptions and list of burners/circuits."""
|
||||||
for description in sensor_descriptions:
|
for description in sensor_descriptions:
|
||||||
|
@ -350,20 +351,17 @@ async def _entities_from_descriptions(
|
||||||
_build_entity,
|
_build_entity,
|
||||||
f"{name} {description.name}{suffix}",
|
f"{name} {description.name}{suffix}",
|
||||||
current,
|
current,
|
||||||
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG],
|
||||||
description,
|
description,
|
||||||
)
|
)
|
||||||
if entity is not None:
|
if entity is not None:
|
||||||
all_devices.append(entity)
|
all_devices.append(entity)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||||
"""Create the ViCare sensor devices."""
|
"""Create the ViCare sensor devices."""
|
||||||
if discovery_info is None:
|
name = config_entry.data[CONF_NAME]
|
||||||
return
|
api = hass.data[DOMAIN][config_entry.entry_id][VICARE_API]
|
||||||
|
|
||||||
name = hass.data[DOMAIN][VICARE_NAME]
|
|
||||||
api = hass.data[DOMAIN][VICARE_API]
|
|
||||||
|
|
||||||
all_devices = []
|
all_devices = []
|
||||||
for description in GLOBAL_SENSORS:
|
for description in GLOBAL_SENSORS:
|
||||||
|
@ -371,22 +369,22 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||||
_build_entity,
|
_build_entity,
|
||||||
f"{name} {description.name}",
|
f"{name} {description.name}",
|
||||||
api,
|
api,
|
||||||
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG],
|
||||||
description,
|
description,
|
||||||
)
|
)
|
||||||
if entity is not None:
|
if entity is not None:
|
||||||
all_devices.append(entity)
|
all_devices.append(entity)
|
||||||
|
|
||||||
for description in CIRCUIT_SENSORS:
|
for description in CIRCUIT_SENSORS:
|
||||||
for circuit in api.circuits:
|
for circuit in hass.data[DOMAIN][config_entry.entry_id][VICARE_CIRCUITS]:
|
||||||
suffix = ""
|
suffix = ""
|
||||||
if len(api.circuits) > 1:
|
if len(hass.data[DOMAIN][config_entry.entry_id][VICARE_CIRCUITS]) > 1:
|
||||||
suffix = f" {circuit.id}"
|
suffix = f" {circuit.id}"
|
||||||
entity = await hass.async_add_executor_job(
|
entity = await hass.async_add_executor_job(
|
||||||
_build_entity,
|
_build_entity,
|
||||||
f"{name} {description.name}{suffix}",
|
f"{name} {description.name}{suffix}",
|
||||||
circuit,
|
circuit,
|
||||||
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG],
|
||||||
description,
|
description,
|
||||||
)
|
)
|
||||||
if entity is not None:
|
if entity is not None:
|
||||||
|
@ -394,19 +392,19 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await _entities_from_descriptions(
|
await _entities_from_descriptions(
|
||||||
hass, name, all_devices, BURNER_SENSORS, api.burners
|
hass, name, all_devices, BURNER_SENSORS, api.burners, config_entry
|
||||||
)
|
)
|
||||||
except PyViCareNotSupportedFeatureError:
|
except PyViCareNotSupportedFeatureError:
|
||||||
_LOGGER.info("No burners found")
|
_LOGGER.info("No burners found")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await _entities_from_descriptions(
|
await _entities_from_descriptions(
|
||||||
hass, name, all_devices, COMPRESSOR_SENSORS, api.compressors
|
hass, name, all_devices, COMPRESSOR_SENSORS, api.compressors, config_entry
|
||||||
)
|
)
|
||||||
except PyViCareNotSupportedFeatureError:
|
except PyViCareNotSupportedFeatureError:
|
||||||
_LOGGER.info("No compressors found")
|
_LOGGER.info("No compressors found")
|
||||||
|
|
||||||
async_add_entities(all_devices)
|
async_add_devices(all_devices)
|
||||||
|
|
||||||
|
|
||||||
class ViCareSensor(SensorEntity):
|
class ViCareSensor(SensorEntity):
|
||||||
|
|
20
homeassistant/components/vicare/translations/en.json
Normal file
20
homeassistant/components/vicare/translations/en.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"flow_title": "{name}",
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"password": "Password",
|
||||||
|
"client_id": "API Key",
|
||||||
|
"username": "Username",
|
||||||
|
"heating_type": "Heating type"
|
||||||
|
},
|
||||||
|
"description": "Setup ViCare to control your Viessmann device.\nMinimum needed: username, password, API key.",
|
||||||
|
"title": "Setup ViCare"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_auth": "Invalid authentication"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,12 @@ from homeassistant.components.water_heater import (
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
SUPPORT_TARGET_TEMPERATURE,
|
||||||
WaterHeaterEntity,
|
WaterHeaterEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
from homeassistant.const import (
|
||||||
|
ATTR_TEMPERATURE,
|
||||||
|
CONF_NAME,
|
||||||
|
PRECISION_WHOLE,
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
)
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_HEATING_TYPE,
|
CONF_HEATING_TYPE,
|
||||||
|
@ -21,7 +26,6 @@ from .const import (
|
||||||
VICARE_API,
|
VICARE_API,
|
||||||
VICARE_CIRCUITS,
|
VICARE_CIRCUITS,
|
||||||
VICARE_DEVICE_CONFIG,
|
VICARE_DEVICE_CONFIG,
|
||||||
VICARE_NAME,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -66,29 +70,26 @@ def _build_entity(name, vicare_api, circuit, device_config, heating_type):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||||
"""Create the ViCare water_heater devices."""
|
"""Set up the ViCare climate platform."""
|
||||||
if discovery_info is None:
|
name = config_entry.data[CONF_NAME]
|
||||||
return
|
|
||||||
|
|
||||||
name = hass.data[DOMAIN][VICARE_NAME]
|
|
||||||
|
|
||||||
all_devices = []
|
all_devices = []
|
||||||
for circuit in hass.data[DOMAIN][VICARE_CIRCUITS]:
|
for circuit in hass.data[DOMAIN][config_entry.entry_id][VICARE_CIRCUITS]:
|
||||||
suffix = ""
|
suffix = ""
|
||||||
if len(hass.data[DOMAIN][VICARE_CIRCUITS]) > 1:
|
if len(hass.data[DOMAIN][config_entry.entry_id][VICARE_CIRCUITS]) > 1:
|
||||||
suffix = f" {circuit.id}"
|
suffix = f" {circuit.id}"
|
||||||
entity = _build_entity(
|
entity = _build_entity(
|
||||||
f"{name} Water{suffix}",
|
f"{name} Water{suffix}",
|
||||||
hass.data[DOMAIN][VICARE_API],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_API],
|
||||||
circuit,
|
circuit,
|
||||||
hass.data[DOMAIN][VICARE_DEVICE_CONFIG],
|
hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG],
|
||||||
hass.data[DOMAIN][CONF_HEATING_TYPE],
|
config_entry.data[CONF_HEATING_TYPE],
|
||||||
)
|
)
|
||||||
if entity is not None:
|
if entity is not None:
|
||||||
all_devices.append(entity)
|
all_devices.append(entity)
|
||||||
|
|
||||||
async_add_entities(all_devices)
|
async_add_devices(all_devices)
|
||||||
|
|
||||||
|
|
||||||
class ViCareWater(WaterHeaterEntity):
|
class ViCareWater(WaterHeaterEntity):
|
||||||
|
|
|
@ -318,6 +318,7 @@ FLOWS = [
|
||||||
"vera",
|
"vera",
|
||||||
"verisure",
|
"verisure",
|
||||||
"vesync",
|
"vesync",
|
||||||
|
"vicare",
|
||||||
"vilfo",
|
"vilfo",
|
||||||
"vizio",
|
"vizio",
|
||||||
"vlc_telnet",
|
"vlc_telnet",
|
||||||
|
|
|
@ -544,6 +544,10 @@ DHCP = [
|
||||||
"domain": "verisure",
|
"domain": "verisure",
|
||||||
"macaddress": "0023C1*"
|
"macaddress": "0023C1*"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"domain": "vicare",
|
||||||
|
"macaddress": "B87424*"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"domain": "yeelight",
|
"domain": "yeelight",
|
||||||
"hostname": "yeelink-*"
|
"hostname": "yeelink-*"
|
||||||
|
|
|
@ -32,6 +32,9 @@ PyTransportNSW==0.1.1
|
||||||
# homeassistant.components.camera
|
# homeassistant.components.camera
|
||||||
PyTurboJPEG==1.6.1
|
PyTurboJPEG==1.6.1
|
||||||
|
|
||||||
|
# homeassistant.components.vicare
|
||||||
|
PyViCare==2.13.1
|
||||||
|
|
||||||
# homeassistant.components.xiaomi_aqara
|
# homeassistant.components.xiaomi_aqara
|
||||||
PyXiaomiGateway==0.13.4
|
PyXiaomiGateway==0.13.4
|
||||||
|
|
||||||
|
|
20
tests/components/vicare/__init__.py
Normal file
20
tests/components/vicare/__init__.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
"""Test for ViCare."""
|
||||||
|
from homeassistant.components.vicare.const import CONF_HEATING_TYPE
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_CLIENT_ID,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_SCAN_INTERVAL,
|
||||||
|
CONF_USERNAME,
|
||||||
|
)
|
||||||
|
|
||||||
|
ENTRY_CONFIG = {
|
||||||
|
CONF_USERNAME: "foo@bar.com",
|
||||||
|
CONF_PASSWORD: "1234",
|
||||||
|
CONF_CLIENT_ID: "5678",
|
||||||
|
CONF_HEATING_TYPE: "auto",
|
||||||
|
CONF_SCAN_INTERVAL: 60,
|
||||||
|
CONF_NAME: "ViCare",
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_MAC = "B874241B7B9"
|
274
tests/components/vicare/test_config_flow.py
Normal file
274
tests/components/vicare/test_config_flow.py
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
"""Test the ViCare config flow."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from PyViCare.PyViCareUtils import PyViCareInvalidCredentialsError
|
||||||
|
|
||||||
|
from homeassistant import config_entries, data_entry_flow, setup
|
||||||
|
from homeassistant.components import dhcp
|
||||||
|
from homeassistant.components.vicare.const import (
|
||||||
|
CONF_CIRCUIT,
|
||||||
|
CONF_HEATING_TYPE,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME
|
||||||
|
|
||||||
|
from . import ENTRY_CONFIG, MOCK_MAC
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form(hass):
|
||||||
|
"""Test we get the form."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert len(result["errors"]) == 0
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vicare.config_flow.vicare_login",
|
||||||
|
return_value=None,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vicare.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.vicare.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: "foo@bar.com",
|
||||||
|
CONF_PASSWORD: "1234",
|
||||||
|
CONF_CLIENT_ID: "5678",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result2["title"] == "ViCare"
|
||||||
|
assert result2["data"] == ENTRY_CONFIG
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_import(hass):
|
||||||
|
"""Test that the import works."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vicare.config_flow.vicare_login",
|
||||||
|
return_value=True,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vicare.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.vicare.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_IMPORT},
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "Configuration.yaml"
|
||||||
|
assert result["data"] == ENTRY_CONFIG
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_import_removes_circuit(hass):
|
||||||
|
"""Test that the import works."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vicare.config_flow.vicare_login",
|
||||||
|
return_value=True,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vicare.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.vicare.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
ENTRY_CONFIG[CONF_CIRCUIT] = 1
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_IMPORT},
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "Configuration.yaml"
|
||||||
|
assert result["data"] == ENTRY_CONFIG
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_import_adds_heating_type(hass):
|
||||||
|
"""Test that the import works."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vicare.config_flow.vicare_login",
|
||||||
|
return_value=True,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vicare.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.vicare.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
del ENTRY_CONFIG[CONF_HEATING_TYPE]
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_IMPORT},
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "Configuration.yaml"
|
||||||
|
assert result["data"] == ENTRY_CONFIG
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_invalid_login(hass) -> None:
|
||||||
|
"""Test a flow with an invalid Vicare login."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vicare.config_flow.vicare_login",
|
||||||
|
side_effect=PyViCareInvalidCredentialsError,
|
||||||
|
):
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: "foo@bar.com",
|
||||||
|
CONF_PASSWORD: "1234",
|
||||||
|
CONF_CLIENT_ID: "5678",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result2["step_id"] == "user"
|
||||||
|
assert result2["errors"] == {"base": "invalid_auth"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_dhcp(hass):
|
||||||
|
"""Test we can setup from dhcp."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_DHCP},
|
||||||
|
data={
|
||||||
|
dhcp.MAC_ADDRESS: MOCK_MAC,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vicare.config_flow.vicare_login",
|
||||||
|
return_value=None,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vicare.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.vicare.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: "foo@bar.com",
|
||||||
|
CONF_PASSWORD: "1234",
|
||||||
|
CONF_CLIENT_ID: "5678",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result2["title"] == "ViCare"
|
||||||
|
assert result2["data"] == ENTRY_CONFIG
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_import_already_configured(hass):
|
||||||
|
"""Test that configuring same instance is rejectes."""
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="Configuration.yaml",
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_IMPORT},
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_import_single_instance_allowed(hass):
|
||||||
|
"""Test that configuring more than one instance is rejected."""
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="Configuration.yaml",
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_IMPORT},
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_dhcp_single_instance_allowed(hass):
|
||||||
|
"""Test that configuring more than one instance is rejected."""
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="Configuration.yaml",
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_DHCP},
|
||||||
|
data={
|
||||||
|
dhcp.MAC_ADDRESS: MOCK_MAC,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "single_instance_allowed"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_input_single_instance_allowed(hass):
|
||||||
|
"""Test that configuring more than one instance is rejected."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
mock_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="ViCare",
|
||||||
|
data=ENTRY_CONFIG,
|
||||||
|
)
|
||||||
|
mock_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "single_instance_allowed"
|
Loading…
Add table
Add a link
Reference in a new issue