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()
Beispiel #2
0
    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()
Beispiel #4
0
    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()
Beispiel #5
0
    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()
Beispiel #6
0
    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])
Beispiel #7
0
    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],
                ),
            )
Beispiel #8
0
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)
Beispiel #11
0
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"))