コード例 #1
0
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
    """Set up SmartThinQ integration from a config entry."""

    if not is_valid_ha_version():
        msg = "This integration require at least HomeAssistant version " \
              f" {__min_ha_version__}, you are running version {__version__}." \
              " Please upgrade HomeAssistant to continue use this integration."
        _notify_error(hass, "inv_ha_version", "SmartThinQ Sensors", msg)
        _LOGGER.warning(msg)
        return False

    refresh_token = config_entry.data.get(CONF_TOKEN)
    region = config_entry.data.get(CONF_REGION)
    language = config_entry.data.get(CONF_LANGUAGE)
    use_api_v2 = config_entry.data.get(CONF_USE_API_V2, False)
    oauth_url = config_entry.data.get(CONF_OAUTH_URL)
    # oauth_user_num = config_entry.data.get(CONF_OAUTH_USER_NUM)
    use_tls_v1 = config_entry.data.get(CONF_USE_TLS_V1, False)
    exclude_dh = config_entry.data.get(CONF_EXCLUDE_DH, False)

    _LOGGER.info(STARTUP)
    _LOGGER.info(
        "Initializing ThinQ platform with region: %s - language: %s",
        region,
        language,
    )

    # if network is not connected we can have some error
    # raising ConfigEntryNotReady platform setup will be retried
    lge_auth = LGEAuthentication(region, language, use_api_v2)
    lge_auth.init_http_adapter(use_tls_v1, exclude_dh)
    try:
        client = await hass.async_add_executor_job(
            lge_auth.create_client_from_token, refresh_token, oauth_url)
    except InvalidCredentialError:
        msg = "Invalid ThinQ credential error, integration setup aborted." \
              " Please use the LG App on your mobile device to ensure your" \
              " credentials are correct, then restart HomeAssistant." \
              " If your credential changed, you must reconfigure integration"
        _notify_error(hass, "inv_credential", "SmartThinQ Sensors", msg)
        _LOGGER.error(msg)
        return False

    except Exception as exc:
        _LOGGER.warning("Connection not available. ThinQ platform not ready",
                        exc_info=True)
        raise ConfigEntryNotReady("ThinQ platform not ready") from exc

    if not client.hasdevices:
        _LOGGER.error("No ThinQ devices found. Component setup aborted")
        return False

    _LOGGER.info("ThinQ client connected")

    try:
        lge_devices = await lge_devices_setup(hass, client)
    except Exception as exc:
        _LOGGER.warning("Connection not available. ThinQ platform not ready",
                        exc_info=True)
        raise ConfigEntryNotReady("ThinQ platform not ready") from exc

    if not use_api_v2:
        _LOGGER.warning(
            "Integration configuration is using ThinQ APIv1 that is obsolete"
            " and not able to manage all ThinQ devices."
            " Please remove and re-add integration from HA user interface to"
            " enable the use of ThinQ APIv2")

    # remove device not available anymore
    await cleanup_orphan_lge_devices(hass, config_entry.entry_id, client)

    hass.data[DOMAIN] = {CLIENT: client, LGE_DEVICES: lge_devices}
    hass.config_entries.async_setup_platforms(config_entry,
                                              SMARTTHINQ_PLATFORMS)

    return True
コード例 #2
0
async def async_ensure_addon_running(hass: HomeAssistant, entry: ConfigEntry) -> None:
    """Ensure that Z-Wave JS add-on is installed and running."""
    addon_manager: AddonManager = get_addon_manager(hass)
    if addon_manager.task_in_progress():
        raise ConfigEntryNotReady
    try:
        addon_info = await addon_manager.async_get_addon_info()
    except AddonError as err:
        raise ConfigEntryNotReady(err) from err

    usb_path: str = entry.data[CONF_USB_PATH]
    # s0_legacy_key was saved as network_key before s2 was added.
    s0_legacy_key: str = entry.data.get(CONF_S0_LEGACY_KEY, "")
    if not s0_legacy_key:
        s0_legacy_key = entry.data.get(CONF_NETWORK_KEY, "")
    s2_access_control_key: str = entry.data.get(CONF_S2_ACCESS_CONTROL_KEY, "")
    s2_authenticated_key: str = entry.data.get(CONF_S2_AUTHENTICATED_KEY, "")
    s2_unauthenticated_key: str = entry.data.get(CONF_S2_UNAUTHENTICATED_KEY, "")
    addon_state = addon_info.state

    if addon_state == AddonState.NOT_INSTALLED:
        addon_manager.async_schedule_install_setup_addon(
            usb_path,
            s0_legacy_key,
            s2_access_control_key,
            s2_authenticated_key,
            s2_unauthenticated_key,
            catch_error=True,
        )
        raise ConfigEntryNotReady

    if addon_state == AddonState.NOT_RUNNING:
        addon_manager.async_schedule_setup_addon(
            usb_path,
            s0_legacy_key,
            s2_access_control_key,
            s2_authenticated_key,
            s2_unauthenticated_key,
            catch_error=True,
        )
        raise ConfigEntryNotReady

    addon_options = addon_info.options
    addon_device = addon_options[CONF_ADDON_DEVICE]
    # s0_legacy_key was saved as network_key before s2 was added.
    addon_s0_legacy_key = addon_options.get(CONF_ADDON_S0_LEGACY_KEY, "")
    if not addon_s0_legacy_key:
        addon_s0_legacy_key = addon_options.get(CONF_ADDON_NETWORK_KEY, "")
    addon_s2_access_control_key = addon_options.get(
        CONF_ADDON_S2_ACCESS_CONTROL_KEY, ""
    )
    addon_s2_authenticated_key = addon_options.get(CONF_ADDON_S2_AUTHENTICATED_KEY, "")
    addon_s2_unauthenticated_key = addon_options.get(
        CONF_ADDON_S2_UNAUTHENTICATED_KEY, ""
    )
    updates = {}
    if usb_path != addon_device:
        updates[CONF_USB_PATH] = addon_device
    if s0_legacy_key != addon_s0_legacy_key:
        updates[CONF_S0_LEGACY_KEY] = addon_s0_legacy_key
    if s2_access_control_key != addon_s2_access_control_key:
        updates[CONF_S2_ACCESS_CONTROL_KEY] = addon_s2_access_control_key
    if s2_authenticated_key != addon_s2_authenticated_key:
        updates[CONF_S2_AUTHENTICATED_KEY] = addon_s2_authenticated_key
    if s2_unauthenticated_key != addon_s2_unauthenticated_key:
        updates[CONF_S2_UNAUTHENTICATED_KEY] = addon_s2_unauthenticated_key
    if updates:
        hass.config_entries.async_update_entry(entry, data={**entry.data, **updates})
コード例 #3
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Synology DSM sensors."""

    # Migrate device indentifiers
    dev_reg = await get_dev_reg(hass)
    devices: list[
        DeviceEntry] = device_registry.async_entries_for_config_entry(
            dev_reg, entry.entry_id)
    for device in devices:
        old_identifier = list(next(iter(device.identifiers)))
        if len(old_identifier) > 2:
            new_identifier = {(old_identifier.pop(0),
                               "_".join([str(x) for x in old_identifier]))}
            _LOGGER.debug("migrate identifier '%s' to '%s'",
                          device.identifiers, new_identifier)
            dev_reg.async_update_device(device.id,
                                        new_identifiers=new_identifier)

    # Migrate existing entry configuration
    if entry.data.get(CONF_VERIFY_SSL) is None:
        hass.config_entries.async_update_entry(
            entry, data={
                **entry.data, CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL
            })

    # Continue setup
    api = SynoApi(hass, entry)
    try:
        await api.async_setup()
    except (
            SynologyDSMLogin2SARequiredException,
            SynologyDSMLoginDisabledAccountException,
            SynologyDSMLoginInvalidException,
            SynologyDSMLoginPermissionDeniedException,
    ) as err:
        if err.args[0] and isinstance(err.args[0], dict):
            details = err.args[0].get(EXCEPTION_DETAILS, EXCEPTION_UNKNOWN)
        else:
            details = EXCEPTION_UNKNOWN
        raise ConfigEntryAuthFailed(f"reason: {details}") from err
    except (SynologyDSMLoginFailedException,
            SynologyDSMRequestException) as err:
        if err.args[0] and isinstance(err.args[0], dict):
            details = err.args[0].get(EXCEPTION_DETAILS, EXCEPTION_UNKNOWN)
        else:
            details = EXCEPTION_UNKNOWN
        raise ConfigEntryNotReady(details) from err

    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.unique_id] = {
        UNDO_UPDATE_LISTENER:
        entry.add_update_listener(_async_update_listener),
        SYNO_API: api,
        SYSTEM_LOADED: True,
    }

    # Services
    await async_setup_services(hass)

    # For SSDP compat
    if not entry.data.get(CONF_MAC):
        network = await hass.async_add_executor_job(getattr, api.dsm,
                                                    "network")
        hass.config_entries.async_update_entry(entry,
                                               data={
                                                   **entry.data, CONF_MAC:
                                                   network.macs
                                               })

    async def async_coordinator_update_data_cameras(
    ) -> dict[str, dict[str, SynoCamera]] | None:
        """Fetch all camera data from api."""
        if not hass.data[DOMAIN][entry.unique_id][SYSTEM_LOADED]:
            raise UpdateFailed("System not fully loaded")

        if SynoSurveillanceStation.CAMERA_API_KEY not in api.dsm.apis:
            return None

        surveillance_station = api.surveillance_station

        try:
            async with async_timeout.timeout(30):
                await hass.async_add_executor_job(surveillance_station.update)
        except SynologyDSMAPIErrorException as err:
            raise UpdateFailed(f"Error communicating with API: {err}") from err

        return {
            "cameras": {
                camera.id: camera
                for camera in surveillance_station.get_all_cameras()
            }
        }

    async def async_coordinator_update_data_central() -> None:
        """Fetch all device and sensor data from api."""
        try:
            await api.async_update()
        except Exception as err:
            raise UpdateFailed(f"Error communicating with API: {err}") from err
        return None

    async def async_coordinator_update_data_switches(
    ) -> dict[str, dict[str, Any]] | None:
        """Fetch all switch data from api."""
        if not hass.data[DOMAIN][entry.unique_id][SYSTEM_LOADED]:
            raise UpdateFailed("System not fully loaded")
        if SynoSurveillanceStation.HOME_MODE_API_KEY not in api.dsm.apis:
            return None

        surveillance_station = api.surveillance_station

        return {
            "switches": {
                "home_mode":
                await hass.async_add_executor_job(
                    surveillance_station.get_home_mode_status)
            }
        }

    hass.data[DOMAIN][
        entry.unique_id][COORDINATOR_CAMERAS] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=f"{entry.unique_id}_cameras",
            update_method=async_coordinator_update_data_cameras,
            update_interval=timedelta(seconds=30),
        )

    hass.data[DOMAIN][
        entry.unique_id][COORDINATOR_CENTRAL] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=f"{entry.unique_id}_central",
            update_method=async_coordinator_update_data_central,
            update_interval=timedelta(minutes=entry.options.get(
                CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)),
        )

    hass.data[DOMAIN][
        entry.unique_id][COORDINATOR_SWITCHES] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=f"{entry.unique_id}_switches",
            update_method=async_coordinator_update_data_switches,
            update_interval=timedelta(seconds=30),
        )

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
コード例 #4
0
ファイル: __init__.py プロジェクト: rikroe/core
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up UPnP/IGD device from a config entry."""
    LOGGER.debug("Setting up config entry: %s", entry.entry_id)

    hass.data.setdefault(DOMAIN, {})

    udn = entry.data[CONFIG_ENTRY_UDN]
    st = entry.data[CONFIG_ENTRY_ST]  # pylint: disable=invalid-name
    usn = f"{udn}::{st}"

    # Register device discovered-callback.
    device_discovered_event = asyncio.Event()
    discovery_info: ssdp.SsdpServiceInfo | None = None

    async def device_discovered(headers: ssdp.SsdpServiceInfo,
                                change: ssdp.SsdpChange) -> None:
        if change == ssdp.SsdpChange.BYEBYE:
            return

        nonlocal discovery_info
        LOGGER.debug("Device discovered: %s, at: %s", usn,
                     headers.ssdp_location)
        discovery_info = headers
        device_discovered_event.set()

    cancel_discovered_callback = await ssdp.async_register_callback(
        hass,
        device_discovered,
        {
            "usn": usn,
        },
    )

    try:
        await asyncio.wait_for(device_discovered_event.wait(), timeout=10)
    except asyncio.TimeoutError as err:
        raise ConfigEntryNotReady(f"Device not discovered: {usn}") from err
    finally:
        cancel_discovered_callback()

    # Create device.
    assert discovery_info is not None
    assert discovery_info.ssdp_location is not None
    location = discovery_info.ssdp_location
    try:
        device = await async_create_device(hass, location)
    except UpnpConnectionError as err:
        raise ConfigEntryNotReady(
            f"Error connecting to device at location: {location}, err: {err}"
        ) from err

    # Track the original UDN such that existing sensors do not change their unique_id.
    if CONFIG_ENTRY_ORIGINAL_UDN not in entry.data:
        hass.config_entries.async_update_entry(
            entry=entry,
            data={
                **entry.data,
                CONFIG_ENTRY_ORIGINAL_UDN: device.udn,
            },
        )
    device.original_udn = entry.data[CONFIG_ENTRY_ORIGINAL_UDN]

    # Store mac address for changed UDN matching.
    if device.host:
        device.mac_address = await async_get_mac_address_from_host(
            hass, device.host)
    if device.mac_address and not entry.data.get("CONFIG_ENTRY_MAC_ADDRESS"):
        hass.config_entries.async_update_entry(
            entry=entry,
            data={
                **entry.data,
                CONFIG_ENTRY_MAC_ADDRESS: device.mac_address,
            },
        )

    connections = {(dr.CONNECTION_UPNP, device.udn)}
    if device.mac_address:
        connections.add((dr.CONNECTION_NETWORK_MAC, device.mac_address))

    device_registry = dr.async_get(hass)
    device_entry = device_registry.async_get_device(identifiers=set(),
                                                    connections=connections)
    if device_entry:
        LOGGER.debug(
            "Found device using connections: %s, device_entry: %s",
            connections,
            device_entry,
        )
    if not device_entry:
        # No device found, create new device entry.
        device_entry = device_registry.async_get_or_create(
            config_entry_id=entry.entry_id,
            connections=connections,
            identifiers={(DOMAIN, device.usn)},
            name=device.name,
            manufacturer=device.manufacturer,
            model=device.model_name,
        )
        LOGGER.debug("Created device using UDN '%s', device_entry: %s",
                     device.udn, device_entry)
    else:
        # Update identifier.
        device_entry = device_registry.async_update_device(
            device_entry.id,
            new_identifiers={(DOMAIN, device.usn)},
        )

    assert device_entry
    update_interval = timedelta(seconds=DEFAULT_SCAN_INTERVAL)
    coordinator = UpnpDataUpdateCoordinator(
        hass,
        device=device,
        device_entry=device_entry,
        update_interval=update_interval,
    )

    # Try an initial refresh.
    await coordinator.async_config_entry_first_refresh()

    # Save coordinator.
    hass.data[DOMAIN][entry.entry_id] = coordinator

    # Setup platforms, creating sensors/binary_sensors.
    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
コード例 #5
0
ファイル: __init__.py プロジェクト: mback2k/home-assistant
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up System Bridge from a config entry."""
    bridge = Bridge(
        BridgeClient(aiohttp_client.async_get_clientsession(hass)),
        f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}",
        entry.data[CONF_API_KEY],
    )

    try:
        async with async_timeout.timeout(30):
            await bridge.async_get_information()
    except BridgeAuthenticationException as exception:
        raise ConfigEntryAuthFailed(
            f"Authentication failed for {entry.title} ({entry.data[CONF_HOST]})"
        ) from exception
    except BRIDGE_CONNECTION_ERRORS as exception:
        raise ConfigEntryNotReady(
            f"Could not connect to {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

    coordinator = SystemBridgeDataUpdateCoordinator(hass,
                                                    bridge,
                                                    _LOGGER,
                                                    entry=entry)
    await coordinator.async_config_entry_first_refresh()

    # Wait for initial data
    try:
        async with async_timeout.timeout(60):
            while (coordinator.bridge.battery is None
                   or coordinator.bridge.cpu is None
                   or coordinator.bridge.display is None
                   or coordinator.bridge.filesystem is None
                   or coordinator.bridge.graphics is None
                   or coordinator.bridge.information is None
                   or coordinator.bridge.memory is None
                   or coordinator.bridge.network is None
                   or coordinator.bridge.os is None
                   or coordinator.bridge.processes is None
                   or coordinator.bridge.system is None):
                _LOGGER.debug(
                    "Waiting for initial data from %s (%s)",
                    entry.title,
                    entry.data[CONF_HOST],
                )
                await asyncio.sleep(1)
    except asyncio.TimeoutError as exception:
        raise ConfigEntryNotReady(
            f"Timed out waiting for {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.entry_id] = coordinator

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    if hass.services.has_service(DOMAIN, SERVICE_SEND_COMMAND):
        return True

    async def handle_send_command(call):
        """Handle the send_command service call."""
        device_registry = dr.async_get(hass)
        device_id = call.data[CONF_BRIDGE]
        device_entry = device_registry.async_get(device_id)
        if device_entry is None:
            _LOGGER.warning("Missing device: %s", device_id)
            return

        command = call.data[CONF_COMMAND]
        arguments = shlex.split(call.data.get(CONF_ARGUMENTS, ""))

        entry_id = next(entry.entry_id
                        for entry in hass.config_entries.async_entries(DOMAIN)
                        if entry.entry_id in device_entry.config_entries)
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            entry_id]
        bridge: Bridge = coordinator.bridge

        _LOGGER.debug(
            "Command payload: %s",
            {
                CONF_COMMAND: command,
                CONF_ARGUMENTS: arguments,
                CONF_WAIT: False
            },
        )
        try:
            response: CommandResponse = await bridge.async_send_command({
                CONF_COMMAND:
                command,
                CONF_ARGUMENTS:
                arguments,
                CONF_WAIT:
                False
            })
            if response.success:
                _LOGGER.debug("Sent command. Response message was: %s",
                              response.message)
            else:
                _LOGGER.warning(
                    "Error sending command. Response message was: %s",
                    response.message)
        except (BridgeAuthenticationException,
                *BRIDGE_CONNECTION_ERRORS) as exception:
            _LOGGER.warning("Error sending command. Error was: %s", exception)

    async def handle_open(call):
        """Handle the open service call."""
        device_registry = dr.async_get(hass)
        device_id = call.data[CONF_BRIDGE]
        device_entry = device_registry.async_get(device_id)
        if device_entry is None:
            _LOGGER.warning("Missing device: %s", device_id)
            return

        path = call.data[CONF_PATH]

        entry_id = next(entry.entry_id
                        for entry in hass.config_entries.async_entries(DOMAIN)
                        if entry.entry_id in device_entry.config_entries)
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            entry_id]
        bridge: Bridge = coordinator.bridge

        _LOGGER.debug("Open payload: %s", {CONF_PATH: path})
        try:
            await bridge.async_open({CONF_PATH: path})
            _LOGGER.debug("Sent open request")
        except (BridgeAuthenticationException,
                *BRIDGE_CONNECTION_ERRORS) as exception:
            _LOGGER.warning("Error sending. Error was: %s", exception)

    hass.services.async_register(
        DOMAIN,
        SERVICE_SEND_COMMAND,
        handle_send_command,
        schema=SERVICE_SEND_COMMAND_SCHEMA,
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_OPEN,
        handle_open,
        schema=SERVICE_OPEN_SCHEMA,
    )

    # Reload entry when its updated.
    entry.async_on_unload(entry.add_update_listener(async_reload_entry))

    return True
コード例 #6
0
ファイル: __init__.py プロジェクト: alandtse/tesla
async def async_setup_entry(hass, config_entry):
    """Set up Tesla as config entry."""
    # pylint: disable=too-many-locals
    hass.data.setdefault(DOMAIN, {})
    config = config_entry.data
    # Because users can have multiple accounts, we always create a new session so they have separate cookies
    async_client = httpx.AsyncClient(headers={USER_AGENT: SERVER_SOFTWARE},
                                     timeout=60)
    email = config_entry.title
    if not hass.data[DOMAIN]:
        async_setup_services(hass)
    if email in hass.data[DOMAIN] and CONF_SCAN_INTERVAL in hass.data[DOMAIN][
            email]:
        scan_interval = hass.data[DOMAIN][email][CONF_SCAN_INTERVAL]
        hass.config_entries.async_update_entry(
            config_entry, options={CONF_SCAN_INTERVAL: scan_interval})
        hass.data[DOMAIN].pop(email)
    try:
        controller = TeslaAPI(
            async_client,
            email=config.get(CONF_USERNAME),
            refresh_token=config[CONF_TOKEN],
            access_token=config[CONF_ACCESS_TOKEN],
            expiration=config.get(CONF_EXPIRATION, 0),
            auth_domain=config.get(CONF_DOMAIN, AUTH_DOMAIN),
            update_interval=config_entry.options.get(CONF_SCAN_INTERVAL,
                                                     DEFAULT_SCAN_INTERVAL),
            polling_policy=config_entry.options.get(CONF_POLLING_POLICY,
                                                    DEFAULT_POLLING_POLICY),
        )
        result = await controller.connect(
            wake_if_asleep=config_entry.options.get(CONF_WAKE_ON_START,
                                                    DEFAULT_WAKE_ON_START))
        refresh_token = result["refresh_token"]
        access_token = result["access_token"]
        expiration = result["expiration"]
    except IncompleteCredentials as ex:
        await async_client.aclose()
        raise ConfigEntryAuthFailed from ex
    except httpx.ConnectTimeout as ex:
        await async_client.aclose()
        raise ConfigEntryNotReady from ex
    except TeslaException as ex:
        await async_client.aclose()
        if ex.code == HTTPStatus.UNAUTHORIZED:
            raise ConfigEntryAuthFailed from ex
        if ex.message in [
                "VEHICLE_UNAVAILABLE",
                "TOO_MANY_REQUESTS",
                "SERVICE_MAINTENANCE",
                "UPSTREAM_TIMEOUT",
        ]:
            raise ConfigEntryNotReady(
                f"Temporarily unable to communicate with Tesla API: {ex.message}"
            ) from ex
        _LOGGER.error("Unable to communicate with Tesla API: %s", ex.message)
        return False

    async def _async_close_client(*_):
        await async_client.aclose()

    @callback
    def _async_create_close_task():
        asyncio.create_task(_async_close_client())

    config_entry.async_on_unload(
        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE,
                                   _async_close_client))
    config_entry.async_on_unload(_async_create_close_task)

    _async_save_tokens(hass, config_entry, access_token, refresh_token,
                       expiration)
    coordinator = TeslaDataUpdateCoordinator(hass,
                                             config_entry=config_entry,
                                             controller=controller)
    # Fetch initial data so we have data when entities subscribe
    entry_data = hass.data[DOMAIN][config_entry.entry_id] = {
        "coordinator": coordinator,
        "devices": defaultdict(list),
        DATA_LISTENER: [config_entry.add_update_listener(update_listener)],
    }
    _LOGGER.debug("Connected to the Tesla API")

    await coordinator.async_config_entry_first_refresh()

    all_devices = controller.get_homeassistant_components()

    if not all_devices:
        return False

    for device in all_devices:
        entry_data["devices"][device.hass_type].append(device)

    hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)

    return True
コード例 #7
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Notion as a config entry."""
    hass.data.setdefault(DOMAIN, {DATA_COORDINATOR: {}})

    if not entry.unique_id:
        hass.config_entries.async_update_entry(
            entry, unique_id=entry.data[CONF_USERNAME])

    session = aiohttp_client.async_get_clientsession(hass)

    try:
        client = await async_get_client(entry.data[CONF_USERNAME],
                                        entry.data[CONF_PASSWORD],
                                        session=session)
    except InvalidCredentialsError as err:
        raise ConfigEntryAuthFailed(
            "Invalid username and/or password") from err
    except NotionError as err:
        raise ConfigEntryNotReady("Config entry failed to load") from err

    async def async_update() -> dict[str, dict[str, Any]]:
        """Get the latest data from the Notion API."""
        data: dict[str, dict[str, Any]] = {
            "bridges": {},
            "sensors": {},
            "tasks": {}
        }
        tasks = {
            "bridges": client.bridge.async_all(),
            "sensors": client.sensor.async_all(),
            "tasks": client.task.async_all(),
        }

        results = await asyncio.gather(*tasks.values(), return_exceptions=True)
        for attr, result in zip(tasks, results):
            if isinstance(result, InvalidCredentialsError):
                raise ConfigEntryAuthFailed(
                    "Invalid username and/or password") from result
            if isinstance(result, NotionError):
                raise UpdateFailed(
                    f"There was a Notion error while updating {attr}: {result}"
                ) from result
            if isinstance(result, Exception):
                raise UpdateFailed(
                    f"There was an unknown error while updating {attr}: {result}"
                ) from result

            for item in result:
                if attr == "bridges" and item["id"] not in data["bridges"]:
                    # If a new bridge is discovered, register it:
                    hass.async_create_task(
                        async_register_new_bridge(hass, item, entry))
                data[attr][item["id"]] = item

        return data

    coordinator = hass.data[DOMAIN][DATA_COORDINATOR][
        entry.entry_id] = DataUpdateCoordinator(
            hass,
            LOGGER,
            name=entry.data[CONF_USERNAME],
            update_interval=DEFAULT_SCAN_INTERVAL,
            update_method=async_update,
        )

    await coordinator.async_config_entry_first_refresh()

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
コード例 #8
0
    device.discovery = discovery
    if white_channel_type := entry.data.get(CONF_WHITE_CHANNEL_TYPE):
        device.white_channel_channel_type = NAME_TO_WHITE_CHANNEL_TYPE[
            white_channel_type
        ]

    @callback
    def _async_state_changed(*_: Any) -> None:
        _LOGGER.debug("%s: Device state updated: %s", device.ipaddr, device.raw_state)
        async_dispatcher_send(hass, signal)

    try:
        await device.async_setup(_async_state_changed)
    except FLUX_LED_EXCEPTIONS as ex:
        raise ConfigEntryNotReady(
            str(ex) or f"Timed out trying to connect to {device.ipaddr}"
        ) from ex

    # UDP probe after successful connect only
    if discovery_cached:
        if directed_discovery := await async_discover_device(hass, host):
            device.discovery = discovery = directed_discovery
            discovery_cached = False

    if entry.unique_id and discovery.get(ATTR_ID):
        mac = dr.format_mac(cast(str, discovery[ATTR_ID]))
        if not mac_matches_by_one(mac, entry.unique_id):
            # The device is offline and another flux_led device is now using the ip address
            raise ConfigEntryNotReady(
                f"Unexpected device found at {host}; Expected {entry.unique_id}, found {mac}"
            )
コード例 #9
0
ファイル: __init__.py プロジェクト: vlebourl/ha-tahoma
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Overkiz from a config entry."""
    username = entry.data[CONF_USERNAME]
    password = entry.data[CONF_PASSWORD]
    server = SUPPORTED_SERVERS[entry.data[CONF_HUB]]

    if await _block_if_core_is_configured(hass, entry):
        raise ConfigEntryNotReady(
            "You cannot use Overkiz from core and custom component at the same time."
        )

    # To allow users with multiple accounts/hubs, we create a new session so they have separate cookies
    session = async_create_clientsession(hass)
    client = OverkizClient(username=username,
                           password=password,
                           session=session,
                           server=server)

    try:
        await client.login()

        tasks = [
            client.get_setup(),
            client.get_scenarios(),
        ]
        setup, scenarios = await asyncio.gather(*tasks)
    except BadCredentialsException as exception:
        raise ConfigEntryAuthFailed from exception
    except TooManyRequestsException as exception:
        raise ConfigEntryNotReady(
            "Too many requests, try again later") from exception
    except (TimeoutError, ClientError, ServerDisconnectedError) as exception:
        raise ConfigEntryNotReady("Failed to connect") from exception
    except MaintenanceException as exception:
        raise ConfigEntryNotReady(
            "Server is down for maintenance") from exception
    except Exception as exception:  # pylint: disable=broad-except
        _LOGGER.exception(exception)
        return False

    coordinator = OverkizDataUpdateCoordinator(
        hass,
        _LOGGER,
        name="device events",
        client=client,
        devices=setup.devices,
        places=setup.root_place,
        update_interval=UPDATE_INTERVAL,
        config_entry_id=entry.entry_id,
    )

    await coordinator.async_config_entry_first_refresh()

    if coordinator.is_stateless:
        _LOGGER.debug(
            "All devices have assumed state. Update interval has been reduced to: %s",
            UPDATE_INTERVAL_ALL_ASSUMED_STATE,
        )
        coordinator.update_interval = UPDATE_INTERVAL_ALL_ASSUMED_STATE

    platforms: defaultdict[Platform, list[Device]] = defaultdict(list)

    hass.data.setdefault(DOMAIN,
                         {})[entry.entry_id] = HomeAssistantOverkizData(
                             coordinator=coordinator,
                             platforms=platforms,
                             scenarios=scenarios)

    # Map Overkiz device to Home Assistant platform
    for device in coordinator.data.values():
        platform = OVERKIZ_DEVICE_TO_PLATFORM.get(
            device.widget) or OVERKIZ_DEVICE_TO_PLATFORM.get(device.ui_class)
        if platform:
            platforms[platform].append(device)
            log_device("Added device", device)
        elif (device.widget not in IGNORED_OVERKIZ_DEVICES
              and device.ui_class not in IGNORED_OVERKIZ_DEVICES):
            log_device("Unsupported device detected", device)

    hass.config_entries.async_setup_platforms(entry, SUPPORTED_PLATFORMS)

    device_registry = await dr.async_get_registry(hass)

    for gateway in setup.gateways:
        _LOGGER.debug("Added gateway (%s)", gateway)

        device_registry.async_get_or_create(
            config_entry_id=entry.entry_id,
            identifiers={(DOMAIN, gateway.id)},
            model=gateway.sub_type.beautify_name if gateway.sub_type else None,
            manufacturer=server.manufacturer,
            name=gateway.type.beautify_name,
            sw_version=gateway.connectivity.protocol_version,
            configuration_url=server.configuration_url,
        )

    async def handle_execute_command(call: ServiceCall):
        """Handle execute command service."""
        entity_registry = await hass.helpers.entity_registry.async_get_registry(
        )

        for entity_id in call.data.get("entity_id"):
            entity = entity_registry.entities.get(entity_id)

            try:
                await coordinator.client.execute_command(
                    entity.unique_id,
                    Command(call.data.get("command"), call.data.get("args")),
                    "Home Assistant Service",
                )
            except InvalidCommandException as exception:
                _LOGGER.error(exception)

    service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_EXECUTE_COMMAND,
        handle_execute_command,
        vol.Schema(
            {
                vol.Required("entity_id"): [cv.entity_id],
                vol.Required("command"):
                cv.string,
                vol.Optional("args", default=[]):
                vol.All(cv.ensure_list, [vol.Any(str, int)]),
            }, ),
    )

    async def handle_get_execution_history(call):
        """Handle get execution history service."""
        await write_execution_history_to_log(coordinator.client)

    service.async_register_admin_service(
        hass,
        DOMAIN,
        "get_execution_history",
        handle_get_execution_history,
    )

    return True
コード例 #10
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Sense from a config entry."""

    entry_data = entry.data
    email = entry_data[CONF_EMAIL]
    timeout = entry_data[CONF_TIMEOUT]

    access_token = entry_data.get("access_token", "")
    user_id = entry_data.get("user_id", "")
    monitor_id = entry_data.get("monitor_id", "")

    client_session = async_get_clientsession(hass)

    gateway = ASyncSenseable(api_timeout=timeout,
                             wss_timeout=timeout,
                             client_session=client_session)
    gateway.rate_limit = ACTIVE_UPDATE_RATE

    try:
        gateway.load_auth(access_token, user_id, monitor_id)
        await gateway.get_monitor_data()
    except (SenseAuthenticationException, SenseMFARequiredException) as err:
        _LOGGER.warning("Sense authentication expired")
        raise ConfigEntryAuthFailed(err) from err

    sense_devices_data = SenseDevicesData()
    try:
        sense_discovered_devices = await gateway.get_discovered_device_data()
        await gateway.update_realtime()
    except SENSE_TIMEOUT_EXCEPTIONS as err:
        raise ConfigEntryNotReady(
            str(err) or "Timed out during realtime update") from err
    except SENSE_EXCEPTIONS as err:
        raise ConfigEntryNotReady(str(err)
                                  or "Error during realtime update") from err

    async def _async_update_trend():
        """Update the trend data."""
        try:
            await gateway.update_trend_data()
        except (SenseAuthenticationException,
                SenseMFARequiredException) as err:
            _LOGGER.warning("Sense authentication expired")
            raise ConfigEntryAuthFailed(err) from err

    trends_coordinator: DataUpdateCoordinator[None] = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name=f"Sense Trends {email}",
        update_method=_async_update_trend,
        update_interval=timedelta(seconds=300),
    )
    # Start out as unavailable so we do not report 0 data
    # until the update happens
    trends_coordinator.last_update_success = False

    # This can take longer than 60s and we already know
    # sense is online since get_discovered_device_data was
    # successful so we do it later.
    asyncio.create_task(trends_coordinator.async_request_refresh())

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
        SENSE_DATA: gateway,
        SENSE_DEVICES_DATA: sense_devices_data,
        SENSE_TRENDS_COORDINATOR: trends_coordinator,
        SENSE_DISCOVERED_DEVICES_DATA: sense_discovered_devices,
    }

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    async def async_sense_update(_):
        """Retrieve latest state."""
        try:
            await gateway.update_realtime()
        except SENSE_TIMEOUT_EXCEPTIONS as ex:
            _LOGGER.error("Timeout retrieving data: %s", ex)
        except SENSE_EXCEPTIONS as ex:
            _LOGGER.error("Failed to update data: %s", ex)

        data = gateway.get_realtime()
        if "devices" in data:
            sense_devices_data.set_devices_data(data["devices"])
        async_dispatcher_send(
            hass, f"{SENSE_DEVICE_UPDATE}-{gateway.sense_monitor_id}")

    remove_update_callback = async_track_time_interval(
        hass, async_sense_update, timedelta(seconds=ACTIVE_UPDATE_RATE))

    @callback
    def _remove_update_callback_at_stop(event):
        remove_update_callback()

    entry.async_on_unload(remove_update_callback)
    entry.async_on_unload(
        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                   _remove_update_callback_at_stop))

    return True
コード例 #11
0
async def async_setup_entry(hass: HomeAssistantType,
                            entry: ConfigEntry) -> bool:
    """
    set up digitalSTROM component from config entry
    """
    _LOGGER.debug("digitalstrom setup started")

    # initialize component data
    hass.data.setdefault(DOMAIN, dict())

    # old installations don't have an app token in their config entry
    if not entry.data.get(CONF_TOKEN, None):
        raise InvalidStateError(
            "No app token in config entry, please re-setup the integration")

    # setup client and listener
    client = DSClient(
        host=entry.data[CONF_HOST],
        port=entry.data[CONF_PORT],
        apptoken=entry.data[CONF_TOKEN],
        apartment_name=entry.data[CONF_ALIAS],
        stack_delay=entry.data.get(CONF_DELAY, DEFAULT_DELAY),
        loop=hass.loop,
    )
    listener = DSWebsocketEventListener(client=client, event_name="callScene")

    # store client in hass data for future usage
    entry_slug = slugify_entry(host=entry.data[CONF_HOST],
                               port=entry.data[CONF_PORT])
    hass.data[DOMAIN].setdefault(entry_slug, dict())
    hass.data[DOMAIN][entry_slug]["client"] = client
    hass.data[DOMAIN][entry_slug]["listener"] = listener

    # load all scenes from digitalSTROM server
    # this fails often on the first connection, but works on the second
    try:
        await client.initialize()
    except (DSException, RuntimeError, ConnectionResetError):
        try:
            await client.initialize()
        except (DSException, RuntimeError, ConnectionResetError):
            raise ConfigEntryNotReady(
                f"Failed to initialize digitalSTROM server at {client.host}")

    # we're connected
    _LOGGER.debug(
        f"Successfully retrieved session token from digitalSTROM server at {client.host}"
    )

    # register devices
    for component in COMPONENT_TYPES:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component))

    # start websocket listener and action delayer loops on hass startup
    async def digitalstrom_start_loops(event):
        _LOGGER.debug(
            f"loops started for digitalSTROM server at {client.host}")
        hass.async_add_job(listener.start)
        hass.async_add_job(client.stack.start)

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START,
                               digitalstrom_start_loops)

    # start websocket listener and action delayer loops on hass shutdown
    async def digitalstrom_stop_loops(event):
        _LOGGER.debug(
            f"loops stopped for digitalSTROM server at {client.host}")
        hass.async_add_job(client.stack.stop)
        hass.async_add_job(listener.stop)

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                               digitalstrom_stop_loops)

    return True
コード例 #12
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Aqualink from a config entry."""
    username = entry.data[CONF_USERNAME]
    password = entry.data[CONF_PASSWORD]

    hass.data.setdefault(DOMAIN, {})

    # These will contain the initialized devices
    binary_sensors = hass.data[DOMAIN][BINARY_SENSOR_DOMAIN] = []
    climates = hass.data[DOMAIN][CLIMATE_DOMAIN] = []
    lights = hass.data[DOMAIN][LIGHT_DOMAIN] = []
    sensors = hass.data[DOMAIN][SENSOR_DOMAIN] = []
    switches = hass.data[DOMAIN][SWITCH_DOMAIN] = []

    session = async_get_clientsession(hass)
    aqualink = AqualinkClient(username, password, session)
    try:
        await aqualink.login()
    except AqualinkServiceException as login_exception:
        _LOGGER.error("Failed to login: %s", login_exception)
        return False
    except (
            asyncio.TimeoutError,
            aiohttp.client_exceptions.ClientConnectorError,
    ) as aio_exception:
        raise ConfigEntryNotReady(
            f"Error while attempting login: {aio_exception}"
        ) from aio_exception

    try:
        systems = await aqualink.get_systems()
    except AqualinkServiceException as svc_exception:
        raise ConfigEntryNotReady(
            f"Error while attempting to retrieve systems list: {svc_exception}"
        ) from svc_exception

    systems = list(systems.values())
    if not systems:
        _LOGGER.error("No systems detected or supported")
        return False

    # Only supporting the first system for now.
    try:
        devices = await systems[0].get_devices()
    except AqualinkServiceException as svc_exception:
        raise ConfigEntryNotReady(
            f"Error while attempting to retrieve devices list: {svc_exception}"
        ) from svc_exception

    for dev in devices.values():
        if isinstance(dev, AqualinkThermostat):
            climates += [dev]
        elif isinstance(dev, AqualinkLight):
            lights += [dev]
        elif isinstance(dev, AqualinkBinarySensor):
            binary_sensors += [dev]
        elif isinstance(dev, AqualinkSensor):
            sensors += [dev]
        elif isinstance(dev, AqualinkToggle):
            switches += [dev]

    forward_setup = hass.config_entries.async_forward_entry_setup
    if binary_sensors:
        _LOGGER.debug("Got %s binary sensors: %s", len(binary_sensors),
                      binary_sensors)
        hass.async_create_task(forward_setup(entry, Platform.BINARY_SENSOR))
    if climates:
        _LOGGER.debug("Got %s climates: %s", len(climates), climates)
        hass.async_create_task(forward_setup(entry, Platform.CLIMATE))
    if lights:
        _LOGGER.debug("Got %s lights: %s", len(lights), lights)
        hass.async_create_task(forward_setup(entry, Platform.LIGHT))
    if sensors:
        _LOGGER.debug("Got %s sensors: %s", len(sensors), sensors)
        hass.async_create_task(forward_setup(entry, Platform.SENSOR))
    if switches:
        _LOGGER.debug("Got %s switches: %s", len(switches), switches)
        hass.async_create_task(forward_setup(entry, Platform.SWITCH))

    async def _async_systems_update(now):
        """Refresh internal state for all systems."""
        prev = systems[0].online

        try:
            await systems[0].update()
        except AqualinkServiceException as svc_exception:
            if prev is not None:
                _LOGGER.warning("Failed to refresh iAqualink state: %s",
                                svc_exception)
        else:
            cur = systems[0].online
            if cur is True and prev is not True:
                _LOGGER.warning("Reconnected to iAqualink")

        async_dispatcher_send(hass, DOMAIN)

    async_track_time_interval(hass, _async_systems_update, UPDATE_INTERVAL)

    return True
コード例 #13
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Async setup hass config entry."""
    hass.data.setdefault(DOMAIN, {})

    # Project type has been renamed to auth type in the upstream Tuya IoT SDK.
    # This migrates existing config entries to reflect that name change.
    if CONF_PROJECT_TYPE in entry.data:
        data = {**entry.data, CONF_AUTH_TYPE: entry.data[CONF_PROJECT_TYPE]}
        data.pop(CONF_PROJECT_TYPE)
        hass.config_entries.async_update_entry(entry, data=data)

    auth_type = AuthType(entry.data[CONF_AUTH_TYPE])
    api = TuyaOpenAPI(
        endpoint=entry.data[CONF_ENDPOINT],
        access_id=entry.data[CONF_ACCESS_ID],
        access_secret=entry.data[CONF_ACCESS_SECRET],
        auth_type=auth_type,
    )

    api.set_dev_channel("hass")

    try:
        if auth_type == AuthType.CUSTOM:
            response = await hass.async_add_executor_job(
                api.connect, entry.data[CONF_USERNAME],
                entry.data[CONF_PASSWORD])
        else:
            response = await hass.async_add_executor_job(
                api.connect,
                entry.data[CONF_USERNAME],
                entry.data[CONF_PASSWORD],
                entry.data[CONF_COUNTRY_CODE],
                entry.data[CONF_APP_TYPE],
            )
    except requests.exceptions.RequestException as err:
        raise ConfigEntryNotReady(err) from err

    if response.get("success", False) is False:
        raise ConfigEntryNotReady(response)

    tuya_mq = TuyaOpenMQ(api)
    tuya_mq.start()

    device_ids: set[str] = set()
    device_manager = TuyaDeviceManager(api, tuya_mq)
    home_manager = TuyaHomeManager(api, tuya_mq, device_manager)
    listener = DeviceListener(hass, device_manager, device_ids)
    device_manager.add_device_listener(listener)

    hass.data[DOMAIN][entry.entry_id] = HomeAssistantTuyaData(
        device_listener=listener,
        device_manager=device_manager,
        home_manager=home_manager,
    )

    # Get devices & clean up device entities
    await hass.async_add_executor_job(home_manager.update_device_cache)
    await cleanup_device_registry(hass, device_manager)

    # Migrate old unique_ids to the new format
    async_migrate_entities_unique_ids(hass, entry, device_manager)

    # Register known device IDs
    device_registry = dr.async_get(hass)
    for device in device_manager.device_map.values():
        device_registry.async_get_or_create(
            config_entry_id=entry.entry_id,
            identifiers={(DOMAIN, device.id)},
            manufacturer="Tuya",
            name=device.name,
            model=f"{device.product_name} (unsupported)",
        )
        device_ids.add(device.id)

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)
    return True
コード例 #14
0
async def async_setup_entry(hass: HomeAssistant, entry: config_entries.ConfigEntry):
    """Set up Hive from a config entry."""
    # Store an API object for your platforms to access
    # hass.data[DOMAIN][entry.entry_id] = MyApi(...)

    hive = await hive_data(hass)
    hive_config = dict(entry.data)
    hive_options = dict(entry.options)

    async def heating_boost(service_call):
        """Handle the service call."""
        node_id = hive.entity_lookup.get(service_call.data[ATTR_ENTITY_ID])
        device = pyhiveapi.Hive_Helper.get_device_from_id(node_id)
        if not node_id:
            # log or raise error
            _LOGGER.error("Cannot boost entity id entered")
            return

        minutes = service_call.data[ATTR_TIME_PERIOD]
        temperature = service_call.data[ATTR_TEMPERATURE]

        await hive.heating.turn_boost_on(device, minutes, temperature)

    async def hot_water_boost(service_call):
        """Handle the service call."""
        node_id = hive.entity_lookup.get(service_call.data[ATTR_ENTITY_ID])
        device = pyhiveapi.Hive_Helper.get_device_from_id(node_id)
        if not node_id:
            # log or raise error
            _LOGGER.error("Cannot boost entity id entered")
            return
        minutes = service_call.data[ATTR_TIME_PERIOD]
        mode = service_call.data[ATTR_MODE]

        if mode == "on":
            await hive.hotwater.turn_boost_on(device, minutes)
        elif mode == "off":
            await hive.hotwater.turn_boost_off(device)

    Tokens = hive_config.get("tokens", None)
    Username = hive_config["options"].get(CONF_USERNAME)
    Password = hive_config.get(CONF_PASSWORD)
    Update = "Y" if datetime.now() >= datetime.strptime(entry.data.get(
        "created"), '%Y-%m-%d %H:%M:%S.%f') + timedelta(minutes=60) else "N"

    # Update config entry options
    hive_options = hive_options if len(
        hive_options) > 0 else hive_config["options"]
    hive_config["options"].update(hive_options)
    hass.config_entries.async_update_entry(entry, options=hive_options)
    hass.data[DOMAIN][entry.entry_id] = hive

    try:
        devices = await hive.session.start_session(Tokens,
                                                   Update,
                                                   hive_config)
    except HTTPException as error:
        _LOGGER.error("Could not connect to the internet: %s", error)
        raise ConfigEntryNotReady() from error

    if devices == "INVALID_REAUTH":
        return hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN,
                context={"source": config_entries.SOURCE_REAUTH},
                data={"username": Username, "password": Password}
            )
        )

    hive.devices = devices
    for component in PLATFORMS:
        devicelist = devices.get(component)
        if devicelist:
            hass.async_create_task(
                hass.config_entries.async_forward_entry_setup(
                    entry, component)
            )
            if component == "climate":
                hass.services.async_register(
                    DOMAIN,
                    SERVICE_BOOST_HEATING,
                    heating_boost,
                    schema=BOOST_HEATING_SCHEMA,
                )
            if component == "water_heater":
                hass.services.async_register(
                    DOMAIN,
                    SERVICE_BOOST_HOT_WATER,
                    hot_water_boost,
                    schema=BOOST_HOT_WATER_SCHEMA,
                )

    return True
コード例 #15
0
async def async_setup_gateway_entry(
    hass: core.HomeAssistant, entry: config_entries.ConfigEntry
):
    """Set up the Xiaomi Gateway component from a config entry."""
    host = entry.data[CONF_HOST]
    token = entry.data[CONF_TOKEN]
    name = entry.title
    gateway_id = entry.unique_id

    # For backwards compat
    if entry.unique_id.endswith("-gateway"):
        hass.config_entries.async_update_entry(entry, unique_id=entry.data["mac"])

    entry.async_on_unload(entry.add_update_listener(update_listener))

    # Connect to gateway
    gateway = ConnectXiaomiGateway(hass, entry)
    try:
        await gateway.async_connect_gateway(host, token)
    except AuthException as error:
        raise ConfigEntryAuthFailed() from error
    except SetupException as error:
        raise ConfigEntryNotReady() from error
    gateway_info = gateway.gateway_info

    gateway_model = f"{gateway_info.model}-{gateway_info.hardware_version}"

    device_registry = dr.async_get(hass)
    device_registry.async_get_or_create(
        config_entry_id=entry.entry_id,
        connections={(dr.CONNECTION_NETWORK_MAC, gateway_info.mac_address)},
        identifiers={(DOMAIN, gateway_id)},
        manufacturer="Xiaomi",
        name=name,
        model=gateway_model,
        sw_version=gateway_info.firmware_version,
    )

    def update_data():
        """Fetch data from the subdevice."""
        data = {}
        for sub_device in gateway.gateway_device.devices.values():
            try:
                sub_device.update()
            except GatewayException as ex:
                _LOGGER.error("Got exception while fetching the state: %s", ex)
                data[sub_device.sid] = {ATTR_AVAILABLE: False}
            else:
                data[sub_device.sid] = {ATTR_AVAILABLE: True}
        return data

    async def async_update_data():
        """Fetch data from the subdevice using async_add_executor_job."""
        return await hass.async_add_executor_job(update_data)

    # Create update coordinator
    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name=name,
        update_method=async_update_data,
        # Polling interval. Will only be polled if there are subscribers.
        update_interval=UPDATE_INTERVAL,
    )

    hass.data[DOMAIN][entry.entry_id] = {
        CONF_GATEWAY: gateway.gateway_device,
        KEY_COORDINATOR: coordinator,
    }

    for platform in GATEWAY_PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, platform)
        )
コード例 #16
0
ファイル: __init__.py プロジェクト: dmulcahey/home-assistant
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up the wiz integration from a config entry."""
    ip_address = entry.data[CONF_HOST]
    _LOGGER.debug("Get bulb with IP: %s", ip_address)
    bulb = wizlight(ip_address)
    try:
        scenes = await bulb.getSupportedScenes()
        await bulb.getMac()
    except WIZ_CONNECT_EXCEPTIONS as err:
        await bulb.async_close()
        raise ConfigEntryNotReady(f"{ip_address}: {err}") from err

    async def _async_update() -> None:
        """Update the WiZ device."""
        try:
            await bulb.updateState()
        except WIZ_EXCEPTIONS as ex:
            raise UpdateFailed(
                f"Failed to update device at {ip_address}: {ex}") from ex

    coordinator = DataUpdateCoordinator(
        hass=hass,
        logger=_LOGGER,
        name=entry.title,
        update_interval=timedelta(seconds=15),
        update_method=_async_update,
        # We don't want an immediate refresh since the device
        # takes a moment to reflect the state change
        request_refresh_debouncer=Debouncer(hass,
                                            _LOGGER,
                                            cooldown=REQUEST_REFRESH_DELAY,
                                            immediate=False),
    )

    try:
        await coordinator.async_config_entry_first_refresh()
    except ConfigEntryNotReady as err:
        await bulb.async_close()
        raise err

    async def _async_shutdown_on_stop(event: Event) -> None:
        await bulb.async_close()

    entry.async_on_unload(
        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                   _async_shutdown_on_stop))

    @callback
    def _async_push_update(state: PilotParser) -> None:
        """Receive a push update."""
        _LOGGER.debug("%s: Got push update: %s", bulb.mac, state.pilotResult)
        coordinator.async_set_updated_data(None)
        if state.get_source() == PIR_SOURCE:
            async_dispatcher_send(hass, SIGNAL_WIZ_PIR.format(bulb.mac))

    await bulb.start_push(_async_push_update)
    bulb.set_discovery_callback(
        lambda bulb: async_trigger_discovery(hass, [bulb]))

    hass.data.setdefault(DOMAIN,
                         {})[entry.entry_id] = WizData(coordinator=coordinator,
                                                       bulb=bulb,
                                                       scenes=scenes)
    hass.config_entries.async_setup_platforms(entry, PLATFORMS)
    return True
コード例 #17
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up LED BLE from a config entry."""
    address: str = entry.data[CONF_ADDRESS]
    ble_device = bluetooth.async_ble_device_from_address(
        hass, address.upper(), True)
    if not ble_device:
        raise ConfigEntryNotReady(
            f"Could not find LED BLE device with address {address}")

    led_ble = LEDBLE(ble_device)

    @callback
    def _async_update_ble(
        service_info: bluetooth.BluetoothServiceInfoBleak,
        change: bluetooth.BluetoothChange,
    ) -> None:
        """Update from a ble callback."""
        led_ble.set_ble_device(service_info.device)

    entry.async_on_unload(
        bluetooth.async_register_callback(
            hass,
            _async_update_ble,
            BluetoothCallbackMatcher({ADDRESS: address}),
            bluetooth.BluetoothScanningMode.PASSIVE,
        ))

    async def _async_update():
        """Update the device state."""
        try:
            await led_ble.update()
        except BLEAK_EXCEPTIONS as ex:
            raise UpdateFailed(str(ex)) from ex

    startup_event = asyncio.Event()
    cancel_first_update = led_ble.register_callback(
        lambda *_: startup_event.set())
    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name=led_ble.name,
        update_method=_async_update,
        update_interval=timedelta(seconds=UPDATE_SECONDS),
    )

    try:
        await coordinator.async_config_entry_first_refresh()
    except ConfigEntryNotReady:
        cancel_first_update()
        raise

    try:
        async with async_timeout.timeout(DEVICE_TIMEOUT):
            await startup_event.wait()
    except asyncio.TimeoutError as ex:
        raise ConfigEntryNotReady(
            "Unable to communicate with the device; "
            f"Try moving the Bluetooth adapter closer to {led_ble.name}"
        ) from ex
    finally:
        cancel_first_update()

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = LEDBLEData(
        entry.title, led_ble, coordinator)

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
    entry.async_on_unload(entry.add_update_listener(_async_update_listener))

    async def _async_stop(event: Event) -> None:
        """Close the connection."""
        await led_ble.stop()

    entry.async_on_unload(
        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop))
    return True
コード例 #18
0
async def async_setup_entry(hass: HomeAssistantType,
                            config_entry: ConfigEntry) -> bool:
    username = config_entry.data[CONF_USERNAME]
    yaml_config = hass.data.get(DATA_CONFIG)

    if config_entry.source == SOURCE_IMPORT and not (yaml_config and username
                                                     in yaml_config):
        _LOGGER.info(
            "Removing entry %s after removal from YAML configuration." %
            config_entry.entry_id)
        hass.async_create_task(
            hass.config_entries.async_remove(config_entry.entry_id))
        return False

    config = extract_config(hass, config_entry)

    device_info = None

    if config_entry.options:
        device_info = config_entry.options.get(CONF_DEVICE_INFO)

    if device_info is None:
        device_info = config.get(CONF_DEVICE_INFO)
    else:
        device_info = DEVICE_INFO_SCHEMA(device_info)

    _LOGGER.debug('Setting up config entry for user "%s"' % username)

    from custom_components.moscow_pgu._base import MoscowPGUEntity

    password = config[CONF_PASSWORD]
    additional_args = {
        "cache_lifetime": MoscowPGUEntity.MIN_SCAN_INTERVAL.total_seconds()
    }

    if device_info:
        additional_args.update({
            arg: device_info[conf]
            for arg, conf in {
                "app_version": CONF_APP_VERSION,
                "device_os": CONF_DEVICE_OS,
                "device_agent": CONF_DEVICE_AGENT,
                "user_agent": CONF_USER_AGENT,
                "guid": CONF_GUID,
            }.items() if conf in device_info
        })

    if not additional_args.get("guid"):
        # @TODO: this can be randomly generated?
        additional_args["guid"] = generate_guid(config)

    token = config_entry.options.get(CONF_TOKEN)
    if not token:
        token = config.get(CONF_TOKEN)

    if token:
        additional_args["token"] = token

    session_id = await async_load_session(hass, username)

    api_object = API(
        username=username,
        password=config[CONF_PASSWORD],
        session_id=session_id,
        **additional_args,
    )

    try:
        try:
            await api_object.init_session()
            await async_authenticate_api_object(hass, api_object)

        except MoscowPGUException as e:
            raise ConfigEntryNotReady(
                "Error occurred while authenticating: %s", e)
    except BaseException:
        await api_object.close_session()
        raise

    hass.data.setdefault(DOMAIN, {})[username] = api_object
    hass.data.setdefault(DATA_ENTITIES, {})[config_entry.entry_id] = {}

    for platform in SUPPORTED_PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, platform))

    update_listener = config_entry.add_update_listener(async_reload_entry)
    hass.data.setdefault(DATA_UPDATE_LISTENERS,
                         {})[config_entry.entry_id] = update_listener

    return True
コード例 #19
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up SmartThinQ integration from a config entry."""

    if not is_valid_ha_version():
        msg = "This integration require at least HomeAssistant version " \
              f" {__min_ha_version__}, you are running version {__version__}." \
              " Please upgrade HomeAssistant to continue use this integration."
        _notify_error(hass, "inv_ha_version", "SmartThinQ Sensors", msg)
        _LOGGER.warning(msg)
        return False

    refresh_token = entry.data[CONF_TOKEN]
    region = entry.data[CONF_REGION]
    language = entry.data[CONF_LANGUAGE]
    oauth_url = entry.data.get(CONF_OAUTH_URL)
    use_api_v2 = entry.data.get(CONF_USE_API_V2, False)

    if not use_api_v2:
        _LOGGER.warning(
            "Integration configuration is using ThinQ APIv1 that is unsupported. Please reconfigure"
        )
        # Launch config entries setup
        hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN, context={"source": SOURCE_IMPORT}, data=entry.data))
        return False

    _LOGGER.info(STARTUP)
    _LOGGER.info(
        "Initializing ThinQ platform with region: %s - language: %s",
        region,
        language,
    )

    # if network is not connected we can have some error
    # raising ConfigEntryNotReady platform setup will be retried
    lge_auth = LGEAuthentication(region, language)
    try:
        client = await lge_auth.create_client_from_token(
            hass, refresh_token, oauth_url)

    except InvalidCredentialError:
        msg = "Invalid ThinQ credential error, integration setup aborted." \
              " Please use the LG App on your mobile device to ensure your" \
              " credentials are correct, then restart HomeAssistant." \
              " If your credential changed, you must reconfigure integration"
        _notify_error(hass, "inv_credential", "SmartThinQ Sensors", msg)
        _LOGGER.error(msg)
        return False

    except Exception as exc:
        _LOGGER.warning("Connection not available. ThinQ platform not ready",
                        exc_info=True)
        raise ConfigEntryNotReady("ThinQ platform not ready") from exc

    if not client.has_devices:
        _LOGGER.error("No ThinQ devices found. Component setup aborted")
        return False

    _LOGGER.info("ThinQ client connected")

    try:
        lge_devices, unsupported_devices = await lge_devices_setup(
            hass, client)
    except Exception as exc:
        _LOGGER.warning("Connection not available. ThinQ platform not ready",
                        exc_info=True)
        raise ConfigEntryNotReady("ThinQ platform not ready") from exc

    # remove device not available anymore
    cleanup_orphan_lge_devices(hass, entry.entry_id, client)

    hass.data[DOMAIN] = {
        CLIENT: client,
        LGE_DEVICES: lge_devices,
        UNSUPPORTED_DEVICES: unsupported_devices,
    }
    hass.config_entries.async_setup_platforms(entry, SMARTTHINQ_PLATFORMS)

    return True
コード例 #20
0
ファイル: __init__.py プロジェクト: home-ha/home-assistant
async def async_setup_entry(  # noqa: C901
        hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Synology DSM sensors."""

    # Migrate old unique_id
    @callback
    def _async_migrator(
        entity_entry: entity_registry.RegistryEntry,
    ) -> dict[str, str] | None:
        """Migrate away from ID using label."""
        # Reject if new unique_id
        if "SYNO." in entity_entry.unique_id:
            return None

        entries = (
            *STORAGE_DISK_BINARY_SENSORS,
            *STORAGE_DISK_SENSORS,
            *STORAGE_VOL_SENSORS,
            *UTILISATION_SENSORS,
        )
        infos = entity_entry.unique_id.split("_")
        serial = infos.pop(0)
        label = infos.pop(0)
        device_id = "_".join(infos)

        # Removed entity
        if ("Type" in entity_entry.unique_id
                or "Device" in entity_entry.unique_id
                or "Name" in entity_entry.unique_id):
            return None

        entity_type: str | None = None
        for description in entries:
            if (device_id and description.name == "Status"
                    and "Status" in entity_entry.unique_id
                    and "(Smart)" not in entity_entry.unique_id):
                if "sd" in device_id and "disk" in description.key:
                    entity_type = description.key
                    continue
                if "volume" in device_id and "volume" in description.key:
                    entity_type = description.key
                    continue

            if description.name == label:
                entity_type = description.key

        if entity_type is None:
            return None

        new_unique_id = "_".join([serial, entity_type])
        if device_id:
            new_unique_id += f"_{device_id}"

        _LOGGER.info(
            "Migrating unique_id from [%s] to [%s]",
            entity_entry.unique_id,
            new_unique_id,
        )
        return {"new_unique_id": new_unique_id}

    await entity_registry.async_migrate_entries(hass, entry.entry_id,
                                                _async_migrator)

    # migrate device indetifiers
    dev_reg = await get_dev_reg(hass)
    devices: list[
        DeviceEntry] = device_registry.async_entries_for_config_entry(
            dev_reg, entry.entry_id)
    for device in devices:
        old_identifier = list(next(iter(device.identifiers)))
        if len(old_identifier) > 2:
            new_identifier = {(old_identifier.pop(0),
                               "_".join([str(x) for x in old_identifier]))}
            _LOGGER.debug("migrate identifier '%s' to '%s'",
                          device.identifiers, new_identifier)
            dev_reg.async_update_device(device.id,
                                        new_identifiers=new_identifier)

    # Migrate existing entry configuration
    if entry.data.get(CONF_VERIFY_SSL) is None:
        hass.config_entries.async_update_entry(
            entry, data={
                **entry.data, CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL
            })

    # Continue setup
    api = SynoApi(hass, entry)
    try:
        await api.async_setup()
    except (
            SynologyDSMLogin2SARequiredException,
            SynologyDSMLoginDisabledAccountException,
            SynologyDSMLoginInvalidException,
            SynologyDSMLoginPermissionDeniedException,
    ) as err:
        if err.args[0] and isinstance(err.args[0], dict):
            # pylint: disable=no-member
            details = err.args[0].get(EXCEPTION_DETAILS, EXCEPTION_UNKNOWN)
        else:
            details = EXCEPTION_UNKNOWN
        raise ConfigEntryAuthFailed(f"reason: {details}") from err
    except (SynologyDSMLoginFailedException,
            SynologyDSMRequestException) as err:
        if err.args[0] and isinstance(err.args[0], dict):
            # pylint: disable=no-member
            details = err.args[0].get(EXCEPTION_DETAILS, EXCEPTION_UNKNOWN)
        else:
            details = EXCEPTION_UNKNOWN
        raise ConfigEntryNotReady(details) from err

    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.unique_id] = {
        UNDO_UPDATE_LISTENER:
        entry.add_update_listener(_async_update_listener),
        SYNO_API: api,
        SYSTEM_LOADED: True,
    }

    # Services
    await _async_setup_services(hass)

    # For SSDP compat
    if not entry.data.get(CONF_MAC):
        network = await hass.async_add_executor_job(getattr, api.dsm,
                                                    "network")
        hass.config_entries.async_update_entry(entry,
                                               data={
                                                   **entry.data, CONF_MAC:
                                                   network.macs
                                               })

    async def async_coordinator_update_data_cameras(
    ) -> dict[str, dict[str, SynoCamera]] | None:
        """Fetch all camera data from api."""
        if not hass.data[DOMAIN][entry.unique_id][SYSTEM_LOADED]:
            raise UpdateFailed("System not fully loaded")

        if SynoSurveillanceStation.CAMERA_API_KEY not in api.dsm.apis:
            return None

        surveillance_station = api.surveillance_station

        try:
            async with async_timeout.timeout(10):
                await hass.async_add_executor_job(surveillance_station.update)
        except SynologyDSMAPIErrorException as err:
            raise UpdateFailed(f"Error communicating with API: {err}") from err

        return {
            "cameras": {
                camera.id: camera
                for camera in surveillance_station.get_all_cameras()
            }
        }

    async def async_coordinator_update_data_central() -> None:
        """Fetch all device and sensor data from api."""
        try:
            await api.async_update()
        except Exception as err:
            raise UpdateFailed(f"Error communicating with API: {err}") from err
        return None

    async def async_coordinator_update_data_switches(
    ) -> dict[str, dict[str, Any]] | None:
        """Fetch all switch data from api."""
        if not hass.data[DOMAIN][entry.unique_id][SYSTEM_LOADED]:
            raise UpdateFailed("System not fully loaded")
        if SynoSurveillanceStation.HOME_MODE_API_KEY not in api.dsm.apis:
            return None

        surveillance_station = api.surveillance_station

        return {
            "switches": {
                "home_mode":
                await hass.async_add_executor_job(
                    surveillance_station.get_home_mode_status)
            }
        }

    hass.data[DOMAIN][
        entry.unique_id][COORDINATOR_CAMERAS] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=f"{entry.unique_id}_cameras",
            update_method=async_coordinator_update_data_cameras,
            update_interval=timedelta(seconds=30),
        )

    hass.data[DOMAIN][
        entry.unique_id][COORDINATOR_CENTRAL] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=f"{entry.unique_id}_central",
            update_method=async_coordinator_update_data_central,
            update_interval=timedelta(minutes=entry.options.get(
                CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)),
        )

    hass.data[DOMAIN][
        entry.unique_id][COORDINATOR_SWITCHES] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=f"{entry.unique_id}_switches",
            update_method=async_coordinator_update_data_switches,
            update_interval=timedelta(seconds=30),
        )

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
コード例 #21
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Withings from a config entry."""
    config_updates = {}

    # Add a unique id if it's an older config entry.
    if entry.unique_id != entry.data["token"]["userid"] or not isinstance(
            entry.unique_id, str):
        config_updates["unique_id"] = str(entry.data["token"]["userid"])

    # Add the webhook configuration.
    if CONF_WEBHOOK_ID not in entry.data:
        webhook_id = webhook.async_generate_id()
        config_updates["data"] = {
            **entry.data,
            **{
                const.CONF_USE_WEBHOOK:
                hass.data[DOMAIN][const.CONFIG][const.CONF_USE_WEBHOOK],
                CONF_WEBHOOK_ID:
                webhook_id,
                const.CONF_WEBHOOK_URL:
                entry.data.get(
                    const.CONF_WEBHOOK_URL,
                    webhook.async_generate_url(hass, webhook_id),
                ),
            },
        }

    if config_updates:
        hass.config_entries.async_update_entry(entry, **config_updates)

    data_manager = await async_get_data_manager(hass, entry)

    _LOGGER.debug("Confirming %s is authenticated to withings",
                  data_manager.profile)
    await data_manager.poll_data_update_coordinator.async_refresh()
    if not data_manager.poll_data_update_coordinator.last_update_success:
        raise ConfigEntryNotReady()

    webhook.async_register(
        hass,
        const.DOMAIN,
        "Withings notify",
        data_manager.webhook_config.id,
        async_webhook_handler,
    )

    # Perform first webhook subscription check.
    if data_manager.webhook_config.enabled:
        data_manager.async_start_polling_webhook_subscriptions()

        @callback
        def async_call_later_callback(now) -> None:
            hass.async_create_task(
                data_manager.subscription_update_coordinator.async_refresh())

        # Start subscription check in the background, outside this component's setup.
        async_call_later(hass, 1, async_call_later_callback)

    hass.async_create_task(
        hass.config_entries.async_forward_entry_setup(entry,
                                                      BINARY_SENSOR_DOMAIN))
    hass.async_create_task(
        hass.config_entries.async_forward_entry_setup(entry, SENSOR_DOMAIN))

    return True
コード例 #22
0
async def async_setup_entry(hass: HomeAssistantType, config_entry):
    """
    This class is called by the HomeAssistant framework when a configuration entry is provided.
    """

    refresh_token = config_entry.data.get(CONF_TOKEN)
    region = config_entry.data.get(CONF_REGION)
    language = config_entry.data.get(CONF_LANGUAGE)
    use_api_v2 = config_entry.data.get(CONF_USE_API_V2, False)
    oauth_url = config_entry.data.get(CONF_OAUTH_URL)
    oauth_user_num = config_entry.data.get(CONF_OAUTH_USER_NUM)
    use_tls_v1 = config_entry.data.get(CONF_USE_TLS_V1, False)
    exclude_dh = config_entry.data.get(CONF_EXCLUDE_DH, False)

    _LOGGER.info(STARTUP)
    _LOGGER.info(
        "Initializing ThinQ platform with region: %s - language: %s",
        region,
        language,
    )

    hass.data.setdefault(DOMAIN, {})[LGE_DEVICES] = {}

    # if network is not connected we can have some error
    # raising ConfigEntryNotReady platform setup will be retried
    lgeauth = LGEAuthentication(region, language, use_api_v2)
    lgeauth.initHttpAdapter(use_tls_v1, exclude_dh)
    try:
        client = await hass.async_add_executor_job(
            lgeauth.createClientFromToken, refresh_token, oauth_url,
            oauth_user_num)
    except InvalidCredentialError:
        _LOGGER.error(
            "Invalid ThinQ credential error. Component setup aborted")
        return False

    except Exception:
        _LOGGER.warning("Connection not available. ThinQ platform not ready",
                        exc_info=True)
        raise ConfigEntryNotReady()

    if not client.hasdevices:
        _LOGGER.error("No ThinQ devices found. Component setup aborted")
        return False

    _LOGGER.info("ThinQ client connected")

    try:
        lge_devices = await lge_devices_setup(hass, client)
    except Exception:
        _LOGGER.warning("Connection not available. ThinQ platform not ready",
                        exc_info=True)
        raise ConfigEntryNotReady()

    if not use_api_v2:
        _LOGGER.warning(
            "Integration configuration is using ThinQ APIv1 that is obsolete"
            " and not able to manage all ThinQ devices."
            " Please remove and re-add integration from HA user interface to"
            " enable the use of ThinQ APIv2")

    hass.data.setdefault(DOMAIN, {}).update({
        CLIENT: client,
        LGE_DEVICES: lge_devices
    })

    for platform in SMARTTHINQ_COMPONENTS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, platform))

    return True
コード例 #23
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up System Bridge from a config entry."""

    # Check version before initialising
    version = Version(
        entry.data[CONF_HOST],
        entry.data[CONF_PORT],
        entry.data[CONF_API_KEY],
        session=async_get_clientsession(hass),
    )
    try:
        if not await version.check_supported():
            raise ConfigEntryNotReady(
                f"You are not running a supported version of System Bridge. Please update to {SUPPORTED_VERSION} or higher."
            )
    except AuthenticationException as exception:
        _LOGGER.error("Authentication failed for %s: %s", entry.title,
                      exception)
        raise ConfigEntryAuthFailed from exception
    except (ConnectionClosedException, ConnectionErrorException) as exception:
        raise ConfigEntryNotReady(
            f"Could not connect to {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception
    except asyncio.TimeoutError as exception:
        raise ConfigEntryNotReady(
            f"Timed out waiting for {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

    coordinator = SystemBridgeDataUpdateCoordinator(
        hass,
        _LOGGER,
        entry=entry,
    )
    try:
        async with async_timeout.timeout(30):
            await coordinator.async_get_data(MODULES)
    except AuthenticationException as exception:
        _LOGGER.error("Authentication failed for %s: %s", entry.title,
                      exception)
        raise ConfigEntryAuthFailed from exception
    except (ConnectionClosedException, ConnectionErrorException) as exception:
        raise ConfigEntryNotReady(
            f"Could not connect to {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception
    except asyncio.TimeoutError as exception:
        raise ConfigEntryNotReady(
            f"Timed out waiting for {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

    await coordinator.async_config_entry_first_refresh()

    try:
        # Wait for initial data
        async with async_timeout.timeout(30):
            while not coordinator.is_ready():
                _LOGGER.debug(
                    "Waiting for initial data from %s (%s)",
                    entry.title,
                    entry.data[CONF_HOST],
                )
                await asyncio.sleep(1)
    except asyncio.TimeoutError as exception:
        raise ConfigEntryNotReady(
            f"Timed out waiting for {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

    _LOGGER.debug(
        "Initial coordinator data for %s (%s):\n%s",
        entry.title,
        entry.data[CONF_HOST],
        coordinator.data.json(),
    )

    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.entry_id] = coordinator

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

    if hass.services.has_service(DOMAIN, SERVICE_OPEN_URL):
        return True

    def valid_device(device: str):
        """Check device is valid."""
        device_registry = dr.async_get(hass)
        device_entry = device_registry.async_get(device)
        if device_entry is not None:
            try:
                return next(
                    entry.entry_id
                    for entry in hass.config_entries.async_entries(DOMAIN)
                    if entry.entry_id in device_entry.config_entries)
            except StopIteration as exception:
                raise vol.Invalid from exception
        raise vol.Invalid(f"Device {device} does not exist")

    async def handle_open_path(call: ServiceCall) -> None:
        """Handle the open path service call."""
        _LOGGER.info("Open: %s", call.data)
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            call.data[CONF_BRIDGE]]
        await coordinator.websocket_client.open_path(
            OpenPath(path=call.data[CONF_PATH]))

    async def handle_open_url(call: ServiceCall) -> None:
        """Handle the open url service call."""
        _LOGGER.info("Open: %s", call.data)
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            call.data[CONF_BRIDGE]]
        await coordinator.websocket_client.open_url(
            OpenUrl(url=call.data[CONF_URL]))

    async def handle_send_keypress(call: ServiceCall) -> None:
        """Handle the send_keypress service call."""
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            call.data[CONF_BRIDGE]]
        await coordinator.websocket_client.keyboard_keypress(
            KeyboardKey(key=call.data[CONF_KEY]))

    async def handle_send_text(call: ServiceCall) -> None:
        """Handle the send_keypress service call."""
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            call.data[CONF_BRIDGE]]
        await coordinator.websocket_client.keyboard_text(
            KeyboardText(text=call.data[CONF_TEXT]))

    hass.services.async_register(
        DOMAIN,
        SERVICE_OPEN_PATH,
        handle_open_path,
        schema=vol.Schema(
            {
                vol.Required(CONF_BRIDGE): valid_device,
                vol.Required(CONF_PATH): cv.string,
            }, ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_OPEN_URL,
        handle_open_url,
        schema=vol.Schema(
            {
                vol.Required(CONF_BRIDGE): valid_device,
                vol.Required(CONF_URL): cv.string,
            }, ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_SEND_KEYPRESS,
        handle_send_keypress,
        schema=vol.Schema(
            {
                vol.Required(CONF_BRIDGE): valid_device,
                vol.Required(CONF_KEY): cv.string,
            }, ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_SEND_TEXT,
        handle_send_text,
        schema=vol.Schema(
            {
                vol.Required(CONF_BRIDGE): valid_device,
                vol.Required(CONF_TEXT): cv.string,
            }, ),
    )

    # Reload entry when its updated.
    entry.async_on_unload(entry.add_update_listener(async_reload_entry))

    return True
コード例 #24
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Emporia Vue from a config entry."""
    global device_gids
    global device_information
    device_gids = []
    device_information = {}

    entry_data = entry.data
    email = entry_data[CONF_EMAIL]
    password = entry_data[CONF_PASSWORD]
    # _LOGGER.info(entry_data)
    vue = PyEmVue()
    loop = asyncio.get_event_loop()
    try:
        result = await loop.run_in_executor(None, vue.login, email, password)
        if not result:
            raise Exception("Could not authenticate with Emporia API")
    except Exception:
        _LOGGER.error("Could not authenticate with Emporia API")
        return False

    scales_1m = []
    scales_1s = []
    try:
        devices = await loop.run_in_executor(None, vue.get_devices)
        total_channels = 0
        for d in devices:
            total_channels += len(d.channels)
        _LOGGER.warn(
            "Found {0} Emporia devices with {1} total channels".format(
                len(devices), total_channels
            )
        )
        for device in devices:
            if not device.device_gid in device_gids:
                device_gids.append(device.device_gid)
                await loop.run_in_executor(None, vue.populate_device_properties, device)
                device_information[device.device_gid] = device
            else:
                device_information[device.device_gid].channels += device.channels

        async def async_update_data_1min():
            """Fetch data from API endpoint at a 1 minute interval

            This is the place to pre-process the data to lookup tables
            so entities can quickly look up their data.
            """
            return await update_sensors(vue, scales_1m)

        async def async_update_data_1second():
            """Fetch data from API endpoint at a 1 second interval

            This is the place to pre-process the data to lookup tables
            so entities can quickly look up their data.
            """
            return await update_sensors(vue, scales_1s)

        if ENABLE_1M not in entry_data or entry_data[ENABLE_1M]:
            scales_1m.append(Scale.MINUTE.value)
        if ENABLE_1D not in entry_data or entry_data[ENABLE_1D]:
            scales_1m.append(Scale.DAY.value)
        if ENABLE_1MON not in entry_data or entry_data[ENABLE_1MON]:
            scales_1m.append(Scale.MONTH.value)

        coordinator_1min = None
        if scales_1m:
            coordinator_1min = DataUpdateCoordinator(
                hass,
                _LOGGER,
                # Name of the data. For logging purposes.
                name="sensor",
                update_method=async_update_data_1min,
                # Polling interval. Will only be polled if there are subscribers.
                update_interval=timedelta(seconds=60),
            )
            await coordinator_1min.async_config_entry_first_refresh()
            _LOGGER.warn(f"1min Update data: {coordinator_1min.data}")
        coordinator_1s = None
        if ENABLE_1S in entry_data and entry_data[ENABLE_1S]:
            scales_1s.append(Scale.SECOND.value)
            coordinator_1s = DataUpdateCoordinator(
                hass,
                _LOGGER,
                # Name of the data. For logging purposes.
                name="sensor1s",
                update_method=async_update_data_1second,
                # Polling interval. Will only be polled if there are subscribers.
                update_interval=timedelta(seconds=1),
            )
            await coordinator_1s.async_config_entry_first_refresh()
            _LOGGER.warn(f"1s Update data: {coordinator_1s.data}")
    except Exception as err:
        _LOGGER.warn(f"Exception while setting up Emporia Vue. Will retry. {err}")
        raise ConfigEntryNotReady(
            f"Exception while setting up Emporia Vue. Will retry. {err}"
        )

    hass.data[DOMAIN][entry.entry_id] = {
        VUE_DATA: vue,
        "coordinator_1min": coordinator_1min,
        "coordinator_1s": coordinator_1s,
    }

    try:
        for component in PLATFORMS:
            hass.async_create_task(
                hass.config_entries.async_forward_entry_setup(entry, component)
            )
    except Exception as err:
        _LOGGER.warn(f"Error setting up platforms: {err}")
        raise ConfigEntryNotReady(f"Error setting up platforms: {err}")

    return True
コード例 #25
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Z-Wave JS from a config entry."""
    if use_addon := entry.data.get(CONF_USE_ADDON):
        await async_ensure_addon_running(hass, entry)

    client = ZwaveClient(entry.data[CONF_URL], async_get_clientsession(hass))

    # connect and throw error if connection failed
    try:
        async with timeout(CONNECT_TIMEOUT):
            await client.connect()
    except InvalidServerVersion as err:
        if use_addon:
            async_ensure_addon_updated(hass)
        raise ConfigEntryNotReady(f"Invalid server version: {err}") from err
    except (asyncio.TimeoutError, BaseZwaveJSServerError) as err:
        raise ConfigEntryNotReady(f"Failed to connect: {err}") from err
    else:
        LOGGER.info("Connected to Zwave JS Server")

    dev_reg = device_registry.async_get(hass)
    ent_reg = entity_registry.async_get(hass)
    services = ZWaveServices(hass, ent_reg, dev_reg)
    services.async_register()

    # Set up websocket API
    async_register_api(hass)

    platform_task = hass.async_create_task(start_platforms(hass, entry, client))
    hass.data[DOMAIN].setdefault(entry.entry_id, {})[
コード例 #26
0
async def async_setup_entry(hass: HomeAssistant,
                            entry: config_entries.ConfigEntry) -> bool:
    """Set up the ISY 994 integration."""
    # As there currently is no way to import options from yaml
    # when setting up a config entry, we fallback to adding
    # the options to the config entry and pull them out here if
    # they are missing from the options
    _async_import_options_from_data_if_missing(hass, entry)

    hass.data[DOMAIN][entry.entry_id] = {}
    hass_isy_data = hass.data[DOMAIN][entry.entry_id]

    hass_isy_data[ISY994_NODES] = {}
    for platform in PLATFORMS:
        hass_isy_data[ISY994_NODES][platform] = []

    hass_isy_data[ISY994_PROGRAMS] = {}
    for platform in PROGRAM_PLATFORMS:
        hass_isy_data[ISY994_PROGRAMS][platform] = []

    hass_isy_data[ISY994_VARIABLES] = []

    isy_config = entry.data
    isy_options = entry.options

    # Required
    user = isy_config[CONF_USERNAME]
    password = isy_config[CONF_PASSWORD]
    host = urlparse(isy_config[CONF_HOST])

    # Optional
    tls_version = isy_config.get(CONF_TLS_VER)
    ignore_identifier = isy_options.get(CONF_IGNORE_STRING,
                                        DEFAULT_IGNORE_STRING)
    sensor_identifier = isy_options.get(CONF_SENSOR_STRING,
                                        DEFAULT_SENSOR_STRING)
    variable_identifier = isy_options.get(CONF_VAR_SENSOR_STRING,
                                          DEFAULT_VAR_SENSOR_STRING)

    if host.scheme == "http":
        https = False
        port = host.port or 80
        session = aiohttp_client.async_create_clientsession(
            hass, verify_ssl=None, cookie_jar=CookieJar(unsafe=True))
    elif host.scheme == "https":
        https = True
        port = host.port or 443
        session = aiohttp_client.async_get_clientsession(hass)
    else:
        _LOGGER.error("The isy994 host value in configuration is invalid")
        return False

    # Connect to ISY controller.
    isy = ISY(
        host.hostname,
        port,
        username=user,
        password=password,
        use_https=https,
        tls_ver=tls_version,
        webroot=host.path,
        websession=session,
        use_websocket=True,
    )

    try:
        async with async_timeout.timeout(60):
            await isy.initialize()
    except asyncio.TimeoutError as err:
        raise ConfigEntryNotReady(
            f"Timed out initializing the ISY; device may be busy, trying again later: {err}"
        ) from err
    except ISYInvalidAuthError as err:
        _LOGGER.error(
            "Invalid credentials for the ISY, please adjust settings and try again: %s",
            err,
        )
        return False
    except ISYConnectionError as err:
        raise ConfigEntryNotReady(
            f"Failed to connect to the ISY, please adjust settings and try again: {err}"
        ) from err
    except ISYResponseParseError as err:
        raise ConfigEntryNotReady(
            f"Invalid XML response from ISY; Ensure the ISY is running the latest firmware: {err}"
        ) from err

    _categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier,
                      sensor_identifier)
    _categorize_programs(hass_isy_data, isy.programs)
    _categorize_variables(hass_isy_data, isy.variables, variable_identifier)

    # Dump ISY Clock Information. Future: Add ISY as sensor to Hass with attrs
    _LOGGER.info(repr(isy.clock))

    hass_isy_data[ISY994_ISY] = isy
    await _async_get_or_create_isy_device_in_registry(hass, entry, isy)

    # Load platforms for the devices in the ISY controller that we support.
    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    @callback
    def _async_stop_auto_update(event) -> None:
        """Stop the isy auto update on Home Assistant Shutdown."""
        _LOGGER.debug("ISY Stopping Event Stream and automatic updates")
        isy.websocket.stop()

    _LOGGER.debug("ISY Starting Event Stream and automatic updates")
    isy.websocket.start()

    entry.async_on_unload(entry.add_update_listener(_async_update_listener))
    entry.async_on_unload(
        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                   _async_stop_auto_update))

    # Register Integration-wide Services:
    async_setup_services(hass)

    return True
コード例 #27
0
ファイル: __init__.py プロジェクト: tsotsos/core
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Tile as config entry."""
    hass.data.setdefault(DOMAIN, {DATA_COORDINATOR: {}, DATA_TILE: {}})
    hass.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = {}
    hass.data[DOMAIN][DATA_TILE][entry.entry_id] = {}

    @callback
    def async_migrate_callback(entity_entry: RegistryEntry) -> dict | None:
        """
        Define a callback to migrate appropriate Tile entities to new unique IDs.

        Old: tile_{uuid}
        New: {username}_{uuid}
        """
        if entity_entry.unique_id.startswith(entry.data[CONF_USERNAME]):
            return None

        new_unique_id = f"{entry.data[CONF_USERNAME]}_".join(
            entity_entry.unique_id.split(f"{DOMAIN}_"))

        LOGGER.debug(
            "Migrating entity %s from old unique ID '%s' to new unique ID '%s'",
            entity_entry.entity_id,
            entity_entry.unique_id,
            new_unique_id,
        )

        return {"new_unique_id": new_unique_id}

    await async_migrate_entries(hass, entry.entry_id, async_migrate_callback)

    # Tile's API uses cookies to identify a consumer; in order to allow for multiple
    # instances of this config entry, we use a new session each time:
    websession = aiohttp_client.async_create_clientsession(hass)

    try:
        client = await async_login(
            entry.data[CONF_USERNAME],
            entry.data[CONF_PASSWORD],
            session=websession,
        )
        hass.data[DOMAIN][DATA_TILE][
            entry.entry_id] = await client.async_get_tiles()
    except InvalidAuthError:
        LOGGER.error("Invalid credentials provided")
        return False
    except TileError as err:
        raise ConfigEntryNotReady("Error during integration setup") from err

    async def async_update_tile(tile: Tile) -> None:
        """Update the Tile."""
        try:
            await tile.async_update()
        except SessionExpiredError:
            LOGGER.info("Tile session expired; creating a new one")
            await client.async_init()
        except TileError as err:
            raise UpdateFailed(f"Error while retrieving data: {err}") from err

    coordinator_init_tasks = []
    for tile_uuid, tile in hass.data[DOMAIN][DATA_TILE][
            entry.entry_id].items():
        coordinator = hass.data[DOMAIN][DATA_COORDINATOR][
            entry.entry_id][tile_uuid] = DataUpdateCoordinator(
                hass,
                LOGGER,
                name=tile.name,
                update_interval=DEFAULT_UPDATE_INTERVAL,
                update_method=partial(async_update_tile, tile),
            )
        coordinator_init_tasks.append(coordinator.async_refresh())

    await gather_with_concurrency(DEFAULT_INIT_TASK_LIMIT,
                                  *coordinator_init_tasks)

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
コード例 #28
0
ファイル: __init__.py プロジェクト: mback2k/home-assistant
            async_update_entry_from_discovery(hass, entry, discovery)

    device: AIOWifiLedBulb = async_wifi_bulb_for_host(host)
    signal = SIGNAL_STATE_UPDATED.format(device.ipaddr)

    @callback
    def _async_state_changed(*_: Any) -> None:
        _LOGGER.debug("%s: Device state updated: %s", device.ipaddr,
                      device.raw_state)
        async_dispatcher_send(hass, signal)

    try:
        await device.async_setup(_async_state_changed)
    except FLUX_LED_EXCEPTIONS as ex:
        raise ConfigEntryNotReady(
            str(ex)
            or f"Timed out trying to connect to {device.ipaddr}") from ex
    coordinator = FluxLedUpdateCoordinator(hass, device)
    hass.data[DOMAIN][entry.entry_id] = coordinator
    hass.config_entries.async_setup_platforms(
        entry, PLATFORMS_BY_TYPE[device.device_type])
    entry.async_on_unload(entry.add_update_listener(async_update_listener))

    return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Unload a config entry."""
    device: AIOWifiLedBulb = hass.data[DOMAIN][entry.entry_id].device
    platforms = PLATFORMS_BY_TYPE[device.device_type]
    if unload_ok := await hass.config_entries.async_unload_platforms(
コード例 #29
0
ファイル: __init__.py プロジェクト: testitesti22/core-1
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Tuya platform."""

    tuya = TuyaApi()
    username = entry.data[CONF_USERNAME]
    password = entry.data[CONF_PASSWORD]
    country_code = entry.data[CONF_COUNTRYCODE]
    platform = entry.data[CONF_PLATFORM]

    try:
        await hass.async_add_executor_job(
            tuya.init, username, password, country_code, platform
        )
    except (
        TuyaNetException,
        TuyaServerException,
        TuyaFrequentlyInvokeException,
    ) as exc:
        raise ConfigEntryNotReady() from exc

    except TuyaAPIRateLimitException as exc:
        _LOGGER.error("Tuya login rate limited")
        raise ConfigEntryNotReady() from exc

    except TuyaAPIException as exc:
        _LOGGER.error(
            "Connection error during integration setup. Error: %s",
            exc,
        )
        return False

    hass.data[DOMAIN] = {
        TUYA_DATA: tuya,
        TUYA_DEVICES_CONF: entry.options.copy(),
        TUYA_TRACKER: None,
        ENTRY_IS_SETUP: set(),
        "entities": {},
        "pending": {},
        "listener": entry.add_update_listener(update_listener),
    }

    _update_discovery_interval(
        hass, entry.options.get(CONF_DISCOVERY_INTERVAL, DEFAULT_DISCOVERY_INTERVAL)
    )

    _update_query_interval(
        hass, entry.options.get(CONF_QUERY_INTERVAL, DEFAULT_QUERY_INTERVAL)
    )

    async def async_load_devices(device_list):
        """Load new devices by device_list."""
        device_type_list = {}
        for device in device_list:
            dev_type = device.device_type()
            if (
                dev_type in TUYA_TYPE_TO_HA
                and device.object_id() not in hass.data[DOMAIN]["entities"]
            ):
                ha_type = TUYA_TYPE_TO_HA[dev_type]
                if ha_type not in device_type_list:
                    device_type_list[ha_type] = []
                device_type_list[ha_type].append(device.object_id())
                hass.data[DOMAIN]["entities"][device.object_id()] = None

        for ha_type, dev_ids in device_type_list.items():
            config_entries_key = f"{ha_type}.tuya"
            if config_entries_key not in hass.data[DOMAIN][ENTRY_IS_SETUP]:
                hass.data[DOMAIN]["pending"][ha_type] = dev_ids
                hass.async_create_task(
                    hass.config_entries.async_forward_entry_setup(entry, ha_type)
                )
                hass.data[DOMAIN][ENTRY_IS_SETUP].add(config_entries_key)
            else:
                async_dispatcher_send(hass, TUYA_DISCOVERY_NEW.format(ha_type), dev_ids)

    await async_load_devices(tuya.get_all_devices())

    def _get_updated_devices():
        try:
            tuya.poll_devices_update()
        except TuyaFrequentlyInvokeException as exc:
            _LOGGER.error(exc)
        return tuya.get_all_devices()

    async def async_poll_devices_update(event_time):
        """Check if accesstoken is expired and pull device list from server."""
        _LOGGER.debug("Pull devices from Tuya")
        # Add new discover device.
        device_list = await hass.async_add_executor_job(_get_updated_devices)
        await async_load_devices(device_list)
        # Delete not exist device.
        newlist_ids = []
        for device in device_list:
            newlist_ids.append(device.object_id())
        for dev_id in list(hass.data[DOMAIN]["entities"]):
            if dev_id not in newlist_ids:
                async_dispatcher_send(hass, SIGNAL_DELETE_ENTITY, dev_id)
                hass.data[DOMAIN]["entities"].pop(dev_id)

    hass.data[DOMAIN][TUYA_TRACKER] = async_track_time_interval(
        hass, async_poll_devices_update, timedelta(minutes=2)
    )

    hass.services.async_register(
        DOMAIN, SERVICE_PULL_DEVICES, async_poll_devices_update
    )

    async def async_force_update(call):
        """Force all devices to pull data."""
        async_dispatcher_send(hass, SIGNAL_UPDATE_ENTITY)

    hass.services.async_register(DOMAIN, SERVICE_FORCE_UPDATE, async_force_update)

    return True
コード例 #30
0
ファイル: __init__.py プロジェクト: pasna/myconfig
async def async_setup_entry(hass: HomeAssistantType,
                            config_entry: config_entries.ConfigEntry):
    username = config_entry.data[CONF_USERNAME]

    # Check if leftovers from previous setup are present
    if config_entry.entry_id in hass.data.get(DATA_FINAL_CONFIG, {}):
        raise ConfigEntryNotReady(
            'Configuration entry with username "%s" already set up' %
            (username, ))

    # Source full configuration
    if config_entry.source == config_entries.SOURCE_IMPORT:
        # Source configuration from YAML
        yaml_config = hass.data.get(DATA_YAML_CONFIG)

        if not yaml_config or username not in yaml_config:
            _LOGGER.info(
                'Removing entry %s after removal from YAML configuration.' %
                config_entry.entry_id)
            hass.async_create_task(
                hass.config_entries.async_remove(config_entry.entry_id))
            return False

        user_cfg = yaml_config[username]

    else:
        # Source and convert configuration from input data
        all_cfg = {**config_entry.data}

        if config_entry.options:
            all_cfg.update(config_entry.options)

        user_cfg = CONFIG_ENTRY_SCHEMA(all_cfg)

    _LOGGER.info('Setting up config entry for user "%s"' % username)

    from custom_components.mosenergosbyt.api import API, MosenergosbytException

    try:
        api_object = API(username=username,
                         password=user_cfg[CONF_PASSWORD],
                         user_agent=user_cfg.get(CONF_USER_AGENT))

        await api_object.login()

        # Fetch all accounts
        accounts, unsupported_accounts = \
            await api_object.get_accounts(return_unsupported_accounts=True)

        # Filter accounts
        if CONF_FILTER in user_cfg:
            account_filter = user_cfg[CONF_FILTER]
            accounts = [
                account for account in accounts
                if account_filter[account.account_code]
            ]

    except MosenergosbytException as e:
        _LOGGER.error('Error authenticating with user "%s": %s' %
                      (username, str(e)))
        return False

    if unsupported_accounts:
        async_handle_unsupported_accounts(hass, username, unsupported_accounts)

    if not accounts:
        # Cancel setup because no accounts provided
        _LOGGER.warning('No supported accounts found under username "%s"',
                        username)
        return False

    entry_id = config_entry.entry_id

    # Create data placeholders
    hass.data.setdefault(DATA_API_OBJECTS, {})[entry_id] = api_object
    hass.data.setdefault(DATA_ENTITIES, {})[entry_id] = {}
    hass.data.setdefault(DATA_UPDATERS, {})[entry_id] = {}

    # Save final configuration data
    hass.data.setdefault(DATA_FINAL_CONFIG, {})[entry_id] = user_cfg

    # Forward entry setup to sensor platform
    hass.async_create_task(
        hass.config_entries.async_forward_entry_setup(config_entry,
                                                      SENSOR_DOMAIN))

    hass.data.setdefault(DATA_UPDATE_LISTENERS, {})[entry_id] = \
        config_entry.add_update_listener(async_reload_entry)

    _LOGGER.debug('Successfully set up user "%s"' % username)
    return True