Add Plugwise zeroconf discovery (#37289)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Tom Scholten <tom@sue.nl>
Co-authored-by: Tom <CoMPaTech@users.noreply.github.com>
pull/37445/head
Bouwe Westerdijk 2020-07-04 00:28:34 +02:00 committed by GitHub
parent 80aebcc7d2
commit d57dbb4319
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 9 deletions

View File

@ -7,32 +7,50 @@ import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.const import CONF_HOST, CONF_PASSWORD
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import DiscoveryInfoType
from .const import DOMAIN # pylint:disable=unused-import
_LOGGER = logging.getLogger(__name__)
DATA_SCHEMA = vol.Schema(
{vol.Required(CONF_HOST): str, vol.Required(CONF_PASSWORD): str}
)
ZEROCONF_MAP = {
"smile": "P1 DSMR",
"smile_thermo": "Climate (Anna)",
"smile_open_therm": "Climate (Adam)",
}
def _base_schema(discovery_info):
"""Generate base schema."""
base_schema = {}
if not discovery_info:
base_schema[vol.Required(CONF_HOST)] = str
base_schema[vol.Required(CONF_PASSWORD)] = str
return vol.Schema(base_schema)
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 _base_schema() with values provided by the user.
"""
websession = async_get_clientsession(hass, verify_ssl=False)
api = Smile(
host=data["host"], password=data["password"], timeout=30, websession=websession
host=data[CONF_HOST],
password=data[CONF_PASSWORD],
timeout=30,
websession=websession,
)
try:
await api.connect()
except Smile.InvalidAuthentication:
raise InvalidAuth
except Smile.ConnectionFailedError:
except Smile.PlugwiseError:
raise CannotConnect
return api
@ -44,12 +62,39 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
def __init__(self):
"""Initialize the Plugwise config flow."""
self.discovery_info = {}
async def async_step_zeroconf(self, discovery_info: DiscoveryInfoType):
"""Prepare configuration for a discovered Plugwise Smile."""
self.discovery_info = discovery_info
_properties = self.discovery_info.get("properties")
unique_id = self.discovery_info.get("hostname").split(".")[0]
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
_product = _properties.get("product", None)
_version = _properties.get("version", "n/a")
_name = f"{ZEROCONF_MAP.get(_product,_product)} v{_version}"
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["title_placeholders"] = {
CONF_HOST: discovery_info[CONF_HOST],
"name": _name,
}
return await self.async_step_user()
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
if self.discovery_info:
user_input[CONF_HOST] = self.discovery_info[CONF_HOST]
try:
api = await validate_input(self.hass, user_input)
@ -64,12 +109,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if not errors:
await self.async_set_unique_id(api.gateway_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(title=api.smile_name, data=user_input)
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
step_id="user", data_schema=_base_schema(self.discovery_info), errors=errors
)

View File

@ -4,5 +4,6 @@
"documentation": "https://www.home-assistant.io/integrations/plugwise",
"requirements": ["Plugwise_Smile==1.1.0"],
"codeowners": ["@CoMPaTech", "@bouwew"],
"zeroconf": ["_plugwise._tcp.local."],
"config_flow": true
}

View File

@ -17,6 +17,7 @@
},
"abort": {
"already_configured": "This Smile is already configured"
}
},
"flow_title": "Smile: {name}"
}
}

View File

@ -44,6 +44,9 @@ ZEROCONF = {
"_nut._tcp.local.": [
"nut"
],
"_plugwise._tcp.local.": [
"plugwise"
],
"_printer._tcp.local.": [
"brother"
],

View File

@ -12,6 +12,7 @@ from tests.async_mock import patch
def mock_smile():
"""Create a Mock Smile for testing exceptions."""
with patch("homeassistant.components.plugwise.config_flow.Smile",) as smile_mock:
smile_mock.PlugwiseError = Smile.PlugwiseError
smile_mock.InvalidAuthentication = Smile.InvalidAuthentication
smile_mock.ConnectionFailedError = Smile.ConnectionFailedError
smile_mock.return_value.connect.return_value = True