Add lutron_caseta config entries (#34133)
* lutron_caseta: allow for multiple bridges; use config entries Refactor to use config entries/flows, but only implemented import (async_setup) flow handler for now. * lutron_caseta: config_flow.py pylint hint Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * lutron_caseta: tweaks to __init__.py per PR feedback * lutron_caseta: add config_flow tests * lutron_caseta: verify connectivity to bridge check connectivity before creating config entry; cleanup translation/strings * lutron_caseta: allow for multiple bridges; use config entries Refactor to use config entries/flows, but only implemented import (async_setup) flow handler for now. * lutron_caseta: config_flow.py pylint hint Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * lutron_caseta: tweaks to __init__.py per PR feedback * lutron_caseta: add config_flow tests * lutron_caseta: verify connectivity to bridge check connectivity before creating config entry; cleanup translation/strings * lutron_caseta: add error logging when exception is encountered checking connectivity * lutron_caseta: tests mock bridge creation, not ha-side connectivity check * lutron_caseta: catch more specific Error types while checking bridge conn. Co-authored-by: Martin Hjelmare <marhje52@gmail.com>pull/35497/head
parent
9d83059f14
commit
d072091926
|
@ -4,30 +4,30 @@ import logging
|
|||
from pylutron_caseta.smartbridge import Smartbridge
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.helpers import discovery
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import CONF_CA_CERTS, CONF_CERTFILE, CONF_KEYFILE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
LUTRON_CASETA_SMARTBRIDGE = "lutron_smartbridge"
|
||||
|
||||
DOMAIN = "lutron_caseta"
|
||||
|
||||
CONF_KEYFILE = "keyfile"
|
||||
CONF_CERTFILE = "certfile"
|
||||
CONF_CA_CERTS = "ca_certs"
|
||||
DATA_BRIDGE_CONFIG = "lutron_caseta_bridges"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Required(CONF_KEYFILE): cv.string,
|
||||
vol.Required(CONF_CERTFILE): cv.string,
|
||||
vol.Required(CONF_CA_CERTS): cv.string,
|
||||
}
|
||||
DOMAIN: vol.All(
|
||||
cv.ensure_list,
|
||||
[
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Required(CONF_KEYFILE): cv.string,
|
||||
vol.Required(CONF_CERTFILE): cv.string,
|
||||
vol.Required(CONF_CA_CERTS): cv.string,
|
||||
}
|
||||
],
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
|
@ -39,29 +39,57 @@ LUTRON_CASETA_COMPONENTS = ["light", "switch", "cover", "scene", "fan", "binary_
|
|||
async def async_setup(hass, base_config):
|
||||
"""Set up the Lutron component."""
|
||||
|
||||
config = base_config.get(DOMAIN)
|
||||
keyfile = hass.config.path(config[CONF_KEYFILE])
|
||||
certfile = hass.config.path(config[CONF_CERTFILE])
|
||||
ca_certs = hass.config.path(config[CONF_CA_CERTS])
|
||||
bridge = Smartbridge.create_tls(
|
||||
hostname=config[CONF_HOST],
|
||||
keyfile=keyfile,
|
||||
certfile=certfile,
|
||||
ca_certs=ca_certs,
|
||||
)
|
||||
hass.data[LUTRON_CASETA_SMARTBRIDGE] = bridge
|
||||
await bridge.connect()
|
||||
if not hass.data[LUTRON_CASETA_SMARTBRIDGE].is_connected():
|
||||
_LOGGER.error(
|
||||
"Unable to connect to Lutron smartbridge at %s", config[CONF_HOST]
|
||||
bridge_configs = base_config.get(DOMAIN)
|
||||
|
||||
if not bridge_configs:
|
||||
return True
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
for config in bridge_configs:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
# extract the config keys one-by-one just to be explicit
|
||||
data={
|
||||
CONF_HOST: config[CONF_HOST],
|
||||
CONF_KEYFILE: config[CONF_KEYFILE],
|
||||
CONF_CERTFILE: config[CONF_CERTFILE],
|
||||
CONF_CA_CERTS: config[CONF_CA_CERTS],
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry):
|
||||
"""Set up a bridge from a config entry."""
|
||||
|
||||
host = config_entry.data[CONF_HOST]
|
||||
keyfile = config_entry.data[CONF_KEYFILE]
|
||||
certfile = config_entry.data[CONF_CERTFILE]
|
||||
ca_certs = config_entry.data[CONF_CA_CERTS]
|
||||
|
||||
bridge = Smartbridge.create_tls(
|
||||
hostname=host, keyfile=keyfile, certfile=certfile, ca_certs=ca_certs
|
||||
)
|
||||
|
||||
await bridge.connect()
|
||||
if not bridge.is_connected():
|
||||
_LOGGER.error("Unable to connect to Lutron Caseta bridge at %s", host)
|
||||
return False
|
||||
|
||||
_LOGGER.info("Connected to Lutron smartbridge at %s", config[CONF_HOST])
|
||||
_LOGGER.debug("Connected to Lutron Caseta bridge at %s", host)
|
||||
|
||||
# Store this bridge (keyed by entry_id) so it can be retrieved by the
|
||||
# components we're setting up.
|
||||
hass.data[DOMAIN][config_entry.entry_id] = bridge
|
||||
|
||||
for component in LUTRON_CASETA_COMPONENTS:
|
||||
hass.async_create_task(
|
||||
discovery.async_load_platform(hass, component, DOMAIN, {}, config)
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, component)
|
||||
)
|
||||
|
||||
return True
|
||||
|
|
|
@ -6,14 +6,20 @@ from homeassistant.components.binary_sensor import (
|
|||
BinarySensorEntity,
|
||||
)
|
||||
|
||||
from . import LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice
|
||||
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the Lutron Caseta lights."""
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Lutron Caseta binary_sensor platform.
|
||||
|
||||
Adds occupancy groups from the Caseta bridge associated with the
|
||||
config_entry as binary_sensor entities.
|
||||
"""
|
||||
|
||||
entities = []
|
||||
bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE]
|
||||
bridge = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
occupancy_groups = bridge.occupancy_groups
|
||||
|
||||
for occupancy_group in occupancy_groups.values():
|
||||
entity = LutronOccupancySensor(occupancy_group, bridge)
|
||||
entities.append(entity)
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
"""Config flow for Lutron Caseta."""
|
||||
import logging
|
||||
|
||||
from pylutron_caseta.smartbridge import Smartbridge
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_HOST
|
||||
|
||||
from . import DOMAIN # pylint: disable=unused-import
|
||||
from .const import (
|
||||
ABORT_REASON_ALREADY_CONFIGURED,
|
||||
ABORT_REASON_CANNOT_CONNECT,
|
||||
CONF_CA_CERTS,
|
||||
CONF_CERTFILE,
|
||||
CONF_KEYFILE,
|
||||
ERROR_CANNOT_CONNECT,
|
||||
STEP_IMPORT_FAILED,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ENTRY_DEFAULT_TITLE = "Caséta bridge"
|
||||
|
||||
|
||||
class LutronCasetaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle Lutron Caseta config flow."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize a Lutron Caseta flow."""
|
||||
self.data = {}
|
||||
|
||||
async def async_step_import(self, import_info):
|
||||
"""Import a new Caseta bridge as a config entry.
|
||||
|
||||
This flow is triggered by `async_setup`.
|
||||
"""
|
||||
|
||||
# Abort if existing entry with matching host exists.
|
||||
host = import_info[CONF_HOST]
|
||||
if any(
|
||||
host == entry.data[CONF_HOST] for entry in self._async_current_entries()
|
||||
):
|
||||
return self.async_abort(reason=ABORT_REASON_ALREADY_CONFIGURED)
|
||||
|
||||
# Store the imported config for other steps in this flow to access.
|
||||
self.data[CONF_HOST] = host
|
||||
self.data[CONF_KEYFILE] = import_info[CONF_KEYFILE]
|
||||
self.data[CONF_CERTFILE] = import_info[CONF_CERTFILE]
|
||||
self.data[CONF_CA_CERTS] = import_info[CONF_CA_CERTS]
|
||||
|
||||
if not await self.async_validate_connectable_bridge_config():
|
||||
# Ultimately we won't have a dedicated step for import failure, but
|
||||
# in order to keep configuration.yaml-based configs transparently
|
||||
# working without requiring further actions from the user, we don't
|
||||
# display a form at all before creating a config entry in the
|
||||
# default case, so we're only going to show a form in case the
|
||||
# import fails.
|
||||
# This will change in an upcoming release where UI-based config flow
|
||||
# will become the default for the Lutron Caseta integration (which
|
||||
# will require users to go through a confirmation flow for imports).
|
||||
return await self.async_step_import_failed()
|
||||
|
||||
return self.async_create_entry(title=ENTRY_DEFAULT_TITLE, data=self.data)
|
||||
|
||||
async def async_step_import_failed(self, user_input=None):
|
||||
"""Make failed import surfaced to user."""
|
||||
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id=STEP_IMPORT_FAILED,
|
||||
description_placeholders={"host": self.data[CONF_HOST]},
|
||||
errors={"base": ERROR_CANNOT_CONNECT},
|
||||
)
|
||||
|
||||
return self.async_abort(reason=ABORT_REASON_CANNOT_CONNECT)
|
||||
|
||||
async def async_validate_connectable_bridge_config(self):
|
||||
"""Check if we can connect to the bridge with the current config."""
|
||||
|
||||
try:
|
||||
bridge = Smartbridge.create_tls(
|
||||
hostname=self.data[CONF_HOST],
|
||||
keyfile=self.data[CONF_KEYFILE],
|
||||
certfile=self.data[CONF_CERTFILE],
|
||||
ca_certs=self.data[CONF_CA_CERTS],
|
||||
)
|
||||
|
||||
await bridge.connect()
|
||||
if not bridge.is_connected():
|
||||
return False
|
||||
|
||||
await bridge.close()
|
||||
return True
|
||||
except (KeyError, ValueError):
|
||||
_LOGGER.error(
|
||||
"Error while checking connectivity to bridge %s", self.data[CONF_HOST],
|
||||
)
|
||||
return False
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception(
|
||||
"Unknown exception while checking connectivity to bridge %s",
|
||||
self.data[CONF_HOST],
|
||||
)
|
||||
return False
|
|
@ -0,0 +1,10 @@
|
|||
"""Lutron Caseta constants."""
|
||||
|
||||
CONF_KEYFILE = "keyfile"
|
||||
CONF_CERTFILE = "certfile"
|
||||
CONF_CA_CERTS = "ca_certs"
|
||||
|
||||
STEP_IMPORT_FAILED = "import_failed"
|
||||
ERROR_CANNOT_CONNECT = "cannot_connect"
|
||||
ABORT_REASON_CANNOT_CONNECT = "cannot_connect"
|
||||
ABORT_REASON_ALREADY_CONFIGURED = "already_configured"
|
|
@ -10,16 +10,22 @@ from homeassistant.components.cover import (
|
|||
CoverEntity,
|
||||
)
|
||||
|
||||
from . import LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice
|
||||
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the Lutron Caseta shades as a cover device."""
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Lutron Caseta cover platform.
|
||||
|
||||
Adds shades from the Caseta bridge associated with the config_entry as
|
||||
cover entities.
|
||||
"""
|
||||
|
||||
entities = []
|
||||
bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE]
|
||||
bridge = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
cover_devices = bridge.get_devices_by_domain(DOMAIN)
|
||||
|
||||
for cover_device in cover_devices:
|
||||
entity = LutronCasetaCover(cover_device, bridge)
|
||||
entities.append(entity)
|
||||
|
|
|
@ -13,7 +13,7 @@ from homeassistant.components.fan import (
|
|||
FanEntity,
|
||||
)
|
||||
|
||||
from . import LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice
|
||||
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -36,10 +36,15 @@ SPEED_TO_VALUE = {
|
|||
FAN_SPEEDS = [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up Lutron fan."""
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Lutron Caseta fan platform.
|
||||
|
||||
Adds fan controllers from the Caseta bridge associated with the config_entry
|
||||
as fan entities.
|
||||
"""
|
||||
|
||||
entities = []
|
||||
bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE]
|
||||
bridge = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
fan_devices = bridge.get_devices_by_domain(DOMAIN)
|
||||
|
||||
for fan_device in fan_devices:
|
||||
|
|
|
@ -8,7 +8,7 @@ from homeassistant.components.light import (
|
|||
LightEntity,
|
||||
)
|
||||
|
||||
from . import LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice
|
||||
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -23,11 +23,17 @@ def to_hass_level(level):
|
|||
return int((level * 255) // 100)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the Lutron Caseta lights."""
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Lutron Caseta light platform.
|
||||
|
||||
Adds dimmers from the Caseta bridge associated with the config_entry as
|
||||
light entities.
|
||||
"""
|
||||
|
||||
entities = []
|
||||
bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE]
|
||||
bridge = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
light_devices = bridge.get_devices_by_domain(DOMAIN)
|
||||
|
||||
for light_device in light_devices:
|
||||
entity = LutronCasetaLight(light_device, bridge)
|
||||
entities.append(entity)
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
"name": "Lutron Caséta",
|
||||
"documentation": "https://www.home-assistant.io/integrations/lutron_caseta",
|
||||
"requirements": ["pylutron-caseta==0.6.1"],
|
||||
"codeowners": ["@swails"]
|
||||
}
|
||||
"codeowners": ["@swails"],
|
||||
"config_flow": true
|
||||
}
|
|
@ -4,16 +4,22 @@ from typing import Any
|
|||
|
||||
from homeassistant.components.scene import Scene
|
||||
|
||||
from . import LUTRON_CASETA_SMARTBRIDGE
|
||||
from . import DOMAIN as CASETA_DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the Lutron Caseta lights."""
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Lutron Caseta scene platform.
|
||||
|
||||
Adds scenes from the Caseta bridge associated with the config_entry as
|
||||
scene entities.
|
||||
"""
|
||||
|
||||
entities = []
|
||||
bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE]
|
||||
bridge = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
scenes = bridge.get_scenes()
|
||||
|
||||
for scene in scenes:
|
||||
entity = LutronCasetaScene(scenes[scene], bridge)
|
||||
entities.append(entity)
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"import_failed": {
|
||||
"title": "Failed to import Caséta bridge configuration.",
|
||||
"description": "Couldn’t setup bridge (host: {host}) imported from configuration.yaml."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect to Caséta bridge; check your host and certificate configuration."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Caséta bridge already configured.",
|
||||
"cannot_connect": "Cancelled setup of Caséta bridge due to connection failure."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,15 +3,20 @@ import logging
|
|||
|
||||
from homeassistant.components.switch import DOMAIN, SwitchEntity
|
||||
|
||||
from . import LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice
|
||||
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up Lutron switch."""
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Lutron Caseta switch platform.
|
||||
|
||||
Adds switches from the Caseta bridge associated with the config_entry as
|
||||
switch entities.
|
||||
"""
|
||||
|
||||
entities = []
|
||||
bridge = hass.data[LUTRON_CASETA_SMARTBRIDGE]
|
||||
bridge = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
switch_devices = bridge.get_devices_by_domain(DOMAIN)
|
||||
|
||||
for switch_device in switch_devices:
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
{
|
||||
"title": "Lutron Cas\u00e9ta"
|
||||
"config": {
|
||||
"step": {
|
||||
"import_failed": {
|
||||
"title": "Failed to import Caséta bridge configuration.",
|
||||
"description": "Couldn’t import Caséta bridge (host: {host}) from configuration.yaml."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect to Caséta bridge; check your host and certificate configuration."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Caséta bridge already configured.",
|
||||
"cannot_connect": "Cancelled setup of Caséta bridge due to connection failure."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -81,6 +81,7 @@ FLOWS = [
|
|||
"locative",
|
||||
"logi_circle",
|
||||
"luftdaten",
|
||||
"lutron_caseta",
|
||||
"mailgun",
|
||||
"melcloud",
|
||||
"met",
|
||||
|
|
|
@ -599,6 +599,9 @@ pylinky==0.4.0
|
|||
# homeassistant.components.litejet
|
||||
pylitejet==0.1
|
||||
|
||||
# homeassistant.components.lutron_caseta
|
||||
pylutron-caseta==0.6.1
|
||||
|
||||
# homeassistant.components.mailgun
|
||||
pymailgunner==1.4
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
"""Tests for the Lutron Caseta integration."""
|
|
@ -0,0 +1,122 @@
|
|||
"""Test the Lutron Caseta config flow."""
|
||||
from asynctest import patch
|
||||
from pylutron_caseta.smartbridge import Smartbridge
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.lutron_caseta import DOMAIN
|
||||
import homeassistant.components.lutron_caseta.config_flow as CasetaConfigFlow
|
||||
from homeassistant.components.lutron_caseta.const import (
|
||||
CONF_CA_CERTS,
|
||||
CONF_CERTFILE,
|
||||
CONF_KEYFILE,
|
||||
ERROR_CANNOT_CONNECT,
|
||||
STEP_IMPORT_FAILED,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
class MockBridge:
|
||||
"""Mock Lutron bridge that emulates configured connected status."""
|
||||
|
||||
def __init__(self, can_connect=True):
|
||||
"""Initialize MockBridge instance with configured mock connectivity."""
|
||||
self.can_connect = can_connect
|
||||
self.is_currently_connected = False
|
||||
|
||||
async def connect(self):
|
||||
"""Connect the mock bridge."""
|
||||
if self.can_connect:
|
||||
self.is_currently_connected = True
|
||||
|
||||
def is_connected(self):
|
||||
"""Return whether the mock bridge is connected."""
|
||||
return self.is_currently_connected
|
||||
|
||||
async def close(self):
|
||||
"""Close the mock bridge connection."""
|
||||
self.is_currently_connected = False
|
||||
|
||||
|
||||
async def test_bridge_import_flow(hass):
|
||||
"""Test a bridge entry gets created and set up during the import flow."""
|
||||
|
||||
entry_mock_data = {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_KEYFILE: "",
|
||||
CONF_CERTFILE: "",
|
||||
CONF_CA_CERTS: "",
|
||||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lutron_caseta.async_setup_entry", return_value=True,
|
||||
) as mock_setup_entry, patch.object(Smartbridge, "create_tls") as create_tls:
|
||||
create_tls.return_value = MockBridge(can_connect=True)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=entry_mock_data,
|
||||
)
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == CasetaConfigFlow.ENTRY_DEFAULT_TITLE
|
||||
assert result["data"] == entry_mock_data
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_bridge_cannot_connect(hass):
|
||||
"""Test checking for connection and cannot_connect error."""
|
||||
|
||||
entry_mock_data = {
|
||||
CONF_HOST: "not.a.valid.host",
|
||||
CONF_KEYFILE: "",
|
||||
CONF_CERTFILE: "",
|
||||
CONF_CA_CERTS: "",
|
||||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lutron_caseta.async_setup_entry", return_value=True,
|
||||
) as mock_setup_entry, patch.object(Smartbridge, "create_tls") as create_tls:
|
||||
create_tls.return_value = MockBridge(can_connect=False)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=entry_mock_data,
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == STEP_IMPORT_FAILED
|
||||
assert result["errors"] == {"base": ERROR_CANNOT_CONNECT}
|
||||
# validate setup_entry was not called
|
||||
assert len(mock_setup_entry.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_duplicate_bridge_import(hass):
|
||||
"""Test that creating a bridge entry with a duplicate host errors."""
|
||||
|
||||
entry_mock_data = {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_KEYFILE: "",
|
||||
CONF_CERTFILE: "",
|
||||
CONF_CA_CERTS: "",
|
||||
}
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, data=entry_mock_data)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lutron_caseta.async_setup_entry", return_value=True,
|
||||
) as mock_setup_entry:
|
||||
# Mock entry added, try initializing flow with duplicate host
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=entry_mock_data,
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == CasetaConfigFlow.ABORT_REASON_ALREADY_CONFIGURED
|
||||
assert len(mock_setup_entry.mock_calls) == 0
|
Loading…
Reference in New Issue