Ejemplo n.º 1
0
def register_services(hass):
    """Register services used by alarmo component."""

    coordinator = hass.data[const.DOMAIN]["coordinator"]

    async def async_srv_toggle_user(call):
        """Enable a user by service call"""
        name = call.data.get(ATTR_NAME)
        enable = True if call.service == const.SERVICE_ENABLE_USER else False
        users = coordinator.store.async_get_users()
        user = next(
            (item for item in list(users.values()) if item[ATTR_NAME] == name),
            None)
        if user is None:
            _LOGGER.warning("Failed to {} user, no match for name '{}'".format(
                "enable" if enable else "disable", name))
            return

        coordinator.store.async_update_user(user[const.ATTR_USER_ID],
                                            {const.ATTR_ENABLED: enable})
        _LOGGER.debug("User user '{}' was {}".format(
            name, "enabled" if enable else "disabled"))

    async_register_admin_service(hass,
                                 const.DOMAIN,
                                 const.SERVICE_ENABLE_USER,
                                 async_srv_toggle_user,
                                 schema=const.SERVICE_TOGGLE_USER_SCHEMA)
    async_register_admin_service(hass,
                                 const.DOMAIN,
                                 const.SERVICE_DISABLE_USER,
                                 async_srv_toggle_user,
                                 schema=const.SERVICE_TOGGLE_USER_SCHEMA)
Ejemplo n.º 2
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Scheduler integration from a config entry."""
    session = async_get_clientsession(hass)

    coordinator = SchedulerCoordinator(hass, session, entry)

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

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

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

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

    async def async_service_add(data):
        # TODO: add validation

        await coordinator.add_entity(data.data)

    service.async_register_admin_service(hass, DOMAIN, SERVICE_ADD,
                                         async_service_add, SCHEMA_ADD)

    return True
Ejemplo n.º 3
0
def register_component_services(
    component: EntityComponent, coordinator: IUCoordinator
) -> None:
    """Register the component"""

    @callback
    async def reload_service_handler(call: ServiceCall) -> None:
        """Reload yaml entities."""
        # pylint: disable=unused-argument
        # pylint: disable=import-outside-toplevel
        from .binary_sensor import async_reload_platform

        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None or conf == {}:
            conf = {DOMAIN: {}}
        coordinator.load(conf[DOMAIN])
        await async_reload_platform(component, coordinator)
        coordinator.start()

    async_register_admin_service(
        component.hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
        schema=RELOAD_SERVICE_SCHEMA,
    )
Ejemplo n.º 4
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up configured zones as well as Home Assistant zone if necessary."""
    component = entity_component.EntityComponent(_LOGGER, DOMAIN, hass)
    id_manager = collection.IDManager()

    yaml_collection = collection.IDLessCollection(
        logging.getLogger(f"{__name__}.yaml_collection"), id_manager)
    collection.sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component,
                                     yaml_collection, Zone.from_yaml)

    storage_collection = ZoneStorageCollection(
        storage.Store(hass, STORAGE_VERSION, STORAGE_KEY),
        logging.getLogger(f"{__name__}.storage_collection"),
        id_manager,
    )
    collection.sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component,
                                     storage_collection, Zone)

    if config[DOMAIN]:
        # AIS dom config can be empty
        if config[DOMAIN] != [{}]:
            await yaml_collection.async_load(config[DOMAIN])

    await storage_collection.async_load()

    collection.StorageCollectionWebsocket(storage_collection, DOMAIN, DOMAIN,
                                          CREATE_FIELDS,
                                          UPDATE_FIELDS).async_setup(hass)

    async def reload_service_handler(service_call: ServiceCall) -> None:
        """Remove all zones and load new ones from config."""
        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None:
            return
        await yaml_collection.async_load(conf[DOMAIN])

    service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
        schema=RELOAD_SERVICE_SCHEMA,
    )

    if component.get_entity("zone.home"):
        return True

    home_zone = Zone(_home_conf(hass))
    home_zone.entity_id = ENTITY_ID_HOME
    await component.async_add_entities([home_zone])

    async def core_config_updated(_: Event) -> None:
        """Handle core config updated."""
        await home_zone.async_update_config(_home_conf(hass))

    hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, core_config_updated)

    hass.data[DOMAIN] = storage_collection

    return True
Ejemplo n.º 5
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the template integration."""
    if DOMAIN in config:
        await _process_config(hass, config)

    async def _reload_config(call: Event | ServiceCall) -> None:
        """Reload top-level + platforms."""
        try:
            unprocessed_conf = await conf_util.async_hass_config_yaml(hass)
        except HomeAssistantError as err:
            _LOGGER.error(err)
            return

        conf = await conf_util.async_process_component_config(
            hass, unprocessed_conf, await async_get_integration(hass, DOMAIN))

        if conf is None:
            return

        await async_reload_integration_platforms(hass, DOMAIN, PLATFORMS)

        if DOMAIN in conf:
            await _process_config(hass, conf)

        hass.bus.async_fire(f"event_{DOMAIN}_reloaded", context=call.context)

    async_register_admin_service(hass, DOMAIN, SERVICE_RELOAD, _reload_config)

    return True
Ejemplo n.º 6
0
async def async_setup(hass, config):
    """Set up all automations."""
    # Local import to avoid circular import
    hass.data[DOMAIN] = component = EntityComponent(LOGGER, DOMAIN, hass)

    # To register the automation blueprints
    async_get_blueprints(hass)

    if not await _async_process_config(hass, config, component):
        await async_get_blueprints(hass).async_populate()

    async def trigger_service_handler(entity, service_call):
        """Handle forced automation trigger, e.g. from frontend."""
        await entity.async_trigger(
            {
                **service_call.data[ATTR_VARIABLES], "trigger": {
                    "platform": None
                }
            },
            skip_condition=service_call.data[CONF_SKIP_CONDITION],
            context=service_call.context,
        )

    component.async_register_entity_service(
        SERVICE_TRIGGER,
        {
            vol.Optional(ATTR_VARIABLES, default={}): dict,
            vol.Optional(CONF_SKIP_CONDITION, default=True): bool,
        },
        trigger_service_handler,
    )
    component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
    component.async_register_entity_service(SERVICE_TURN_ON, {},
                                            "async_turn_on")
    component.async_register_entity_service(
        SERVICE_TURN_OFF,
        {
            vol.Optional(CONF_STOP_ACTIONS, default=DEFAULT_STOP_ACTIONS):
            cv.boolean
        },
        "async_turn_off",
    )

    async def reload_service_handler(service_call):
        """Remove all automations and load new ones from config."""
        conf = await component.async_prepare_reload()
        if conf is None:
            return
        async_get_blueprints(hass).async_reset_cache()
        await _async_process_config(hass, conf, component)
        hass.bus.async_fire(EVENT_AUTOMATION_RELOADED,
                            context=service_call.context)

    async_register_admin_service(hass,
                                 DOMAIN,
                                 SERVICE_RELOAD,
                                 reload_service_handler,
                                 schema=vol.Schema({}))

    return True
Ejemplo n.º 7
0
async def async_setup_ha_cast(hass: core.HomeAssistant,
                              entry: config_entries.ConfigEntry):
    """Set up Home Assistant Cast."""
    user_id: str | None = entry.data.get("user_id")
    user: auth.models.User | None = None

    if user_id is not None:
        user = await hass.auth.async_get_user(user_id)

    if user is None:
        user = await hass.auth.async_create_system_user(
            "Home Assistant Cast", group_ids=[auth.const.GROUP_ID_ADMIN])
        hass.config_entries.async_update_entry(entry,
                                               data={
                                                   **entry.data, "user_id":
                                                   user.id
                                               })

    if user.refresh_tokens:
        refresh_token: auth.models.RefreshToken = list(
            user.refresh_tokens.values())[0]
    else:
        refresh_token = await hass.auth.async_create_refresh_token(user)

    async def handle_show_view(call: core.ServiceCall) -> None:
        """Handle a Show View service call."""
        try:
            hass_url = get_url(hass, require_ssl=True, prefer_external=True)
        except NoURLAvailableError as err:
            raise HomeAssistantError(NO_URL_AVAILABLE_ERROR) from err

        controller = HomeAssistantController(
            # If you are developing Home Assistant Cast, uncomment and set to your dev app id.
            # app_id="5FE44367",
            hass_url=hass_url,
            client_id=None,
            refresh_token=refresh_token.token,
        )

        dispatcher.async_dispatcher_send(
            hass,
            SIGNAL_HASS_CAST_SHOW_VIEW,
            controller,
            call.data[ATTR_ENTITY_ID],
            call.data[ATTR_VIEW_PATH],
            call.data.get(ATTR_URL_PATH),
        )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_SHOW_VIEW,
        handle_show_view,
        vol.Schema({
            ATTR_ENTITY_ID: cv.entity_id,
            ATTR_VIEW_PATH: str,
            vol.Optional(ATTR_URL_PATH): str,
        }),
    )
Ejemplo n.º 8
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up an input select."""
    component = EntityComponent(LOGGER, DOMAIN, hass)

    # Process integration platforms right away since
    # we will create entities before firing EVENT_COMPONENT_LOADED
    await async_process_integration_platform_for_component(hass, DOMAIN)

    id_manager = IDManager()

    yaml_collection = YamlCollection(LOGGER, id_manager)
    sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component, yaml_collection,
                          Schedule.from_yaml)

    storage_collection = ScheduleStorageCollection(
        Store(
            hass,
            key=DOMAIN,
            version=STORAGE_VERSION,
            minor_version=STORAGE_VERSION_MINOR,
        ),
        logging.getLogger(f"{__name__}.storage_collection"),
        id_manager,
    )
    sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component, storage_collection,
                          Schedule)

    await yaml_collection.async_load([{
        CONF_ID: id_,
        **cfg
    } for id_, cfg in config.get(DOMAIN, {}).items()])
    await storage_collection.async_load()

    StorageCollectionWebsocket(
        storage_collection,
        DOMAIN,
        DOMAIN,
        BASE_SCHEMA | STORAGE_SCHEDULE_SCHEMA,
        BASE_SCHEMA | STORAGE_SCHEDULE_SCHEMA,
    ).async_setup(hass)

    async def reload_service_handler(service_call: ServiceCall) -> None:
        """Reload yaml entities."""
        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None:
            conf = {DOMAIN: {}}
        await yaml_collection.async_load([{
            CONF_ID: id_,
            **cfg
        } for id_, cfg in conf.get(DOMAIN, {}).items()])

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
    )

    return True
Ejemplo n.º 9
0
async def async_setup(hass, config):
    """Set up the KNX component."""
    try:
        hass.data[DOMAIN] = KNXModule(hass, config)
        hass.data[DOMAIN].async_create_exposures()
        await hass.data[DOMAIN].start()
    except XKNXException as ex:
        _LOGGER.warning("Could not connect to KNX interface: %s", ex)
        hass.components.persistent_notification.async_create(
            f"Could not connect to KNX interface: <br><b>{ex}</b>", title="KNX"
        )

    for platform in SupportedPlatforms:
        if platform.value in config[DOMAIN]:
            for device_config in config[DOMAIN][platform.value]:
                create_knx_device(platform, hass.data[DOMAIN].xknx, device_config)

    # We need to wait until all entities are loaded into the device list since they could also be created from other platforms
    for platform in SupportedPlatforms:
        hass.async_create_task(
            discovery.async_load_platform(hass, platform.value, DOMAIN, {}, config)
        )

    if not hass.data[DOMAIN].xknx.devices:
        _LOGGER.warning(
            "No KNX devices are configured. Please read "
            "https://www.home-assistant.io/blog/2020/09/17/release-115/#breaking-changes"
        )

    hass.services.async_register(
        DOMAIN,
        SERVICE_KNX_SEND,
        hass.data[DOMAIN].service_send_to_knx_bus,
        schema=SERVICE_KNX_SEND_SCHEMA,
    )

    async def reload_service_handler(service_call: ServiceCallType) -> None:
        """Remove all KNX components and load new ones from config."""

        # First check for config file. If for some reason it is no longer there
        # or knx is no longer mentioned, stop the reload.
        config = await async_integration_yaml_config(hass, DOMAIN)

        if not config or DOMAIN not in config:
            return

        await hass.data[DOMAIN].xknx.stop()

        await asyncio.gather(
            *[platform.async_reset() for platform in async_get_platforms(hass, DOMAIN)]
        )

        await async_setup(hass, config)

    async_register_admin_service(
        hass, DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=vol.Schema({})
    )

    return True
Ejemplo n.º 10
0
async def async_setup(hass: HomeAssistant, config: ConfigType):
    """Set up the person component."""
    entity_component = EntityComponent(_LOGGER, DOMAIN, hass)
    id_manager = collection.IDManager()
    yaml_collection = collection.YamlCollection(
        logging.getLogger(f"{__name__}.yaml_collection"), id_manager
    )
    storage_collection = PersonStorageCollection(
        PersonStore(hass, STORAGE_VERSION, STORAGE_KEY),
        logging.getLogger(f"{__name__}.storage_collection"),
        id_manager,
        yaml_collection,
    )

    collection.sync_entity_lifecycle(
        hass, DOMAIN, DOMAIN, entity_component, yaml_collection, Person
    )
    collection.sync_entity_lifecycle(
        hass, DOMAIN, DOMAIN, entity_component, storage_collection, Person.from_yaml
    )

    await yaml_collection.async_load(
        await filter_yaml_data(hass, config.get(DOMAIN, []))
    )
    await storage_collection.async_load()

    hass.data[DOMAIN] = (yaml_collection, storage_collection)

    collection.StorageCollectionWebsocket(
        storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS
    ).async_setup(hass, create_list=False)

    websocket_api.async_register_command(hass, ws_list_person)

    async def _handle_user_removed(event: Event) -> None:
        """Handle a user being removed."""
        user_id = event.data[ATTR_USER_ID]
        for person in storage_collection.async_items():
            if person[CONF_USER_ID] == user_id:
                await storage_collection.async_update_item(
                    person[CONF_ID], {CONF_USER_ID: None}
                )

    hass.bus.async_listen(EVENT_USER_REMOVED, _handle_user_removed)

    async def async_reload_yaml(call: ServiceCall):
        """Reload YAML."""
        conf = await entity_component.async_prepare_reload(skip_reset=True)
        if conf is None:
            return
        await yaml_collection.async_load(
            await filter_yaml_data(hass, conf.get(DOMAIN, []))
        )

    service.async_register_admin_service(
        hass, DOMAIN, SERVICE_RELOAD, async_reload_yaml
    )

    return True
Ejemplo n.º 11
0
async def async_setup(hass, config):
    """Set up the automation."""
    hass.data[DOMAIN] = component = EntityComponent(_LOGGER, DOMAIN, hass)

    await _async_process_config(hass, config, component)

    async def trigger_service_handler(entity, service_call):
        """Handle automation triggers."""
        await entity.async_trigger(
            service_call.data[ATTR_VARIABLES],
            skip_condition=service_call.data[CONF_SKIP_CONDITION],
            context=service_call.context,
        )

    component.async_register_entity_service(
        SERVICE_TRIGGER,
        {
            vol.Optional(ATTR_VARIABLES, default={}): dict,
            vol.Optional(CONF_SKIP_CONDITION, default=True): bool,
        },
        trigger_service_handler,
    )
    component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
    component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
    component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")

    async def reload_service_handler(service_call):
        """Remove all automations and load new ones from config."""
        conf = await component.async_prepare_reload()
        if conf is None:
            return
        await _async_process_config(hass, conf, component)
        hass.bus.async_fire(EVENT_AUTOMATION_RELOADED, context=service_call.context)

    async_register_admin_service(
        hass, DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=vol.Schema({})
    )

    @callback
    def async_describe_logbook_event(event):
        """Describe a logbook event."""
        return {
            "name": event.data.get(ATTR_NAME),
            "message": "has been triggered",
            "entity_id": event.data.get(ATTR_ENTITY_ID),
        }

    hass.components.logbook.async_describe_event(
        DOMAIN, EVENT_AUTOMATION_TRIGGERED, async_describe_logbook_event
    )

    return True
Ejemplo n.º 12
0
def _async_setup_themes(hass, themes):
    """Set up themes data and services."""
    hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
    hass.data[DATA_THEMES] = themes or {}

    @callback
    def update_theme_and_fire_event():
        """Update theme_color in manifest."""
        name = hass.data[DATA_DEFAULT_THEME]
        themes = hass.data[DATA_THEMES]
        MANIFEST_JSON["theme_color"] = DEFAULT_THEME_COLOR
        if name != DEFAULT_THEME:
            MANIFEST_JSON["theme_color"] = themes[name].get(
                "app-header-background-color",
                themes[name].get(PRIMARY_COLOR, DEFAULT_THEME_COLOR),
            )
        hass.bus.async_fire(EVENT_THEMES_UPDATED)

    @callback
    def set_theme(call):
        """Set backend-preferred theme."""
        data = call.data
        name = data[CONF_NAME]
        if name == DEFAULT_THEME or name in hass.data[DATA_THEMES]:
            _LOGGER.info("Theme %s set as default", name)
            hass.data[DATA_DEFAULT_THEME] = name
            update_theme_and_fire_event()
        else:
            _LOGGER.warning("Theme %s is not defined.", name)

    async def reload_themes(_):
        """Reload themes."""
        config = await async_hass_config_yaml(hass)
        new_themes = config[DOMAIN].get(CONF_THEMES, {})
        hass.data[DATA_THEMES] = new_themes
        if hass.data[DATA_DEFAULT_THEME] not in new_themes:
            hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
        update_theme_and_fire_event()

    service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_SET_THEME,
        set_theme,
        vol.Schema({vol.Required(CONF_NAME): cv.string}),
    )

    service.async_register_admin_service(
        hass, DOMAIN, SERVICE_RELOAD_THEMES, reload_themes
    )
Ejemplo n.º 13
0
async def async_setup(hass, config):
    """Set up the automation."""
    hass.data[DOMAIN] = component = EntityComponent(_LOGGER, DOMAIN, hass)

    await _async_process_config(hass, config, component)

    async def trigger_service_handler(entity, service_call):
        """Handle automation triggers."""
        await entity.async_trigger(
            service_call.data[ATTR_VARIABLES],
            skip_condition=service_call.data[CONF_SKIP_CONDITION],
            context=service_call.context,
        )

    component.async_register_entity_service(
        SERVICE_TRIGGER,
        {
            vol.Optional(ATTR_VARIABLES, default={}): dict,
            vol.Optional(CONF_SKIP_CONDITION, default=True): bool,
        },
        trigger_service_handler,
    )
    component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
    component.async_register_entity_service(SERVICE_TURN_ON, {},
                                            "async_turn_on")
    component.async_register_entity_service(
        SERVICE_TURN_OFF,
        {
            vol.Optional(CONF_STOP_ACTIONS, default=DEFAULT_STOP_ACTIONS):
            cv.boolean
        },
        "async_turn_off",
    )

    async def reload_service_handler(service_call):
        """Remove all automations and load new ones from config."""
        conf = await component.async_prepare_reload()
        if conf is None:
            return
        await _async_process_config(hass, conf, component)
        hass.bus.async_fire(EVENT_AUTOMATION_RELOADED,
                            context=service_call.context)

    async_register_admin_service(hass,
                                 DOMAIN,
                                 SERVICE_RELOAD,
                                 reload_service_handler,
                                 schema=vol.Schema({}))

    return True
Ejemplo n.º 14
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Remote Python Debugger component."""
    conf = config[DOMAIN]

    async def debug_start(call: ServiceCall | None = None,
                          *,
                          wait: bool = True) -> None:
        """Enable asyncio debugging and start the debugger."""
        get_running_loop().set_debug(True)

        debugpy.listen((conf[CONF_HOST], conf[CONF_PORT]))

        wait = conf[CONF_WAIT]
        if wait:
            _LOGGER.warning(
                "Waiting for remote debug connection on %s:%s",
                conf[CONF_HOST],
                conf[CONF_PORT],
            )
            ready = Event()

            def waitfor():
                debugpy.wait_for_client()
                hass.loop.call_soon_threadsafe(ready.set)

            Thread(target=waitfor).start()

            await ready.wait()
        else:
            _LOGGER.warning(
                "Listening for remote debug connection on %s:%s",
                conf[CONF_HOST],
                conf[CONF_PORT],
            )

    async_register_admin_service(hass,
                                 DOMAIN,
                                 SERVICE_START,
                                 debug_start,
                                 schema=vol.Schema({}))

    # If set to start the debugger on startup, do so
    if conf[CONF_START]:
        await debug_start(wait=conf[CONF_WAIT])

    return True
Ejemplo n.º 15
0
def register_component_services(component: EntityComponent,
                                coordinator: IUCoordinator) -> None:
    async def reload_service_handler(call: ServiceCall) -> None:
        """Reload yaml entities."""
        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None:
            conf = {DOMAIN: {}}
        coordinator.load(conf[DOMAIN])
        return

    async_register_admin_service(
        component.hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
        schema=RELOAD_SERVICE_SCHEMA,
    )
    return
Ejemplo n.º 16
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Profiler from a config entry."""

    lock = asyncio.Lock()

    async def _async_run_profile(call: ServiceCall):
        async with lock:
            await _async_generate_profile(hass, call)

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_START,
        _async_run_profile,
        schema=vol.Schema(
            {vol.Optional(CONF_SECONDS, default=60.0): vol.Coerce(float)}),
    )

    return True
Ejemplo n.º 17
0
async def async_setup(hass: HomeAssistant, config: Dict) -> bool:
    """Set up configured zones as well as Home Assistant zone if necessary."""
    component = entity_component.EntityComponent(_LOGGER, DOMAIN, hass)
    id_manager = collection.IDManager()

    yaml_collection = collection.IDLessCollection(
        logging.getLogger(f"{__name__}.yaml_collection"), id_manager
    )
    collection.attach_entity_component_collection(
        component, yaml_collection, lambda conf: Zone(conf, False)
    )

    storage_collection = ZoneStorageCollection(
        storage.Store(hass, STORAGE_VERSION, STORAGE_KEY),
        logging.getLogger(f"{__name__}.storage_collection"),
        id_manager,
    )
    collection.attach_entity_component_collection(
        component, storage_collection, lambda conf: Zone(conf, True)
    )

    if config[DOMAIN]:
        # AIS dom config can be empty
        if config[DOMAIN] != [{}]:
            await yaml_collection.async_load(config[DOMAIN])

    await storage_collection.async_load()

    collection.StorageCollectionWebsocket(
        storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS
    ).async_setup(hass)

    async def _collection_changed(change_type: str, item_id: str, config: Dict) -> None:
        """Handle a collection change: clean up entity registry on removals."""
        if change_type != collection.CHANGE_REMOVED:
            return

        ent_reg = await entity_registry.async_get_registry(hass)
        ent_reg.async_remove(
            cast(str, ent_reg.async_get_entity_id(DOMAIN, DOMAIN, item_id))
        )

    storage_collection.async_add_listener(_collection_changed)

    async def reload_service_handler(service_call: ServiceCall) -> None:
        """Remove all zones and load new ones from config."""
        conf = await component.async_prepare_reload(skip_reset=True)
        if conf is None:
            return
        await yaml_collection.async_load(conf[DOMAIN])

    service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
        schema=RELOAD_SERVICE_SCHEMA,
    )

    if component.get_entity("zone.home"):
        return True

    home_zone = Zone(
        _home_conf(hass),
        True,
    )
    home_zone.entity_id = ENTITY_ID_HOME
    await component.async_add_entities([home_zone])

    async def core_config_updated(_: Event) -> None:
        """Handle core config updated."""
        await home_zone.async_update_config(_home_conf(hass))

    hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, core_config_updated)

    hass.data[DOMAIN] = storage_collection

    return True
Ejemplo n.º 18
0
async def async_setup(hass, config):
    """Set up the KNX component."""
    try:
        knx_module = KNXModule(hass, config)
        hass.data[DOMAIN] = knx_module
        await knx_module.start()
    except XKNXException as ex:
        _LOGGER.warning("Could not connect to KNX interface: %s", ex)
        hass.components.persistent_notification.async_create(
            f"Could not connect to KNX interface: <br><b>{ex}</b>",
            title="KNX")

    if CONF_KNX_EXPOSE in config[DOMAIN]:
        for expose_config in config[DOMAIN][CONF_KNX_EXPOSE]:
            knx_module.exposures.append(
                create_knx_exposure(hass, knx_module.xknx, expose_config))

    for platform in SupportedPlatforms:
        if platform.value in config[DOMAIN]:
            for device_config in config[DOMAIN][platform.value]:
                create_knx_device(platform, knx_module.xknx, device_config)

    # We need to wait until all entities are loaded into the device list since they could also be created from other platforms
    for platform in SupportedPlatforms:
        hass.async_create_task(
            discovery.async_load_platform(hass, platform.value, DOMAIN, {},
                                          config))

    hass.services.async_register(
        DOMAIN,
        SERVICE_KNX_SEND,
        knx_module.service_send_to_knx_bus,
        schema=SERVICE_KNX_SEND_SCHEMA,
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_KNX_READ,
        knx_module.service_read_to_knx_bus,
        schema=SERVICE_KNX_READ_SCHEMA,
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_KNX_EVENT_REGISTER,
        knx_module.service_event_register_modify,
        schema=SERVICE_KNX_EVENT_REGISTER_SCHEMA,
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_KNX_EXPOSURE_REGISTER,
        knx_module.service_exposure_register_modify,
        schema=SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA,
    )

    async def reload_service_handler(service_call: ServiceCallType) -> None:
        """Remove all KNX components and load new ones from config."""

        # First check for config file. If for some reason it is no longer there
        # or knx is no longer mentioned, stop the reload.
        config = await async_integration_yaml_config(hass, DOMAIN)

        if not config or DOMAIN not in config:
            return

        await knx_module.xknx.stop()

        await asyncio.gather(*[
            platform.async_reset()
            for platform in async_get_platforms(hass, DOMAIN)
        ])

        await async_setup(hass, config)

    async_register_admin_service(hass,
                                 DOMAIN,
                                 SERVICE_RELOAD,
                                 reload_service_handler,
                                 schema=vol.Schema({}))

    return True
Ejemplo n.º 19
0
    async def reload_service_handler(service_call):
        """Remove all automations and load new ones from config."""
        if (conf := await component.async_prepare_reload()) is None:
            return
        async_get_blueprints(hass).async_reset_cache()
        await _async_process_config(hass, conf, component)
        hass.bus.async_fire(EVENT_AUTOMATION_RELOADED,
                            context=service_call.context)

    reload_helper = ReloadServiceHelper(reload_service_handler)

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_RELOAD,
        reload_helper.execute_service,
        schema=vol.Schema({}),
    )

    return True


class AutomationEntity(ToggleEntity, RestoreEntity):
    """Entity to show status of entity."""

    _attr_should_poll = False

    def __init__(
        self,
        automation_id,
Ejemplo n.º 20
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Profiler from a config entry."""
    lock = asyncio.Lock()
    domain_data = hass.data[DOMAIN] = {}

    async def _async_run_profile(call: ServiceCall) -> None:
        async with lock:
            await _async_generate_profile(hass, call)

    async def _async_run_memory_profile(call: ServiceCall) -> None:
        async with lock:
            await _async_generate_memory_profile(hass, call)

    async def _async_start_log_objects(call: ServiceCall) -> None:
        if LOG_INTERVAL_SUB in domain_data:
            domain_data[LOG_INTERVAL_SUB]()

        hass.components.persistent_notification.async_create(
            "Object growth logging has started. See [the logs](/config/logs) to track the growth of new objects.",
            title="Object growth logging started",
            notification_id="profile_object_logging",
        )
        await hass.async_add_executor_job(_log_objects)
        domain_data[LOG_INTERVAL_SUB] = async_track_time_interval(
            hass, _log_objects, call.data[CONF_SCAN_INTERVAL])

    async def _async_stop_log_objects(call: ServiceCall) -> None:
        if LOG_INTERVAL_SUB not in domain_data:
            return

        hass.components.persistent_notification.async_dismiss(
            "profile_object_logging")
        domain_data.pop(LOG_INTERVAL_SUB)()

    def _dump_log_objects(call: ServiceCall) -> None:
        obj_type = call.data[CONF_TYPE]

        _LOGGER.critical(
            "%s objects in memory: %s",
            obj_type,
            objgraph.by_type(obj_type),
        )

        hass.components.persistent_notification.create(
            f"Objects with type {obj_type} have been dumped to the log. See [the logs](/config/logs) to review the repr of the objects.",
            title="Object dump completed",
            notification_id="profile_object_dump",
        )

    async def _async_dump_thread_frames(call: ServiceCall) -> None:
        """Log all thread frames."""
        frames = sys._current_frames()  # pylint: disable=protected-access
        main_thread = threading.main_thread()
        for thread in threading.enumerate():
            if thread == main_thread:
                continue
            _LOGGER.critical(
                "Thread [%s]: %s",
                thread.name,
                "".join(traceback.format_stack(frames.get(
                    thread.ident))).strip(),
            )

    async def _async_dump_scheduled(call: ServiceCall) -> None:
        """Log all scheduled in the event loop."""
        arepr = reprlib.aRepr
        original_maxstring = arepr.maxstring
        original_maxother = arepr.maxother
        arepr.maxstring = 300
        arepr.maxother = 300
        try:
            for handle in hass.loop._scheduled:  # pylint: disable=protected-access
                if not handle.cancelled():
                    _LOGGER.critical("Scheduled: %s", handle)
        finally:
            arepr.max_string = original_maxstring
            arepr.max_other = original_maxother

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_START,
        _async_run_profile,
        schema=vol.Schema(
            {vol.Optional(CONF_SECONDS, default=60.0): vol.Coerce(float)}),
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_MEMORY,
        _async_run_memory_profile,
        schema=vol.Schema(
            {vol.Optional(CONF_SECONDS, default=60.0): vol.Coerce(float)}),
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_START_LOG_OBJECTS,
        _async_start_log_objects,
        schema=vol.Schema({
            vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL):
            cv.time_period
        }),
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_STOP_LOG_OBJECTS,
        _async_stop_log_objects,
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_DUMP_LOG_OBJECTS,
        _dump_log_objects,
        schema=vol.Schema({vol.Required(CONF_TYPE): str}),
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_LOG_THREAD_FRAMES,
        _async_dump_thread_frames,
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_LOG_EVENT_LOOP_SCHEDULED,
        _async_dump_scheduled,
    )

    return True
Ejemplo n.º 21
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Load a config entry."""
    conf = hass.data.get(DATA_KNX_CONFIG)

    #  When reloading
    if conf is None:
        conf = await async_integration_yaml_config(hass, DOMAIN)
        if not conf or DOMAIN not in conf:
            return False

        conf = conf[DOMAIN]

    # If user didn't have configuration.yaml config, generate defaults
    if conf is None:
        conf = CONFIG_SCHEMA({DOMAIN: dict(entry.data)})[DOMAIN]

    config = {**conf, **entry.data}

    try:
        knx_module = KNXModule(hass, config, entry)
        await knx_module.start()
    except XKNXException as ex:
        raise ConfigEntryNotReady from ex

    hass.data[DATA_KNX_CONFIG] = conf
    hass.data[DOMAIN] = knx_module

    if CONF_KNX_EXPOSE in config:
        for expose_config in config[CONF_KNX_EXPOSE]:
            knx_module.exposures.append(
                create_knx_exposure(hass, knx_module.xknx, expose_config)
            )

    hass.config_entries.async_setup_platforms(
        entry, [platform for platform in SUPPORTED_PLATFORMS if platform in config]
    )

    # set up notify platform, no entry support for notify component yet,
    # have to use discovery to load platform.
    if NotifySchema.PLATFORM in conf:
        hass.async_create_task(
            discovery.async_load_platform(
                hass, "notify", DOMAIN, conf[NotifySchema.PLATFORM], config
            )
        )

    hass.services.async_register(
        DOMAIN,
        SERVICE_KNX_SEND,
        knx_module.service_send_to_knx_bus,
        schema=SERVICE_KNX_SEND_SCHEMA,
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_KNX_READ,
        knx_module.service_read_to_knx_bus,
        schema=SERVICE_KNX_READ_SCHEMA,
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_KNX_EVENT_REGISTER,
        knx_module.service_event_register_modify,
        schema=SERVICE_KNX_EVENT_REGISTER_SCHEMA,
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_KNX_EXPOSURE_REGISTER,
        knx_module.service_exposure_register_modify,
        schema=SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA,
    )

    return True
Ejemplo n.º 22
0
async def async_setup_services(hass: HomeAssistantType) -> None:
    """Set up the HomematicIP Cloud services."""

    if hass.services.async_services().get(HMIPC_DOMAIN):
        return

    @verify_domain_control(hass, HMIPC_DOMAIN)
    async def async_call_hmipc_service(service: ServiceCallType):
        """Call correct HomematicIP Cloud service."""
        service_name = service.service

        if service_name == SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION:
            await _async_activate_eco_mode_with_duration(hass, service)
        elif service_name == SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD:
            await _async_activate_eco_mode_with_period(hass, service)
        elif service_name == SERVICE_ACTIVATE_VACATION:
            await _async_activate_vacation(hass, service)
        elif service_name == SERVICE_DEACTIVATE_ECO_MODE:
            await _async_deactivate_eco_mode(hass, service)
        elif service_name == SERVICE_DEACTIVATE_VACATION:
            await _async_deactivate_vacation(hass, service)
        elif service_name == SERVICE_DUMP_HAP_CONFIG:
            await _async_dump_hap_config(hass, service)
        elif service_name == SERVICE_RESET_ENERGY_COUNTER:
            await _async_reset_energy_counter(hass, service)
        elif service_name == SERVICE_SET_ACTIVE_CLIMATE_PROFILE:
            await _set_active_climate_profile(hass, service)

    hass.services.async_register(
        domain=HMIPC_DOMAIN,
        service=SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_ACTIVATE_ECO_MODE_WITH_DURATION,
    )

    hass.services.async_register(
        domain=HMIPC_DOMAIN,
        service=SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_ACTIVATE_ECO_MODE_WITH_PERIOD,
    )

    hass.services.async_register(
        domain=HMIPC_DOMAIN,
        service=SERVICE_ACTIVATE_VACATION,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_ACTIVATE_VACATION,
    )

    hass.services.async_register(
        domain=HMIPC_DOMAIN,
        service=SERVICE_DEACTIVATE_ECO_MODE,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_DEACTIVATE_ECO_MODE,
    )

    hass.services.async_register(
        domain=HMIPC_DOMAIN,
        service=SERVICE_DEACTIVATE_VACATION,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_DEACTIVATE_VACATION,
    )

    hass.services.async_register(
        domain=HMIPC_DOMAIN,
        service=SERVICE_SET_ACTIVE_CLIMATE_PROFILE,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_SET_ACTIVE_CLIMATE_PROFILE,
    )

    async_register_admin_service(
        hass=hass,
        domain=HMIPC_DOMAIN,
        service=SERVICE_DUMP_HAP_CONFIG,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_DUMP_HAP_CONFIG,
    )

    async_register_admin_service(
        hass=hass,
        domain=HMIPC_DOMAIN,
        service=SERVICE_RESET_ENERGY_COUNTER,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_RESET_ENERGY_COUNTER,
    )
Ejemplo n.º 23
0
async def _async_setup_themes(hass: HomeAssistant,
                              themes: dict[str, Any] | None) -> None:
    """Set up themes data and services."""
    hass.data[DATA_THEMES] = themes or {}

    store = hass.data[DATA_THEMES_STORE] = hass.helpers.storage.Store(
        THEMES_STORAGE_VERSION, THEMES_STORAGE_KEY)

    theme_data = await store.async_load() or {}
    theme_name = theme_data.get(DATA_DEFAULT_THEME, DEFAULT_THEME)
    dark_theme_name = theme_data.get(DATA_DEFAULT_DARK_THEME)

    if theme_name == DEFAULT_THEME or theme_name in hass.data[DATA_THEMES]:
        hass.data[DATA_DEFAULT_THEME] = theme_name
    else:
        hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME

    if dark_theme_name == DEFAULT_THEME or dark_theme_name in hass.data[
            DATA_THEMES]:
        hass.data[DATA_DEFAULT_DARK_THEME] = dark_theme_name

    @callback
    def update_theme_and_fire_event() -> None:
        """Update theme_color in manifest."""
        name = hass.data[DATA_DEFAULT_THEME]
        themes = hass.data[DATA_THEMES]
        if name != DEFAULT_THEME:
            MANIFEST_JSON.update_key(
                "theme_color",
                themes[name].get(
                    "app-header-background-color",
                    themes[name].get(PRIMARY_COLOR, DEFAULT_THEME_COLOR),
                ),
            )
        else:
            MANIFEST_JSON.update_key("theme_color", DEFAULT_THEME_COLOR)
        hass.bus.async_fire(EVENT_THEMES_UPDATED)

    @callback
    def set_theme(call: ServiceCall) -> None:
        """Set backend-preferred theme."""
        name = call.data[CONF_NAME]
        mode = call.data.get("mode", "light")

        if (name not in (DEFAULT_THEME, VALUE_NO_THEME)
                and name not in hass.data[DATA_THEMES]):
            _LOGGER.warning("Theme %s not found", name)
            return

        light_mode = mode == "light"

        theme_key = DATA_DEFAULT_THEME if light_mode else DATA_DEFAULT_DARK_THEME

        if name == VALUE_NO_THEME:
            to_set = DEFAULT_THEME if light_mode else None
        else:
            _LOGGER.info("Theme %s set as default %s theme", name, mode)
            to_set = name

        hass.data[theme_key] = to_set
        store.async_delay_save(
            lambda: {
                DATA_DEFAULT_THEME: hass.data[DATA_DEFAULT_THEME],
                DATA_DEFAULT_DARK_THEME: hass.data.get(DATA_DEFAULT_DARK_THEME
                                                       ),
            },
            THEMES_SAVE_DELAY,
        )
        update_theme_and_fire_event()

    async def reload_themes(_: ServiceCall) -> None:
        """Reload themes."""
        config = await async_hass_config_yaml(hass)
        new_themes = config[DOMAIN].get(CONF_THEMES, {})
        hass.data[DATA_THEMES] = new_themes
        if hass.data[DATA_DEFAULT_THEME] not in new_themes:
            hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
        if (hass.data.get(DATA_DEFAULT_DARK_THEME)
                and hass.data.get(DATA_DEFAULT_DARK_THEME) not in new_themes):
            hass.data[DATA_DEFAULT_DARK_THEME] = None
        update_theme_and_fire_event()

    service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_SET_THEME,
        set_theme,
        vol.Schema({
            vol.Required(CONF_NAME): cv.string,
            vol.Optional(CONF_MODE): vol.Any("dark", "light"),
        }),
    )

    service.async_register_admin_service(hass, DOMAIN, SERVICE_RELOAD_THEMES,
                                         reload_themes)
Ejemplo n.º 24
0
        """Reload themes."""
        config = await async_hass_config_yaml(hass)
        new_themes = config.get(DOMAIN, {}).get(CONF_THEMES, {})
        hass.data[DATA_THEMES] = new_themes
        if hass.data[DATA_DEFAULT_THEME] not in new_themes:
            hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
        if (hass.data.get(DATA_DEFAULT_DARK_THEME)
                and hass.data.get(DATA_DEFAULT_DARK_THEME) not in new_themes):
            hass.data[DATA_DEFAULT_DARK_THEME] = None
        update_theme_and_fire_event()

    service.async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_SET_THEME,
        set_theme,
        vol.Schema({
            vol.Required(CONF_NAME): cv.string,
            vol.Optional(CONF_MODE): vol.Any("dark", "light"),
        }),
    )

    service.async_register_admin_service(hass, DOMAIN, SERVICE_RELOAD_THEMES,
                                         reload_themes)


@callback
@lru_cache(maxsize=1)
def _async_render_index_cached(template: jinja2.Template,
                               **kwargs: Any) -> str:
    return template.render(**kwargs)
Ejemplo n.º 25
0
async def test_register_admin_service(hass, hass_read_only_user,
                                      hass_admin_user):
    """Test the register admin service."""
    calls = []

    async def mock_service(call):
        calls.append(call)

    service.async_register_admin_service(hass, "test", "test", mock_service)
    service.async_register_admin_service(
        hass,
        "test",
        "test2",
        mock_service,
        vol.Schema({vol.Required("required"): cv.boolean}),
    )

    with pytest.raises(exceptions.UnknownUser):
        await hass.services.async_call(
            "test",
            "test",
            {},
            blocking=True,
            context=ha.Context(user_id="non-existing"),
        )
    assert len(calls) == 0

    with pytest.raises(exceptions.Unauthorized):
        await hass.services.async_call(
            "test",
            "test",
            {},
            blocking=True,
            context=ha.Context(user_id=hass_read_only_user.id),
        )
    assert len(calls) == 0

    with pytest.raises(vol.Invalid):
        await hass.services.async_call(
            "test",
            "test",
            {"invalid": True},
            blocking=True,
            context=ha.Context(user_id=hass_admin_user.id),
        )
    assert len(calls) == 0

    with pytest.raises(vol.Invalid):
        await hass.services.async_call(
            "test",
            "test2",
            {},
            blocking=True,
            context=ha.Context(user_id=hass_admin_user.id),
        )
    assert len(calls) == 0

    await hass.services.async_call(
        "test",
        "test2",
        {"required": True},
        blocking=True,
        context=ha.Context(user_id=hass_admin_user.id),
    )
    assert len(calls) == 1
    assert calls[0].context.user_id == hass_admin_user.id
Ejemplo n.º 26
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up SimpliSafe as config entry."""
    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.entry_id] = {}

    _async_standardize_config_entry(hass, entry)

    _verify_domain_control = verify_domain_control(hass, DOMAIN)
    websession = aiohttp_client.async_get_clientsession(hass)

    try:
        api = await API.async_from_refresh_token(entry.data[CONF_TOKEN],
                                                 session=websession)
    except InvalidCredentialsError as err:
        raise ConfigEntryAuthFailed from err
    except SimplipyError as err:
        LOGGER.error("Config entry failed: %s", err)
        raise ConfigEntryNotReady from err

    simplisafe = SimpliSafe(hass, entry, api)

    try:
        await simplisafe.async_init()
    except SimplipyError as err:
        raise ConfigEntryNotReady from err

    hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] = simplisafe
    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    @callback
    def verify_system_exists(
            coro: Callable[..., Awaitable]) -> Callable[..., Awaitable]:
        """Log an error if a service call uses an invalid system ID."""
        async def decorator(call: ServiceCall) -> None:
            """Decorate."""
            system_id = int(call.data[ATTR_SYSTEM_ID])
            if system_id not in simplisafe.systems:
                LOGGER.error("Unknown system ID in service call: %s",
                             system_id)
                return
            await coro(call)

        return decorator

    @callback
    def v3_only(coro: Callable[..., Awaitable]) -> Callable[..., Awaitable]:
        """Log an error if the decorated coroutine is called with a v2 system."""
        async def decorator(call: ServiceCall) -> None:
            """Decorate."""
            system = simplisafe.systems[int(call.data[ATTR_SYSTEM_ID])]
            if system.version != 3:
                LOGGER.error("Service only available on V3 systems")
                return
            await coro(call)

        return decorator

    @verify_system_exists
    @_verify_domain_control
    async def clear_notifications(call: ServiceCall) -> None:
        """Clear all active notifications."""
        system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.async_clear_notifications()
        except SimplipyError as err:
            LOGGER.error("Error during service call: %s", err)

    @verify_system_exists
    @_verify_domain_control
    async def remove_pin(call: ServiceCall) -> None:
        """Remove a PIN."""
        system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.async_remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE])
        except SimplipyError as err:
            LOGGER.error("Error during service call: %s", err)

    @verify_system_exists
    @_verify_domain_control
    async def set_pin(call: ServiceCall) -> None:
        """Set a PIN."""
        system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.async_set_pin(call.data[ATTR_PIN_LABEL],
                                       call.data[ATTR_PIN_VALUE])
        except SimplipyError as err:
            LOGGER.error("Error during service call: %s", err)

    @verify_system_exists
    @v3_only
    @_verify_domain_control
    async def set_system_properties(call: ServiceCall) -> None:
        """Set one or more system parameters."""
        system = cast(SystemV3, simplisafe.systems[call.data[ATTR_SYSTEM_ID]])
        try:
            await system.async_set_properties({
                prop: value
                for prop, value in call.data.items() if prop != ATTR_SYSTEM_ID
            })
        except SimplipyError as err:
            LOGGER.error("Error during service call: %s", err)

    for service, method, schema in (
        ("clear_notifications", clear_notifications, None),
        ("remove_pin", remove_pin, SERVICE_REMOVE_PIN_SCHEMA),
        ("set_pin", set_pin, SERVICE_SET_PIN_SCHEMA),
        (
            "set_system_properties",
            set_system_properties,
            SERVICE_SET_SYSTEM_PROPERTIES_SCHEMA,
        ),
    ):
        async_register_admin_service(hass,
                                     DOMAIN,
                                     service,
                                     method,
                                     schema=schema)

    entry.async_on_unload(entry.add_update_listener(async_reload_entry))

    return True
Ejemplo n.º 27
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Initialize the Home Assistant cloud."""
    # Process configs
    if DOMAIN in config:
        kwargs = dict(config[DOMAIN])
    else:
        kwargs = {CONF_MODE: DEFAULT_MODE}

    # Alexa/Google custom config
    alexa_conf = kwargs.pop(CONF_ALEXA, None) or ALEXA_SCHEMA({})
    google_conf = kwargs.pop(CONF_GOOGLE_ACTIONS, None) or GACTIONS_SCHEMA({})

    # Cloud settings
    prefs = CloudPreferences(hass)
    await prefs.async_initialize()

    # Initialize Cloud
    websession = async_get_clientsession(hass)
    client = CloudClient(hass, prefs, websession, alexa_conf, google_conf)
    cloud = hass.data[DOMAIN] = Cloud(client, **kwargs)

    async def _shutdown(event):
        """Shutdown event."""
        await cloud.stop()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown)

    _remote_handle_prefs_updated(cloud)

    async def _service_handler(service: ServiceCall) -> None:
        """Handle service for cloud."""
        if service.service == SERVICE_REMOTE_CONNECT:
            await prefs.async_update(remote_enabled=True)
        elif service.service == SERVICE_REMOTE_DISCONNECT:
            await prefs.async_update(remote_enabled=False)

    async_register_admin_service(hass, DOMAIN, SERVICE_REMOTE_CONNECT,
                                 _service_handler)
    async_register_admin_service(hass, DOMAIN, SERVICE_REMOTE_DISCONNECT,
                                 _service_handler)

    loaded = False

    async def _on_connect():
        """Discover RemoteUI binary sensor."""
        nonlocal loaded

        # Prevent multiple discovery
        if loaded:
            return
        loaded = True

        await async_load_platform(hass, Platform.BINARY_SENSOR, DOMAIN, {},
                                  config)
        await async_load_platform(hass, Platform.STT, DOMAIN, {}, config)
        await async_load_platform(hass, Platform.TTS, DOMAIN, {}, config)

        async_dispatcher_send(hass, SIGNAL_CLOUD_CONNECTION_STATE,
                              CloudConnectionState.CLOUD_CONNECTED)

    async def _on_disconnect():
        """Handle cloud disconnect."""
        async_dispatcher_send(hass, SIGNAL_CLOUD_CONNECTION_STATE,
                              CloudConnectionState.CLOUD_DISCONNECTED)

    async def _on_initialized():
        """Update preferences."""
        await prefs.async_update(remote_domain=cloud.remote.instance_domain)

    cloud.iot.register_on_connect(_on_connect)
    cloud.iot.register_on_disconnect(_on_disconnect)
    cloud.register_on_initialized(_on_initialized)

    await cloud.initialize()
    await http_api.async_setup(hass)

    account_link.async_setup(hass)

    return True
Ejemplo n.º 28
0
async def async_setup_entry(hass, config_entry):  # noqa: C901
    """Set up SimpliSafe as config entry."""
    hass.data[DOMAIN][DATA_LISTENER][config_entry.entry_id] = []

    entry_updates = {}
    if not config_entry.unique_id:
        # If the config entry doesn't already have a unique ID, set one:
        entry_updates["unique_id"] = config_entry.data[CONF_USERNAME]
    if CONF_CODE in config_entry.data:
        # If an alarm code was provided as part of configuration.yaml, pop it out of
        # the config entry's data and move it to options:
        data = {**config_entry.data}
        entry_updates["data"] = data
        entry_updates["options"] = {
            **config_entry.options,
            CONF_CODE: data.pop(CONF_CODE),
        }
    if entry_updates:
        hass.config_entries.async_update_entry(config_entry, **entry_updates)

    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    client_id = await async_get_client_id(hass)
    websession = aiohttp_client.async_get_clientsession(hass)

    try:
        api = await API.login_via_token(config_entry.data[CONF_TOKEN],
                                        client_id=client_id,
                                        session=websession)
    except InvalidCredentialsError:
        LOGGER.error("Invalid credentials provided")
        return False
    except SimplipyError as err:
        LOGGER.error("Config entry failed: %s", err)
        raise ConfigEntryNotReady from err

    _async_save_refresh_token(hass, config_entry, api.refresh_token)

    simplisafe = SimpliSafe(hass, api, config_entry)

    try:
        await simplisafe.async_init()
    except SimplipyError as err:
        raise ConfigEntryNotReady from err

    hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe
    hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)

    @callback
    def verify_system_exists(coro):
        """Log an error if a service call uses an invalid system ID."""
        async def decorator(call):
            """Decorate."""
            system_id = int(call.data[ATTR_SYSTEM_ID])
            if system_id not in simplisafe.systems:
                LOGGER.error("Unknown system ID in service call: %s",
                             system_id)
                return
            await coro(call)

        return decorator

    @callback
    def v3_only(coro):
        """Log an error if the decorated coroutine is called with a v2 system."""
        async def decorator(call):
            """Decorate."""
            system = simplisafe.systems[int(call.data[ATTR_SYSTEM_ID])]
            if system.version != 3:
                LOGGER.error("Service only available on V3 systems")
                return
            await coro(call)

        return decorator

    @verify_system_exists
    @_verify_domain_control
    async def clear_notifications(call):
        """Clear all active notifications."""
        system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.clear_notifications()
        except SimplipyError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    @verify_system_exists
    @_verify_domain_control
    async def remove_pin(call):
        """Remove a PIN."""
        system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE])
        except SimplipyError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    @verify_system_exists
    @_verify_domain_control
    async def set_pin(call):
        """Set a PIN."""
        system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.set_pin(call.data[ATTR_PIN_LABEL],
                                 call.data[ATTR_PIN_VALUE])
        except SimplipyError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    @verify_system_exists
    @v3_only
    @_verify_domain_control
    async def set_system_properties(call):
        """Set one or more system parameters."""
        system = simplisafe.systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.set_properties({
                prop: value
                for prop, value in call.data.items() if prop != ATTR_SYSTEM_ID
            })
        except SimplipyError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    for service, method, schema in [
        ("clear_notifications", clear_notifications, None),
        ("remove_pin", remove_pin, SERVICE_REMOVE_PIN_SCHEMA),
        ("set_pin", set_pin, SERVICE_SET_PIN_SCHEMA),
        (
            "set_system_properties",
            set_system_properties,
            SERVICE_SET_SYSTEM_PROPERTIES_SCHEMA,
        ),
    ]:
        async_register_admin_service(hass,
                                     DOMAIN,
                                     service,
                                     method,
                                     schema=schema)

    hass.data[DOMAIN][DATA_LISTENER][config_entry.entry_id].append(
        config_entry.add_update_listener(async_reload_entry))

    return True
Ejemplo n.º 29
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Lovelace commands."""
    mode = config[DOMAIN][CONF_MODE]
    yaml_resources = config[DOMAIN].get(CONF_RESOURCES)

    frontend.async_register_built_in_panel(hass, DOMAIN, config={"mode": mode})

    async def reload_resources_service_handler(service_call: ServiceCall) -> None:
        """Reload yaml resources."""
        try:
            conf = await async_hass_config_yaml(hass)
        except HomeAssistantError as err:
            _LOGGER.error(err)
            return

        integration = await async_get_integration(hass, DOMAIN)

        config = await async_process_component_config(hass, conf, integration)

        resource_collection = await create_yaml_resource_col(
            hass, config[DOMAIN].get(CONF_RESOURCES)
        )
        hass.data[DOMAIN]["resources"] = resource_collection

    if mode == MODE_YAML:
        default_config = dashboard.LovelaceYAML(hass, None, None)
        resource_collection = await create_yaml_resource_col(hass, yaml_resources)

        async_register_admin_service(
            hass,
            DOMAIN,
            SERVICE_RELOAD_RESOURCES,
            reload_resources_service_handler,
            schema=RESOURCE_RELOAD_SERVICE_SCHEMA,
        )

    else:
        default_config = dashboard.LovelaceStorage(hass, None)

        if yaml_resources is not None:
            _LOGGER.warning(
                "Lovelace is running in storage mode. Define resources via user interface"
            )

        resource_collection = resources.ResourceStorageCollection(hass, default_config)

        collection.StorageCollectionWebsocket(
            resource_collection,
            "lovelace/resources",
            "resource",
            RESOURCE_CREATE_FIELDS,
            RESOURCE_UPDATE_FIELDS,
        ).async_setup(hass, create_list=False)

    websocket_api.async_register_command(hass, websocket.websocket_lovelace_config)
    websocket_api.async_register_command(hass, websocket.websocket_lovelace_save_config)
    websocket_api.async_register_command(
        hass, websocket.websocket_lovelace_delete_config
    )
    websocket_api.async_register_command(hass, websocket.websocket_lovelace_resources)

    websocket_api.async_register_command(hass, websocket.websocket_lovelace_dashboards)

    hass.data[DOMAIN] = {
        # We store a dictionary mapping url_path: config. None is the default.
        "mode": mode,
        "dashboards": {None: default_config},
        "resources": resource_collection,
        "yaml_dashboards": config[DOMAIN].get(CONF_DASHBOARDS, {}),
    }

    if hass.config.safe_mode:
        return True

    async def storage_dashboard_changed(change_type, item_id, item):
        """Handle a storage dashboard change."""
        url_path = item[CONF_URL_PATH]

        if change_type == collection.CHANGE_REMOVED:
            frontend.async_remove_panel(hass, url_path)
            await hass.data[DOMAIN]["dashboards"].pop(url_path).async_delete()
            return

        if change_type == collection.CHANGE_ADDED:

            existing = hass.data[DOMAIN]["dashboards"].get(url_path)

            if existing:
                _LOGGER.warning(
                    "Cannot register panel at %s, it is already defined in %s",
                    url_path,
                    existing,
                )
                return

            hass.data[DOMAIN]["dashboards"][url_path] = dashboard.LovelaceStorage(
                hass, item
            )

            update = False
        else:
            hass.data[DOMAIN]["dashboards"][url_path].config = item
            update = True

        try:
            _register_panel(hass, url_path, MODE_STORAGE, item, update)
        except ValueError:
            _LOGGER.warning("Failed to %s panel %s from storage", change_type, url_path)

    # Process YAML dashboards
    for url_path, dashboard_conf in hass.data[DOMAIN]["yaml_dashboards"].items():
        # For now always mode=yaml
        config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf)
        hass.data[DOMAIN]["dashboards"][url_path] = config

        try:
            _register_panel(hass, url_path, MODE_YAML, dashboard_conf, False)
        except ValueError:
            _LOGGER.warning("Panel url path %s is not unique", url_path)

    # Process storage dashboards
    dashboards_collection = dashboard.DashboardsCollection(hass)

    dashboards_collection.async_add_listener(storage_dashboard_changed)
    await dashboards_collection.async_load()

    collection.StorageCollectionWebsocket(
        dashboards_collection,
        "lovelace/dashboards",
        "dashboard",
        STORAGE_DASHBOARD_CREATE_FIELDS,
        STORAGE_DASHBOARD_UPDATE_FIELDS,
    ).async_setup(hass, create_list=False)

    return True
Ejemplo n.º 30
0
        SERVICE_KNX_SEND,
        knx_module.service_send_to_knx_bus,
        schema=SERVICE_KNX_SEND_SCHEMA,
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_KNX_READ,
        knx_module.service_read_to_knx_bus,
        schema=SERVICE_KNX_READ_SCHEMA,
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_KNX_EVENT_REGISTER,
        knx_module.service_event_register_modify,
        schema=SERVICE_KNX_EVENT_REGISTER_SCHEMA,
    )

    async_register_admin_service(
        hass,
        DOMAIN,
        SERVICE_KNX_EXPOSURE_REGISTER,
        knx_module.service_exposure_register_modify,
        schema=SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA,
    )

    return True