async def async_step_zeroconf(self, discovery_info): """Prepare configuration for a discovered Axis device.""" serial_number = discovery_info["properties"]["macaddress"] if serial_number[:6] not in AXIS_OUI: return self.async_abort(reason="not_axis_device") if is_link_local(ip_address(discovery_info[CONF_HOST])): return self.async_abort(reason="link_local_address") await self.async_set_unique_id(serial_number) self._abort_if_unique_id_configured( updates={ CONF_HOST: discovery_info[CONF_HOST], CONF_PORT: discovery_info[CONF_PORT], }) # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 self.context["title_placeholders"] = { CONF_NAME: discovery_info["hostname"][:-7], CONF_HOST: discovery_info[CONF_HOST], } self.discovery_schema = { vol.Required(CONF_HOST, default=discovery_info[CONF_HOST]): str, vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str, vol.Required(CONF_PORT, default=discovery_info[CONF_PORT]): int, } return await self.async_step_user()
async def async_step_zeroconf(self, discovery_info): """Prepare configuration for a discovered doorbird device.""" macaddress = discovery_info["properties"]["macaddress"] if macaddress[:6] != DOORBIRD_OUI: return self.async_abort(reason="not_doorbird_device") if is_link_local(ip_address(discovery_info[CONF_HOST])): return self.async_abort(reason="link_local_address") await self.async_set_unique_id(macaddress) self._abort_if_unique_id_configured( updates={CONF_HOST: discovery_info[CONF_HOST]}) chop_ending = "._axis-video._tcp.local." friendly_hostname = discovery_info["name"] if friendly_hostname.endswith(chop_ending): friendly_hostname = friendly_hostname[:-len(chop_ending)] # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 self.context["title_placeholders"] = { CONF_NAME: friendly_hostname, CONF_HOST: discovery_info[CONF_HOST], } self.discovery_schema = _schema_with_defaults( host=discovery_info[CONF_HOST], name=friendly_hostname) return await self.async_step_user()
async def _process_discovered_device(self, device: dict): """Prepare configuration for a discovered Axis device.""" if device[CONF_MAC][:8] not in AXIS_OUI: return self.async_abort(reason="not_axis_device") if is_link_local(ip_address(device[CONF_HOST])): return self.async_abort(reason="link_local_address") await self.async_set_unique_id(device[CONF_MAC]) self._abort_if_unique_id_configured(updates={ CONF_HOST: device[CONF_HOST], CONF_PORT: device[CONF_PORT], }) self.context.update({ "title_placeholders": { CONF_NAME: device[CONF_NAME], CONF_HOST: device[CONF_HOST], }, "configuration_url": f"http://{device[CONF_HOST]}:{device[CONF_PORT]}", }) self.discovery_schema = { vol.Required(CONF_HOST, default=device[CONF_HOST]): str, vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str, vol.Required(CONF_PORT, default=device[CONF_PORT]): int, } return await self.async_step_user()
async def _process_discovered_device(self, device: dict): """Prepare configuration for a discovered Axis device.""" if device[CONF_MAC][:8] not in AXIS_OUI: return self.async_abort(reason="not_axis_device") if is_link_local(ip_address(device[CONF_HOST])): return self.async_abort(reason="link_local_address") await self.async_set_unique_id(device[CONF_MAC]) self._abort_if_unique_id_configured(updates={ CONF_HOST: device[CONF_HOST], CONF_PORT: device[CONF_PORT], }) # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 self.context["title_placeholders"] = { CONF_NAME: device[CONF_NAME], CONF_HOST: device[CONF_HOST], } self.discovery_schema = { vol.Required(CONF_HOST, default=device[CONF_HOST]): str, vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str, vol.Required(CONF_PORT, default=device[CONF_PORT]): int, } return await self.async_step_user()
async def async_step_zeroconf(self, discovery_info): """Prepare configuration for a discovered doorbird device.""" macaddress = discovery_info["properties"]["macaddress"] host = discovery_info[CONF_HOST] if macaddress[:6] != DOORBIRD_OUI: return self.async_abort(reason="not_doorbird_device") if is_link_local(ip_address(host)): return self.async_abort(reason="link_local_address") if not await async_verify_supported_device(self.hass, host): return self.async_abort(reason="not_doorbird_device") await self.async_set_unique_id(macaddress) self._abort_if_unique_id_configured(updates={CONF_HOST: host}) chop_ending = "._axis-video._tcp.local." friendly_hostname = discovery_info["name"] if friendly_hostname.endswith(chop_ending): friendly_hostname = friendly_hostname[:-len(chop_ending)] self.context["title_placeholders"] = { CONF_NAME: friendly_hostname, CONF_HOST: host, } self.discovery_schema = _schema_with_defaults(host=host, name=friendly_hostname) return await self.async_step_user()
def process_client(self, ip_address, hostname, mac_address): """Process a client.""" made_ip_address = make_ip_address(ip_address) if ( is_link_local(made_ip_address) or is_loopback(made_ip_address) or is_invalid(made_ip_address) ): # Ignore self assigned addresses, loopback, invalid return data = self._address_data.get(ip_address) if ( data and data[MAC_ADDRESS] == mac_address and data[HOSTNAME].startswith(hostname) ): # If the address data is the same no need # to process it return self._address_data[ip_address] = {MAC_ADDRESS: mac_address, HOSTNAME: hostname} self.process_updated_address_data(ip_address, self._address_data[ip_address])
def async_process_client(self, ip_address, hostname, mac_address): """Process a client.""" made_ip_address = make_ip_address(ip_address) if ( is_link_local(made_ip_address) or is_loopback(made_ip_address) or is_invalid(made_ip_address) ): # Ignore self assigned addresses, loopback, invalid return data = self._address_data.get(ip_address) if ( data and data[MAC_ADDRESS] == mac_address and data[HOSTNAME].startswith(hostname) ): # If the address data is the same no need # to process it return data = {MAC_ADDRESS: mac_address, HOSTNAME: hostname} self._address_data[ip_address] = data lowercase_hostname = data[HOSTNAME].lower() uppercase_mac = data[MAC_ADDRESS].upper() _LOGGER.debug( "Processing updated address data for %s: mac=%s hostname=%s", ip_address, uppercase_mac, lowercase_hostname, ) for entry in self._integration_matchers: if MAC_ADDRESS in entry and not fnmatch.fnmatch( uppercase_mac, entry[MAC_ADDRESS] ): continue if HOSTNAME in entry and not fnmatch.fnmatch( lowercase_hostname, entry[HOSTNAME] ): continue _LOGGER.debug("Matched %s against %s", data, entry) discovery_flow.async_create_flow( self.hass, entry["domain"], {"source": config_entries.SOURCE_DHCP}, DhcpServiceInfo( ip=ip_address, hostname=lowercase_hostname, macaddress=data[MAC_ADDRESS], ), )
class LaMetricFlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN): """Handle a LaMetric config flow.""" DOMAIN = DOMAIN VERSION = 1 devices: dict[str, CloudDevice] discovered_host: str discovered_serial: str discovered: bool = False @property def logger(self) -> logging.Logger: """Return logger.""" return LOGGER @property def extra_authorize_data(self) -> dict[str, Any]: """Extra data that needs to be appended to the authorize url.""" return {"scope": "basic devices_read"} async def async_step_user(self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle a flow initiated by the user.""" return await self.async_step_choice_enter_manual_or_fetch_cloud() async def async_step_ssdp(self, discovery_info: SsdpServiceInfo) -> FlowResult: """Handle a flow initiated by SSDP discovery.""" url = URL(discovery_info.ssdp_location or "") if url.host is None or not (serial := discovery_info.upnp.get(ATTR_UPNP_SERIAL)): return self.async_abort(reason="invalid_discovery_info") if is_link_local(ip_address(url.host)): return self.async_abort(reason="link_local_address") await self.async_set_unique_id(serial) self._abort_if_unique_id_configured(updates={CONF_HOST: url.host}) self.context.update({ "title_placeholders": { "name": discovery_info.upnp.get(ATTR_UPNP_FRIENDLY_NAME, "LaMetric TIME"), }, "configuration_url": "https://developer.lametric.com", }) self.discovered = True self.discovered_host = str(url.host) self.discovered_serial = serial return await self.async_step_choice_enter_manual_or_fetch_cloud()
def process_client(self, ip_address, hostname, mac_address): """Process a client.""" if is_link_local(make_ip_address(ip_address)): # Ignore self assigned addresses return data = self._address_data.get(ip_address) if data and data[MAC_ADDRESS] == mac_address and data[HOSTNAME] == hostname: # If the address data is the same no need # to process it return self._address_data[ip_address] = {MAC_ADDRESS: mac_address, HOSTNAME: hostname} self.process_updated_address_data(ip_address, self._address_data[ip_address])
def async_process_client(self, ip_address: str, hostname: str, mac_address: str) -> None: """Process a client.""" made_ip_address = make_ip_address(ip_address) if (is_link_local(made_ip_address) or is_loopback(made_ip_address) or is_invalid(made_ip_address)): # Ignore self assigned addresses, loopback, invalid return data = self._address_data.get(ip_address) if (data and data[MAC_ADDRESS] == mac_address and data[HOSTNAME].startswith(hostname)): # If the address data is the same no need # to process it return data = {MAC_ADDRESS: mac_address, HOSTNAME: hostname} self._address_data[ip_address] = data lowercase_hostname = hostname.lower() uppercase_mac = mac_address.upper() _LOGGER.debug( "Processing updated address data for %s: mac=%s hostname=%s", ip_address, uppercase_mac, lowercase_hostname, ) matched_domains = set() device_domains = set() dev_reg: DeviceRegistry = async_get(self.hass) if device := dev_reg.async_get_device(identifiers=set(), connections={ (CONNECTION_NETWORK_MAC, uppercase_mac) }): for entry_id in device.config_entries: if entry := self.hass.config_entries.async_get_entry(entry_id): device_domains.add(entry.domain)
def test_is_link_local(): """Test link local addresses.""" assert network_util.is_link_local(ip_address("169.254.12.3")) assert network_util.is_link_local(ip_address("fe80::1234:5678:abcd")) assert not network_util.is_link_local(ip_address("127.0.0.1")) assert not network_util.is_link_local(ip_address("::1"))