Esempio n. 1
0
async def async_setup_entry(opp: OpenPeerPower, config_entry: ConfigEntry):
    """Set up AEMET OpenData as config entry."""
    name = config_entry.data[CONF_NAME]
    api_key = config_entry.data[CONF_API_KEY]
    latitude = config_entry.data[CONF_LATITUDE]
    longitude = config_entry.data[CONF_LONGITUDE]
    station_updates = config_entry.options.get(CONF_STATION_UPDATES, True)

    aemet = AEMET(api_key)
    weather_coordinator = WeatherUpdateCoordinator(opp, aemet, latitude,
                                                   longitude, station_updates)

    await weather_coordinator.async_config_entry_first_refresh()

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][config_entry.entry_id] = {
        ENTRY_NAME: name,
        ENTRY_WEATHER_COORDINATOR: weather_coordinator,
    }

    opp.config_entries.async_setup_platforms(config_entry, PLATFORMS)

    config_entry.async_on_unload(
        config_entry.add_update_listener(async_update_options))

    return True
Esempio n. 2
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up NZBGet from a config entry."""
    if not entry.options:
        options = {
            CONF_SCAN_INTERVAL: entry.data.get(
                CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
            ),
        }
        opp.config_entries.async_update_entry(entry, options=options)

    coordinator = NZBGetDataUpdateCoordinator(
        opp,
        config=entry.data,
        options=entry.options,
    )

    await coordinator.async_config_entry_first_refresh()

    undo_listener = entry.add_update_listener(_async_update_listener)

    opp.data[DOMAIN][entry.entry_id] = {
        DATA_COORDINATOR: coordinator,
        DATA_UNDO_UPDATE_LISTENER: undo_listener,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    _async_register_services(opp, coordinator)

    return True
Esempio n. 3
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up AsusWrt platform."""

    # import options from yaml if empty
    yaml_options = opp.data.get(DOMAIN, {}).pop("yaml_options", {})
    if not entry.options and yaml_options:
        opp.config_entries.async_update_entry(entry, options=yaml_options)

    router = AsusWrtRouter(opp, entry)
    await router.setup()

    router.async_on_close(entry.add_update_listener(update_listener))

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    async def async_close_connection(event):
        """Close AsusWrt connection on OPP Stop."""
        await router.close()

    stop_listener = opp.bus.async_listen_once(
        EVENT_OPENPEERPOWER_STOP, async_close_connection
    )

    opp.data.setdefault(DOMAIN, {})[entry.entry_id] = {
        DATA_ASUSWRT: router,
        "stop_listener": stop_listener,
    }

    return True
Esempio n. 4
0
async def _async_initialize(
    opp: OpenPeerPower,
    entry: ConfigEntry,
    host: str,
    device: YeelightDevice | None = None,
) -> None:
    entry_data = opp.data[DOMAIN][DATA_CONFIG_ENTRIES][entry.entry_id] = {
        DATA_PLATFORMS_LOADED: False
    }
    entry.async_on_unload(entry.add_update_listener(_async_update_listener))

    @callback
    def _async_load_platforms():
        if entry_data[DATA_PLATFORMS_LOADED]:
            return
        entry_data[DATA_PLATFORMS_LOADED] = True
        opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    if not device:
        device = await _async_get_device(opp, host, entry)
    entry_data[DATA_DEVICE] = device

    entry.async_on_unload(
        async_dispatcher_connect(
            opp,
            DEVICE_INITIALIZED.format(host),
            _async_load_platforms,
        ))

    entry.async_on_unload(device.async_unload)
    await device.async_setup()
Esempio n. 5
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up Canary from a config entry."""
    if not entry.options:
        options = {
            CONF_FFMPEG_ARGUMENTS:
            entry.data.get(CONF_FFMPEG_ARGUMENTS, DEFAULT_FFMPEG_ARGUMENTS),
            CONF_TIMEOUT:
            entry.data.get(CONF_TIMEOUT, DEFAULT_TIMEOUT),
        }
        opp.config_entries.async_update_entry(entry, options=options)

    try:
        canary_api = await opp.async_add_executor_job(_get_canary_api_instance,
                                                      entry)
    except (ConnectTimeout, HTTPError) as error:
        _LOGGER.error("Unable to connect to Canary service: %s", str(error))
        raise ConfigEntryNotReady from error

    coordinator = CanaryDataUpdateCoordinator(opp, api=canary_api)
    await coordinator.async_config_entry_first_refresh()

    undo_listener = entry.add_update_listener(_async_update_listener)

    opp.data[DOMAIN][entry.entry_id] = {
        DATA_COORDINATOR: coordinator,
        DATA_UNDO_UPDATE_LISTENER: undo_listener,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 6
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up AccuWeather as config entry."""
    api_key: str = entry.data[CONF_API_KEY]
    assert entry.unique_id is not None
    location_key = entry.unique_id
    forecast: bool = entry.options.get(CONF_FORECAST, False)

    _LOGGER.debug("Using location_key: %s, get forecast: %s", location_key,
                  forecast)

    websession = async_get_clientsession(opp)

    coordinator = AccuWeatherDataUpdateCoordinator(opp, websession, api_key,
                                                   location_key, forecast)
    await coordinator.async_config_entry_first_refresh()

    undo_listener = entry.add_update_listener(update_listener)

    opp.data.setdefault(DOMAIN, {})[entry.entry_id] = {
        COORDINATOR: coordinator,
        UNDO_UPDATE_LISTENER: undo_listener,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 7
0
async def async_setup_entry(opp: core.OpenPeerPower,
                            entry: config_entries.ConfigEntry):
    """Set up the denonavr components from a config entry."""
    opp.data.setdefault(DOMAIN, {})

    # Connect to receiver
    connect_denonavr = ConnectDenonAVR(
        entry.data[CONF_HOST],
        DEFAULT_TIMEOUT,
        entry.options.get(CONF_SHOW_ALL_SOURCES, DEFAULT_SHOW_SOURCES),
        entry.options.get(CONF_ZONE2, DEFAULT_ZONE2),
        entry.options.get(CONF_ZONE3, DEFAULT_ZONE3),
        lambda: get_async_client(opp),
    )
    try:
        await connect_denonavr.async_connect_receiver()
    except (AvrNetworkError, AvrTimoutError) as ex:
        raise ConfigEntryNotReady from ex
    receiver = connect_denonavr.receiver

    undo_listener = entry.add_update_listener(update_listener)

    opp.data[DOMAIN][entry.entry_id] = {
        CONF_RECEIVER: receiver,
        UNDO_UPDATE_LISTENER: undo_listener,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 8
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up Monoprice 6-Zone Amplifier from a config entry."""
    port = entry.data[CONF_PORT]

    try:
        monoprice = await opp.async_add_executor_job(get_monoprice, port)
    except SerialException as err:
        _LOGGER.error("Error connecting to Monoprice controller at %s", port)
        raise ConfigEntryNotReady from err

    # double negative to handle absence of value
    first_run = not bool(entry.data.get(CONF_NOT_FIRST_RUN))

    if first_run:
        opp.config_entries.async_update_entry(
            entry, data={**entry.data, CONF_NOT_FIRST_RUN: True}
        )

    undo_listener = entry.add_update_listener(_update_listener)

    opp.data.setdefault(DOMAIN, {})[entry.entry_id] = {
        MONOPRICE_OBJECT: monoprice,
        UNDO_UPDATE_LISTENER: undo_listener,
        FIRST_RUN: first_run,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 9
0
async def async_setup_entry(opp: OpenPeerPower, config_entry: ConfigEntry):
    """Set up OpenWeatherMap as config entry."""
    name = config_entry.data[CONF_NAME]
    api_key = config_entry.data[CONF_API_KEY]
    latitude = config_entry.data.get(CONF_LATITUDE, opp.config.latitude)
    longitude = config_entry.data.get(CONF_LONGITUDE, opp.config.longitude)
    forecast_mode = _get_config_value(config_entry, CONF_MODE)
    language = _get_config_value(config_entry, CONF_LANGUAGE)

    config_dict = _get_owm_config(language)

    owm = OWM(api_key, config_dict).weather_manager()
    weather_coordinator = WeatherUpdateCoordinator(owm, latitude, longitude,
                                                   forecast_mode, opp)

    await weather_coordinator.async_config_entry_first_refresh()

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][config_entry.entry_id] = {
        ENTRY_NAME: name,
        ENTRY_WEATHER_COORDINATOR: weather_coordinator,
    }

    opp.config_entries.async_setup_platforms(config_entry, PLATFORMS)

    update_listener = config_entry.add_update_listener(async_update_options)
    opp.data[DOMAIN][config_entry.entry_id][UPDATE_LISTENER] = update_listener

    return True
Esempio n. 10
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up DoorBird from a config entry."""

    _async_import_options_from_data_if_missing(opp, entry)

    doorstation_config = entry.data
    doorstation_options = entry.options
    config_entry_id = entry.entry_id

    device_ip = doorstation_config[CONF_HOST]
    username = doorstation_config[CONF_USERNAME]
    password = doorstation_config[CONF_PASSWORD]

    device = DoorBird(device_ip, username, password)
    try:
        status, info = await opp.async_add_executor_job(
            _init_doorbird_device, device)
    except requests.exceptions.HTTPError as err:
        if err.response.status_code == HTTP_UNAUTHORIZED:
            _LOGGER.error("Authorization rejected by DoorBird for %s@%s",
                          username, device_ip)
            return False
        raise ConfigEntryNotReady from err
    except OSError as oserr:
        _LOGGER.error("Failed to setup doorbird at %s: %s", device_ip, oserr)
        raise ConfigEntryNotReady from oserr

    if not status[0]:
        _LOGGER.error(
            "Could not connect to DoorBird as %s@%s: Error %s",
            username,
            device_ip,
            str(status[1]),
        )
        raise ConfigEntryNotReady

    token = doorstation_config.get(CONF_TOKEN, config_entry_id)
    custom_url = doorstation_config.get(CONF_CUSTOM_URL)
    name = doorstation_config.get(CONF_NAME)
    events = doorstation_options.get(CONF_EVENTS, [])
    doorstation = ConfiguredDoorBird(device, name, custom_url, token)
    doorstation.update_events(events)
    # Subscribe to doorbell or motion events
    if not await _async_register_events(opp, doorstation):
        raise ConfigEntryNotReady

    undo_listener = entry.add_update_listener(_update_listener)

    opp.data[DOMAIN][config_entry_id] = {
        DOOR_STATION: doorstation,
        DOOR_STATION_INFO: info,
        UNDO_UPDATE_LISTENER: undo_listener,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 11
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up HomeKit from a config entry."""
    _async_import_options_from_data_if_missing(opp, entry)

    conf = entry.data
    options = entry.options

    name = conf[CONF_NAME]
    port = conf[CONF_PORT]
    _LOGGER.debug("Begin setup HomeKit for %s", name)

    # ip_address and advertise_ip are yaml only
    ip_address = conf.get(CONF_IP_ADDRESS)
    advertise_ip = conf.get(CONF_ADVERTISE_IP)
    # exclude_accessory_mode is only used for config flow
    # to indicate that the config entry was setup after
    # we started creating config entries for entities that
    # to run in accessory mode and that we should never include
    # these entities on the bridge. For backwards compatibility
    # with users who have not migrated yet we do not do exclude
    # these entities by default as we cannot migrate automatically
    # since it requires a re-pairing.
    exclude_accessory_mode = conf.get(CONF_EXCLUDE_ACCESSORY_MODE,
                                      DEFAULT_EXCLUDE_ACCESSORY_MODE)
    homekit_mode = options.get(CONF_HOMEKIT_MODE, DEFAULT_HOMEKIT_MODE)
    entity_config = options.get(CONF_ENTITY_CONFIG, {}).copy()
    auto_start = options.get(CONF_AUTO_START, DEFAULT_AUTO_START)
    entity_filter = FILTER_SCHEMA(options.get(CONF_FILTER, {}))

    homekit = HomeKit(
        opp,
        name,
        port,
        ip_address,
        entity_filter,
        exclude_accessory_mode,
        entity_config,
        homekit_mode,
        advertise_ip,
        entry.entry_id,
        entry.title,
    )

    entry.async_on_unload(entry.add_update_listener(_async_update_listener))
    entry.async_on_unload(
        opp.bus.async_listen_once(EVENT_OPENPEERPOWER_STOP,
                                  homekit.async_stop))

    opp.data[DOMAIN][entry.entry_id] = {HOMEKIT: homekit}

    if opp.state == CoreState.running:
        await homekit.async_start()
    elif auto_start:
        opp.bus.async_listen_once(EVENT_OPENPEERPOWER_STARTED,
                                  homekit.async_start)

    return True
Esempio n. 12
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up DSMR from a config entry."""
    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = {}

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    listener = entry.add_update_listener(async_update_options)
    opp.data[DOMAIN][entry.entry_id][DATA_LISTENER] = listener

    return True
Esempio n. 13
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up BMW Connected Drive from a config entry."""
    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN].setdefault(DATA_ENTRIES, {})

    _async_migrate_options_from_data_if_missing(opp, entry)

    try:
        account = await opp.async_add_executor_job(setup_account, entry, opp,
                                                   entry.data[CONF_USERNAME])
    except OSError as ex:
        raise ConfigEntryNotReady from ex

    async def _async_update_all(service_call=None):
        """Update all BMW accounts."""
        await opp.async_add_executor_job(_update_all)

    def _update_all() -> None:
        """Update all BMW accounts."""
        for entry in opp.data[DOMAIN][DATA_ENTRIES].copy().values():
            entry[CONF_ACCOUNT].update()

    # Add update listener for config entry changes (options)
    undo_listener = entry.add_update_listener(update_listener)

    opp.data[DOMAIN][DATA_ENTRIES][entry.entry_id] = {
        CONF_ACCOUNT: account,
        UNDO_UPDATE_LISTENER: undo_listener,
    }

    # Service to manually trigger updates for all accounts.
    opp.services.async_register(DOMAIN, SERVICE_UPDATE_STATE,
                                _async_update_all)

    await _async_update_all()

    opp.config_entries.async_setup_platforms(
        entry,
        [platform for platform in PLATFORMS if platform != NOTIFY_DOMAIN])

    # set up notify platform, no entry support for notify platform yet,
    # have to use discovery to load platform.
    opp.async_create_task(
        discovery.async_load_platform(
            opp,
            NOTIFY_DOMAIN,
            DOMAIN,
            {CONF_NAME: DOMAIN},
            opp.data[DOMAIN][DATA_OPP_CONFIG],
        ))

    return True
Esempio n. 14
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up Tado from a config entry."""

    _async_import_options_from_data_if_missing(opp, entry)

    username = entry.data[CONF_USERNAME]
    password = entry.data[CONF_PASSWORD]
    fallback = entry.options.get(CONF_FALLBACK, True)

    tadoconnector = TadoConnector(opp, username, password, fallback)

    try:
        await opp.async_add_executor_job(tadoconnector.setup)
    except KeyError:
        _LOGGER.error("Failed to login to tado")
        return False
    except RuntimeError as exc:
        _LOGGER.error("Failed to setup tado: %s", exc)
        return ConfigEntryNotReady
    except requests.exceptions.Timeout as ex:
        raise ConfigEntryNotReady from ex
    except requests.exceptions.HTTPError as ex:
        if ex.response.status_code > 400 and ex.response.status_code < 500:
            _LOGGER.error("Failed to login to tado: %s", ex)
            return False
        raise ConfigEntryNotReady from ex

    # Do first update
    await opp.async_add_executor_job(tadoconnector.update)

    # Poll for updates in the background
    update_track = async_track_time_interval(
        opp,
        lambda now: tadoconnector.update(),
        SCAN_INTERVAL,
    )

    update_listener = entry.add_update_listener(_async_update_listener)

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = {
        DATA: tadoconnector,
        UPDATE_TRACK: update_track,
        UPDATE_LISTENER: update_listener,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 15
0
async def async_setup_entry(opp: OpenPeerPower, config_entry: ConfigEntry):
    """Set up wiffi from a config entry, config_entry contains data from config entry database."""
    if not config_entry.update_listeners:
        config_entry.add_update_listener(async_update_options)

    # create api object
    api = WiffiIntegrationApi(opp)
    api.async_setup(config_entry)

    # store api object
    opp.data.setdefault(DOMAIN, {})[config_entry.entry_id] = api

    try:
        await api.server.start_server()
    except OSError as exc:
        if exc.errno != errno.EADDRINUSE:
            _LOGGER.error("Start_server failed, errno: %d", exc.errno)
            return False
        _LOGGER.error("Port %s already in use", config_entry.data[CONF_PORT])
        raise ConfigEntryNotReady from exc

    opp.config_entries.async_setup_platforms(config_entry, PLATFORMS)

    return True
Esempio n. 16
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up a bridge from a config entry."""
    LOGGER.debug("Setting up entry %s", entry.data)
    bridge = DynaliteBridge(opp, entry.data)
    # need to do it before the listener
    opp.data[DOMAIN][entry.entry_id] = bridge
    entry.async_on_unload(entry.add_update_listener(async_entry_changed))

    if not await bridge.async_setup():
        LOGGER.error("Could not set up bridge for entry %s", entry.data)
        opp.data[DOMAIN][entry.entry_id] = None
        raise ConfigEntryNotReady

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 17
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up panel from a config entry."""
    client = AlarmPanel(opp, entry)
    # creates a panel data store in opp.data[DOMAIN][CONF_DEVICES]
    await client.async_save_data()

    # if the cfg entry was created we know we could connect to the panel at some point
    # async_connect will handle retries until it establishes a connection
    await client.async_connect()

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    # config entry specific data to enable unload
    opp.data[DOMAIN][entry.entry_id] = {
        UNDO_UPDATE_LISTENER: entry.add_update_listener(async_entry_updated)
    }
    return True
Esempio n. 18
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up Dexcom from a config entry."""
    try:
        dexcom = await opp.async_add_executor_job(
            Dexcom,
            entry.data[CONF_USERNAME],
            entry.data[CONF_PASSWORD],
            entry.data[CONF_SERVER] == SERVER_OUS,
        )
    except AccountError:
        return False
    except SessionError as error:
        raise ConfigEntryNotReady from error

    if not entry.options:
        opp.config_entries.async_update_entry(
            entry, options={CONF_UNIT_OF_MEASUREMENT: MG_DL})

    async def async_update_data():
        try:
            return await opp.async_add_executor_job(
                dexcom.get_current_glucose_reading)
        except SessionError as error:
            raise UpdateFailed(error) from error

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = {
        COORDINATOR:
        DataUpdateCoordinator(
            opp,
            _LOGGER,
            name=DOMAIN,
            update_method=async_update_data,
            update_interval=SCAN_INTERVAL,
        ),
        UNDO_UPDATE_LISTENER:
        entry.add_update_listener(update_listener),
    }

    await opp.data[DOMAIN][entry.entry_id
                           ][COORDINATOR].async_config_entry_first_refresh()

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 19
0
async def async_setup_entry(opp: OpenPeerPower,
                            config_entry: ConfigEntry) -> bool:
    """Set up the component."""
    opp.data.setdefault(DOMAIN, {})
    async_add_defaults(opp, config_entry)

    router = KeeneticRouter(opp, config_entry)
    await router.async_setup()

    undo_listener = config_entry.add_update_listener(update_listener)

    opp.data[DOMAIN][config_entry.entry_id] = {
        ROUTER: router,
        UNDO_UPDATE_LISTENER: undo_listener,
    }

    opp.config_entries.async_setup_platforms(config_entry, PLATFORMS)

    return True
Esempio n. 20
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up kmtronic from a config entry."""
    session = aiohttp_client.async_get_clientsession(opp)
    auth = Auth(
        session,
        f"http://{entry.data[CONF_HOST]}",
        entry.data[CONF_USERNAME],
        entry.data[CONF_PASSWORD],
    )
    hub = KMTronicHubAPI(auth)

    async def async_update_data():
        try:
            async with async_timeout.timeout(10):
                await hub.async_update_relays()
        except aiohttp.client_exceptions.ClientResponseError as err:
            raise UpdateFailed(f"Wrong credentials: {err}") from err
        except aiohttp.client_exceptions.ClientConnectorError as err:
            raise UpdateFailed(f"Error communicating with API: {err}") from err

    coordinator = DataUpdateCoordinator(
        opp,
        _LOGGER,
        name=f"{MANUFACTURER} {hub.name}",
        update_method=async_update_data,
        update_interval=timedelta(seconds=30),
    )
    await coordinator.async_config_entry_first_refresh()

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = {
        DATA_HUB: hub,
        DATA_COORDINATOR: coordinator,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    update_listener = entry.add_update_listener(async_update_options)
    opp.data[DOMAIN][entry.entry_id][UPDATE_LISTENER] = update_listener

    return True
Esempio n. 21
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up Sonarr from a config entry."""
    if not entry.options:
        options = {
            CONF_UPCOMING_DAYS:
            entry.data.get(CONF_UPCOMING_DAYS, DEFAULT_UPCOMING_DAYS),
            CONF_WANTED_MAX_ITEMS:
            entry.data.get(CONF_WANTED_MAX_ITEMS, DEFAULT_WANTED_MAX_ITEMS),
        }
        opp.config_entries.async_update_entry(entry, options=options)

    sonarr = Sonarr(
        host=entry.data[CONF_HOST],
        port=entry.data[CONF_PORT],
        api_key=entry.data[CONF_API_KEY],
        base_path=entry.data[CONF_BASE_PATH],
        session=async_get_clientsession(opp),
        tls=entry.data[CONF_SSL],
        verify_ssl=entry.data[CONF_VERIFY_SSL],
    )

    try:
        await sonarr.update()
    except SonarrAccessRestricted as err:
        raise ConfigEntryAuthFailed(
            "API Key is no longer valid. Please reauthenticate") from err
    except SonarrError as err:
        raise ConfigEntryNotReady from err

    undo_listener = entry.add_update_listener(_async_update_listener)

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = {
        DATA_SONARR: sonarr,
        DATA_UNDO_UPDATE_LISTENER: undo_listener,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 22
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up fritzboxtools from config entry."""
    _LOGGER.debug("Setting up FRITZ!Box Tools component")
    fritz_tools = FritzBoxTools(
        opp=opp,
        host=entry.data[CONF_HOST],
        port=entry.data[CONF_PORT],
        username=entry.data[CONF_USERNAME],
        password=entry.data[CONF_PASSWORD],
    )

    try:
        await fritz_tools.async_setup()
        await fritz_tools.async_start(entry.options)
    except FritzSecurityError as ex:
        raise ConfigEntryAuthFailed from ex
    except FritzConnectionException as ex:
        raise ConfigEntryNotReady from ex

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = fritz_tools

    if DATA_FRITZ not in opp.data:
        opp.data[DATA_FRITZ] = FritzData()

    @callback
    def _async_unload(event):
        fritz_tools.async_unload()

    entry.async_on_unload(
        opp.bus.async_listen_once(EVENT_OPENPEERPOWER_STOP, _async_unload)
    )
    entry.async_on_unload(entry.add_update_listener(update_listener))

    # Load the other platforms like switch
    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    await async_setup_services(opp)

    return True
Esempio n. 23
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up Risco from a config entry."""
    data = entry.data
    risco = RiscoAPI(data[CONF_USERNAME], data[CONF_PASSWORD], data[CONF_PIN])
    try:
        await risco.login(async_get_clientsession(opp))
    except CannotConnectError as error:
        raise ConfigEntryNotReady() from error
    except UnauthorizedError:
        _LOGGER.exception("Failed to login to Risco cloud")
        return False

    scan_interval = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
    coordinator = RiscoDataUpdateCoordinator(opp, risco, scan_interval)
    await coordinator.async_config_entry_first_refresh()
    events_coordinator = RiscoEventsDataUpdateCoordinator(
        opp, risco, entry.entry_id, 60
    )

    undo_listener = entry.add_update_listener(_update_listener)

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = {
        DATA_COORDINATOR: coordinator,
        UNDO_UPDATE_LISTENER: undo_listener,
        EVENTS_COORDINATOR: events_coordinator,
    }

    async def start_platforms():
        await asyncio.gather(
            *[
                opp.config_entries.async_forward_entry_setup(entry, platform)
                for platform in PLATFORMS
            ]
        )
        await events_coordinator.async_refresh()

    opp.async_create_task(start_platforms())

    return True
Esempio n. 24
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up Somfy MyLink from a config entry."""
    opp.data.setdefault(DOMAIN, {})

    config = entry.data
    somfy_mylink = SomfyMyLinkSynergy(config[CONF_SYSTEM_ID],
                                      config[CONF_HOST], config[CONF_PORT])

    try:
        mylink_status = await somfy_mylink.status_info()
    except asyncio.TimeoutError as ex:
        raise ConfigEntryNotReady(
            "Unable to connect to the Somfy MyLink device, please check your settings"
        ) from ex

    if not mylink_status or "error" in mylink_status:
        _LOGGER.error(
            "Somfy Mylink failed to setup because of an error: %s",
            mylink_status.get("error",
                              {}).get("message",
                                      "Empty response from mylink device"),
        )
        return False

    if "result" not in mylink_status:
        raise ConfigEntryNotReady(
            "The Somfy MyLink device returned an empty result")

    undo_listener = entry.add_update_listener(_async_update_listener)

    opp.data[DOMAIN][entry.entry_id] = {
        DATA_SOMFY_MYLINK: somfy_mylink,
        MYLINK_STATUS: mylink_status,
        UNDO_UPDATE_LISTENER: undo_listener,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 25
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up Logitech Harmony Hub from a config entry."""
    # 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(opp, entry)

    address = entry.data[CONF_HOST]
    name = entry.data[CONF_NAME]
    data = HarmonyData(opp, address, name, entry.unique_id)
    try:
        connected_ok = await data.connect()
    except (asyncio.TimeoutError, ValueError, AttributeError) as err:
        raise ConfigEntryNotReady from err

    if not connected_ok:
        raise ConfigEntryNotReady

    await _migrate_old_unique_ids(opp, entry.entry_id, data)

    cancel_listener = entry.add_update_listener(_update_listener)

    async def _async_on_stop(event):
        await data.shutdown()

    cancel_stop = opp.bus.async_listen(EVENT_OPENPEERPOWER_STOP,
                                       _async_on_stop)

    opp.data.setdefault(DOMAIN, {})
    opp.data[DOMAIN][entry.entry_id] = {
        HARMONY_DATA: data,
        CANCEL_LISTENER: cancel_listener,
        CANCEL_STOP: cancel_stop,
    }

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 26
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up RainMachine as config entry."""
    session = aiohttp_client.async_get_clientsession(opp)
    client = Client(entry.data[CONF_PLACE_ID],
                    entry.data[CONF_SERVICE_ID],
                    session=session)

    async def async_get_pickup_events() -> list[PickupEvent]:
        """Get the next pickup."""
        try:
            return await client.async_get_pickup_events(
                start_date=date.today(),
                end_date=date.today() + timedelta(weeks=4))
        except RecollectError as err:
            raise UpdateFailed(
                f"Error while requesting data from ReCollect: {err}") from err

    coordinator = DataUpdateCoordinator(
        opp,
        LOGGER,
        name=
        f"Place {entry.data[CONF_PLACE_ID]}, Service {entry.data[CONF_SERVICE_ID]}",
        update_interval=DEFAULT_UPDATE_INTERVAL,
        update_method=async_get_pickup_events,
    )

    await coordinator.async_config_entry_first_refresh()

    opp.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = coordinator

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    opp.data[DOMAIN][DATA_LISTENER][
        entry.entry_id] = entry.add_update_listener(async_reload_entry)

    return True
Esempio n. 27
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool:
    """Set up RainMachine as config entry."""
    opp.data.setdefault(DOMAIN, {DATA_CONTROLLER: {}, DATA_COORDINATOR: {}})
    opp.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = {}
    websession = aiohttp_client.async_get_clientsession(opp)
    client = Client(session=websession)

    try:
        await client.load_local(
            entry.data[CONF_IP_ADDRESS],
            entry.data[CONF_PASSWORD],
            port=entry.data[CONF_PORT],
            ssl=entry.data.get(CONF_SSL, DEFAULT_SSL),
        )
    except RainMachineError as err:
        raise ConfigEntryNotReady from err

    # regenmaschine can load multiple controllers at once, but we only grab the one
    # we loaded above:
    controller = opp.data[DOMAIN][DATA_CONTROLLER][
        entry.entry_id] = get_client_controller(client)

    entry_updates = {}
    if not entry.unique_id or is_ip_address(entry.unique_id):
        # If the config entry doesn't already have a unique ID, set one:
        entry_updates["unique_id"] = controller.mac
    if CONF_ZONE_RUN_TIME in entry.data:
        # If a zone run time exists in the config entry's data, pop it and move it to
        # options:
        data = {**entry.data}
        entry_updates["data"] = data
        entry_updates["options"] = {
            **entry.options,
            CONF_ZONE_RUN_TIME: data.pop(CONF_ZONE_RUN_TIME),
        }
    if entry_updates:
        opp.config_entries.async_update_entry(entry, **entry_updates)

    async def async_update(api_category: str) -> dict:
        """Update the appropriate API data based on a category."""
        try:
            if api_category == DATA_PROGRAMS:
                return await controller.programs.all(include_inactive=True)

            if api_category == DATA_PROVISION_SETTINGS:
                return await controller.provisioning.settings()

            if api_category == DATA_RESTRICTIONS_CURRENT:
                return await controller.restrictions.current()

            if api_category == DATA_RESTRICTIONS_UNIVERSAL:
                return await controller.restrictions.universal()

            return await controller.zones.all(details=True,
                                              include_inactive=True)
        except RainMachineError as err:
            raise UpdateFailed(err) from err

    controller_init_tasks = []
    for api_category in [
            DATA_PROGRAMS,
            DATA_PROVISION_SETTINGS,
            DATA_RESTRICTIONS_CURRENT,
            DATA_RESTRICTIONS_UNIVERSAL,
            DATA_ZONES,
    ]:
        coordinator = opp.data[DOMAIN][DATA_COORDINATOR][
            entry.entry_id][api_category] = DataUpdateCoordinator(
                opp,
                LOGGER,
                name=f'{controller.name} ("{api_category}")',
                update_interval=DEFAULT_UPDATE_INTERVAL,
                update_method=partial(async_update, api_category),
            )
        controller_init_tasks.append(coordinator.async_refresh())

    await asyncio.gather(*controller_init_tasks)

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    entry.async_on_unload(entry.add_update_listener(async_reload_entry))

    return True
Esempio n. 28
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up Netatmo from a config entry."""
    implementation = (
        await config_entry_oauth2_flow.async_get_config_entry_implementation(
            opp, entry))

    # Set unique id if non was set (migration)
    if not entry.unique_id:
        opp.config_entries.async_update_entry(entry, unique_id=DOMAIN)

    session = config_entry_oauth2_flow.OAuth2Session(opp, entry,
                                                     implementation)
    opp.data[DOMAIN][entry.entry_id] = {
        AUTH:
        api.AsyncConfigEntryNetatmoAuth(
            aiohttp_client.async_get_clientsession(opp), session)
    }

    data_handler = NetatmoDataHandler(opp, entry)
    await data_handler.async_setup()
    opp.data[DOMAIN][entry.entry_id][DATA_HANDLER] = data_handler

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    async def unregister_webhook(_):
        if CONF_WEBHOOK_ID not in entry.data:
            return
        _LOGGER.debug("Unregister Netatmo webhook (%s)",
                      entry.data[CONF_WEBHOOK_ID])
        async_dispatcher_send(
            opp,
            f"signal-{DOMAIN}-webhook-None",
            {
                "type": "None",
                "data": {
                    WEBHOOK_PUSH_TYPE: WEBHOOK_DEACTIVATION
                }
            },
        )
        webhook_unregister(opp, entry.data[CONF_WEBHOOK_ID])
        await opp.data[DOMAIN][entry.entry_id][AUTH].async_dropwebhook()

    async def register_webhook(event):
        if CONF_WEBHOOK_ID not in entry.data:
            data = {**entry.data, CONF_WEBHOOK_ID: secrets.token_hex()}
            opp.config_entries.async_update_entry(entry, data=data)

        if opp.components.cloud.async_active_subscription():
            if CONF_CLOUDHOOK_URL not in entry.data:
                webhook_url = await opp.components.cloud.async_create_cloudhook(
                    entry.data[CONF_WEBHOOK_ID])
                data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url}
                opp.config_entries.async_update_entry(entry, data=data)
            else:
                webhook_url = entry.data[CONF_CLOUDHOOK_URL]
        else:
            webhook_url = opp.components.webhook.async_generate_url(
                entry.data[CONF_WEBHOOK_ID])

        if entry.data[
                "auth_implementation"] == cloud.DOMAIN and not webhook_url.startswith(
                    "https://"):
            _LOGGER.warning(
                "Webhook not registered - "
                "https and port 443 is required to register the webhook")
            return

        try:
            webhook_register(
                opp,
                DOMAIN,
                "Netatmo",
                entry.data[CONF_WEBHOOK_ID],
                async_handle_webhook,
            )

            async def handle_event(event):
                """Handle webhook events."""
                if event["data"][WEBHOOK_PUSH_TYPE] == WEBHOOK_ACTIVATION:
                    if activation_listener is not None:
                        activation_listener()

                    if activation_timeout is not None:
                        activation_timeout()

            activation_listener = async_dispatcher_connect(
                opp,
                f"signal-{DOMAIN}-webhook-None",
                handle_event,
            )

            activation_timeout = async_call_later(opp, 30, unregister_webhook)

            await opp.data[DOMAIN][entry.entry_id
                                   ][AUTH].async_addwebhook(webhook_url)
            _LOGGER.info("Register Netatmo webhook: %s", webhook_url)
        except pyatmo.ApiError as err:
            _LOGGER.error("Error during webhook registration - %s", err)

        entry.async_on_unload(
            opp.bus.async_listen_once(EVENT_OPENPEERPOWER_STOP,
                                      unregister_webhook))

    if opp.state == CoreState.running:
        await register_webhook(None)
    else:
        opp.bus.async_listen_once(EVENT_OPENPEERPOWER_START, register_webhook)

    opp.services.async_register(DOMAIN, "register_webhook", register_webhook)
    opp.services.async_register(DOMAIN, "unregister_webhook",
                                unregister_webhook)

    entry.add_update_listener(async_config_entry_updated)

    return True
Esempio n. 29
0
async def async_setup_entry(  # noqa: C901
        opp: OpenPeerPower, 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 entity_key, entity_attrs in entries.items():
            if (device_id and entity_attrs[ENTITY_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 entity_key:
                    entity_type = entity_key
                    continue
                if "volume" in device_id and "volume" in entity_key:
                    entity_type = entity_key
                    continue

            if entity_attrs[ENTITY_NAME] == label:
                entity_type = entity_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(opp, entry.entry_id,
                                                _async_migrator)

    # migrate device indetifiers
    dev_reg = await get_dev_reg(opp)
    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:
        opp.config_entries.async_update_entry(
            entry, data={
                **entry.data, CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL
            })

    # Continue setup
    api = SynoApi(opp, entry)
    try:
        await api.async_setup()
    except (SynologyDSMLoginFailedException,
            SynologyDSMRequestException) as err:
        _LOGGER.debug("Unable to connect to DSM '%s' during setup: %s",
                      entry.unique_id, err)
        raise ConfigEntryNotReady from err

    opp.data.setdefault(DOMAIN, {})
    opp.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(opp)

    # For SSDP compat
    if not entry.data.get(CONF_MAC):
        network = await opp.async_add_executor_job(getattr, api.dsm, "network")
        opp.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 opp.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 opp.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 opp.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 opp.async_add_executor_job(
                    surveillance_station.get_home_mode_status)
            }
        }

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

    opp.data[DOMAIN][
        entry.unique_id][COORDINATOR_CENTRAL] = DataUpdateCoordinator(
            opp,
            _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)),
        )

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

    opp.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Esempio n. 30
0
async def async_setup_entry(opp: OpenPeerPower, 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 opp.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:
        raise ConfigEntryNotReady("Tuya login rate limited") from exc

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

    domain_data = opp.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(
        opp,
        entry.options.get(CONF_DISCOVERY_INTERVAL, DEFAULT_DISCOVERY_INTERVAL))

    _update_query_interval(
        opp, 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 domain_data["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())
                domain_data["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 domain_data[ENTRY_IS_SETUP]:
                domain_data["pending"][ha_type] = dev_ids
                opp.async_create_task(
                    opp.config_entries.async_forward_entry_setup(
                        entry, ha_type))
                domain_data[ENTRY_IS_SETUP].add(config_entries_key)
            else:
                async_dispatcher_send(opp, 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 opp.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(domain_data["entities"]):
            if dev_id not in newlist_ids:
                async_dispatcher_send(opp, SIGNAL_DELETE_ENTITY, dev_id)
                domain_data["entities"].pop(dev_id)

    domain_data[TUYA_TRACKER] = async_track_time_interval(
        opp, async_poll_devices_update, timedelta(minutes=2))

    @callback
    def _async_cancel_tuya_tracker(event):
        domain_data[TUYA_TRACKER]()

    domain_data[STOP_CANCEL] = opp.bus.async_listen_once(
        EVENT_OPENPEERPOWER_STOP, _async_cancel_tuya_tracker)

    opp.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(opp, SIGNAL_UPDATE_ENTITY)

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

    return True