Add dhcp support to guardian (#50378)

This commit is contained in:
J. Nick Koston 2021-05-10 21:26:15 -05:00 committed by GitHub
parent 887ec2d9b5
commit b36c840909
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 33 deletions

View File

@ -4,13 +4,19 @@ from aioguardian.errors import GuardianError
import voluptuous as vol
from homeassistant import config_entries, core
from homeassistant.components.dhcp import IP_ADDRESS
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
from homeassistant.core import callback
from .const import CONF_UID, DOMAIN, LOGGER
DEFAULT_PORT = 7777
DATA_SCHEMA = vol.Schema(
{vol.Required(CONF_IP_ADDRESS): str, vol.Required(CONF_PORT, default=7777): int}
{
vol.Required(CONF_IP_ADDRESS): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
}
)
UNIQUE_ID = "guardian_{0}"
@ -53,7 +59,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def _async_set_unique_id(self, pin):
"""Set the config entry's unique ID (based on the device's 4-digit PIN)."""
await self.async_set_unique_id(UNIQUE_ID.format(pin))
self._abort_if_unique_id_configured()
if self.discovery_info:
self._abort_if_unique_id_configured(
updates={CONF_IP_ADDRESS: self.discovery_info[CONF_IP_ADDRESS]}
)
else:
self._abort_if_unique_id_configured()
async def async_step_user(self, user_input=None):
"""Handle configuration via the UI."""
@ -79,31 +90,38 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
title=info[CONF_UID], data={CONF_UID: info["uid"], **user_input}
)
async def async_step_dhcp(self, discovery_info):
"""Handle the configuration via dhcp."""
self.discovery_info = {
CONF_IP_ADDRESS: discovery_info[IP_ADDRESS],
CONF_PORT: DEFAULT_PORT,
}
return await self._async_handle_discovery()
async def async_step_zeroconf(self, discovery_info):
"""Handle the configuration via zeroconf."""
if discovery_info is None:
return self.async_abort(reason="cannot_connect")
pin = async_get_pin_from_discovery_hostname(discovery_info["hostname"])
await self._async_set_unique_id(pin)
self.context[CONF_IP_ADDRESS] = discovery_info["host"]
if any(
discovery_info["host"] == flow["context"][CONF_IP_ADDRESS]
for flow in self._async_in_progress()
):
return self.async_abort(reason="already_in_progress")
self.discovery_info = {
CONF_IP_ADDRESS: discovery_info["host"],
CONF_PORT: discovery_info["port"],
}
pin = async_get_pin_from_discovery_hostname(discovery_info["hostname"])
await self._async_set_unique_id(pin)
return await self._async_handle_discovery()
return await self.async_step_zeroconf_confirm()
async def _async_handle_discovery(self):
"""Handle any discovery."""
self.context[CONF_IP_ADDRESS] = self.discovery_info[CONF_IP_ADDRESS]
if any(
self.context[CONF_IP_ADDRESS] == flow["context"][CONF_IP_ADDRESS]
for flow in self._async_in_progress()
):
return self.async_abort(reason="already_in_progress")
async def async_step_zeroconf_confirm(self, user_input=None):
"""Finish the configuration via zeroconf."""
return await self.async_step_discovery_confirm()
async def async_step_discovery_confirm(self, user_input=None):
"""Finish the configuration via any discovery."""
if user_input is None:
return self.async_show_form(step_id="zeroconf_confirm")
self._set_confirm_only()
return self.async_show_form(step_id="discovery_confirm")
return await self.async_step_user(self.discovery_info)

View File

@ -6,5 +6,15 @@
"requirements": ["aioguardian==1.0.4"],
"zeroconf": ["_api._udp.local."],
"codeowners": ["@bachya"],
"iot_class": "local_polling"
"iot_class": "local_polling",
"dhcp": [
{
"hostname": "gvc*",
"macaddress": "30AEA4*"
},
{
"hostname": "guardian*",
"macaddress": "30AEA4*"
}
]
}

View File

@ -8,7 +8,7 @@
"port": "[%key:common::config_flow::data::port%]"
}
},
"zeroconf_confirm": {
"discovery_confirm": {
"description": "Do you want to set up this Guardian device?"
}
},

View File

@ -6,15 +6,15 @@
"cannot_connect": "Failed to connect"
},
"step": {
"discovery_confirm": {
"description": "Do you want to set up this Guardian device?"
},
"user": {
"data": {
"ip_address": "IP Address",
"port": "Port"
},
"description": "Configure a local Elexa Guardian device."
},
"zeroconf_confirm": {
"description": "Do you want to set up this Guardian device?"
}
}
}

View File

@ -66,6 +66,16 @@ DHCP = [
"domain": "flume",
"hostname": "flume-gw-*"
},
{
"domain": "guardian",
"hostname": "gvc*",
"macaddress": "30AEA4*"
},
{
"domain": "guardian",
"hostname": "guardian*",
"macaddress": "30AEA4*"
},
{
"domain": "hunterdouglas_powerview",
"hostname": "hunter*",

View File

@ -4,12 +4,13 @@ from unittest.mock import patch
from aioguardian.errors import GuardianError
from homeassistant import data_entry_flow
from homeassistant.components.dhcp import HOSTNAME, IP_ADDRESS, MAC_ADDRESS
from homeassistant.components.guardian import CONF_UID, DOMAIN
from homeassistant.components.guardian.config_flow import (
async_get_pin_from_discovery_hostname,
async_get_pin_from_uid,
)
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF
from homeassistant.config_entries import SOURCE_DHCP, SOURCE_USER, SOURCE_ZEROCONF
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
from tests.common import MockConfigEntry
@ -95,7 +96,7 @@ async def test_step_zeroconf(hass, ping_client):
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=zeroconf_data
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "zeroconf_confirm"
assert result["step_id"] == "discovery_confirm"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
@ -124,7 +125,7 @@ async def test_step_zeroconf_already_in_progress(hass):
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=zeroconf_data
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "zeroconf_confirm"
assert result["step_id"] == "discovery_confirm"
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=zeroconf_data
@ -133,10 +134,48 @@ async def test_step_zeroconf_already_in_progress(hass):
assert result["reason"] == "already_in_progress"
async def test_step_zeroconf_no_discovery_info(hass):
"""Test the zeroconf step aborting because no discovery info came along."""
async def test_step_dhcp(hass, ping_client):
"""Test the dhcp step."""
dhcp_data = {
IP_ADDRESS: "192.168.1.100",
HOSTNAME: "GVC1-ABCD.local.",
MAC_ADDRESS: "aa:bb:cc:dd:ee:ff",
}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_ZEROCONF}
DOMAIN, context={"source": SOURCE_DHCP}, data=dhcp_data
)
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "cannot_connect"
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "discovery_confirm"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["title"] == "ABCDEF123456"
assert result["data"] == {
CONF_IP_ADDRESS: "192.168.1.100",
CONF_PORT: 7777,
CONF_UID: "ABCDEF123456",
}
async def test_step_dhcp_already_in_progress(hass):
"""Test the zeroconf step aborting because it's already in progress."""
dhcp_data = {
IP_ADDRESS: "192.168.1.100",
HOSTNAME: "GVC1-ABCD.local.",
MAC_ADDRESS: "aa:bb:cc:dd:ee:ff",
}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_DHCP}, data=dhcp_data
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "discovery_confirm"
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_DHCP}, data=dhcp_data
)
assert result["type"] == "abort"
assert result["reason"] == "already_in_progress"