Beispiel #1
0
async def test_domain_control_not_async(hass, mock_entities):
    """Test domain verification in a service call with an unknown user."""
    calls = []

    def mock_service_log(call):
        """Define a protected service."""
        calls.append(call)

    with pytest.raises(exceptions.HomeAssistantError):
        service.verify_domain_control(hass, "test_domain")(mock_service_log)
Beispiel #2
0
async def test_domain_control_unknown(hass, mock_entities):
    """Test domain verification in a service call with an unknown user."""
    calls = []

    async def mock_service_log(call):
        """Define a protected service."""
        calls.append(call)

    with patch(
            "homeassistant.helpers.entity_registry.async_get",
            return_value=Mock(entities=mock_entities),
    ):
        protected_mock_service = service.verify_domain_control(
            hass, "test_domain")(mock_service_log)

        hass.services.async_register("test_domain",
                                     "test_service",
                                     protected_mock_service,
                                     schema=None)

        with pytest.raises(exceptions.UnknownUser):
            await hass.services.async_call(
                "test_domain",
                "test_service",
                {},
                blocking=True,
                context=ha.Context(user_id="fake_user_id"),
            )
        assert len(calls) == 0
Beispiel #3
0
async def async_setup_entry(hass, config_entry):
    """Set up Alexa Media Player as config entry."""
    async def close_alexa_media(event=None) -> None:
        """Clean up Alexa connections."""
        _LOGGER.debug("Received shutdown request: %s", event)
        for email, _ in (hass.data[DATA_ALEXAMEDIA]['accounts'].items()):
            await close_connections(hass, email)

    _verify_domain_control = verify_domain_control(hass, DOMAIN)
    if DATA_ALEXAMEDIA not in hass.data:
        hass.data[DATA_ALEXAMEDIA] = {}
        hass.data[DATA_ALEXAMEDIA]['accounts'] = {}
    from alexapy import AlexaLogin, __version__ as alexapy_version
    _LOGGER.info(STARTUP)
    _LOGGER.info("Loaded alexapy==%s", alexapy_version)
    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, close_alexa_media)
    account = config_entry.data
    email = account.get(CONF_EMAIL)
    password = account.get(CONF_PASSWORD)
    url = account.get(CONF_URL)
    login = AlexaLogin(url, email, password, hass.config.path,
                       account.get(CONF_DEBUG))
    if email not in hass.data[DATA_ALEXAMEDIA]['accounts']:
        hass.data[DATA_ALEXAMEDIA]['accounts'][email] = {}
    (hass.data[DATA_ALEXAMEDIA]['accounts'][email]['login_obj']) = login
    await login.login_with_cookie()
    await test_login_status(hass, config_entry, login, setup_platform_callback)
    return True
async def async_setup_entry(hass: HomeAssistant,
                            config_entry: ConfigEntry) -> bool:
    """Set up OpenUV as config entry."""
    hass.data.setdefault(DOMAIN, {DATA_CLIENT: {}, DATA_LISTENER: {}})

    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    try:
        websession = aiohttp_client.async_get_clientsession(hass)
        openuv = OpenUV(
            config_entry,
            Client(
                config_entry.data[CONF_API_KEY],
                config_entry.data.get(CONF_LATITUDE, hass.config.latitude),
                config_entry.data.get(CONF_LONGITUDE, hass.config.longitude),
                altitude=config_entry.data.get(CONF_ELEVATION,
                                               hass.config.elevation),
                session=websession,
            ),
        )
        await openuv.async_update()
        hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = openuv
    except OpenUvError as err:
        LOGGER.error("Config entry failed: %s", err)
        raise ConfigEntryNotReady from err

    hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)

    @_verify_domain_control
    async def update_data(_: ServiceCall) -> None:
        """Refresh all OpenUV data."""
        LOGGER.debug("Refreshing all OpenUV data")
        await openuv.async_update()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    @_verify_domain_control
    async def update_uv_index_data(_: ServiceCall) -> None:
        """Refresh OpenUV UV index data."""
        LOGGER.debug("Refreshing OpenUV UV index data")
        await openuv.async_update_uv_index_data()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    @_verify_domain_control
    async def update_protection_data(_: ServiceCall) -> None:
        """Refresh OpenUV protection window data."""
        LOGGER.debug("Refreshing OpenUV protection window data")
        await openuv.async_update_protection_data()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    for service, method in (
        ("update_data", update_data),
        ("update_uv_index_data", update_uv_index_data),
        ("update_protection_data", update_protection_data),
    ):
        hass.services.async_register(DOMAIN, service, method)

    return True
Beispiel #5
0
async def async_setup_entry(hass, config_entry):
    """Set up OpenUV as config entry."""

    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    try:
        websession = aiohttp_client.async_get_clientsession(hass)
        openuv = OpenUV(
            Client(
                config_entry.data[CONF_API_KEY],
                config_entry.data.get(CONF_LATITUDE, hass.config.latitude),
                config_entry.data.get(CONF_LONGITUDE, hass.config.longitude),
                websession,
                altitude=config_entry.data.get(CONF_ELEVATION,
                                               hass.config.elevation),
            ))
        await openuv.async_update()
        hass.data[DOMAIN][DATA_OPENUV_CLIENT][config_entry.entry_id] = openuv
    except OpenUvError as err:
        _LOGGER.error("Config entry failed: %s", err)
        raise ConfigEntryNotReady

    for component in ("binary_sensor", "sensor"):
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, component))

    @_verify_domain_control
    async def update_data(service):
        """Refresh all OpenUV data."""
        _LOGGER.debug("Refreshing all OpenUV data")
        await openuv.async_update()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    hass.services.async_register(DOMAIN, "update_data", update_data)

    @_verify_domain_control
    async def update_uv_index_data(service):
        """Refresh OpenUV UV index data."""
        _LOGGER.debug("Refreshing OpenUV UV index data")
        await openuv.async_update_uv_index_data()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    hass.services.async_register(DOMAIN, "update_uv_index_data",
                                 update_uv_index_data)

    @_verify_domain_control
    async def update_protection_data(service):
        """Refresh OpenUV protection window data."""
        _LOGGER.debug("Refreshing OpenUV protection window data")
        await openuv.async_update_protection_data()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    hass.services.async_register(DOMAIN, "update_protection_data",
                                 update_protection_data)

    return True
Beispiel #6
0
async def async_setup_entry(hass, config_entry):
    """Set up OpenUV as config entry."""
    from pyopenuv import Client
    from pyopenuv.errors import OpenUvError

    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    try:
        websession = aiohttp_client.async_get_clientsession(hass)
        openuv = OpenUV(
            Client(
                config_entry.data[CONF_API_KEY],
                config_entry.data.get(CONF_LATITUDE, hass.config.latitude),
                config_entry.data.get(CONF_LONGITUDE, hass.config.longitude),
                websession,
                altitude=config_entry.data.get(CONF_ELEVATION,
                                               hass.config.elevation),
            ),
            config_entry.data.get(CONF_BINARY_SENSORS,
                                  {}).get(CONF_MONITORED_CONDITIONS,
                                          list(BINARY_SENSORS)),
            config_entry.data.get(CONF_SENSORS,
                                  {}).get(CONF_MONITORED_CONDITIONS,
                                          list(SENSORS)),
        )
        await openuv.async_update()
        hass.data[DOMAIN][DATA_OPENUV_CLIENT][config_entry.entry_id] = openuv
    except OpenUvError as err:
        _LOGGER.error("Config entry failed: %s", err)
        raise ConfigEntryNotReady

    for component in ("binary_sensor", "sensor"):
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, component))

    @_verify_domain_control
    async def update_data(service):
        """Refresh OpenUV data."""
        _LOGGER.debug("Refreshing OpenUV data")

        try:
            await openuv.async_update()
        except OpenUvError as err:
            _LOGGER.error("Error during data update: %s", err)
            return

        async_dispatcher_send(hass, TOPIC_UPDATE)

    hass.services.async_register(DOMAIN, "update_data", update_data)

    return True
def async_register_services(hass: HomeAssistant) -> None:
    """Register services for Hue integration."""

    async def hue_activate_scene(call: ServiceCall, skip_reload=True) -> None:
        """Handle activation of Hue scene."""
        # Get parameters
        group_name = call.data[ATTR_GROUP_NAME]
        scene_name = call.data[ATTR_SCENE_NAME]
        transition = call.data.get(ATTR_TRANSITION)
        dynamic = call.data.get(ATTR_DYNAMIC, False)

        # Call the set scene function on each bridge
        tasks = [
            hue_activate_scene_v1(bridge, group_name, scene_name, transition)
            if bridge.api_version == 1
            else hue_activate_scene_v2(
                bridge, group_name, scene_name, transition, dynamic
            )
            for bridge in hass.data[DOMAIN].values()
            if isinstance(bridge, HueBridge)
        ]
        results = await asyncio.gather(*tasks)

        # Did *any* bridge succeed?
        # Note that we'll get a "True" value for a successful call
        if True not in results:
            LOGGER.warning(
                "No bridge was able to activate scene %s in group %s",
                scene_name,
                group_name,
            )

    if not hass.services.has_service(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE):
        # Register a local handler for scene activation
        hass.services.async_register(
            DOMAIN,
            SERVICE_HUE_ACTIVATE_SCENE,
            verify_domain_control(hass, DOMAIN)(hue_activate_scene),
            schema=vol.Schema(
                {
                    vol.Required(ATTR_GROUP_NAME): cv.string,
                    vol.Required(ATTR_SCENE_NAME): cv.string,
                    vol.Optional(ATTR_TRANSITION): cv.positive_int,
                    vol.Optional(ATTR_DYNAMIC): cv.boolean,
                }
            ),
        )
Beispiel #8
0
def _register_services(hass):
    """Register Hue services."""

    async def hue_activate_scene(call, skip_reload=True):
        """Handle activation of Hue scene."""
        # Get parameters
        group_name = call.data[ATTR_GROUP_NAME]
        scene_name = call.data[ATTR_SCENE_NAME]

        # Call the set scene function on each bridge
        tasks = [
            bridge.hue_activate_scene(
                call.data, skip_reload=skip_reload, hide_warnings=skip_reload
            )
            for bridge in hass.data[DOMAIN].values()
            if isinstance(bridge, HueBridge)
        ]
        results = await asyncio.gather(*tasks)

        # Did *any* bridge succeed? If not, refresh / retry
        # Note that we'll get a "None" value for a successful call
        if None not in results:
            if skip_reload:
                await hue_activate_scene(call, skip_reload=False)
                return
            _LOGGER.warning(
                "No bridge was able to activate " "scene %s in group %s",
                scene_name,
                group_name,
            )

    if not hass.services.has_service(DOMAIN, SERVICE_HUE_SCENE):
        # Register a local handler for scene activation
        hass.services.async_register(
            DOMAIN,
            SERVICE_HUE_SCENE,
            verify_domain_control(hass, DOMAIN)(hue_activate_scene),
            schema=vol.Schema(
                {
                    vol.Required(ATTR_GROUP_NAME): cv.string,
                    vol.Required(ATTR_SCENE_NAME): cv.string,
                    vol.Optional(ATTR_TRANSITION): cv.positive_int,
                }
            ),
        )
Beispiel #9
0
async def test_domain_control_unauthorized(hass, hass_read_only_user):
    """Test domain verification in a service call with an unauthorized user."""
    mock_registry(
        hass,
        {
            "light.kitchen":
            ent_reg.RegistryEntry(
                entity_id="light.kitchen",
                unique_id="kitchen",
                platform="test_domain",
            )
        },
    )

    calls = []

    async def mock_service_log(call):
        """Define a protected service."""
        calls.append(call)

    protected_mock_service = service.verify_domain_control(
        hass, "test_domain")(mock_service_log)

    hass.services.async_register("test_domain",
                                 "test_service",
                                 protected_mock_service,
                                 schema=None)

    with pytest.raises(exceptions.Unauthorized):
        await hass.services.async_call(
            "test_domain",
            "test_service",
            {},
            blocking=True,
            context=ha.Context(user_id=hass_read_only_user.id),
        )

    assert len(calls) == 0
Beispiel #10
0
async def test_domain_control_no_user(hass):
    """Test domain verification in a service call with no user."""
    mock_registry(
        hass,
        {
            "light.kitchen":
            ent_reg.RegistryEntry(
                entity_id="light.kitchen",
                unique_id="kitchen",
                platform="test_domain",
            )
        },
    )

    calls = []

    async def mock_service_log(call):
        """Define a protected service."""
        calls.append(call)

    protected_mock_service = service.verify_domain_control(
        hass, "test_domain")(mock_service_log)

    hass.services.async_register("test_domain",
                                 "test_service",
                                 protected_mock_service,
                                 schema=None)

    await hass.services.async_call(
        "test_domain",
        "test_service",
        {},
        blocking=True,
        context=ha.Context(user_id=None),
    )

    assert len(calls) == 1
Beispiel #11
0
async def async_setup_entry(hass, config_entry):
    """Set up RainMachine as config entry."""
    if not config_entry.unique_id:
        hass.config_entries.async_update_entry(
            config_entry, unique_id=config_entry.data[CONF_IP_ADDRESS])

    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    websession = aiohttp_client.async_get_clientsession(hass)
    client = Client(session=websession)

    try:
        await client.load_local(
            config_entry.data[CONF_IP_ADDRESS],
            config_entry.data[CONF_PASSWORD],
            port=config_entry.data[CONF_PORT],
            ssl=config_entry.data.get(CONF_SSL, DEFAULT_SSL),
        )
    except RainMachineError as err:
        _LOGGER.error("An error occurred: %s", err)
        raise ConfigEntryNotReady
    else:
        # regenmaschine can load multiple controllers at once, but we only grab the one
        # we loaded above:
        controller = next(iter(client.controllers.values()))

        rainmachine = RainMachine(
            hass,
            controller,
            config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN),
            config_entry.data.get(CONF_SCAN_INTERVAL,
                                  DEFAULT_SCAN_INTERVAL.total_seconds()),
        )

    # Update the data object, which at this point (prior to any sensors registering
    # "interest" in the API), will focus on grabbing the latest program and zone data:
    await rainmachine.async_update()
    hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = rainmachine

    for component in ("binary_sensor", "sensor", "switch"):
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, component))

    @_verify_domain_control
    async def disable_program(call):
        """Disable a program."""
        await rainmachine.controller.programs.disable(
            call.data[CONF_PROGRAM_ID])
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def disable_zone(call):
        """Disable a zone."""
        await rainmachine.controller.zones.disable(call.data[CONF_ZONE_ID])
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def enable_program(call):
        """Enable a program."""
        await rainmachine.controller.programs.enable(call.data[CONF_PROGRAM_ID]
                                                     )
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def enable_zone(call):
        """Enable a zone."""
        await rainmachine.controller.zones.enable(call.data[CONF_ZONE_ID])
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def pause_watering(call):
        """Pause watering for a set number of seconds."""
        await rainmachine.controller.watering.pause_all(call.data[CONF_SECONDS]
                                                        )
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def start_program(call):
        """Start a particular program."""
        await rainmachine.controller.programs.start(call.data[CONF_PROGRAM_ID])
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def start_zone(call):
        """Start a particular zone for a certain amount of time."""
        await rainmachine.controller.zones.start(call.data[CONF_ZONE_ID],
                                                 call.data[CONF_ZONE_RUN_TIME])
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def stop_all(call):
        """Stop all watering."""
        await rainmachine.controller.watering.stop_all()
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def stop_program(call):
        """Stop a program."""
        await rainmachine.controller.programs.stop(call.data[CONF_PROGRAM_ID])
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def stop_zone(call):
        """Stop a zone."""
        await rainmachine.controller.zones.stop(call.data[CONF_ZONE_ID])
        await rainmachine.async_update_programs_and_zones()

    @_verify_domain_control
    async def unpause_watering(call):
        """Unpause watering."""
        await rainmachine.controller.watering.unpause_all()
        await rainmachine.async_update_programs_and_zones()

    for service, method, schema in [
        ("disable_program", disable_program, SERVICE_ALTER_PROGRAM),
        ("disable_zone", disable_zone, SERVICE_ALTER_ZONE),
        ("enable_program", enable_program, SERVICE_ALTER_PROGRAM),
        ("enable_zone", enable_zone, SERVICE_ALTER_ZONE),
        ("pause_watering", pause_watering, SERVICE_PAUSE_WATERING),
        ("start_program", start_program, SERVICE_START_PROGRAM_SCHEMA),
        ("start_zone", start_zone, SERVICE_START_ZONE_SCHEMA),
        ("stop_all", stop_all, {}),
        ("stop_program", stop_program, SERVICE_STOP_PROGRAM_SCHEMA),
        ("stop_zone", stop_zone, SERVICE_STOP_ZONE_SCHEMA),
        ("unpause_watering", unpause_watering, {}),
    ]:
        hass.services.async_register(DOMAIN, service, method, schema=schema)

    return True
Beispiel #12
0
async def async_setup_entry(hass, config_entry):
    """Set up RainMachine as config entry."""
    from regenmaschine import login
    from regenmaschine.errors import RainMachineError

    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    websession = aiohttp_client.async_get_clientsession(hass)

    try:
        client = await login(config_entry.data[CONF_IP_ADDRESS],
                             config_entry.data[CONF_PASSWORD],
                             websession,
                             port=config_entry.data[CONF_PORT],
                             ssl=config_entry.data[CONF_SSL])
        rainmachine = RainMachine(
            client,
            config_entry.data.get(CONF_BINARY_SENSORS,
                                  {}).get(CONF_MONITORED_CONDITIONS,
                                          list(BINARY_SENSORS)),
            config_entry.data.get(CONF_SENSORS,
                                  {}).get(CONF_MONITORED_CONDITIONS,
                                          list(SENSORS)),
            config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN))
        await rainmachine.async_update()
    except RainMachineError as err:
        _LOGGER.error('An error occurred: %s', err)
        raise ConfigEntryNotReady

    hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = rainmachine

    for component in ('binary_sensor', 'sensor', 'switch'):
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, component))

    async def refresh(event_time):
        """Refresh RainMachine sensor data."""
        _LOGGER.debug('Updating RainMachine sensor data')
        await rainmachine.async_update()
        async_dispatcher_send(hass, SENSOR_UPDATE_TOPIC)

    hass.data[DOMAIN][DATA_LISTENER][
        config_entry.entry_id] = async_track_time_interval(
            hass, refresh,
            timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL]))

    @_verify_domain_control
    async def disable_program(call):
        """Disable a program."""
        await rainmachine.client.programs.disable(call.data[CONF_PROGRAM_ID])
        async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC)

    @_verify_domain_control
    async def disable_zone(call):
        """Disable a zone."""
        await rainmachine.client.zones.disable(call.data[CONF_ZONE_ID])
        async_dispatcher_send(hass, ZONE_UPDATE_TOPIC)

    @_verify_domain_control
    async def enable_program(call):
        """Enable a program."""
        await rainmachine.client.programs.enable(call.data[CONF_PROGRAM_ID])
        async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC)

    @_verify_domain_control
    async def enable_zone(call):
        """Enable a zone."""
        await rainmachine.client.zones.enable(call.data[CONF_ZONE_ID])
        async_dispatcher_send(hass, ZONE_UPDATE_TOPIC)

    @_verify_domain_control
    async def pause_watering(call):
        """Pause watering for a set number of seconds."""
        await rainmachine.client.watering.pause_all(call.data[CONF_SECONDS])
        async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC)

    @_verify_domain_control
    async def start_program(call):
        """Start a particular program."""
        await rainmachine.client.programs.start(call.data[CONF_PROGRAM_ID])
        async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC)

    @_verify_domain_control
    async def start_zone(call):
        """Start a particular zone for a certain amount of time."""
        await rainmachine.client.zones.start(call.data[CONF_ZONE_ID],
                                             call.data[CONF_ZONE_RUN_TIME])
        async_dispatcher_send(hass, ZONE_UPDATE_TOPIC)

    @_verify_domain_control
    async def stop_all(call):
        """Stop all watering."""
        await rainmachine.client.watering.stop_all()
        async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC)

    @_verify_domain_control
    async def stop_program(call):
        """Stop a program."""
        await rainmachine.client.programs.stop(call.data[CONF_PROGRAM_ID])
        async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC)

    @_verify_domain_control
    async def stop_zone(call):
        """Stop a zone."""
        await rainmachine.client.zones.stop(call.data[CONF_ZONE_ID])
        async_dispatcher_send(hass, ZONE_UPDATE_TOPIC)

    @_verify_domain_control
    async def unpause_watering(call):
        """Unpause watering."""
        await rainmachine.client.watering.unpause_all()
        async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC)

    for service, method, schema in [
        ('disable_program', disable_program, SERVICE_ALTER_PROGRAM),
        ('disable_zone', disable_zone, SERVICE_ALTER_ZONE),
        ('enable_program', enable_program, SERVICE_ALTER_PROGRAM),
        ('enable_zone', enable_zone, SERVICE_ALTER_ZONE),
        ('pause_watering', pause_watering, SERVICE_PAUSE_WATERING),
        ('start_program', start_program, SERVICE_START_PROGRAM_SCHEMA),
        ('start_zone', start_zone, SERVICE_START_ZONE_SCHEMA),
        ('stop_all', stop_all, {}),
        ('stop_program', stop_program, SERVICE_STOP_PROGRAM_SCHEMA),
        ('stop_zone', stop_zone, SERVICE_STOP_ZONE_SCHEMA),
        ('unpause_watering', unpause_watering, {}),
    ]:
        hass.services.async_register(DOMAIN, service, method, schema=schema)

    return True
Beispiel #13
0
async def async_setup_entry(hass, config_entry):
    """Set up SimpliSafe as config entry."""
    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    websession = aiohttp_client.async_get_clientsession(hass)

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

    _async_save_refresh_token(hass, config_entry, api.refresh_token)

    systems = await api.get_systems()
    simplisafe = SimpliSafe(hass, config_entry, systems)
    await simplisafe.async_update()
    hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe

    hass.async_create_task(
        hass.config_entries.async_forward_entry_setup(config_entry,
                                                      "alarm_control_panel"))

    async def refresh(event_time):
        """Refresh data from the SimpliSafe account."""
        await simplisafe.async_update()
        _LOGGER.debug("Updated data for all SimpliSafe systems")
        async_dispatcher_send(hass, TOPIC_UPDATE)

    hass.data[DOMAIN][DATA_LISTENER][
        config_entry.entry_id] = async_track_time_interval(
            hass, refresh,
            timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL]))

    # Register the base station for each system:
    for system in systems.values():
        hass.async_create_task(
            async_register_base_station(hass, system, config_entry.entry_id))

    @_verify_domain_control
    async def remove_pin(call):
        """Remove a PIN."""
        system = systems[int(call.data[ATTR_SYSTEM_ID])]
        await system.remove_pin(call.data[ATTR_PIN_LABEL_OR_VALUE])

    @_verify_domain_control
    async def set_pin(call):
        """Set a PIN."""
        system = systems[int(call.data[ATTR_SYSTEM_ID])]
        await system.set_pin(call.data[ATTR_PIN_LABEL],
                             call.data[ATTR_PIN_VALUE])

    for service, method, schema in [
        ("remove_pin", remove_pin, SERVICE_REMOVE_PIN_SCHEMA),
        ("set_pin", set_pin, SERVICE_SET_PIN_SCHEMA),
    ]:
        hass.services.async_register(DOMAIN, service, method, schema=schema)

    return True
Beispiel #14
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Elexa Guardian from a config entry."""
    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    guardian = Guardian(hass, entry)
    await guardian.async_update()
    hass.data[DOMAIN][DATA_CLIENT][entry.entry_id] = guardian

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

    @_verify_domain_control
    async def disable_ap(call):
        """Disable the device's onboard access point."""
        try:
            async with guardian.client:
                await guardian.client.device.wifi_disable_ap()
        except GuardianError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    @_verify_domain_control
    async def enable_ap(call):
        """Enable the device's onboard access point."""
        try:
            async with guardian.client:
                await guardian.client.device.wifi_enable_ap()
        except GuardianError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    @_verify_domain_control
    async def reboot(call):
        """Reboot the device."""
        try:
            async with guardian.client:
                await guardian.client.device.reboot()
        except GuardianError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    @_verify_domain_control
    async def reset_valve_diagnostics(call):
        """Fully reset system motor diagnostics."""
        try:
            async with guardian.client:
                await guardian.client.valve.valve_reset()
        except GuardianError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    @_verify_domain_control
    async def upgrade_firmware(call):
        """Upgrade the device firmware."""
        try:
            async with guardian.client:
                await guardian.client.device.upgrade_firmware(
                    url=call.data[CONF_URL],
                    port=call.data[CONF_PORT],
                    filename=call.data[CONF_FILENAME],
                )
        except GuardianError as err:
            LOGGER.error("Error during service call: %s", err)
            return

    for service, method, schema in [
        ("disable_ap", disable_ap, None),
        ("enable_ap", enable_ap, None),
        ("reboot", reboot, None),
        ("reset_valve_diagnostics", reset_valve_diagnostics, None),
        ("upgrade_firmware", upgrade_firmware,
         SERVICE_UPGRADE_FIRMWARE_SCHEMA),
    ]:
        async_register_admin_service(hass,
                                     DOMAIN,
                                     service,
                                     method,
                                     schema=schema)

    return True
Beispiel #15
0
async def async_setup_entry(hass, config_entry):
    """Set up SimpliSafe as config entry."""
    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    websession = aiohttp_client.async_get_clientsession(hass)

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

    _async_save_refresh_token(hass, config_entry, api.refresh_token)

    systems = await api.get_systems()
    simplisafe = SimpliSafe(hass, api, systems, config_entry)
    await simplisafe.async_update()
    hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe

    for component in ("alarm_control_panel", "lock"):
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(config_entry, component)
        )

    async def refresh(event_time):
        """Refresh data from the SimpliSafe account."""
        await simplisafe.async_update()
        _LOGGER.debug("Updated data for all SimpliSafe systems")
        async_dispatcher_send(hass, TOPIC_UPDATE)

    hass.data[DOMAIN][DATA_LISTENER][config_entry.entry_id] = async_track_time_interval(
        hass, refresh, timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL])
    )

    # Register the base station for each system:
    for system in systems.values():
        hass.async_create_task(
            async_register_base_station(hass, system, config_entry.entry_id)
        )

    @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 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 = 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 remove_pin(call):
        """Remove a PIN."""
        system = 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
    @v3_only
    @_verify_domain_control
    async def set_alarm_duration(call):
        """Set the duration of a running alarm."""
        system = systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.set_alarm_duration(call.data[ATTR_SECONDS])
        except SimplipyError as err:
            _LOGGER.error("Error during service call: %s", err)
            return

    @verify_system_exists
    @v3_only
    @_verify_domain_control
    async def set_delay(call):
        """Set the delay duration for entry/exit, away/home (any combo)."""
        system = systems[call.data[ATTR_SYSTEM_ID]]
        coro = getattr(
            system,
            f"set_{call.data[ATTR_TRANSITION]}_delay_{call.data[ATTR_ARRIVAL_STATE]}",
        )

        try:
            await coro(call.data[ATTR_SECONDS])
        except SimplipyError as err:
            _LOGGER.error("Error during service call: %s", err)
            return

    @verify_system_exists
    @v3_only
    @_verify_domain_control
    async def set_armed_light(call):
        """Turn the base station light on/off."""
        system = systems[call.data[ATTR_SYSTEM_ID]]
        try:
            await system.set_light(call.data[ATTR_ARMED_LIGHT_STATE])
        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 = 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_volume_property(call):
        """Set a volume parameter in an appropriate service call."""
        system = systems[call.data[ATTR_SYSTEM_ID]]
        try:
            volume = V3Volume[call.data[ATTR_VOLUME]]
        except KeyError:
            _LOGGER.error("Unknown volume string: %s", call.data[ATTR_VOLUME])
            return
        except SimplipyError as err:
            _LOGGER.error("Error during service call: %s", err)
            return
        else:
            coro = getattr(system, f"set_{call.data[ATTR_VOLUME_PROPERTY]}_volume")
            await coro(volume)

    for service, method, schema in [
        ("remove_pin", remove_pin, SERVICE_REMOVE_PIN_SCHEMA),
        ("set_alarm_duration", set_alarm_duration, SERVICE_SET_DELAY_SCHEMA),
        ("set_delay", set_delay, SERVICE_SET_DELAY_SCHEMA),
        ("set_armed_light", set_armed_light, SERVICE_SET_LIGHT_SCHEMA),
        ("set_pin", set_pin, SERVICE_SET_PIN_SCHEMA),
        ("set_volume_property", set_volume_property, SERVICE_SET_VOLUME_SCHEMA),
    ]:
        hass.services.async_register(DOMAIN, service, method, schema=schema)

    return True
Beispiel #16
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up RainMachine as config entry."""
    hass.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = {}

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

    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    websession = aiohttp_client.async_get_clientsession(hass)
    client = Client(session=websession)

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

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

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

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

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

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

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

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

    await asyncio.gather(*controller_init_tasks)

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

    @_verify_domain_control
    async def disable_program(call: ServiceCall):
        """Disable a program."""
        await controller.programs.disable(call.data[CONF_PROGRAM_ID])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def disable_zone(call: ServiceCall):
        """Disable a zone."""
        await controller.zones.disable(call.data[CONF_ZONE_ID])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def enable_program(call: ServiceCall):
        """Enable a program."""
        await controller.programs.enable(call.data[CONF_PROGRAM_ID])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def enable_zone(call: ServiceCall):
        """Enable a zone."""
        await controller.zones.enable(call.data[CONF_ZONE_ID])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def pause_watering(call: ServiceCall):
        """Pause watering for a set number of seconds."""
        await controller.watering.pause_all(call.data[CONF_SECONDS])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def start_program(call: ServiceCall):
        """Start a particular program."""
        await controller.programs.start(call.data[CONF_PROGRAM_ID])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def start_zone(call: ServiceCall):
        """Start a particular zone for a certain amount of time."""
        await controller.zones.start(call.data[CONF_ZONE_ID],
                                     call.data[CONF_ZONE_RUN_TIME])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def stop_all(call: ServiceCall):
        """Stop all watering."""
        await controller.watering.stop_all()
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def stop_program(call: ServiceCall):
        """Stop a program."""
        await controller.programs.stop(call.data[CONF_PROGRAM_ID])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def stop_zone(call: ServiceCall):
        """Stop a zone."""
        await controller.zones.stop(call.data[CONF_ZONE_ID])
        await async_update_programs_and_zones(hass, entry)

    @_verify_domain_control
    async def unpause_watering(call: ServiceCall):
        """Unpause watering."""
        await controller.watering.unpause_all()
        await async_update_programs_and_zones(hass, entry)

    for service, method, schema in [
        ("disable_program", disable_program, SERVICE_ALTER_PROGRAM),
        ("disable_zone", disable_zone, SERVICE_ALTER_ZONE),
        ("enable_program", enable_program, SERVICE_ALTER_PROGRAM),
        ("enable_zone", enable_zone, SERVICE_ALTER_ZONE),
        ("pause_watering", pause_watering, SERVICE_PAUSE_WATERING),
        ("start_program", start_program, SERVICE_START_PROGRAM_SCHEMA),
        ("start_zone", start_zone, SERVICE_START_ZONE_SCHEMA),
        ("stop_all", stop_all, {}),
        ("stop_program", stop_program, SERVICE_STOP_PROGRAM_SCHEMA),
        ("stop_zone", stop_zone, SERVICE_STOP_ZONE_SCHEMA),
        ("unpause_watering", unpause_watering, {}),
    ]:
        hass.services.async_register(DOMAIN, service, method, schema=schema)

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

    return True
Beispiel #17
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up OpenUV as config entry."""
    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    websession = aiohttp_client.async_get_clientsession(hass)
    openuv = OpenUV(
        entry,
        Client(
            entry.data[CONF_API_KEY],
            entry.data.get(CONF_LATITUDE, hass.config.latitude),
            entry.data.get(CONF_LONGITUDE, hass.config.longitude),
            altitude=entry.data.get(CONF_ELEVATION, hass.config.elevation),
            session=websession,
        ),
    )

    # We disable the client's request retry abilities here to avoid a lengthy (and
    # blocking) startup:
    openuv.client.disable_request_retries()

    try:
        await openuv.async_update()
    except HomeAssistantError as err:
        LOGGER.error("Config entry failed: %s", err)
        raise ConfigEntryNotReady from err

    # Once we've successfully authenticated, we re-enable client request retries:
    openuv.client.enable_request_retries()

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

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    @_verify_domain_control
    async def update_data(_: ServiceCall) -> None:
        """Refresh all OpenUV data."""
        LOGGER.debug("Refreshing all OpenUV data")
        await openuv.async_update()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    @_verify_domain_control
    async def update_uv_index_data(_: ServiceCall) -> None:
        """Refresh OpenUV UV index data."""
        LOGGER.debug("Refreshing OpenUV UV index data")
        await openuv.async_update_uv_index_data()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    @_verify_domain_control
    async def update_protection_data(_: ServiceCall) -> None:
        """Refresh OpenUV protection window data."""
        LOGGER.debug("Refreshing OpenUV protection window data")
        await openuv.async_update_protection_data()
        async_dispatcher_send(hass, TOPIC_UPDATE)

    for service, method in (
        ("update_data", update_data),
        ("update_uv_index_data", update_uv_index_data),
        ("update_protection_data", update_protection_data),
    ):
        hass.services.async_register(DOMAIN, service, method)

    return True
Beispiel #18
0
async def async_setup_entry(hass, config_entry):
    """Set up SimpliSafe as config entry."""
    _verify_domain_control = verify_domain_control(hass, DOMAIN)

    websession = aiohttp_client.async_get_clientsession(hass)

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

    _async_save_refresh_token(hass, config_entry, api.refresh_token)

    simplisafe = SimpliSafe(hass, api, config_entry)
    await simplisafe.async_init()
    hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe

    for component in ("alarm_control_panel", "lock"):
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, component))

    @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 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 [
        ("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)

    return True
Beispiel #19
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
Beispiel #20
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