#!/usr/bin/env bash set -euo pipefail fail() { printf 'Control Center services contract: %s\n' "$1" >&2 exit 1 } cleanup() { qs ipc call kdeconnect reset >/dev/null 2>&1 || true qs ipc call home-assistant reset >/dev/null 2>&1 || true qs ipc call status-events reset >/dev/null 2>&1 || true qs ipc call quicksettings close >/dev/null 2>&1 || true } trap cleanup EXIT qs ipc show | rg -q '^target kdeconnect$' \ || fail 'KDE Connect IPC target is missing' qs ipc show | rg -q '^target home-assistant$' \ || fail 'Home Assistant IPC target is missing' qs ipc call kdeconnect fixture reachable >/dev/null jq -e ' .fixture == true and .available == true and .reachable == true and .actionCount == 4 and .transferActive == false and .ongoingCount == 0 ' <<<"$(qs ipc call kdeconnect status)" >/dev/null \ || fail 'reachable phone fixture is malformed' qs ipc call kdeconnect fixture offline >/dev/null jq -e ' .fixture == true and .available == true and .reachable == false and .pairedCount == 1 and .ongoingCount == 0 ' <<<"$(qs ipc call kdeconnect status)" >/dev/null \ || fail 'offline phone fixture is malformed' qs ipc call kdeconnect fixture transfer >/dev/null jq -e ' .fixture == true and .transferActive == true and .transferFileName == "Fixture document.pdf" and .ongoingCount == 1 ' <<<"$(qs ipc call kdeconnect status)" >/dev/null \ || fail 'phone transfer did not enter Ongoing' qs ipc call kdeconnect cancel >/dev/null jq -e '.transferActive == false and .ongoingCount == 0' \ <<<"$(qs ipc call kdeconnect status)" >/dev/null \ || fail 'phone transfer did not leave Ongoing' qs ipc call home-assistant fixture ready >/dev/null home_ready="$(qs ipc call home-assistant status)" jq -e ' .fixture == true and .phase == "ready" and .discoveredCount == 7 and .configuredCount == 7 and .visibleCount == 4 and .selectedIds[0:4] == [ "light.fixture_all", "light.fixture_kitchen", "light.fixture_living", "light.fixture_bedroom" ] and (.entities[0:4] | map(.name)) == [ "Whole home", "Kitchen island", "Living room", "Bedroom" ] and .stale == false and .lastError == "" ' <<<"$home_ready" >/dev/null \ || fail 'ready Home fixture is malformed' qs ipc call home-assistant brightness light.fixture_living 64 >/dev/null jq -e ' .entities[] | select(.id == "light.fixture_living") | .active == true and .state == "on" and .brightnessPct == 64 ' <<<"$(qs ipc call home-assistant status)" >/dev/null \ || fail 'Home brightness fixture did not update local catalog state' qs ipc call home-assistant toggle light.fixture_living >/dev/null jq -e ' .entities[] | select(.id == "light.fixture_living") | .active == false and .state == "off" ' <<<"$(qs ipc call home-assistant status)" >/dev/null \ || fail 'Home toggle fixture did not update local catalog state' qs ipc call home-assistant fixture missing-selected >/dev/null jq -e ' .fixture == true and .phase == "ready" and .discoveredCount == 7 and .configuredCount == 8 and (.entities[] | select(.id == "light.fixture_missing") | .sourceName == "fixture missing" and .name == "Porch" and .state == "unavailable" and .available == false and .active == false and .dimmable == false and .brightnessPct == 0) ' <<<"$(qs ipc call home-assistant status)" >/dev/null \ || fail 'missing Home selection was not retained as unavailable' qs ipc call home-assistant fixture action-error >/dev/null jq -e ' .fixture == true and .phase == "ready" and .stale == false and .entityErrors["light.fixture_kitchen"] == "request-failed" and (.entityErrors["light.fixture_hall"] // "") == "" and .lastError == "" ' <<<"$(qs ipc call home-assistant status)" >/dev/null \ || fail 'Home action error was not isolated to one entity' qs ipc call home-assistant fixture stale >/dev/null jq -e ' .fixture == true and .phase == "degraded" and .discoveredCount == 7 and .configuredCount == 7 and .visibleCount == 4 and .stale == true and .lastError == "unreachable" ' <<<"$(qs ipc call home-assistant status)" >/dev/null \ || fail 'stale Home fixture did not retain entities' qs ipc call home-assistant fixture unavailable >/dev/null jq -e ' .fixture == true and .phase == "unavailable" and .discoveredCount == 0 and .configuredCount == 0 and .visibleCount == 0 and .lastError == "not-configured" ' <<<"$(qs ipc call home-assistant status)" >/dev/null \ || fail 'unavailable Home fixture is malformed' trap - EXIT cleanup printf 'Control Center services contract: PASS\n'