Beispiel #1
0
async def async_setup(opp: OpenPeerPower, config: dict) -> bool:
    """Set up configured zones as well as Open Peer Power zone if necessary."""
    component = entity_component.EntityComponent(_LOGGER, DOMAIN, opp)
    id_manager = collection.IDManager()

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

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

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

    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(
        opp,
        DOMAIN,
        SERVICE_RELOAD,
        reload_service_handler,
        schema=RELOAD_SERVICE_SCHEMA,
    )

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

    home_zone = Zone(_home_conf(opp))
    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(opp))

    opp.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, core_config_updated)

    opp.data[DOMAIN] = storage_collection

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

    collection.attach_entity_component_collection(
        entity_component, yaml_collection, lambda conf: Person(conf, False))
    collection.attach_entity_component_collection(
        entity_component, storage_collection, lambda conf: Person(conf, True))
    collection.attach_entity_registry_cleaner(opp, DOMAIN, DOMAIN,
                                              yaml_collection)
    collection.attach_entity_registry_cleaner(opp, DOMAIN, DOMAIN,
                                              storage_collection)

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

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

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

    websocket_api.async_register_command(opp, 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})

    opp.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(
            opp, conf.get(DOMAIN, [])))

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

    return True
Beispiel #3
0
async def async_setup(opp, config):
    """Set up all automations."""
    # Local import to avoid circular import
    opp.data[DOMAIN] = component = EntityComponent(LOGGER, DOMAIN, opp)

    # To register the automation blueprints
    async_get_blueprints(opp)

    if not await _async_process_config(opp, config, component):
        await async_get_blueprints(opp).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(opp).async_reset_cache()
        await _async_process_config(opp, conf, component)
        opp.bus.async_fire(EVENT_AUTOMATION_RELOADED, context=service_call.context)

    reload_helper = ReloadServiceHelper(reload_service_handler)

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

    return True
Beispiel #4
0
def _async_setup_themes(opp, themes):
    """Set up themes data and services."""
    opp.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
    opp.data[DATA_THEMES] = themes or {}

    @callback
    def update_theme_and_fire_event():
        """Update theme_color in manifest."""
        name = opp.data[DATA_DEFAULT_THEME]
        themes = opp.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),
            )
        opp.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 opp.data[DATA_THEMES]:
            _LOGGER.info("Theme %s set as default", name)
            opp.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_opp_config_yaml(opp)
        new_themes = config[DOMAIN].get(CONF_THEMES, {})
        opp.data[DATA_THEMES] = new_themes
        if opp.data[DATA_DEFAULT_THEME] not in new_themes:
            opp.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
        update_theme_and_fire_event()

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

    service.async_register_admin_service(
        opp, DOMAIN, SERVICE_RELOAD_THEMES, reload_themes
    )
Beispiel #5
0
async def async_setup(opp: OpenPeerPower, 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:
        """Start the debugger."""
        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()
                opp.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(opp,
                                 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
async def async_setup(opp, config):
    """Set up the automation."""
    opp.data[DOMAIN] = component = EntityComponent(_LOGGER, DOMAIN, opp)

    await _async_process_config(opp, 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(opp, conf, component)

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

    return True
Beispiel #7
0
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry):
    """Set up Profiler from a config entry."""
    lock = asyncio.Lock()
    domain_data = opp.data[DOMAIN] = {}

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

    async def _async_run_memory_profile(call: ServiceCall):
        async with lock:
            await _async_generate_memory_profile(opp, call)

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

        opp.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 opp.async_add_executor_job(_log_objects)
        domain_data[LOG_INTERVAL_SUB] = async_track_time_interval(
            opp, _log_objects, call.data[CONF_SCAN_INTERVAL]
        )

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

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

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

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

        opp.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 opp.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(
        opp,
        DOMAIN,
        SERVICE_START,
        _async_run_profile,
        schema=vol.Schema(
            {vol.Optional(CONF_SECONDS, default=60.0): vol.Coerce(float)}
        ),
    )

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

    async_register_admin_service(
        opp,
        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(
        opp,
        DOMAIN,
        SERVICE_STOP_LOG_OBJECTS,
        _async_stop_log_objects,
    )

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

    async_register_admin_service(
        opp,
        DOMAIN,
        SERVICE_LOG_THREAD_FRAMES,
        _async_dump_thread_frames,
    )

    async_register_admin_service(
        opp,
        DOMAIN,
        SERVICE_LOG_EVENT_LOOP_SCHEDULED,
        _async_dump_scheduled,
    )

    return True
Beispiel #8
0
async def _async_setup_themes(opp, themes):
    """Set up themes data and services."""
    opp.data[DATA_THEMES] = themes or {}

    store = opp.data[DATA_THEMES_STORE] = opp.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 opp.data[DATA_THEMES]:
        opp.data[DATA_DEFAULT_THEME] = theme_name
    else:
        opp.data[DATA_DEFAULT_THEME] = DEFAULT_THEME

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

    @callback
    def update_theme_and_fire_event():
        """Update theme_color in manifest."""
        name = opp.data[DATA_DEFAULT_THEME]
        themes = opp.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)
        opp.bus.async_fire(EVENT_THEMES_UPDATED)

    @callback
    def set_theme(call):
        """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 opp.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

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

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

    service.async_register_admin_service(
        opp,
        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(opp, DOMAIN, SERVICE_RELOAD_THEMES,
                                         reload_themes)
Beispiel #9
0
async def async_setup(opp: OpenPeerPower, config: ConfigType):
    """Set up the Lovelace commands."""
    mode = config[DOMAIN][CONF_MODE]
    yaml_resources = config[DOMAIN].get(CONF_RESOURCES)

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

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

        integration = await async_get_integration(opp, DOMAIN)

        config = await async_process_component_config(opp, conf, integration)

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

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

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

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

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

        resource_collection = resources.ResourceStorageCollection(opp, default_config)

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

    opp.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_config
    )
    opp.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_save_config
    )
    opp.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_delete_config
    )
    opp.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_resources
    )

    opp.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_dashboards
    )

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

    if opp.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(opp, url_path)
            await opp.data[DOMAIN]["dashboards"].pop(url_path).async_delete()
            return

        if change_type == collection.CHANGE_ADDED:

            existing = opp.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

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

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

        try:
            _register_panel(opp, 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 opp.data[DOMAIN]["yaml_dashboards"].items():
        # For now always mode=yaml
        config = dashboard.LovelaceYAML(opp, url_path, dashboard_conf)
        opp.data[DOMAIN]["dashboards"][url_path] = config

        try:
            _register_panel(opp, 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(opp)

    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(opp, create_list=False)

    return True
Beispiel #10
0
async def async_setup_services(opp: OpenPeerPower) -> None:
    """Set up the HomematicIP Cloud services."""

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

    @verify_domain_control(opp, HMIPC_DOMAIN)
    async def async_call_hmipc_service(service: ServiceCall):
        """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(opp, service)
        elif service_name == SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD:
            await _async_activate_eco_mode_with_period(opp, service)
        elif service_name == SERVICE_ACTIVATE_VACATION:
            await _async_activate_vacation(opp, service)
        elif service_name == SERVICE_DEACTIVATE_ECO_MODE:
            await _async_deactivate_eco_mode(opp, service)
        elif service_name == SERVICE_DEACTIVATE_VACATION:
            await _async_deactivate_vacation(opp, service)
        elif service_name == SERVICE_DUMP_HAP_CONFIG:
            await _async_dump_hap_config(opp, service)
        elif service_name == SERVICE_RESET_ENERGY_COUNTER:
            await _async_reset_energy_counter(opp, service)
        elif service_name == SERVICE_SET_ACTIVE_CLIMATE_PROFILE:
            await _set_active_climate_profile(opp, service)

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

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

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

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

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

    opp.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(
        opp=opp,
        domain=HMIPC_DOMAIN,
        service=SERVICE_DUMP_HAP_CONFIG,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_DUMP_HAP_CONFIG,
    )

    async_register_admin_service(
        opp=opp,
        domain=HMIPC_DOMAIN,
        service=SERVICE_RESET_ENERGY_COUNTER,
        service_func=async_call_hmipc_service,
        schema=SCHEMA_RESET_ENERGY_COUNTER,
    )
Beispiel #11
0
async def async_setup(opp: OpenPeerPower, config: Dict) -> bool:
    """Set up configured zones as well as Open Peer Power zone if necessary."""
    component = entity_component.EntityComponent(_LOGGER, DOMAIN, opp)
    id_manager = collection.IDManager()

    yaml_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(opp, 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 DOMAIN in config:
        await yaml_collection.async_load(config[DOMAIN])

    await storage_collection.async_load()

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

    async def _collection_changed(change_type: str, item_id: str,
                                  config: Optional[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(opp)
        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.get(DOMAIN, []))

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

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

    home_zone = Zone(
        _home_conf(opp),
        True,
    )
    home_zone.entity_id = ENTITY_ID_HOME
    await component.async_add_entities([home_zone])  # type: ignore

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

    opp.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, core_config_updated)

    opp.data[DOMAIN] = storage_collection

    return True
Beispiel #12
0
async def async_setup_entry(opp, config_entry):  # noqa: C901
    """Set up SimpliSafe as config entry."""
    opp.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:
        opp.config_entries.async_update_entry(config_entry, **entry_updates)

    _verify_domain_control = verify_domain_control(opp, DOMAIN)

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

    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(opp, config_entry, api.refresh_token)

    simplisafe = SimpliSafe(opp, api, config_entry)

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

    opp.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe
    opp.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(opp,
                                     DOMAIN,
                                     service,
                                     method,
                                     schema=schema)

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

    return True