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
parent
80aebcc7d2
commit
d57dbb4319
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
},
|
||||
"abort": {
|
||||
"already_configured": "This Smile is already configured"
|
||||
}
|
||||
},
|
||||
"flow_title": "Smile: {name}"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@ ZEROCONF = {
|
|||
"_nut._tcp.local.": [
|
||||
"nut"
|
||||
],
|
||||
"_plugwise._tcp.local.": [
|
||||
"plugwise"
|
||||
],
|
||||
"_printer._tcp.local.": [
|
||||
"brother"
|
||||
],
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue