Example #1
0
async def validate_input(hass: HomeAssistant,
                         data: dict[str, Any]) -> dict[str, str]:
    """Validate the user input allows us to connect.

    Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
    """
    bridge = Bridge(
        BridgeClient(aiohttp_client.async_get_clientsession(hass)),
        f"http://{data[CONF_HOST]}:{data[CONF_PORT]}",
        data[CONF_API_KEY],
    )

    hostname = data[CONF_HOST]
    try:
        async with async_timeout.timeout(30):
            bridge_os: Os = await bridge.async_get_os()
            if bridge_os.hostname is not None:
                hostname = bridge_os.hostname
            bridge_system: System = await bridge.async_get_system()
    except BridgeAuthenticationException as exception:
        _LOGGER.info(exception)
        raise InvalidAuth from exception
    except BRIDGE_CONNECTION_ERRORS as exception:
        _LOGGER.info(exception)
        raise CannotConnect from exception

    return {"hostname": hostname, "uuid": bridge_system.uuid.os}
Example #2
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up System Bridge from a config entry."""
    bridge = Bridge(
        BridgeClient(aiohttp_client.async_get_clientsession(hass)),
        f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}",
        entry.data[CONF_API_KEY],
    )

    try:
        async with async_timeout.timeout(30):
            await bridge.async_get_information()
    except BridgeAuthenticationException as exception:
        raise ConfigEntryAuthFailed(
            f"Authentication failed for {entry.title} ({entry.data[CONF_HOST]})"
        ) from exception
    except BRIDGE_CONNECTION_ERRORS as exception:
        raise ConfigEntryNotReady(
            f"Could not connect to {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

    coordinator = SystemBridgeDataUpdateCoordinator(hass, bridge, _LOGGER, entry=entry)
    await coordinator.async_config_entry_first_refresh()

    # Wait for initial data
    try:
        async with async_timeout.timeout(60):
            while (
                coordinator.bridge.battery is None
                or coordinator.bridge.cpu is None
                or coordinator.bridge.filesystem is None
                or coordinator.bridge.graphics is None
                or coordinator.bridge.information is None
                or coordinator.bridge.memory is None
                or coordinator.bridge.network is None
                or coordinator.bridge.os is None
                or coordinator.bridge.processes is None
                or coordinator.bridge.system is None
            ):
                _LOGGER.debug(
                    "Waiting for initial data from %s (%s)",
                    entry.title,
                    entry.data[CONF_HOST],
                )
                await asyncio.sleep(1)
    except asyncio.TimeoutError as exception:
        raise ConfigEntryNotReady(
            f"Timed out waiting for {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

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

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    if hass.services.has_service(DOMAIN, SERVICE_SEND_COMMAND):
        return True

    async def handle_send_command(call):
        """Handle the send_command service call."""
        device_registry = dr.async_get(hass)
        device_id = call.data[CONF_BRIDGE]
        device_entry = device_registry.async_get(device_id)
        if device_entry is None:
            _LOGGER.warning("Missing device: %s", device_id)
            return

        command = call.data[CONF_COMMAND]
        arguments = shlex.split(call.data.get(CONF_ARGUMENTS, ""))

        entry_id = next(
            entry.entry_id
            for entry in hass.config_entries.async_entries(DOMAIN)
            if entry.entry_id in device_entry.config_entries
        )
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry_id]
        bridge: Bridge = coordinator.bridge

        _LOGGER.debug(
            "Command payload: %s",
            {CONF_COMMAND: command, CONF_ARGUMENTS: arguments, CONF_WAIT: False},
        )
        try:
            response: CommandResponse = await bridge.async_send_command(
                {CONF_COMMAND: command, CONF_ARGUMENTS: arguments, CONF_WAIT: False}
            )
            if response.success:
                _LOGGER.debug(
                    "Sent command. Response message was: %s", response.message
                )
            else:
                _LOGGER.warning(
                    "Error sending command. Response message was: %s", response.message
                )
        except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception:
            _LOGGER.warning("Error sending command. Error was: %s", exception)

    async def handle_open(call):
        """Handle the open service call."""
        device_registry = dr.async_get(hass)
        device_id = call.data[CONF_BRIDGE]
        device_entry = device_registry.async_get(device_id)
        if device_entry is None:
            _LOGGER.warning("Missing device: %s", device_id)
            return

        path = call.data[CONF_PATH]

        entry_id = next(
            entry.entry_id
            for entry in hass.config_entries.async_entries(DOMAIN)
            if entry.entry_id in device_entry.config_entries
        )
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry_id]
        bridge: Bridge = coordinator.bridge

        _LOGGER.debug("Open payload: %s", {CONF_PATH: path})
        try:
            await bridge.async_open({CONF_PATH: path})
            _LOGGER.debug("Sent open request")
        except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception:
            _LOGGER.warning("Error sending. Error was: %s", exception)

    hass.services.async_register(
        DOMAIN,
        SERVICE_SEND_COMMAND,
        handle_send_command,
        schema=SERVICE_SEND_COMMAND_SCHEMA,
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_OPEN,
        handle_open,
        schema=SERVICE_OPEN_SCHEMA,
    )

    # Reload entry when its updated.
    entry.async_on_unload(entry.add_update_listener(async_reload_entry))

    return True
Example #3
0
async def main() -> None:
    async with ClientSession() as session:
        client = Bridge(
            BridgeClient(session),
            f"http://{HOST}:{PORT}",
            API_KEY,
        )

        async def handle_event(event: Event):
            print("Unused Event:", event.__dict__)

        await client.async_connect_websocket(HOST,
                                             (await client.async_get_setting(
                                                 "network", "wsPort")).value)
        await client.async_send_event("test", "text")
        asyncio.ensure_future(client.listen_for_events(handle_event))

        async with async_timeout.timeout(60):
            await asyncio.gather(*[
                client.async_get_audio(),
                client.async_get_battery(),
                client.async_get_bluetooth(),
                client.async_get_cpu(),
                client.async_get_display(),
                client.async_get_filesystem(),
                client.async_get_graphics(),
                client.async_get_memory(),
                client.async_get_network(),
                client.async_get_os(),
                client.async_get_processes(),
                client.async_get_settings(),
                client.async_get_system(),
            ])
        print()
        print("Results")
        print()
        print(client.audio)
        # print(client.audio.__dict__)
        # print()
        print(client.bluetooth)
        # print(client.bluetooth.__dict__)
        # print()
        print(client.battery)
        # print(client.battery.__dict__)
        # print()
        print(client.cpu)
        # print(client.cpu.__dict__)
        # print()
        print(client.display)
        # print(client.display.__dict__)
        # print()
        print(client.filesystem)
        # print(client.filesystem.__dict__)
        # print()
        print(client.graphics)
        # print(client.graphics.__dict__)
        # print()
        print(client.memory)
        # print(client.memory.__dict__)
        # print()
        print(client.network)
        # print(client.network.__dict__)
        # print()
        print(client.os)
        # print(client.os.__dict__)
        # print()
        print(client.processes)
        # print(client.processes.__dict__)
        # print()
        print(client.settings)
        # for setting in client.settings:
        #     print(setting.__dict__)
        #     print(setting.key)
        #     print(setting.name)
        #     print(setting.value)
        # print()
        print(client.system)
        # print(client.system.__dict__)

        while True:
            await asyncio.sleep(1)
Example #4
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up System Bridge from a config entry."""
    bridge = Bridge(
        BridgeClient(aiohttp_client.async_get_clientsession(hass)),
        f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}",
        entry.data[CONF_API_KEY],
    )

    try:
        async with async_timeout.timeout(30):
            await bridge.async_get_information()
    except BridgeAuthenticationException as exception:
        raise ConfigEntryAuthFailed(
            f"Authentication failed for {entry.title} ({entry.data[CONF_HOST]})"
        ) from exception
    except BRIDGE_CONNECTION_ERRORS as exception:
        raise ConfigEntryNotReady(
            f"Could not connect to {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

    coordinator = SystemBridgeDataUpdateCoordinator(hass,
                                                    bridge,
                                                    _LOGGER,
                                                    entry=entry)
    await coordinator.async_config_entry_first_refresh()

    # Wait for initial data
    try:
        async with async_timeout.timeout(60):
            while (coordinator.bridge.battery is None
                   or coordinator.bridge.cpu is None
                   or coordinator.bridge.display is None
                   or coordinator.bridge.filesystem is None
                   or coordinator.bridge.graphics is None
                   or coordinator.bridge.information is None
                   or coordinator.bridge.memory is None
                   or coordinator.bridge.network is None
                   or coordinator.bridge.os is None
                   or coordinator.bridge.processes is None
                   or coordinator.bridge.system is None):
                _LOGGER.debug(
                    "Waiting for initial data from %s (%s)",
                    entry.title,
                    entry.data[CONF_HOST],
                )
                await asyncio.sleep(1)
    except asyncio.TimeoutError as exception:
        raise ConfigEntryNotReady(
            f"Timed out waiting for {entry.title} ({entry.data[CONF_HOST]})."
        ) from exception

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

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    if hass.services.has_service(DOMAIN, SERVICE_SEND_COMMAND):
        return True

    def valid_device(device: str):
        """Check device is valid."""
        device_registry = dr.async_get(hass)
        device_entry = device_registry.async_get(device)
        if device_entry is not None:
            try:
                return next(
                    entry.entry_id
                    for entry in hass.config_entries.async_entries(DOMAIN)
                    if entry.entry_id in device_entry.config_entries)
            except StopIteration as exception:
                raise vol.Invalid from exception
        raise vol.Invalid(f"Device {device} does not exist")

    async def handle_send_command(call):
        """Handle the send_command service call."""
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            call.data[CONF_BRIDGE]]
        bridge: Bridge = coordinator.bridge

        command = call.data[CONF_COMMAND]
        arguments = shlex.split(call.data[CONF_ARGUMENTS])

        _LOGGER.debug(
            "Command payload: %s",
            {
                CONF_COMMAND: command,
                CONF_ARGUMENTS: arguments,
                CONF_WAIT: False
            },
        )
        try:
            response: CommandResponse = await bridge.async_send_command({
                CONF_COMMAND:
                command,
                CONF_ARGUMENTS:
                arguments,
                CONF_WAIT:
                False
            })
            if not response.success:
                raise HomeAssistantError(
                    f"Error sending command. Response message was: {response.message}"
                )
        except (BridgeAuthenticationException,
                *BRIDGE_CONNECTION_ERRORS) as exception:
            raise HomeAssistantError("Error sending command") from exception
        _LOGGER.debug("Sent command. Response message was: %s",
                      response.message)

    async def handle_open(call):
        """Handle the open service call."""
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            call.data[CONF_BRIDGE]]
        bridge: Bridge = coordinator.bridge

        path = call.data[CONF_PATH]

        _LOGGER.debug("Open payload: %s", {CONF_PATH: path})
        try:
            await bridge.async_open({CONF_PATH: path})
        except (BridgeAuthenticationException,
                *BRIDGE_CONNECTION_ERRORS) as exception:
            raise HomeAssistantError("Error sending") from exception
        _LOGGER.debug("Sent open request")

    async def handle_send_keypress(call):
        """Handle the send_keypress service call."""
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            call.data[CONF_BRIDGE]]
        bridge: Bridge = coordinator.data

        keyboard_payload: KeyboardPayload = {
            CONF_KEY: call.data[CONF_KEY],
            CONF_MODIFIERS: shlex.split(call.data.get(CONF_MODIFIERS, "")),
        }

        _LOGGER.debug("Keypress payload: %s", keyboard_payload)
        try:
            await bridge.async_send_keypress(keyboard_payload)
        except (BridgeAuthenticationException,
                *BRIDGE_CONNECTION_ERRORS) as exception:
            raise HomeAssistantError("Error sending") from exception
        _LOGGER.debug("Sent keypress request")

    async def handle_send_text(call):
        """Handle the send_keypress service call."""
        coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
            call.data[CONF_BRIDGE]]
        bridge: Bridge = coordinator.data

        keyboard_payload: KeyboardPayload = {CONF_TEXT: call.data[CONF_TEXT]}

        _LOGGER.debug("Text payload: %s", keyboard_payload)
        try:
            await bridge.async_send_keypress(keyboard_payload)
        except (BridgeAuthenticationException,
                *BRIDGE_CONNECTION_ERRORS) as exception:
            raise HomeAssistantError("Error sending") from exception
        _LOGGER.debug("Sent text request")

    hass.services.async_register(
        DOMAIN,
        SERVICE_SEND_COMMAND,
        handle_send_command,
        schema=vol.Schema(
            {
                vol.Required(CONF_BRIDGE): valid_device,
                vol.Required(CONF_COMMAND): cv.string,
                vol.Optional(CONF_ARGUMENTS, ""): cv.string,
            }, ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_OPEN,
        handle_open,
        schema=vol.Schema(
            {
                vol.Required(CONF_BRIDGE): valid_device,
                vol.Required(CONF_PATH): cv.string,
            }, ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_SEND_KEYPRESS,
        handle_send_keypress,
        schema=vol.Schema(
            {
                vol.Required(CONF_BRIDGE): valid_device,
                vol.Required(CONF_KEY): cv.string,
                vol.Optional(CONF_MODIFIERS): cv.string,
            }, ),
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_SEND_TEXT,
        handle_send_text,
        schema=vol.Schema(
            {
                vol.Required(CONF_BRIDGE): valid_device,
                vol.Required(CONF_TEXT): cv.string,
            }, ),
    )

    # Reload entry when its updated.
    entry.async_on_unload(entry.add_update_listener(async_reload_entry))

    return True
Example #5
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up System Bridge from a config entry."""

    client = Bridge(
        BridgeClient(aiohttp_client.async_get_clientsession(hass)),
        f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}",
        entry.data[CONF_API_KEY],
    )

    async def async_update_data() -> Bridge:
        """Fetch data from Bridge."""
        try:
            async with async_timeout.timeout(60):
                await asyncio.gather(*[
                    client.async_get_battery(),
                    client.async_get_cpu(),
                    client.async_get_filesystem(),
                    client.async_get_memory(),
                    client.async_get_network(),
                    client.async_get_os(),
                    client.async_get_processes(),
                    client.async_get_system(),
                ])
            return client
        except BridgeAuthenticationException as exception:
            raise ConfigEntryAuthFailed from exception
        except BRIDGE_CONNECTION_ERRORS as exception:
            raise UpdateFailed(
                "Could not connect to System Bridge.") from exception

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        # Name of the data. For logging purposes.
        name=f"{DOMAIN}_coordinator",
        update_method=async_update_data,
        # Polling interval. Will only be polled if there are subscribers.
        update_interval=timedelta(seconds=60),
    )

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

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

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    if hass.services.has_service(DOMAIN, SERVICE_SEND_COMMAND):
        return True

    async def handle_send_command(call):
        """Handle the send_command service call."""
        device_registry = dr.async_get(hass)
        device_id = call.data[CONF_BRIDGE]
        device_entry = device_registry.async_get(device_id)
        if device_entry is None:
            _LOGGER.warning("Missing device: %s", device_id)
            return

        command = call.data[CONF_COMMAND]
        arguments = shlex.split(call.data.get(CONF_ARGUMENTS, ""))

        entry_id = next(entry.entry_id
                        for entry in hass.config_entries.async_entries(DOMAIN)
                        if entry.entry_id in device_entry.config_entries)
        coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry_id]
        bridge: Bridge = coordinator.data

        _LOGGER.debug(
            "Command payload: %s",
            {
                CONF_COMMAND: command,
                CONF_ARGUMENTS: arguments,
                CONF_WAIT: False
            },
        )
        try:
            response: CommandResponse = await bridge.async_send_command({
                CONF_COMMAND:
                command,
                CONF_ARGUMENTS:
                arguments,
                CONF_WAIT:
                False
            })
            if response.success:
                _LOGGER.debug("Sent command. Response message was: %s",
                              response.message)
            else:
                _LOGGER.warning(
                    "Error sending command. Response message was: %s",
                    response.message)
        except (BridgeAuthenticationException,
                *BRIDGE_CONNECTION_ERRORS) as exception:
            _LOGGER.warning("Error sending command. Error was: %s", exception)

    async def handle_open(call):
        """Handle the open service call."""
        device_registry = dr.async_get(hass)
        device_id = call.data[CONF_BRIDGE]
        device_entry = device_registry.async_get(device_id)
        if device_entry is None:
            _LOGGER.warning("Missing device: %s", device_id)
            return

        path = call.data[CONF_PATH]

        entry_id = next(entry.entry_id
                        for entry in hass.config_entries.async_entries(DOMAIN)
                        if entry.entry_id in device_entry.config_entries)
        coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry_id]
        bridge: Bridge = coordinator.data

        _LOGGER.debug("Open payload: %s", {CONF_PATH: path})
        try:
            await bridge.async_open({CONF_PATH: path})
            _LOGGER.debug("Sent open request")
        except (BridgeAuthenticationException,
                *BRIDGE_CONNECTION_ERRORS) as exception:
            _LOGGER.warning("Error sending. Error was: %s", exception)

    hass.services.async_register(
        DOMAIN,
        SERVICE_SEND_COMMAND,
        handle_send_command,
        schema=SERVICE_SEND_COMMAND_SCHEMA,
    )

    hass.services.async_register(
        DOMAIN,
        SERVICE_OPEN,
        handle_open,
        schema=SERVICE_OPEN_SCHEMA,
    )

    return True