Add Reolink dhcp discovery (#85880)
Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
parent
9c76cd1b6a
commit
32c1a01159
6 changed files with 119 additions and 7 deletions
|
@ -9,10 +9,12 @@ from reolink_aio.exceptions import ApiError, CredentialsInvalidError, ReolinkErr
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import dhcp
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
|
||||
from .const import CONF_PROTOCOL, CONF_USE_HTTPS, DOMAIN
|
||||
from .exceptions import ReolinkException, UserNotAdmin
|
||||
|
@ -87,6 +89,21 @@ class ReolinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
return await self.async_step_user()
|
||||
return self.async_show_form(step_id="reauth_confirm")
|
||||
|
||||
async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult:
|
||||
"""Handle discovery via dhcp."""
|
||||
mac_address = format_mac(discovery_info.macaddress)
|
||||
await self.async_set_unique_id(mac_address)
|
||||
self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.ip})
|
||||
|
||||
short_mac = mac_address[-8:].upper()
|
||||
self.context["title_placeholders"] = {
|
||||
"short_mac": short_mac,
|
||||
"ip_address": discovery_info.ip,
|
||||
}
|
||||
|
||||
self._host = discovery_info.ip
|
||||
return await self.async_step_user()
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
|
@ -95,6 +112,9 @@ class ReolinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
placeholders = {"error": ""}
|
||||
|
||||
if user_input is not None:
|
||||
if CONF_HOST not in user_input:
|
||||
user_input[CONF_HOST] = self._host
|
||||
|
||||
host = ReolinkHost(self.hass, user_input, DEFAULT_OPTIONS)
|
||||
try:
|
||||
await host.async_init()
|
||||
|
@ -144,9 +164,14 @@ class ReolinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
{
|
||||
vol.Required(CONF_USERNAME, default=self._username): str,
|
||||
vol.Required(CONF_PASSWORD, default=self._password): str,
|
||||
vol.Required(CONF_HOST, default=self._host): str,
|
||||
}
|
||||
)
|
||||
if self._host is None or errors:
|
||||
data_schema = data_schema.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST, default=self._host): str,
|
||||
}
|
||||
)
|
||||
if errors:
|
||||
data_schema = data_schema.extend(
|
||||
{
|
||||
|
|
|
@ -7,5 +7,11 @@
|
|||
"dependencies": ["webhook"],
|
||||
"codeowners": ["@starkillerOG"],
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["reolink_aio"]
|
||||
"loggers": ["reolink_aio"],
|
||||
"dhcp": [
|
||||
{
|
||||
"hostname": "reolink*",
|
||||
"macaddress": "EC71DB*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"config": {
|
||||
"flow_title": "{short_mac} ({ip_address})",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "{error}",
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"not_admin": "User needs to be admin, user ''{username}'' has authorisation level ''{userlevel}''",
|
||||
"unknown": "Unexpected error"
|
||||
},
|
||||
"flow_title": "{short_mac} ({ip_address})",
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"description": "The Reolink integration needs to re-authenticate your connection details",
|
||||
|
|
|
@ -384,6 +384,11 @@ DHCP: list[dict[str, str | bool]] = [
|
|||
"domain": "rainforest_eagle",
|
||||
"macaddress": "D8D5B9*",
|
||||
},
|
||||
{
|
||||
"domain": "reolink",
|
||||
"hostname": "reolink*",
|
||||
"macaddress": "EC71DB*",
|
||||
},
|
||||
{
|
||||
"domain": "ring",
|
||||
"hostname": "ring*",
|
||||
|
|
|
@ -6,6 +6,7 @@ import pytest
|
|||
from reolink_aio.exceptions import ApiError, CredentialsInvalidError, ReolinkError
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components import dhcp
|
||||
from homeassistant.components.reolink import const
|
||||
from homeassistant.components.reolink.config_flow import DEFAULT_PROTOCOL
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
|
||||
|
@ -314,7 +315,7 @@ async def test_reauth(hass):
|
|||
data=config_entry.data,
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
|
@ -322,21 +323,94 @@ async def test_reauth(hass):
|
|||
{},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: TEST_HOST2,
|
||||
CONF_USERNAME: TEST_USERNAME2,
|
||||
CONF_PASSWORD: TEST_PASSWORD2,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == "abort"
|
||||
assert result["type"] == data_entry_flow.FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
assert config_entry.data[CONF_HOST] == TEST_HOST2
|
||||
assert config_entry.data[CONF_HOST] == TEST_HOST
|
||||
assert config_entry.data[CONF_USERNAME] == TEST_USERNAME2
|
||||
assert config_entry.data[CONF_PASSWORD] == TEST_PASSWORD2
|
||||
|
||||
|
||||
async def test_dhcp_flow(hass):
|
||||
"""Successful flow from DHCP discovery."""
|
||||
dhcp_data = dhcp.DhcpServiceInfo(
|
||||
ip=TEST_HOST,
|
||||
hostname="Reolink",
|
||||
macaddress=TEST_MAC,
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
const.DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=dhcp_data
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == TEST_NVR_NAME
|
||||
assert result["data"] == {
|
||||
CONF_HOST: TEST_HOST,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
CONF_PORT: TEST_PORT,
|
||||
const.CONF_USE_HTTPS: TEST_USE_HTTPS,
|
||||
}
|
||||
assert result["options"] == {
|
||||
const.CONF_PROTOCOL: DEFAULT_PROTOCOL,
|
||||
}
|
||||
|
||||
|
||||
async def test_dhcp_abort_flow(hass):
|
||||
"""Test dhcp discovery aborts if already configured."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=const.DOMAIN,
|
||||
unique_id=format_mac(TEST_MAC),
|
||||
data={
|
||||
CONF_HOST: TEST_HOST,
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
CONF_PORT: TEST_PORT,
|
||||
const.CONF_USE_HTTPS: TEST_USE_HTTPS,
|
||||
},
|
||||
options={
|
||||
const.CONF_PROTOCOL: DEFAULT_PROTOCOL,
|
||||
},
|
||||
title=TEST_NVR_NAME,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
dhcp_data = dhcp.DhcpServiceInfo(
|
||||
ip=TEST_HOST,
|
||||
hostname="Reolink",
|
||||
macaddress=TEST_MAC,
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
const.DOMAIN, context={"source": config_entries.SOURCE_DHCP}, data=dhcp_data
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
|
Loading…
Add table
Reference in a new issue