コード例 #1
0
async def test_discovery_ignored_hk_bridge(opp, 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_opp(opp)
    formatted_mac = device_registry.format_mac("AA:BB:CC:DD:EE:FF")

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

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

    # Device is discovered
    result = await opp.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"
コード例 #2
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
    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,
        }

        self._async_abort_entries_match({CONF_HOST: self.config[CONF_HOST]})

        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()
コード例 #3
0
ファイル: __init__.py プロジェクト: OpenPeerPower/core
    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
コード例 #4
0
 def _async_register_bridge(self):
     """Register the bridge as a device so homekit_controller and exclude it from discovery."""
     dev_reg = device_registry.async_get(self.opp)
     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)
     is_accessory_mode = self._homekit_mode == HOMEKIT_MODE_ACCESSORY
     hk_mode_name = "Accessory" if is_accessory_mode else "Bridge"
     dev_reg.async_get_or_create(
         config_entry_id=self._entry_id,
         identifiers={identifier},
         connections={connection},
         manufacturer=MANUFACTURER,
         name=accessory_friendly_name(self._entry_title,
                                      self.driver.accessory),
         model=f"HomeKit {hk_mode_name}",
         entry_type="service",
     )
コード例 #5
0
ファイル: __init__.py プロジェクト: OpenPeerPower/core
async def async_migrate_entry(opp, config_entry):
    """Migrate old entry."""
    _LOGGER.debug("Migrating from version %s", config_entry.version)

    #  Flatten configuration but keep old data if user rollbacks OPP prior to 0.106
    if config_entry.version == 1:
        unique_id = config_entry.data[CONF_MAC]
        data = {**config_entry.data, **config_entry.data[CONF_DEVICE]}
        opp.config_entries.async_update_entry(
            config_entry, unique_id=unique_id, data=data
        )
        config_entry.version = 2

    # Normalise MAC address of device which also affects entity unique IDs
    if config_entry.version == 2:
        old_unique_id = config_entry.unique_id
        new_unique_id = format_mac(old_unique_id)

        @callback
        def update_unique_id(entity_entry):
            """Update unique ID of entity entry."""
            return {
                "new_unique_id": entity_entry.unique_id.replace(
                    old_unique_id, new_unique_id
                )
            }

        if old_unique_id != new_unique_id:
            await async_migrate_entries(opp, config_entry.entry_id, update_unique_id)

            opp.config_entries.async_update_entry(config_entry, unique_id=new_unique_id)

    _LOGGER.info("Migration to version %s successful", config_entry.version)

    return True
コード例 #6
0
    async def async_step_user(self, user_input=None):
        """Handle a flow initiated by the user."""
        errors = {}
        if user_input is not None:
            host = user_input[CONF_HOST]
            name = user_input[CONF_NAME]

            self._async_abort_entries_match({CONF_HOST: host})

            mac_address, error = await self._async_try_connect(host)
            if error is None:
                await self.async_set_unique_id(format_mac(mac_address))
                self._abort_if_unique_id_configured(updates={CONF_HOST: host})
                return self.async_create_entry(
                    title=name,
                    data={CONF_HOST: host, CONF_NAME: name},
                )
            errors["base"] = error

        user_input = user_input or {}
        return self.async_show_form(
            step_id="user",
            data_schema=vol.Schema(
                {
                    vol.Required(
                        CONF_HOST, default=user_input.get(CONF_HOST) or ""
                    ): str,
                    vol.Optional(
                        CONF_NAME, default=user_input.get(CONF_NAME) or DEFAULT_NAME
                    ): str,
                }
            ),
            errors=errors,
        )
コード例 #7
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
    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()
コード例 #8
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
    async def async_step_user(self, user_input=None):
        """Handle a Axis config flow start.

        Manage device specific parameters.
        """
        errors = {}

        if user_input is not None:
            try:
                device = await get_device(
                    self.opp,
                    host=user_input[CONF_HOST],
                    port=user_input[CONF_PORT],
                    username=user_input[CONF_USERNAME],
                    password=user_input[CONF_PASSWORD],
                )

                self.serial = device.vapix.serial_number
                await self.async_set_unique_id(format_mac(self.serial))

                self._abort_if_unique_id_configured(
                    updates={
                        CONF_HOST: user_input[CONF_HOST],
                        CONF_PORT: user_input[CONF_PORT],
                        CONF_USERNAME: user_input[CONF_USERNAME],
                        CONF_PASSWORD: user_input[CONF_PASSWORD],
                    }
                )

                self.device_config = {
                    CONF_HOST: user_input[CONF_HOST],
                    CONF_PORT: user_input[CONF_PORT],
                    CONF_USERNAME: user_input[CONF_USERNAME],
                    CONF_PASSWORD: user_input[CONF_PASSWORD],
                    CONF_MODEL: device.vapix.product_number,
                }

                return await self._create_entry()

            except AuthenticationRequired:
                errors["base"] = "invalid_auth"

            except CannotConnect:
                errors["base"] = "cannot_connect"

        data = self.discovery_schema or {
            vol.Required(CONF_HOST): str,
            vol.Required(CONF_USERNAME): str,
            vol.Required(CONF_PASSWORD): str,
            vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
        }

        return self.async_show_form(
            step_id="user",
            description_placeholders=self.device_config,
            data_schema=vol.Schema(data),
            errors=errors,
        )
コード例 #9
0
 async def async_step_zeroconf(self, discovery_info: DiscoveryInfoType):
     """Handle a flow initialized by zeroconf discovery."""
     LOGGER.debug("Samsung device found via ZEROCONF: %s", discovery_info)
     self._mac = format_mac(discovery_info[ATTR_PROPERTIES]["deviceid"])
     self._host = discovery_info[CONF_HOST]
     await self._async_start_discovery()
     await self._async_set_device_unique_id()
     self.context["title_placeholders"] = {"device": self._title}
     return await self.async_step_confirm()
コード例 #10
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
 async def async_step_dhcp(self, discovery_info: dict):
     """Prepare configuration for a DHCP discovered Axis device."""
     return await self._process_discovered_device(
         {
             CONF_HOST: discovery_info[IP_ADDRESS],
             CONF_MAC: format_mac(discovery_info.get(MAC_ADDRESS, "")),
             CONF_NAME: discovery_info.get(HOSTNAME),
             CONF_PORT: DEFAULT_PORT,
         }
     )
コード例 #11
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
 async def async_step_zeroconf(self, discovery_info: dict):
     """Prepare configuration for a Zeroconf discovered Axis device."""
     return await self._process_discovered_device(
         {
             CONF_HOST: discovery_info[CONF_HOST],
             CONF_MAC: format_mac(discovery_info["properties"]["macaddress"]),
             CONF_NAME: discovery_info["name"].split(".", 1)[0],
             CONF_PORT: discovery_info[CONF_PORT],
         }
     )
コード例 #12
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
 async def async_step_ssdp(self, discovery_info: dict):
     """Prepare configuration for a SSDP discovered Axis device."""
     url = urlsplit(discovery_info["presentationURL"])
     return await self._process_discovered_device(
         {
             CONF_HOST: url.hostname,
             CONF_MAC: format_mac(discovery_info["serialNumber"]),
             CONF_NAME: f"{discovery_info['friendlyName']}",
             CONF_PORT: url.port,
         }
     )
コード例 #13
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up Bosch SHC from a config entry."""
    data = entry.data

    zeroconf = await async_get_instance(opp)
    try:
        session = await opp.async_add_executor_job(
            SHCSession,
            data[CONF_HOST],
            data[CONF_SSL_CERTIFICATE],
            data[CONF_SSL_KEY],
            False,
            zeroconf,
        )
    except SHCAuthenticationError as err:
        raise ConfigEntryAuthFailed from err
    except SHCConnectionError as err:
        raise ConfigEntryNotReady from err

    shc_info = session.information
    if shc_info.updateState.name == "UPDATE_AVAILABLE":
        _LOGGER.warning(
            "Please check for software updates in the Bosch Smart Home App")

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = {
        DATA_SESSION: session,
    }

    device_registry = dr.async_get(opp)
    device_registry.async_get_or_create(
        config_entry_id=entry.entry_id,
        connections={(dr.CONNECTION_NETWORK_MAC,
                      dr.format_mac(shc_info.unique_id))},
        identifiers={(DOMAIN, shc_info.unique_id)},
        manufacturer="Bosch",
        name=entry.title,
        model="SmartHomeController",
        sw_version=shc_info.version,
    )

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    async def stop_polling(event):
        """Stop polling service."""
        await opp.async_add_executor_job(session.stop_polling)

    await opp.async_add_executor_job(session.start_polling)
    opp.data[DOMAIN][
        entry.entry_id][DATA_POLLING_HANDLER] = opp.bus.async_listen_once(
            EVENT_OPENPEERPOWER_STOP, stop_polling)

    return True
コード例 #14
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
    async def async_step_zeroconf(self, discovery_info):
        """Handle zeroconf discovery."""
        name = discovery_info.get("name")
        self.host = discovery_info.get("host")
        self.mac = discovery_info.get("properties", {}).get("mac")
        if self.mac is None:
            poch = discovery_info.get("properties", {}).get("poch", "")
            result = search(r"mac=\w+", poch)
            if result is not None:
                self.mac = result.group(0).split("=")[1]

        if not name or not self.host or not self.mac:
            return self.async_abort(reason="not_xiaomi_miio")

        self.mac = format_mac(self.mac)

        # Check which device is discovered.
        for gateway_model in MODELS_GATEWAY:
            if name.startswith(gateway_model.replace(".", "-")):
                unique_id = self.mac
                await self.async_set_unique_id(unique_id)
                self._abort_if_unique_id_configured({CONF_HOST: self.host})

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

                return await self.async_step_device()

        for device_model in MODELS_ALL_DEVICES:
            if name.startswith(device_model.replace(".", "-")):
                unique_id = self.mac
                await self.async_set_unique_id(unique_id)
                self._abort_if_unique_id_configured({CONF_HOST: self.host})

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

                return await self.async_step_device()

        # Discovered device is not yet supported
        _LOGGER.debug(
            "Not yet supported Xiaomi Miio device '%s' discovered with host %s",
            name,
            self.host,
        )
        return self.async_abort(reason="not_xiaomi_miio")
コード例 #15
0
 async def _async_get_and_check_device_info(self):
     """Try to get the device info."""
     info = await async_get_device_info(self.opp, self._bridge, self._host)
     if not info:
         raise data_entry_flow.AbortFlow(RESULT_NOT_SUPPORTED)
     dev_info = info.get("device", {})
     device_type = dev_info.get("type")
     if device_type != "Samsung SmartTV":
         raise data_entry_flow.AbortFlow(RESULT_NOT_SUPPORTED)
     self._model = dev_info.get("modelName")
     name = dev_info.get("name")
     self._name = name.replace("[TV] ", "") if name else device_type
     self._title = f"{self._name} ({self._model})"
     self._udn = _strip_uuid(dev_info.get("udn", info["id"]))
     if dev_info.get("networkType") == "wireless" and dev_info.get(
             "wifiMac"):
         self._mac = format_mac(dev_info.get("wifiMac"))
     self._device_info = info
コード例 #16
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
    async def async_step_user(self, user_input=None):
        """Handle the initial step."""
        errors = {}
        if user_input is not None:

            hub = Control4Validator(
                user_input["host"],
                user_input["username"],
                user_input["password"],
                self.opp,
            )
            try:
                if not await hub.authenticate():
                    raise InvalidAuth
                if not await hub.connect_to_director():
                    raise CannotConnect
            except InvalidAuth:
                errors["base"] = "invalid_auth"
            except CannotConnect:
                errors["base"] = "cannot_connect"
            except Exception:  # pylint: disable=broad-except
                _LOGGER.exception("Unexpected exception")
                errors["base"] = "unknown"

            if not errors:
                controller_unique_id = hub.controller_unique_id
                mac = (controller_unique_id.split("_", 3))[2]
                formatted_mac = format_mac(mac)
                await self.async_set_unique_id(formatted_mac)
                self._abort_if_unique_id_configured()
                return self.async_create_entry(
                    title=controller_unique_id,
                    data={
                        CONF_HOST: user_input["host"],
                        CONF_USERNAME: user_input["username"],
                        CONF_PASSWORD: user_input["password"],
                        CONF_CONTROLLER_UNIQUE_ID: controller_unique_id,
                    },
                )

        return self.async_show_form(
            step_id="user", data_schema=DATA_SCHEMA, errors=errors
        )
コード例 #17
0
async def test_dhcp_device_not_supported(opp):
    """Test DHCP discovery flow that fails because the device is not supported."""
    await setup.async_setup_component(opp, "persistent_notification", {})
    device = get_device("Kitchen")
    mock_api = device.get_mock_api()

    with patch(DEVICE_HELLO, return_value=mock_api):
        result = await opp.config_entries.flow.async_init(
            DOMAIN,
            context={"source": config_entries.SOURCE_DHCP},
            data={
                HOSTNAME: "broadlink",
                IP_ADDRESS: device.host,
                MAC_ADDRESS: device_registry.format_mac(device.mac),
            },
        )

    assert result["type"] == "abort"
    assert result["reason"] == "not_supported"
コード例 #18
0
 def __init__(
     self,
     opp,
     name,
     host,
     mac_address,
     off_action,
     broadcast_address,
     broadcast_port,
 ):
     """Initialize the WOL switch."""
     self._opp = opp
     self._name = name
     self._host = host
     self._mac_address = mac_address
     self._broadcast_address = broadcast_address
     self._broadcast_port = broadcast_port
     domain = __name__.split(".")[-2]
     self._off_script = Script(opp, off_action, name,
                               domain) if off_action else None
     self._state = False
     self._assumed_state = host is None
     self._unique_id = dr.format_mac(mac_address)
コード例 #19
0
async def test_dhcp_can_finish(opp):
    """Test DHCP discovery flow can finish right away."""
    await setup.async_setup_component(opp, "persistent_notification", {})

    device = get_device("Living Room")
    device.host = "1.2.3.4"
    mock_api = device.get_mock_api()

    with patch(DEVICE_HELLO, return_value=mock_api):
        result = await opp.config_entries.flow.async_init(
            DOMAIN,
            context={"source": config_entries.SOURCE_DHCP},
            data={
                HOSTNAME: "broadlink",
                IP_ADDRESS: "1.2.3.4",
                MAC_ADDRESS: device_registry.format_mac(device.mac),
            },
        )
        await opp.async_block_till_done()

    assert result["type"] == "form"
    assert result["step_id"] == "finish"

    result2 = await opp.config_entries.flow.async_configure(
        result["flow_id"],
        {},
    )
    await opp.async_block_till_done()

    assert result2["type"] == "create_entry"
    assert result2["title"] == "Living Room"
    assert result2["data"] == {
        "host": "1.2.3.4",
        "mac": "34ea34b43b5a",
        "timeout": 10,
        "type": 24374,
    }
コード例 #20
0
async def test_discovery_does_not_ignore_non_homekit(opp, 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_opp(opp)
    formatted_mac = device_registry.format_mac("AA:BB:CC:DD:EE:FF")

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

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

    # Device is discovered
    result = await opp.config_entries.flow.async_init(
        "homekit_controller",
        context={"source": config_entries.SOURCE_ZEROCONF},
        data=discovery_info,
    )
    assert result["type"] == "form"
コード例 #21
0
ファイル: __init__.py プロジェクト: OpenPeerPower/core
def _format_mac(mac_address):
    """Format a mac address for matching."""
    return format_mac(mac_address).replace(":", "")
コード例 #22
0
async def test_setup_entry(opp):
    """Test successful setup of entry."""
    await setup_axis_integration(opp)
    assert len(opp.data[AXIS_DOMAIN]) == 1
    assert format_mac(MAC) in opp.data[AXIS_DOMAIN]
コード例 #23
0
async def test_homekit_start(opp, hk_driver, mock_zeroconf, device_reg):
    """Test HomeKit start method."""
    entry = await async_init_integration(opp)

    homekit = _mock_homekit(opp, entry, HOMEKIT_MODE_BRIDGE)

    homekit.bridge = Mock()
    homekit.bridge.accessories = []
    homekit.driver = hk_driver
    acc = Accessory(hk_driver, "any")
    homekit.driver.accessory = acc

    connection = (device_registry.CONNECTION_NETWORK_MAC, "AA:BB:CC:DD:EE:FF")
    bridge_with_wrong_mac = device_reg.async_get_or_create(
        config_entry_id=entry.entry_id,
        connections={connection},
        manufacturer="Any",
        name="Any",
        model="Open Peer Power HomeKit Bridge",
    )

    opp.states.async_set("light.demo", "on")
    opp.states.async_set("light.demo2", "on")
    state = opp.states.async_all()[0]

    with patch(
            f"{PATH_HOMEKIT}.HomeKit.add_bridge_accessory"
    ) as mock_add_acc, patch(
            f"{PATH_HOMEKIT}.show_setup_message") as mock_setup_msg, patch(
                "pyhap.accessory_driver.AccessoryDriver.async_start"
            ) as hk_driver_start:
        await homekit.async_start()

    await opp.async_block_till_done()
    mock_add_acc.assert_any_call(state)
    mock_setup_msg.assert_called_with(opp, entry.entry_id,
                                      "Mock Title (Open Peer Power Bridge)",
                                      ANY, ANY)
    assert hk_driver_start.called
    assert homekit.status == STATUS_RUNNING

    # Test start() if already started
    hk_driver_start.reset_mock()
    await homekit.async_start()
    await opp.async_block_till_done()
    assert not hk_driver_start.called

    assert device_reg.async_get(bridge_with_wrong_mac.id) is None

    device = device_reg.async_get_device({(DOMAIN, entry.entry_id,
                                           BRIDGE_SERIAL_NUMBER)})
    assert device
    formatted_mac = device_registry.format_mac(homekit.driver.state.mac)
    assert (device_registry.CONNECTION_NETWORK_MAC,
            formatted_mac) in device.connections

    # Start again to make sure the registry entry is kept
    homekit.status = STATUS_READY
    with patch(
            f"{PATH_HOMEKIT}.HomeKit.add_bridge_accessory"
    ) as mock_add_acc, patch(
            f"{PATH_HOMEKIT}.show_setup_message") as mock_setup_msg, patch(
                "pyhap.accessory_driver.AccessoryDriver.async_start"
            ) as hk_driver_start:
        await homekit.async_start()

    device = device_reg.async_get_device({(DOMAIN, entry.entry_id,
                                           BRIDGE_SERIAL_NUMBER)})
    assert device
    formatted_mac = device_registry.format_mac(homekit.driver.state.mac)
    assert (device_registry.CONNECTION_NETWORK_MAC,
            formatted_mac) in device.connections

    assert len(device_reg.devices) == 1
    assert homekit.driver.state.config_version == 2
コード例 #24
0
ファイル: config_flow.py プロジェクト: OpenPeerPower/core
    async def async_step_device(self, user_input=None):
        """Handle a flow initialized by the user to configure a xiaomi miio device."""
        errors = {}
        if user_input is not None:
            token = user_input[CONF_TOKEN]
            model = user_input.get(CONF_MODEL)
            if user_input.get(CONF_HOST):
                self.host = user_input[CONF_HOST]

            # Try to connect to a Xiaomi Device.
            connect_device_class = ConnectXiaomiDevice(self.opp)
            await connect_device_class.async_connect_device(self.host, token)
            device_info = connect_device_class.device_info

            if model is None and device_info is not None:
                model = device_info.model

            if model is not None:
                if self.mac is None and device_info is not None:
                    self.mac = format_mac(device_info.mac_address)

                # Setup Gateways
                for gateway_model in MODELS_GATEWAY:
                    if model.startswith(gateway_model):
                        unique_id = self.mac
                        await self.async_set_unique_id(unique_id,
                                                       raise_on_progress=False)
                        self._abort_if_unique_id_configured()
                        return self.async_create_entry(
                            title=DEFAULT_GATEWAY_NAME,
                            data={
                                CONF_FLOW_TYPE: CONF_GATEWAY,
                                CONF_HOST: self.host,
                                CONF_TOKEN: token,
                                CONF_MODEL: model,
                                CONF_MAC: self.mac,
                            },
                        )

                # Setup all other Miio Devices
                name = user_input.get(CONF_NAME, model)

                for device_model in MODELS_ALL_DEVICES:
                    if model.startswith(device_model):
                        unique_id = self.mac
                        await self.async_set_unique_id(unique_id,
                                                       raise_on_progress=False)
                        self._abort_if_unique_id_configured()
                        return self.async_create_entry(
                            title=name,
                            data={
                                CONF_FLOW_TYPE: CONF_DEVICE,
                                CONF_HOST: self.host,
                                CONF_TOKEN: token,
                                CONF_MODEL: model,
                                CONF_MAC: self.mac,
                            },
                        )
                errors["base"] = "unknown_device"
            else:
                errors["base"] = "cannot_connect"

        if self.host:
            schema = vol.Schema(DEVICE_SETTINGS)
        else:
            schema = DEVICE_CONFIG

        if errors:
            schema = schema.extend(DEVICE_MODEL_CONFIG)

        return self.async_show_form(step_id="device",
                                    data_schema=schema,
                                    errors=errors)