Exemple #1
0
def async_when_setup(
        hass: core.HomeAssistant, component: str,
        when_setup_cb: Callable[
            [core.HomeAssistant, str], Awaitable[None]]) -> None:
    """Call a method when a component is setup."""
    async def when_setup() -> None:
        """Call the callback."""
        try:
            await when_setup_cb(hass, component)
        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception('Error handling when_setup callback for %s',
                              component)

    # Running it in a new task so that it always runs after
    if component in hass.config.components:
        hass.async_create_task(when_setup())
        return

    unsub = None

    async def loaded_event(event: core.Event) -> None:
        """Call the callback."""
        if event.data[ATTR_COMPONENT] != component:
            return

        unsub()  # type: ignore
        await when_setup()

    unsub = hass.bus.async_listen(EVENT_COMPONENT_LOADED, loaded_event)
def websocket_setup_mfa(
        hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
    """Return a setup flow for mfa auth module."""
    async def async_setup_flow(msg):
        """Return a setup flow for mfa auth module."""
        flow_manager = hass.data[DATA_SETUP_FLOW_MGR]

        flow_id = msg.get('flow_id')
        if flow_id is not None:
            result = await flow_manager.async_configure(
                flow_id, msg.get('user_input'))
            connection.send_message_outside(
                websocket_api.result_message(
                    msg['id'], _prepare_result_json(result)))
            return

        mfa_module_id = msg.get('mfa_module_id')
        mfa_module = hass.auth.get_auth_mfa_module(mfa_module_id)
        if mfa_module is None:
            connection.send_message_outside(websocket_api.error_message(
                msg['id'], 'no_module',
                'MFA module {} is not found'.format(mfa_module_id)))
            return

        result = await flow_manager.async_init(
            mfa_module_id, data={'user_id': connection.user.id})

        connection.send_message_outside(
            websocket_api.result_message(
                msg['id'], _prepare_result_json(result)))

    hass.async_create_task(async_setup_flow(msg))
Exemple #3
0
def async_create(hass: HomeAssistant, message: str, title: str = None,
                 notification_id: str = None) -> None:
    """Generate a notification."""
    data = {
        key: value for key, value in [
            (ATTR_TITLE, title),
            (ATTR_MESSAGE, message),
            (ATTR_NOTIFICATION_ID, notification_id),
        ] if value is not None
    }

    hass.async_create_task(
        hass.services.async_call(DOMAIN, SERVICE_CREATE, data))
Exemple #4
0
async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool:
    """Set up the Elk M1 platform."""
    from elkm1_lib.const import Max
    import elkm1_lib as elkm1

    configs = {
        CONF_AREA: Max.AREAS.value,
        CONF_COUNTER: Max.COUNTERS.value,
        CONF_KEYPAD: Max.KEYPADS.value,
        CONF_OUTPUT: Max.OUTPUTS.value,
        CONF_PLC: Max.LIGHTS.value,
        CONF_SETTING: Max.SETTINGS.value,
        CONF_TASK: Max.TASKS.value,
        CONF_THERMOSTAT: Max.THERMOSTATS.value,
        CONF_ZONE: Max.ZONES.value,
    }

    def _included(ranges, set_to, values):
        for rng in ranges:
            if not rng[0] <= rng[1] <= len(values):
                raise vol.Invalid("Invalid range {}".format(rng))
            values[rng[0]-1:rng[1]] = [set_to] * (rng[1] - rng[0] + 1)

    conf = hass_config[DOMAIN]
    config = {'temperature_unit': conf[CONF_TEMPERATURE_UNIT]}
    config['panel'] = {'enabled': True, 'included': [True]}

    for item, max_ in configs.items():
        config[item] = {'enabled': conf[item][CONF_ENABLED],
                        'included': [not conf[item]['include']] * max_}
        try:
            _included(conf[item]['include'], True, config[item]['included'])
            _included(conf[item]['exclude'], False, config[item]['included'])
        except (ValueError, vol.Invalid) as err:
            _LOGGER.error("Config item: %s; %s", item, err)
            return False

    elk = elkm1.Elk({'url': conf[CONF_HOST], 'userid': conf[CONF_USERNAME],
                     'password': conf[CONF_PASSWORD]})
    elk.connect()

    _create_elk_services(hass, elk)

    hass.data[DOMAIN] = {'elk': elk, 'config': config, 'keypads': {}}
    for component in SUPPORTED_DOMAINS:
        hass.async_create_task(
            discovery.async_load_platform(hass, component, DOMAIN, {},
                                          hass_config))

    return True
Exemple #5
0
def websocket_delete_refresh_token(
        hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
    """Handle a delete refresh token request."""
    async def async_delete_refresh_token(user, refresh_token_id):
        """Delete a refresh token."""
        refresh_token = connection.user.refresh_tokens.get(refresh_token_id)

        if refresh_token is None:
            return websocket_api.error_message(
                msg['id'], 'invalid_token_id', 'Received invalid token')

        await hass.auth.async_remove_refresh_token(refresh_token)

        connection.send_message(
            websocket_api.result_message(msg['id'], {}))

    hass.async_create_task(
        async_delete_refresh_token(connection.user, msg['refresh_token_id']))
Exemple #6
0
def websocket_create_long_lived_access_token(
        hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
    """Create or a long-lived access token."""
    async def async_create_long_lived_access_token(user):
        """Create or a long-lived access token."""
        refresh_token = await hass.auth.async_create_refresh_token(
            user,
            client_name=msg['client_name'],
            client_icon=msg.get('client_icon'),
            token_type=TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN,
            access_token_expiration=timedelta(days=msg['lifespan']))

        access_token = hass.auth.async_create_access_token(
            refresh_token)

        connection.send_message(
            websocket_api.result_message(msg['id'], access_token))

    hass.async_create_task(
        async_create_long_lived_access_token(connection.user))
def websocket_depose_mfa(
        hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
    """Remove user from mfa module."""
    async def async_depose(msg):
        """Remove user from mfa auth module."""
        mfa_module_id = msg['mfa_module_id']
        try:
            await hass.auth.async_disable_user_mfa(
                connection.user, msg['mfa_module_id'])
        except ValueError as err:
            connection.send_message_outside(websocket_api.error_message(
                msg['id'], 'disable_failed',
                'Cannot disable MFA Module {}: {}'.format(
                    mfa_module_id, err)))
            return

        connection.send_message_outside(
            websocket_api.result_message(
                msg['id'], 'done'))

    hass.async_create_task(async_depose(msg))
Exemple #8
0
def websocket_current_user(
        hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
    """Return the current user."""
    async def async_get_current_user(user):
        """Get current user."""
        enabled_modules = await hass.auth.async_get_enabled_mfa(user)

        connection.send_message(
            websocket_api.result_message(msg['id'], {
                'id': user.id,
                'name': user.name,
                'is_owner': user.is_owner,
                'credentials': [{'auth_provider_type': c.auth_provider_type,
                                 'auth_provider_id': c.auth_provider_id}
                                for c in user.credentials],
                'mfa_modules': [{
                    'id': module.id,
                    'name': module.name,
                    'enabled': module.id in enabled_modules,
                } for module in hass.auth.auth_mfa_modules],
            }))

    hass.async_create_task(async_get_current_user(connection.user))
Exemple #9
0
async def async_setup_component(hass: core.HomeAssistant, domain: str,
                                config: Optional[Dict] = None) -> bool:
    """Set up a component and all its dependencies.

    This method is a coroutine.
    """
    if domain in hass.config.components:
        return True

    setup_tasks = hass.data.get(DATA_SETUP)

    if setup_tasks is not None and domain in setup_tasks:
        return await setup_tasks[domain]  # type: ignore

    if config is None:
        config = {}

    if setup_tasks is None:
        setup_tasks = hass.data[DATA_SETUP] = {}

    task = setup_tasks[domain] = hass.async_create_task(
        _async_setup_component(hass, domain, config))

    return await task  # type: ignore
Exemple #10
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)
    if not await gateway.async_connect_gateway(host, token):
        return False
    gateway_info = gateway.gateway_info

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

    device_registry = await dr.async_get_registry(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=timedelta(seconds=10),
    )

    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))

    return True
Exemple #11
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up yi-hack from a config entry."""

    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.entry_id] = entry.data[CONF_MAC]
    hass.data[DOMAIN][entry.data[CONF_NAME]] = None
    hass.data[DOMAIN][entry.data[CONF_NAME] + END_OF_POWER_OFF] = None
    hass.data[DOMAIN][entry.data[CONF_NAME] + END_OF_POWER_ON] = None

    conf = await hass.async_add_executor_job(get_system_conf, entry.data)
    mqtt = await hass.async_add_executor_job(get_mqtt_conf, entry.data)

    if conf is not None and mqtt is not None:
        updated_data = {
            **entry.data,
            CONF_MQTT_PREFIX:
            mqtt[CONF_MQTT_PREFIX],
            CONF_TOPIC_STATUS:
            mqtt[CONF_TOPIC_STATUS],
            CONF_TOPIC_MOTION_DETECTION:
            mqtt[CONF_TOPIC_MOTION_DETECTION],
            CONF_MOTION_START_MSG:
            mqtt[CONF_MOTION_START_MSG],
            CONF_MOTION_STOP_MSG:
            mqtt[CONF_MOTION_STOP_MSG],
            CONF_BIRTH_MSG:
            mqtt[CONF_BIRTH_MSG],
            CONF_WILL_MSG:
            mqtt[CONF_WILL_MSG],
            CONF_TOPIC_MOTION_DETECTION_IMAGE:
            mqtt[CONF_TOPIC_MOTION_DETECTION_IMAGE],
        }
        if (entry.data[CONF_HACK_NAME]
                == DEFAULT_BRAND) or (entry.data[CONF_HACK_NAME] == MSTAR):
            updated_data.update(
                **{
                    CONF_RTSP_PORT: conf[CONF_RTSP_PORT],
                    CONF_TOPIC_BABY_CRYING: mqtt[CONF_TOPIC_BABY_CRYING],
                    CONF_BABY_CRYING_MSG: mqtt[CONF_BABY_CRYING_MSG],
                })
        elif (entry.data[CONF_HACK_NAME]
              == ALLWINNER) or (entry.data[CONF_HACK_NAME]
                                == ALLWINNERV2) or (entry.data[CONF_HACK_NAME]
                                                    == V5):
            updated_data.update(
                **{
                    CONF_RTSP_PORT: conf[CONF_RTSP_PORT],
                    CONF_TOPIC_BABY_CRYING: mqtt[CONF_TOPIC_BABY_CRYING],
                    CONF_TOPIC_SOUND_DETECTION:
                    mqtt[CONF_TOPIC_SOUND_DETECTION],
                    CONF_BABY_CRYING_MSG: mqtt[CONF_BABY_CRYING_MSG],
                    CONF_SOUND_DETECTION_MSG: mqtt[CONF_SOUND_DETECTION_MSG],
                })
        elif entry.data[CONF_HACK_NAME] == SONOFF:
            updated_data.update(**{
                CONF_RTSP_PORT: conf[CONF_RTSP_PORT],
            })

        hass.config_entries.async_update_entry(entry, data=updated_data)

        if (entry.data[CONF_HACK_NAME]
                == SONOFF) or (entry.data[CONF_HACK_NAME] == V5):
            for component in PLATFORMS_NOMEDIA:
                hass.async_create_task(
                    hass.config_entries.async_forward_entry_setup(
                        entry, component))
        else:
            for component in PLATFORMS:
                hass.async_create_task(
                    hass.config_entries.async_forward_entry_setup(
                        entry, component))

        return True
    else:
        _LOGGER.error("Unable to get configuration from the cam")
        return False
Exemple #12
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Hunter Douglas PowerView from a config entry."""

    config = entry.data

    hub_address = config.get(CONF_HOST)
    websession = async_get_clientsession(hass)

    pv_request = AioRequest(hub_address, loop=hass.loop, websession=websession)

    try:
        async with async_timeout.timeout(10):
            device_info = await async_get_device_info(pv_request)
    except HUB_EXCEPTIONS:
        _LOGGER.error("Connection error to PowerView hub: %s", hub_address)
        raise ConfigEntryNotReady
    if not device_info:
        _LOGGER.error("Unable to initialize PowerView hub: %s", hub_address)
        raise ConfigEntryNotReady

    rooms = Rooms(pv_request)
    room_data = _async_map_data_by_id((await rooms.get_resources())[ROOM_DATA])

    scenes = Scenes(pv_request)
    scene_data = _async_map_data_by_id((await scenes.get_resources())[SCENE_DATA])

    shades = Shades(pv_request)
    shade_data = _async_map_data_by_id((await shades.get_resources())[SHADE_DATA])

    async def async_update_data():
        """Fetch data from shade endpoint."""
        async with async_timeout.timeout(10):
            shade_entries = await shades.get_resources()
        if not shade_entries:
            raise UpdateFailed(f"Failed to fetch new shade data.")
        return _async_map_data_by_id(shade_entries[SHADE_DATA])

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name="powerview hub",
        update_method=async_update_data,
        update_interval=timedelta(seconds=60),
    )

    hass.data[DOMAIN][entry.entry_id] = {
        PV_API: pv_request,
        PV_ROOM_DATA: room_data,
        PV_SCENE_DATA: scene_data,
        PV_SHADES: shades,
        PV_SHADE_DATA: shade_data,
        COORDINATOR: coordinator,
        DEVICE_INFO: device_info,
    }

    for component in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component)
        )

    return True
Exemple #13
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up the esphome component."""
    host = entry.data[CONF_HOST]
    port = entry.data[CONF_PORT]
    password = entry.data[CONF_PASSWORD]
    noise_psk = entry.data.get(CONF_NOISE_PSK)
    device_id = None

    zeroconf_instance = await zeroconf.async_get_instance(hass)

    cli = APIClient(
        host,
        port,
        password,
        client_info=f"Home Assistant {const.__version__}",
        zeroconf_instance=zeroconf_instance,
        noise_psk=noise_psk,
    )

    domain_data = DomainData.get(hass)
    entry_data = RuntimeEntryData(
        client=cli,
        entry_id=entry.entry_id,
        store=domain_data.get_or_create_store(hass, entry),
    )
    domain_data.set_entry_data(entry, entry_data)

    async def on_stop(event: Event) -> None:
        """Cleanup the socket client on HA stop."""
        await _cleanup_instance(hass, entry)

    # Use async_listen instead of async_listen_once so that we don't deregister
    # the callback twice when shutting down Home Assistant.
    # "Unable to remove unknown listener <function EventBus.async_listen_once.<locals>.onetime_listener>"
    entry_data.cleanup_callbacks.append(
        hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, on_stop))

    @callback
    def async_on_state(state: EntityState) -> None:
        """Send dispatcher updates when a new state is received."""
        entry_data.async_update_state(hass, state)

    @callback
    def async_on_service_call(service: HomeassistantServiceCall) -> None:
        """Call service when user automation in ESPHome config is triggered."""
        domain, service_name = service.service.split(".", 1)
        service_data = service.data

        if service.data_template:
            try:
                data_template = {
                    key: Template(value)  # type: ignore[no-untyped-call]
                    for key, value in service.data_template.items()
                }
                template.attach(hass, data_template)
                service_data.update(
                    template.render_complex(data_template, service.variables))
            except TemplateError as ex:
                _LOGGER.error("Error rendering data template for %s: %s", host,
                              ex)
                return

        if service.is_event:
            # ESPHome uses servicecall packet for both events and service calls
            # Ensure the user can only send events of form 'esphome.xyz'
            if domain != "esphome":
                _LOGGER.error(
                    "Can only generate events under esphome domain! (%s)",
                    host)
                return

            # Call native tag scan
            if service_name == "tag_scanned":
                tag_id = service_data["tag_id"]
                hass.async_create_task(
                    hass.components.tag.async_scan_tag(tag_id, device_id))
                return

            hass.bus.async_fire(service.service, service_data)
        else:
            hass.async_create_task(
                hass.services.async_call(domain,
                                         service_name,
                                         service_data,
                                         blocking=True))

    async def _send_home_assistant_state(entity_id: str, attribute: str | None,
                                         state: State | None) -> None:
        """Forward Home Assistant states to ESPHome."""
        if state is None or (attribute and attribute not in state.attributes):
            return

        send_state = state.state
        if attribute:
            attr_val = state.attributes[attribute]
            # ESPHome only handles "on"/"off" for boolean values
            if isinstance(attr_val, bool):
                send_state = "on" if attr_val else "off"
            else:
                send_state = attr_val

        await cli.send_home_assistant_state(entity_id, attribute,
                                            str(send_state))

    @callback
    def async_on_state_subscription(entity_id: str,
                                    attribute: str | None = None) -> None:
        """Subscribe and forward states for requested entities."""
        async def send_home_assistant_state_event(event: Event) -> None:
            """Forward Home Assistant states updates to ESPHome."""

            # Only communicate changes to the state or attribute tracked
            if (event.data.get("old_state") is not None
                    and "new_state" in event.data and
                ((not attribute and event.data["old_state"].state
                  == event.data["new_state"].state) or
                 (attribute and attribute in event.data["old_state"].attributes
                  and attribute in event.data["new_state"].attributes
                  and event.data["old_state"].attributes[attribute]
                  == event.data["new_state"].attributes[attribute]))):
                return

            await _send_home_assistant_state(event.data["entity_id"],
                                             attribute,
                                             event.data.get("new_state"))

        unsub = async_track_state_change_event(
            hass, [entity_id], send_home_assistant_state_event)
        entry_data.disconnect_callbacks.append(unsub)

        # Send initial state
        hass.async_create_task(
            _send_home_assistant_state(entity_id, attribute,
                                       hass.states.get(entity_id)))

    async def on_connect() -> None:
        """Subscribe to states and list entities on successful API login."""
        nonlocal device_id
        try:
            entry_data.device_info = await cli.device_info()
            assert cli.api_version is not None
            entry_data.api_version = cli.api_version
            entry_data.available = True
            device_id = _async_setup_device_registry(hass, entry,
                                                     entry_data.device_info)
            entry_data.async_update_device_state(hass)

            entity_infos, services = await cli.list_entities_services()
            await entry_data.async_update_static_infos(hass, entry,
                                                       entity_infos)
            await _setup_services(hass, entry_data, services)
            await cli.subscribe_states(async_on_state)
            await cli.subscribe_service_calls(async_on_service_call)
            await cli.subscribe_home_assistant_states(
                async_on_state_subscription)

            hass.async_create_task(entry_data.async_save_to_store())
        except APIConnectionError as err:
            _LOGGER.warning("Error getting initial data for %s: %s", host, err)
            # Re-connection logic will trigger after this
            await cli.disconnect()

    async def on_disconnect() -> None:
        """Run disconnect callbacks on API disconnect."""
        for disconnect_cb in entry_data.disconnect_callbacks:
            disconnect_cb()
        entry_data.disconnect_callbacks = []
        entry_data.available = False
        entry_data.async_update_device_state(hass)

    async def on_connect_error(err: Exception) -> None:
        """Start reauth flow if appropriate connect error type."""
        if isinstance(
                err,
            (RequiresEncryptionAPIError, InvalidEncryptionKeyAPIError)):
            entry.async_start_reauth(hass)

    reconnect_logic = ReconnectLogic(
        client=cli,
        on_connect=on_connect,
        on_disconnect=on_disconnect,
        zeroconf_instance=zeroconf_instance,
        name=host,
        on_connect_error=on_connect_error,
    )

    async def complete_setup() -> None:
        """Complete the config entry setup."""
        infos, services = await entry_data.async_load_from_store()
        await entry_data.async_update_static_infos(hass, entry, infos)
        await _setup_services(hass, entry_data, services)

        await reconnect_logic.start()
        entry_data.cleanup_callbacks.append(reconnect_logic.stop_callback)

    hass.async_create_task(complete_setup())
    return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Hyperion from a config entry."""
    host = entry.data[CONF_HOST]
    port = entry.data[CONF_PORT]
    token = entry.data.get(CONF_TOKEN)

    hyperion_client = await async_create_connect_hyperion_client(
        host, port, token=token, raw_connection=True
    )

    # Client won't connect? => Not ready.
    if not hyperion_client:
        raise ConfigEntryNotReady
    version = await hyperion_client.async_sysinfo_version()
    if version is not None:
        with suppress(ValueError):
            if AwesomeVersion(version) < AwesomeVersion(HYPERION_VERSION_WARN_CUTOFF):
                _LOGGER.warning(
                    "Using a Hyperion server version < %s is not recommended -- "
                    "some features may be unavailable or may not function correctly. "
                    "Please consider upgrading: %s",
                    HYPERION_VERSION_WARN_CUTOFF,
                    HYPERION_RELEASES_URL,
                )

    # Client needs authentication, but no token provided? => Reauth.
    auth_resp = await hyperion_client.async_is_auth_required()
    if (
        auth_resp is not None
        and client.ResponseOK(auth_resp)
        and auth_resp.get(hyperion_const.KEY_INFO, {}).get(
            hyperion_const.KEY_REQUIRED, False
        )
        and token is None
    ):
        await hyperion_client.async_client_disconnect()
        raise ConfigEntryAuthFailed

    # Client login doesn't work? => Reauth.
    if not await hyperion_client.async_client_login():
        await hyperion_client.async_client_disconnect()
        raise ConfigEntryAuthFailed

    # Cannot switch instance or cannot load state? => Not ready.
    if (
        not await hyperion_client.async_client_switch_instance()
        or not client.ServerInfoResponseOK(await hyperion_client.async_get_serverinfo())
    ):
        await hyperion_client.async_client_disconnect()
        raise ConfigEntryNotReady

    # We need 1 root client (to manage instances being removed/added) and then 1 client
    # per Hyperion server instance which is shared for all entities associated with
    # that instance.
    hass.data[DOMAIN][entry.entry_id] = {
        CONF_ROOT_CLIENT: hyperion_client,
        CONF_INSTANCE_CLIENTS: {},
        CONF_ON_UNLOAD: [],
    }

    async def async_instances_to_clients(response: dict[str, Any]) -> None:
        """Convert instances to Hyperion clients."""
        if not response or hyperion_const.KEY_DATA not in response:
            return
        await async_instances_to_clients_raw(response[hyperion_const.KEY_DATA])

    async def async_instances_to_clients_raw(instances: list[dict[str, Any]]) -> None:
        """Convert instances to Hyperion clients."""
        device_registry = dr.async_get(hass)
        running_instances: set[int] = set()
        stopped_instances: set[int] = set()
        existing_instances = hass.data[DOMAIN][entry.entry_id][CONF_INSTANCE_CLIENTS]
        server_id = cast(str, entry.unique_id)

        # In practice, an instance can be in 3 states as seen by this function:
        #
        #    * Exists, and is running: Should be present in HASS/registry.
        #    * Exists, but is not running: Cannot add it yet, but entity may have be
        #      registered from a previous time it was running.
        #    * No longer exists at all: Should not be present in HASS/registry.

        # Add instances that are missing.
        for instance in instances:
            instance_num = instance.get(hyperion_const.KEY_INSTANCE)
            if instance_num is None:
                continue
            if not instance.get(hyperion_const.KEY_RUNNING, False):
                stopped_instances.add(instance_num)
                continue
            running_instances.add(instance_num)
            if instance_num in existing_instances:
                continue
            hyperion_client = await async_create_connect_hyperion_client(
                host, port, instance=instance_num, token=token
            )
            if not hyperion_client:
                continue
            existing_instances[instance_num] = hyperion_client
            instance_name = instance.get(hyperion_const.KEY_FRIENDLY_NAME, DEFAULT_NAME)
            async_dispatcher_send(
                hass,
                SIGNAL_INSTANCE_ADD.format(entry.entry_id),
                instance_num,
                instance_name,
            )

        # Remove entities that are are not running instances on Hyperion.
        for instance_num in set(existing_instances) - running_instances:
            del existing_instances[instance_num]
            async_dispatcher_send(
                hass, SIGNAL_INSTANCE_REMOVE.format(entry.entry_id), instance_num
            )

        # Ensure every device associated with this config entry is still in the list of
        # motionEye cameras, otherwise remove the device (and thus entities).
        known_devices = {
            get_hyperion_device_id(server_id, instance_num)
            for instance_num in running_instances | stopped_instances
        }
        for device_entry in dr.async_entries_for_config_entry(
            device_registry, entry.entry_id
        ):
            for (kind, key) in device_entry.identifiers:
                if kind == DOMAIN and key in known_devices:
                    break
            else:
                device_registry.async_remove_device(device_entry.id)

    hyperion_client.set_callbacks(
        {
            f"{hyperion_const.KEY_INSTANCE}-{hyperion_const.KEY_UPDATE}": async_instances_to_clients,
        }
    )

    async def setup_then_listen() -> None:
        await asyncio.gather(
            *(
                hass.config_entries.async_forward_entry_setup(entry, platform)
                for platform in PLATFORMS
            )
        )
        assert hyperion_client
        if hyperion_client.instances is not None:
            await async_instances_to_clients_raw(hyperion_client.instances)
        hass.data[DOMAIN][entry.entry_id][CONF_ON_UNLOAD].append(
            entry.add_update_listener(_async_entry_updated)
        )

    hass.async_create_task(setup_then_listen())
    return True
Exemple #15
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up nordpool as config entry."""
    hass.async_create_task(
        hass.config_entries.async_forward_entry_setup(entry, "sensor"))
    return True
Exemple #16
0
async def async_setup_entry(
        hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
    """Set up SMHI forecast as config entry."""
    hass.async_create_task(hass.config_entries.async_forward_entry_setup(
        config_entry, 'weather'))
    return True
async def async_initialize_integration(
    hass: HomeAssistant,
    *,
    config_entry: ConfigEntry | None = None,
    config: dict[str, Any] | None = None,
) -> bool:
    """Initialize the integration"""
    hass.data[DOMAIN] = hacs = HacsBase()
    hacs.enable_hacs()

    if config is not None:
        if DOMAIN not in config:
            return True
        if hacs.configuration.config_type == ConfigurationType.CONFIG_ENTRY:
            return True
        hacs.configuration.update_from_dict(
            {
                "config_type": ConfigurationType.YAML,
                **config[DOMAIN],
                "config": config[DOMAIN],
            }
        )

    if config_entry is not None:
        if config_entry.source == SOURCE_IMPORT:
            hass.async_create_task(hass.config_entries.async_remove(config_entry.entry_id))
            return False

        hacs.configuration.update_from_dict(
            {
                "config_entry": config_entry,
                "config_type": ConfigurationType.CONFIG_ENTRY,
                **config_entry.data,
                **config_entry.options,
            }
        )

    integration = await async_get_integration(hass, DOMAIN)

    hacs.set_stage(None)

    hacs.log.info(STARTUP, integration.version)

    clientsession = async_get_clientsession(hass)

    hacs.integration = integration
    hacs.version = integration.version
    hacs.configuration.dev = integration.version == "0.0.0"
    hacs.hass = hass
    hacs.queue = QueueManager(hass=hass)
    hacs.data = HacsData(hacs=hacs)
    hacs.system.running = True
    hacs.session = clientsession

    hacs.core.lovelace_mode = LovelaceMode.YAML
    try:
        lovelace_info = await system_health_info(hacs.hass)
        hacs.core.lovelace_mode = LovelaceMode(lovelace_info.get("mode", "yaml"))
    except BaseException:  # lgtm [py/catch-base-exception] pylint: disable=broad-except
        # If this happens, the users YAML is not valid, we assume YAML mode
        pass
    hacs.log.debug("Configuration type: %s", hacs.configuration.config_type)
    hacs.core.config_path = hacs.hass.config.path()

    if hacs.core.ha_version is None:
        hacs.core.ha_version = AwesomeVersion(HAVERSION)

    ## Legacy GitHub client
    hacs.github = GitHub(
        hacs.configuration.token,
        clientsession,
        headers={
            "User-Agent": f"HACS/{hacs.version}",
            "Accept": ACCEPT_HEADERS["preview"],
        },
    )

    ## New GitHub client
    hacs.githubapi = GitHubAPI(
        token=hacs.configuration.token,
        session=clientsession,
        **{"client_name": f"HACS/{hacs.version}"},
    )

    async def async_startup():
        """HACS startup tasks."""
        hacs.enable_hacs()

        for location in (
            hass.config.path("custom_components/custom_updater.py"),
            hass.config.path("custom_components/custom_updater/__init__.py"),
        ):
            if os.path.exists(location):
                hacs.log.critical(
                    "This cannot be used with custom_updater. "
                    "To use this you need to remove custom_updater form %s",
                    location,
                )

                hacs.disable_hacs(HacsDisabledReason.CONSTRAINS)
                return False

        if not version_left_higher_or_equal_then_right(
            hacs.core.ha_version.string,
            MINIMUM_HA_VERSION,
        ):
            hacs.log.critical(
                "You need HA version %s or newer to use this integration.",
                MINIMUM_HA_VERSION,
            )
            hacs.disable_hacs(HacsDisabledReason.CONSTRAINS)
            return False

        if not await hacs.data.restore():
            hacs.disable_hacs(HacsDisabledReason.RESTORE)
            return False

        can_update = await hacs.async_can_update()
        hacs.log.debug("Can update %s repositories", can_update)

        hacs.set_active_categories()

        async_register_websocket_commands(hass)
        async_register_frontend(hass, hacs)

        if hacs.configuration.config_type == ConfigurationType.YAML:
            hass.async_create_task(
                async_load_platform(hass, Platform.SENSOR, DOMAIN, {}, hacs.configuration.config)
            )
            hacs.log.info("Update entities are only supported when using UI configuration")

        else:
            if hacs.configuration.experimental:
                hass.config_entries.async_setup_platforms(
                    hacs.configuration.config_entry, [Platform.SENSOR, Platform.UPDATE]
                )
            else:
                hass.config_entries.async_setup_platforms(
                    hacs.configuration.config_entry, [Platform.SENSOR]
                )

        hacs.set_stage(HacsStage.SETUP)
        if hacs.system.disabled:
            return False

        # Schedule startup tasks
        async_at_start(hass=hass, at_start_cb=hacs.startup_tasks)

        hacs.set_stage(HacsStage.WAITING)
        hacs.log.info("Setup complete, waiting for Home Assistant before startup tasks starts")

        return not hacs.system.disabled

    async def async_try_startup(_=None):
        """Startup wrapper for yaml config."""
        try:
            startup_result = await async_startup()
        except AIOGitHubAPIException:
            startup_result = False
        if not startup_result:
            if (
                hacs.configuration.config_type == ConfigurationType.YAML
                or hacs.system.disabled_reason != HacsDisabledReason.INVALID_TOKEN
            ):
                hacs.log.info("Could not setup HACS, trying again in 15 min")
                async_call_later(hass, 900, async_try_startup)
            return
        hacs.enable_hacs()

    await async_try_startup()

    # Mischief managed!
    return True
Exemple #18
0
async def async_from_config_dict(config: Dict[str, Any],
                                 hass: core.HomeAssistant,
                                 config_dir: Optional[str] = None,
                                 enable_log: bool = True,
                                 verbose: bool = False,
                                 skip_pip: bool = False,
                                 log_rotate_days: Any = None,
                                 log_file: Any = None,
                                 log_no_color: bool = False) \
                           -> Optional[core.HomeAssistant]:
    """Try to configure Home Assistant from a configuration dictionary.

    Dynamically loads required components and its dependencies.
    This method is a coroutine.
    """
    start = time()

    if enable_log:
        async_enable_logging(hass, verbose, log_rotate_days, log_file,
                             log_no_color)

    core_config = config.get(core.DOMAIN, {})

    try:
        await conf_util.async_process_ha_core_config(hass, core_config)
    except vol.Invalid as ex:
        conf_util.async_log_exception(ex, 'homeassistant', core_config, hass)
        return None

    await hass.async_add_executor_job(
        conf_util.process_ha_config_upgrade, hass)

    hass.config.skip_pip = skip_pip
    if skip_pip:
        _LOGGER.warning("Skipping pip installation of required modules. "
                        "This may cause issues")

    # Make a copy because we are mutating it.
    config = OrderedDict(config)

    # Merge packages
    conf_util.merge_packages_config(
        hass, config, core_config.get(conf_util.CONF_PACKAGES, {}))

    # Ensure we have no None values after merge
    for key, value in config.items():
        if not value:
            config[key] = {}

    hass.config_entries = config_entries.ConfigEntries(hass, config)
    await hass.config_entries.async_load()

    # Filter out the repeating and common config section [homeassistant]
    components = set(key.split(' ')[0] for key in config.keys()
                     if key != core.DOMAIN)
    components.update(hass.config_entries.async_domains())

    # setup components
    res = await core_components.async_setup(hass, config)
    if not res:
        _LOGGER.error("Home Assistant core failed to initialize. "
                      "further initialization aborted")
        return hass

    await persistent_notification.async_setup(hass, config)

    _LOGGER.info("Home Assistant core initialized")

    # stage 1
    for component in components:
        if component not in FIRST_INIT_COMPONENT:
            continue
        hass.async_create_task(async_setup_component(hass, component, config))

    await hass.async_block_till_done()

    # stage 2
    for component in components:
        if component in FIRST_INIT_COMPONENT:
            continue
        hass.async_create_task(async_setup_component(hass, component, config))

    await hass.async_block_till_done()

    stop = time()
    _LOGGER.info("Home Assistant initialized in %.2fs", stop-start)

    async_register_signal_handling(hass)
    return hass
Exemple #19
0
def async_setup_scanner_platform(
    hass: HomeAssistant,
    config: ConfigType,
    scanner: DeviceScanner,
    async_see_device: Callable[..., Coroutine[None, None, None]],
    platform: str,
) -> None:
    """Set up the connect scanner-based platform to device tracker.

    This method must be run in the event loop.
    """
    interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
    update_lock = asyncio.Lock()
    scanner.hass = hass

    # Initial scan of each mac we also tell about host name for config
    seen: Any = set()

    async def async_device_tracker_scan(
            now: dt_util.dt.datetime | None) -> None:
        """Handle interval matches."""
        if update_lock.locked():
            LOGGER.warning(
                "Updating device list from %s took longer than the scheduled "
                "scan interval %s",
                platform,
                interval,
            )
            return

        async with update_lock:
            found_devices = await scanner.async_scan_devices()

        for mac in found_devices:
            if mac in seen:
                host_name = None
            else:
                host_name = await scanner.async_get_device_name(mac)
                seen.add(mac)

            try:
                extra_attributes = await scanner.async_get_extra_attributes(mac
                                                                            )
            except NotImplementedError:
                extra_attributes = {}

            kwargs: dict[str, Any] = {
                "mac": mac,
                "host_name": host_name,
                "source_type": SOURCE_TYPE_ROUTER,
                "attributes": {
                    "scanner": scanner.__class__.__name__,
                    **extra_attributes,
                },
            }

            zone_home = hass.states.get(hass.components.zone.ENTITY_ID_HOME)
            if zone_home is not None:
                kwargs["gps"] = [
                    zone_home.attributes[ATTR_LATITUDE],
                    zone_home.attributes[ATTR_LONGITUDE],
                ]
                kwargs["gps_accuracy"] = 0

            hass.async_create_task(async_see_device(**kwargs))

    async_track_time_interval(hass, async_device_tracker_scan, interval)
    hass.async_create_task(async_device_tracker_scan(None))
async def async_setup_entry_gw(hass: HomeAssistant,
                               entry: ConfigEntry) -> bool:
    """Set up Plugwise Smiles from a config entry."""
    websession = async_get_clientsession(hass, verify_ssl=False)

    api = Smile(
        host=entry.data[CONF_HOST],
        username=entry.data.get(CONF_USERNAME, DEFAULT_USERNAME),
        password=entry.data[CONF_PASSWORD],
        port=entry.data.get(CONF_PORT, DEFAULT_PORT),
        timeout=30,
        websession=websession,
    )

    try:
        connected = await api.connect()

        if not connected:
            _LOGGER.error("Unable to connect to Smile")
            raise ConfigEntryNotReady

    except InvalidAuthentication:
        _LOGGER.error("Invalid username or Smile ID")
        return False

    except PlugwiseException as err:
        _LOGGER.error("Error while communicating to device %s", api.smile_name)
        raise ConfigEntryNotReady from err

    except asyncio.TimeoutError as err:
        _LOGGER.error("Timeout while connecting to Smile %s", api.smile_name)
        raise ConfigEntryNotReady from err

    update_interval = timedelta(seconds=entry.options.get(
        CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL[api.smile_type]))

    async def async_update_data():
        """Update data via API endpoint."""
        try:
            async with async_timeout.timeout(DEFAULT_TIMEOUT):
                await api.full_update_device()
                return True
        except XMLDataMissingError as err:
            raise UpdateFailed("Smile update failed") from err

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name=f"Smile {api.smile_name}",
        update_method=async_update_data,
        update_interval=update_interval,
    )

    await coordinator.async_refresh()

    if not coordinator.last_update_success:
        raise ConfigEntryNotReady

    api.get_all_devices()

    if entry.unique_id is None:
        if api.smile_version[0] != "1.8.0":
            hass.config_entries.async_update_entry(
                entry, unique_id=api.smile_hostname)

    undo_listener = entry.add_update_listener(_update_listener)

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
        "api": api,
        COORDINATOR: coordinator,
        PW_TYPE: GATEWAY,
        UNDO_UPDATE_LISTENER: undo_listener,
    }

    device_registry = await dr.async_get_registry(hass)
    device_registry.async_get_or_create(
        config_entry_id=entry.entry_id,
        identifiers={(DOMAIN, api.gateway_id)},
        manufacturer="Plugwise",
        name=entry.title,
        model=f"Smile {api.smile_name}",
        sw_version=api.smile_version[0],
    )

    single_master_thermostat = api.single_master_thermostat()

    platforms = PLATFORMS_GATEWAY
    if single_master_thermostat is None:
        platforms = SENSOR_PLATFORMS

    for component in platforms:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component))

    return True
Exemple #21
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up TDAmeritrade from a config entry."""
    _LOGGER.debug("Setting up entry")
    config_flow.OAuth2FlowHandler.async_register_implementation(
        hass,
        config_entry_oauth2_flow.LocalOAuth2Implementation(
            hass,
            entry.domain,
            entry.data["consumer_key"],
            None,
            OAUTH2_AUTHORIZE,
            OAUTH2_TOKEN,
        ),
    )

    implementation = (
        await config_entry_oauth2_flow.async_get_config_entry_implementation(
            hass, entry))

    session = config_entry_oauth2_flow.OAuth2Session(hass, entry,
                                                     implementation)

    auth = api.AsyncConfigEntryAuth(
        aiohttp_client.async_get_clientsession(hass), session)

    client = AmeritradeAPI(auth)

    async def place_order_service(call):
        """Handle a place trade service call."""

        price = call.data["price"]
        instruction = call.data["instruction"]
        quantity = call.data["quantity"]
        symbol = call.data["symbol"]
        account_id = call.data["account_id"]
        order_type = call.data["order_type"]
        session = call.data["session"]
        duration = call.data["duration"]
        order_strategy_type = call.data["orderStrategyType"]
        asset_type = call.data["assetType"]

        return await client.async_place_order(
            price,
            instruction,
            quantity,
            symbol,
            account_id,
            order_type=order_type,
            session=session,
            duration=duration,
            orderStrategyType=order_strategy_type,
            assetType=asset_type,
        )

    async def get_quote_service(call):
        """Handle a place trade service call."""
        symbol = call.data["symbol"]
        res = await client.async_get_quote(ticker=symbol)

        hass.states.async_set(
            f"get_quote_service.{symbol}",
            res[symbol]["lastPrice"],
            attributes=res[symbol],
        )

        return True

    _LOGGER.debug("Registering Services")
    hass.services.async_register(DOMAIN, "place_order", place_order_service)
    hass.services.async_register(DOMAIN, "get_quote", get_quote_service)

    hass.data.setdefault(DOMAIN, {})
    hass_data = dict(entry.data)
    entry.update_listeners = []
    hass_data["unsub"] = entry.add_update_listener(options_update_listener)
    hass_data["client"] = AmeritradeAPI(auth)
    hass.data[DOMAIN][entry.entry_id] = hass_data
    if entry.state == ConfigEntryState.NOT_LOADED:
        for component in PLATFORMS:
            _LOGGER.debug("Setting up %s component", component)
            hass.async_create_task(
                hass.config_entries.async_forward_entry_setup(
                    entry, component))
    return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    for component in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component))

    return True
Exemple #23
0
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
    autolog("<<<")

    global UPDATE_UNLISTENER
    if UPDATE_UNLISTENER:
        UPDATE_UNLISTENER()

    if not config_entry.unique_id:
        hass.config_entries.async_update_entry(
            config_entry, unique_id=config_entry.title
        )

    config = {}
    for key, value in config_entry.data.items():
        config[key] = value
    for key, value in config_entry.options.items():
        config[key] = value
    if config_entry.options:
        hass.config_entries.async_update_entry(config_entry, data=config, options={})

    UPDATE_UNLISTENER = config_entry.add_update_listener(_update_listener)

    hass.data[DOMAIN][config.get(CONF_URL)] = {}
    _jelly = JellyfinClientManager(hass, config)
    try:
        await _jelly.connect()
        hass.data[DOMAIN][config.get(CONF_URL)]["manager"] = _jelly
    except:
        _LOGGER.error("Cannot connect to Jellyfin server.")
        raise ConfigEntryNotReady

    async def async_service_handler(service):
        """Map services to methods"""
        method = SERVICE_TO_METHOD.get(service.service)
        params = {key: value for key, value in service.data.items() if key != "entity_id"}

        entity_id = service.data.get(ATTR_ENTITY_ID)

        for sensor in hass.data[DOMAIN][config.get(CONF_URL)]["sensor"]["entities"]:
            if sensor.entity_id == entity_id:
                await getattr(sensor, method['method'])(**params)

        for media_player in hass.data[DOMAIN][config.get(CONF_URL)]["media_player"]["entities"]:
            if media_player.entity_id == entity_id:
                await getattr(media_player, method['method'])(**params)

    for my_service in SERVICE_TO_METHOD:
        schema = SERVICE_TO_METHOD[my_service].get('schema', SERVICE_SCHEMA)
        hass.services.async_register(
            DOMAIN, my_service, async_service_handler, schema=schema)

    await _jelly.start()

    for platform in PLATFORMS:
        hass.data[DOMAIN][config.get(CONF_URL)][platform] = {}
        hass.data[DOMAIN][config.get(CONF_URL)][platform]["entities"] = []
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(config_entry, platform)
        )

    async_dispatcher_send(hass, SIGNAL_STATE_UPDATED)

    async def stop_jellyfin(event):
        """Stop Jellyfin connection."""
        await _jelly.stop()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_jellyfin)
    
    return True
Exemple #24
0
async def async_setup(hass: HomeAssistant, base_config: dict):
    _LOGGER.info(f'Используется версия модуля mosportal: {pkg_resources.get_distribution("mosportal").version}')
    config = base_config[DOMAIN]
    _LOGGER.debug("настройка компонента моспортал")
    client = PortalWrap(
        hass,
        Session(
            config[CONF_USERNAME],
            config[CONF_PASSWORD],
            cookie_save_path=join(dirname(abspath(__file__)), '..', '..', '.storage')
        ),
        config[CONF_FLAT],
        config[CONF_PAYCODE]
    )

    hass.data[DOMAIN] = client

    meter_list = await client.fetch_data()

    if meter_list:
        _LOGGER.debug("счетчики получены")
        hass.async_create_task(
            async_load_platform(
                hass,
                SENSOR_DOMAIN,
                DOMAIN,
                discovered={meter.meter_id: meter.name for meter in meter_list.values()},
                hass_config=config,
            )
        )

    async def trigger_get_epd_service(call):
        try:
            month = call.data.get('month', datetime.now().month)
            year = call.data.get('year', datetime.now().year)
            data = call.data.get('data', {})

            await hass.async_add_executor_job(
                client.get_epd_service, month, year, data
            )

        except BaseException as e:
            _LOGGER.exception(f'ошибка постановки задачи {e}')

    async def publish_water_usage(call):
        try:
            if 'meter_list_to_update' not in call.data:
                _LOGGER.error('переданы не корректные данные на вход в сервис')
                return

            meter_list_to_update = {
                item['meter_id']: item for item in call.data['meter_list_to_update']
            }
            await hass.async_add_executor_job(
                client.publish_water_usage, meter_list_to_update
            )

        except BaseException as e:
            _LOGGER.exception(f'ошибка постановки задачи {e}')

    hass.services.async_register(DOMAIN, 'get_epd', trigger_get_epd_service)
    hass.services.async_register(DOMAIN, 'publish_water_usage', publish_water_usage)

    return True
Exemple #25
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Elk-M1 Control from a config entry."""

    conf = entry.data

    _LOGGER.debug("Setting up elkm1 %s", conf["host"])

    temperature_unit = TEMP_FAHRENHEIT
    if conf[CONF_TEMPERATURE_UNIT] in (BARE_TEMP_CELSIUS, TEMP_CELSIUS):
        temperature_unit = TEMP_CELSIUS

    config = {"temperature_unit": temperature_unit}

    if not conf[CONF_AUTO_CONFIGURE]:
        # With elkm1-lib==0.7.16 and later auto configure is available
        config["panel"] = {"enabled": True, "included": [True]}
        for item, max_ in ELK_ELEMENTS.items():
            config[item] = {
                "enabled": conf[item][CONF_ENABLED],
                "included": [not conf[item]["include"]] * max_,
            }
            try:
                _included(conf[item]["include"], True,
                          config[item]["included"])
                _included(conf[item]["exclude"], False,
                          config[item]["included"])
            except (ValueError, vol.Invalid) as err:
                _LOGGER.error("Config item: %s; %s", item, err)
                return False

    elk = elkm1.Elk({
        "url": conf[CONF_HOST],
        "userid": conf[CONF_USERNAME],
        "password": conf[CONF_PASSWORD],
    })
    elk.connect()

    if not await async_wait_for_elk_to_sync(elk, SYNC_TIMEOUT):
        _LOGGER.error(
            "Timed out after %d seconds while trying to sync with ElkM1 at %s",
            SYNC_TIMEOUT,
            conf[CONF_HOST],
        )
        elk.disconnect()
        raise ConfigEntryNotReady

    if elk.invalid_auth:
        _LOGGER.error("Authentication failed for ElkM1")
        return False

    hass.data[DOMAIN][entry.entry_id] = {
        "elk": elk,
        "prefix": conf[CONF_PREFIX],
        "auto_configure": conf[CONF_AUTO_CONFIGURE],
        "config": config,
        "keypads": {},
    }

    for component in SUPPORTED_DOMAINS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component))

    return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Scheduler integration from a config entry."""
    session = async_get_clientsession(hass)
    store = await async_get_registry(hass)
    coordinator = SchedulerCoordinator(hass, session, entry, store)

    device_registry = await dr.async_get_registry(hass)
    device_registry.async_get_or_create(
        config_entry_id=entry.entry_id,
        identifiers={(const.DOMAIN, coordinator.id)},
        name="Scheduler",
        model="Scheduler",
        sw_version=const.VERSION,
        manufacturer="@nielsfaber",
    )

    hass.data.setdefault(const.DOMAIN, {})
    hass.data[const.DOMAIN] = {"coordinator": coordinator, "schedules": {}}

    if entry.unique_id is None:
        hass.config_entries.async_update_entry(entry, unique_id=coordinator.id)

    hass.async_create_task(
        hass.config_entries.async_forward_entry_setup(entry, PLATFORM))

    await async_register_websockets(hass)

    def service_create_schedule(service):
        coordinator.async_create_schedule(dict(service.data))

    hass.services.async_register(const.DOMAIN,
                                 const.SERVICE_ADD,
                                 service_create_schedule,
                                 schema=const.SCHEDULE_SCHEMA)

    async def async_service_edit_schedule(service):
        match = None
        for (schedule_id,
             entity) in hass.data[const.DOMAIN]["schedules"].items():
            if entity.entity_id == service.data[const.ATTR_ENTITY_ID]:
                match = schedule_id
                continue
        if not match:
            raise vol.Invalid("Entity not found: {}".format(
                service.data[const.ATTR_ENTITY_ID]))
        else:
            data = dict(service.data)
            del data[const.ATTR_ENTITY_ID]
            await coordinator.async_edit_schedule(match, data)

    hass.services.async_register(const.DOMAIN,
                                 const.SERVICE_EDIT,
                                 async_service_edit_schedule,
                                 schema=const.SCHEDULE_SCHEMA.extend(
                                     {vol.Required(ATTR_ENTITY_ID):
                                      cv.string}))

    async def async_service_remove_schedule(service):
        match = None
        for (schedule_id,
             entity) in hass.data[const.DOMAIN]["schedules"].items():
            if entity.entity_id == service.data["entity_id"]:
                match = schedule_id
                continue
        if not match:
            raise vol.Invalid("Entity not found: {}".format(
                service.data["entity_id"]))
        else:
            await coordinator.async_delete_schedule(match)

    hass.services.async_register(
        const.DOMAIN,
        const.SERVICE_REMOVE,
        async_service_remove_schedule,
        schema=vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.string}))

    return True
Exemple #27
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Plugwise Smiles from a config entry."""
    websession = async_get_clientsession(hass, verify_ssl=False)
    api = Smile(
        host=entry.data["host"], password=entry.data["password"], websession=websession
    )

    try:
        connected = await api.connect()

        if not connected:
            _LOGGER.error("Unable to connect to Smile")
            raise ConfigEntryNotReady

    except Smile.InvalidAuthentication:
        _LOGGER.error("Invalid Smile ID")
        return False

    except Smile.PlugwiseError:
        _LOGGER.error("Error while communicating to device")
        raise ConfigEntryNotReady

    except asyncio.TimeoutError:
        _LOGGER.error("Timeout while connecting to Smile")
        raise ConfigEntryNotReady

    if api.smile_type == "power":
        update_interval = timedelta(seconds=10)
    else:
        update_interval = timedelta(seconds=60)

    async def async_update_data():
        """Update data via API endpoint."""
        try:
            async with async_timeout.timeout(10):
                await api.full_update_device()
                return True
        except Smile.XMLDataMissingError:
            raise UpdateFailed("Smile update failed")

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name="Smile",
        update_method=async_update_data,
        update_interval=update_interval,
    )

    await coordinator.async_refresh()

    if not coordinator.last_update_success:
        raise ConfigEntryNotReady

    api.get_all_devices()

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

    device_registry = await dr.async_get_registry(hass)
    device_registry.async_get_or_create(
        config_entry_id=entry.entry_id,
        identifiers={(DOMAIN, api.gateway_id)},
        manufacturer="Plugwise",
        name=entry.title,
        model=f"Smile {api.smile_name}",
        sw_version=api.smile_version[0],
    )

    platforms = ALL_PLATFORMS

    single_master_thermostat = api.single_master_thermostat()
    if single_master_thermostat is None:
        platforms = SENSOR_PLATFORMS

    for component in platforms:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component)
        )

    return True
Exemple #28
0
async def async_setup_entry(hass: HomeAssistant,
                            config_entry: ConfigEntry) -> bool:
    """Do setup of vera."""
    # Use options entered during initial config flow or provided from configuration.yml
    if config_entry.data.get(CONF_LIGHTS) or config_entry.data.get(
            CONF_EXCLUDE):
        hass.config_entries.async_update_entry(
            entry=config_entry,
            data=config_entry.data,
            options=new_options(
                config_entry.data.get(CONF_LIGHTS, []),
                config_entry.data.get(CONF_EXCLUDE, []),
            ),
        )

    base_url = config_entry.data[CONF_CONTROLLER]
    light_ids = config_entry.options.get(CONF_LIGHTS, [])
    exclude_ids = config_entry.options.get(CONF_EXCLUDE, [])

    # Initialize the Vera controller.
    controller = veraApi.VeraController(base_url)
    controller.start()

    hass.bus.async_listen_once(
        EVENT_HOMEASSISTANT_STOP,
        lambda event: hass.async_add_executor_job(controller.stop),
    )

    try:
        all_devices = await hass.async_add_executor_job(controller.get_devices)

        all_scenes = await hass.async_add_executor_job(controller.get_scenes)
    except RequestException:
        # There was a network related error connecting to the Vera controller.
        _LOGGER.exception("Error communicating with Vera API")
        return False

    # Exclude devices unwanted by user.
    devices = [
        device for device in all_devices if device.device_id not in exclude_ids
    ]

    vera_devices = defaultdict(list)
    for device in devices:
        device_type = map_vera_device(device, light_ids)
        if device_type is None:
            continue

        vera_devices[device_type].append(device)

    vera_scenes = []
    for scene in all_scenes:
        vera_scenes.append(scene)

    controller_data = ControllerData(controller=controller,
                                     devices=vera_devices,
                                     scenes=vera_scenes)

    hass.data[DOMAIN] = controller_data

    # Forward the config data to the necessary platforms.
    for platform in get_configured_platforms(controller_data):
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, platform))

    return True
Exemple #29
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """
    Set up Strava Home Assistant config entry initiated through the HASS-UI.
    """

    hass.data.setdefault(DOMAIN, {})

    # OAuth Stuff
    try:
        implementation = await config_entry_oauth2_flow.async_get_config_entry_implementation(
            hass=hass, config_entry=entry
        )
    except ValueError:
        implementation = config_entry_oauth2_flow.LocalOAuth2Implementation(
            hass,
            DOMAIN,
            entry.data[CONF_CLIENT_ID],
            entry.data[CONF_CLIENT_SECRET],
            OAUTH2_AUTHORIZE,
            OAUTH2_TOKEN,
        )

        OAuth2FlowHandler.async_register_implementation(hass, implementation)

    oauth_websession = config_entry_oauth2_flow.OAuth2Session(
        hass, entry, implementation
    )

    await oauth_websession.async_ensure_token_valid()

    # webhook view to get notifications for strava activity updates
    def strava_update_event_factory(data, event_type=CONF_STRAVA_DATA_UPDATE_EVENT):
        hass.bus.fire(event_type, data)

    strava_webhook_view = StravaWebhookView(
        oauth_websession=oauth_websession,
        event_factory=strava_update_event_factory,
        host=get_url(hass, allow_internal=False, allow_ip=False),
        hass=hass,
    )

    hass.http.register_view(strava_webhook_view)

    # event listeners
    async def strava_startup_functions():
        await renew_webhook_subscription(
            hass=hass, entry=entry, webhook_view=strava_webhook_view
        )
        await strava_webhook_view.fetch_strava_data()
        return True

    def ha_start_handler(event):
        """
        called when HA rebooted
        i.e. after all webhook views have been registered and are available
        """
        hass.async_create_task(strava_startup_functions())

    def component_reload_handler(event):
        """called when the component reloads"""
        hass.async_create_task(strava_startup_functions())

    async def async_strava_config_update_handler():
        """called when user changes sensor configs"""
        await strava_webhook_view.fetch_strava_data()
        return

    def strava_config_update_handler(event):
        hass.async_create_task(async_strava_config_update_handler())

    def core_config_update_handler(event):
        """
        handles relevant changes to the HA core config.
        In particular, for URL and Unit System changes
        """
        if "external_url" in event.data.keys():
            hass.async_create_task(
                renew_webhook_subscription(
                    hass=hass, entry=entry, webhook_view=strava_webhook_view
                )
            )
        if "unit_system" in event.data.keys():
            hass.async_create_task(strava_webhook_view.fetch_strava_data())

    # register event listeners
    hass.data[DOMAIN]["remove_update_listener"] = []

    # if hass.bus.async_listeners().get(EVENT_HOMEASSISTANT_START, 0) < 1:
    hass.data[DOMAIN]["remove_update_listener"].append(
        hass.bus.async_listen(EVENT_HOMEASSISTANT_START, ha_start_handler)
    )

    # if hass.bus.async_listeners().get(EVENT_CORE_CONFIG_UPDATE, 0) < 1:
    hass.data[DOMAIN]["remove_update_listener"].append(
        hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, core_config_update_handler)
    )

    if hass.bus.async_listeners().get(CONF_STRAVA_RELOAD_EVENT, 0) < 1:
        hass.data[DOMAIN]["remove_update_listener"].append(
            hass.bus.async_listen(CONF_STRAVA_RELOAD_EVENT, component_reload_handler)
        )

    if hass.bus.async_listeners().get(CONF_STRAVA_CONFIG_UPDATE_EVENT, 0) < 1:
        hass.data[DOMAIN]["remove_update_listener"].append(
            hass.bus.async_listen(
                CONF_STRAVA_CONFIG_UPDATE_EVENT, strava_config_update_handler
            )
        )

    hass.data[DOMAIN]["remove_update_listener"] = [
        entry.add_update_listener(strava_config_update_helper)
    ]

    for component in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component)
        )

    return True
Exemple #30
0
async def async_setup_entry(  # noqa: C901
        hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up ozw from a config entry."""
    hass.data.setdefault(DOMAIN, {})
    ozw_data = hass.data[DOMAIN][entry.entry_id] = {}
    ozw_data[DATA_UNSUBSCRIBE] = []

    data_nodes = {}
    hass.data[DOMAIN][NODES_VALUES] = data_values = {}
    removed_nodes = []
    manager_options = {"topic_prefix": f"{TOPIC_OPENZWAVE}/"}

    if entry.unique_id is None:
        hass.config_entries.async_update_entry(entry, unique_id=DOMAIN)

    if entry.data.get(CONF_USE_ADDON):
        # Do not use MQTT integration. Use own MQTT client.
        # Retrieve discovery info from the OpenZWave add-on.
        discovery_info = await hass.components.hassio.async_get_addon_discovery_info(
            "core_zwave")

        if not discovery_info:
            _LOGGER.error("Failed to get add-on discovery info")
            raise ConfigEntryNotReady

        discovery_info_config = discovery_info["config"]

        host = discovery_info_config["host"]
        port = discovery_info_config["port"]
        username = discovery_info_config["username"]
        password = discovery_info_config["password"]
        mqtt_client = MQTTClient(host,
                                 port,
                                 username=username,
                                 password=password)
        manager_options["send_message"] = mqtt_client.send_message

    else:
        mqtt_entries = hass.config_entries.async_entries("mqtt")
        if not mqtt_entries or mqtt_entries[
                0].state is not ConfigEntryState.LOADED:
            _LOGGER.error("MQTT integration is not set up")
            return False

        mqtt_entry = mqtt_entries[0]  # MQTT integration only has one entry.

        @callback
        def send_message(topic, payload):
            if mqtt_entry.state is not ConfigEntryState.LOADED:
                _LOGGER.error("MQTT integration is not set up")
                return

            mqtt.async_publish(hass, topic, json.dumps(payload))

        manager_options["send_message"] = send_message

    options = OZWOptions(**manager_options)
    manager = OZWManager(options)

    hass.data[DOMAIN][MANAGER] = manager

    @callback
    def async_node_added(node):
        # Caution: This is also called on (re)start.
        _LOGGER.debug("[NODE ADDED] node_id: %s", node.id)
        data_nodes[node.id] = node
        if node.id not in data_values:
            data_values[node.id] = []

    @callback
    def async_node_changed(node):
        _LOGGER.debug("[NODE CHANGED] node_id: %s", node.id)
        data_nodes[node.id] = node
        # notify devices about the node change
        if node.id not in removed_nodes:
            hass.async_create_task(async_handle_node_update(hass, node))

    @callback
    def async_node_removed(node):
        _LOGGER.debug("[NODE REMOVED] node_id: %s", node.id)
        data_nodes.pop(node.id)
        # node added/removed events also happen on (re)starts of hass/mqtt/ozw
        # cleanup device/entity registry if we know this node is permanently deleted
        # entities itself are removed by the values logic
        if node.id in removed_nodes:
            hass.async_create_task(async_handle_remove_node(hass, node))
            removed_nodes.remove(node.id)

    @callback
    def async_instance_event(message):
        event = message["event"]
        event_data = message["data"]
        _LOGGER.debug("[INSTANCE EVENT]: %s - data: %s", event, event_data)
        # The actual removal action of a Z-Wave node is reported as instance event
        # Only when this event is detected we cleanup the device and entities from hass
        # Note: Find a more elegant way of doing this, e.g. a notification of this event from OZW
        if event in ("removenode",
                     "removefailednode") and "Node" in event_data:
            removed_nodes.append(event_data["Node"])

    @callback
    def async_value_added(value):
        node = value.node
        # Clean up node.node_id and node.id use. They are the same.
        node_id = value.node.node_id

        # Filter out CommandClasses we're definitely not interested in.
        if value.command_class in (CommandClass.MANUFACTURER_SPECIFIC, ):
            return

        _LOGGER.debug(
            "[VALUE ADDED] node_id: %s - label: %s - value: %s - value_id: %s - CC: %s",
            value.node.id,
            value.label,
            value.value,
            value.value_id_key,
            value.command_class,
        )

        node_data_values = data_values[node_id]

        # Check if this value should be tracked by an existing entity
        value_unique_id = create_value_id(value)
        for values in node_data_values:
            values.async_check_value(value)
            if values.values_id == value_unique_id:
                return  # this value already has an entity

        # Run discovery on it and see if any entities need created
        for schema in DISCOVERY_SCHEMAS:
            if not check_node_schema(node, schema):
                continue
            if not check_value_schema(
                    value, schema[const.DISC_VALUES][const.DISC_PRIMARY]):
                continue

            values = ZWaveDeviceEntityValues(hass, options, schema, value)
            values.async_setup()

            # This is legacy and can be cleaned up since we are in the main thread:
            # We create a new list and update the reference here so that
            # the list can be safely iterated over in the main thread
            data_values[node_id] = node_data_values + [values]

    @callback
    def async_value_changed(value):
        # if an entity belonging to this value needs updating,
        # it's handled within the entity logic
        _LOGGER.debug(
            "[VALUE CHANGED] node_id: %s - label: %s - value: %s - value_id: %s - CC: %s",
            value.node.id,
            value.label,
            value.value,
            value.value_id_key,
            value.command_class,
        )
        # Handle a scene activation message
        if value.command_class in (
                CommandClass.SCENE_ACTIVATION,
                CommandClass.CENTRAL_SCENE,
        ):
            async_handle_scene_activated(hass, value)
            return

    @callback
    def async_value_removed(value):
        _LOGGER.debug(
            "[VALUE REMOVED] node_id: %s - label: %s - value: %s - value_id: %s - CC: %s",
            value.node.id,
            value.label,
            value.value,
            value.value_id_key,
            value.command_class,
        )
        # signal all entities using this value for removal
        value_unique_id = create_value_id(value)
        async_dispatcher_send(hass, const.SIGNAL_DELETE_ENTITY,
                              value_unique_id)
        # remove value from our local list
        node_data_values = data_values[value.node.id]
        node_data_values[:] = [
            item for item in node_data_values
            if item.values_id != value_unique_id
        ]

    # Listen to events for node and value changes
    for event, event_callback in (
        (EVENT_NODE_ADDED, async_node_added),
        (EVENT_NODE_CHANGED, async_node_changed),
        (EVENT_NODE_REMOVED, async_node_removed),
        (EVENT_VALUE_ADDED, async_value_added),
        (EVENT_VALUE_CHANGED, async_value_changed),
        (EVENT_VALUE_REMOVED, async_value_removed),
        (EVENT_INSTANCE_EVENT, async_instance_event),
    ):
        ozw_data[DATA_UNSUBSCRIBE].append(options.listen(
            event, event_callback))

    # Register Services
    services = ZWaveServices(hass, manager)
    services.async_register()

    # Register WebSocket API
    async_register_api(hass)

    @callback
    def async_receive_message(msg):
        manager.receive_message(msg.topic, msg.payload)

    async def start_platforms():
        await asyncio.gather(
            *(hass.config_entries.async_forward_entry_setup(entry, platform)
              for platform in PLATFORMS))
        if entry.data.get(CONF_USE_ADDON):
            mqtt_client_task = asyncio.create_task(
                mqtt_client.start_client(manager))

            async def async_stop_mqtt_client(event=None):
                """Stop the mqtt client.

                Do not unsubscribe the manager topic.
                """
                mqtt_client_task.cancel()
                with suppress(asyncio.CancelledError):
                    await mqtt_client_task

            ozw_data[DATA_UNSUBSCRIBE].append(
                hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                           async_stop_mqtt_client))
            ozw_data[DATA_STOP_MQTT_CLIENT] = async_stop_mqtt_client

        else:
            ozw_data[DATA_UNSUBSCRIBE].append(await mqtt.async_subscribe(
                hass, f"{manager.options.topic_prefix}#",
                async_receive_message))

    hass.async_create_task(start_platforms())

    return True
Exemple #31
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up an Utility Meter."""
    component = EntityComponent(_LOGGER, DOMAIN, hass)
    hass.data[DATA_UTILITY] = {}
    register_services = False

    for meter, conf in config[DOMAIN].items():
        _LOGGER.debug("Setup %s.%s", DOMAIN, meter)

        hass.data[DATA_UTILITY][meter] = conf
        hass.data[DATA_UTILITY][meter][DATA_TARIFF_SENSORS] = []

        if not conf[CONF_TARIFFS]:
            # only one entity is required
            name = conf.get(CONF_NAME, meter)
            hass.async_create_task(
                discovery.async_load_platform(
                    hass,
                    SENSOR_DOMAIN,
                    DOMAIN,
                    {name: {CONF_METER: meter, CONF_NAME: name}},
                    config,
                )
            )
        else:
            # create tariff selection
            await component.async_add_entities(
                [TariffSelect(meter, list(conf[CONF_TARIFFS]))]
            )
            hass.data[DATA_UTILITY][meter][CONF_TARIFF_ENTITY] = "{}.{}".format(
                DOMAIN, meter
            )

            # add one meter for each tariff
            tariff_confs = {}
            for tariff in conf[CONF_TARIFFS]:
                name = f"{meter} {tariff}"
                tariff_confs[name] = {
                    CONF_METER: meter,
                    CONF_NAME: name,
                    CONF_TARIFF: tariff,
                }

            hass.async_create_task(
                discovery.async_load_platform(
                    hass, SENSOR_DOMAIN, DOMAIN, tariff_confs, config
                )
            )
            register_services = True

    if register_services:
        component.async_register_entity_service(SERVICE_RESET, {}, "async_reset_meters")

        component.async_register_entity_service(
            SERVICE_SELECT_TARIFF,
            {vol.Required(ATTR_TARIFF): cv.string},
            "async_select_tariff",
        )

        component.async_register_entity_service(
            SERVICE_SELECT_NEXT_TARIFF, {}, "async_next_tariff"
        )

    return True
Exemple #32
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Toyota Connected Services from a config entry."""
    if hass.data.get(DOMAIN) is None:
        hass.data.setdefault(DOMAIN, {})
        _LOGGER.info(STARTUP_MESSAGE)

    email = entry.data[CONF_EMAIL]
    password = entry.data[CONF_PASSWORD]
    locale = entry.data[CONF_LOCALE]
    uuid = entry.data[CONF_UUID]
    token = entry.data[CONF_API_TOKEN]
    region = entry.data[CONF_REGION]

    client = MyT(
        username=email,
        password=password,
        locale=locale,
        uuid=uuid,
        region=region.lower(),
        token=token,
    )

    async def async_update_data():
        """Fetch data from Toyota API."""

        vehicles = []

        try:
            vehicles = await client.gather_information()
        except ToyotaLoginError as ex:
            _LOGGER.error(ex)
        except Exception as ex:  # pylint: disable=broad-except
            _LOGGER.error(ex)

        _LOGGER.debug(vehicles)
        return vehicles

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name=DOMAIN,
        update_method=async_update_data,
        update_interval=UPDATE_INTERVAL,
    )

    # Fetch initial data so we have data when entities subscribe
    await coordinator.async_refresh()

    hass.data[DOMAIN][entry.entry_id] = {
        DATA_CLIENT: client,
        DATA_COORDINATOR: coordinator,
    }

    if not coordinator.last_update_success:
        raise ConfigEntryNotReady

    # Setup components
    for platform in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, platform))

    return True
Exemple #33
0
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
    """Set up BLE Monitor from a config entry."""
    _LOGGER.debug("Initializing BLE Monitor entry (config entry): %s",
                  config_entry)

    # Prevent unload to be triggered each time we update the config entry
    global UPDATE_UNLISTENER
    if UPDATE_UNLISTENER:
        UPDATE_UNLISTENER()

    if not config_entry.unique_id:
        hass.config_entries.async_update_entry(config_entry,
                                               unique_id=config_entry.title)

    _LOGGER.debug("async_setup_entry: domain %s", CONFIG_YAML)

    config = {}

    if not CONFIG_YAML:
        for key, value in config_entry.data.items():
            config[key] = value

        for key, value in config_entry.options.items():
            config[key] = value

        config[CONFIG_IS_FLOW] = True
        if CONF_DEVICES not in config:
            config[CONF_DEVICES] = []
        else:
            # device configuration is taken from yaml, but yaml config already removed
            # save unique IDs (only once)
            if "ids_from_name" in config:
                devlist = config[CONF_DEVICES]
                for dev_idx, dev_conf in enumerate(devlist):
                    if CONF_NAME in dev_conf:
                        devlist[dev_idx][CONF_UNIQUE_ID] = dev_conf[CONF_NAME]
                del config["ids_from_name"]
    else:
        for key, value in CONFIG_YAML.items():
            config[key] = value
        if CONF_HCI_INTERFACE in CONFIG_YAML:
            hci_list = []
            if isinstance(CONFIG_YAML[CONF_HCI_INTERFACE], list):
                for hci in CONFIG_YAML[CONF_HCI_INTERFACE]:
                    hci_list.append(str(hci))
            else:
                hci_list.append(str(CONFIG_YAML[CONF_HCI_INTERFACE]))
            config[CONF_HCI_INTERFACE] = hci_list

    hass.config_entries.async_update_entry(config_entry,
                                           data={},
                                           options=config)

    _LOGGER.debug("async_setup_entry: %s", config)

    UPDATE_UNLISTENER = config_entry.add_update_listener(
        _async_update_listener)

    if CONF_HCI_INTERFACE not in config:
        config[CONF_HCI_INTERFACE] = [DEFAULT_HCI_INTERFACE]
    else:
        hci_list = config_entry.options.get(CONF_HCI_INTERFACE)
        for i, hci in enumerate(hci_list):
            hci_list[i] = int(hci)
        config[CONF_HCI_INTERFACE] = hci_list
    _LOGGER.debug("HCI interface is %s", config[CONF_HCI_INTERFACE])

    blemonitor = BLEmonitor(config)
    hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP,
                          blemonitor.shutdown_handler)
    blemonitor.start()

    hass.data[DOMAIN] = {}
    hass.data[DOMAIN]["blemonitor"] = blemonitor
    hass.data[DOMAIN]["config_entry_id"] = config_entry.entry_id

    for component in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, component))

    return True
Exemple #34
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Load a config entry."""
    conf = hass.data.get(DATA_MQTT_CONFIG)

    # Config entry was created because user had configuration.yaml entry
    # They removed that, so remove entry.
    if conf is None and entry.source == config_entries.SOURCE_IMPORT:
        hass.async_create_task(hass.config_entries.async_remove(
            entry.entry_id))
        return False

    # If user didn't have configuration.yaml config, generate defaults
    if conf is None:
        conf = CONFIG_SCHEMA({DOMAIN: dict(entry.data)})[DOMAIN]
    elif any(key in conf for key in entry.data):
        shared_keys = conf.keys() & entry.data.keys()
        override = {k: entry.data[k] for k in shared_keys}
        if CONF_PASSWORD in override:
            override[CONF_PASSWORD] = "********"
        _LOGGER.info(
            "Data in your configuration entry is going to override your "
            "configuration.yaml: %s",
            override,
        )

    conf = _merge_config(entry, conf)

    hass.data[DATA_MQTT] = MQTT(
        hass,
        entry,
        conf,
    )

    await hass.data[DATA_MQTT].async_connect()

    async def async_stop_mqtt(_event: Event):
        """Stop MQTT component."""
        await hass.data[DATA_MQTT].async_disconnect()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_stop_mqtt)

    async def async_publish_service(call: ServiceCall) -> None:
        """Handle MQTT publish service calls."""
        msg_topic = call.data.get(ATTR_TOPIC)
        msg_topic_template = call.data.get(ATTR_TOPIC_TEMPLATE)
        payload = call.data.get(ATTR_PAYLOAD)
        payload_template = call.data.get(ATTR_PAYLOAD_TEMPLATE)
        qos: int = call.data[ATTR_QOS]
        retain: bool = call.data[ATTR_RETAIN]
        if msg_topic_template is not None:
            try:
                rendered_topic = template.Template(
                    msg_topic_template, hass).async_render(parse_result=False)
                msg_topic = valid_publish_topic(rendered_topic)
            except (jinja2.TemplateError, TemplateError) as exc:
                _LOGGER.error(
                    "Unable to publish: rendering topic template of %s "
                    "failed because %s",
                    msg_topic_template,
                    exc,
                )
                return
            except vol.Invalid as err:
                _LOGGER.error(
                    "Unable to publish: topic template '%s' produced an "
                    "invalid topic '%s' after rendering (%s)",
                    msg_topic_template,
                    rendered_topic,
                    err,
                )
                return

        if payload_template is not None:
            try:
                payload = MqttCommandTemplate(
                    template.Template(payload_template),
                    hass=hass).async_render()
            except (jinja2.TemplateError, TemplateError) as exc:
                _LOGGER.error(
                    "Unable to publish to %s: rendering payload template of "
                    "%s failed because %s",
                    msg_topic,
                    payload_template,
                    exc,
                )
                return

        await hass.data[DATA_MQTT].async_publish(msg_topic, payload, qos,
                                                 retain)

    hass.services.async_register(DOMAIN,
                                 SERVICE_PUBLISH,
                                 async_publish_service,
                                 schema=MQTT_PUBLISH_SCHEMA)

    async def async_dump_service(call: ServiceCall) -> None:
        """Handle MQTT dump service calls."""
        messages = []

        @callback
        def collect_msg(msg):
            messages.append((msg.topic, msg.payload.replace("\n", "")))

        unsub = await async_subscribe(hass, call.data["topic"], collect_msg)

        def write_dump():
            with open(hass.config.path("mqtt_dump.txt"), "wt",
                      encoding="utf8") as fp:
                for msg in messages:
                    fp.write(",".join(msg) + "\n")

        async def finish_dump(_):
            """Write dump to file."""
            unsub()
            await hass.async_add_executor_job(write_dump)

        event.async_call_later(hass, call.data["duration"], finish_dump)

    hass.services.async_register(
        DOMAIN,
        SERVICE_DUMP,
        async_dump_service,
        schema=vol.Schema({
            vol.Required("topic"): valid_subscribe_topic,
            vol.Optional("duration", default=5): int,
        }),
    )

    if conf.get(CONF_DISCOVERY):
        await _async_setup_discovery(hass, conf, entry)

    return True
Exemple #35
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Netatmo from a config entry."""
    implementation = await config_entry_oauth2_flow.async_get_config_entry_implementation(
        hass, entry)

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

    hass.data[DOMAIN][entry.entry_id] = {
        AUTH: api.ConfigEntryNetatmoAuth(hass, entry, implementation)
    }

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

    for component in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component))

    async def unregister_webhook(_):
        if CONF_WEBHOOK_ID not in entry.data:
            return
        _LOGGER.debug("Unregister Netatmo webhook (%s)",
                      entry.data[CONF_WEBHOOK_ID])
        webhook_unregister(hass, entry.data[CONF_WEBHOOK_ID])

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

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

        if entry.data[
                "auth_implementation"] == "cloud" 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(hass, DOMAIN, "Netatmo",
                             entry.data[CONF_WEBHOOK_ID], handle_webhook)
            await hass.async_add_executor_job(
                hass.data[DOMAIN][entry.entry_id][AUTH].addwebhook,
                webhook_url)
            _LOGGER.info("Register Netatmo webhook: %s", webhook_url)
            hass.async_create_task(
                hass.config_entries.async_forward_entry_setup(entry, "light"))
        except pyatmo.ApiError as err:
            _LOGGER.error("Error during webhook registration - %s", err)

        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                   unregister_webhook)

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

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

    return True
Exemple #36
0
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
    """Set up the StarLine device from a config entry."""
    account = StarlineAccount(hass, config_entry)
    await account.update()
    await account.update_obd()
    if not account.api.available:
        raise ConfigEntryNotReady

    if DOMAIN not in hass.data:
        hass.data[DOMAIN] = {}
    hass.data[DOMAIN][config_entry.entry_id] = account

    device_registry = await hass.helpers.device_registry.async_get_registry()
    for device in account.api.devices.values():
        device_registry.async_get_or_create(
            config_entry_id=config_entry.entry_id, **account.device_info(device)
        )

    for domain in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(config_entry, domain)
        )

    async def async_set_scan_interval(call):
        """Set scan interval."""
        options = dict(config_entry.options)
        options[CONF_SCAN_INTERVAL] = call.data[CONF_SCAN_INTERVAL]
        hass.config_entries.async_update_entry(entry=config_entry, options=options)

    async def async_set_scan_obd_interval(call):
        """Set OBD info scan interval."""
        options = dict(config_entry.options)
        options[CONF_SCAN_OBD_INTERVAL] = call.data[CONF_SCAN_INTERVAL]
        hass.config_entries.async_update_entry(entry=config_entry, options=options)

    async def async_update(call=None):
        """Update all data."""
        await account.update()
        await account.update_obd()

    hass.services.async_register(DOMAIN, SERVICE_UPDATE_STATE, async_update)
    hass.services.async_register(
        DOMAIN,
        SERVICE_SET_SCAN_INTERVAL,
        async_set_scan_interval,
        schema=vol.Schema(
            {
                vol.Required(CONF_SCAN_INTERVAL): vol.All(
                    vol.Coerce(int), vol.Range(min=10)
                )
            }
        ),
    )
    hass.services.async_register(
        DOMAIN,
        SERVICE_SET_SCAN_OBD_INTERVAL,
        async_set_scan_obd_interval,
        schema=vol.Schema(
            {
                vol.Required(CONF_SCAN_INTERVAL): vol.All(
                    vol.Coerce(int), vol.Range(min=180)
                )
            }
        ),
    )

    config_entry.add_update_listener(async_options_updated)
    await async_options_updated(hass, config_entry)

    return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Create a (EMEA/EU-based) Honeywell TCC system."""
    async def load_auth_tokens(store) -> tuple[dict, dict | None]:
        app_storage = await store.async_load()
        tokens = dict(app_storage or {})

        if tokens.pop(CONF_USERNAME, None) != config[DOMAIN][CONF_USERNAME]:
            # any tokens won't be valid, and store might be be corrupt
            await store.async_save({})
            return ({}, None)

        # evohomeasync2 requires naive/local datetimes as strings
        if tokens.get(ACCESS_TOKEN_EXPIRES) is not None:
            tokens[ACCESS_TOKEN_EXPIRES] = _dt_aware_to_naive(
                dt_util.parse_datetime(tokens[ACCESS_TOKEN_EXPIRES]))

        user_data = tokens.pop(USER_DATA, None)
        return (tokens, user_data)

    store = hass.helpers.storage.Store(STORAGE_VER, STORAGE_KEY)
    tokens, user_data = await load_auth_tokens(store)

    client_v2 = evohomeasync2.EvohomeClient(
        config[DOMAIN][CONF_USERNAME],
        config[DOMAIN][CONF_PASSWORD],
        **tokens,
        session=async_get_clientsession(hass),
    )

    try:
        await client_v2.login()
    except (aiohttp.ClientError, evohomeasync2.AuthenticationError) as err:
        _handle_exception(err)
        return False
    finally:
        config[DOMAIN][CONF_PASSWORD] = "REDACTED"

    loc_idx = config[DOMAIN][CONF_LOCATION_IDX]
    try:
        loc_config = client_v2.installation_info[loc_idx]
    except IndexError:
        _LOGGER.error(
            "Config error: '%s' = %s, but the valid range is 0-%s. "
            "Unable to continue. Fix any configuration errors and restart HA",
            CONF_LOCATION_IDX,
            loc_idx,
            len(client_v2.installation_info) - 1,
        )
        return False

    if _LOGGER.isEnabledFor(logging.DEBUG):
        _config = {"locationInfo": {"timeZone": None}, GWS: [{TCS: None}]}
        _config["locationInfo"]["timeZone"] = loc_config["locationInfo"][
            "timeZone"]
        _config[GWS][0][TCS] = loc_config[GWS][0][TCS]
        _LOGGER.debug("Config = %s", _config)

    client_v1 = evohomeasync.EvohomeClient(
        client_v2.username,
        client_v2.password,
        user_data=user_data,
        session=async_get_clientsession(hass),
    )

    hass.data[DOMAIN] = {}
    hass.data[DOMAIN]["broker"] = broker = EvoBroker(hass, client_v2,
                                                     client_v1, store,
                                                     config[DOMAIN])

    await broker.save_auth_tokens()
    await broker.async_update()  # get initial state

    hass.async_create_task(
        async_load_platform(hass, Platform.CLIMATE, DOMAIN, {}, config))
    if broker.tcs.hotwater:
        hass.async_create_task(
            async_load_platform(hass, Platform.WATER_HEATER, DOMAIN, {},
                                config))

    hass.helpers.event.async_track_time_interval(
        broker.async_update, config[DOMAIN][CONF_SCAN_INTERVAL])

    setup_service_functions(hass, broker)

    return True
Exemple #38
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Ness Alarm platform."""

    conf = config[DOMAIN]

    zones = conf[CONF_ZONES]
    host = conf[CONF_HOST]
    port = conf[CONF_DEVICE_PORT]
    scan_interval = conf[CONF_SCAN_INTERVAL]
    infer_arming_state = conf[CONF_INFER_ARMING_STATE]

    client = Client(
        host=host,
        port=port,
        loop=hass.loop,
        update_interval=scan_interval.total_seconds(),
        infer_arming_state=infer_arming_state,
    )
    hass.data[DATA_NESS] = client

    async def _close(event):
        await client.close()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _close)

    hass.async_create_task(
        async_load_platform(hass, "binary_sensor", DOMAIN, {CONF_ZONES: zones},
                            config))
    hass.async_create_task(
        async_load_platform(hass, "alarm_control_panel", DOMAIN, {}, config))

    def on_zone_change(zone_id: int, state: bool):
        """Receives and propagates zone state updates."""
        async_dispatcher_send(hass, SIGNAL_ZONE_CHANGED,
                              ZoneChangedData(zone_id=zone_id, state=state))

    def on_state_change(arming_state: ArmingState):
        """Receives and propagates arming state updates."""
        async_dispatcher_send(hass, SIGNAL_ARMING_STATE_CHANGED, arming_state)

    client.on_zone_change(on_zone_change)
    client.on_state_change(on_state_change)

    # Force update for current arming status and current zone states
    hass.loop.create_task(client.keepalive())
    hass.loop.create_task(client.update())

    async def handle_panic(call: ServiceCall) -> None:
        await client.panic(call.data[ATTR_CODE])

    async def handle_aux(call: ServiceCall) -> None:
        await client.aux(call.data[ATTR_OUTPUT_ID], call.data[ATTR_STATE])

    hass.services.async_register(DOMAIN,
                                 SERVICE_PANIC,
                                 handle_panic,
                                 schema=SERVICE_SCHEMA_PANIC)
    hass.services.async_register(DOMAIN,
                                 SERVICE_AUX,
                                 handle_aux,
                                 schema=SERVICE_SCHEMA_AUX)

    return True
Exemple #39
0
async def async_setup_entry(
    hass: core.HomeAssistant, entry: config_entries.ConfigEntry
):
    """Set up the xiaomi aqara components from a config entry."""
    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN].setdefault(GATEWAYS_KEY, {})

    # Connect to Xiaomi Aqara Gateway
    xiaomi_gateway = await hass.async_add_executor_job(
        XiaomiGateway,
        entry.data[CONF_HOST],
        entry.data[CONF_SID],
        entry.data[CONF_KEY],
        DEFAULT_DISCOVERY_RETRY,
        entry.data[CONF_INTERFACE],
        entry.data[CONF_PORT],
        entry.data[CONF_PROTOCOL],
    )
    hass.data[DOMAIN][GATEWAYS_KEY][entry.entry_id] = xiaomi_gateway

    gateway_discovery = hass.data[DOMAIN].setdefault(
        LISTENER_KEY,
        XiaomiGatewayDiscovery(hass.add_job, [], entry.data[CONF_INTERFACE]),
    )

    if len(hass.data[DOMAIN][GATEWAYS_KEY]) == 1:
        # start listining for local pushes (only once)
        await hass.async_add_executor_job(gateway_discovery.listen)

        # register stop callback to shutdown listining for local pushes
        def stop_xiaomi(event):
            """Stop Xiaomi Socket."""
            _LOGGER.debug("Shutting down Xiaomi Gateway Listener")
            gateway_discovery.stop_listen()

        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_xiaomi)

    gateway_discovery.gateways[entry.data[CONF_HOST]] = xiaomi_gateway
    _LOGGER.debug(
        "Gateway with host '%s' connected, listening for broadcasts",
        entry.data[CONF_HOST],
    )

    device_registry = await dr.async_get_registry(hass)
    device_registry.async_get_or_create(
        config_entry_id=entry.entry_id,
        identifiers={(DOMAIN, entry.unique_id)},
        manufacturer="Xiaomi Aqara",
        name=entry.title,
        sw_version=entry.data[CONF_PROTOCOL],
    )

    if entry.data[CONF_KEY] is not None:
        platforms = GATEWAY_PLATFORMS
    else:
        platforms = GATEWAY_PLATFORMS_NO_KEY

    for platform in platforms:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, platform)
        )

    return True
Exemple #40
0
async def async_from_config_dict(config: Dict[str, Any],
                                 hass: core.HomeAssistant,
                                 config_dir: Optional[str] = None,
                                 enable_log: bool = True,
                                 verbose: bool = False,
                                 skip_pip: bool = False,
                                 log_rotate_days: Any = None,
                                 log_file: Any = None,
                                 log_no_color: bool = False) \
                           -> Optional[core.HomeAssistant]:
    """Try to configure Home Assistant from a configuration dictionary.

    Dynamically loads required components and its dependencies.
    This method is a coroutine.
    """
    start = time()

    if enable_log:
        async_enable_logging(hass, verbose, log_rotate_days, log_file,
                             log_no_color)

    core_config = config.get(core.DOMAIN, {})
    has_api_password = bool((config.get('http') or {}).get('api_password'))
    has_trusted_networks = bool((config.get('http') or {})
                                .get('trusted_networks'))

    try:
        await conf_util.async_process_ha_core_config(
            hass, core_config, has_api_password, has_trusted_networks)
    except vol.Invalid as config_err:
        conf_util.async_log_exception(
            config_err, 'homeassistant', core_config, hass)
        return None
    except HomeAssistantError:
        _LOGGER.error("Home Assistant core failed to initialize. "
                      "Further initialization aborted")
        return None

    await hass.async_add_executor_job(
        conf_util.process_ha_config_upgrade, hass)

    hass.config.skip_pip = skip_pip
    if skip_pip:
        _LOGGER.warning("Skipping pip installation of required modules. "
                        "This may cause issues")

    # Make a copy because we are mutating it.
    config = OrderedDict(config)

    # Merge packages
    conf_util.merge_packages_config(
        hass, config, core_config.get(conf_util.CONF_PACKAGES, {}))

    hass.config_entries = config_entries.ConfigEntries(hass, config)
    await hass.config_entries.async_load()

    # Filter out the repeating and common config section [homeassistant]
    components = set(key.split(' ')[0] for key in config.keys()
                     if key != core.DOMAIN)
    components.update(hass.config_entries.async_domains())

    # Resolve all dependencies of all components.
    for component in list(components):
        try:
            components.update(loader.component_dependencies(hass, component))
        except loader.LoaderError:
            # Ignore it, or we'll break startup
            # It will be properly handled during setup.
            pass

    # setup components
    res = await core_components.async_setup(hass, config)
    if not res:
        _LOGGER.error("Home Assistant core failed to initialize. "
                      "Further initialization aborted")
        return hass

    await persistent_notification.async_setup(hass, config)

    _LOGGER.info("Home Assistant core initialized")

    # stage 1
    for component in components:
        if component not in FIRST_INIT_COMPONENT:
            continue
        hass.async_create_task(async_setup_component(hass, component, config))

    await hass.async_block_till_done()

    # stage 2
    for component in components:
        if component in FIRST_INIT_COMPONENT:
            continue
        hass.async_create_task(async_setup_component(hass, component, config))

    await hass.async_block_till_done()

    stop = time()
    _LOGGER.info("Home Assistant initialized in %.2fs", stop-start)

    # TEMP: warn users for invalid slugs
    # Remove after 0.94 or 1.0
    if cv.INVALID_SLUGS_FOUND or cv.INVALID_ENTITY_IDS_FOUND:
        msg = []

        if cv.INVALID_ENTITY_IDS_FOUND:
            msg.append(
                "Your configuration contains invalid entity ID references. "
                "Please find and update the following. "
                "This will become a breaking change."
            )
            msg.append('\n'.join('- {} -> {}'.format(*item)
                                 for item
                                 in cv.INVALID_ENTITY_IDS_FOUND.items()))

        if cv.INVALID_SLUGS_FOUND:
            msg.append(
                "Your configuration contains invalid slugs. "
                "Please find and update the following. "
                "This will become a breaking change."
            )
            msg.append('\n'.join('- {} -> {}'.format(*item)
                                 for item in cv.INVALID_SLUGS_FOUND.items()))

        hass.components.persistent_notification.async_create(
            '\n\n'.join(msg), "Config Warning", "config_warning"
        )

    # TEMP: warn users of invalid extra keys
    # Remove after 0.92
    if cv.INVALID_EXTRA_KEYS_FOUND:
        msg = []
        msg.append(
            "Your configuration contains extra keys "
            "that the platform does not support (but were silently "
            "accepted before 0.88). Please find and remove the following."
            "This will become a breaking change."
        )
        msg.append('\n'.join('- {}'.format(it)
                             for it in cv.INVALID_EXTRA_KEYS_FOUND))

        hass.components.persistent_notification.async_create(
            '\n\n'.join(msg), "Config Warning", "config_warning"
        )

    return hass
Exemple #41
0
async def websocket_update_topology(hass: HomeAssistant,
                                    connection: ActiveConnection,
                                    msg: dict[str, Any]) -> None:
    """Update the ZHA network topology."""
    zha_gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
    hass.async_create_task(zha_gateway.application_controller.topology.scan())
Exemple #42
0
def async_dismiss(hass: HomeAssistant, notification_id: str) -> None:
    """Remove a notification."""
    data = {ATTR_NOTIFICATION_ID: notification_id}

    hass.async_create_task(
        hass.services.async_call(DOMAIN, SERVICE_DISMISS, data))
Exemple #43
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up motionEye from a config entry."""
    hass.data.setdefault(DOMAIN, {})

    client = create_motioneye_client(
        entry.data[CONF_URL],
        admin_username=entry.data.get(CONF_ADMIN_USERNAME),
        admin_password=entry.data.get(CONF_ADMIN_PASSWORD),
        surveillance_username=entry.data.get(CONF_SURVEILLANCE_USERNAME),
        surveillance_password=entry.data.get(CONF_SURVEILLANCE_PASSWORD),
    )

    try:
        await client.async_client_login()
    except MotionEyeClientInvalidAuthError as exc:
        await client.async_client_close()
        raise ConfigEntryAuthFailed from exc
    except MotionEyeClientError as exc:
        await client.async_client_close()
        raise ConfigEntryNotReady from exc

    @callback
    async def async_update_data() -> dict[str, Any] | None:
        try:
            return await client.async_get_cameras()
        except MotionEyeClientError as exc:
            raise UpdateFailed("Error communicating with API") from exc

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name=DOMAIN,
        update_method=async_update_data,
        update_interval=DEFAULT_SCAN_INTERVAL,
    )
    hass.data[DOMAIN][entry.entry_id] = {
        CONF_CLIENT: client,
        CONF_COORDINATOR: coordinator,
    }

    current_cameras: set[tuple[str, str]] = set()
    device_registry = await dr.async_get_registry(hass)

    @callback
    def _async_process_motioneye_cameras() -> None:
        """Process motionEye camera additions and removals."""
        inbound_camera: set[tuple[str, str]] = set()
        if coordinator.data is None or KEY_CAMERAS not in coordinator.data:
            return

        for camera in coordinator.data[KEY_CAMERAS]:
            if not is_acceptable_camera(camera):
                return
            camera_id = camera[KEY_ID]
            device_identifier = get_motioneye_device_identifier(
                entry.entry_id, camera_id
            )
            inbound_camera.add(device_identifier)

            if device_identifier in current_cameras:
                continue
            current_cameras.add(device_identifier)
            _add_camera(
                hass,
                device_registry,
                client,
                entry,
                camera_id,
                camera,
                device_identifier,
            )

        # Ensure every device associated with this config entry is still in the list of
        # motionEye cameras, otherwise remove the device (and thus entities).
        for device_entry in dr.async_entries_for_config_entry(
            device_registry, entry.entry_id
        ):
            for identifier in device_entry.identifiers:
                if identifier in inbound_camera:
                    break
            else:
                device_registry.async_remove_device(device_entry.id)

    async def setup_then_listen() -> None:
        await asyncio.gather(
            *[
                hass.config_entries.async_forward_entry_setup(entry, platform)
                for platform in PLATFORMS
            ]
        )
        entry.async_on_unload(
            coordinator.async_add_listener(_async_process_motioneye_cameras)
        )
        await coordinator.async_refresh()

    hass.async_create_task(setup_then_listen())
    return True