mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Add config flow to fibaro (#65203)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
00b53502fb
commit
e844c2380a
11
.coveragerc
11
.coveragerc
@ -335,7 +335,16 @@ omit =
|
||||
homeassistant/components/faa_delays/binary_sensor.py
|
||||
homeassistant/components/fastdotcom/*
|
||||
homeassistant/components/ffmpeg/camera.py
|
||||
homeassistant/components/fibaro/*
|
||||
homeassistant/components/fibaro/__init__.py
|
||||
homeassistant/components/fibaro/binary_sensor.py
|
||||
homeassistant/components/fibaro/climate.py
|
||||
homeassistant/components/fibaro/cover.py
|
||||
homeassistant/components/fibaro/light.py
|
||||
homeassistant/components/fibaro/lock.py
|
||||
homeassistant/components/fibaro/scene.py
|
||||
homeassistant/components/fibaro/sensor.py
|
||||
homeassistant/components/fibaro/switch.py
|
||||
homeassistant/components/filesize/sensor.py
|
||||
homeassistant/components/fints/sensor.py
|
||||
homeassistant/components/fireservicerota/__init__.py
|
||||
homeassistant/components/fireservicerota/binary_sensor.py
|
||||
|
@ -306,6 +306,8 @@ tests/components/faa_delays/* @ntilley905
|
||||
homeassistant/components/fan/* @home-assistant/core
|
||||
tests/components/fan/* @home-assistant/core
|
||||
homeassistant/components/fastdotcom/* @rohankapoorcom
|
||||
homeassistant/components/fibaro/* @rappenze
|
||||
tests/components/fibaro/* @rappenze
|
||||
homeassistant/components/file/* @fabaff
|
||||
tests/components/file/* @fabaff
|
||||
homeassistant/components/filesize/* @gjohansson-ST
|
||||
|
@ -3,10 +3,13 @@ from __future__ import annotations
|
||||
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from fiblary3.client.v4.client import Client as FibaroClient, StateHandler
|
||||
from fiblary3.common.exceptions import HTTPException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_ARMED,
|
||||
ATTR_BATTERY_LEVEL,
|
||||
@ -17,16 +20,17 @@ from homeassistant.const import (
|
||||
CONF_URL,
|
||||
CONF_USERNAME,
|
||||
CONF_WHITE_VALUE,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import discovery
|
||||
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util import convert, slugify
|
||||
|
||||
from .const import CONF_IMPORT_PLUGINS, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_CURRENT_POWER_W = "current_power_w"
|
||||
@ -37,8 +41,7 @@ CONF_DIMMING = "dimming"
|
||||
CONF_GATEWAYS = "gateways"
|
||||
CONF_PLUGINS = "plugins"
|
||||
CONF_RESET_COLOR = "reset_color"
|
||||
DOMAIN = "fibaro"
|
||||
FIBARO_CONTROLLERS = "fibaro_controllers"
|
||||
FIBARO_CONTROLLER = "fibaro_controller"
|
||||
FIBARO_DEVICES = "fibaro_devices"
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
@ -102,11 +105,14 @@ GATEWAY_CONFIG = vol.Schema(
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{vol.Required(CONF_GATEWAYS): vol.All(cv.ensure_list, [GATEWAY_CONFIG])}
|
||||
)
|
||||
},
|
||||
vol.All(
|
||||
cv.deprecated(DOMAIN),
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{vol.Required(CONF_GATEWAYS): vol.All(cv.ensure_list, [GATEWAY_CONFIG])}
|
||||
)
|
||||
},
|
||||
),
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
@ -116,21 +122,19 @@ class FibaroController:
|
||||
|
||||
def __init__(self, config):
|
||||
"""Initialize the Fibaro controller."""
|
||||
|
||||
self._client = FibaroClient(
|
||||
config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||
)
|
||||
self._scene_map = None
|
||||
# Whether to import devices from plugins
|
||||
self._import_plugins = config[CONF_PLUGINS]
|
||||
self._device_config = config[CONF_DEVICE_CONFIG]
|
||||
self._import_plugins = config[CONF_IMPORT_PLUGINS]
|
||||
self._room_map = None # Mapping roomId to room object
|
||||
self._device_map = None # Mapping deviceId to device object
|
||||
self.fibaro_devices = None # List of devices by type
|
||||
self._callbacks = {} # Update value callbacks by deviceId
|
||||
self._state_handler = None # Fiblary's StateHandler object
|
||||
self._excluded_devices = config[CONF_EXCLUDE]
|
||||
self.hub_serial = None # Unique serial number of the hub
|
||||
self.name = None # The friendly name of the hub
|
||||
|
||||
def connect(self):
|
||||
"""Start the communication with the Fibaro controller."""
|
||||
@ -138,6 +142,7 @@ class FibaroController:
|
||||
login = self._client.login.get()
|
||||
info = self._client.info.get()
|
||||
self.hub_serial = slugify(info.serialNumber)
|
||||
self.name = slugify(info.hcName)
|
||||
except AssertionError:
|
||||
_LOGGER.error("Can't connect to Fibaro HC. Please check URL")
|
||||
return False
|
||||
@ -152,6 +157,23 @@ class FibaroController:
|
||||
self._read_scenes()
|
||||
return True
|
||||
|
||||
def connect_with_error_handling(self) -> None:
|
||||
"""Translate connect errors to easily differentiate auth and connect failures.
|
||||
|
||||
When there is a better error handling in the used library this can be improved.
|
||||
"""
|
||||
try:
|
||||
connected = self.connect()
|
||||
if not connected:
|
||||
raise FibaroConnectFailed("Connect status is false")
|
||||
except HTTPException as http_ex:
|
||||
if http_ex.details == "Forbidden":
|
||||
raise FibaroAuthFailed from http_ex
|
||||
|
||||
raise FibaroConnectFailed from http_ex
|
||||
except Exception as ex:
|
||||
raise FibaroConnectFailed from ex
|
||||
|
||||
def enable_state_handler(self):
|
||||
"""Start StateHandler thread for monitoring updates."""
|
||||
self._state_handler = StateHandler(self._client, self._on_state_change)
|
||||
@ -299,16 +321,11 @@ class FibaroController:
|
||||
device.ha_id = (
|
||||
f"{slugify(room_name)}_{slugify(device.name)}_{device.id}"
|
||||
)
|
||||
if (
|
||||
device.enabled
|
||||
and (
|
||||
"isPlugin" not in device
|
||||
or (not device.isPlugin or self._import_plugins)
|
||||
)
|
||||
and device.ha_id not in self._excluded_devices
|
||||
if device.enabled and (
|
||||
"isPlugin" not in device
|
||||
or (not device.isPlugin or self._import_plugins)
|
||||
):
|
||||
device.mapped_type = self._map_device_to_type(device)
|
||||
device.device_config = self._device_config.get(device.ha_id, {})
|
||||
else:
|
||||
device.mapped_type = None
|
||||
if (dtype := device.mapped_type) is None:
|
||||
@ -357,39 +374,78 @@ class FibaroController:
|
||||
pass
|
||||
|
||||
|
||||
def setup(hass: HomeAssistant, base_config: ConfigType) -> bool:
|
||||
"""Set up the Fibaro Component."""
|
||||
async def async_setup(hass: HomeAssistant, base_config: ConfigType) -> bool:
|
||||
"""Migrate configuration from configuration.yaml."""
|
||||
if DOMAIN not in base_config:
|
||||
return True
|
||||
gateways = base_config[DOMAIN][CONF_GATEWAYS]
|
||||
hass.data[FIBARO_CONTROLLERS] = {}
|
||||
|
||||
def stop_fibaro(event):
|
||||
"""Stop Fibaro Thread."""
|
||||
_LOGGER.info("Shutting down Fibaro connection")
|
||||
for controller in hass.data[FIBARO_CONTROLLERS].values():
|
||||
controller.disable_state_handler()
|
||||
|
||||
hass.data[FIBARO_DEVICES] = {}
|
||||
for platform in PLATFORMS:
|
||||
hass.data[FIBARO_DEVICES][platform] = []
|
||||
|
||||
for gateway in gateways:
|
||||
controller = FibaroController(gateway)
|
||||
if controller.connect():
|
||||
hass.data[FIBARO_CONTROLLERS][controller.hub_serial] = controller
|
||||
for platform in PLATFORMS:
|
||||
hass.data[FIBARO_DEVICES][platform].extend(
|
||||
controller.fibaro_devices[platform]
|
||||
)
|
||||
|
||||
if hass.data[FIBARO_CONTROLLERS]:
|
||||
for platform in PLATFORMS:
|
||||
discovery.load_platform(hass, platform, DOMAIN, {}, base_config)
|
||||
for controller in hass.data[FIBARO_CONTROLLERS].values():
|
||||
controller.enable_state_handler()
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_fibaro)
|
||||
if gateways is None:
|
||||
return True
|
||||
|
||||
return False
|
||||
# check if already configured
|
||||
if hass.config_entries.async_entries(DOMAIN):
|
||||
return True
|
||||
|
||||
for gateway in gateways:
|
||||
# prepare new config based on configuration.yaml
|
||||
conf = {
|
||||
CONF_URL: gateway[CONF_URL],
|
||||
CONF_USERNAME: gateway[CONF_USERNAME],
|
||||
CONF_PASSWORD: gateway[CONF_PASSWORD],
|
||||
CONF_IMPORT_PLUGINS: gateway[CONF_PLUGINS],
|
||||
}
|
||||
|
||||
# import into config flow based configuration
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=conf
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _init_controller(data: dict[str, Any]) -> FibaroController:
|
||||
"""Validate the user input allows us to connect to fibaro."""
|
||||
controller = FibaroController(data)
|
||||
controller.connect_with_error_handling()
|
||||
return controller
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up the Fibaro Component."""
|
||||
try:
|
||||
controller = await hass.async_add_executor_job(_init_controller, entry.data)
|
||||
except FibaroConnectFailed as connect_ex:
|
||||
raise ConfigEntryNotReady(
|
||||
f"Could not connect to controller at {entry.data[CONF_URL]}"
|
||||
) from connect_ex
|
||||
except FibaroAuthFailed:
|
||||
return False
|
||||
|
||||
data: dict[str, Any] = {}
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = data
|
||||
data[FIBARO_CONTROLLER] = controller
|
||||
devices = data[FIBARO_DEVICES] = {}
|
||||
for platform in PLATFORMS:
|
||||
devices[platform] = [*controller.fibaro_devices[platform]]
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
controller.enable_state_handler()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
_LOGGER.info("Shutting down Fibaro connection")
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id][FIBARO_CONTROLLER].disable_state_handler()
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
class FibaroDevice(Entity):
|
||||
@ -519,3 +575,11 @@ class FibaroDevice(Entity):
|
||||
pass
|
||||
|
||||
return attr
|
||||
|
||||
|
||||
class FibaroConnectFailed(HomeAssistantError):
|
||||
"""Error to indicate we cannot connect to fibaro home center."""
|
||||
|
||||
|
||||
class FibaroAuthFailed(HomeAssistantError):
|
||||
"""Error to indicate that authentication failed on fibaro home center."""
|
||||
|
@ -2,16 +2,16 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_ICON
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
SENSOR_TYPES = {
|
||||
"com.fibaro.floodSensor": ["Flood", "mdi:water", "flood"],
|
||||
@ -28,20 +28,18 @@ SENSOR_TYPES = {
|
||||
}
|
||||
|
||||
|
||||
def setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Perform the setup for Fibaro controller devices."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
add_entities(
|
||||
async_add_entities(
|
||||
[
|
||||
FibaroBinarySensor(device)
|
||||
for device in hass.data[FIBARO_DEVICES]["binary_sensor"]
|
||||
for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES][
|
||||
"binary_sensor"
|
||||
]
|
||||
],
|
||||
True,
|
||||
)
|
||||
@ -54,9 +52,8 @@ class FibaroBinarySensor(FibaroDevice, BinarySensorEntity):
|
||||
"""Initialize the binary_sensor."""
|
||||
self._state = None
|
||||
super().__init__(fibaro_device)
|
||||
self.entity_id = f"{DOMAIN}.{self.ha_id}"
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
|
||||
stype = None
|
||||
devconf = fibaro_device.device_config
|
||||
if fibaro_device.type in SENSOR_TYPES:
|
||||
stype = fibaro_device.type
|
||||
elif fibaro_device.baseType in SENSOR_TYPES:
|
||||
@ -67,9 +64,6 @@ class FibaroBinarySensor(FibaroDevice, BinarySensorEntity):
|
||||
else:
|
||||
self._device_class = None
|
||||
self._icon = None
|
||||
# device_config overrides:
|
||||
self._device_class = devconf.get(CONF_DEVICE_CLASS, self._device_class)
|
||||
self._icon = devconf.get(CONF_ICON, self._icon)
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.components.climate import ENTITY_ID_FORMAT, ClimateEntity
|
||||
from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_COOL,
|
||||
@ -17,12 +17,13 @@ from homeassistant.components.climate.const import (
|
||||
SUPPORT_PRESET_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
PRESET_RESUME = "resume"
|
||||
PRESET_MOIST = "moist"
|
||||
@ -98,18 +99,17 @@ HA_OPMODES_HVAC = {
|
||||
}
|
||||
|
||||
|
||||
def setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Perform the setup for Fibaro controller devices."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
add_entities(
|
||||
[FibaroThermostat(device) for device in hass.data[FIBARO_DEVICES]["climate"]],
|
||||
async_add_entities(
|
||||
[
|
||||
FibaroThermostat(device)
|
||||
for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["climate"]
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
@ -125,7 +125,7 @@ class FibaroThermostat(FibaroDevice, ClimateEntity):
|
||||
self._op_mode_device = None
|
||||
self._fan_mode_device = None
|
||||
self._support_flags = 0
|
||||
self.entity_id = f"climate.{self.ha_id}"
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
|
||||
self._hvac_support = []
|
||||
self._preset_support = []
|
||||
self._fan_support = []
|
||||
|
81
homeassistant/components/fibaro/config_flow.py
Normal file
81
homeassistant/components/fibaro/config_flow.py
Normal file
@ -0,0 +1,81 @@
|
||||
"""Config flow for Fibaro integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import FibaroAuthFailed, FibaroConnectFailed, FibaroController
|
||||
from .const import CONF_IMPORT_PLUGINS, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_URL): str,
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Optional(CONF_IMPORT_PLUGINS, default=False): bool,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _connect_to_fibaro(data: dict[str, Any]) -> FibaroController:
|
||||
"""Validate the user input allows us to connect to fibaro."""
|
||||
controller = FibaroController(data)
|
||||
controller.connect_with_error_handling()
|
||||
return controller
|
||||
|
||||
|
||||
async def _validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
|
||||
"""
|
||||
controller = await hass.async_add_executor_job(_connect_to_fibaro, data)
|
||||
|
||||
_LOGGER.debug(
|
||||
"Successfully connected to fibaro home center %s with name %s",
|
||||
controller.hub_serial,
|
||||
controller.name,
|
||||
)
|
||||
return {"serial_number": controller.hub_serial, "name": controller.name}
|
||||
|
||||
|
||||
class FibaroConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Fibaro."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
try:
|
||||
info = await _validate_input(self.hass, user_input)
|
||||
except FibaroConnectFailed:
|
||||
errors["base"] = "cannot_connect"
|
||||
except FibaroAuthFailed:
|
||||
errors["base"] = "invalid_auth"
|
||||
else:
|
||||
await self.async_set_unique_id(info["serial_number"])
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(title=info["name"], data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||
)
|
||||
|
||||
async def async_step_import(self, import_config: ConfigType | None) -> FlowResult:
|
||||
"""Import a config entry."""
|
||||
return await self.async_step_user(import_config)
|
4
homeassistant/components/fibaro/const.py
Normal file
4
homeassistant/components/fibaro/const.py
Normal file
@ -0,0 +1,4 @@
|
||||
"""Constants for the Fibaro integration."""
|
||||
|
||||
DOMAIN = "fibaro"
|
||||
CONF_IMPORT_PLUGINS = "import_plugins"
|
@ -4,28 +4,29 @@ from __future__ import annotations
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
ATTR_TILT_POSITION,
|
||||
DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
CoverEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
def setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Fibaro covers."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
add_entities(
|
||||
[FibaroCover(device) for device in hass.data[FIBARO_DEVICES]["cover"]], True
|
||||
async_add_entities(
|
||||
[
|
||||
FibaroCover(device)
|
||||
for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["cover"]
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
@ -35,7 +36,7 @@ class FibaroCover(FibaroDevice, CoverEntity):
|
||||
def __init__(self, fibaro_device):
|
||||
"""Initialize the Vera device."""
|
||||
super().__init__(fibaro_device)
|
||||
self.entity_id = f"{DOMAIN}.{self.ha_id}"
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
|
||||
|
||||
@staticmethod
|
||||
def bound(position):
|
||||
|
@ -8,19 +8,19 @@ from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_HS_COLOR,
|
||||
ATTR_WHITE_VALUE,
|
||||
DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
LightEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_WHITE_VALUE
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
import homeassistant.util.color as color_util
|
||||
|
||||
from . import CONF_COLOR, CONF_DIMMING, CONF_RESET_COLOR, FIBARO_DEVICES, FibaroDevice
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
def scaleto255(value):
|
||||
@ -40,18 +40,18 @@ def scaleto100(value):
|
||||
return max(0, min(100, ((value * 100.0) / 255.0)))
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Perform the setup for Fibaro controller devices."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
async_add_entities(
|
||||
[FibaroLight(device) for device in hass.data[FIBARO_DEVICES]["light"]], True
|
||||
[
|
||||
FibaroLight(device)
|
||||
for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["light"]
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
@ -67,8 +67,7 @@ class FibaroLight(FibaroDevice, LightEntity):
|
||||
self._update_lock = asyncio.Lock()
|
||||
self._white = 0
|
||||
|
||||
devconf = fibaro_device.device_config
|
||||
self._reset_color = devconf.get(CONF_RESET_COLOR, False)
|
||||
self._reset_color = False
|
||||
supports_color = (
|
||||
"color" in fibaro_device.properties
|
||||
or "colorComponents" in fibaro_device.properties
|
||||
@ -91,15 +90,15 @@ class FibaroLight(FibaroDevice, LightEntity):
|
||||
)
|
||||
|
||||
# Configuration can override default capability detection
|
||||
if devconf.get(CONF_DIMMING, supports_dimming):
|
||||
if supports_dimming:
|
||||
self._supported_flags |= SUPPORT_BRIGHTNESS
|
||||
if devconf.get(CONF_COLOR, supports_color):
|
||||
if supports_color:
|
||||
self._supported_flags |= SUPPORT_COLOR
|
||||
if devconf.get(CONF_WHITE_VALUE, supports_white_v):
|
||||
if supports_white_v:
|
||||
self._supported_flags |= SUPPORT_WHITE_VALUE
|
||||
|
||||
super().__init__(fibaro_device)
|
||||
self.entity_id = f"{DOMAIN}.{self.ha_id}"
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
|
@ -1,26 +1,27 @@
|
||||
"""Support for Fibaro locks."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.lock import DOMAIN, LockEntity
|
||||
from homeassistant.components.lock import ENTITY_ID_FORMAT, LockEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
def setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Fibaro locks."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
add_entities(
|
||||
[FibaroLock(device) for device in hass.data[FIBARO_DEVICES]["lock"]], True
|
||||
async_add_entities(
|
||||
[
|
||||
FibaroLock(device)
|
||||
for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["lock"]
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
@ -31,7 +32,7 @@ class FibaroLock(FibaroDevice, LockEntity):
|
||||
"""Initialize the Fibaro device."""
|
||||
self._state = False
|
||||
super().__init__(fibaro_device)
|
||||
self.entity_id = f"{DOMAIN}.{self.ha_id}"
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
|
||||
|
||||
def lock(self, **kwargs):
|
||||
"""Lock the device."""
|
||||
|
@ -3,7 +3,8 @@
|
||||
"name": "Fibaro",
|
||||
"documentation": "https://www.home-assistant.io/integrations/fibaro",
|
||||
"requirements": ["fiblary3==0.1.8"],
|
||||
"codeowners": [],
|
||||
"codeowners": ["@rappenze"],
|
||||
"iot_class": "local_push",
|
||||
"config_flow": true,
|
||||
"loggers": ["fiblary3"]
|
||||
}
|
||||
|
@ -4,25 +4,26 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.scene import Scene
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Perform the setup for Fibaro scenes."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
async_add_entities(
|
||||
[FibaroScene(scene) for scene in hass.data[FIBARO_DEVICES]["scene"]], True
|
||||
[
|
||||
FibaroScene(scene)
|
||||
for scene in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["scene"]
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
|
@ -4,11 +4,12 @@ from __future__ import annotations
|
||||
from contextlib import suppress
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
ENERGY_KILO_WATT_HOUR,
|
||||
@ -19,10 +20,10 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import convert
|
||||
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
SENSOR_TYPES = {
|
||||
"com.fibaro.temperatureSensor": [
|
||||
@ -54,25 +55,21 @@ SENSOR_TYPES = {
|
||||
}
|
||||
|
||||
|
||||
def setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Fibaro controller devices."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
entities: list[SensorEntity] = []
|
||||
for device in hass.data[FIBARO_DEVICES]["sensor"]:
|
||||
for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["sensor"]:
|
||||
entities.append(FibaroSensor(device))
|
||||
for device_type in ("cover", "light", "switch"):
|
||||
for device in hass.data[FIBARO_DEVICES][device_type]:
|
||||
for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES][device_type]:
|
||||
if "energy" in device.interfaces:
|
||||
entities.append(FibaroEnergySensor(device))
|
||||
|
||||
add_entities(entities, True)
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class FibaroSensor(FibaroDevice, SensorEntity):
|
||||
@ -83,7 +80,7 @@ class FibaroSensor(FibaroDevice, SensorEntity):
|
||||
self.current_value = None
|
||||
self.last_changed_time = None
|
||||
super().__init__(fibaro_device)
|
||||
self.entity_id = f"{DOMAIN}.{self.ha_id}"
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
|
||||
if fibaro_device.type in SENSOR_TYPES:
|
||||
self._unit = SENSOR_TYPES[fibaro_device.type][1]
|
||||
self._icon = SENSOR_TYPES[fibaro_device.type][2]
|
||||
@ -139,7 +136,7 @@ class FibaroEnergySensor(FibaroDevice, SensorEntity):
|
||||
def __init__(self, fibaro_device):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(fibaro_device)
|
||||
self.entity_id = f"{DOMAIN}.{self.ha_id}_energy"
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(f"{self.ha_id}_energy")
|
||||
self._attr_name = f"{fibaro_device.friendly_name} Energy"
|
||||
self._attr_unique_id = f"{fibaro_device.unique_id_str}_energy"
|
||||
|
||||
|
22
homeassistant/components/fibaro/strings.json
Normal file
22
homeassistant/components/fibaro/strings.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"url": "URL in the format http://HOST/api/",
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"import_plugins": "Import entities from fibaro plugins?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +1,28 @@
|
||||
"""Support for Fibaro switches."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.switch import DOMAIN, SwitchEntity
|
||||
from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import convert
|
||||
|
||||
from . import FIBARO_DEVICES, FibaroDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
def setup_platform(
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Fibaro switches."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
add_entities(
|
||||
[FibaroSwitch(device) for device in hass.data[FIBARO_DEVICES]["switch"]], True
|
||||
async_add_entities(
|
||||
[
|
||||
FibaroSwitch(device)
|
||||
for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["switch"]
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
@ -32,7 +33,7 @@ class FibaroSwitch(FibaroDevice, SwitchEntity):
|
||||
"""Initialize the Fibaro device."""
|
||||
self._state = False
|
||||
super().__init__(fibaro_device)
|
||||
self.entity_id = f"{DOMAIN}.{self.ha_id}"
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn device on."""
|
||||
|
22
homeassistant/components/fibaro/translations/en.json
Normal file
22
homeassistant/components/fibaro/translations/en.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect",
|
||||
"invalid_auth": "Invalid authentication",
|
||||
"unknown": "Unexpected error"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"url": "URL in the format http://HOST/api/",
|
||||
"import_plugins": "Import entities from fibaro plugins?",
|
||||
"password": "Password",
|
||||
"username": "Username"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -97,6 +97,7 @@ FLOWS = {
|
||||
"evil_genius_labs",
|
||||
"ezviz",
|
||||
"faa_delays",
|
||||
"fibaro",
|
||||
"filesize",
|
||||
"fireservicerota",
|
||||
"fivem",
|
||||
|
@ -433,6 +433,9 @@ faadelays==0.0.7
|
||||
# homeassistant.components.feedreader
|
||||
feedparser==6.0.2
|
||||
|
||||
# homeassistant.components.fibaro
|
||||
fiblary3==0.1.8
|
||||
|
||||
# homeassistant.components.fivem
|
||||
fivem-api==0.1.2
|
||||
|
||||
|
1
tests/components/fibaro/__init__.py
Normal file
1
tests/components/fibaro/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Tests for the Fibaro integration."""
|
204
tests/components/fibaro/test_config_flow.py
Normal file
204
tests/components/fibaro/test_config_flow.py
Normal file
@ -0,0 +1,204 @@
|
||||
"""Test the Fibaro config flow."""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from fiblary3.common.exceptions import HTTPException
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.fibaro import DOMAIN
|
||||
from homeassistant.components.fibaro.const import CONF_IMPORT_PLUGINS
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
||||
|
||||
TEST_SERIALNUMBER = "HC2-111111"
|
||||
TEST_NAME = "my_fibaro_home_center"
|
||||
TEST_URL = "http://192.168.1.1/api/"
|
||||
TEST_USERNAME = "user"
|
||||
TEST_PASSWORD = "password"
|
||||
|
||||
|
||||
@pytest.fixture(name="fibaro_client", autouse=True)
|
||||
def fibaro_client_fixture():
|
||||
"""Mock common methods and attributes of fibaro client."""
|
||||
info_mock = Mock()
|
||||
info_mock.get.return_value = Mock(serialNumber=TEST_SERIALNUMBER, hcName=TEST_NAME)
|
||||
|
||||
array_mock = Mock()
|
||||
array_mock.list.return_value = []
|
||||
|
||||
with patch("fiblary3.client.v4.client.Client.__init__", return_value=None,), patch(
|
||||
"fiblary3.client.v4.client.Client.info",
|
||||
info_mock,
|
||||
create=True,
|
||||
), patch("fiblary3.client.v4.client.Client.rooms", array_mock, create=True,), patch(
|
||||
"fiblary3.client.v4.client.Client.devices",
|
||||
array_mock,
|
||||
create=True,
|
||||
), patch(
|
||||
"fiblary3.client.v4.client.Client.scenes",
|
||||
array_mock,
|
||||
create=True,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
async def test_config_flow_user_initiated_success(hass):
|
||||
"""Successful flow manually initialized by the user."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
login_mock = Mock()
|
||||
login_mock.get.return_value = Mock(status=True)
|
||||
with patch("fiblary3.client.v4.client.Client.login", login_mock, create=True):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_URL: TEST_URL,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == TEST_NAME
|
||||
assert result["data"] == {
|
||||
CONF_URL: TEST_URL,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
CONF_IMPORT_PLUGINS: False,
|
||||
}
|
||||
|
||||
|
||||
async def test_config_flow_user_initiated_connect_failure(hass):
|
||||
"""Connect failure in flow manually initialized by the user."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
login_mock = Mock()
|
||||
login_mock.get.return_value = Mock(status=False)
|
||||
with patch("fiblary3.client.v4.client.Client.login", login_mock, create=True):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_URL: TEST_URL,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_config_flow_user_initiated_auth_failure(hass):
|
||||
"""Authentication failure in flow manually initialized by the user."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
login_mock = Mock()
|
||||
login_mock.get.side_effect = HTTPException(details="Forbidden")
|
||||
with patch("fiblary3.client.v4.client.Client.login", login_mock, create=True):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_URL: TEST_URL,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_config_flow_user_initiated_unknown_failure_1(hass):
|
||||
"""Unknown failure in flow manually initialized by the user."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
login_mock = Mock()
|
||||
login_mock.get.side_effect = HTTPException(details="Any")
|
||||
with patch("fiblary3.client.v4.client.Client.login", login_mock, create=True):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_URL: TEST_URL,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_config_flow_user_initiated_unknown_failure_2(hass):
|
||||
"""Unknown failure in flow manually initialized by the user."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_URL: TEST_URL,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_config_flow_import(hass):
|
||||
"""Test for importing config from configuration.yaml."""
|
||||
login_mock = Mock()
|
||||
login_mock.get.return_value = Mock(status=True)
|
||||
with patch("fiblary3.client.v4.client.Client.login", login_mock, create=True):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_URL: TEST_URL,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
CONF_IMPORT_PLUGINS: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == TEST_NAME
|
||||
assert result["data"] == {
|
||||
CONF_URL: TEST_URL,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
CONF_IMPORT_PLUGINS: False,
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user