Add processes services to System Bridge (#103564)

* Add processes services to System Bridge

* Update import and fixes from model updates

* Change log level from info to debug for process retrieval

* Add exception handling for process not found

* Consistency

* Change HomeAssistantError to ServiceValidationError

* Update homeassistant/components/system_bridge/__init__.py

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
pull/113236/head
Aidan Timson 2024-03-13 13:56:20 +00:00 committed by GitHub
parent 0ccd813a99
commit 081a38a21c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 139 additions and 1 deletions

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import asyncio
from dataclasses import asdict
import logging
from typing import Any
from systembridgeconnector.exceptions import (
AuthenticationException,
@ -14,6 +15,7 @@ from systembridgeconnector.exceptions import (
from systembridgeconnector.version import Version
from systembridgemodels.keyboard_key import KeyboardKey
from systembridgemodels.keyboard_text import KeyboardText
from systembridgemodels.modules.processes import Process
from systembridgemodels.open_path import OpenPath
from systembridgemodels.open_url import OpenUrl
import voluptuous as vol
@ -24,6 +26,7 @@ from homeassistant.const import (
CONF_COMMAND,
CONF_ENTITY_ID,
CONF_HOST,
CONF_ID,
CONF_NAME,
CONF_PATH,
CONF_PORT,
@ -37,7 +40,11 @@ from homeassistant.core import (
ServiceResponse,
SupportsResponse,
)
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
ConfigEntryNotReady,
ServiceValidationError,
)
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
@ -64,6 +71,8 @@ CONF_BRIDGE = "bridge"
CONF_KEY = "key"
CONF_TEXT = "text"
SERVICE_GET_PROCESS_BY_ID = "get_process_by_id"
SERVICE_GET_PROCESSES_BY_NAME = "get_processes_by_name"
SERVICE_OPEN_PATH = "open_path"
SERVICE_POWER_COMMAND = "power_command"
SERVICE_OPEN_URL = "open_url"
@ -202,6 +211,52 @@ async def async_setup_entry(
raise vol.Invalid(f"Could not find device {device}") from exception
raise vol.Invalid(f"Device {device} does not exist")
async def handle_get_process_by_id(service_call: ServiceCall) -> ServiceResponse:
"""Handle the get process by id service call."""
_LOGGER.debug("Get process by id: %s", service_call.data)
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
service_call.data[CONF_BRIDGE]
]
processes: list[Process] = coordinator.data.processes
# Find process.id from list, raise ServiceValidationError if not found
try:
return asdict(
next(
process
for process in processes
if process.id == service_call.data[CONF_ID]
)
)
except StopIteration as exception:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="process_not_found",
translation_placeholders={"id": service_call.data[CONF_ID]},
) from exception
async def handle_get_processes_by_name(
service_call: ServiceCall,
) -> ServiceResponse:
"""Handle the get process by name service call."""
_LOGGER.debug("Get process by name: %s", service_call.data)
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
service_call.data[CONF_BRIDGE]
]
processes: list[Process] = coordinator.data.processes
# Find processes from list
items: list[dict[str, Any]] = [
asdict(process)
for process in processes
if process.name is not None
and service_call.data[CONF_NAME].lower() in process.name.lower()
]
return {
"count": len(items),
"processes": list(items),
}
async def handle_open_path(service_call: ServiceCall) -> ServiceResponse:
"""Handle the open path service call."""
_LOGGER.debug("Open path: %s", service_call.data)
@ -256,6 +311,32 @@ async def async_setup_entry(
)
return asdict(response)
hass.services.async_register(
DOMAIN,
SERVICE_GET_PROCESS_BY_ID,
handle_get_process_by_id,
schema=vol.Schema(
{
vol.Required(CONF_BRIDGE): valid_device,
vol.Required(CONF_ID): cv.positive_int,
},
),
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
SERVICE_GET_PROCESSES_BY_NAME,
handle_get_processes_by_name,
schema=vol.Schema(
{
vol.Required(CONF_BRIDGE): valid_device,
vol.Required(CONF_NAME): cv.string,
},
),
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
SERVICE_OPEN_PATH,

View File

@ -1,3 +1,27 @@
get_process_by_id:
fields:
bridge:
required: true
selector:
device:
integration: system_bridge
id:
required: true
example: 1234
selector:
number:
get_processes_by_name:
fields:
bridge:
required: true
selector:
device:
integration: system_bridge
name:
required: true
example: "chrome.exe"
selector:
text:
open_path:
fields:
bridge:

View File

@ -86,6 +86,11 @@
}
}
},
"exceptions": {
"process_not_found": {
"message": "Could not find process with id {id}."
}
},
"issues": {
"unsupported_version": {
"title": "System Bridge Upgrade Required",
@ -107,6 +112,34 @@
}
}
},
"get_process_by_id": {
"name": "Get process by ID",
"description": "Gets a process by the ID.",
"fields": {
"bridge": {
"name": "[%key:component::system_bridge::services::open_path::fields::bridge::name%]",
"description": "[%key:component::system_bridge::services::open_path::fields::bridge::description%]"
},
"id": {
"name": "ID",
"description": "ID of the process to get."
}
}
},
"get_processes_by_name": {
"name": "Get processes by name",
"description": "Gets a list of processes by the name.",
"fields": {
"bridge": {
"name": "[%key:component::system_bridge::services::open_path::fields::bridge::name%]",
"description": "[%key:component::system_bridge::services::open_path::fields::bridge::description%]"
},
"name": {
"name": "Name",
"description": "Name of the process to get."
}
}
},
"open_url": {
"name": "Open URL",
"description": "Opens a URL on the server using the default application.",