Beispiel #1
0
def async_from_config_file(config_path: str,
                           hass: core.HomeAssistant,
                           verbose: bool=False,
                           skip_pip: bool=True,
                           log_rotate_days: Any=None):
    """Read the configuration file and try to start all the functionality.

    Will add functionality to 'hass' parameter.
    This method is a coroutine.
    """
    # Set config dir to directory holding config file
    config_dir = os.path.abspath(os.path.dirname(config_path))
    hass.config.config_dir = config_dir
    yield from hass.async_add_job(mount_local_lib_path, config_dir)

    async_enable_logging(hass, verbose, log_rotate_days)

    try:
        config_dict = yield from hass.async_add_job(
            conf_util.load_yaml_config_file, config_path)
    except HomeAssistantError as err:
        _LOGGER.error('Error loading %s: %s', config_path, err)
        return None
    finally:
        clear_secret_cache()

    hass = yield from async_from_config_dict(
        config_dict, hass, enable_log=False, skip_pip=skip_pip)
    return hass
def _load_restore_cache(hass: HomeAssistant):
    """Load the restore cache to be used by other components."""
    @callback
    def remove_cache(event):
        """Remove the states cache."""
        hass.data.pop(DATA_RESTORE_CACHE, None)

    hass.bus.listen_once(EVENT_HOMEASSISTANT_START, remove_cache)

    last_run = last_recorder_run(hass)

    if last_run is None or last_run.end is None:
        _LOGGER.debug('Not creating cache - no suitable last run found: %s',
                      last_run)
        hass.data[DATA_RESTORE_CACHE] = {}
        return

    last_end_time = last_run.end - timedelta(seconds=1)
    # Unfortunately the recorder_run model do not return offset-aware time
    last_end_time = last_end_time.replace(tzinfo=dt_util.UTC)
    _LOGGER.debug("Last run: %s - %s", last_run.start, last_end_time)

    states = get_states(hass, last_end_time, run=last_run)

    # Cache the states
    hass.data[DATA_RESTORE_CACHE] = {
        state.entity_id: state for state in states}
    _LOGGER.debug('Created cache with %s', list(hass.data[DATA_RESTORE_CACHE]))
Beispiel #3
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))
Beispiel #5
0
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the ISY 994 platform."""
    hass.data[ISY994_NODES] = {}
    for domain in SUPPORTED_DOMAINS:
        hass.data[ISY994_NODES][domain] = []

    hass.data[ISY994_WEATHER] = []

    hass.data[ISY994_PROGRAMS] = {}
    for domain in SUPPORTED_DOMAINS:
        hass.data[ISY994_PROGRAMS][domain] = []

    isy_config = config.get(DOMAIN)

    user = isy_config.get(CONF_USERNAME)
    password = isy_config.get(CONF_PASSWORD)
    tls_version = isy_config.get(CONF_TLS_VER)
    host = urlparse(isy_config.get(CONF_HOST))
    ignore_identifier = isy_config.get(CONF_IGNORE_STRING)
    sensor_identifier = isy_config.get(CONF_SENSOR_STRING)
    enable_climate = isy_config.get(CONF_ENABLE_CLIMATE)

    if host.scheme == 'http':
        https = False
        port = host.port or 80
    elif host.scheme == 'https':
        https = True
        port = host.port or 443
    else:
        _LOGGER.error("isy994 host value in configuration is invalid")
        return False

    import PyISY
    # Connect to ISY controller.
    isy = PyISY.ISY(host.hostname, port, username=user, password=password,
                    use_https=https, tls_ver=tls_version, log=_LOGGER)
    if not isy.connected:
        return False

    _categorize_nodes(hass, isy.nodes, ignore_identifier, sensor_identifier)
    _categorize_programs(hass, isy.programs)

    if enable_climate and isy.configuration.get('Weather Information'):
        _categorize_weather(hass, isy.climate)

    def stop(event: object) -> None:
        """Stop ISY auto updates."""
        isy.auto_update = False

    # Listen for HA stop to disconnect.
    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop)

    # Load platforms for the devices in the ISY controller that we support.
    for component in SUPPORTED_DOMAINS:
        discovery.load_platform(hass, component, DOMAIN, {}, config)

    isy.auto_update = True
    return True
Beispiel #6
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))
async def async_setup_platform(hass: HomeAssistant,
                               config,
                               async_add_devices,
                               discovery_info=None):
    """Set up DLNA DMR platform."""
    if config.get(CONF_URL) is not None:
        url = config[CONF_URL]
        name = config.get(CONF_NAME)
    elif discovery_info is not None:
        url = discovery_info['ssdp_description']
        name = discovery_info.get('name')

    if DLNA_DMR_DATA not in hass.data:
        hass.data[DLNA_DMR_DATA] = {}

    if 'lock' not in hass.data[DLNA_DMR_DATA]:
        hass.data[DLNA_DMR_DATA]['lock'] = asyncio.Lock()

    # build upnp/aiohttp requester
    from async_upnp_client.aiohttp import AiohttpSessionRequester
    session = async_get_clientsession(hass)
    requester = AiohttpSessionRequester(session, True)

    # ensure event handler has been started
    with await hass.data[DLNA_DMR_DATA]['lock']:
        server_host = config.get(CONF_LISTEN_IP)
        if server_host is None:
            server_host = get_local_ip()
        server_port = config.get(CONF_LISTEN_PORT, DEFAULT_LISTEN_PORT)
        event_handler = await async_start_event_handler(hass,
                                                        server_host,
                                                        server_port,
                                                        requester)

    # create upnp device
    from async_upnp_client import UpnpFactory
    factory = UpnpFactory(requester, disable_state_variable_validation=True)
    try:
        upnp_device = await factory.async_create_device(url)
    except (asyncio.TimeoutError, aiohttp.ClientError):
        raise PlatformNotReady()

    # wrap with DmrDevice
    from async_upnp_client.dlna import DmrDevice
    dlna_device = DmrDevice(upnp_device, event_handler)

    # create our own device
    device = DlnaDmrDevice(dlna_device, name)
    _LOGGER.debug("Adding device: %s", device)
    async_add_devices([device], True)
Beispiel #8
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
Beispiel #9
0
def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the recorder."""
    conf = config.get(DOMAIN, {})
    keep_days = conf.get(CONF_PURGE_KEEP_DAYS)
    purge_interval = conf.get(CONF_PURGE_INTERVAL)

    db_url = conf.get(CONF_DB_URL, None)
    if not db_url:
        db_url = DEFAULT_URL.format(
            hass_config_path=hass.config.path(DEFAULT_DB_FILE))

    include = conf.get(CONF_INCLUDE, {})
    exclude = conf.get(CONF_EXCLUDE, {})
    instance = hass.data[DATA_INSTANCE] = Recorder(
        hass=hass, keep_days=keep_days, purge_interval=purge_interval,
        uri=db_url, include=include, exclude=exclude)
    instance.async_initialize()
    instance.start()

    @asyncio.coroutine
    def async_handle_purge_service(service):
        """Handle calls to the purge service."""
        instance.do_adhoc_purge(service.data[ATTR_KEEP_DAYS])

    descriptions = yield from hass.async_add_job(
        conf_util.load_yaml_config_file, path.join(
            path.dirname(__file__), 'services.yaml'))

    hass.services.async_register(DOMAIN, SERVICE_PURGE,
                                 async_handle_purge_service,
                                 descriptions.get(SERVICE_PURGE),
                                 schema=SERVICE_PURGE_SCHEMA)

    return (yield from instance.async_db_ready)
Beispiel #10
0
def _async_process_requirements(hass: core.HomeAssistant, name: str,
                                requirements) -> bool:
    """Install the requirements for a component.

    This method is a coroutine.
    """
    if hass.config.skip_pip:
        return True

    pip_lock = hass.data.get(DATA_PIP_LOCK)
    if pip_lock is None:
        pip_lock = hass.data[DATA_PIP_LOCK] = asyncio.Lock(loop=hass.loop)

    def pip_install(mod):
        """Install packages."""
        if pkg_util.running_under_virtualenv():
            return pkg_util.install_package(
                mod, constraints=os.path.join(
                    os.path.dirname(__file__), CONSTRAINT_FILE))
        return pkg_util.install_package(
            mod, target=hass.config.path('deps'),
            constraints=os.path.join(
                os.path.dirname(__file__), CONSTRAINT_FILE))

    with (yield from pip_lock):
        for req in requirements:
            ret = yield from hass.async_add_job(pip_install, req)
            if not ret:
                _LOGGER.error("Not initializing %s because could not install "
                              "dependency %s", name, req)
                async_notify_setup_error(hass, name)
                return False

    return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the HomematicIP Cloud component."""
    hass.data[DOMAIN] = {}

    accesspoints = config.get(DOMAIN, [])

    for conf in accesspoints:
        if conf[CONF_ACCESSPOINT] not in configured_haps(hass):
            hass.async_add_job(hass.config_entries.flow.async_init(
                DOMAIN, context={'source': config_entries.SOURCE_IMPORT},
                data={
                    HMIPC_HAPID: conf[CONF_ACCESSPOINT],
                    HMIPC_AUTHTOKEN: conf[CONF_AUTHTOKEN],
                    HMIPC_NAME: conf[CONF_NAME],
                }
            ))

    return True
Beispiel #12
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']))
Beispiel #13
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))
Beispiel #15
0
def run(args):
    """Handle Home Assistant auth provider script."""
    parser = argparse.ArgumentParser(
        description="Manage Home Assistant users")
    parser.add_argument(
        '--script', choices=['auth'])
    parser.add_argument(
        '-c', '--config',
        default=get_default_config_dir(),
        help="Directory that contains the Home Assistant configuration")

    subparsers = parser.add_subparsers(dest='func')
    subparsers.required = True
    parser_list = subparsers.add_parser('list')
    parser_list.set_defaults(func=list_users)

    parser_add = subparsers.add_parser('add')
    parser_add.add_argument('username', type=str)
    parser_add.add_argument('password', type=str)
    parser_add.set_defaults(func=add_user)

    parser_validate_login = subparsers.add_parser('validate')
    parser_validate_login.add_argument('username', type=str)
    parser_validate_login.add_argument('password', type=str)
    parser_validate_login.set_defaults(func=validate_login)

    parser_change_pw = subparsers.add_parser('change_password')
    parser_change_pw.add_argument('username', type=str)
    parser_change_pw.add_argument('new_password', type=str)
    parser_change_pw.set_defaults(func=change_password)

    args = parser.parse_args(args)
    loop = asyncio.get_event_loop()
    hass = HomeAssistant(loop=loop)
    loop.run_until_complete(run_command(hass, args))

    # Triggers save on used storage helpers with delay (core auth)
    logging.getLogger('homeassistant.core').setLevel(logging.WARNING)
    loop.run_until_complete(hass.async_stop())
Beispiel #16
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))
def async_setup(hass: HomeAssistant, yaml_config: Dict[str, Any]):
    """Activate Google Actions component."""
    config = yaml_config.get(DOMAIN, {})
    agent_user_id = config.get(CONF_AGENT_USER_ID)
    api_key = config.get(CONF_API_KEY)
    if api_key is not None:
        descriptions = yield from hass.async_add_job(
            conf_util.load_yaml_config_file, os.path.join(
                os.path.dirname(__file__), 'services.yaml')
        )
    hass.http.register_view(GoogleAssistantAuthView(hass, config))
    hass.http.register_view(GoogleAssistantView(hass, config))

    @asyncio.coroutine
    def request_sync_service_handler(call):
        """Handle request sync service calls."""
        websession = async_get_clientsession(hass)
        try:
            with async_timeout.timeout(5, loop=hass.loop):
                res = yield from websession.post(
                    REQUEST_SYNC_BASE_URL,
                    params={'key': api_key},
                    json={'agent_user_id': agent_user_id})
                _LOGGER.info("Submitted request_sync request to Google")
                res.raise_for_status()
        except aiohttp.ClientResponseError:
            body = yield from res.read()
            _LOGGER.error(
                'request_sync request failed: %d %s', res.status, body)
        except (asyncio.TimeoutError, aiohttp.ClientError):
            _LOGGER.error("Could not contact Google for request_sync")

# Register service only if api key is provided
    if api_key is not None:
        hass.services.async_register(
            DOMAIN, SERVICE_REQUEST_SYNC, request_sync_service_handler,
            descriptions.get(SERVICE_REQUEST_SYNC))

    return True
Beispiel #18
0
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 (yield from setup_tasks[domain])

    if config is None:
        config = {}

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

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

    return (yield from task)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up an access point from a config entry."""
    hap = HomematicipHAP(hass, entry)
    hapid = entry.data[HMIPC_HAPID].replace('-', '').upper()
    hass.data[DOMAIN][hapid] = hap

    if not await hap.async_setup():
        return False

    # Register hap as device in registry.
    device_registry = await dr.async_get_registry(hass)
    home = hap.home
    # Add the HAP name from configuration if set.
    hapname = home.label \
        if not home.name else "{} {}".format(home.label, home.name)
    device_registry.async_get_or_create(
        config_entry_id=home.id,
        identifiers={(DOMAIN, home.id)},
        manufacturer='eQ-3',
        name=hapname,
        model=home.modelType,
        sw_version=home.currentAPVersion,
    )
    return True
Beispiel #20
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Initialize config entry which represents an installed SmartApp."""
    # For backwards compat
    if entry.unique_id is None:
        hass.config_entries.async_update_entry(
            entry,
            unique_id=format_unique_id(entry.data[CONF_APP_ID],
                                       entry.data[CONF_LOCATION_ID]),
        )

    if not validate_webhook_requirements(hass):
        _LOGGER.warning(
            "The 'base_url' of the 'http' integration must be configured and start with 'https://'"
        )
        return False

    api = SmartThings(async_get_clientsession(hass),
                      entry.data[CONF_ACCESS_TOKEN])

    remove_entry = False
    try:
        # See if the app is already setup. This occurs when there are
        # installs in multiple SmartThings locations (valid use-case)
        manager = hass.data[DOMAIN][DATA_MANAGER]
        smart_app = manager.smartapps.get(entry.data[CONF_APP_ID])
        if not smart_app:
            # Validate and setup the app.
            app = await api.app(entry.data[CONF_APP_ID])
            smart_app = setup_smartapp(hass, app)

        # Validate and retrieve the installed app.
        installed_app = await validate_installed_app(
            api, entry.data[CONF_INSTALLED_APP_ID])

        # Get scenes
        scenes = await async_get_entry_scenes(entry, api)

        # Get SmartApp token to sync subscriptions
        token = await api.generate_tokens(
            entry.data[CONF_CLIENT_ID],
            entry.data[CONF_CLIENT_SECRET],
            entry.data[CONF_REFRESH_TOKEN],
        )
        hass.config_entries.async_update_entry(
            entry,
            data={
                **entry.data, CONF_REFRESH_TOKEN: token.refresh_token
            })

        # Get devices and their current status
        devices = await api.devices(location_ids=[installed_app.location_id])

        async def retrieve_device_status(device):
            try:
                await device.status.refresh()
            except ClientResponseError:
                _LOGGER.debug(
                    "Unable to update status for device: %s (%s), the device will be excluded",
                    device.label,
                    device.device_id,
                    exc_info=True,
                )
                devices.remove(device)

        await asyncio.gather(*(retrieve_device_status(d)
                               for d in devices.copy()))

        # Sync device subscriptions
        await smartapp_sync_subscriptions(
            hass,
            token.access_token,
            installed_app.location_id,
            installed_app.installed_app_id,
            devices,
        )

        # Setup device broker
        broker = DeviceBroker(hass, entry, token, smart_app, devices, scenes)
        broker.connect()
        hass.data[DOMAIN][DATA_BROKERS][entry.entry_id] = broker

    except ClientResponseError as ex:
        if ex.status in (HTTP_UNAUTHORIZED, HTTP_FORBIDDEN):
            _LOGGER.exception(
                "Unable to setup configuration entry '%s' - please reconfigure the integration",
                entry.title,
            )
            remove_entry = True
        else:
            _LOGGER.debug(ex, exc_info=True)
            raise ConfigEntryNotReady from ex
    except (ClientConnectionError, RuntimeWarning) as ex:
        _LOGGER.debug(ex, exc_info=True)
        raise ConfigEntryNotReady from ex

    if remove_entry:
        hass.async_create_task(hass.config_entries.async_remove(
            entry.entry_id))
        # only create new flow if there isn't a pending one for SmartThings.
        flows = hass.config_entries.flow.async_progress()
        if not [flow for flow in flows if flow["handler"] == DOMAIN]:
            hass.async_create_task(
                hass.config_entries.flow.async_init(
                    DOMAIN, context={"source": SOURCE_IMPORT}))
        return False

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)
    return True
Beispiel #21
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))

    hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)

    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.async_on_unload(
        config_entry.add_update_listener(async_options_updated))
    await async_options_updated(hass, config_entry)

    return True
Beispiel #22
0
def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Monoprice Blackbird 4k 8x8 HDBaseT Matrix platform."""
    if DATA_BLACKBIRD not in hass.data:
        hass.data[DATA_BLACKBIRD] = {}

    port = config.get(CONF_PORT)
    host = config.get(CONF_HOST)

    connection = None
    if port is not None:
        try:
            blackbird = get_blackbird(port)
            connection = port
        except SerialException:
            _LOGGER.error("Error connecting to the Blackbird controller")
            return

    if host is not None:
        try:
            blackbird = get_blackbird(host, False)
            connection = host
        except socket.timeout:
            _LOGGER.error("Error connecting to the Blackbird controller")
            return

    sources = {
        source_id: extra[CONF_NAME]
        for source_id, extra in config[CONF_SOURCES].items()
    }

    devices = []
    for zone_id, extra in config[CONF_ZONES].items():
        _LOGGER.info("Adding zone %d - %s", zone_id, extra[CONF_NAME])
        unique_id = f"{connection}-{zone_id}"
        device = BlackbirdZone(blackbird, sources, zone_id, extra[CONF_NAME])
        hass.data[DATA_BLACKBIRD][unique_id] = device
        devices.append(device)

    add_entities(devices, True)

    def service_handle(service: ServiceCall) -> None:
        """Handle for services."""
        entity_ids = service.data.get(ATTR_ENTITY_ID)
        source = service.data.get(ATTR_SOURCE)
        if entity_ids:
            devices = [
                device for device in hass.data[DATA_BLACKBIRD].values()
                if device.entity_id in entity_ids
            ]

        else:
            devices = hass.data[DATA_BLACKBIRD].values()

        for device in devices:
            if service.service == SERVICE_SETALLZONES:
                device.set_all_zones(source)

    hass.services.register(DOMAIN,
                           SERVICE_SETALLZONES,
                           service_handle,
                           schema=BLACKBIRD_SETALLZONES_SCHEMA)
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Zabbix component."""

    conf = config[DOMAIN]
    protocol = "https" if conf[CONF_SSL] else "http"

    url = urljoin(f"{protocol}://{conf[CONF_HOST]}", conf[CONF_PATH])
    username = conf.get(CONF_USERNAME)
    password = conf.get(CONF_PASSWORD)

    publish_states_host = conf.get(CONF_PUBLISH_STATES_HOST)

    entities_filter = convert_include_exclude_filter(conf)

    try:
        zapi = ZabbixAPI(url=url, user=username, password=password)
        _LOGGER.info("Connected to Zabbix API Version %s", zapi.api_version())
    except ZabbixAPIException as login_exception:
        _LOGGER.error("Unable to login to the Zabbix API: %s", login_exception)
        return False
    except HTTPError as http_error:
        _LOGGER.error("HTTPError when connecting to Zabbix API: %s",
                      http_error)
        zapi = None
        _LOGGER.error(RETRY_MESSAGE, http_error)
        event_helper.call_later(hass, RETRY_INTERVAL,
                                lambda _: setup(hass, config))
        return True

    hass.data[DOMAIN] = zapi

    def event_to_metrics(event, float_keys, string_keys):
        """Add an event to the outgoing Zabbix list."""
        state = event.data.get("new_state")
        if state is None or state.state in (STATE_UNKNOWN, "",
                                            STATE_UNAVAILABLE):
            return

        entity_id = state.entity_id
        if not entities_filter(entity_id):
            return

        floats = {}
        strings = {}
        try:
            _state_as_value = float(state.state)
            floats[entity_id] = _state_as_value
        except ValueError:
            try:
                _state_as_value = float(state_helper.state_as_number(state))
                floats[entity_id] = _state_as_value
            except ValueError:
                strings[entity_id] = state.state

        for key, value in state.attributes.items():
            # For each value we try to cast it as float
            # But if we can not do it we store the value
            # as string
            attribute_id = f"{entity_id}/{key}"
            try:
                float_value = float(value)
            except (ValueError, TypeError):
                float_value = None
            if float_value is None or not math.isfinite(float_value):
                strings[attribute_id] = str(value)
            else:
                floats[attribute_id] = float_value

        metrics = []
        float_keys_count = len(float_keys)
        float_keys.update(floats)
        if len(float_keys) != float_keys_count:
            floats_discovery = []
            for float_key in float_keys:
                floats_discovery.append({"{#KEY}": float_key})
            metric = ZabbixMetric(
                publish_states_host,
                "homeassistant.floats_discovery",
                json.dumps(floats_discovery),
            )
            metrics.append(metric)
        for key, value in floats.items():
            metric = ZabbixMetric(publish_states_host,
                                  f"homeassistant.float[{key}]", value)
            metrics.append(metric)

        string_keys.update(strings)
        return metrics

    if publish_states_host:
        zabbix_sender = ZabbixSender(zabbix_server=conf[CONF_HOST])
        instance = ZabbixThread(hass, zabbix_sender, event_to_metrics)
        instance.setup(hass)

    return True
Beispiel #24
0
def async_enable_logging(hass: core.HomeAssistant,
                         verbose: bool = False,
                         log_rotate_days=None,
                         log_file=None) -> None:
    """Set up the logging.

    This method must be run in the event loop.
    """
    logging.basicConfig(level=logging.INFO)
    fmt = ("%(asctime)s %(levelname)s (%(threadName)s) "
           "[%(name)s] %(message)s")
    colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
    datefmt = '%Y-%m-%d %H:%M:%S'

    # Suppress overly verbose logs from libraries that aren't helpful
    logging.getLogger('requests').setLevel(logging.WARNING)
    logging.getLogger('urllib3').setLevel(logging.WARNING)
    logging.getLogger('aiohttp.access').setLevel(logging.WARNING)

    try:
        from colorlog import ColoredFormatter
        logging.getLogger().handlers[0].setFormatter(
            ColoredFormatter(colorfmt,
                             datefmt=datefmt,
                             reset=True,
                             log_colors={
                                 'DEBUG': 'cyan',
                                 'INFO': 'green',
                                 'WARNING': 'yellow',
                                 'ERROR': 'red',
                                 'CRITICAL': 'red',
                             }))
    except ImportError:
        pass

    # Log errors to a file if we have write access to file or config dir
    if log_file is None:
        err_log_path = hass.config.path(ERROR_LOG_FILENAME)
    else:
        err_log_path = os.path.abspath(log_file)

    err_path_exists = os.path.isfile(err_log_path)
    err_dir = os.path.dirname(err_log_path)

    # Check if we can write to the error log if it exists or that
    # we can create files in the containing directory if not.
    if (err_path_exists and os.access(err_log_path, os.W_OK)) or \
       (not err_path_exists and os.access(err_dir, os.W_OK)):

        if log_rotate_days:
            err_handler = logging.handlers.TimedRotatingFileHandler(
                err_log_path, when='midnight', backupCount=log_rotate_days)
        else:
            err_handler = logging.FileHandler(err_log_path,
                                              mode='w',
                                              delay=True)

        err_handler.setLevel(logging.INFO if verbose else logging.WARNING)
        err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt))

        async_handler = AsyncHandler(hass.loop, err_handler)

        @asyncio.coroutine
        def async_stop_async_handler(event):
            """Cleanup async handler."""
            logging.getLogger('').removeHandler(async_handler)
            yield from async_handler.async_close(blocking=True)

        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE,
                                   async_stop_async_handler)

        logger = logging.getLogger('')
        logger.addHandler(async_handler)
        logger.setLevel(logging.INFO)

        # Save the log file location for access by other components.
        hass.data[DATA_LOGGING] = err_log_path
    else:
        _LOGGER.error("Unable to setup error log %s (access denied)",
                      err_log_path)
Beispiel #25
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
Beispiel #26
0
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) \
                           -> Optional[core.HomeAssistant]:
    """Try to configure Home Assistant from a config dict.

    Dynamically loads required components and its dependencies.
    This method is a coroutine.
    """
    hass.async_track_tasks()
    setup_lock = hass.data.get('setup_lock')
    if setup_lock is None:
        setup_lock = hass.data['setup_lock'] = asyncio.Lock(loop=hass.loop)

    yield from setup_lock.acquire()

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

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

    yield from hass.loop.run_in_executor(
        None, conf_util.process_ha_config_upgrade, hass)

    if enable_log:
        async_enable_logging(hass, verbose, log_rotate_days)

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

    if not loader.PREPARED:
        yield from hass.loop.run_in_executor(None, loader.prepare, hass)

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

    # Make a copy because we are mutating it.
    # Use OrderedDict in case original one was one.
    # Convert values to dictionaries if they are None
    new_config = OrderedDict()
    for key, value in config.items():
        new_config[key] = value or {}
    config = new_config

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

    # setup components
    # pylint: disable=not-an-iterable
    res = yield from core_components.async_setup(hass, config)
    if not res:
        _LOGGER.error('Home Assistant core failed to initialize. '
                      'Further initialization aborted.')
        return hass

    yield from persistent_notification.async_setup(hass, config)

    _LOGGER.info('Home Assistant core initialized')

    # Give event decorators access to HASS
    event_decorators.HASS = hass
    service.HASS = hass

    # Setup the components
    dependency_blacklist = loader.DEPENDENCY_BLACKLIST - set(components)

    for domain in loader.load_order_components(components):
        if domain in dependency_blacklist:
            raise HomeAssistantError(
                '{} is not allowed to be a dependency'.format(domain))

        yield from _async_setup_component(hass, domain, config)

    setup_lock.release()

    yield from hass.async_stop_track_tasks()

    async_register_signal_handling(hass)
    return hass
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Z-Wave JS component."""
    hass.data[DOMAIN] = {}
    return True
Beispiel #28
0
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
Beispiel #29
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Dyson from a config entry."""
    device = get_device(
        entry.data[CONF_SERIAL],
        entry.data[CONF_CREDENTIAL],
        entry.data[CONF_DEVICE_TYPE],
    )

    if not isinstance(device, Dyson360Eye):
        # Set up coordinator
        async def async_update_data():
            """Poll environmental data from the device."""
            try:
                await hass.async_add_executor_job(
                    device.request_environmental_data)
            except DysonException as err:
                raise UpdateFailed(
                    "Failed to request environmental data") from err

        coordinator = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name="environmental",
            update_method=async_update_data,
            update_interval=ENVIRONMENTAL_DATA_UPDATE_INTERVAL,
        )
    else:
        coordinator = None

    async def _async_forward_entry_setup():
        for component in _async_get_platforms(device):
            hass.async_create_task(
                hass.config_entries.async_forward_entry_setup(
                    entry, component))

    def setup_entry(host: str, is_discovery: bool = True) -> bool:
        try:
            device.connect(host)
        except DysonException:
            if is_discovery:
                _LOGGER.error(
                    "Failed to connect to device %s at %s",
                    device.serial,
                    host,
                )
                return
            raise ConfigEntryNotReady
        hass.data[DOMAIN][DATA_DEVICES][entry.entry_id] = device
        hass.data[DOMAIN][DATA_COORDINATORS][entry.entry_id] = coordinator
        asyncio.run_coroutine_threadsafe(_async_forward_entry_setup(),
                                         hass.loop).result()

    host = entry.data.get(CONF_HOST)
    if host:
        await hass.async_add_executor_job(
            partial(setup_entry, host, is_discovery=False))
    else:
        discovery = hass.data[DOMAIN][DATA_DISCOVERY]
        if discovery is None:
            discovery = DysonDiscovery()
            hass.data[DOMAIN][DATA_DISCOVERY] = discovery
            _LOGGER.debug("Starting dyson discovery")
            discovery.start_discovery(await async_get_instance(hass))

            def stop_discovery(_):
                _LOGGER.debug("Stopping dyson discovery")
                discovery.stop_discovery()

            hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                       stop_discovery)

        await hass.async_add_executor_job(discovery.register_device, device,
                                          setup_entry)

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

    client = ZwaveClient(entry.data[CONF_URL], async_get_clientsession(hass))
    dev_reg = device_registry.async_get(hass)
    ent_reg = entity_registry.async_get(hass)

    @callback
    def async_on_node_ready(node: ZwaveNode) -> None:
        """Handle node ready event."""
        LOGGER.debug("Processing node %s", node)

        # register (or update) node in device registry
        register_node_in_dev_reg(hass, entry, dev_reg, client, node)

        # run discovery on all node values and create/update entities
        for disc_info in async_discover_values(node):
            LOGGER.debug("Discovered entity: %s", disc_info)

            # This migration logic was added in 2021.3 to handle a breaking change to
            # the value_id format. Some time in the future, this call (as well as the
            # helper functions) can be removed.
            async_migrate_discovered_value(ent_reg, client, disc_info)
            async_dispatcher_send(
                hass, f"{DOMAIN}_{entry.entry_id}_add_{disc_info.platform}",
                disc_info)
        # add listener for stateless node value notification events
        node.on(
            "value notification",
            lambda event: async_on_value_notification(event[
                "value_notification"]),
        )
        # add listener for stateless node notification events
        node.on("notification",
                lambda event: async_on_notification(event["notification"]))

    @callback
    def async_on_node_added(node: ZwaveNode) -> None:
        """Handle node added event."""
        # we only want to run discovery when the node has reached ready state,
        # otherwise we'll have all kinds of missing info issues.
        if node.ready:
            async_on_node_ready(node)
            return
        # if node is not yet ready, register one-time callback for ready state
        LOGGER.debug("Node added: %s - waiting for it to become ready",
                     node.node_id)
        node.once(
            "ready",
            lambda event: async_on_node_ready(event["node"]),
        )
        # we do submit the node to device registry so user has
        # some visual feedback that something is (in the process of) being added
        register_node_in_dev_reg(hass, entry, dev_reg, client, node)

    @callback
    def async_on_node_removed(node: ZwaveNode) -> None:
        """Handle node removed event."""
        # grab device in device registry attached to this node
        dev_id = get_device_id(client, node)
        device = dev_reg.async_get_device({dev_id})
        # note: removal of entity registry entry is handled by core
        dev_reg.async_remove_device(device.id)  # type: ignore

    @callback
    def async_on_value_notification(notification: ValueNotification) -> None:
        """Relay stateless value notification events from Z-Wave nodes to hass."""
        device = dev_reg.async_get_device(
            {get_device_id(client, notification.node)})
        raw_value = value = notification.value
        if notification.metadata.states:
            value = notification.metadata.states.get(str(value), value)
        hass.bus.async_fire(
            ZWAVE_JS_VALUE_NOTIFICATION_EVENT,
            {
                ATTR_DOMAIN: DOMAIN,
                ATTR_NODE_ID: notification.node.node_id,
                ATTR_HOME_ID: client.driver.controller.home_id,
                ATTR_ENDPOINT: notification.endpoint,
                ATTR_DEVICE_ID: device.id,  # type: ignore
                ATTR_COMMAND_CLASS: notification.command_class,
                ATTR_COMMAND_CLASS_NAME: notification.command_class_name,
                ATTR_LABEL: notification.metadata.label,
                ATTR_PROPERTY: notification.property_,
                ATTR_PROPERTY_NAME: notification.property_name,
                ATTR_PROPERTY_KEY: notification.property_key,
                ATTR_PROPERTY_KEY_NAME: notification.property_key_name,
                ATTR_VALUE: value,
                ATTR_VALUE_RAW: raw_value,
            },
        )

    @callback
    def async_on_notification(
        notification: EntryControlNotification | NotificationNotification,
    ) -> None:
        """Relay stateless notification events from Z-Wave nodes to hass."""
        device = dev_reg.async_get_device(
            {get_device_id(client, notification.node)})
        event_data = {
            ATTR_DOMAIN: DOMAIN,
            ATTR_NODE_ID: notification.node.node_id,
            ATTR_HOME_ID: client.driver.controller.home_id,
            ATTR_DEVICE_ID: device.id,  # type: ignore
            ATTR_COMMAND_CLASS: notification.command_class,
        }

        if isinstance(notification, EntryControlNotification):
            event_data.update({
                ATTR_COMMAND_CLASS_NAME: "Entry Control",
                ATTR_EVENT_TYPE: notification.event_type,
                ATTR_DATA_TYPE: notification.data_type,
                ATTR_EVENT_DATA: notification.event_data,
            })
        else:
            event_data.update({
                ATTR_COMMAND_CLASS_NAME: "Notification",
                ATTR_LABEL: notification.label,
                ATTR_TYPE: notification.type_,
                ATTR_EVENT: notification.event,
                ATTR_EVENT_LABEL: notification.event_label,
                ATTR_PARAMETERS: notification.parameters,
            })

        hass.bus.async_fire(ZWAVE_JS_NOTIFICATION_EVENT, event_data)

    entry_hass_data: dict = hass.data[DOMAIN].setdefault(entry.entry_id, {})
    # connect and throw error if connection failed
    try:
        async with timeout(CONNECT_TIMEOUT):
            await client.connect()
    except InvalidServerVersion as err:
        if not entry_hass_data.get(DATA_INVALID_SERVER_VERSION_LOGGED):
            LOGGER.error("Invalid server version: %s", err)
            entry_hass_data[DATA_INVALID_SERVER_VERSION_LOGGED] = True
        if use_addon:
            async_ensure_addon_updated(hass)
        raise ConfigEntryNotReady from err
    except (asyncio.TimeoutError, BaseZwaveJSServerError) as err:
        if not entry_hass_data.get(DATA_CONNECT_FAILED_LOGGED):
            LOGGER.error("Failed to connect: %s", err)
            entry_hass_data[DATA_CONNECT_FAILED_LOGGED] = True
        raise ConfigEntryNotReady from err
    else:
        LOGGER.info("Connected to Zwave JS Server")
        entry_hass_data[DATA_CONNECT_FAILED_LOGGED] = False
        entry_hass_data[DATA_INVALID_SERVER_VERSION_LOGGED] = False

    unsubscribe_callbacks: list[Callable] = []
    entry_hass_data[DATA_CLIENT] = client
    entry_hass_data[DATA_UNSUBSCRIBE] = unsubscribe_callbacks

    services = ZWaveServices(hass, ent_reg)
    services.async_register()

    # Set up websocket API
    async_register_api(hass)

    async def start_platforms() -> None:
        """Start platforms and perform discovery."""
        # wait until all required platforms are ready
        await asyncio.gather(*[
            hass.config_entries.async_forward_entry_setup(entry, platform)
            for platform in PLATFORMS
        ])

        driver_ready = asyncio.Event()

        async def handle_ha_shutdown(event: Event) -> None:
            """Handle HA shutdown."""
            await disconnect_client(hass, entry, client, listen_task,
                                    platform_task)

        listen_task = asyncio.create_task(
            client_listen(hass, entry, client, driver_ready))
        entry_hass_data[DATA_CLIENT_LISTEN_TASK] = listen_task
        unsubscribe_callbacks.append(
            hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP,
                                  handle_ha_shutdown))

        try:
            await driver_ready.wait()
        except asyncio.CancelledError:
            LOGGER.debug("Cancelling start platforms")
            return

        LOGGER.info("Connection to Zwave JS Server initialized")

        # Check for nodes that no longer exist and remove them
        stored_devices = device_registry.async_entries_for_config_entry(
            dev_reg, entry.entry_id)
        known_devices = [
            dev_reg.async_get_device({get_device_id(client, node)})
            for node in client.driver.controller.nodes.values()
        ]

        # Devices that are in the device registry that are not known by the controller can be removed
        for device in stored_devices:
            if device not in known_devices:
                dev_reg.async_remove_device(device.id)

        # run discovery on all ready nodes
        for node in client.driver.controller.nodes.values():
            async_on_node_added(node)

        # listen for new nodes being added to the mesh
        client.driver.controller.on(
            "node added", lambda event: async_on_node_added(event["node"]))
        # listen for nodes being removed from the mesh
        # NOTE: This will not remove nodes that were removed when HA was not running
        client.driver.controller.on(
            "node removed", lambda event: async_on_node_removed(event["node"]))

    platform_task = hass.async_create_task(start_platforms())
    entry_hass_data[DATA_START_PLATFORM_TASK] = platform_task

    return True
Beispiel #31
0
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the ISY 994 platform."""
    hass.data[ISY994_NODES] = {}
    for domain in SUPPORTED_DOMAINS:
        hass.data[ISY994_NODES][domain] = []

    hass.data[ISY994_WEATHER] = []

    hass.data[ISY994_PROGRAMS] = {}
    for domain in SUPPORTED_DOMAINS:
        hass.data[ISY994_PROGRAMS][domain] = []

    isy_config = config.get(DOMAIN)

    user = isy_config.get(CONF_USERNAME)
    password = isy_config.get(CONF_PASSWORD)
    tls_version = isy_config.get(CONF_TLS_VER)
    host = urlparse(isy_config.get(CONF_HOST))
    ignore_identifier = isy_config.get(CONF_IGNORE_STRING)
    sensor_identifier = isy_config.get(CONF_SENSOR_STRING)
    enable_climate = isy_config.get(CONF_ENABLE_CLIMATE)

    if host.scheme == 'http':
        https = False
        port = host.port or 80
    elif host.scheme == 'https':
        https = True
        port = host.port or 443
    else:
        _LOGGER.error("isy994 host value in configuration is invalid")
        return False

    import PyISY
    # Connect to ISY controller.
    isy = PyISY.ISY(host.hostname,
                    port,
                    username=user,
                    password=password,
                    use_https=https,
                    tls_ver=tls_version,
                    log=_LOGGER)
    if not isy.connected:
        return False

    _categorize_nodes(hass, isy.nodes, ignore_identifier, sensor_identifier)
    _categorize_programs(hass, isy.programs)

    if enable_climate and isy.configuration.get('Weather Information'):
        _categorize_weather(hass, isy.climate)

    def stop(event: object) -> None:
        """Stop ISY auto updates."""
        isy.auto_update = False

    # Listen for HA stop to disconnect.
    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop)

    # Load platforms for the devices in the ISY controller that we support.
    for component in SUPPORTED_DOMAINS:
        discovery.load_platform(hass, component, DOMAIN, {}, config)

    isy.auto_update = True
    return True
Beispiel #32
0
async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the KEF platform."""
    if DOMAIN not in hass.data:
        hass.data[DOMAIN] = {}

    host = config[CONF_HOST]
    speaker_type = config[CONF_TYPE]
    port = config[CONF_PORT]
    name = config[CONF_NAME]
    maximum_volume = config[CONF_MAX_VOLUME]
    volume_step = config[CONF_VOLUME_STEP]
    inverse_speaker_mode = config[CONF_INVERSE_SPEAKER_MODE]
    supports_on = config[CONF_SUPPORTS_ON]
    standby_time = config.get(CONF_STANDBY_TIME)

    sources = SOURCES[speaker_type]

    _LOGGER.debug(
        "Setting up %s with host: %s, port: %s, name: %s, sources: %s",
        DOMAIN,
        host,
        port,
        name,
        sources,
    )

    mode = get_ip_mode(host)
    mac = await hass.async_add_executor_job(
        partial(get_mac_address, **{mode: host}))
    if mac is None:
        raise PlatformNotReady("Cannot get the ip address of kef speaker.")

    unique_id = f"kef-{mac}"

    media_player = KefMediaPlayer(
        name,
        host,
        port,
        maximum_volume,
        volume_step,
        standby_time,
        inverse_speaker_mode,
        supports_on,
        sources,
        speaker_type,
        loop=hass.loop,
        unique_id=unique_id,
    )

    if host in hass.data[DOMAIN]:
        _LOGGER.debug("%s is already configured", host)
    else:
        hass.data[DOMAIN][host] = media_player
        async_add_entities([media_player], update_before_add=True)

    platform = entity_platform.async_get_current_platform()

    platform.async_register_entity_service(
        SERVICE_MODE,
        {
            vol.Optional("desk_mode"): cv.boolean,
            vol.Optional("wall_mode"): cv.boolean,
            vol.Optional("phase_correction"): cv.boolean,
            vol.Optional("high_pass"): cv.boolean,
            vol.Optional("sub_polarity"): vol.In(["-", "+"]),
            vol.Optional("bass_extension"): vol.In(
                ["Less", "Standard", "Extra"]),
        },
        "set_mode",
    )
    platform.async_register_entity_service(SERVICE_UPDATE_DSP, {},
                                           "update_dsp")

    def add_service(name, which, option):
        options = DSP_OPTION_MAPPING[which]
        dtype = type(options[0])  # int or float
        platform.async_register_entity_service(
            name,
            {
                vol.Required(option):
                vol.All(vol.Coerce(float), vol.Coerce(dtype), vol.In(options))
            },
            f"set_{which}",
        )

    add_service(SERVICE_DESK_DB, "desk_db", "db_value")
    add_service(SERVICE_WALL_DB, "wall_db", "db_value")
    add_service(SERVICE_TREBLE_DB, "treble_db", "db_value")
    add_service(SERVICE_HIGH_HZ, "high_hz", "hz_value")
    add_service(SERVICE_LOW_HZ, "low_hz", "hz_value")
    add_service(SERVICE_SUB_DB, "sub_db", "db_value")
Beispiel #33
0
def setup(hass: HomeAssistant, base_config: ConfigType) -> bool:  # noqa: C901
    """Set up the CEC capability."""

    hass.data[DOMAIN] = {}

    # Parse configuration into a dict of device name to physical address
    # represented as a list of four elements.
    device_aliases = {}
    devices = base_config[DOMAIN].get(CONF_DEVICES, {})
    _LOGGER.debug("Parsing config %s", devices)
    device_aliases.update(parse_mapping(devices))
    _LOGGER.debug("Parsed devices: %s", device_aliases)

    platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH)

    loop = (
        # Create own thread if more than 1 CPU
        hass.loop if multiprocessing.cpu_count() < 2 else None)
    host = base_config[DOMAIN].get(CONF_HOST)
    display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME,
                                           DEFAULT_DISPLAY_NAME)
    if host:
        adapter = TcpAdapter(host, name=display_name, activate_source=False)
    else:
        adapter = CecAdapter(name=display_name[:12], activate_source=False)
    hdmi_network = HDMINetwork(adapter, loop=loop)

    def _adapter_watchdog(now=None):
        _LOGGER.debug("Reached _adapter_watchdog")
        event.call_later(hass, WATCHDOG_INTERVAL, _adapter_watchdog)
        if not adapter.initialized:
            _LOGGER.info("Adapter not initialized; Trying to restart")
            hass.bus.fire(EVENT_HDMI_CEC_UNAVAILABLE)
            adapter.init()

    @callback
    def _async_initialized_callback(*_: Any):
        """Add watchdog on initialization."""
        return event.async_call_later(hass, WATCHDOG_INTERVAL,
                                      _adapter_watchdog)

    hdmi_network.set_initialized_callback(_async_initialized_callback)

    def _volume(call: ServiceCall) -> None:
        """Increase/decrease volume and mute/unmute system."""
        mute_key_mapping = {
            ATTR_TOGGLE: KEY_MUTE_TOGGLE,
            ATTR_ON: KEY_MUTE_ON,
            ATTR_OFF: KEY_MUTE_OFF,
        }
        for cmd, att in call.data.items():
            if cmd == CMD_UP:
                _process_volume(KEY_VOLUME_UP, att)
            elif cmd == CMD_DOWN:
                _process_volume(KEY_VOLUME_DOWN, att)
            elif cmd == CMD_MUTE:
                hdmi_network.send_command(
                    KeyPressCommand(mute_key_mapping[att],
                                    dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
                _LOGGER.info("Audio muted")
            else:
                _LOGGER.warning("Unknown command %s", cmd)

    def _process_volume(cmd, att):
        if isinstance(att, (str, )):
            att = att.strip()
        if att == CMD_PRESS:
            hdmi_network.send_command(
                KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
        elif att == CMD_RELEASE:
            hdmi_network.send_command(KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
        else:
            att = 1 if att == "" else int(att)
            for _ in range(0, att):
                hdmi_network.send_command(
                    KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))

    def _tx(call: ServiceCall) -> None:
        """Send CEC command."""
        data = call.data
        if ATTR_RAW in data:
            command = CecCommand(data[ATTR_RAW])
        else:
            if ATTR_SRC in data:
                src = data[ATTR_SRC]
            else:
                src = ADDR_UNREGISTERED
            if ATTR_DST in data:
                dst = data[ATTR_DST]
            else:
                dst = ADDR_BROADCAST
            if ATTR_CMD in data:
                cmd = data[ATTR_CMD]
            else:
                _LOGGER.error("Attribute 'cmd' is missing")
                return
            if ATTR_ATT in data:
                if isinstance(data[ATTR_ATT], (list, )):
                    att = data[ATTR_ATT]
                else:
                    att = reduce(lambda x, y: f"{x}:{y:x}", data[ATTR_ATT])
            else:
                att = ""
            command = CecCommand(cmd, dst, src, att)
        hdmi_network.send_command(command)

    def _standby(call: ServiceCall) -> None:
        hdmi_network.standby()

    def _power_on(call: ServiceCall) -> None:
        hdmi_network.power_on()

    def _select_device(call: ServiceCall) -> None:
        """Select the active device."""
        if not (addr := call.data[ATTR_DEVICE]):
            _LOGGER.error("Device not found: %s", call.data[ATTR_DEVICE])
            return
        if addr in device_aliases:
            addr = device_aliases[addr]
        else:
            entity = hass.states.get(addr)
            _LOGGER.debug("Selecting entity %s", entity)
            if entity is not None:
                addr = entity.attributes["physical_address"]
                _LOGGER.debug("Address acquired: %s", addr)
                if addr is None:
                    _LOGGER.error("Device %s has not physical address",
                                  call.data[ATTR_DEVICE])
                    return
        if not isinstance(addr, (PhysicalAddress, )):
            addr = PhysicalAddress(addr)
        hdmi_network.active_source(addr)
        _LOGGER.info("Selected %s (%s)", call.data[ATTR_DEVICE], addr)
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)

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

    core_config = config.get(core.DOMAIN, {})
    api_password = config.get("http", {}).get("api_password")
    trusted_networks = config.get("http", {}).get("trusted_networks")

    try:
        await conf_util.async_process_ha_core_config(hass, core_config,
                                                     api_password,
                                                     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

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

    # Merge packages
    await 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_initialize()

    await _async_set_up_integrations(hass, config)

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

    if sys.version_info[:3] < (3, 6, 1):
        msg = ("Python 3.6.0 support is deprecated and will "
               "be removed in the first release after October 2. Please "
               "upgrade Python to 3.6.1 or higher.")
        _LOGGER.warning(msg)
        hass.components.persistent_notification.async_create(
            msg, "Python version", "python_version")

    return hass
Beispiel #35
0
async def async_setup_entry(
    hass: HomeAssistant, entry: config_entries.ConfigEntry
) -> bool:
    """Set up the ISY 994 integration."""
    # As there currently is no way to import options from yaml
    # when setting up a config entry, we fallback to adding
    # the options to the config entry and pull them out here if
    # they are missing from the options
    _async_import_options_from_data_if_missing(hass, entry)

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

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

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

    hass_isy_data[ISY994_VARIABLES] = []

    isy_config = entry.data
    isy_options = entry.options

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

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

    if host.scheme == "http":
        https = False
        port = host.port or 80
    elif host.scheme == "https":
        https = True
        port = host.port or 443
    else:
        _LOGGER.error("isy994 host value in configuration is invalid")
        return False

    # Connect to ISY controller.
    isy = await hass.async_add_executor_job(
        partial(
            ISY,
            host.hostname,
            port,
            username=user,
            password=password,
            use_https=https,
            tls_ver=tls_version,
            log=_LOGGER,
            webroot=host.path,
        )
    )
    if not isy.connected:
        return False

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

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

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

    # Load platforms for the devices in the ISY controller that we support.
    for platform in SUPPORTED_PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, platform)
        )

    def _start_auto_update() -> None:
        """Start isy auto update."""
        _LOGGER.debug("ISY Starting Event Stream and automatic updates.")
        isy.auto_update = True

    await hass.async_add_executor_job(_start_auto_update)

    undo_listener = entry.add_update_listener(_async_update_listener)

    hass_isy_data[UNDO_UPDATE_LISTENER] = undo_listener

    # Register Integration-wide Services:
    async_setup_services(hass)

    return True
Beispiel #36
0
async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool:
    """Set up the Traccar component."""
    hass.data[DOMAIN] = {"devices": set(), "unsub_device_tracker": {}}
    return True
Beispiel #37
0
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) \
                           -> 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)

    if sys.version_info[:2] < (3, 5):
        _LOGGER.warning(
            'Python 3.4 support has been deprecated and will be removed in '
            'the beginning of 2018. Please upgrade Python or your operating '
            'system. More info: https://home-assistant.io/blog/2017/10/06/'
            'deprecating-python-3.4-support/')

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

    try:
        yield from 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

    yield from hass.async_add_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")

    if not loader.PREPARED:
        yield from hass.async_add_job(loader.prepare, hass)

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

    # Make a copy because we are mutating it.
    # Use OrderedDict in case original one was one.
    # Convert values to dictionaries if they are None
    new_config = OrderedDict()
    for key, value in config.items():
        new_config[key] = value or {}
    config = new_config

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

    # setup components
    # pylint: disable=not-an-iterable
    res = yield from core_components.async_setup(hass, config)
    if not res:
        _LOGGER.error("Home Assistant core failed to initialize. "
                      "further initialization aborted")
        return hass

    yield from 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_add_job(async_setup_component(hass, component, config))

    yield from hass.async_block_till_done()

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

    yield from hass.async_block_till_done()

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

    async_register_signal_handling(hass)
    return hass
async def async_setup(hass: HomeAssistant, config: dict):
    """Set up the WiLight with Config Flow component."""

    hass.data[DOMAIN] = {}

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

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

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

    try:
        await conf_util.async_process_ha_core_config(
            hass, core_config, api_password, 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

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

    # Merge packages
    await 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_initialize()

    await _async_set_up_integrations(hass, config)

    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
Beispiel #40
0
def _async_setup_component(hass: core.HomeAssistant,
                           domain: str, config) -> bool:
    """Set up a component for Home Assistant.

    This method is a coroutine.
    """
    def log_error(msg, link=True):
        """Log helper."""
        _LOGGER.error("Setup failed for %s: %s", domain, msg)
        async_notify_setup_error(hass, domain, link)

    component = loader.get_component(domain)

    if not component:
        log_error("Component not found.", False)
        return False

    # Validate no circular dependencies
    components = loader.load_order_component(domain)

    # OrderedSet is empty if component or dependencies could not be resolved
    if not components:
        log_error("Unable to resolve component or dependencies.")
        return False

    processed_config = \
        conf_util.async_process_component_config(hass, config, domain)

    if processed_config is None:
        log_error("Invalid config.")
        return False

    try:
        yield from async_process_deps_reqs(hass, config, domain, component)
    except HomeAssistantError as err:
        log_error(str(err))
        return False

    start = timer()
    _LOGGER.info("Setting up %s", domain)

    if hasattr(component, 'PLATFORM_SCHEMA'):
        # Entity components have their own warning
        warn_task = None
    else:
        warn_task = hass.loop.call_later(
            SLOW_SETUP_WARNING, _LOGGER.warning,
            "Setup of %s is taking over %s seconds.",
            domain, SLOW_SETUP_WARNING)

    try:
        if hasattr(component, 'async_setup'):
            result = yield from component.async_setup(hass, processed_config)
        else:
            result = yield from hass.async_add_job(
                component.setup, hass, processed_config)
    except Exception:  # pylint: disable=broad-except
        _LOGGER.exception("Error during setup of component %s", domain)
        async_notify_setup_error(hass, domain, True)
        return False
    finally:
        end = timer()
        if warn_task:
            warn_task.cancel()
    _LOGGER.info("Setup of domain %s took %.1f seconds.", domain, end - start)

    if result is False:
        log_error("Component failed to initialize.")
        return False
    elif result is not True:
        log_error("Component did not return boolean if setup was successful. "
                  "Disabling component.")
        loader.set_component(domain, None)
        return False

    for entry in hass.config_entries.async_entries(domain):
        yield from entry.async_setup(hass, component=component)

    hass.config.components.add(component.DOMAIN)

    # Cleanup
    if domain in hass.data[DATA_SETUP]:
        hass.data[DATA_SETUP].pop(domain)

    hass.bus.async_fire(
        EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}
    )

    return True
Beispiel #41
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
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) \
                           -> 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)

    if sys.version_info[:2] < (3, 5):
        _LOGGER.warning(
            'Python 3.4 support has been deprecated and will be removed in '
            'the beginning of 2018. Please upgrade Python or your operating '
            'system. More info: https://home-assistant.io/blog/2017/10/06/'
            'deprecating-python-3.4-support/'
        )

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

    try:
        yield from 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

    yield from hass.async_add_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")

    if not loader.PREPARED:
        yield from hass.async_add_job(loader.prepare, hass)

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

    # Make a copy because we are mutating it.
    # Use OrderedDict in case original one was one.
    # Convert values to dictionaries if they are None
    new_config = OrderedDict()
    for key, value in config.items():
        new_config[key] = value or {}
    config = new_config

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

    # setup components
    # pylint: disable=not-an-iterable
    res = yield from core_components.async_setup(hass, config)
    if not res:
        _LOGGER.error("Home Assistant core failed to initialize. "
                      "further initialization aborted")
        return hass

    yield from 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_add_job(async_setup_component(hass, component, config))

    yield from hass.async_block_till_done()

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

    yield from hass.async_block_till_done()

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

    async_register_signal_handling(hass)
    return hass
Beispiel #43
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
def async_enable_logging(
    hass: core.HomeAssistant,
    verbose: bool = False,
    log_rotate_days: Optional[int] = None,
    log_file: Optional[str] = None,
    log_no_color: bool = False,
) -> None:
    """Set up the logging.

    This method must be run in the event loop.
    """
    fmt = "%(asctime)s %(levelname)s (%(threadName)s) " "[%(name)s] %(message)s"
    datefmt = "%Y-%m-%d %H:%M:%S"

    if not log_no_color:
        try:
            from colorlog import ColoredFormatter

            # basicConfig must be called after importing colorlog in order to
            # ensure that the handlers it sets up wraps the correct streams.
            logging.basicConfig(level=logging.INFO)

            colorfmt = f"%(log_color)s{fmt}%(reset)s"
            logging.getLogger().handlers[0].setFormatter(
                ColoredFormatter(
                    colorfmt,
                    datefmt=datefmt,
                    reset=True,
                    log_colors={
                        "DEBUG": "cyan",
                        "INFO": "green",
                        "WARNING": "yellow",
                        "ERROR": "red",
                        "CRITICAL": "red",
                    },
                ))
        except ImportError:
            pass

    # If the above initialization failed for any reason, setup the default
    # formatting.  If the above succeeds, this wil result in a no-op.
    logging.basicConfig(format=fmt, datefmt=datefmt, level=logging.INFO)

    # Suppress overly verbose logs from libraries that aren't helpful
    logging.getLogger("requests").setLevel(logging.WARNING)
    logging.getLogger("urllib3").setLevel(logging.WARNING)
    logging.getLogger("aiohttp.access").setLevel(logging.WARNING)

    # Log errors to a file if we have write access to file or config dir
    if log_file is None:
        err_log_path = hass.config.path(ERROR_LOG_FILENAME)
    else:
        err_log_path = os.path.abspath(log_file)

    err_path_exists = os.path.isfile(err_log_path)
    err_dir = os.path.dirname(err_log_path)

    # Check if we can write to the error log if it exists or that
    # we can create files in the containing directory if not.
    if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
            not err_path_exists and os.access(err_dir, os.W_OK)):

        if log_rotate_days:
            err_handler: logging.FileHandler = logging.handlers.TimedRotatingFileHandler(
                err_log_path, when="midnight", backupCount=log_rotate_days)
        else:
            err_handler = logging.FileHandler(err_log_path,
                                              mode="w",
                                              delay=True)

        err_handler.setLevel(logging.INFO if verbose else logging.WARNING)
        err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt))

        async_handler = AsyncHandler(hass.loop, err_handler)

        async def async_stop_async_handler(_: Any) -> None:
            """Cleanup async handler."""
            logging.getLogger("").removeHandler(async_handler)  # type: ignore
            await async_handler.async_close(blocking=True)

        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE,
                                   async_stop_async_handler)

        logger = logging.getLogger("")
        logger.addHandler(async_handler)  # type: ignore
        logger.setLevel(logging.INFO)

        # Save the log file location for access by other components.
        hass.data[DATA_LOGGING] = err_log_path
    else:
        _LOGGER.error("Unable to set up error log %s (access denied)",
                      err_log_path)
Beispiel #45
0
def _compile_statistics(  # noqa: C901
    hass: HomeAssistant,
    session: Session,
    start: datetime.datetime,
    end: datetime.datetime,
) -> list[StatisticResult]:
    """Compile statistics for all entities during start-end."""
    result: list[StatisticResult] = []

    sensor_states = _get_sensor_states(hass)
    wanted_statistics = _wanted_statistics(sensor_states)
    old_metadatas = statistics.get_metadata_with_session(
        hass, session, statistic_ids=[i.entity_id for i in sensor_states])

    # Get history between start and end
    entities_full_history = [
        i.entity_id for i in sensor_states
        if "sum" in wanted_statistics[i.entity_id]
    ]
    history_list = {}
    if entities_full_history:
        history_list = history.get_significant_states_with_session(  # type: ignore[no-untyped-call]
            hass,
            session,
            start - datetime.timedelta.resolution,
            end,
            entity_ids=entities_full_history,
            significant_changes_only=False,
        )
    entities_significant_history = [
        i.entity_id for i in sensor_states
        if "sum" not in wanted_statistics[i.entity_id]
    ]
    if entities_significant_history:
        _history_list = history.get_significant_states_with_session(  # type: ignore[no-untyped-call]
            hass,
            session,
            start - datetime.timedelta.resolution,
            end,
            entity_ids=entities_significant_history,
        )
        history_list = {**history_list, **_history_list}
    # If there are no recent state changes, the sensor's state may already be pruned
    # from the recorder. Get the state from the state machine instead.
    for _state in sensor_states:
        if _state.entity_id not in history_list:
            history_list[_state.entity_id] = (_state, )

    for _state in sensor_states:  # pylint: disable=too-many-nested-blocks
        entity_id = _state.entity_id
        if entity_id not in history_list:
            continue

        state_class = _state.attributes[ATTR_STATE_CLASS]
        device_class = _state.attributes.get(ATTR_DEVICE_CLASS)
        entity_history = history_list[entity_id]
        unit, fstates = _normalize_states(hass, session, old_metadatas,
                                          entity_history, device_class,
                                          entity_id)

        if not fstates:
            continue

        # Check metadata
        if old_metadata := old_metadatas.get(entity_id):
            if old_metadata[1]["unit_of_measurement"] != unit:
                if WARN_UNSTABLE_UNIT not in hass.data:
                    hass.data[WARN_UNSTABLE_UNIT] = set()
                if entity_id not in hass.data[WARN_UNSTABLE_UNIT]:
                    hass.data[WARN_UNSTABLE_UNIT].add(entity_id)
                    _LOGGER.warning(
                        "The %sunit of %s (%s) does not match the unit of already "
                        "compiled statistics (%s). Generation of long term statistics "
                        "will be suppressed unless the unit changes back to %s. "
                        "Go to %s to fix this",
                        "normalized "
                        if device_class in DEVICE_CLASS_UNITS else "",
                        entity_id,
                        unit,
                        old_metadata[1]["unit_of_measurement"],
                        old_metadata[1]["unit_of_measurement"],
                        LINK_DEV_STATISTICS,
                    )
                continue

        # Set meta data
        meta: StatisticMetaData = {
            "has_mean": "mean" in wanted_statistics[entity_id],
            "has_sum": "sum" in wanted_statistics[entity_id],
            "name": None,
            "source": RECORDER_DOMAIN,
            "statistic_id": entity_id,
            "unit_of_measurement": unit,
        }

        # Make calculations
        stat: StatisticData = {"start": start}
        if "max" in wanted_statistics[entity_id]:
            stat["max"] = max(*itertools.islice(
                zip(*fstates), 1))  # type: ignore[typeddict-item]
        if "min" in wanted_statistics[entity_id]:
            stat["min"] = min(*itertools.islice(
                zip(*fstates), 1))  # type: ignore[typeddict-item]

        if "mean" in wanted_statistics[entity_id]:
            stat["mean"] = _time_weighted_average(fstates, start, end)

        if "sum" in wanted_statistics[entity_id]:
            last_reset = old_last_reset = None
            new_state = old_state = None
            _sum = 0.0
            last_stats = statistics.get_last_short_term_statistics(
                hass, 1, entity_id, False)
            if entity_id in last_stats:
                # We have compiled history for this sensor before, use that as a starting point
                last_reset = old_last_reset = last_stats[entity_id][0][
                    "last_reset"]
                new_state = old_state = last_stats[entity_id][0]["state"]
                _sum = last_stats[entity_id][0]["sum"] or 0.0

            for fstate, state in fstates:
                reset = False
                if (state_class != STATE_CLASS_TOTAL_INCREASING
                        and (last_reset := _last_reset_as_utc_isoformat(
                            state.attributes.get("last_reset"), entity_id)) !=
                        old_last_reset and last_reset is not None):
                    if old_state is None:
                        _LOGGER.info(
                            "Compiling initial sum statistics for %s, zero point set to %s",
                            entity_id,
                            fstate,
                        )
                    else:
                        _LOGGER.info(
                            "Detected new cycle for %s, last_reset set to %s (old last_reset %s)",
                            entity_id,
                            last_reset,
                            old_last_reset,
                        )
                    reset = True
                elif old_state is None and last_reset is None:
                    reset = True
                    _LOGGER.info(
                        "Compiling initial sum statistics for %s, zero point set to %s",
                        entity_id,
                        fstate,
                    )
                elif state_class == STATE_CLASS_TOTAL_INCREASING:
                    try:
                        if old_state is None or reset_detected(
                                hass, entity_id, fstate, new_state, state):
                            reset = True
                            _LOGGER.info(
                                "Detected new cycle for %s, value dropped from %s to %s, "
                                "triggered by state with last_updated set to %s",
                                entity_id,
                                new_state,
                                state.last_updated.isoformat(),
                                fstate,
                            )
                    except HomeAssistantError:
                        continue

                if reset:
                    # The sensor has been reset, update the sum
                    if old_state is not None:
                        _sum += new_state - old_state
                    # ..and update the starting point
                    new_state = fstate
                    old_last_reset = last_reset
                    # Force a new cycle for an existing sensor to start at 0
                    if old_state is not None:
                        old_state = 0.0
                    else:
                        old_state = new_state
                else:
                    new_state = fstate
def publish(hass: HomeAssistant, topic, payload, qos=0, retain=False) -> None:
    """Publish message to an MQTT topic."""
    hass.add_job(async_publish, hass, topic, payload, qos, retain)
Beispiel #47
0
def async_set_agent(hass: core.HomeAssistant,
                    agent: AbstractConversationAgent):
    """Set the agent to handle the conversations."""
    hass.data[DATA_AGENT] = agent
Beispiel #48
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)

        async with async_timeout.timeout(10):
            rooms = Rooms(pv_request)
            room_data = _async_map_data_by_id(
                (await rooms.get_resources())[ROOM_DATA])

        async with async_timeout.timeout(10):
            scenes = Scenes(pv_request)
            scene_data = _async_map_data_by_id(
                (await scenes.get_resources())[SCENE_DATA])

        async with async_timeout.timeout(10):
            shades = Shades(pv_request)
            shade_data = _async_map_data_by_id(
                (await shades.get_resources())[SHADE_DATA])
    except HUB_EXCEPTIONS as err:
        _LOGGER.error("Connection error to PowerView hub: %s", hub_address)
        raise ConfigEntryNotReady from err

    if not device_info:
        _LOGGER.error("Unable to initialize PowerView hub: %s", hub_address)
        raise ConfigEntryNotReady

    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("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 platform in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, platform))

    return True
Beispiel #49
0
async def async_process_ha_core_config(
        hass: HomeAssistant,
        config: Dict,
        api_password: Optional[str] = None,
        trusted_networks: Optional[Any] = None) -> None:
    """Process the [homeassistant] section from the configuration.

    This method is a coroutine.
    """
    config = CORE_CONFIG_SCHEMA(config)

    # Only load auth during startup.
    if not hasattr(hass, 'auth'):
        auth_conf = config.get(CONF_AUTH_PROVIDERS)

        if auth_conf is None:
            auth_conf = [{'type': 'homeassistant'}]
            if api_password:
                auth_conf.append({
                    'type': 'legacy_api_password',
                    'api_password': api_password,
                })
            if trusted_networks:
                auth_conf.append({
                    'type': 'trusted_networks',
                    'trusted_networks': trusted_networks,
                })

        mfa_conf = config.get(CONF_AUTH_MFA_MODULES, [
            {
                'type': 'totp',
                'id': 'totp',
                'name': 'Authenticator app'
            },
        ])

        setattr(hass, 'auth', await
                auth.auth_manager_from_config(hass, auth_conf, mfa_conf))

    hac = hass.config

    def set_time_zone(time_zone_str: Optional[str]) -> None:
        """Help to set the time zone."""
        if time_zone_str is None:
            return

        time_zone = date_util.get_time_zone(time_zone_str)

        if time_zone:
            hac.time_zone = time_zone
            date_util.set_default_time_zone(time_zone)
        else:
            _LOGGER.error("Received invalid time zone %s", time_zone_str)

    for key, attr in ((CONF_LATITUDE, 'latitude'), (CONF_LONGITUDE,
                                                    'longitude'),
                      (CONF_NAME, 'location_name'), (CONF_ELEVATION,
                                                     'elevation')):
        if key in config:
            setattr(hac, attr, config[key])

    set_time_zone(config.get(CONF_TIME_ZONE))

    # Init whitelist external dir
    hac.whitelist_external_dirs = {hass.config.path('www')}
    if CONF_WHITELIST_EXTERNAL_DIRS in config:
        hac.whitelist_external_dirs.update(
            set(config[CONF_WHITELIST_EXTERNAL_DIRS]))

    # Customize
    cust_exact = dict(config[CONF_CUSTOMIZE])
    cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN])
    cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB])

    for name, pkg in config[CONF_PACKAGES].items():
        pkg_cust = pkg.get(CONF_CORE)

        if pkg_cust is None:
            continue

        try:
            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
        except vol.Invalid:
            _LOGGER.warning("Package %s contains invalid customize", name)
            continue

        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])

    hass.data[DATA_CUSTOMIZE] = \
        EntityValues(cust_exact, cust_domain, cust_glob)

    if CONF_UNIT_SYSTEM in config:
        if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
            hac.units = IMPERIAL_SYSTEM
        else:
            hac.units = METRIC_SYSTEM
    elif CONF_TEMPERATURE_UNIT in config:
        unit = config[CONF_TEMPERATURE_UNIT]
        if unit == TEMP_CELSIUS:
            hac.units = METRIC_SYSTEM
        else:
            hac.units = IMPERIAL_SYSTEM
        _LOGGER.warning(
            "Found deprecated temperature unit in core "
            "configuration expected unit system. Replace '%s: %s' "
            "with '%s: %s'", CONF_TEMPERATURE_UNIT, unit, CONF_UNIT_SYSTEM,
            hac.units.name)

    # Shortcut if no auto-detection necessary
    if None not in (hac.latitude, hac.longitude, hac.units, hac.time_zone,
                    hac.elevation):
        return

    discovered = []  # type: List[Tuple[str, Any]]

    # If we miss some of the needed values, auto detect them
    if None in (hac.latitude, hac.longitude, hac.units, hac.time_zone):
        info = await hass.async_add_executor_job(loc_util.detect_location_info)

        if info is None:
            _LOGGER.error("Could not detect location information")
            return

        if hac.latitude is None and hac.longitude is None:
            hac.latitude, hac.longitude = (info.latitude, info.longitude)
            discovered.append(('latitude', hac.latitude))
            discovered.append(('longitude', hac.longitude))

        if hac.units is None:
            hac.units = METRIC_SYSTEM if info.use_metric else IMPERIAL_SYSTEM
            discovered.append((CONF_UNIT_SYSTEM, hac.units.name))

        if hac.location_name is None:
            hac.location_name = info.city
            discovered.append(('name', info.city))

        if hac.time_zone is None:
            set_time_zone(info.time_zone)
            discovered.append(('time_zone', info.time_zone))

    if hac.elevation is None and hac.latitude is not None and \
       hac.longitude is not None:
        elevation = await hass.async_add_executor_job(loc_util.elevation,
                                                      hac.latitude,
                                                      hac.longitude)
        hac.elevation = elevation
        discovered.append(('elevation', elevation))

    if discovered:
        _LOGGER.warning(
            "Incomplete core configuration. Auto detected %s",
            ", ".join('{}: {}'.format(key, val) for key, val in discovered))
Beispiel #50
0
async def async_setup(hass: HomeAssistant, config: dict):
    """Set up the kmtronic component."""
    hass.data[DOMAIN] = {}

    return True
Beispiel #51
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))
Beispiel #52
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 = {}
    hci_list = []
    bt_mac_list = []

    if not CONFIG_YAML:
        # Configuration in UI
        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"]

        if not config[CONF_BT_INTERFACE]:
            default_hci = list(BT_INTERFACES.keys())[list(BT_INTERFACES.values()).index(DEFAULT_BT_INTERFACE)]
            hci_list.append(int(default_hci))
            bt_mac_list.append(str(DEFAULT_BT_INTERFACE))
        else:
            bt_interface_list = config[CONF_BT_INTERFACE]
            for bt_mac in bt_interface_list:
                hci = list(BT_INTERFACES.keys())[list(BT_INTERFACES.values()).index(bt_mac)]
                hci_list.append(int(hci))
                bt_mac_list.append(str(bt_mac))
    else:
        # Configuration in YAML
        for key, value in CONFIG_YAML.items():
            config[key] = value
        _LOGGER.warning("Available Bluetooth interfaces for BLE monitor: %s", BT_MAC_INTERFACES)

        if config[CONF_HCI_INTERFACE]:
            # Configuration of BT interface with hci number
            for hci in CONFIG_YAML[CONF_HCI_INTERFACE]:
                try:
                    hci_list.append(int(hci))
                    bt_mac = BT_INTERFACES.get(hci)
                    if bt_mac:
                        bt_mac_list.append(str(bt_mac))
                    else:
                        _LOGGER.error("Bluetooth interface hci%i is not available", hci)
                except ValueError:
                    _LOGGER.error("Bluetooth interface hci%i is not available", hci)
        else:
            # Configuration of BT interface with mac address
            CONF_BT_INTERFACES = [x.upper() for x in CONFIG_YAML[CONF_BT_INTERFACE]]
            for bt_mac in CONF_BT_INTERFACES:
                try:
                    hci = list(BT_INTERFACES.keys())[list(BT_INTERFACES.values()).index(bt_mac)]
                    hci_list.append(int(hci))
                    bt_mac_list.append(str(bt_mac))
                except ValueError:
                    _LOGGER.error("Bluetooth interface with MAC address %s is not available", bt_mac)

    if not hci_list:
        # Fall back in case no hci interfaces are added
        default_hci = list(BT_INTERFACES.keys())[list(BT_INTERFACES.values()).index(DEFAULT_BT_INTERFACE)]
        hci_list.append(int(default_hci))
        bt_mac_list.append(str(DEFAULT_BT_INTERFACE))
        _LOGGER.warning("No configured Bluetooth interfaces was found, using default interface instead")

    config[CONF_HCI_INTERFACE] = hci_list
    config[CONF_BT_INTERFACE] = bt_mac_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)

    _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
Beispiel #53
0
async def async_process_ha_core_config(
        hass: HomeAssistant, config: Dict) -> None:
    """Process the [homeassistant] section from the configuration.

    This method is a coroutine.
    """
    config = CORE_CONFIG_SCHEMA(config)

    # Only load auth during startup.
    if not hasattr(hass, 'auth'):
        setattr(hass, 'auth', await auth.auth_manager_from_config(
            hass, config.get(CONF_AUTH_PROVIDERS, [])))

    hac = hass.config

    def set_time_zone(time_zone_str: Optional[str]) -> None:
        """Help to set the time zone."""
        if time_zone_str is None:
            return

        time_zone = date_util.get_time_zone(time_zone_str)

        if time_zone:
            hac.time_zone = time_zone
            date_util.set_default_time_zone(time_zone)
        else:
            _LOGGER.error("Received invalid time zone %s", time_zone_str)

    for key, attr in ((CONF_LATITUDE, 'latitude'),
                      (CONF_LONGITUDE, 'longitude'),
                      (CONF_NAME, 'location_name'),
                      (CONF_ELEVATION, 'elevation')):
        if key in config:
            setattr(hac, attr, config[key])

    set_time_zone(config.get(CONF_TIME_ZONE))

    # Init whitelist external dir
    hac.whitelist_external_dirs = {hass.config.path('www')}
    if CONF_WHITELIST_EXTERNAL_DIRS in config:
        hac.whitelist_external_dirs.update(
            set(config[CONF_WHITELIST_EXTERNAL_DIRS]))

    # Customize
    cust_exact = dict(config[CONF_CUSTOMIZE])
    cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN])
    cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB])

    for name, pkg in config[CONF_PACKAGES].items():
        pkg_cust = pkg.get(CONF_CORE)

        if pkg_cust is None:
            continue

        try:
            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
        except vol.Invalid:
            _LOGGER.warning("Package %s contains invalid customize", name)
            continue

        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])

    hass.data[DATA_CUSTOMIZE] = \
        EntityValues(cust_exact, cust_domain, cust_glob)

    if CONF_UNIT_SYSTEM in config:
        if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
            hac.units = IMPERIAL_SYSTEM
        else:
            hac.units = METRIC_SYSTEM
    elif CONF_TEMPERATURE_UNIT in config:
        unit = config[CONF_TEMPERATURE_UNIT]
        if unit == TEMP_CELSIUS:
            hac.units = METRIC_SYSTEM
        else:
            hac.units = IMPERIAL_SYSTEM
        _LOGGER.warning("Found deprecated temperature unit in core "
                        "configuration expected unit system. Replace '%s: %s' "
                        "with '%s: %s'", CONF_TEMPERATURE_UNIT, unit,
                        CONF_UNIT_SYSTEM, hac.units.name)

    # Shortcut if no auto-detection necessary
    if None not in (hac.latitude, hac.longitude, hac.units,
                    hac.time_zone, hac.elevation):
        return

    discovered = []  # type: List[Tuple[str, Any]]

    # If we miss some of the needed values, auto detect them
    if None in (hac.latitude, hac.longitude, hac.units,
                hac.time_zone):
        info = await hass.async_add_executor_job(
            loc_util.detect_location_info)

        if info is None:
            _LOGGER.error("Could not detect location information")
            return

        if hac.latitude is None and hac.longitude is None:
            hac.latitude, hac.longitude = (info.latitude, info.longitude)
            discovered.append(('latitude', hac.latitude))
            discovered.append(('longitude', hac.longitude))

        if hac.units is None:
            hac.units = METRIC_SYSTEM if info.use_metric else IMPERIAL_SYSTEM
            discovered.append((CONF_UNIT_SYSTEM, hac.units.name))

        if hac.location_name is None:
            hac.location_name = info.city
            discovered.append(('name', info.city))

        if hac.time_zone is None:
            set_time_zone(info.time_zone)
            discovered.append(('time_zone', info.time_zone))

    if hac.elevation is None and hac.latitude is not None and \
       hac.longitude is not None:
        elevation = await hass.async_add_executor_job(
            loc_util.elevation, hac.latitude, hac.longitude)
        hac.elevation = elevation
        discovered.append(('elevation', elevation))

    if discovered:
        _LOGGER.warning(
            "Incomplete core configuration. Auto detected %s",
            ", ".join('{}: {}'.format(key, val) for key, val in discovered))
Beispiel #54
0
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Unload a config entry."""
    # unload srp client
    hass.data[SRP_ENERGY_DOMAIN] = None
    # Remove config entry
    return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
Beispiel #55
0
def async_enable_logging(hass: core.HomeAssistant,
                         verbose: bool = False,
                         log_rotate_days: Optional[int] = None,
                         log_file: Optional[str] = None,
                         log_no_color: bool = False) -> None:
    """Set up the logging.

    This method must be run in the event loop.
    """
    fmt = ("%(asctime)s %(levelname)s (%(threadName)s) "
           "[%(name)s] %(message)s")
    datefmt = '%Y-%m-%d %H:%M:%S'

    if not log_no_color:
        try:
            from colorlog import ColoredFormatter
            # basicConfig must be called after importing colorlog in order to
            # ensure that the handlers it sets up wraps the correct streams.
            logging.basicConfig(level=logging.INFO)

            colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
            logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
                colorfmt,
                datefmt=datefmt,
                reset=True,
                log_colors={
                    'DEBUG': 'cyan',
                    'INFO': 'green',
                    'WARNING': 'yellow',
                    'ERROR': 'red',
                    'CRITICAL': 'red',
                }
            ))
        except ImportError:
            pass

    # If the above initialization failed for any reason, setup the default
    # formatting.  If the above succeeds, this wil result in a no-op.
    logging.basicConfig(format=fmt, datefmt=datefmt, level=logging.INFO)

    # Suppress overly verbose logs from libraries that aren't helpful
    logging.getLogger('requests').setLevel(logging.WARNING)
    logging.getLogger('urllib3').setLevel(logging.WARNING)
    logging.getLogger('aiohttp.access').setLevel(logging.WARNING)

    # Log errors to a file if we have write access to file or config dir
    if log_file is None:
        err_log_path = hass.config.path(ERROR_LOG_FILENAME)
    else:
        err_log_path = os.path.abspath(log_file)

    err_path_exists = os.path.isfile(err_log_path)
    err_dir = os.path.dirname(err_log_path)

    # Check if we can write to the error log if it exists or that
    # we can create files in the containing directory if not.
    if (err_path_exists and os.access(err_log_path, os.W_OK)) or \
       (not err_path_exists and os.access(err_dir, os.W_OK)):

        if log_rotate_days:
            err_handler = logging.handlers.TimedRotatingFileHandler(
                err_log_path, when='midnight',
                backupCount=log_rotate_days)  # type: logging.FileHandler
        else:
            err_handler = logging.FileHandler(
                err_log_path, mode='w', delay=True)

        err_handler.setLevel(logging.INFO if verbose else logging.WARNING)
        err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt))

        async_handler = AsyncHandler(hass.loop, err_handler)

        async def async_stop_async_handler(_: Any) -> None:
            """Cleanup async handler."""
            logging.getLogger('').removeHandler(async_handler)  # type: ignore
            await async_handler.async_close(blocking=True)

        hass.bus.async_listen_once(
            EVENT_HOMEASSISTANT_CLOSE, async_stop_async_handler)

        logger = logging.getLogger('')
        logger.addHandler(async_handler)  # type: ignore
        logger.setLevel(logging.INFO)

        # Save the log file location for access by other components.
        hass.data[DATA_LOGGING] = err_log_path
    else:
        _LOGGER.error(
            "Unable to set up error log %s (access denied)", err_log_path)
Beispiel #56
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the camera component."""
    component = hass.data[DOMAIN] = EntityComponent(_LOGGER, DOMAIN, hass,
                                                    SCAN_INTERVAL)

    prefs = CameraPreferences(hass)
    await prefs.async_initialize()
    hass.data[DATA_CAMERA_PREFS] = prefs

    hass.http.register_view(CameraImageView(component))
    hass.http.register_view(CameraMjpegStream(component))
    hass.components.websocket_api.async_register_command(
        WS_TYPE_CAMERA_THUMBNAIL, websocket_camera_thumbnail,
        SCHEMA_WS_CAMERA_THUMBNAIL)
    hass.components.websocket_api.async_register_command(ws_camera_stream)
    hass.components.websocket_api.async_register_command(
        ws_camera_web_rtc_offer)
    hass.components.websocket_api.async_register_command(websocket_get_prefs)
    hass.components.websocket_api.async_register_command(
        websocket_update_prefs)

    await component.async_setup(config)

    async def preload_stream(_event: Event) -> None:
        for camera in component.entities:
            camera = cast(Camera, camera)
            camera_prefs = prefs.get(camera.entity_id)
            if not camera_prefs.preload_stream:
                continue
            stream = await camera.create_stream()
            if not stream:
                continue
            stream.keepalive = True
            stream.add_provider("hls")
            stream.start()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, preload_stream)

    @callback
    def update_tokens(time: datetime) -> None:
        """Update tokens of the entities."""
        for entity in component.entities:
            entity = cast(Camera, entity)
            entity.async_update_token()
            entity.async_write_ha_state()

    hass.helpers.event.async_track_time_interval(update_tokens,
                                                 TOKEN_CHANGE_INTERVAL)

    component.async_register_entity_service(SERVICE_ENABLE_MOTION, {},
                                            "async_enable_motion_detection")
    component.async_register_entity_service(SERVICE_DISABLE_MOTION, {},
                                            "async_disable_motion_detection")
    component.async_register_entity_service(SERVICE_TURN_OFF, {},
                                            "async_turn_off")
    component.async_register_entity_service(SERVICE_TURN_ON, {},
                                            "async_turn_on")
    component.async_register_entity_service(SERVICE_SNAPSHOT,
                                            CAMERA_SERVICE_SNAPSHOT,
                                            async_handle_snapshot_service)
    component.async_register_entity_service(
        SERVICE_PLAY_STREAM,
        CAMERA_SERVICE_PLAY_STREAM,
        async_handle_play_stream_service,
    )
    component.async_register_entity_service(SERVICE_RECORD,
                                            CAMERA_SERVICE_RECORD,
                                            async_handle_record_service)

    return True
Beispiel #57
0
async def async_setup(hass: HomeAssistant, config: dict):
    """Set up the Garmin Connect component."""
    hass.data[DOMAIN] = {}
    return True
Beispiel #58
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Homewizard from a config entry."""

    _LOGGER.debug("__init__ async_setup_entry")

    # Migrate `homewizard_energy` (custom_component) to `homewizard`
    if entry.source == SOURCE_IMPORT and "old_config_entry_id" in entry.data:
        # Remove the old config entry ID from the entry data so we don't try this again
        # on the next setup
        data = entry.data.copy()
        old_config_entry_id = data.pop("old_config_entry_id")

        hass.config_entries.async_update_entry(entry, data=data)
        _LOGGER.debug(
            ("Setting up imported homewizard_energy entry %s for the first time as "
             "homewizard entry %s"),
            old_config_entry_id,
            entry.entry_id,
        )

        ent_reg = er.async_get(hass)
        for entity in er.async_entries_for_config_entry(
                ent_reg, old_config_entry_id):
            _LOGGER.debug("Removing %s", entity.entity_id)
            ent_reg.async_remove(entity.entity_id)

            _LOGGER.debug("Re-creating %s for the new config entry",
                          entity.entity_id)
            # We will precreate the entity so that any customizations can be preserved
            new_entity = ent_reg.async_get_or_create(
                entity.domain,
                DOMAIN,
                entity.unique_id,
                suggested_object_id=entity.entity_id.split(".")[1],
                disabled_by=entity.disabled_by,
                config_entry=entry,
                original_name=entity.original_name,
                original_icon=entity.original_icon,
            )
            _LOGGER.debug("Re-created %s", new_entity.entity_id)

            # If there are customizations on the old entity, apply them to the new one
            if entity.name or entity.icon:
                ent_reg.async_update_entity(new_entity.entity_id,
                                            name=entity.name,
                                            icon=entity.icon)

        # Remove the old config entry and now the entry is fully migrated
        hass.async_create_task(
            hass.config_entries.async_remove(old_config_entry_id))

    # Create coordinator
    coordinator = Coordinator(hass, entry.data[CONF_IP_ADDRESS])
    try:
        await coordinator.async_config_entry_first_refresh()
    except ConfigEntryNotReady:
        await coordinator.api.close()
        raise

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

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Beispiel #59
0
async def async_setup(hass: HomeAssistant, config: dict) -> Awaitable[bool]:
    """Set up the persistent notification component."""
    persistent_notifications = OrderedDict()
    hass.data[DOMAIN] = {'notifications': persistent_notifications}

    @callback
    def create_service(call):
        """Handle a create notification service call."""
        title = call.data.get(ATTR_TITLE)
        message = call.data.get(ATTR_MESSAGE)
        notification_id = call.data.get(ATTR_NOTIFICATION_ID)

        if notification_id is not None:
            entity_id = ENTITY_ID_FORMAT.format(slugify(notification_id))
        else:
            entity_id = async_generate_entity_id(
                ENTITY_ID_FORMAT, DEFAULT_OBJECT_ID, hass=hass)
            notification_id = entity_id.split('.')[1]

        attr = {}
        if title is not None:
            try:
                title.hass = hass
                title = title.async_render()
            except TemplateError as ex:
                _LOGGER.error('Error rendering title %s: %s', title, ex)
                title = title.template

            attr[ATTR_TITLE] = title

        try:
            message.hass = hass
            message = message.async_render()
        except TemplateError as ex:
            _LOGGER.error('Error rendering message %s: %s', message, ex)
            message = message.template

        attr[ATTR_MESSAGE] = message

        hass.states.async_set(entity_id, STATE, attr)

        # Store notification and fire event
        # This will eventually replace state machine storage
        persistent_notifications[entity_id] = {
            ATTR_MESSAGE: message,
            ATTR_NOTIFICATION_ID: notification_id,
            ATTR_STATUS: STATUS_UNREAD,
            ATTR_TITLE: title,
            ATTR_CREATED_AT: dt_util.utcnow(),
        }

        hass.bus.async_fire(EVENT_PERSISTENT_NOTIFICATIONS_UPDATED)

    @callback
    def dismiss_service(call):
        """Handle the dismiss notification service call."""
        notification_id = call.data.get(ATTR_NOTIFICATION_ID)
        entity_id = ENTITY_ID_FORMAT.format(slugify(notification_id))

        if entity_id not in persistent_notifications:
            return

        hass.states.async_remove(entity_id)

        del persistent_notifications[entity_id]
        hass.bus.async_fire(EVENT_PERSISTENT_NOTIFICATIONS_UPDATED)

    @callback
    def mark_read_service(call):
        """Handle the mark_read notification service call."""
        notification_id = call.data.get(ATTR_NOTIFICATION_ID)
        entity_id = ENTITY_ID_FORMAT.format(slugify(notification_id))

        if entity_id not in persistent_notifications:
            _LOGGER.error('Marking persistent_notification read failed: '
                          'Notification ID %s not found.', notification_id)
            return

        persistent_notifications[entity_id][ATTR_STATUS] = STATUS_READ
        hass.bus.async_fire(EVENT_PERSISTENT_NOTIFICATIONS_UPDATED)

    hass.services.async_register(DOMAIN, SERVICE_CREATE, create_service,
                                 SCHEMA_SERVICE_CREATE)

    hass.services.async_register(DOMAIN, SERVICE_DISMISS, dismiss_service,
                                 SCHEMA_SERVICE_DISMISS)

    hass.services.async_register(DOMAIN, SERVICE_MARK_READ, mark_read_service,
                                 SCHEMA_SERVICE_MARK_READ)

    hass.components.websocket_api.async_register_command(
        WS_TYPE_GET_NOTIFICATIONS, websocket_get_notifications,
        SCHEMA_WS_GET
    )

    return True
Beispiel #60
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Nest from a config entry with dispatch between old/new flows."""

    if DATA_SDM not in entry.data:
        return await async_setup_legacy_entry(hass, entry)

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

    config = hass.data[DOMAIN][DATA_NEST_CONFIG]

    session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
    auth = api.AsyncConfigEntryAuth(
        aiohttp_client.async_get_clientsession(hass),
        session,
        API_URL,
    )
    subscriber = GoogleNestSubscriber(
        auth, config[CONF_PROJECT_ID], config[CONF_SUBSCRIBER_ID]
    )
    callback = SignalUpdateCallback(hass)
    subscriber.set_update_callback(callback.async_handle_event)

    try:
        await subscriber.start_async()
    except AuthException as err:
        _LOGGER.debug("Subscriber authentication error: %s", err)
        hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN,
                context={"source": SOURCE_REAUTH},
                data=entry.data,
            )
        )
        return False
    except ConfigurationException as err:
        _LOGGER.error("Configuration error: %s", err)
        subscriber.stop_async()
        return False
    except GoogleNestException as err:
        if DATA_NEST_UNAVAILABLE not in hass.data[DOMAIN]:
            _LOGGER.error("Subscriber error: %s", err)
            hass.data[DOMAIN][DATA_NEST_UNAVAILABLE] = True
        subscriber.stop_async()
        raise ConfigEntryNotReady from err

    try:
        await subscriber.async_get_device_manager()
    except GoogleNestException as err:
        if DATA_NEST_UNAVAILABLE not in hass.data[DOMAIN]:
            _LOGGER.error("Device manager error: %s", err)
            hass.data[DOMAIN][DATA_NEST_UNAVAILABLE] = True
        subscriber.stop_async()
        raise ConfigEntryNotReady from err

    hass.data[DOMAIN].pop(DATA_NEST_UNAVAILABLE, None)
    hass.data[DOMAIN][DATA_SUBSCRIBER] = subscriber

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

    return True