feat(pi): add a Wi-Fi hotspot mode for routerless direct streaming
scripts/pi-hotspot.sh turns the receiver into a WPA2 access point via NetworkManager (ipv4 shared mode gives the Pi built-in DHCP/NAT at 10.42.0.1), so a sender connects directly with no router in between — which also sidesteps LAN quirks like unreachable 6to4 addresses, since the direct link is plain private IPv4. The passphrase is generated on first use and stored root-only in /etc/screencast-hotspot.conf; on|off|status subcommands manage it, and off restores normal client Wi-Fi. The hotspot owns wlan0 while active (documented). No receiver changes were needed: it already announces on every interface. nmcli property syntax validated against NetworkManager 1.58 with a disposable never-activated profile; actual AP bring-up can only be validated on the Pi.
This commit is contained in:
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# pi-hotspot.sh — turn the receiver into a Wi-Fi access point so a sender
|
||||
# can connect directly, without any router in between.
|
||||
#
|
||||
# Usage (on the Pi):
|
||||
#
|
||||
# sudo scripts/pi-hotspot.sh on [--ssid NAME] [--psk KEY] [--band bg|a]
|
||||
# sudo scripts/pi-hotspot.sh off
|
||||
# sudo scripts/pi-hotspot.sh status
|
||||
#
|
||||
# The hotspot takes over the Wi-Fi interface: the Pi leaves your router's
|
||||
# network while it is active. Connect to the SSID from the sending machine;
|
||||
# the Pi is reachable at 10.42.0.1 (SSH, screencast) and the sender gets an
|
||||
# address via the built-in DHCP. Discovery and streaming then run over the
|
||||
# direct link.
|
||||
#
|
||||
# The passphrase is stored in /etc/screencast-hotspot.conf (root only) so
|
||||
# `status` can show it again.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CONNECTION_NAME="screencast-hotspot"
|
||||
INTERFACE="${SC_HOTSPOT_INTERFACE:-wlan0}"
|
||||
CONFIG_FILE="/etc/screencast-hotspot.conf"
|
||||
|
||||
log() { printf '\n=== %s\n' "$*"; }
|
||||
die() { printf 'pi-hotspot: error: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
require_root() {
|
||||
[ "$(id -u)" = 0 ] || die "run with sudo"
|
||||
}
|
||||
|
||||
random_passphrase() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -base64 12 | tr -d '=+/' | head -c 16
|
||||
else
|
||||
tr -dc 'A-Za-z0-9' </dev/urandom | head -c 16
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_on() {
|
||||
local ssid="screencast" psk="" band="bg"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--ssid) ssid="$2"; shift 2 ;;
|
||||
--psk) psk="$2"; shift 2 ;;
|
||||
--band) band="$2"; shift 2 ;;
|
||||
*) die "unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
command -v nmcli >/dev/null 2>&1 || die "NetworkManager (nmcli) not found"
|
||||
systemctl is-active --quiet NetworkManager || die "NetworkManager is not running"
|
||||
ip link show "$INTERFACE" >/dev/null 2>&1 || die "interface ${INTERFACE} does not exist"
|
||||
|
||||
if [ -z "$psk" ]; then
|
||||
psk="$(random_passphrase)"
|
||||
fi
|
||||
|
||||
log "warning: this takes over ${INTERFACE}; the Pi leaves any router Wi-Fi while the hotspot is active"
|
||||
log "creating hotspot '${ssid}' (WPA2, band ${band})"
|
||||
|
||||
apt-get install -y --no-install-recommends network-manager dnsmasq-base >/dev/null 2>&1 || true
|
||||
|
||||
nmcli connection delete "$CONNECTION_NAME" >/dev/null 2>&1 || true
|
||||
nmcli connection add \
|
||||
type wifi \
|
||||
ifname "$INTERFACE" \
|
||||
con-name "$CONNECTION_NAME" \
|
||||
autoconnect yes \
|
||||
connection.autoconnect-priority 100 \
|
||||
802-11-wireless.ssid "$ssid" \
|
||||
802-11-wireless.mode ap \
|
||||
802-11-wireless.band "$band" \
|
||||
802-11-wireless-security.key-mgmt wpa-psk \
|
||||
802-11-wireless-security.psk "$psk" \
|
||||
ipv4.method shared \
|
||||
ipv6.method disabled >/dev/null
|
||||
|
||||
nmcli connection up "$CONNECTION_NAME" >/dev/null
|
||||
|
||||
umask 077
|
||||
printf 'ssid=%s\npsk=%s\n' "$ssid" "$psk" >"$CONFIG_FILE"
|
||||
|
||||
log "hotspot is up"
|
||||
printf ' SSID: %s\n' "$ssid"
|
||||
printf ' Password: %s\n' "$psk"
|
||||
printf ' Pi address: 10.42.0.1 (also announced via mDNS)\n'
|
||||
printf '\nOn the sending machine: connect to the Wi-Fi above, then\n'
|
||||
printf ' screencast --send\n'
|
||||
}
|
||||
|
||||
cmd_off() {
|
||||
log "removing the hotspot (Wi-Fi returns to normal client mode)"
|
||||
nmcli connection delete "$CONNECTION_NAME" >/dev/null 2>&1 || {
|
||||
log "no hotspot configured"
|
||||
exit 0
|
||||
}
|
||||
rm -f "$CONFIG_FILE"
|
||||
log "done; the Pi will reconnect to any known router Wi-Fi"
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
log "hotspot configuration (${CONFIG_FILE})"
|
||||
grep -E '^(ssid|psk)=' "$CONFIG_FILE" | sed -e 's/^/ /'
|
||||
else
|
||||
log "no hotspot configured (run: sudo scripts/pi-hotspot.sh on)"
|
||||
fi
|
||||
nmcli --get-values NAME,TYPE,DEVICE connection show --active 2>/dev/null | grep wifi | sed 's/^/ /' || true
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
on) require_root; shift; cmd_on "$@" ;;
|
||||
off) require_root; shift; cmd_off "$@" ;;
|
||||
status) cmd_status ;;
|
||||
*)
|
||||
cat <<'EOF'
|
||||
usage: sudo scripts/pi-hotspot.sh on [--ssid NAME] [--psk KEY] [--band bg|a]
|
||||
sudo scripts/pi-hotspot.sh off
|
||||
sudo scripts/pi-hotspot.sh status
|
||||
EOF
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user