dw 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. #!/bin/bash
  2. #
  3. # Dune Weaver CLI
  4. #
  5. # Usage: dw <command>
  6. #
  7. # Commands:
  8. # install Run initial setup
  9. # start Start Dune Weaver
  10. # stop Stop Dune Weaver
  11. # restart Restart Dune Weaver
  12. # update Pull latest changes and restart
  13. # logs View live logs (Ctrl+C to exit)
  14. # status Show service status
  15. # shell Open a shell with the venv activated
  16. # checkout Switch to a branch and rebuild
  17. # touch Manage touch screen app
  18. # wifi Manage WiFi and hotspot
  19. # help Show this help message
  20. #
  21. set -e
  22. # Colors
  23. RED='\033[0;31m'
  24. GREEN='\033[0;32m'
  25. YELLOW='\033[1;33m'
  26. BLUE='\033[0;34m'
  27. NC='\033[0m'
  28. # Find dune-weaver directory
  29. find_install_dir() {
  30. # Check common locations
  31. if [[ -f "$HOME/dune-weaver/main.py" ]]; then
  32. echo "$HOME/dune-weaver"
  33. elif [[ -f "/home/pi/dune-weaver/main.py" ]]; then
  34. echo "/home/pi/dune-weaver"
  35. elif [[ -f "./main.py" ]]; then
  36. pwd
  37. else
  38. echo ""
  39. fi
  40. }
  41. INSTALL_DIR=$(find_install_dir)
  42. # Run a command as the real (non-root) user when invoked via sudo.
  43. # For dw commands that are not typically run with sudo, this is a no-op.
  44. run_as_user() {
  45. if [[ $EUID -eq 0 && -n "$SUDO_USER" ]]; then
  46. sudo -u "$SUDO_USER" -- "$@"
  47. else
  48. "$@"
  49. fi
  50. }
  51. # Ensure repo files are owned by the real user (not root).
  52. fix_repo_ownership() {
  53. if [[ -n "$INSTALL_DIR" && $EUID -eq 0 && -n "$SUDO_USER" ]]; then
  54. chown -R "$SUDO_USER:$SUDO_USER" "$INSTALL_DIR"
  55. fi
  56. }
  57. # Check if installed
  58. check_installed() {
  59. if [[ -z "$INSTALL_DIR" ]]; then
  60. echo -e "${RED}Error: Dune Weaver not found${NC}"
  61. echo ""
  62. echo "Please install first:"
  63. echo " git clone https://github.com/tuanchris/dune-weaver --single-branch"
  64. echo " cd dune-weaver"
  65. echo " dw install"
  66. exit 1
  67. fi
  68. }
  69. # Commands
  70. cmd_install() {
  71. if [[ -z "$INSTALL_DIR" ]]; then
  72. # Not installed, check if we're in the right directory
  73. if [[ -f "./main.py" ]] && [[ -f "./requirements.txt" ]]; then
  74. INSTALL_DIR=$(pwd)
  75. else
  76. echo -e "${RED}Error: Run this from the dune-weaver directory${NC}"
  77. echo ""
  78. echo " git clone https://github.com/tuanchris/dune-weaver --single-branch"
  79. echo " cd dune-weaver"
  80. echo " dw install"
  81. exit 1
  82. fi
  83. fi
  84. cd "$INSTALL_DIR"
  85. bash setup-pi.sh "$@"
  86. }
  87. cmd_start() {
  88. check_installed
  89. echo -e "${BLUE}Starting Dune Weaver...${NC}"
  90. sudo systemctl start dune-weaver
  91. echo -e "${GREEN}Started!${NC} Access at http://$(hostname -I | awk '{print $1}')"
  92. }
  93. cmd_stop() {
  94. check_installed
  95. echo -e "${BLUE}Stopping Dune Weaver...${NC}"
  96. sudo systemctl stop dune-weaver
  97. echo -e "${GREEN}Stopped${NC}"
  98. }
  99. cmd_restart() {
  100. check_installed
  101. echo -e "${BLUE}Restarting Dune Weaver...${NC}"
  102. sudo systemctl restart dune-weaver
  103. echo -e "${GREEN}Restarted${NC}"
  104. }
  105. cmd_update() {
  106. check_installed
  107. echo -e "${BLUE}Updating Dune Weaver...${NC}"
  108. cd "$INSTALL_DIR"
  109. # Check if we should skip the pull phase (called after re-exec)
  110. if [[ "$1" != "--continue" ]]; then
  111. echo "Pulling latest code..."
  112. run_as_user git config --global --add safe.directory "$INSTALL_DIR" 2>/dev/null || true
  113. local branch
  114. branch=$(git rev-parse --abbrev-ref HEAD)
  115. run_as_user git fetch origin "$branch"
  116. run_as_user git reset --hard "origin/$branch"
  117. fix_repo_ownership
  118. # Update dw CLI
  119. echo "Updating dw command..."
  120. sudo cp "$INSTALL_DIR/dw" /usr/local/bin/dw
  121. sudo chmod +x /usr/local/bin/dw
  122. # Re-exec with the new script to ensure new code runs
  123. echo "Restarting with updated CLI..."
  124. exec /usr/local/bin/dw update --continue
  125. fi
  126. # Detect Docker-to-native migration: no venv means first native run
  127. if [[ ! -d "$INSTALL_DIR/.venv" ]]; then
  128. echo ""
  129. echo -e "${YELLOW}Migrating to native deployment (first time setup)...${NC}"
  130. echo "This may take a few minutes."
  131. echo ""
  132. # Install native system dependencies (nginx, python3-venv, gcc, etc.)
  133. echo "Installing system dependencies..."
  134. sudo apt update
  135. sudo DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \
  136. python3-venv python3-pip python3-dev \
  137. gcc g++ make swig unzip wget \
  138. libjpeg-dev zlib1g-dev \
  139. libgpiod-dev gpiod \
  140. nginx
  141. # Create Python venv as real user
  142. echo "Creating Python virtual environment..."
  143. run_as_user python3 -m venv .venv
  144. # Stop and remove Docker containers/service if running
  145. if command -v docker &> /dev/null; then
  146. echo "Stopping Docker containers..."
  147. sudo docker compose down 2>/dev/null || true
  148. fi
  149. fi
  150. # Fix venv ownership if created by sudo (legacy installs)
  151. if [[ -d ".venv" ]] && [[ "$(stat -c '%U' .venv 2>/dev/null)" == "root" ]]; then
  152. echo "Fixing .venv ownership..."
  153. local real_user="${SUDO_USER:-$USER}"
  154. sudo chown -R "$real_user:$real_user" .venv
  155. fi
  156. echo "Updating Python dependencies..."
  157. source .venv/bin/activate
  158. run_as_user .venv/bin/pip install -r requirements.txt
  159. # Update nginx config
  160. echo "Updating nginx config..."
  161. sudo cp "$INSTALL_DIR/nginx/dune-weaver.conf" /etc/nginx/sites-available/dune-weaver.conf
  162. sudo sed -i "s|INSTALL_DIR_PLACEHOLDER|$INSTALL_DIR|g" /etc/nginx/sites-available/dune-weaver.conf
  163. sudo ln -sf /etc/nginx/sites-available/dune-weaver.conf /etc/nginx/sites-enabled/dune-weaver.conf
  164. sudo rm -f /etc/nginx/sites-enabled/default
  165. sudo nginx -t && sudo systemctl restart nginx
  166. # Update systemd service
  167. echo "Updating systemd service..."
  168. sudo cp "$INSTALL_DIR/dune-weaver.service" /etc/systemd/system/dune-weaver.service
  169. sudo sed -i "s|INSTALL_DIR_PLACEHOLDER|$INSTALL_DIR|g" /etc/systemd/system/dune-weaver.service
  170. sudo systemctl daemon-reload
  171. sudo systemctl enable dune-weaver
  172. echo "Restarting service..."
  173. sudo systemctl restart dune-weaver
  174. # Install/update WiFi host scripts if not present
  175. if [[ -f "$INSTALL_DIR/wifi/setup-wifi.sh" ]]; then
  176. if [[ ! -f /usr/local/bin/autohotspot ]]; then
  177. echo ""
  178. echo -e "${BLUE}Installing autohotspot WiFi scripts...${NC}"
  179. sudo bash "$INSTALL_DIR/wifi/setup-wifi.sh"
  180. else
  181. # Always update the script and service files to latest
  182. echo "Updating WiFi host scripts..."
  183. sudo cp "$INSTALL_DIR/wifi/autohotspot" /usr/local/bin/autohotspot
  184. sudo chmod +x /usr/local/bin/autohotspot
  185. # Update service/timer files
  186. sudo cp "$INSTALL_DIR/wifi/autohotspot.service" /etc/systemd/system/autohotspot.service
  187. sudo cp "$INSTALL_DIR/wifi/autohotspot-check.service" /etc/systemd/system/autohotspot-check.service 2>/dev/null || true
  188. sudo cp "$INSTALL_DIR/wifi/autohotspot-check.timer" /etc/systemd/system/autohotspot-check.timer 2>/dev/null || true
  189. sudo systemctl daemon-reload
  190. sudo systemctl enable autohotspot-check.timer 2>/dev/null || true
  191. fi
  192. fi
  193. # Update touch app if installed (check both directory and service exist)
  194. local touch_dir="$INSTALL_DIR/dune-weaver-touch"
  195. if [[ -d "$touch_dir" ]] && systemctl list-unit-files dune-weaver-touch.service 2>/dev/null | grep -q dune-weaver-touch; then
  196. echo ""
  197. echo -e "${BLUE}Updating touch app...${NC}"
  198. cd "$touch_dir"
  199. # Update Python dependencies (code already pulled with main repo)
  200. if [[ -f "requirements.txt" ]] && [[ -d ".venv" ]]; then
  201. echo "Updating touch app dependencies..."
  202. source .venv/bin/activate
  203. pip install -q -r requirements.txt
  204. deactivate
  205. fi
  206. # Restart to apply changes
  207. sudo systemctl restart dune-weaver-touch
  208. echo -e "${GREEN}Touch app updated!${NC}"
  209. fi
  210. echo -e "${GREEN}Update complete!${NC}"
  211. echo ""
  212. echo -e "${YELLOW}Please reload the web page in your browser to see the changes.${NC}"
  213. }
  214. cmd_logs() {
  215. check_installed
  216. # Parse optional line count (e.g., dw logs 100)
  217. local lines="${1:-}"
  218. if [[ -n "$lines" ]]; then
  219. sudo journalctl -u dune-weaver -n "$lines"
  220. else
  221. sudo journalctl -u dune-weaver -f
  222. fi
  223. }
  224. cmd_status() {
  225. check_installed
  226. sudo systemctl status dune-weaver
  227. }
  228. cmd_shell() {
  229. check_installed
  230. cd "$INSTALL_DIR"
  231. echo -e "${BLUE}Activating venv...${NC}"
  232. exec bash --init-file <(echo "source '$INSTALL_DIR/.venv/bin/activate'; cd '$INSTALL_DIR'; echo 'Dune Weaver venv activated. Type \"exit\" to leave.'")
  233. }
  234. # Touch app commands (systemd service)
  235. cmd_touch() {
  236. local subcmd="${1:-help}"
  237. case "$subcmd" in
  238. logs)
  239. local lines="${2:-}"
  240. if [[ -n "$lines" ]]; then
  241. sudo journalctl -u dune-weaver-touch -n "$lines"
  242. else
  243. sudo journalctl -u dune-weaver-touch -f
  244. fi
  245. ;;
  246. restart)
  247. echo -e "${BLUE}Restarting touch app...${NC}"
  248. sudo systemctl restart dune-weaver-touch
  249. echo -e "${GREEN}Touch app restarted${NC}"
  250. ;;
  251. stop)
  252. echo -e "${BLUE}Stopping touch app...${NC}"
  253. sudo systemctl stop dune-weaver-touch
  254. echo -e "${GREEN}Touch app stopped${NC}"
  255. ;;
  256. start)
  257. echo -e "${BLUE}Starting touch app...${NC}"
  258. sudo systemctl start dune-weaver-touch
  259. echo -e "${GREEN}Touch app started${NC}"
  260. ;;
  261. status)
  262. sudo systemctl status dune-weaver-touch
  263. ;;
  264. install)
  265. check_installed
  266. local touch_dir="$INSTALL_DIR/dune-weaver-touch"
  267. if [[ ! -f "$touch_dir/install.sh" ]]; then
  268. echo -e "${RED}Touch app installer not found at $touch_dir/install.sh${NC}"
  269. echo "Make sure you have the dune-weaver-touch directory in your dune-weaver folder."
  270. exit 1
  271. fi
  272. echo -e "${BLUE}Running touch app installer...${NC}"
  273. cd "$touch_dir"
  274. sudo bash install.sh
  275. ;;
  276. help|*)
  277. echo -e "${GREEN}Touch App Commands${NC}"
  278. echo ""
  279. echo "Usage: dw touch <command>"
  280. echo ""
  281. echo "Commands:"
  282. echo " install Install touch app (clone, setup, enable service)"
  283. echo " logs [N] View logs (N lines or follow if omitted)"
  284. echo " restart Restart touch app"
  285. echo " stop Stop touch app"
  286. echo " start Start touch app"
  287. echo " status Show touch app status"
  288. echo " help Show this help message"
  289. ;;
  290. esac
  291. }
  292. cmd_checkout() {
  293. check_installed
  294. local branch="$1"
  295. if [[ -z "$branch" ]]; then
  296. echo -e "${RED}Usage: dw checkout <branch>${NC}"
  297. echo ""
  298. echo "Examples:"
  299. echo " dw checkout main"
  300. echo " dw checkout feature/security-settings-split"
  301. echo ""
  302. echo "Current branch: $(git -C "$INSTALL_DIR" rev-parse --abbrev-ref HEAD)"
  303. exit 1
  304. fi
  305. cd "$INSTALL_DIR"
  306. # Repo may be cloned with --single-branch; fetch the target branch explicitly
  307. echo -e "${BLUE}Fetching branch ${branch}...${NC}"
  308. if ! run_as_user git fetch origin "$branch" 2>/dev/null; then
  309. echo -e "${RED}Branch '${branch}' not found on remote${NC}"
  310. exit 1
  311. fi
  312. # Checkout: create local tracking branch if it doesn't exist
  313. if git show-ref --verify --quiet "refs/heads/$branch"; then
  314. run_as_user git checkout "$branch"
  315. run_as_user git reset --hard "origin/$branch"
  316. else
  317. run_as_user git checkout -b "$branch" "origin/$branch"
  318. fi
  319. fix_repo_ownership
  320. echo -e "Switched to branch: ${GREEN}${branch}${NC}"
  321. # Update Python dependencies
  322. echo "Updating Python dependencies..."
  323. source .venv/bin/activate
  324. run_as_user .venv/bin/pip install -r requirements.txt
  325. # Update dw CLI from the new branch
  326. sudo cp "$INSTALL_DIR/dw" /usr/local/bin/dw
  327. sudo chmod +x /usr/local/bin/dw
  328. # Restart service
  329. sudo systemctl restart dune-weaver
  330. # Install WiFi scripts if available on this branch but not yet installed
  331. if [[ -f "$INSTALL_DIR/wifi/setup-wifi.sh" ]] && [[ ! -f /usr/local/bin/autohotspot ]]; then
  332. echo ""
  333. echo -e "${BLUE}Installing autohotspot WiFi scripts...${NC}"
  334. sudo bash "$INSTALL_DIR/wifi/setup-wifi.sh"
  335. fi
  336. echo -e "${GREEN}Checkout complete!${NC}"
  337. }
  338. # WiFi / Hotspot commands
  339. cmd_wifi() {
  340. local subcmd="${1:-help}"
  341. case "$subcmd" in
  342. status)
  343. check_installed
  344. echo -e "${BLUE}WiFi Status${NC}"
  345. echo ""
  346. # Show current mode
  347. local mode="unknown"
  348. if [[ -f /tmp/dw-wifi-mode ]]; then
  349. mode=$(cat /tmp/dw-wifi-mode)
  350. fi
  351. echo -e "Mode: ${GREEN}${mode}${NC}"
  352. # Show connected network
  353. local ssid
  354. ssid=$(nmcli -t -f GENERAL.CONNECTION dev show wlan0 2>/dev/null | head -1 | cut -d: -f2)
  355. if [[ -n "$ssid" && "$ssid" != "--" ]]; then
  356. echo -e "Network: ${GREEN}${ssid}${NC}"
  357. fi
  358. # Show IP
  359. local ip
  360. ip=$(nmcli -t -f IP4.ADDRESS dev show wlan0 2>/dev/null | head -1 | cut -d: -f2 | cut -d/ -f1)
  361. if [[ -n "$ip" ]]; then
  362. echo -e "IP: ${GREEN}${ip}${NC}"
  363. fi
  364. ;;
  365. scan)
  366. echo -e "${BLUE}Scanning for WiFi networks...${NC}"
  367. nmcli dev wifi rescan 2>/dev/null || true
  368. sleep 2
  369. nmcli dev wifi list
  370. ;;
  371. connect)
  372. echo -e "${BLUE}Available WiFi networks:${NC}"
  373. nmcli dev wifi rescan 2>/dev/null || true
  374. sleep 2
  375. nmcli dev wifi list
  376. echo ""
  377. read -p "Enter SSID to connect to: " ssid
  378. if [[ -z "$ssid" ]]; then
  379. echo -e "${RED}No SSID provided${NC}"
  380. exit 1
  381. fi
  382. read -sp "Enter password (leave empty for open networks): " password
  383. echo ""
  384. # Delete any stale connection profile for this SSID
  385. sudo nmcli con delete "$ssid" 2>/dev/null || true
  386. if [[ -n "$password" ]]; then
  387. # Create connection with explicit security settings
  388. # (nmcli dev wifi connect can fail on Trixie without key-mgmt)
  389. sudo nmcli con add type wifi ifname wlan0 con-name "$ssid" \
  390. ssid "$ssid" \
  391. wifi-sec.key-mgmt wpa-psk \
  392. wifi-sec.psk "$password"
  393. else
  394. sudo nmcli con add type wifi ifname wlan0 con-name "$ssid" \
  395. ssid "$ssid"
  396. fi
  397. echo -e "${BLUE}Connecting to '$ssid'...${NC}"
  398. if sudo nmcli con up "$ssid"; then
  399. echo -e "${GREEN}Connected!${NC}"
  400. else
  401. echo -e "${RED}Failed to connect. Check password and try again.${NC}"
  402. sudo nmcli con delete "$ssid" 2>/dev/null || true
  403. exit 1
  404. fi
  405. ;;
  406. hotspot)
  407. echo -e "${BLUE}Switching to hotspot mode...${NC}"
  408. sudo nmcli dev disconnect wlan0 2>/dev/null || true
  409. sudo nmcli con up DuneWeaver-Hotspot 2>/dev/null
  410. echo "hotspot" | sudo tee /tmp/dw-wifi-mode > /dev/null
  411. echo -e "${GREEN}Hotspot active!${NC} Connect to the 'Dune Weaver' WiFi network."
  412. ;;
  413. setup)
  414. check_installed
  415. cd "$INSTALL_DIR"
  416. sudo bash wifi/setup-wifi.sh
  417. ;;
  418. help|*)
  419. echo -e "${GREEN}WiFi Commands${NC}"
  420. echo ""
  421. echo "Usage: dw wifi <command>"
  422. echo ""
  423. echo "Commands:"
  424. echo " status Show current WiFi mode and connection"
  425. echo " scan Scan for available networks"
  426. echo " connect Interactive: scan + select + connect"
  427. echo " hotspot Force hotspot mode"
  428. echo " setup Run initial autohotspot setup"
  429. echo " help Show this help message"
  430. ;;
  431. esac
  432. }
  433. cmd_help() {
  434. echo -e "${GREEN}Dune Weaver CLI${NC}"
  435. echo ""
  436. echo "Usage: dw <command>"
  437. echo ""
  438. echo "Commands:"
  439. echo " install Run initial setup"
  440. echo " start Start Dune Weaver"
  441. echo " stop Stop Dune Weaver"
  442. echo " restart Restart Dune Weaver"
  443. echo " update Pull latest changes and restart"
  444. echo " checkout Switch to a branch and rebuild"
  445. echo " logs [N] View logs (N=number of lines, or follow if omitted)"
  446. echo " status Show service status"
  447. echo " shell Open a shell with the venv activated"
  448. echo " touch Manage touch screen app (run 'dw touch help')"
  449. echo " wifi Manage WiFi and hotspot (run 'dw wifi help')"
  450. echo " help Show this help message"
  451. echo ""
  452. if [[ -n "$INSTALL_DIR" ]]; then
  453. echo -e "Install directory: ${BLUE}$INSTALL_DIR${NC}"
  454. fi
  455. }
  456. # Main
  457. case "${1:-help}" in
  458. install)
  459. shift
  460. cmd_install "$@"
  461. ;;
  462. start)
  463. cmd_start
  464. ;;
  465. stop)
  466. cmd_stop
  467. ;;
  468. restart)
  469. cmd_restart
  470. ;;
  471. update)
  472. cmd_update "$2"
  473. ;;
  474. checkout)
  475. cmd_checkout "$2"
  476. ;;
  477. logs)
  478. cmd_logs "$2"
  479. ;;
  480. status)
  481. cmd_status
  482. ;;
  483. shell)
  484. cmd_shell
  485. ;;
  486. touch)
  487. shift
  488. cmd_touch "$@"
  489. ;;
  490. wifi)
  491. shift
  492. cmd_wifi "$@"
  493. ;;
  494. help|--help|-h)
  495. cmd_help
  496. ;;
  497. *)
  498. echo -e "${RED}Unknown command: $1${NC}"
  499. echo ""
  500. cmd_help
  501. exit 1
  502. ;;
  503. esac