Fix live network and phone discovery

This commit is contained in:
Gabriel Brown
2026-08-18 12:34:02 -04:00
parent 60a321e6f4
commit 08b16fa03f
3 changed files with 235 additions and 7 deletions
+100 -5
View File
@@ -25,6 +25,9 @@ PLUGIN_ACTIONS = {
"kdeconnect_share": "share",
}
DEVICE_OBJECT_PREFIX = "/modules/kdeconnect/devices"
DEVICE_OBJECT_LINE = re.compile(
rf"(?P<path>{re.escape(DEVICE_OBJECT_PREFIX)}/(?P<id>[A-Fa-f0-9]{{32,64}}))$"
)
Runner = Callable[..., subprocess.CompletedProcess[str]]
@@ -107,6 +110,13 @@ def parse_string_property(output: str) -> str:
return parts[1] if len(parts) == 2 and parts[0] == "s" else ""
def parse_bool_property(output: str) -> bool | None:
parts = output.split()
if len(parts) != 2 or parts[0] != "b" or parts[1] not in {"true", "false"}:
return None
return parts[1] == "true"
def run_command(
command: list[str],
*,
@@ -179,6 +189,82 @@ def reported_type(device_id: str, runner: Runner = subprocess.run) -> str:
return parse_string_property(result.stdout) if result.returncode == 0 else ""
def device_property(
device_id: str,
member: str,
runner: Runner = subprocess.run,
) -> subprocess.CompletedProcess[str]:
return run_command(
[
"busctl",
"--user",
"get-property",
"org.kde.kdeconnect",
device_object(device_id),
"org.kde.kdeconnect.device",
member,
],
runner=runner,
)
def dbus_device_ids(runner: Runner = subprocess.run) -> list[str]:
try:
result = run_command(
["busctl", "--user", "tree", "org.kde.kdeconnect"],
runner=runner,
)
except (FileNotFoundError, subprocess.TimeoutExpired):
return []
if result.returncode != 0:
return []
return [
match.group("id")
for line in result.stdout.splitlines()
if (match := DEVICE_OBJECT_LINE.search(line.strip())) is not None
]
def dbus_devices(runner: Runner = subprocess.run) -> list[dict[str, object]]:
devices: list[dict[str, object]] = []
for device_id in dbus_device_ids(runner):
try:
name_result = device_property(device_id, "name", runner)
type_result = device_property(device_id, "type", runner)
paired_result = device_property(device_id, "isPaired", runner)
reachable_result = device_property(device_id, "isReachable", runner)
except (FileNotFoundError, subprocess.TimeoutExpired):
continue
name = parse_string_property(name_result.stdout) if name_result.returncode == 0 else ""
device_type = parse_string_property(type_result.stdout) if type_result.returncode == 0 else ""
paired = parse_bool_property(paired_result.stdout) if paired_result.returncode == 0 else None
reachable = parse_bool_property(reachable_result.stdout) if reachable_result.returncode == 0 else None
if not name or paired is not True or reachable is None:
continue
try:
plugins = device_plugins(device_id, runner)
except (FileNotFoundError, subprocess.TimeoutExpired):
plugins = []
actions = sorted(
{
action
for plugin, action in PLUGIN_ACTIONS.items()
if plugin in plugins
}
)
devices.append(
{
"id": device_id,
"name": name,
"type": device_type or inferred_type(name),
"paired": paired,
"reachable": reachable,
"actions": actions,
}
)
return devices
def collect_status(runner: Runner = subprocess.run) -> dict[str, object]:
try:
listing = run_command(
@@ -200,15 +286,24 @@ def collect_status(runner: Runner = subprocess.run) -> dict[str, object]:
continue
device_id = match.group("id")
try:
device = normalize_device_line(
line,
device_plugins(device_id, runner),
reported_type(device_id, runner),
)
plugins = device_plugins(device_id, runner)
device_type = reported_type(device_id, runner)
except (FileNotFoundError, subprocess.TimeoutExpired):
plugins = []
device_type = ""
try:
device = normalize_device_line(line, plugins, device_type)
except ValueError:
continue
devices.append(device)
known_ids = {str(device["id"]) for device in devices}
devices.extend(
device
for device in dbus_devices(runner)
if str(device["id"]) not in known_ids
)
devices.sort(
key=lambda device: (
not bool(device["reachable"]),