Prefill the ip address for powerwall discovery (#45209)

This commit is contained in:
J. Nick Koston 2021-01-16 16:09:04 -10:00 committed by GitHub
parent 11cbf1152d
commit b71a9b5e28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 154 additions and 21 deletions

View File

@ -5,19 +5,19 @@ from tesla_powerwall import MissingAttributeError, Powerwall, PowerwallUnreachab
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.components.dhcp import IP_ADDRESS
from homeassistant.const import CONF_IP_ADDRESS
from homeassistant.core import callback
from .const import DOMAIN # pylint:disable=unused-import
_LOGGER = logging.getLogger(__name__)
DATA_SCHEMA = vol.Schema({vol.Required(CONF_IP_ADDRESS): str})
async def validate_input(hass: core.HomeAssistant, data):
"""Validate the user input allows us to connect.
Data has the keys from DATA_SCHEMA with values provided by the user.
Data has the keys from schema with values provided by the user.
"""
power_wall = Powerwall(data[CONF_IP_ADDRESS])
@ -42,6 +42,20 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
def __init__(self):
"""Initialize the powerwall flow."""
self.ip_address = None
async def async_step_dhcp(self, dhcp_discovery):
"""Handle dhcp discovery."""
if self._async_ip_address_already_configured(dhcp_discovery[IP_ADDRESS]):
return self.async_abort(reason="already_configured")
self.ip_address = dhcp_discovery[IP_ADDRESS]
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["title_placeholders"] = {CONF_IP_ADDRESS: self.ip_address}
return await self.async_step_user()
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
@ -62,7 +76,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_create_entry(title=info["title"], data=user_input)
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
step_id="user",
data_schema=vol.Schema(
{vol.Required(CONF_IP_ADDRESS, default=self.ip_address): str}
),
errors=errors,
)
async def async_step_import(self, user_input):
@ -72,6 +90,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_user(user_input)
@callback
def _async_ip_address_already_configured(self, ip_address):
"""See if we already have an entry matching the ip_address."""
for entry in self._async_current_entries():
if entry.data.get(CONF_IP_ADDRESS) == ip_address:
return True
return False
class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""

View File

@ -1,5 +1,6 @@
{
"config": {
"flow_title": "Tesla Powerwall ({ip_address})",
"step": {
"user": {
"title": "Connect to the powerwall",

View File

@ -1,20 +1,21 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured"
},
"error": {
"cannot_connect": "Failed to connect",
"unknown": "Unexpected error",
"wrong_version": "Your powerwall uses a software version that is not supported. Please consider upgrading or reporting this issue so it can be resolved."
},
"step": {
"user": {
"data": {
"ip_address": "IP Address"
},
"title": "Connect to the powerwall"
}
"config": {
"flow_title": "Tesla Powerwall ({ip_address})",
"step": {
"user": {
"title": "Connect to the powerwall",
"data": {
"ip_address": "[%key:common::config_flow::data::ip%]"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"wrong_version": "Your powerwall uses a software version that is not supported. Please consider upgrading or reporting this issue so it can be resolved.",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
}
}
}

View File

@ -5,11 +5,14 @@ from unittest.mock import patch
from tesla_powerwall import MissingAttributeError, PowerwallUnreachableError
from homeassistant import config_entries, setup
from homeassistant.components.dhcp import HOSTNAME, IP_ADDRESS, MAC_ADDRESS
from homeassistant.components.powerwall.const import DOMAIN
from homeassistant.const import CONF_IP_ADDRESS
from .mocks import _mock_powerwall_side_effect, _mock_powerwall_site_name
from tests.common import MockConfigEntry
async def test_form_source_user(hass):
"""Test we get config flow setup form as a user."""
@ -93,6 +96,27 @@ async def test_form_cannot_connect(hass):
assert result2["errors"] == {"base": "cannot_connect"}
async def test_form_unknown_exeption(hass):
"""Test we handle an unknown exception."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
mock_powerwall = _mock_powerwall_side_effect(site_info=ValueError)
with patch(
"homeassistant.components.powerwall.config_flow.Powerwall",
return_value=mock_powerwall,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_IP_ADDRESS: "1.2.3.4"},
)
assert result2["type"] == "form"
assert result2["errors"] == {"base": "unknown"}
async def test_form_wrong_version(hass):
"""Test we can handle wrong version error."""
result = await hass.config_entries.flow.async_init(
@ -114,3 +138,84 @@ async def test_form_wrong_version(hass):
assert result3["type"] == "form"
assert result3["errors"] == {"base": "wrong_version"}
async def test_already_configured(hass):
"""Test we abort when already configured."""
await setup.async_setup_component(hass, "persistent_notification", {})
config_entry = MockConfigEntry(domain=DOMAIN, data={CONF_IP_ADDRESS: "1.1.1.1"})
config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data={
IP_ADDRESS: "1.1.1.1",
MAC_ADDRESS: "AA:BB:CC:DD:EE:FF",
HOSTNAME: "any",
},
)
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
async def test_already_configured_with_ignored(hass):
"""Test ignored entries do not break checking for existing entries."""
await setup.async_setup_component(hass, "persistent_notification", {})
config_entry = MockConfigEntry(domain=DOMAIN, data={}, source="ignore")
config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data={
IP_ADDRESS: "1.1.1.1",
MAC_ADDRESS: "AA:BB:CC:DD:EE:FF",
HOSTNAME: "any",
},
)
assert result["type"] == "form"
async def test_dhcp_discovery(hass):
"""Test we can process the discovery from dhcp."""
await setup.async_setup_component(hass, "persistent_notification", {})
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_DHCP},
data={
IP_ADDRESS: "1.1.1.1",
MAC_ADDRESS: "AA:BB:CC:DD:EE:FF",
HOSTNAME: "any",
},
)
assert result["type"] == "form"
assert result["errors"] == {}
mock_powerwall = await _mock_powerwall_site_name(hass, "Some site")
with patch(
"homeassistant.components.powerwall.config_flow.Powerwall",
return_value=mock_powerwall,
), patch(
"homeassistant.components.powerwall.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.powerwall.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_IP_ADDRESS: "1.1.1.1",
},
)
await hass.async_block_till_done()
assert result2["type"] == "create_entry"
assert result2["title"] == "Some site"
assert result2["data"] == {
CONF_IP_ADDRESS: "1.1.1.1",
}
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1