Add the Panama KDE Connect bridge
This commit is contained in:
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
fail() {
|
||||
printf 'KDE Connect helper contract: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
helper="$project_root/config/dot/quickshell/scripts/panama-kdeconnect"
|
||||
|
||||
[[ -x "$helper" ]] || fail 'helper is missing or not executable'
|
||||
|
||||
status="$($helper status)"
|
||||
jq -e '
|
||||
.available == true and
|
||||
(.devices | type == "array" and length >= 1) and
|
||||
([.devices[] | (keys | sort) == (["actions", "id", "name", "paired", "reachable", "type"] | sort)] | all) and
|
||||
([.devices[] | (.id | test("^[A-Fa-f0-9]{32,64}$"))] | all) and
|
||||
([.devices[].actions[] | . == "clipboard" or . == "ping" or . == "ring" or . == "share"] | all) and
|
||||
([.devices[] | select(.paired)] | length >= 1)
|
||||
' <<<"$status" >/dev/null || fail 'live status shape is invalid'
|
||||
|
||||
paired_count="$(jq '[.devices[] | select(.paired)] | length' <<<"$status")"
|
||||
reachable_count="$(jq '[.devices[] | select(.reachable)] | length' <<<"$status")"
|
||||
printf 'KDE Connect helper contract: PASS (%s paired, %s reachable; identities redacted)\n' \
|
||||
"$paired_count" \
|
||||
"$reachable_count"
|
||||
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.machinery
|
||||
import importlib.util
|
||||
import json
|
||||
import pathlib
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[2]
|
||||
HELPER = ROOT / "config/dot/quickshell/scripts/panama-kdeconnect"
|
||||
|
||||
loader = importlib.machinery.SourceFileLoader("panama_kdeconnect", str(HELPER))
|
||||
spec = importlib.util.spec_from_loader(loader.name, loader)
|
||||
bridge = importlib.util.module_from_spec(spec)
|
||||
loader.exec_module(bridge)
|
||||
|
||||
|
||||
class KdeConnectBridgeTest(unittest.TestCase):
|
||||
def test_status_omits_network_and_exposes_supported_ios_actions(self) -> None:
|
||||
line = (
|
||||
"- Gib's iPhone: 90B54E84548E4F109268EC2D0E7D6930 "
|
||||
"on 192.168.1.7 via LAN (paired and reachable)"
|
||||
)
|
||||
plugins = [
|
||||
"kdeconnect_clipboard",
|
||||
"kdeconnect_findmyphone",
|
||||
"kdeconnect_ping",
|
||||
"kdeconnect_share",
|
||||
"kdeconnect_shareinputdevices",
|
||||
]
|
||||
|
||||
device = bridge.normalize_device_line(line, plugins)
|
||||
|
||||
self.assertEqual(device["name"], "Gib's iPhone")
|
||||
self.assertEqual(device["type"], "phone")
|
||||
self.assertTrue(device["paired"])
|
||||
self.assertTrue(device["reachable"])
|
||||
self.assertEqual(device["actions"], ["clipboard", "ping", "ring", "share"])
|
||||
self.assertNotIn("192.168.1.7", json.dumps(device))
|
||||
self.assertNotIn("LAN", json.dumps(device))
|
||||
|
||||
def test_status_parses_an_offline_paired_device(self) -> None:
|
||||
line = "- Pocket: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (paired)"
|
||||
|
||||
device = bridge.normalize_device_line(line, ["kdeconnect_share"])
|
||||
|
||||
self.assertTrue(device["paired"])
|
||||
self.assertFalse(device["reachable"])
|
||||
self.assertEqual(device["actions"], ["share"])
|
||||
|
||||
def test_invalid_device_id_is_rejected(self) -> None:
|
||||
with self.assertRaisesRegex(ValueError, "invalid-device"):
|
||||
bridge.validate_device_id("phone; shutdown")
|
||||
|
||||
def test_send_file_requires_a_regular_file(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
with self.assertRaisesRegex(ValueError, "invalid-file"):
|
||||
bridge.validate_file(pathlib.Path(directory))
|
||||
|
||||
def test_send_file_resolves_a_regular_file(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
file_path = pathlib.Path(directory) / "hello world.txt"
|
||||
file_path.write_text("fixture", encoding="utf-8")
|
||||
|
||||
self.assertEqual(bridge.validate_file(file_path), file_path.resolve())
|
||||
|
||||
def test_action_commands_use_separate_arguments(self) -> None:
|
||||
device_id = "90B54E84548E4F109268EC2D0E7D6930"
|
||||
self.assertEqual(
|
||||
bridge.action_command("ring", device_id),
|
||||
["kdeconnect-cli", "-d", device_id, "--ring"],
|
||||
)
|
||||
self.assertEqual(
|
||||
bridge.action_command("clipboard", device_id),
|
||||
["kdeconnect-cli", "-d", device_id, "--send-clipboard"],
|
||||
)
|
||||
|
||||
def test_unknown_action_is_rejected(self) -> None:
|
||||
with self.assertRaisesRegex(ValueError, "unsupported-action"):
|
||||
bridge.action_command(
|
||||
"unlock",
|
||||
"90B54E84548E4F109268EC2D0E7D6930",
|
||||
)
|
||||
|
||||
def test_busctl_plugin_output_is_normalized(self) -> None:
|
||||
output = (
|
||||
'as 5 "kdeconnect_ping" "kdeconnect_share" '
|
||||
'"kdeconnect_clipboard" "kdeconnect_findmyphone" "unrelated"\n'
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
bridge.parse_loaded_plugins(output),
|
||||
[
|
||||
"kdeconnect_ping",
|
||||
"kdeconnect_share",
|
||||
"kdeconnect_clipboard",
|
||||
"kdeconnect_findmyphone",
|
||||
"unrelated",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user