Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 44 additions & 28 deletions homeassistant/components/esphome/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@
from aioesphomeapi import APIClient, APIConnectionError
import voluptuous as vol

from homeassistant import config_entries, core
from homeassistant.helpers.typing import ConfigType
from homeassistant.config_entries import CONN_CLASS_LOCAL_PUSH, ConfigFlow
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT
from homeassistant.core import callback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

from .entry_data import DATA_KEY, RuntimeEntryData

DOMAIN = "esphome"

@config_entries.HANDLERS.register("esphome")
class EsphomeFlowHandler(config_entries.ConfigFlow):

class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a esphome config flow."""

VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
CONNECTION_CLASS = CONN_CLASS_LOCAL_PUSH

def __init__(self):
"""Initialize flow."""
Expand All @@ -32,8 +35,8 @@ async def async_step_user(
return await self._async_authenticate_or_add(user_input)

fields = OrderedDict()
fields[vol.Required("host", default=self._host or vol.UNDEFINED)] = str
fields[vol.Optional("port", default=self._port or 6053)] = int
fields[vol.Required(CONF_HOST, default=self._host or vol.UNDEFINED)] = str
fields[vol.Optional(CONF_PORT, default=self._port or 6053)] = int

errors = {}
if error is not None:
Expand All @@ -46,19 +49,19 @@ async def async_step_user(
@property
def _name(self):
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
return self.context.get("name")
return self.context.get(CONF_NAME)

@_name.setter
def _name(self, value):
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["name"] = value
self.context[CONF_NAME] = value
self.context["title_placeholders"] = {"name": self._name}

def _set_user_input(self, user_input):
if user_input is None:
return
self._host = user_input["host"]
self._port = user_input["port"]
self._host = user_input[CONF_HOST]
self._port = user_input[CONF_PORT]

async def _async_authenticate_or_add(self, user_input):
self._set_user_input(user_input)
Expand All @@ -81,56 +84,69 @@ async def async_step_discovery_confirm(self, user_input=None):
step_id="discovery_confirm", description_placeholders={"name": self._name}
)

async def async_step_zeroconf(self, user_input: ConfigType):
async def async_step_zeroconf(self, discovery_info: DiscoveryInfoType):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just user_input: ConfigType actually (but technically they are the same defined right now).

"""Handle zeroconf discovery."""
# Hostname is format: livingroom.local.
local_name = user_input["hostname"][:-1]
local_name = discovery_info["hostname"][:-1]
node_name = local_name[: -len(".local")]
address = user_input["properties"].get("address", local_name)
address = discovery_info["properties"].get("address", local_name)

# Check if already configured
await self.async_set_unique_id(node_name)
self._abort_if_unique_id_configured(
updates={CONF_HOST: discovery_info[CONF_HOST]}
)

for entry in self._async_current_entries():
already_configured = False
if entry.data["host"] == address:
# Is this address already configured?

if (
entry.data[CONF_HOST] == address
or entry.data[CONF_HOST] == discovery_info[CONF_HOST]
):
# Is this address or IP address already configured?
already_configured = True
elif entry.entry_id in self.hass.data.get(DATA_KEY, {}):
# Does a config entry with this name already exist?
data: RuntimeEntryData = self.hass.data[DATA_KEY][entry.entry_id]

# Node names are unique in the network
if data.device_info is not None:
already_configured = data.device_info.name == node_name

if already_configured:
# Backwards compat, we update old entries
if not entry.unique_id:
self.hass.config_entries.async_update_entry(
entry,
data={**entry.data, CONF_HOST: discovery_info[CONF_HOST]},
unique_id=node_name,
)

return self.async_abort(reason="already_configured")

self._host = address
self._port = user_input["port"]
self._host = discovery_info[CONF_HOST]
self._port = discovery_info[CONF_PORT]
self._name = node_name

# Check if flow for this device already in progress
Comment thread
ctalkington marked this conversation as resolved.
for flow in self._async_in_progress():
if flow["context"].get("name") == node_name:
return self.async_abort(reason="already_configured")

return await self.async_step_discovery_confirm()

@core.callback
@callback
def _async_get_entry(self):
return self.async_create_entry(
title=self._name,
data={
"host": self._host,
"port": self._port,
CONF_HOST: self._host,
CONF_PORT: self._port,
# The API uses protobuf, so empty string denotes absence
"password": self._password or "",
CONF_PASSWORD: self._password or "",
},
)

async def async_step_authenticate(self, user_input=None, error=None):
"""Handle getting password for authentication."""
if user_input is not None:
self._password = user_input["password"]
self._password = user_input[CONF_PASSWORD]
error = await self.try_login()
if error:
return await self.async_step_authenticate(error=error)
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/esphome/strings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"config": {
"abort": { "already_configured": "ESP is already configured" },
"abort": {
"already_configured": "ESP is already configured",
"already_in_progress": "ESP configuration is already in progress"
},
"error": {
"resolve_error": "Can't resolve address of the ESP. If this error persists, please set a static IP address: https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips",
"connection_error": "Can't connect to ESP. Please make sure your YAML file contains an 'api:' line.",
Expand Down
5 changes: 3 additions & 2 deletions homeassistant/components/esphome/translations/en.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"config": {
"abort": {
"already_configured": "ESP is already configured"
"already_configured": "ESP is already configured",
"already_in_progress": "ESP configuration is already in progress"
},
"error": {
"connection_error": "Can't connect to ESP. Please make sure your YAML file contains an 'api:' line.",
Expand Down Expand Up @@ -31,4 +32,4 @@
}
}
}
}
}
Loading