Add config flow to HLK-SW16 (#37190)
* Add config flow to HLK-SW16 * Use entry_id for unique_id * Add options update capability * Refactor entry_id under domain * Remove name from config * Set options * Remove options flow * remove unneccesary else block from validate_input and move domain cleanup to async_unload_entry * Add tests and config import * Add back config schema * Remove config import * Refactor unload * Add back config import * Update coveragerc * Don't mock validate_input * Test duplicate configs * Add import test * Use patch for timeout test * Use mock for testing timeout * Use MockSW16Client for tests * Check mock_calls count * Remove unused NameExists exception * Remove title from strings.json * Mock setup for import test * Set PARALLEL_UPDATES for switch * Move hass.data.setdefault(DOMAIN, {}) to async_setup_entry
This commit is contained in:
parent
76b46b9175
commit
064cc52ad6
14 changed files with 482 additions and 90 deletions
|
@ -351,7 +351,8 @@ omit =
|
|||
homeassistant/components/hisense_aehw4a1/*
|
||||
homeassistant/components/hitron_coda/device_tracker.py
|
||||
homeassistant/components/hive/*
|
||||
homeassistant/components/hlk_sw16/*
|
||||
homeassistant/components/hlk_sw16/__init__.py
|
||||
homeassistant/components/hlk_sw16/switch.py
|
||||
homeassistant/components/home_connect/*
|
||||
homeassistant/components/homematic/*
|
||||
homeassistant/components/homematic/climate.py
|
||||
|
|
|
@ -172,6 +172,7 @@ homeassistant/components/hikvisioncam/* @fbradyirl
|
|||
homeassistant/components/hisense_aehw4a1/* @bannhead
|
||||
homeassistant/components/history/* @home-assistant/core
|
||||
homeassistant/components/hive/* @Rendili @KJonline
|
||||
homeassistant/components/hlk_sw16/* @jameshilliard
|
||||
homeassistant/components/home_connect/* @DavidMStraub
|
||||
homeassistant/components/homeassistant/* @home-assistant/core
|
||||
homeassistant/components/homekit/* @bdraco
|
||||
|
|
|
@ -4,31 +4,28 @@ import logging
|
|||
from hlk_sw16 import create_hlk_sw16_connection
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_NAME,
|
||||
CONF_PORT,
|
||||
CONF_SWITCHES,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, CONF_SWITCHES
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.discovery import async_load_platform
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import (
|
||||
CONNECTION_TIMEOUT,
|
||||
DEFAULT_KEEP_ALIVE_INTERVAL,
|
||||
DEFAULT_PORT,
|
||||
DEFAULT_RECONNECT_INTERVAL,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_DEVICE_REGISTER = "hlk_sw16_device_register"
|
||||
DEFAULT_RECONNECT_INTERVAL = 10
|
||||
DEFAULT_KEEP_ALIVE_INTERVAL = 3
|
||||
CONNECTION_TIMEOUT = 10
|
||||
DEFAULT_PORT = 8080
|
||||
|
||||
DOMAIN = "hlk_sw16"
|
||||
DATA_DEVICE_LISTENER = "hlk_sw16_device_listener"
|
||||
|
||||
SWITCH_SCHEMA = vol.Schema({vol.Optional(CONF_NAME): cv.string})
|
||||
|
||||
|
@ -57,84 +54,112 @@ CONFIG_SCHEMA = vol.Schema(
|
|||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the HLK-SW16 switch."""
|
||||
# Allow platform to specify function to register new unknown devices
|
||||
"""Component setup, do nothing."""
|
||||
if DOMAIN not in config:
|
||||
return True
|
||||
|
||||
hass.data[DATA_DEVICE_REGISTER] = {}
|
||||
|
||||
def add_device(device):
|
||||
switches = config[DOMAIN][device][CONF_SWITCHES]
|
||||
|
||||
host = config[DOMAIN][device][CONF_HOST]
|
||||
port = config[DOMAIN][device][CONF_PORT]
|
||||
|
||||
@callback
|
||||
def disconnected():
|
||||
"""Schedule reconnect after connection has been lost."""
|
||||
_LOGGER.warning("HLK-SW16 %s disconnected", device)
|
||||
async_dispatcher_send(hass, f"hlk_sw16_device_available_{device}", False)
|
||||
|
||||
@callback
|
||||
def reconnected():
|
||||
"""Schedule reconnect after connection has been lost."""
|
||||
_LOGGER.warning("HLK-SW16 %s connected", device)
|
||||
async_dispatcher_send(hass, f"hlk_sw16_device_available_{device}", True)
|
||||
|
||||
async def connect():
|
||||
"""Set up connection and hook it into HA for reconnect/shutdown."""
|
||||
_LOGGER.info("Initiating HLK-SW16 connection to %s", device)
|
||||
|
||||
client = await create_hlk_sw16_connection(
|
||||
host=host,
|
||||
port=port,
|
||||
disconnect_callback=disconnected,
|
||||
reconnect_callback=reconnected,
|
||||
loop=hass.loop,
|
||||
timeout=CONNECTION_TIMEOUT,
|
||||
reconnect_interval=DEFAULT_RECONNECT_INTERVAL,
|
||||
keep_alive_interval=DEFAULT_KEEP_ALIVE_INTERVAL,
|
||||
for device_id in config[DOMAIN]:
|
||||
conf = config[DOMAIN][device_id]
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={CONF_HOST: conf[CONF_HOST], CONF_PORT: conf[CONF_PORT]},
|
||||
)
|
||||
|
||||
hass.data[DATA_DEVICE_REGISTER][device] = client
|
||||
|
||||
# Load platforms
|
||||
hass.async_create_task(
|
||||
async_load_platform(hass, "switch", DOMAIN, (switches, device), config)
|
||||
)
|
||||
|
||||
# handle shutdown of HLK-SW16 asyncio transport
|
||||
hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_STOP, lambda x: client.stop()
|
||||
)
|
||||
|
||||
_LOGGER.info("Connected to HLK-SW16 device: %s", device)
|
||||
|
||||
hass.loop.create_task(connect())
|
||||
|
||||
for device in config[DOMAIN]:
|
||||
add_device(device)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Set up the HLK-SW16 switch."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
host = entry.data[CONF_HOST]
|
||||
port = entry.data[CONF_PORT]
|
||||
address = f"{host}:{port}"
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = {}
|
||||
|
||||
@callback
|
||||
def disconnected():
|
||||
"""Schedule reconnect after connection has been lost."""
|
||||
_LOGGER.warning("HLK-SW16 %s disconnected", address)
|
||||
async_dispatcher_send(
|
||||
hass, f"hlk_sw16_device_available_{entry.entry_id}", False
|
||||
)
|
||||
|
||||
@callback
|
||||
def reconnected():
|
||||
"""Schedule reconnect after connection has been lost."""
|
||||
_LOGGER.warning("HLK-SW16 %s connected", address)
|
||||
async_dispatcher_send(hass, f"hlk_sw16_device_available_{entry.entry_id}", True)
|
||||
|
||||
async def connect():
|
||||
"""Set up connection and hook it into HA for reconnect/shutdown."""
|
||||
_LOGGER.info("Initiating HLK-SW16 connection to %s", address)
|
||||
|
||||
client = await create_hlk_sw16_connection(
|
||||
host=host,
|
||||
port=port,
|
||||
disconnect_callback=disconnected,
|
||||
reconnect_callback=reconnected,
|
||||
loop=hass.loop,
|
||||
timeout=CONNECTION_TIMEOUT,
|
||||
reconnect_interval=DEFAULT_RECONNECT_INTERVAL,
|
||||
keep_alive_interval=DEFAULT_KEEP_ALIVE_INTERVAL,
|
||||
)
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_DEVICE_REGISTER] = client
|
||||
|
||||
# Load entities
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, "switch")
|
||||
)
|
||||
|
||||
_LOGGER.info("Connected to HLK-SW16 device: %s", address)
|
||||
|
||||
hass.loop.create_task(connect())
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, entry):
|
||||
"""Unload a config entry."""
|
||||
client = hass.data[DOMAIN][entry.entry_id].pop(DATA_DEVICE_REGISTER)
|
||||
client.stop()
|
||||
unload_ok = await hass.config_entries.async_forward_entry_unload(entry, "switch")
|
||||
|
||||
if unload_ok:
|
||||
if hass.data[DOMAIN][entry.entry_id]:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
if not hass.data[DOMAIN]:
|
||||
hass.data.pop(DOMAIN)
|
||||
return unload_ok
|
||||
|
||||
|
||||
class SW16Device(Entity):
|
||||
"""Representation of a HLK-SW16 device.
|
||||
|
||||
Contains the common logic for HLK-SW16 entities.
|
||||
"""
|
||||
|
||||
def __init__(self, relay_name, device_port, device_id, client):
|
||||
def __init__(self, device_port, entry_id, client):
|
||||
"""Initialize the device."""
|
||||
# HLK-SW16 specific attributes for every component type
|
||||
self._device_id = device_id
|
||||
self._entry_id = entry_id
|
||||
self._device_port = device_port
|
||||
self._is_on = None
|
||||
self._client = client
|
||||
self._name = relay_name
|
||||
self._name = device_port
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique ID."""
|
||||
return f"{self._entry_id}_{self._device_port}"
|
||||
|
||||
@callback
|
||||
def handle_event_callback(self, event):
|
||||
"""Propagate changes through ha."""
|
||||
_LOGGER.debug("Relay %s new state callback: %r", self._device_port, event)
|
||||
_LOGGER.debug("Relay %s new state callback: %r", self.unique_id, event)
|
||||
self._is_on = event
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
@ -167,7 +192,7 @@ class SW16Device(Entity):
|
|||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
f"hlk_sw16_device_available_{self._device_id}",
|
||||
f"hlk_sw16_device_available_{self._entry_id}",
|
||||
self._availability_callback,
|
||||
)
|
||||
)
|
||||
|
|
96
homeassistant/components/hlk_sw16/config_flow.py
Normal file
96
homeassistant/components/hlk_sw16/config_flow.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
"""Config flow for HLK-SW16."""
|
||||
import asyncio
|
||||
|
||||
from hlk_sw16 import create_hlk_sw16_connection
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import (
|
||||
CONNECTION_TIMEOUT,
|
||||
DEFAULT_KEEP_ALIVE_INTERVAL,
|
||||
DEFAULT_PORT,
|
||||
DEFAULT_RECONNECT_INTERVAL,
|
||||
DOMAIN,
|
||||
)
|
||||
from .errors import AlreadyConfigured, CannotConnect
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): str,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): vol.Coerce(int),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def connect_client(hass, user_input):
|
||||
"""Connect the HLK-SW16 client."""
|
||||
client_aw = create_hlk_sw16_connection(
|
||||
host=user_input[CONF_HOST],
|
||||
port=user_input[CONF_PORT],
|
||||
loop=hass.loop,
|
||||
timeout=CONNECTION_TIMEOUT,
|
||||
reconnect_interval=DEFAULT_RECONNECT_INTERVAL,
|
||||
keep_alive_interval=DEFAULT_KEEP_ALIVE_INTERVAL,
|
||||
)
|
||||
return await asyncio.wait_for(client_aw, timeout=CONNECTION_TIMEOUT)
|
||||
|
||||
|
||||
async def validate_input(hass: HomeAssistant, user_input):
|
||||
"""Validate the user input allows us to connect."""
|
||||
for entry in hass.config_entries.async_entries(DOMAIN):
|
||||
if (
|
||||
entry.data[CONF_HOST] == user_input[CONF_HOST]
|
||||
and entry.data[CONF_PORT] == user_input[CONF_PORT]
|
||||
):
|
||||
raise AlreadyConfigured
|
||||
|
||||
try:
|
||||
client = await connect_client(hass, user_input)
|
||||
except asyncio.TimeoutError:
|
||||
raise CannotConnect
|
||||
try:
|
||||
|
||||
def disconnect_callback():
|
||||
if client.in_transaction:
|
||||
client.active_transaction.set_exception(CannotConnect)
|
||||
|
||||
client.disconnect_callback = disconnect_callback
|
||||
await client.status()
|
||||
except CannotConnect:
|
||||
client.disconnect_callback = None
|
||||
client.stop()
|
||||
raise CannotConnect
|
||||
else:
|
||||
client.disconnect_callback = None
|
||||
client.stop()
|
||||
|
||||
|
||||
class SW16FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a HLK-SW16 config flow."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
|
||||
|
||||
async def async_step_import(self, user_input):
|
||||
"""Handle import."""
|
||||
return await self.async_step_user(user_input)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
try:
|
||||
await validate_input(self.hass, user_input)
|
||||
address = f"{user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
|
||||
return self.async_create_entry(title=address, data=user_input)
|
||||
except AlreadyConfigured:
|
||||
errors["base"] = "already_configured"
|
||||
except CannotConnect:
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=DATA_SCHEMA, errors=errors
|
||||
)
|
9
homeassistant/components/hlk_sw16/const.py
Normal file
9
homeassistant/components/hlk_sw16/const.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""Constants for HLK-SW16 component."""
|
||||
|
||||
DOMAIN = "hlk_sw16"
|
||||
|
||||
DEFAULT_NAME = "HLK-SW16"
|
||||
DEFAULT_PORT = 8080
|
||||
DEFAULT_RECONNECT_INTERVAL = 10
|
||||
DEFAULT_KEEP_ALIVE_INTERVAL = 3
|
||||
CONNECTION_TIMEOUT = 10
|
14
homeassistant/components/hlk_sw16/errors.py
Normal file
14
homeassistant/components/hlk_sw16/errors.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
"""Errors for the HLK-SW16 component."""
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
|
||||
class SW16Exception(HomeAssistantError):
|
||||
"""Base class for HLK-SW16 exceptions."""
|
||||
|
||||
|
||||
class AlreadyConfigured(SW16Exception):
|
||||
"""HLK-SW16 is already configured."""
|
||||
|
||||
|
||||
class CannotConnect(SW16Exception):
|
||||
"""Unable to connect to the HLK-SW16."""
|
|
@ -2,6 +2,11 @@
|
|||
"domain": "hlk_sw16",
|
||||
"name": "Hi-Link HLK-SW16",
|
||||
"documentation": "https://www.home-assistant.io/integrations/hlk_sw16",
|
||||
"requirements": ["hlk-sw16==0.0.8"],
|
||||
"codeowners": []
|
||||
}
|
||||
"requirements": [
|
||||
"hlk-sw16==0.0.8"
|
||||
],
|
||||
"codeowners": [
|
||||
"@jameshilliard"
|
||||
],
|
||||
"config_flow": true
|
||||
}
|
21
homeassistant/components/hlk_sw16/strings.json
Normal file
21
homeassistant/components/hlk_sw16/strings.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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,30 +1,30 @@
|
|||
"""Support for HLK-SW16 switches."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.switch import ToggleEntity
|
||||
from homeassistant.const import CONF_NAME
|
||||
|
||||
from . import DATA_DEVICE_REGISTER, SW16Device
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
def devices_from_config(hass, domain_config):
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the HLK-SW16 switches."""
|
||||
|
||||
|
||||
def devices_from_entities(hass, entry):
|
||||
"""Parse configuration and add HLK-SW16 switch devices."""
|
||||
switches = domain_config[0]
|
||||
device_id = domain_config[1]
|
||||
device_client = hass.data[DATA_DEVICE_REGISTER][device_id]
|
||||
device_client = hass.data[DOMAIN][entry.entry_id][DATA_DEVICE_REGISTER]
|
||||
devices = []
|
||||
for device_port, device_config in switches.items():
|
||||
device_name = device_config.get(CONF_NAME, device_port)
|
||||
device = SW16Switch(device_name, device_port, device_id, device_client)
|
||||
for i in range(16):
|
||||
device_port = f"{i:01x}"
|
||||
device = SW16Switch(device_port, entry.entry_id, device_client)
|
||||
devices.append(device)
|
||||
return devices
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up the HLK-SW16 platform."""
|
||||
async_add_entities(devices_from_config(hass, discovery_info))
|
||||
async_add_entities(devices_from_entities(hass, entry))
|
||||
|
||||
|
||||
class SW16Switch(SW16Device, ToggleEntity):
|
||||
|
|
22
homeassistant/components/hlk_sw16/translations/en.json
Normal file
22
homeassistant/components/hlk_sw16/translations/en.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"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%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"username": "[%key:common::config_flow::data::username%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Hi-Link HLK-SW16"
|
||||
}
|
|
@ -71,6 +71,7 @@ FLOWS = [
|
|||
"harmony",
|
||||
"heos",
|
||||
"hisense_aehw4a1",
|
||||
"hlk_sw16",
|
||||
"home_connect",
|
||||
"homekit",
|
||||
"homekit_controller",
|
||||
|
|
|
@ -352,6 +352,9 @@ hdate==0.9.5
|
|||
# homeassistant.components.here_travel_time
|
||||
herepy==2.0.0
|
||||
|
||||
# homeassistant.components.hlk_sw16
|
||||
hlk-sw16==0.0.8
|
||||
|
||||
# homeassistant.components.pi_hole
|
||||
hole==0.5.1
|
||||
|
||||
|
|
1
tests/components/hlk_sw16/__init__.py
Normal file
1
tests/components/hlk_sw16/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Tests for the Hi-Link HLK-SW16 integration."""
|
193
tests/components/hlk_sw16/test_config_flow.py
Normal file
193
tests/components/hlk_sw16/test_config_flow.py
Normal file
|
@ -0,0 +1,193 @@
|
|||
"""Test the Hi-Link HLK-SW16 config flow."""
|
||||
import asyncio
|
||||
|
||||
from homeassistant import config_entries, setup
|
||||
from homeassistant.components.hlk_sw16.const import DOMAIN
|
||||
|
||||
from tests.async_mock import patch
|
||||
|
||||
|
||||
class MockSW16Client:
|
||||
"""Class to mock the SW16Client client."""
|
||||
|
||||
def __init__(self, fail):
|
||||
"""Initialise client with failure modes."""
|
||||
self.fail = fail
|
||||
self.disconnect_callback = None
|
||||
self.in_transaction = False
|
||||
self.active_transaction = None
|
||||
|
||||
async def setup(self):
|
||||
"""Mock successful setup."""
|
||||
fut = asyncio.Future()
|
||||
fut.set_result(True)
|
||||
return fut
|
||||
|
||||
async def status(self):
|
||||
"""Mock status based on failure mode."""
|
||||
self.in_transaction = True
|
||||
self.active_transaction = asyncio.Future()
|
||||
if self.fail:
|
||||
if self.disconnect_callback:
|
||||
self.disconnect_callback()
|
||||
return await self.active_transaction
|
||||
else:
|
||||
self.active_transaction.set_result(True)
|
||||
return self.active_transaction
|
||||
|
||||
def stop(self):
|
||||
"""Mock client stop."""
|
||||
self.in_transaction = False
|
||||
self.active_transaction = None
|
||||
|
||||
|
||||
async def create_mock_hlk_sw16_connection(fail):
|
||||
"""Create a mock HLK-SW16 client."""
|
||||
client = MockSW16Client(fail)
|
||||
await client.setup()
|
||||
return client
|
||||
|
||||
|
||||
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"] == "form"
|
||||
assert result["errors"] == {}
|
||||
|
||||
conf = {
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
}
|
||||
|
||||
mock_hlk_sw16_connection = await create_mock_hlk_sw16_connection(False)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.hlk_sw16.config_flow.create_hlk_sw16_connection",
|
||||
return_value=mock_hlk_sw16_connection,
|
||||
), patch(
|
||||
"homeassistant.components.hlk_sw16.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.hlk_sw16.async_setup_entry", return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], conf,
|
||||
)
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == "127.0.0.1:8080"
|
||||
assert result2["data"] == {
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
mock_hlk_sw16_connection = await create_mock_hlk_sw16_connection(False)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.hlk_sw16.config_flow.create_hlk_sw16_connection",
|
||||
return_value=mock_hlk_sw16_connection,
|
||||
):
|
||||
result3 = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result3["type"] == "form"
|
||||
assert result3["errors"] == {}
|
||||
|
||||
result4 = await hass.config_entries.flow.async_configure(result3["flow_id"], conf,)
|
||||
|
||||
assert result4["type"] == "form"
|
||||
assert result4["errors"] == {"base": "already_configured"}
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_import(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_IMPORT}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["errors"] == {}
|
||||
|
||||
conf = {
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
}
|
||||
|
||||
mock_hlk_sw16_connection = await create_mock_hlk_sw16_connection(False)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.hlk_sw16.config_flow.connect_client",
|
||||
return_value=mock_hlk_sw16_connection,
|
||||
), patch(
|
||||
"homeassistant.components.hlk_sw16.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.hlk_sw16.async_setup_entry", return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], conf,
|
||||
)
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == "127.0.0.1:8080"
|
||||
assert result2["data"] == {
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_form_invalid_data(hass):
|
||||
"""Test we handle invalid auth."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
mock_hlk_sw16_connection = await create_mock_hlk_sw16_connection(True)
|
||||
|
||||
conf = {
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.hlk_sw16.config_flow.connect_client",
|
||||
return_value=mock_hlk_sw16_connection,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], conf,
|
||||
)
|
||||
|
||||
assert result2["type"] == "form"
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_form_cannot_connect(hass):
|
||||
"""Test we handle cannot connect error."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
conf = {
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.hlk_sw16.config_flow.connect_client",
|
||||
side_effect=asyncio.TimeoutError,
|
||||
return_value=None,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], conf,
|
||||
)
|
||||
|
||||
assert result2["type"] == "form"
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
Loading…
Add table
Add a link
Reference in a new issue