Handle Wi-Fi and Bluetooth in Settings
Network & Devices was 92 lines and two buttons that opened GNOME. It now scans, joins, and pairs directly through Quickshell.Networking and Quickshell.Bluetooth -- NetworkManager and BlueZ over DBus, no shelling out to nmcli or bluetoothctl. That was the founding requirement for this desktop: never having to drop to a terminal to join a network. Scanning follows the page being visible. Wi-Fi scanning and especially Bluetooth discovery hold the radio, and running either for a list nobody is looking at spends airtime on nothing. Joining a secured network gets a real password field, not the clipboard popover's search box with different placeholder text: a Wi-Fi key typed into a field that echoes it is readable by anyone behind you, and a search glyph in front of a password prompt is simply wrong. Two bugs found by looking at the rendered page, both silent: The device lookups used enum names that do not exist -- NetworkDeviceType.Wifi rather than DeviceType.Wifi -- so both returned null and the page reported "No Wi-Fi adapter" on a machine whose Wi-Fi was connected. Nothing was logged; QML resolves an unknown enum member to undefined and compares happily. signalStrength is 0.0-1.0, not a percentage, so thresholds written for 0-100 put every network including the connected one in the bottom bucket. The labels now use the same buckets as the icons in quicksettings/WifiList.qml so the two cannot disagree. The contract compares what the service resolves against what nmcli reports, rather than only checking that nothing crashed. Also makes the Home Assistant bridge hermetic: resolve_config read the user's private env file even when a caller supplied an explicit environment, so adding a real PANAMA_HOME_ASSISTANT_ENTITIES to that file silently overrode a fixture asserting the legacy fallback. An explicit environment is now the whole environment; production still reads the file. Its live contract skips when no token is configured -- an absent credential is not a defect, and a suite expected to be red stops being read -- while a configured-but-broken bridge still fails. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
This commit is contained in:
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Network & Devices reads real NetworkManager and BlueZ state.
|
||||
#
|
||||
# Both of the bugs this contract exists to prevent were silent. Neither logged
|
||||
# anything; both produced a page that looked fine and told the user something
|
||||
# false:
|
||||
#
|
||||
# * the device lookups used enum names that do not exist
|
||||
# (NetworkDeviceType.Wifi rather than DeviceType.Wifi), so they returned
|
||||
# null and the page reported "No Wi-Fi adapter" on a machine whose Wi-Fi was
|
||||
# connected;
|
||||
# * signalStrength is 0.0-1.0, not a percentage, so thresholds written for
|
||||
# 0-100 put every network including the connected one in the bottom bucket.
|
||||
#
|
||||
# So this compares what the service resolves against what NetworkManager itself
|
||||
# reports, rather than merely checking the service does not crash.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
harness="$repo_dir/config/dot/quickshell/connectivity-harness.qml"
|
||||
|
||||
fail() {
|
||||
printf 'connectivity contract: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
command -v nmcli >/dev/null || fail 'nmcli is needed to check the service against reality'
|
||||
|
||||
run() { qs -p "$harness" "$@"; }
|
||||
harness_pid=""
|
||||
|
||||
cleanup() {
|
||||
run ipc call connectivity-test setActive false >/dev/null 2>&1 || true
|
||||
# By PID: never `pkill -f connectivity-harness`, which also matches the
|
||||
# shell running this script.
|
||||
[[ -n "$harness_pid" ]] && kill "$harness_pid" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
qs -p "$harness" --daemonize >/dev/null
|
||||
for _ in $(seq 1 40); do
|
||||
run ipc show 2>/dev/null | rg -q '^target connectivity-test$' && break
|
||||
sleep 0.1
|
||||
done
|
||||
run ipc show 2>/dev/null | rg -q '^target connectivity-test$' || fail 'test IPC target did not start'
|
||||
harness_pid="$(run list | awk '/Process ID:/ { print $3; exit }')"
|
||||
|
||||
# Scanning only runs while the page says it is visible.
|
||||
run ipc call connectivity-test setActive true >/dev/null
|
||||
sleep 3
|
||||
|
||||
state="$(run ipc call connectivity-test status)"
|
||||
|
||||
# ── Devices the service finds must match the ones NetworkManager reports ─────
|
||||
nm_wifi="$(nmcli -t -f DEVICE,TYPE device | awk -F: '$2 == "wifi" { print $1; exit }')"
|
||||
nm_wired="$(nmcli -t -f DEVICE,TYPE,STATE device | awk -F: '$2 == "ethernet" && $3 == "connected" { print $1; exit }')"
|
||||
|
||||
if [[ -n "$nm_wifi" ]]; then
|
||||
[[ "$(jq -r .wifiDevice <<<"$state")" == "$nm_wifi" ]] \
|
||||
|| fail "NetworkManager reports Wi-Fi device '$nm_wifi' but the service found '$(jq -r .wifiDevice <<<"$state")'"
|
||||
fi
|
||||
if [[ -n "$nm_wired" ]]; then
|
||||
[[ "$(jq -r .wiredConnected <<<"$state")" == "true" ]] \
|
||||
|| fail "NetworkManager reports '$nm_wired' connected but the service says it is not"
|
||||
fi
|
||||
|
||||
# ── Signal strength is a ratio, and the labels must reflect that ─────────────
|
||||
while IFS='|' read -r value expect; do
|
||||
got="$(run ipc call connectivity-test labelFor "$value")"
|
||||
[[ "$got" == "$expect" ]] || fail "signal $value labelled '$got', expected '$expect'"
|
||||
done <<'CASES'
|
||||
1.0|Excellent
|
||||
0.85|Excellent
|
||||
0.6|Good
|
||||
0.4|Fair
|
||||
0.1|Weak
|
||||
0.0|No signal
|
||||
CASES
|
||||
|
||||
# If a network is connected, it must not be described as the weakest possible
|
||||
# thing -- that was the visible symptom of reading the ratio as a percentage.
|
||||
active_ssid="$(jq -r .activeSsid <<<"$state")"
|
||||
if [[ -n "$active_ssid" ]]; then
|
||||
strength="$(jq -r .activeStrength <<<"$state")"
|
||||
awk -v s="$strength" 'BEGIN { exit !(s >= 0 && s <= 1) }' \
|
||||
|| fail "signalStrength $strength is outside 0.0-1.0; the label buckets assume a ratio"
|
||||
fi
|
||||
|
||||
# ── Bluetooth ────────────────────────────────────────────────────────────────
|
||||
if [[ "$(bluetoothctl list 2>/dev/null | wc -l)" -gt 0 ]]; then
|
||||
[[ "$(jq -r .adapter <<<"$state")" == "true" ]] \
|
||||
|| fail 'an adapter is present but the service did not find it'
|
||||
fi
|
||||
|
||||
trap - EXIT
|
||||
cleanup
|
||||
printf 'connectivity contract: PASS\n'
|
||||
@@ -13,6 +13,20 @@ helper="$project_root/config/dot/quickshell/scripts/panama-home-assistant"
|
||||
[[ -x "$helper" ]] || fail 'helper is missing or not executable'
|
||||
|
||||
catalog="$($helper catalog)"
|
||||
|
||||
# This asserts a LIVE, authenticated Home Assistant. An absent credential is not
|
||||
# a defect in Panama, so it skips rather than fails -- otherwise the suite is red
|
||||
# on any machine that has not been given a token, and a red suite that is
|
||||
# expected to be red stops being read.
|
||||
#
|
||||
# A configured-but-broken bridge still fails, which is the case worth catching.
|
||||
if [[ "$(jq -r '.configured' <<<"$catalog")" != "true" ]]; then
|
||||
printf 'Home Assistant helper contract: SKIP (no token configured)\n'
|
||||
printf ' Set PANAMA_HOME_ASSISTANT_TOKEN in config/bash/env to exercise this.\n'
|
||||
printf ' Reason reported by the helper: %s\n' "$(jq -r '.error // "unknown"' <<<"$catalog")"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
jq -e '
|
||||
.ok == true and .configured == true and .error == "" and
|
||||
(.entities | type == "array" and length > 0) and
|
||||
|
||||
Reference in New Issue
Block a user