Add dhcp discovery to senseme (#64894)

This commit is contained in:
J. Nick Koston 2022-01-25 08:07:17 -10:00 committed by GitHub
parent dfb7ab5c30
commit e28b5aee77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 3 deletions

View File

@ -8,6 +8,7 @@ from aiosenseme import SensemeDevice, async_get_device_by_ip_address
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import dhcp
from homeassistant.const import CONF_DEVICE, CONF_HOST, CONF_ID
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.typing import DiscoveryInfoType
@ -28,6 +29,19 @@ class SensemeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self._discovered_devices: list[SensemeDevice] | None = None
self._discovered_device: SensemeDevice | None = None
async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult:
"""Handle discovery via dhcp."""
# If discovery is already running, it takes precedence since its more efficient
if self._async_current_entries():
return self.async_abort(reason="already_configured")
if device := await async_get_device_by_ip_address(discovery_info.ip):
device.stop()
if device is None or not device.uuid:
return self.async_abort(reason="cannot_connect")
await self.async_set_unique_id(device.uuid)
self._discovered_device = device
return await self.async_step_discovery_confirm()
async def async_step_discovery(
self, discovery_info: DiscoveryInfoType
) -> FlowResult:

View File

@ -9,5 +9,6 @@
"codeowners": [
"@mikelawrence", "@bdraco"
],
"dhcp": [{"macaddress":"20F85E*"}],
"iot_class": "local_push"
}

View File

@ -19,7 +19,8 @@
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"error": {
"invalid_host": "[%key:common::config_flow::error::invalid_host%]",

View File

@ -1,7 +1,8 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured"
"already_configured": "Device is already configured",
"cannot_connect": "Failed to connect"
},
"error": {
"cannot_connect": "Failed to connect",

View File

@ -290,6 +290,10 @@ DHCP = [
"hostname": "sense-*",
"macaddress": "A4D578*"
},
{
"domain": "senseme",
"macaddress": "20F85E*"
},
{
"domain": "simplisafe",
"hostname": "simplisafe*",

View File

@ -10,6 +10,7 @@ from homeassistant.components.senseme import config_flow
MOCK_NAME = "Haiku Fan"
MOCK_UUID = "77a6b7b3-925d-4695-a415-76d76dca4444"
MOCK_ADDRESS = "127.0.0.1"
MOCK_MAC = "20:F8:5E:92:5A:75"
device = MagicMock(auto_spec=SensemeDevice)
device.async_update = AsyncMock()
@ -29,7 +30,7 @@ device.address = MOCK_ADDRESS
device.get_device_info = {
"name": MOCK_NAME,
"uuid": MOCK_UUID,
"mac": "20:F8:5E:92:5A:75",
"mac": MOCK_ADDRESS,
"address": MOCK_ADDRESS,
"base_model": "FAN,HAIKU,HSERIES",
"has_light": False,
@ -94,9 +95,14 @@ device2.get_device_info = {
"is_light": False,
}
device_no_uuid = MagicMock(auto_spec=SensemeDevice)
device_no_uuid.uuid = None
MOCK_DEVICE = device
MOCK_DEVICE_ALTERNATE_IP = device_alternate_ip
MOCK_DEVICE2 = device2
MOCK_DEVICE_NO_UUID = device_no_uuid
def _patch_discovery(device=None, no_device=None):

View File

@ -2,6 +2,7 @@
from unittest.mock import patch
from homeassistant import config_entries
from homeassistant.components import dhcp
from homeassistant.components.senseme.const import DOMAIN
from homeassistant.const import CONF_HOST, CONF_ID
from homeassistant.core import HomeAssistant
@ -16,12 +17,20 @@ from . import (
MOCK_DEVICE,
MOCK_DEVICE2,
MOCK_DEVICE_ALTERNATE_IP,
MOCK_DEVICE_NO_UUID,
MOCK_MAC,
MOCK_UUID,
_patch_discovery,
)
from tests.common import MockConfigEntry
DHCP_DISCOVERY = dhcp.DhcpServiceInfo(
hostname="any",
ip=MOCK_ADDRESS,
macaddress=MOCK_MAC,
)
async def test_form_user(hass: HomeAssistant) -> None:
"""Test we get the form as a user."""
@ -280,3 +289,77 @@ async def test_discovery_existing_device_ip_change(hass: HomeAssistant) -> None:
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
assert entry.data["info"]["address"] == "127.0.0.8"
async def test_dhcp_discovery_existing_config_entry(hass: HomeAssistant) -> None:
"""Test dhcp discovery is aborted if there is an existing config entry."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
"info": MOCK_DEVICE2.get_device_info,
},
unique_id=MOCK_DEVICE2.uuid,
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=DHCP_DISCOVERY
)
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
async def test_dhcp_discovery(hass: HomeAssistant) -> None:
"""Test we can setup a dhcp discovered device."""
with _patch_discovery(), patch(
"homeassistant.components.senseme.config_flow.async_get_device_by_ip_address",
return_value=MOCK_DEVICE,
), patch(
"homeassistant.components.senseme.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=DHCP_DISCOVERY
)
assert result["type"] == RESULT_TYPE_FORM
assert not result["errors"]
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"device": MOCK_UUID,
},
)
await hass.async_block_till_done()
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
assert result2["title"] == "Haiku Fan"
assert result2["data"] == {
"info": MOCK_DEVICE.get_device_info,
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_dhcp_discovery_cannot_connect(hass: HomeAssistant) -> None:
"""Test we abort if we cannot cannot to a dhcp discovered device."""
with _patch_discovery(), patch(
"homeassistant.components.senseme.config_flow.async_get_device_by_ip_address",
return_value=None,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=DHCP_DISCOVERY
)
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "cannot_connect"
async def test_dhcp_discovery_cannot_connect_no_uuid(hass: HomeAssistant) -> None:
"""Test we abort if the discovered device has no uuid."""
with _patch_discovery(), patch(
"homeassistant.components.senseme.config_flow.async_get_device_by_ip_address",
return_value=MOCK_DEVICE_NO_UUID,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=DHCP_DISCOVERY
)
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "cannot_connect"