Use zeroconf attributes (A-D) (#58835)

Co-authored-by: epenet <epenet@users.noreply.github.com>
This commit is contained in:
epenet 2021-11-02 18:27:06 +01:00 committed by GitHub
parent 26055e1f14
commit e983370c27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 78 additions and 50 deletions

View File

@ -9,17 +9,13 @@ from pyatv.convert import protocol_str
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import ( from homeassistant.components import zeroconf
CONF_ADDRESS, from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_PIN, CONF_PROTOCOL
CONF_NAME,
CONF_PIN,
CONF_PROTOCOL,
CONF_TYPE,
)
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import AbortFlow from homeassistant.data_entry_flow import AbortFlow, FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import DiscoveryInfoType
from .const import CONF_CREDENTIALS, CONF_IDENTIFIER, CONF_START_OFF, DOMAIN from .const import CONF_CREDENTIALS, CONF_IDENTIFIER, CONF_START_OFF, DOMAIN
@ -148,16 +144,18 @@ class AppleTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
description_placeholders={"devices": self._devices_str()}, description_placeholders={"devices": self._devices_str()},
) )
async def async_step_zeroconf(self, discovery_info): async def async_step_zeroconf(
self, discovery_info: DiscoveryInfoType
) -> FlowResult:
"""Handle device found via zeroconf.""" """Handle device found via zeroconf."""
service_type = discovery_info[CONF_TYPE] service_type = discovery_info[zeroconf.ATTR_TYPE]
properties = discovery_info["properties"] properties = discovery_info[zeroconf.ATTR_PROPERTIES]
if service_type == "_mediaremotetv._tcp.local.": if service_type == "_mediaremotetv._tcp.local.":
identifier = properties["UniqueIdentifier"] identifier = properties["UniqueIdentifier"]
name = properties["Name"] name = properties["Name"]
elif service_type == "_touch-able._tcp.local.": elif service_type == "_touch-able._tcp.local.":
identifier = discovery_info["name"].split(".")[0] identifier = discovery_info[zeroconf.ATTR_NAME].split(".")[0]
name = properties["CtlN"] name = properties["CtlN"]
else: else:
return self.async_abort(reason="unknown") return self.async_abort(reason="unknown")

View File

@ -6,6 +6,7 @@ from urllib.parse import urlsplit
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components import zeroconf
from homeassistant.components.dhcp import HOSTNAME, IP_ADDRESS, MAC_ADDRESS from homeassistant.components.dhcp import HOSTNAME, IP_ADDRESS, MAC_ADDRESS
from homeassistant.config_entries import SOURCE_IGNORE from homeassistant.config_entries import SOURCE_IGNORE
from homeassistant.const import ( from homeassistant.const import (
@ -17,7 +18,9 @@ from homeassistant.const import (
CONF_USERNAME, CONF_USERNAME,
) )
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.device_registry import format_mac from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.typing import DiscoveryInfoType
from homeassistant.util.network import is_link_local from homeassistant.util.network import is_link_local
from .const import ( from .const import (
@ -174,14 +177,18 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN):
} }
) )
async def async_step_zeroconf(self, discovery_info: dict): async def async_step_zeroconf(
self, discovery_info: DiscoveryInfoType
) -> FlowResult:
"""Prepare configuration for a Zeroconf discovered Axis device.""" """Prepare configuration for a Zeroconf discovered Axis device."""
return await self._process_discovered_device( return await self._process_discovered_device(
{ {
CONF_HOST: discovery_info[CONF_HOST], CONF_HOST: discovery_info[zeroconf.ATTR_HOST],
CONF_MAC: format_mac(discovery_info["properties"]["macaddress"]), CONF_MAC: format_mac(
CONF_NAME: discovery_info["name"].split(".", 1)[0], discovery_info[zeroconf.ATTR_PROPERTIES]["macaddress"]
CONF_PORT: discovery_info[CONF_PORT], ),
CONF_NAME: discovery_info[zeroconf.ATTR_NAME].split(".", 1)[0],
CONF_PORT: discovery_info[zeroconf.ATTR_PORT],
} }
) )

View File

@ -10,6 +10,7 @@ from bond_api import Bond
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, exceptions from homeassistant import config_entries, exceptions
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, CONF_NAME from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, CONF_NAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -94,8 +95,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self, discovery_info: DiscoveryInfoType self, discovery_info: DiscoveryInfoType
) -> FlowResult: ) -> FlowResult:
"""Handle a flow initialized by zeroconf discovery.""" """Handle a flow initialized by zeroconf discovery."""
name: str = discovery_info[CONF_NAME] name: str = discovery_info[zeroconf.ATTR_NAME]
host: str = discovery_info[CONF_HOST] host: str = discovery_info[zeroconf.ATTR_HOST]
bond_id = name.partition(".")[0] bond_id = name.partition(".")[0]
await self.async_set_unique_id(bond_id) await self.async_set_unique_id(bond_id)
for entry in self._async_current_entries(): for entry in self._async_current_entries():

View File

@ -12,7 +12,7 @@ from boschshcpy.exceptions import (
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, core from homeassistant import config_entries, core
from homeassistant.components.zeroconf import async_get_instance from homeassistant.components import zeroconf
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_TOKEN from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_TOKEN
from .const import ( from .const import (
@ -40,7 +40,7 @@ def write_tls_asset(hass: core.HomeAssistant, filename: str, asset: bytes) -> No
file_handle.write(asset.decode("utf-8")) file_handle.write(asset.decode("utf-8"))
def create_credentials_and_validate(hass, host, user_input, zeroconf): def create_credentials_and_validate(hass, host, user_input, zeroconf_instance):
"""Create and store credentials and validate session.""" """Create and store credentials and validate session."""
helper = SHCRegisterClient(host, user_input[CONF_PASSWORD]) helper = SHCRegisterClient(host, user_input[CONF_PASSWORD])
result = helper.register(host, "HomeAssistant") result = helper.register(host, "HomeAssistant")
@ -54,21 +54,21 @@ def create_credentials_and_validate(hass, host, user_input, zeroconf):
hass.config.path(DOMAIN, CONF_SHC_CERT), hass.config.path(DOMAIN, CONF_SHC_CERT),
hass.config.path(DOMAIN, CONF_SHC_KEY), hass.config.path(DOMAIN, CONF_SHC_KEY),
True, True,
zeroconf, zeroconf_instance,
) )
session.authenticate() session.authenticate()
return result return result
def get_info_from_host(hass, host, zeroconf): def get_info_from_host(hass, host, zeroconf_instance):
"""Get information from host.""" """Get information from host."""
session = SHCSession( session = SHCSession(
host, host,
"", "",
"", "",
True, True,
zeroconf, zeroconf_instance,
) )
information = session.mdns_info() information = session.mdns_info()
return {"title": information.name, "unique_id": information.unique_id} return {"title": information.name, "unique_id": information.unique_id}
@ -123,14 +123,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the credentials step.""" """Handle the credentials step."""
errors = {} errors = {}
if user_input is not None: if user_input is not None:
zeroconf = await async_get_instance(self.hass) zeroconf_instance = await zeroconf.async_get_instance(self.hass)
try: try:
result = await self.hass.async_add_executor_job( result = await self.hass.async_add_executor_job(
create_credentials_and_validate, create_credentials_and_validate,
self.hass, self.hass,
self.host, self.host,
user_input, user_input,
zeroconf, zeroconf_instance,
) )
except SHCAuthenticationError: except SHCAuthenticationError:
errors["base"] = "invalid_auth" errors["base"] = "invalid_auth"
@ -183,14 +183,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_zeroconf(self, discovery_info): async def async_step_zeroconf(self, discovery_info):
"""Handle zeroconf discovery.""" """Handle zeroconf discovery."""
if not discovery_info.get("name", "").startswith("Bosch SHC"): if not discovery_info.get(zeroconf.ATTR_NAME, "").startswith("Bosch SHC"):
return self.async_abort(reason="not_bosch_shc") return self.async_abort(reason="not_bosch_shc")
try: try:
hosts = ( hosts = (
discovery_info["host"] discovery_info[zeroconf.ATTR_HOST]
if isinstance(discovery_info["host"], list) if isinstance(discovery_info[zeroconf.ATTR_HOST], list)
else [discovery_info["host"]] else [discovery_info[zeroconf.ATTR_HOST]]
) )
for host in hosts: for host in hosts:
if host.startswith("169."): # skip link local address if host.startswith("169."): # skip link local address
@ -202,7 +202,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
except SHCConnectionError: except SHCConnectionError:
return self.async_abort(reason="cannot_connect") return self.async_abort(reason="cannot_connect")
local_name = discovery_info["hostname"][:-1] local_name = discovery_info[zeroconf.ATTR_HOSTNAME][:-1]
node_name = local_name[: -len(".local")] node_name = local_name[: -len(".local")]
await self.async_set_unique_id(self.info["unique_id"]) await self.async_set_unique_id(self.info["unique_id"])
@ -227,11 +227,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def _get_info(self, host): async def _get_info(self, host):
"""Get additional information.""" """Get additional information."""
zeroconf = await async_get_instance(self.hass) zeroconf_instance = await zeroconf.async_get_instance(self.hass)
return await self.hass.async_add_executor_job( return await self.hass.async_add_executor_job(
get_info_from_host, get_info_from_host,
self.hass, self.hass,
host, host,
zeroconf, zeroconf_instance,
) )

View File

@ -9,6 +9,7 @@ from brother import Brother, SnmpError, UnsupportedModel
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, exceptions from homeassistant import config_entries, exceptions
from homeassistant.components import zeroconf
from homeassistant.const import CONF_HOST, CONF_TYPE from homeassistant.const import CONF_HOST, CONF_TYPE
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.typing import DiscoveryInfoType from homeassistant.helpers.typing import DiscoveryInfoType
@ -84,13 +85,13 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
) -> FlowResult: ) -> FlowResult:
"""Handle zeroconf discovery.""" """Handle zeroconf discovery."""
# Hostname is format: brother.local. # Hostname is format: brother.local.
self.host = discovery_info["hostname"].rstrip(".") self.host = discovery_info[zeroconf.ATTR_HOSTNAME].rstrip(".")
# Do not probe the device if the host is already configured # Do not probe the device if the host is already configured
self._async_abort_entries_match({CONF_HOST: self.host}) self._async_abort_entries_match({CONF_HOST: self.host})
snmp_engine = get_snmp_engine(self.hass) snmp_engine = get_snmp_engine(self.hass)
model = discovery_info.get("properties", {}).get("product") model = discovery_info.get(zeroconf.ATTR_PROPERTIES, {}).get("product")
try: try:
self.brother = Brother(self.host, snmp_engine=snmp_engine, model=model) self.brother = Brother(self.host, snmp_engine=snmp_engine, model=model)

View File

@ -2,7 +2,9 @@
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import DiscoveryInfoType
from .const import CONF_IGNORE_CEC, CONF_KNOWN_HOSTS, CONF_UUID, DOMAIN from .const import CONF_IGNORE_CEC, CONF_KNOWN_HOSTS, CONF_UUID, DOMAIN
@ -49,7 +51,9 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_config() return await self.async_step_config()
async def async_step_zeroconf(self, discovery_info): async def async_step_zeroconf(
self, discovery_info: DiscoveryInfoType
) -> FlowResult:
"""Handle a flow initialized by zeroconf discovery.""" """Handle a flow initialized by zeroconf discovery."""
if self._async_in_progress() or self._async_current_entries(): if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="single_instance_allowed") return self.async_abort(reason="single_instance_allowed")

View File

@ -10,7 +10,10 @@ from pydaikin.discovery import Discovery
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components import zeroconf
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.typing import DiscoveryInfoType
from .const import CONF_UUID, DOMAIN, KEY_MAC, TIMEOUT from .const import CONF_UUID, DOMAIN, KEY_MAC, TIMEOUT
@ -123,18 +126,20 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
user_input.get(CONF_PASSWORD), user_input.get(CONF_PASSWORD),
) )
async def async_step_zeroconf(self, discovery_info): async def async_step_zeroconf(
self, discovery_info: DiscoveryInfoType
) -> FlowResult:
"""Prepare configuration for a discovered Daikin device.""" """Prepare configuration for a discovered Daikin device."""
_LOGGER.debug("Zeroconf user_input: %s", discovery_info) _LOGGER.debug("Zeroconf user_input: %s", discovery_info)
devices = Discovery().poll(ip=discovery_info[CONF_HOST]) devices = Discovery().poll(ip=discovery_info[zeroconf.ATTR_HOST])
if not devices: if not devices:
_LOGGER.debug( _LOGGER.debug(
"Could not find MAC-address for %s," "Could not find MAC-address for %s,"
" make sure the required UDP ports are open (see integration documentation)", " make sure the required UDP ports are open (see integration documentation)",
discovery_info[CONF_HOST], discovery_info[zeroconf.ATTR_HOST],
) )
return self.async_abort(reason="cannot_connect") return self.async_abort(reason="cannot_connect")
await self.async_set_unique_id(next(iter(devices))[KEY_MAC]) await self.async_set_unique_id(next(iter(devices))[KEY_MAC])
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
self.host = discovery_info[CONF_HOST] self.host = discovery_info[zeroconf.ATTR_HOST]
return await self.async_step_user() return await self.async_step_user()

View File

@ -6,6 +6,7 @@ from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback from homeassistant.core import callback
@ -49,7 +50,10 @@ class DevoloHomeControlFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
) -> FlowResult: ) -> FlowResult:
"""Handle zeroconf discovery.""" """Handle zeroconf discovery."""
# Check if it is a gateway # Check if it is a gateway
if discovery_info.get("properties", {}).get("MT") in SUPPORTED_MODEL_TYPES: if (
discovery_info.get(zeroconf.ATTR_PROPERTIES, {}).get("MT")
in SUPPORTED_MODEL_TYPES
):
await self._async_handle_discovery_without_unique_id() await self._async_handle_discovery_without_unique_id()
return await self.async_step_zeroconf_confirm() return await self.async_step_zeroconf_confirm()
return self.async_abort(reason="Not a devolo Home Control gateway.") return self.async_abort(reason="Not a devolo Home Control gateway.")

View File

@ -77,17 +77,17 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self, discovery_info: DiscoveryInfoType self, discovery_info: DiscoveryInfoType
) -> FlowResult: ) -> FlowResult:
"""Handle zerooconf discovery.""" """Handle zerooconf discovery."""
if discovery_info["properties"]["MT"] in ["2600", "2601"]: if discovery_info[zeroconf.ATTR_PROPERTIES]["MT"] in ["2600", "2601"]:
return self.async_abort(reason="home_control") return self.async_abort(reason="home_control")
await self.async_set_unique_id(discovery_info["properties"]["SN"]) await self.async_set_unique_id(discovery_info[zeroconf.ATTR_PROPERTIES]["SN"])
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context[CONF_HOST] = discovery_info["host"] self.context[CONF_HOST] = discovery_info[zeroconf.ATTR_HOST]
self.context["title_placeholders"] = { self.context["title_placeholders"] = {
PRODUCT: discovery_info["properties"]["Product"], PRODUCT: discovery_info[zeroconf.ATTR_PROPERTIES]["Product"],
CONF_NAME: discovery_info["hostname"].split(".")[0], CONF_NAME: discovery_info[zeroconf.ATTR_HOSTNAME].split(".")[0],
} }
return await self.async_step_zeroconf_confirm() return await self.async_step_zeroconf_confirm()

View File

@ -8,8 +8,11 @@ import requests
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, core, exceptions from homeassistant import config_entries, core, exceptions
from homeassistant.components import zeroconf
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.typing import DiscoveryInfoType
from homeassistant.util.network import is_link_local from homeassistant.util.network import is_link_local
from .const import CONF_EVENTS, DOMAIN, DOORBIRD_OUI from .const import CONF_EVENTS, DOMAIN, DOORBIRD_OUI
@ -90,10 +93,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
data = self.discovery_schema or _schema_with_defaults() data = self.discovery_schema or _schema_with_defaults()
return self.async_show_form(step_id="user", data_schema=data, errors=errors) return self.async_show_form(step_id="user", data_schema=data, errors=errors)
async def async_step_zeroconf(self, discovery_info): async def async_step_zeroconf(
self, discovery_info: DiscoveryInfoType
) -> FlowResult:
"""Prepare configuration for a discovered doorbird device.""" """Prepare configuration for a discovered doorbird device."""
macaddress = discovery_info["properties"]["macaddress"] macaddress = discovery_info[zeroconf.ATTR_PROPERTIES]["macaddress"]
host = discovery_info[CONF_HOST] host = discovery_info[zeroconf.ATTR_HOST]
if macaddress[:6] != DOORBIRD_OUI: if macaddress[:6] != DOORBIRD_OUI:
return self.async_abort(reason="not_doorbird_device") return self.async_abort(reason="not_doorbird_device")
@ -109,7 +114,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="not_doorbird_device") return self.async_abort(reason="not_doorbird_device")
chop_ending = "._axis-video._tcp.local." chop_ending = "._axis-video._tcp.local."
friendly_hostname = discovery_info["name"] friendly_hostname = discovery_info[zeroconf.ATTR_NAME]
if friendly_hostname.endswith(chop_ending): if friendly_hostname.endswith(chop_ending):
friendly_hostname = friendly_hostname[: -len(chop_ending)] friendly_hostname = friendly_hostname[: -len(chop_ending)]

View File

@ -63,8 +63,11 @@ MAX_NAME_LEN = 63
# Attributes for HaServiceInfo # Attributes for HaServiceInfo
ATTR_HOST: Final = "host" ATTR_HOST: Final = "host"
ATTR_HOSTNAME: Final = "hostname"
ATTR_NAME: Final = "name" ATTR_NAME: Final = "name"
ATTR_PORT: Final = "port"
ATTR_PROPERTIES: Final = "properties" ATTR_PROPERTIES: Final = "properties"
ATTR_TYPE: Final = "type"
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(