docs: mark APK distribution as deployed

This commit is contained in:
Mathieu Moran
2026-08-15 21:10:27 -04:00
parent a050f3577a
commit 4801508415
7 changed files with 479 additions and 5 deletions
+449
View File
@@ -0,0 +1,449 @@
#!/usr/bin/env bash
#
# yaris-obd — one-shot installer for a freshly flashed Raspberry Pi OS.
#
# Takes a blank Raspberry Pi OS Bookworm (Lite or desktop) install all the way
# to a running yaris-obd deployment:
# * base packages + NetworkManager
# * the `yaris` service user (with sudo + serial/GPIO groups)
# * repo clone at /home/yaris/app/yaris-obd
# * RTL8812BU driver for the two TP-Link Archer T3U dongles (wlan1/wlan2)
# * NetworkManager profiles: home-wifi, Hotspot-local, optional phone-hotspot
# * every deploy/install_*.sh in the correct order
#
# Run ON THE PI as root (or via sudo):
# sudo bash auto_install.sh
#
# The only questions it asks are for the WiFi credentials it needs. You can
# instead pass them through the environment for fully non-interactive runs:
#
# HOME_WIFI_SSID="MyWiFi" \
# HOME_WIFI_PSK="secret" \
# HOTSPOT_SSID="yaris-obd" \
# HOTSPOT_PSK="in-car-password" \
# sudo -E bash auto_install.sh --non-interactive
#
# Optional flags:
# --non-interactive fail instead of prompting for missing credentials
# --skip-t3u-driver do not build the RTL8812BU driver (wlan1/wlan2)
# --no-watchdog do not enable the hardware watchdog
# --no-app-updater do not install the Android APK mirror timer
# --with-rtc configure the DS3231 RTC (I2C + overlay + fake-hwclock off)
# --with-sim868 configure the SIM868 UART (enable_uart, disable-bt,
# no serial login shell, hciuart off)
# -h|--help print this help
#
# Env vars honoured (all optional unless marked required):
# GIT_URL / GIT_BRANCH repo source (defaults to GitHub default branch)
# HOME_WIFI_SSID / HOME_WIFI_PSK home WiFi (required for netwatch)
# HOTSPOT_SSID / HOTSPOT_PSK in-car AP (SSID defaults to "yaris-obd")
# PHONE_HOTSPOT_SSID / PHONE_HOTSPOT_PSK optional phone hotspot uplink
# YARIS_PASSWORD set a password for the yaris user (else disabled)
# OWNER_NAME / CAR_MODEL / DEVICE_NAME prefill config/device.json
# YARIS_SERVER_URL / YARIS_DEVICE_NAME / YARIS_API_KEY prefill db/upload_creds.env
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
REPO_DIR="/home/yaris/app/yaris-obd"
REPO_URL="${GIT_URL:-https://github.com/quoije/yaris-obd.git}"
REPO_BRANCH="${GIT_BRANCH:-}"
# ---- flag defaults (env-overridable) ---------------------------------------
NONINTERACTIVE=${NONINTERACTIVE:-0}
SKIP_T3U_DRIVER=${SKIP_T3U_DRIVER:-0}
INSTALL_WATCHDOG=${INSTALL_WATCHDOG:-1}
INSTALL_APP_UPDATER=${INSTALL_APP_UPDATER:-1}
WITH_RTC=${WITH_RTC:-0}
WITH_SIM868=${WITH_SIM868:-0}
# ---- logging ----------------------------------------------------------------
info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m!!>\033[0m %s\n' "$*" >&2; }
err() { printf '\033[1;31m!!>\033[0m %s\n' "$*" >&2; }
usage() {
sed -n '2,40p' "$0"
}
# ---- small helpers ----------------------------------------------------------
config_txt_path() {
if [ -f /boot/firmware/config.txt ]; then
printf '%s' /boot/firmware/config.txt
elif [ -f /boot/config.txt ]; then
printf '%s' /boot/config.txt
else
printf '%s' /boot/firmware/config.txt
fi
}
# Idempotently set a KEY=... style line in config.txt, replacing any existing
# (possibly commented) occurrence of the key, appending otherwise.
set_config_line() {
local file="$1" key="$2" line="$3"
if [ ! -f "$file" ]; then
warn "$file does not exist; skipping '$line'."
return 0
fi
if grep -qE "^[[:space:]]*#?[[:space:]]*${key}" "$file"; then
sed -i -E "s|^[[:space:]]*#?[[:space:]]*${key}.*|${line}|" "$file"
else
printf '%s\n' "$line" >> "$file"
fi
}
# Return the value of the named env var, an explicit default, or prompt (unless
# non-interactive). Prints the value to stdout.
prompt_value() {
local var="$1" prompt="$2" default="${3:-}"
local val="${!var:-}"
if [ -n "$val" ]; then
printf '%s' "$val"
return 0
fi
if [ -n "$default" ]; then
printf '%s' "$default"
return 0
fi
if [ "$NONINTERACTIVE" = "1" ]; then
err "Missing required value: $var ($prompt). Pass it via the environment or drop --non-interactive."
exit 1
fi
read -rp "$prompt: " val
printf '%s' "$val"
}
# Same as prompt_value but reads the answer without echoing it.
prompt_secret() {
local var="$1" prompt="$2" default="${3:-}"
local val="${!var:-}"
if [ -n "$val" ]; then
printf '%s' "$val"
return 0
fi
if [ -n "$default" ]; then
printf '%s' "$default"
return 0
fi
if [ "$NONINTERACTIVE" = "1" ]; then
err "Missing required value: $var ($prompt). Pass it via the environment or drop --non-interactive."
exit 1
fi
read -rsp "$prompt: " val
printf '\n' >&2
printf '%s' "$val"
}
# ---- install steps ----------------------------------------------------------
install_base_packages() {
info "Installing base packages..."
apt-get update
apt-get install -y \
git curl python3 python3-venv python3-pip \
network-manager nftables avahi-daemon dnsmasq-base iw
systemctl unmask NetworkManager 2>/dev/null || true
systemctl enable --now NetworkManager
}
create_yaris_user() {
info "Creating the 'yaris' service user..."
if ! id -u yaris >/dev/null 2>&1; then
if [ -n "${YARIS_PASSWORD:-}" ]; then
useradd --create-home --shell /bin/bash \
--password "$(openssl passwd -6 "$YARIS_PASSWORD")" yaris
else
useradd --create-home --shell /bin/bash --disabled-password yaris
fi
fi
usermod -aG sudo,dialout,gpio,i2c yaris
info "yaris user ready (groups: $(id -nG yaris | tr ' ' ','))"
}
clone_repo() {
info "Cloning $REPO_URL into $REPO_DIR..."
mkdir -p "$(dirname "$REPO_DIR")"
chown yaris:yaris "$(dirname "$REPO_DIR")"
if [ ! -d "$REPO_DIR/.git" ]; then
local branch_args=()
if [ -n "$REPO_BRANCH" ]; then
branch_args+=(--branch "$REPO_BRANCH")
fi
sudo -u yaris git clone "${branch_args[@]}" "$REPO_URL" "$REPO_DIR"
else
warn "$REPO_DIR already exists — attempting git pull."
sudo -u yaris git -C "$REPO_DIR" pull --ff-only || warn "git pull failed; continuing with the existing checkout."
fi
}
install_t3u_driver() {
if [ "$SKIP_T3U_DRIVER" = "1" ]; then
warn "Skipping RTL8812BU driver build (--skip-t3u-driver). wlan1/wlan2 will not exist."
return 0
fi
info "Building the RTL8812BU driver for the TP-Link Archer T3U dongles..."
apt-get install -y dkms build-essential bc raspberrypi-kernel-headers
rm -rf /tmp/88x2bu-20210702
git clone --depth 1 https://github.com/morrownr/88x2bu-20210702.git /tmp/88x2bu-20210702
( cd /tmp/88x2bu-20210702 && printf 'n\n' | ./install-driver.sh ) || {
warn "RTL8812BU driver build failed. The in-car AP/uplink on wlan1/wlan2 will be unavailable until fixed."
return 0
}
if [ -e /sys/class/net/wlan1 ]; then
info "wlan1 is present."
else
warn "wlan1 still not present after the driver build — check the dongle / USB ports."
fi
}
create_home_wifi() {
local ssid psk
ssid="$(prompt_value HOME_WIFI_SSID "Home WiFi SSID")"
psk="$(prompt_secret HOME_WIFI_PSK "Home WiFi password")"
info "Configuring NetworkManager profile 'home-wifi'..."
if nmcli connection show home-wifi >/dev/null 2>&1; then
nmcli connection modify home-wifi \
802-11-wireless.ssid "$ssid" \
wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$psk" \
connection.autoconnect yes connection.autoconnect-priority 30
else
nmcli connection add type wifi con-name home-wifi ifname wlan2 \
ssid "$ssid" \
wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$psk" \
connection.autoconnect yes connection.autoconnect-priority 30
fi
}
create_hotspot() {
local ssid psk
ssid="$(prompt_value HOTSPOT_SSID "In-car hotspot SSID" "yaris-obd")"
psk="$(prompt_secret HOTSPOT_PSK "In-car hotspot password")"
info "Configuring NetworkManager profile 'Hotspot-local' (5GHz AP on wlan1)..."
if nmcli connection show Hotspot-local >/dev/null 2>&1; then
nmcli connection modify Hotspot-local \
802-11-wireless.ssid "$ssid" \
802-11-wireless.mode ap \
802-11-wireless.band a \
wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$psk" \
ipv4.method shared ipv4.addresses 10.42.0.1/24 \
connection.autoconnect yes connection.autoconnect-priority 10
else
nmcli connection add type wifi con-name Hotspot-local ifname wlan1 \
ssid "$ssid" \
802-11-wireless.mode ap \
802-11-wireless.band a \
wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$psk" \
ipv4.method shared ipv4.addresses 10.42.0.1/24 \
connection.autoconnect yes connection.autoconnect-priority 10
fi
}
create_phone_hotspot() {
local ssid="${PHONE_HOTSPOT_SSID:-}"
if [ -z "$ssid" ]; then
if [ "$NONINTERACTIVE" = "1" ]; then
info "No phone-hotspot credentials provided; skipping phone hotspot."
return 0
fi
read -rp "Phone hotspot SSID (leave empty to skip): " ssid
if [ -z "$ssid" ]; then
info "Skipping phone hotspot."
return 0
fi
fi
local psk
psk="$(prompt_secret PHONE_HOTSPOT_PSK "Phone hotspot password")"
info "Configuring NetworkManager profile 'phone-hotspot'..."
if nmcli connection show phone-hotspot >/dev/null 2>&1; then
nmcli connection modify phone-hotspot \
802-11-wireless.ssid "$ssid" \
wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$psk" \
connection.autoconnect yes connection.autoconnect-priority 20 \
ipv4.dhcp-send-hostname yes ipv4.dhcp-hostname yaris \
802-11-wireless.mtu 1400
else
nmcli connection add type wifi con-name phone-hotspot ifname wlan2 \
ssid "$ssid" \
wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$psk" \
connection.autoconnect yes connection.autoconnect-priority 20 \
ipv4.dhcp-send-hostname yes ipv4.dhcp-hostname yaris \
802-11-wireless.mtu 1400
fi
}
enable_rtc() {
if [ "$WITH_RTC" != "1" ]; then
return 0
fi
info "Configuring the DS3231 RTC (--with-rtc)..."
apt-get install -y i2c-tools
local cfg
cfg="$(config_txt_path)"
set_config_line "$cfg" "dtparam=i2c_arm" "dtparam=i2c_arm=on"
set_config_line "$cfg" "dtoverlay=i2c-rtc,ds3231" "dtoverlay=i2c-rtc,ds3231"
printf '%s\n' "i2c-dev" > /etc/modules-load.d/i2c-dev.conf
systemctl disable --now fake-hwclock 2>/dev/null || true
warn "RTC overlay enabled — reboot for it to take effect, then verify with: i2cdetect -y 1 (expect UU at 0x68)."
}
enable_sim868() {
if [ "$WITH_SIM868" != "1" ]; then
return 0
fi
info "Configuring the SIM868 UART (--with-sim868)..."
local cfg
cfg="$(config_txt_path)"
set_config_line "$cfg" "enable_uart" "enable_uart=1"
set_config_line "$cfg" "dtoverlay=disable-bt" "dtoverlay=disable-bt"
if command -v raspi-config >/dev/null 2>&1; then
raspi-config nonint do_serial_cons 1 || true
fi
systemctl disable --now hciuart 2>/dev/null || true
warn "SIM868 UART enabled — reboot for it to take effect (serial0 frees the full PL011 UART)."
}
prefill_device_config() {
if [ -z "${OWNER_NAME:-}${CAR_MODEL:-}${DEVICE_NAME:-}" ]; then
return 0
fi
info "Prefilling config/device.json..."
python3 - "$REPO_DIR/config/device.json" "${OWNER_NAME:-}" "${CAR_MODEL:-}" "${DEVICE_NAME:-}" <<'PY'
import json, sys
path, owner, car, device = sys.argv[1:5]
with open(path) as f:
data = json.load(f)
if owner:
data["owner_name"] = owner
if car:
data["car_model"] = car
if device:
data["device_name"] = device
with open(path, "w") as f:
json.dump(data, f, indent=2)
f.write("\n")
PY
}
prefill_upload_creds() {
if [ -z "${YARIS_SERVER_URL:-}${YARIS_DEVICE_NAME:-}${YARIS_API_KEY:-}" ]; then
return 0
fi
info "Prefilling db/upload_creds.env..."
local creds="$REPO_DIR/db/upload_creds.env"
python3 - "$creds" \
"YARIS_SERVER_URL" "${YARIS_SERVER_URL:-}" \
"YARIS_DEVICE_NAME" "${YARIS_DEVICE_NAME:-}" \
"YARIS_API_KEY" "${YARIS_API_KEY:-}" <<'PY'
import re, sys
path = sys.argv[1]
pairs = list(zip(sys.argv[2::2], sys.argv[3::2]))
with open(path) as f:
text = f.read()
for key, value in pairs:
if value == "":
continue
pattern = re.compile(rf"^{re.escape(key)}\s*=.*$", re.M)
replacement = f'{key} = "{value}"'
if pattern.search(text):
text = pattern.sub(replacement, text, count=1)
else:
text += replacement + "\n"
with open(path, "w") as f:
f.write(text)
PY
}
run_installers() {
info "Running deploy installers (as root; sudo inside them is a no-op)..."
info "install_service.sh"
bash "$REPO_DIR/deploy/install_service.sh"
info "install_port_redirect.sh"
bash "$REPO_DIR/deploy/install_port_redirect.sh"
info "install_netwatch.sh"
bash "$REPO_DIR/deploy/install_netwatch.sh"
info "install_video_tools.sh"
bash "$REPO_DIR/deploy/install_video_tools.sh"
if [ "$INSTALL_WATCHDOG" = "1" ]; then
info "install_watchdog.sh"
bash "$REPO_DIR/deploy/install_watchdog.sh" || warn "watchdog install failed (non-fatal)."
else
warn "Skipping watchdog install (--no-watchdog)."
fi
if [ "$INSTALL_APP_UPDATER" = "1" ]; then
info "install_app_updater.sh"
bash "$REPO_DIR/deploy/install_app_updater.sh" || warn "APK updater install failed (non-fatal)."
else
warn "Skipping APK updater install (--no-app-updater)."
fi
}
fix_ownership_and_data_dirs() {
info "Fixing repo ownership and creating writable data directories..."
mkdir -p "$REPO_DIR/db/sql" "$REPO_DIR/dash/videos" "$REPO_DIR/static/app"
chown -R yaris:yaris "$REPO_DIR"
}
print_summary() {
echo
info "Installation complete."
echo
echo "What was set up:"
echo " - yaris user (sudo, dialout, gpio, i2c)"
echo " - repo at $REPO_DIR"
echo " - yaris-obd.service (enabled + running on boot)"
echo " - nftables port 80 -> 5000 redirect"
echo " - yaris-netwatch.timer (home-wifi / phone-hotspot / Hotspot-local switching)"
echo " - ffmpeg thumbnails, hardware watchdog, APK mirror timer"
echo
echo "Verify:"
echo " sudo systemctl status yaris-obd.service --no-pager"
echo " curl -I http://127.0.0.1/"
echo
if [ "$WITH_RTC" = "1" ] || [ "$WITH_SIM868" = "1" ]; then
echo "A reboot is required for the RTC / SIM868 boot-config changes to apply."
fi
echo "After reboot, open the web UI at http://yaris.local (or http://10.42.0.1 from the car hotspot)."
}
main() {
while [ $# -gt 0 ]; do
case "$1" in
--non-interactive) NONINTERACTIVE=1 ;;
--skip-t3u-driver) SKIP_T3U_DRIVER=1 ;;
--no-watchdog) INSTALL_WATCHDOG=0 ;;
--no-app-updater) INSTALL_APP_UPDATER=0 ;;
--with-rtc) WITH_RTC=1 ;;
--with-sim868) WITH_SIM868=1 ;;
-h|--help) usage; exit 0 ;;
*) err "Unknown option: $1"; usage; exit 1 ;;
esac
shift
done
if [ "$(id -u)" -ne 0 ]; then
err "This script must be run as root (or via sudo)."
exit 1
fi
install_base_packages
create_yaris_user
clone_repo
enable_rtc
enable_sim868
install_t3u_driver
create_home_wifi
create_hotspot
create_phone_hotspot
run_installers
prefill_device_config
prefill_upload_creds
fix_ownership_and_data_dirs
print_summary
}
main "$@"