mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add System Bridge keyboard services (#53893)
* Add keyboard services * Extract to voluptuous validator * Cleanup * Lint * Catch StopIteration * Match validator Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Raise from Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
8ce3f18295
commit
27b2aa04c9
@ -10,6 +10,7 @@ from systembridge import Bridge
|
||||
from systembridge.client import BridgeClient
|
||||
from systembridge.exceptions import BridgeAuthenticationException
|
||||
from systembridge.objects.command.response import CommandResponse
|
||||
from systembridge.objects.keyboard.payload import KeyboardPayload
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@ -21,7 +22,11 @@ from homeassistant.const import (
|
||||
CONF_PORT,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.exceptions import (
|
||||
ConfigEntryAuthFailed,
|
||||
ConfigEntryNotReady,
|
||||
HomeAssistantError,
|
||||
)
|
||||
from homeassistant.helpers import (
|
||||
aiohttp_client,
|
||||
config_validation as cv,
|
||||
@ -39,20 +44,15 @@ PLATFORMS = ["binary_sensor", "sensor"]
|
||||
|
||||
CONF_ARGUMENTS = "arguments"
|
||||
CONF_BRIDGE = "bridge"
|
||||
CONF_KEY = "key"
|
||||
CONF_MODIFIERS = "modifiers"
|
||||
CONF_TEXT = "text"
|
||||
CONF_WAIT = "wait"
|
||||
|
||||
SERVICE_SEND_COMMAND = "send_command"
|
||||
SERVICE_SEND_COMMAND_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_BRIDGE): cv.string,
|
||||
vol.Required(CONF_COMMAND): cv.string,
|
||||
vol.Optional(CONF_ARGUMENTS, []): cv.string,
|
||||
}
|
||||
)
|
||||
SERVICE_OPEN = "open"
|
||||
SERVICE_OPEN_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_BRIDGE): cv.string, vol.Required(CONF_PATH): cv.string}
|
||||
)
|
||||
SERVICE_SEND_KEYPRESS = "send_keypress"
|
||||
SERVICE_SEND_TEXT = "send_text"
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
@ -113,26 +113,31 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
if hass.services.has_service(DOMAIN, SERVICE_SEND_COMMAND):
|
||||
return True
|
||||
|
||||
async def handle_send_command(call):
|
||||
"""Handle the send_command service call."""
|
||||
def valid_device(device: str):
|
||||
"""Check device is valid."""
|
||||
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(
|
||||
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
|
||||
)
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry_id]
|
||||
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},
|
||||
@ -141,55 +146,113 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
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
|
||||
if not response.success:
|
||||
raise HomeAssistantError(
|
||||
f"Error sending command. Response message was: {response.message}"
|
||||
)
|
||||
except (BridgeAuthenticationException, *BRIDGE_CONNECTION_ERRORS) as exception:
|
||||
_LOGGER.warning("Error sending command. Error was: %s", 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."""
|
||||
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
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
call.data[CONF_BRIDGE]
|
||||
]
|
||||
bridge: Bridge = coordinator.bridge
|
||||
|
||||
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)
|
||||
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=SERVICE_SEND_COMMAND_SCHEMA,
|
||||
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=SERVICE_OPEN_SCHEMA,
|
||||
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.
|
||||
|
@ -42,3 +42,49 @@ open:
|
||||
example: "https://www.home-assistant.io"
|
||||
selector:
|
||||
text:
|
||||
send_keypress:
|
||||
name: Send Keyboard Keypress
|
||||
description: Sends a keyboard keypress.
|
||||
fields:
|
||||
bridge:
|
||||
name: Bridge
|
||||
description: The server to send the command to.
|
||||
required: true
|
||||
selector:
|
||||
device:
|
||||
integration: system_bridge
|
||||
key:
|
||||
name: Key
|
||||
description: "Key to press. List available here: http://robotjs.io/docs/syntax#keys"
|
||||
required: true
|
||||
example: "audio_play"
|
||||
selector:
|
||||
text:
|
||||
modifiers:
|
||||
name: Modifiers
|
||||
description: "List of modifier(s). Accepts alt, command/win, control, and shift."
|
||||
required: false
|
||||
default: ""
|
||||
example:
|
||||
- "control"
|
||||
- "shift"
|
||||
selector:
|
||||
text:
|
||||
send_text:
|
||||
name: Send Keyboard Text
|
||||
description: Sends text for the server to type.
|
||||
fields:
|
||||
bridge:
|
||||
name: Bridge
|
||||
description: The server to send the command to.
|
||||
required: true
|
||||
selector:
|
||||
device:
|
||||
integration: system_bridge
|
||||
text:
|
||||
name: Text
|
||||
description: "Text to type."
|
||||
required: true
|
||||
example: "Hello world"
|
||||
selector:
|
||||
text:
|
||||
|
Loading…
x
Reference in New Issue
Block a user