Esempio n. 1
0
 async def _async_handle_discovery(self) -> FlowResult:
     """Handle any discovery."""
     device = self._discovered_device
     assert device is not None
     await self._async_set_discovered_mac(device, self._allow_update_mac)
     host = device[ATTR_IPADDR]
     self.context[CONF_HOST] = host
     for progress in self._async_in_progress():
         if progress.get("context", {}).get(CONF_HOST) == host:
             return self.async_abort(reason="already_in_progress")
     if not device[ATTR_MODEL_DESCRIPTION]:
         mac_address = device[ATTR_ID]
         assert mac_address is not None
         mac = dr.format_mac(mac_address)
         try:
             device = await self._async_try_connect(host, device)
         except FLUX_LED_EXCEPTIONS:
             return self.async_abort(reason="cannot_connect")
         else:
             discovered_mac = device[ATTR_ID]
             if device[ATTR_MODEL_DESCRIPTION] or (
                     discovered_mac is not None and
                 (formatted_discovered_mac := dr.format_mac(discovered_mac))
                     and formatted_discovered_mac != mac
                     and mac_matches_by_one(discovered_mac, mac)):
                 self._discovered_device = device
                 await self._async_set_discovered_mac(device, True)
Esempio n. 2
0
    async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult:
        """Handle dhcp discovery."""
        self._async_abort_entries_match({CONF_HOST: discovery_info.ip})

        formatted_mac = format_mac(discovery_info.macaddress)
        await self.async_set_unique_id(format_mac(formatted_mac))
        self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.ip})
        self.host = discovery_info.hostname
        self.mac = formatted_mac
        self.ip_address = discovery_info.ip
        self.context["title_placeholders"] = {"ip": self.ip_address, "mac": self.mac}
        return await self.async_step_user()
Esempio n. 3
0
    async def async_step_dhcp(self, discovery_info):
        """Handle dhcp discovery."""
        self._async_abort_entries_match({CONF_HOST: discovery_info[IP_ADDRESS]})

        formatted_mac = format_mac(discovery_info[MAC_ADDRESS])
        await self.async_set_unique_id(format_mac(formatted_mac))
        self._abort_if_unique_id_configured(
            updates={CONF_HOST: discovery_info[IP_ADDRESS]}
        )
        self.host = discovery_info[HOSTNAME]
        self.mac = formatted_mac
        self.ip_address = discovery_info[IP_ADDRESS]
        self.context["title_placeholders"] = {"ip": self.ip_address, "mac": self.mac}
        return await self.async_step_user()
Esempio n. 4
0
async def async_get_config_entry_diagnostics(
        hass: HomeAssistant, config_entry: ConfigEntry) -> dict[str, Any]:
    """Return diagnostics for a config entry."""
    controller: UniFiController = hass.data[UNIFI_DOMAIN][
        config_entry.entry_id]
    diag: dict[str, Any] = {}
    macs_to_redact: dict[str, str] = {}

    counter = 0
    for mac in chain(controller.api.clients, controller.api.devices):
        macs_to_redact[mac] = format_mac(str(counter).zfill(12))
        counter += 1

    for device in controller.api.devices.values():
        for entry in device.raw.get("ethernet_table", []):
            mac = entry.get("mac", "")
            if mac not in macs_to_redact:
                macs_to_redact[mac] = format_mac(str(counter).zfill(12))
                counter += 1

    diag["config"] = async_redact_data(
        async_replace_dict_data(config_entry.as_dict(), macs_to_redact),
        REDACT_CONFIG)
    diag["site_role"] = controller.site_role
    diag["entities"] = async_replace_dict_data(controller.entities,
                                               macs_to_redact)
    diag["clients"] = {
        macs_to_redact[k]:
        async_redact_data(async_replace_dict_data(v.raw, macs_to_redact),
                          REDACT_CLIENTS)
        for k, v in controller.api.clients.items()
    }
    diag["devices"] = {
        macs_to_redact[k]:
        async_redact_data(async_replace_dict_data(v.raw, macs_to_redact),
                          REDACT_DEVICES)
        for k, v in controller.api.devices.items()
    }
    diag["dpi_apps"] = {k: v.raw for k, v in controller.api.dpi_apps.items()}
    diag["dpi_groups"] = {
        k: v.raw
        for k, v in controller.api.dpi_groups.items()
    }
    diag["wlans"] = {
        k: async_redact_data(async_replace_dict_data(v.raw, macs_to_redact),
                             REDACT_WLANS)
        for k, v in controller.api.wlans.items()
    }

    return diag
Esempio n. 5
0
    async def async_step_user(self, user_input=None):
        """Handle the initial step."""
        errors = {}
        if user_input is not None:
            try:
                info = await fetch_mac_and_title(self.hass,
                                                 user_input[CONF_HOST])
            except aiohttp.ClientError:
                errors[CONF_HOST] = "cannot_connect"
            except Exception:  # pylint: disable=broad-except
                _LOGGER.exception("Unexpected exception")
                errors["base"] = "unknown"
            else:
                await self.async_set_unique_id(format_mac(info["mac_address"]),
                                               raise_on_progress=False)
                self._abort_if_unique_id_configured()
                return self.async_create_entry(title=info["title"],
                                               data=user_input)

        return self.async_show_form(
            step_id="user",
            data_schema=vol.Schema(
                {vol.Required("host", default=self.discovered_ip): str}),
            errors=errors,
        )
Esempio n. 6
0
def get_androidtv_mac(dev_props: dict[str, Any]) -> str | None:
    """Return formatted mac from device properties."""
    for prop_mac in (PROP_ETHMAC, PROP_WIFIMAC):
        if if_mac := dev_props.get(prop_mac):
            mac = format_mac(if_mac)
            if mac not in _INVALID_MACS:
                return mac
Esempio n. 7
0
    async def async_step_user(self,
                              user_input: dict[str, Any] | None = None
                              ) -> FlowResult:
        """Handle a flow initialized by the user."""
        errors: dict[str, str] = {}

        if user_input is not None:
            self.host = user_input[CONF_HOST]

            try:
                mac = await async_get_mac(self.hass, self.host, {})
            except AuthFailed:
                return await self.async_step_credentials()
            except (ApiError, ClientConnectorError, asyncio.TimeoutError):
                errors["base"] = "cannot_connect"
            except CannotGetMac:
                return self.async_abort(reason="device_unsupported")
            except Exception:  # pylint: disable=broad-except
                _LOGGER.exception("Unexpected exception")
                errors["base"] = "unknown"
            else:
                await self.async_set_unique_id(format_mac(mac))
                self._abort_if_unique_id_configured({CONF_HOST: self.host})

                return self.async_create_entry(
                    title=self.host,
                    data=user_input,
                )

        return self.async_show_form(
            step_id="user",
            data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
            errors=errors,
        )
Esempio n. 8
0
 def _async_register_bridge(self, dev_reg):
     """Register the bridge as a device so homekit_controller and exclude it from discovery."""
     formatted_mac = device_registry.format_mac(self.driver.state.mac)
     # Connections and identifiers are both used here.
     #
     # connections exists so homekit_controller can know the
     # virtual mac address of the bridge and know to not offer
     # it via discovery.
     #
     # identifiers is used as well since the virtual mac may change
     # because it will not survive manual pairing resets (deleting state file)
     # which we have trained users to do over the past few years
     # because this was the way you had to fix homekit when pairing
     # failed.
     #
     connection = (device_registry.CONNECTION_NETWORK_MAC, formatted_mac)
     identifier = (DOMAIN, self._entry_id, BRIDGE_SERIAL_NUMBER)
     self._async_purge_old_bridges(dev_reg, identifier, connection)
     dev_reg.async_get_or_create(
         config_entry_id=self._entry_id,
         identifiers={identifier},
         connections={connection},
         manufacturer=MANUFACTURER,
         name=self._name,
         model="Home Assistant HomeKit Bridge",
     )
Esempio n. 9
0
    async def async_step_user(self, user_input=None):
        """Handle a flow initialized by the user."""
        errors = {}
        if user_input is None:
            return self.async_show_form_step_user(errors)

        self.interface = user_input[CONF_INTERFACE]

        # allow optional manual setting of host and mac
        if self.host is None:
            self.host = user_input.get(CONF_HOST)
        if self.sid is None:
            mac_address = user_input.get(CONF_MAC)

            # format sid from mac_address
            if mac_address is not None:
                self.sid = format_mac(mac_address).replace(":", "")

        # if host is already known by zeroconf discovery or manual optional settings
        if self.host is not None and self.sid is not None:
            # Connect to Xiaomi Aqara Gateway
            self.selected_gateway = await self.hass.async_add_executor_job(
                XiaomiGateway,
                self.host,
                self.sid,
                None,
                DEFAULT_DISCOVERY_RETRY,
                self.interface,
                MULTICAST_PORT,
                None,
            )

            if self.selected_gateway.connection_error:
                errors[CONF_HOST] = "invalid_host"
            if self.selected_gateway.mac_error:
                errors[CONF_MAC] = "invalid_mac"
            if errors:
                return self.async_show_form_step_user(errors)

            return await self.async_step_settings()

        # Discover Xiaomi Aqara Gateways in the netwerk to get required SIDs.
        xiaomi = XiaomiGatewayDiscovery(self.hass.add_job, [], self.interface)
        try:
            await self.hass.async_add_executor_job(xiaomi.discover_gateways)
        except gaierror:
            errors[CONF_INTERFACE] = "invalid_interface"
            return self.async_show_form_step_user(errors)

        self.gateways = xiaomi.gateways

        if len(self.gateways) == 1:
            self.selected_gateway = list(self.gateways.values())[0]
            self.sid = self.selected_gateway.sid
            return await self.async_step_settings()
        if len(self.gateways) > 1:
            return await self.async_step_select()

        errors["base"] = "discovery_error"
        return self.async_show_form_step_user(errors)
Esempio n. 10
0
def async_update_entry_from_discovery(
    hass: HomeAssistant,
    entry: config_entries.ConfigEntry,
    device: FluxLEDDiscovery,
    model_num: int | None,
    allow_update_mac: bool,
) -> bool:
    """Update a config entry from a flux_led discovery."""
    data_updates: dict[str, Any] = {}
    mac_address = device[ATTR_ID]
    assert mac_address is not None
    updates: dict[str, Any] = {}
    formatted_mac = dr.format_mac(mac_address)
    if not entry.unique_id or (
        allow_update_mac
        and entry.unique_id != formatted_mac
        and mac_matches_by_one(formatted_mac, entry.unique_id)
    ):
        updates["unique_id"] = formatted_mac
    if model_num and entry.data.get(CONF_MODEL_NUM) != model_num:
        data_updates[CONF_MODEL_NUM] = model_num
    async_populate_data_from_discovery(entry.data, data_updates, device)
    if is_ip_address(entry.title):
        updates["title"] = async_name_from_discovery(device, model_num)
    title_matches_name = entry.title == entry.data.get(CONF_NAME)
    if data_updates or title_matches_name:
        updates["data"] = {**entry.data, **data_updates}
        if title_matches_name:
            del updates["data"][CONF_NAME]
    # If the title has changed and the config entry is loaded, a listener is
    # in place, and we should not reload
    if updates and not ("title" in updates and entry.state is ConfigEntryState.LOADED):
        return hass.config_entries.async_update_entry(entry, **updates)
    return False
Esempio n. 11
0
def _async_register_mac(hass: HomeAssistant, domain: str, mac: str,
                        unique_id: str) -> None:
    """Register a mac address with a unique ID."""
    data_key = "device_tracker_mac"
    mac = dr.format_mac(mac)
    if data_key in hass.data:
        hass.data[data_key][mac] = (domain, unique_id)
        return

    # Setup listening.

    # dict mapping mac -> partial unique ID
    data = hass.data[data_key] = {mac: (domain, unique_id)}

    @callback
    def handle_device_event(ev: Event) -> None:
        """Enable the online status entity for the mac of a newly created device."""
        # Only for new devices
        if ev.data["action"] != "create":
            return

        dev_reg = dr.async_get(hass)
        device_entry = dev_reg.async_get(ev.data["device_id"])

        if device_entry is None:
            return

        # Check if device has a mac
        mac = None
        for conn in device_entry.connections:
            if conn[0] == dr.CONNECTION_NETWORK_MAC:
                mac = conn[1]
                break

        if mac is None:
            return

        # Check if we have an entity for this mac
        if (unique_id := data.get(mac)) is None:
            return

        ent_reg = er.async_get(hass)
        entity_id = ent_reg.async_get_entity_id(DOMAIN, *unique_id)

        if entity_id is None:
            return

        entity_entry = ent_reg.async_get(entity_id)

        if entity_entry is None:
            return

        # Make sure entity has a config entry and was disabled by the
        # default disable logic in the integration.
        if (entity_entry.config_entry_id is None or entity_entry.disabled_by !=
                er.RegistryEntryDisabler.INTEGRATION):
            return

        # Enable entity
        ent_reg.async_update_entity(entity_id, disabled_by=None)
Esempio n. 12
0
 async def async_step_zeroconf(self, discovery_info: DiscoveryInfoType):
     """Handle a flow initialized by zeroconf discovery."""
     self._mac = format_mac(discovery_info[ATTR_PROPERTIES]["deviceid"])
     await self._async_start_discovery_for_host(discovery_info[CONF_HOST])
     await self._async_set_device_unique_id()
     self.context["title_placeholders"] = {"device": self._title}
     return await self.async_step_confirm()
Esempio n. 13
0
    def __init__(
        self,
        hass: HomeAssistant,
        client: pyflic.FlicClient,
        address: str,
        timeout: int,
        ignored_click_types: list[str] | None,
    ) -> None:
        """Initialize the flic button."""

        self._attr_extra_state_attributes = {"address": address}
        self._attr_name = f"flic_{address.replace(':', '')}"
        self._attr_unique_id = format_mac(address)
        self._hass = hass
        self._address = address
        self._timeout = timeout
        self._attr_is_on = True
        self._ignored_click_types = ignored_click_types or []
        self._hass_click_types = {
            pyflic.ClickType.ButtonClick: CLICK_TYPE_SINGLE,
            pyflic.ClickType.ButtonSingleClick: CLICK_TYPE_SINGLE,
            pyflic.ClickType.ButtonDoubleClick: CLICK_TYPE_DOUBLE,
            pyflic.ClickType.ButtonHold: CLICK_TYPE_HOLD,
        }

        self._channel = self._create_channel()
        client.add_connection_channel(self._channel)
Esempio n. 14
0
async def async_setup_platform(hass,
                               config,
                               async_add_entities,
                               discovery_info=None):
    """Set up the OpenGarage covers."""
    covers = []
    devices = config.get(CONF_COVERS)

    for device_config in devices.values():
        opengarage_url = (
            f"{'https' if device_config[CONF_SSL] else 'http'}://"
            f"{device_config.get(CONF_HOST)}:{device_config.get(CONF_PORT)}")

        open_garage = opengarage.OpenGarage(
            opengarage_url,
            device_config[CONF_DEVICE_KEY],
            device_config[CONF_VERIFY_SSL],
            async_get_clientsession(hass),
        )
        status = await open_garage.update_state()
        covers.append(
            OpenGarageCover(device_config.get(CONF_NAME), open_garage,
                            format_mac(status["mac"])))

    async_add_entities(covers, True)
Esempio n. 15
0
 async def async_step_user(self, user_input=None):
     errors = {}
     if user_input is not None:
         if user_input.get(CONF_HOST):
             self.host = user_input[CONF_HOST]
         token = user_input.get(CONF_TOKEN)
         device = MiioDevice(self.host, token)
         try:
             info = device.info()
         except DeviceException:
             info = None
             errors['base'] = 'cannot_connect'
         _LOGGER.debug('Xiaomi Miot async_step_user %s', {
             'user_input': user_input,
             'info': info,
             'errors': errors,
         })
         if info is not None:
             unique_id = format_mac(info.mac_address)
             await self.async_set_unique_id(unique_id)
             self._abort_if_unique_id_configured()
             if not user_input.get(CONF_MODEL):
                 user_input[CONF_MODEL] = str(info.model or '')
             user_input['miio_info'] = dict(info.raw or {})
             return self.async_create_entry(
                 title=user_input.get(CONF_NAME),
                 data=user_input,
             )
     return self.async_show_form(
         step_id='user',
         data_schema=MIIO_CONFIG_SCHEMA,
         errors=errors,
     )
Esempio n. 16
0
    async def async_update_device_trackers(self, now=None) -> bool:
        """Update Netgear devices."""
        new_device = False
        ntg_devices = await self.async_get_attached_devices()
        now = dt_util.utcnow()

        if ntg_devices is None:
            return new_device

        if _LOGGER.isEnabledFor(logging.DEBUG):
            _LOGGER.debug("Netgear scan result: \n%s", ntg_devices)

        for ntg_device in ntg_devices:
            device_mac = format_mac(ntg_device.mac)

            if not self.devices.get(device_mac):
                new_device = True

            # ntg_device is a namedtuple from the collections module that needs conversion to a dict through ._asdict method
            self.devices[device_mac] = ntg_device._asdict()
            self.devices[device_mac]["mac"] = device_mac
            self.devices[device_mac]["last_seen"] = now

        for device in self.devices.values():
            device["active"] = now - device["last_seen"] <= self._consider_home

        if new_device:
            _LOGGER.debug("Netgear tracker: new device found")

        return new_device
Esempio n. 17
0
    async def async_step_zeroconf(self, discovery_info):
        """Handle zeroconf discovery."""
        name = discovery_info.get("name")
        self.host = discovery_info.get("host")
        mac_address = discovery_info.get("properties", {}).get("mac")

        if not name or not self.host or not mac_address:
            return self.async_abort(reason="not_xiaomi_aqara")

        # Check if the discovered device is an xiaomi aqara gateway.
        if not (name.startswith(ZEROCONF_GATEWAY)
                or name.startswith(ZEROCONF_ACPARTNER)):
            _LOGGER.debug(
                "Xiaomi device '%s' discovered with host %s, not identified as xiaomi aqara gateway",
                name,
                self.host,
            )
            return self.async_abort(reason="not_xiaomi_aqara")

        # format mac (include semicolns and make lowercase)
        mac_address = format_mac(mac_address)

        # format sid from mac_address
        self.sid = mac_address.replace(":", "")

        unique_id = mac_address
        await self.async_set_unique_id(unique_id)
        self._abort_if_unique_id_configured({
            CONF_HOST: self.host,
            CONF_MAC: mac_address
        })

        self.context.update({"title_placeholders": {"name": self.host}})

        return await self.async_step_user()
Esempio n. 18
0
    async def device_info(self):
        """Get device info"""
        device_info = await self.info()

        if device_info is None:
            return None

        info = {
            "identifiers": {(DOMAIN, device_info["id"])},
            "name":
            device_info["name"],
            "manufacturer":
            MANUFACTURER,
            "model":
            device_info["name"],
            "sw_version":
            f'{device_info["version"]}.{device_info["revision"]}.{device_info["build"]}',
        }

        mac = device_info["mac"]

        if mac is not None:
            info["connections"] = {(CONNECTION_NETWORK_MAC, format_mac(mac))}

        return info
Esempio n. 19
0
    async def async_step_ssdp(self, discovery_info):
        """Handle a discovered UniFi device."""
        parsed_url = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION])
        model_description = discovery_info[ssdp.ATTR_UPNP_MODEL_DESCRIPTION]
        mac_address = format_mac(discovery_info[ssdp.ATTR_UPNP_SERIAL])

        self.config = {
            CONF_HOST: parsed_url.hostname,
        }

        if self._host_already_configured(self.config[CONF_HOST]):
            return self.async_abort(reason="already_configured")

        await self.async_set_unique_id(mac_address)
        self._abort_if_unique_id_configured(updates=self.config)

        self.context["title_placeholders"] = {
            CONF_HOST: self.config[CONF_HOST],
            CONF_SITE_ID: DEFAULT_SITE_ID,
        }

        port = MODEL_PORTS.get(model_description)
        if port is not None:
            self.config[CONF_PORT] = port

        return await self.async_step_user()
Esempio n. 20
0
async def _async_migrate_unique_ids(
    hass: HomeAssistant,
    entry: ConfigEntry,
    coordinator: AirzoneUpdateCoordinator,
) -> None:
    """Migrate entities when the mac address gets discovered."""
    @callback
    def _async_migrator(
            entity_entry: er.RegistryEntry) -> dict[str, Any] | None:
        updates = None

        unique_id = entry.unique_id
        entry_id = entry.entry_id
        entity_unique_id = entity_entry.unique_id

        if entity_unique_id.startswith(entry_id):
            new_unique_id = f"{unique_id}{entity_unique_id[len(entry_id):]}"
            _LOGGER.debug(
                "Migrating unique_id from [%s] to [%s]",
                entity_unique_id,
                new_unique_id,
            )
            updates = {"new_unique_id": new_unique_id}

        return updates

    if (entry.unique_id is None and AZD_WEBSERVER in coordinator.data
            and AZD_MAC in coordinator.data[AZD_WEBSERVER]
            and (mac := coordinator.data[AZD_WEBSERVER][AZD_MAC]) is not None):
        updates: dict[str, Any] = {
            "unique_id": dr.format_mac(mac),
        }
        hass.config_entries.async_update_entry(entry, **updates)

        await er.async_migrate_entries(hass, entry.entry_id, _async_migrator)
Esempio n. 21
0
    async def _async_set_discovered_mac(self, device: FluxLEDDiscovery,
                                        allow_update_mac: bool) -> None:
        """Set the discovered mac.

        We only allow it to be updated if it comes from udp
        discovery since the dhcp mac can be one digit off from
        the udp discovery mac for devices with multiple network interfaces
        """
        mac_address = device[ATTR_ID]
        assert mac_address is not None
        mac = dr.format_mac(mac_address)
        await self.async_set_unique_id(mac)
        for entry in self._async_current_entries(include_ignore=False):
            if entry.data[CONF_HOST] == device[ATTR_IPADDR] or (
                    entry.unique_id and ":" in entry.unique_id
                    and mac_matches_by_one(entry.unique_id, mac)):
                if (async_update_entry_from_discovery(self.hass, entry, device,
                                                      None, allow_update_mac)
                        or entry.state
                        == config_entries.ConfigEntryState.SETUP_RETRY):
                    self.hass.async_create_task(
                        self.hass.config_entries.async_reload(entry.entry_id))
                else:
                    async_dispatcher_send(
                        self.hass,
                        FLUX_LED_DISCOVERY_SIGNAL.format(
                            entry_id=entry.entry_id),
                    )
                raise AbortFlow("already_configured")
Esempio n. 22
0
    def __init__(self, device, device_type, xiaomi_hub, config_entry):
        """Initialize the Xiaomi device."""
        self._state = None
        self._is_available = True
        self._sid = device["sid"]
        self._model = device["model"]
        self._protocol = device["proto"]
        self._name = f"{device_type}_{self._sid}"
        self._device_name = f"{self._model}_{self._sid}"
        self._type = device_type
        self._write_to_hub = xiaomi_hub.write_to_hub
        self._get_from_hub = xiaomi_hub.get_from_hub
        self._extra_state_attributes = {}
        self._remove_unavailability_tracker = None
        self._xiaomi_hub = xiaomi_hub
        self.parse_data(device["data"], device["raw_data"])
        self.parse_voltage(device["data"])

        if hasattr(self, "_data_key") and self._data_key:  # pylint: disable=no-member
            self._unique_id = (
                f"{self._data_key}{self._sid}"  # pylint: disable=no-member
            )
        else:
            self._unique_id = f"{self._type}{self._sid}"

        self._gateway_id = config_entry.unique_id
        if config_entry.data[CONF_MAC] == format_mac(self._sid):
            # this entity belongs to the gateway itself
            self._is_gateway = True
            self._device_id = config_entry.unique_id
        else:
            # this entity is connected through zigbee
            self._is_gateway = False
            self._device_id = self._sid
Esempio n. 23
0
    def device_info(self) -> DeviceInfo:
        """Return device information about this MusicCast device."""

        device_info = DeviceInfo(
            name=self.device_name,
            identifiers={
                (
                    DOMAIN,
                    self.device_id,
                )
            },
            manufacturer=BRAND,
            model=self.coordinator.data.model_name,
            sw_version=self.coordinator.data.system_version,
        )

        if self._zone_id == DEFAULT_ZONE:
            device_info["connections"] = {
                (CONNECTION_NETWORK_MAC, format_mac(mac))
                for mac in self.coordinator.data.mac_addresses.values()
            }
        else:
            device_info["via_device"] = (DOMAIN, self.coordinator.data.device_id)

        return device_info
Esempio n. 24
0
 async def _async_handle_discovery(self) -> FlowResult:
     """Handle any discovery."""
     device = self._discovered_device
     assert device is not None
     mac = dr.format_mac(device.mac_address)
     host = device.ip_address
     await self.async_set_unique_id(mac)
     for entry in self._async_current_entries(include_ignore=False):
         if (entry.unique_id == mac
                 or urlparse(entry.data[CONF_HOST]).hostname == host):
             if async_update_entry_from_discovery(self.hass, entry, device):
                 self.hass.async_create_task(
                     self.hass.config_entries.async_reload(entry.entry_id))
             return self.async_abort(reason="already_configured")
     self.context[CONF_HOST] = host
     for progress in self._async_in_progress():
         if progress.get("context", {}).get(CONF_HOST) == host:
             return self.async_abort(reason="already_in_progress")
     # Handled ignored case since _async_current_entries
     # is called with include_ignore=False
     self._abort_if_unique_id_configured()
     if not device.port:
         if discovered_device := await async_discover_device(
                 self.hass, host):
             self._discovered_device = discovered_device
         else:
             return self.async_abort(reason="cannot_connect")
Esempio n. 25
0
    async def async_step_credentials(self,
                                     user_input: dict[str, Any] | None = None
                                     ) -> FlowResult:
        """Handle the credentials step."""
        errors: dict[str, str] = {}

        if user_input is not None:
            try:
                mac = await async_get_mac(self.hass, self.host, user_input)
            except AuthFailed:
                errors["base"] = "invalid_auth"
            except (ApiError, ClientConnectorError, asyncio.TimeoutError):
                errors["base"] = "cannot_connect"
            except CannotGetMac:
                return self.async_abort(reason="device_unsupported")
            except Exception:  # pylint: disable=broad-except
                _LOGGER.exception("Unexpected exception")
                errors["base"] = "unknown"
            else:
                await self.async_set_unique_id(format_mac(mac))
                self._abort_if_unique_id_configured({CONF_HOST: self.host})

                return self.async_create_entry(
                    title=self.host,
                    data={
                        **user_input, CONF_HOST: self.host
                    },
                )

        return self.async_show_form(step_id="credentials",
                                    data_schema=AUTH_SCHEMA,
                                    errors=errors)
Esempio n. 26
0
async def test_discovery_ignored_model(hass, controller):
    """Already paired."""
    device = setup_mock_accessory(controller)
    discovery_info = get_device_discovery_info(device)

    config_entry = MockConfigEntry(domain=config_flow.HOMEKIT_BRIDGE_DOMAIN,
                                   data={})
    formatted_mac = device_registry.format_mac("AA:BB:CC:DD:EE:FF")

    dev_reg = mock_device_registry(hass)
    dev_reg.async_get_or_create(
        config_entry_id=config_entry.entry_id,
        identifiers={(
            config_flow.HOMEKIT_BRIDGE_DOMAIN,
            config_entry.entry_id,
            config_flow.HOMEKIT_BRIDGE_SERIAL_NUMBER,
        )},
        connections={(device_registry.CONNECTION_NETWORK_MAC, formatted_mac)},
        model=config_flow.HOMEKIT_BRIDGE_MODEL,
    )

    discovery_info["properties"]["id"] = "AA:BB:CC:DD:EE:FF"

    # Device is discovered
    result = await hass.config_entries.flow.async_init(
        "homekit_controller",
        context={"source": "zeroconf"},
        data=discovery_info)
    assert result["type"] == "abort"
    assert result["reason"] == "ignored_model"
Esempio n. 27
0
 def __init__(self, name, device, **kwargs):
     self._device = device
     self._config = dict(kwargs.get('config') or {})
     try:
         miio_info = kwargs.get('miio_info')
         if miio_info and isinstance(miio_info, dict):
             miio_info = MiioInfo(miio_info)
         self._miio_info = miio_info if isinstance(
             miio_info, MiioInfo) else device.info()
     except DeviceException as exc:
         _LOGGER.error("Device %s unavailable or token incorrect: %s", name,
                       exc)
         raise PlatformNotReady from exc
     except socket.gaierror as exc:
         _LOGGER.error("Device %s unavailable: socket.gaierror %s", name,
                       exc)
         raise PlatformNotReady from exc
     self._unique_did = dr.format_mac(self._miio_info.mac_address)
     self._unique_id = self._unique_did
     self._name = name
     self._model = self._miio_info.model or ''
     self._state = None
     self._available = False
     self._state_attrs = {
         CONF_MODEL: self._model,
         'lan_ip': self._miio_info.network_interface.get('localIp'),
         'mac_address': self._miio_info.mac_address,
         'firmware_version': self._miio_info.firmware_version,
         'hardware_version': self._miio_info.hardware_version,
         'entity_class': self.__class__.__name__,
     }
     self._supported_features = 0
     self._props = ['power']
     self._success_result = ['ok']
Esempio n. 28
0
async def test_discovery_ignored_hk_bridge(hass, controller):
    """Ensure we ignore homekit bridges and accessories created by the homekit integration."""
    device = setup_mock_accessory(controller)
    discovery_info = get_device_discovery_info(device)

    config_entry = MockConfigEntry(domain=config_flow.HOMEKIT_BRIDGE_DOMAIN,
                                   data={})
    config_entry.add_to_hass(hass)
    formatted_mac = device_registry.format_mac("AA:BB:CC:DD:EE:FF")

    dev_reg = mock_device_registry(hass)
    dev_reg.async_get_or_create(
        config_entry_id=config_entry.entry_id,
        connections={(device_registry.CONNECTION_NETWORK_MAC, formatted_mac)},
    )

    discovery_info.properties[
        zeroconf.ATTR_PROPERTIES_ID] = "AA:BB:CC:DD:EE:FF"

    # Device is discovered
    result = await hass.config_entries.flow.async_init(
        "homekit_controller",
        context={"source": config_entries.SOURCE_ZEROCONF},
        data=discovery_info,
    )
    assert result["type"] == "abort"
    assert result["reason"] == "ignored_model"
Esempio n. 29
0
    async def async_update_device_trackers(self, now=None) -> None:
        """Update Netgear devices."""
        new_device = False
        ntg_devices = await self.async_get_attached_devices()
        now = dt_util.utcnow()

        for ntg_device in ntg_devices:
            device_mac = format_mac(ntg_device.mac)

            if self._method_version == 2 and not ntg_device.link_rate:
                continue

            if not self.devices.get(device_mac):
                new_device = True

            # ntg_device is a namedtuple from the collections module that needs conversion to a dict through ._asdict method
            self.devices[device_mac] = ntg_device._asdict()
            self.devices[device_mac]["mac"] = device_mac
            self.devices[device_mac]["last_seen"] = now

        for device in self.devices.values():
            device["active"] = now - device["last_seen"] <= self._consider_home

        async_dispatcher_send(self.hass, self.signal_device_update)

        if new_device:
            _LOGGER.debug("Netgear tracker: new device found")
            async_dispatcher_send(self.hass, self.signal_device_new)
Esempio n. 30
0
async def test_discovery_does_not_ignore_non_homekit(hass, controller):
    """Do not ignore devices that are not from the homekit integration."""
    device = setup_mock_accessory(controller)
    discovery_info = get_device_discovery_info(device)

    config_entry = MockConfigEntry(domain="not_homekit", data={})
    config_entry.add_to_hass(hass)
    formatted_mac = device_registry.format_mac("AA:BB:CC:DD:EE:FF")

    dev_reg = mock_device_registry(hass)
    dev_reg.async_get_or_create(
        config_entry_id=config_entry.entry_id,
        connections={(device_registry.CONNECTION_NETWORK_MAC, formatted_mac)},
    )

    discovery_info.properties[
        zeroconf.ATTR_PROPERTIES_ID] = "AA:BB:CC:DD:EE:FF"

    # Device is discovered
    result = await hass.config_entries.flow.async_init(
        "homekit_controller",
        context={"source": config_entries.SOURCE_ZEROCONF},
        data=discovery_info,
    )
    assert result["type"] == "form"