2022-02-05 00:20:21 +00:00
|
|
|
"""Test the WiZ Platform config flow."""
|
2022-02-05 15:23:19 +00:00
|
|
|
from contextlib import contextmanager
|
2022-02-05 16:36:44 +00:00
|
|
|
from copy import deepcopy
|
2022-02-05 00:20:21 +00:00
|
|
|
from unittest.mock import patch
|
|
|
|
|
|
|
|
import pytest
|
2022-02-05 21:23:31 +00:00
|
|
|
from pywizlight.discovery import DiscoveredBulb
|
|
|
|
from pywizlight.exceptions import WizLightConnectionError, WizLightTimeOutError
|
2022-02-05 00:20:21 +00:00
|
|
|
|
|
|
|
from homeassistant import config_entries
|
2022-02-05 16:36:44 +00:00
|
|
|
from homeassistant.components import dhcp
|
2022-02-05 21:23:31 +00:00
|
|
|
from homeassistant.components.wiz.config_flow import CONF_DEVICE
|
2022-02-05 00:20:21 +00:00
|
|
|
from homeassistant.components.wiz.const import DOMAIN
|
2022-02-05 15:23:19 +00:00
|
|
|
from homeassistant.const import CONF_HOST
|
2022-02-05 16:36:44 +00:00
|
|
|
from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_FORM
|
2022-02-05 00:20:21 +00:00
|
|
|
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
|
2022-02-05 16:36:44 +00:00
|
|
|
FAKE_IP = "1.1.1.1"
|
2022-02-05 15:23:19 +00:00
|
|
|
FAKE_MAC = "ABCABCABCABC"
|
|
|
|
FAKE_BULB_CONFIG = {
|
|
|
|
"method": "getSystemConfig",
|
|
|
|
"env": "pro",
|
|
|
|
"result": {
|
|
|
|
"mac": FAKE_MAC,
|
|
|
|
"homeId": 653906,
|
|
|
|
"roomId": 989983,
|
|
|
|
"moduleName": "ESP_0711_STR",
|
|
|
|
"fwVersion": "1.21.0",
|
|
|
|
"groupId": 0,
|
|
|
|
"drvConf": [20, 2],
|
|
|
|
"ewf": [255, 0, 255, 255, 0, 0, 0],
|
|
|
|
"ewfHex": "ff00ffff000000",
|
|
|
|
"ping": 0,
|
|
|
|
},
|
|
|
|
}
|
2022-02-05 16:36:44 +00:00
|
|
|
FAKE_SOCKET_CONFIG = deepcopy(FAKE_BULB_CONFIG)
|
|
|
|
FAKE_SOCKET_CONFIG["result"]["moduleName"] = "ESP10_SOCKET_06"
|
2022-02-05 15:23:19 +00:00
|
|
|
FAKE_EXTENDED_WHITE_RANGE = [2200, 2700, 6500, 6500]
|
|
|
|
TEST_SYSTEM_INFO = {"id": FAKE_MAC, "name": "Test Bulb"}
|
|
|
|
TEST_CONNECTION = {CONF_HOST: "1.1.1.1"}
|
|
|
|
TEST_NO_IP = {CONF_HOST: "this is no IP input"}
|
|
|
|
|
|
|
|
|
2022-02-05 16:36:44 +00:00
|
|
|
DHCP_DISCOVERY = dhcp.DhcpServiceInfo(
|
|
|
|
hostname="wiz_abcabc",
|
|
|
|
ip=FAKE_IP,
|
|
|
|
macaddress=FAKE_MAC,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
INTEGRATION_DISCOVERY = {
|
|
|
|
"ip_address": FAKE_IP,
|
|
|
|
"mac_address": FAKE_MAC,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def _patch_wizlight(device=None, extended_white_range=None):
|
2022-02-05 15:23:19 +00:00
|
|
|
@contextmanager
|
|
|
|
def _patcher():
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.wiz.wizlight.getBulbConfig",
|
2022-02-05 16:36:44 +00:00
|
|
|
return_value=device or FAKE_BULB_CONFIG,
|
2022-02-05 15:23:19 +00:00
|
|
|
), patch(
|
|
|
|
"homeassistant.components.wiz.wizlight.getExtendedWhiteRange",
|
2022-02-05 16:36:44 +00:00
|
|
|
return_value=extended_white_range or FAKE_EXTENDED_WHITE_RANGE,
|
2022-02-05 15:23:19 +00:00
|
|
|
), patch(
|
|
|
|
"homeassistant.components.wiz.wizlight.getMac",
|
|
|
|
return_value=FAKE_MAC,
|
|
|
|
):
|
|
|
|
yield
|
|
|
|
|
|
|
|
return _patcher()
|
2022-02-05 00:20:21 +00:00
|
|
|
|
|
|
|
|
2022-02-05 21:23:31 +00:00
|
|
|
def _patch_discovery():
|
|
|
|
@contextmanager
|
|
|
|
def _patcher():
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.wiz.discovery.find_wizlights",
|
|
|
|
return_value=[DiscoveredBulb(FAKE_IP, FAKE_MAC)],
|
|
|
|
):
|
|
|
|
yield
|
|
|
|
|
|
|
|
return _patcher()
|
|
|
|
|
|
|
|
|
2022-02-05 00:20:21 +00:00
|
|
|
async def test_form(hass):
|
|
|
|
"""Test we get the form."""
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["errors"] == {}
|
|
|
|
# Patch functions
|
2022-02-05 15:23:19 +00:00
|
|
|
with _patch_wizlight(), patch(
|
2022-02-05 00:20:21 +00:00
|
|
|
"homeassistant.components.wiz.async_setup_entry",
|
|
|
|
return_value=True,
|
2022-02-05 21:23:31 +00:00
|
|
|
) as mock_setup_entry, patch(
|
|
|
|
"homeassistant.components.wiz.async_setup", return_value=True
|
|
|
|
) as mock_setup:
|
2022-02-05 00:20:21 +00:00
|
|
|
result2 = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
TEST_CONNECTION,
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result2["type"] == "create_entry"
|
2022-02-05 15:23:19 +00:00
|
|
|
assert result2["title"] == "WiZ Dimmable White ABCABC"
|
|
|
|
assert result2["data"] == {
|
|
|
|
CONF_HOST: "1.1.1.1",
|
|
|
|
}
|
2022-02-05 21:23:31 +00:00
|
|
|
assert len(mock_setup.mock_calls) == 1
|
2022-02-05 00:20:21 +00:00
|
|
|
assert len(mock_setup_entry.mock_calls) == 1
|
|
|
|
|
|
|
|
|
2022-02-09 18:53:32 +00:00
|
|
|
async def test_user_flow_enters_dns_name(hass):
|
|
|
|
"""Test we reject dns names and want ips."""
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["errors"] == {}
|
|
|
|
|
|
|
|
result2 = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
{CONF_HOST: "ip.only"},
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result2["type"] == RESULT_TYPE_FORM
|
|
|
|
assert result2["errors"] == {"base": "no_ip"}
|
|
|
|
|
|
|
|
with _patch_wizlight(), patch(
|
|
|
|
"homeassistant.components.wiz.async_setup_entry",
|
|
|
|
return_value=True,
|
|
|
|
) as mock_setup_entry, patch(
|
|
|
|
"homeassistant.components.wiz.async_setup", return_value=True
|
|
|
|
) as mock_setup:
|
|
|
|
result3 = await hass.config_entries.flow.async_configure(
|
|
|
|
result2["flow_id"],
|
|
|
|
TEST_CONNECTION,
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result3["type"] == "create_entry"
|
|
|
|
assert result3["title"] == "WiZ Dimmable White ABCABC"
|
|
|
|
assert result3["data"] == {
|
|
|
|
CONF_HOST: "1.1.1.1",
|
|
|
|
}
|
|
|
|
assert len(mock_setup.mock_calls) == 1
|
|
|
|
assert len(mock_setup_entry.mock_calls) == 1
|
|
|
|
|
|
|
|
|
2022-02-05 00:20:21 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"side_effect, error_base",
|
|
|
|
[
|
|
|
|
(WizLightTimeOutError, "bulb_time_out"),
|
|
|
|
(WizLightConnectionError, "no_wiz_light"),
|
|
|
|
(Exception, "unknown"),
|
|
|
|
(ConnectionRefusedError, "cannot_connect"),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_user_form_exceptions(hass, side_effect, error_base):
|
|
|
|
"""Test all user exceptions in the flow."""
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
|
|
)
|
|
|
|
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.wiz.wizlight.getBulbConfig",
|
|
|
|
side_effect=side_effect,
|
|
|
|
):
|
|
|
|
result2 = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
TEST_CONNECTION,
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result2["type"] == "form"
|
|
|
|
assert result2["errors"] == {"base": error_base}
|
|
|
|
|
|
|
|
|
|
|
|
async def test_form_updates_unique_id(hass):
|
|
|
|
"""Test a duplicate id aborts and updates existing entry."""
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
domain=DOMAIN,
|
|
|
|
unique_id=TEST_SYSTEM_INFO["id"],
|
2022-02-05 16:36:44 +00:00
|
|
|
data={CONF_HOST: "dummy"},
|
2022-02-05 00:20:21 +00:00
|
|
|
)
|
|
|
|
entry.add_to_hass(hass)
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
|
|
)
|
2022-02-05 21:23:31 +00:00
|
|
|
with _patch_wizlight():
|
2022-02-05 00:20:21 +00:00
|
|
|
result2 = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
TEST_CONNECTION,
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result2["type"] == "abort"
|
|
|
|
assert result2["reason"] == "already_configured"
|
2022-02-05 17:56:17 +00:00
|
|
|
assert entry.data[CONF_HOST] == FAKE_IP
|
2022-02-05 16:36:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"source, data",
|
|
|
|
[
|
|
|
|
(config_entries.SOURCE_DHCP, DHCP_DISCOVERY),
|
|
|
|
(config_entries.SOURCE_INTEGRATION_DISCOVERY, INTEGRATION_DISCOVERY),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_discovered_by_dhcp_connection_fails(hass, source, data):
|
|
|
|
"""Test we abort on connection failure."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.wiz.wizlight.getBulbConfig",
|
|
|
|
side_effect=WizLightTimeOutError,
|
|
|
|
):
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": source}, data=data
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result["type"] == RESULT_TYPE_ABORT
|
|
|
|
assert result["reason"] == "cannot_connect"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"source, data, device, extended_white_range, name",
|
|
|
|
[
|
|
|
|
(
|
|
|
|
config_entries.SOURCE_DHCP,
|
|
|
|
DHCP_DISCOVERY,
|
|
|
|
FAKE_BULB_CONFIG,
|
|
|
|
FAKE_EXTENDED_WHITE_RANGE,
|
|
|
|
"WiZ Dimmable White ABCABC",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
config_entries.SOURCE_INTEGRATION_DISCOVERY,
|
|
|
|
INTEGRATION_DISCOVERY,
|
|
|
|
FAKE_BULB_CONFIG,
|
|
|
|
FAKE_EXTENDED_WHITE_RANGE,
|
|
|
|
"WiZ Dimmable White ABCABC",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
config_entries.SOURCE_DHCP,
|
|
|
|
DHCP_DISCOVERY,
|
|
|
|
FAKE_SOCKET_CONFIG,
|
|
|
|
None,
|
|
|
|
"WiZ Socket ABCABC",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
config_entries.SOURCE_INTEGRATION_DISCOVERY,
|
|
|
|
INTEGRATION_DISCOVERY,
|
|
|
|
FAKE_SOCKET_CONFIG,
|
|
|
|
None,
|
|
|
|
"WiZ Socket ABCABC",
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_discovered_by_dhcp_or_integration_discovery(
|
|
|
|
hass, source, data, device, extended_white_range, name
|
|
|
|
):
|
|
|
|
"""Test we can configure when discovered from dhcp or discovery."""
|
|
|
|
with _patch_wizlight(device=device, extended_white_range=extended_white_range):
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": source}, data=data
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result["type"] == RESULT_TYPE_FORM
|
|
|
|
assert result["step_id"] == "discovery_confirm"
|
|
|
|
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.wiz.async_setup_entry",
|
|
|
|
return_value=True,
|
2022-02-05 21:23:31 +00:00
|
|
|
) as mock_setup_entry, patch(
|
|
|
|
"homeassistant.components.wiz.async_setup", return_value=True
|
|
|
|
) as mock_setup:
|
2022-02-05 16:36:44 +00:00
|
|
|
result2 = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
{},
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result2["type"] == "create_entry"
|
|
|
|
assert result2["title"] == name
|
|
|
|
assert result2["data"] == {
|
|
|
|
CONF_HOST: "1.1.1.1",
|
|
|
|
}
|
2022-02-05 21:23:31 +00:00
|
|
|
assert len(mock_setup.mock_calls) == 1
|
2022-02-05 16:36:44 +00:00
|
|
|
assert len(mock_setup_entry.mock_calls) == 1
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"source, data",
|
|
|
|
[
|
|
|
|
(config_entries.SOURCE_DHCP, DHCP_DISCOVERY),
|
|
|
|
(config_entries.SOURCE_INTEGRATION_DISCOVERY, INTEGRATION_DISCOVERY),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_discovered_by_dhcp_or_integration_discovery_updates_host(
|
|
|
|
hass, source, data
|
|
|
|
):
|
|
|
|
"""Test dhcp or discovery updates existing host."""
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
domain=DOMAIN,
|
|
|
|
unique_id=TEST_SYSTEM_INFO["id"],
|
|
|
|
data={CONF_HOST: "dummy"},
|
|
|
|
)
|
|
|
|
entry.add_to_hass(hass)
|
|
|
|
|
|
|
|
with _patch_wizlight():
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": source}, data=data
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result["type"] == RESULT_TYPE_ABORT
|
|
|
|
assert result["reason"] == "already_configured"
|
|
|
|
assert entry.data[CONF_HOST] == FAKE_IP
|
2022-02-05 21:23:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_setup_via_discovery(hass):
|
|
|
|
"""Test setting up via discovery."""
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "user"
|
|
|
|
assert not result["errors"]
|
|
|
|
|
|
|
|
with _patch_discovery():
|
|
|
|
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result2["type"] == "form"
|
|
|
|
assert result2["step_id"] == "pick_device"
|
|
|
|
assert not result2["errors"]
|
|
|
|
|
|
|
|
# test we can try again
|
|
|
|
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 not result["errors"]
|
|
|
|
|
|
|
|
with _patch_discovery():
|
|
|
|
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result2["type"] == "form"
|
|
|
|
assert result2["step_id"] == "pick_device"
|
|
|
|
assert not result2["errors"]
|
|
|
|
|
|
|
|
with _patch_wizlight(), patch(
|
|
|
|
"homeassistant.components.wiz.async_setup", return_value=True
|
|
|
|
) as mock_setup, patch(
|
|
|
|
"homeassistant.components.wiz.async_setup_entry", return_value=True
|
|
|
|
) as mock_setup_entry:
|
|
|
|
result3 = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
{CONF_DEVICE: FAKE_MAC},
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result3["type"] == "create_entry"
|
|
|
|
assert result3["title"] == "WiZ Dimmable White ABCABC"
|
|
|
|
assert result3["data"] == {
|
|
|
|
CONF_HOST: "1.1.1.1",
|
|
|
|
}
|
|
|
|
assert len(mock_setup.mock_calls) == 1
|
|
|
|
assert len(mock_setup_entry.mock_calls) == 1
|
|
|
|
|
|
|
|
# ignore configured devices
|
|
|
|
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 not result["errors"]
|
|
|
|
|
|
|
|
with _patch_discovery():
|
|
|
|
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result2["type"] == "abort"
|
|
|
|
assert result2["reason"] == "no_devices_found"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_setup_via_discovery_cannot_connect(hass):
|
|
|
|
"""Test setting up via discovery and we fail to connect to the discovered device."""
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "user"
|
|
|
|
assert not result["errors"]
|
|
|
|
|
|
|
|
with _patch_discovery():
|
|
|
|
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result2["type"] == "form"
|
|
|
|
assert result2["step_id"] == "pick_device"
|
|
|
|
assert not result2["errors"]
|
|
|
|
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.wiz.wizlight.getBulbConfig",
|
|
|
|
side_effect=WizLightTimeOutError,
|
|
|
|
), _patch_discovery():
|
|
|
|
result3 = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
{CONF_DEVICE: FAKE_MAC},
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert result3["type"] == "abort"
|
|
|
|
assert result3["reason"] == "cannot_connect"
|