setup-pi.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #!/bin/bash
  2. #
  3. # Dune Weaver Raspberry Pi Setup Script
  4. #
  5. # ONE-COMMAND INSTALL (recommended):
  6. # curl -fsSL https://raw.githubusercontent.com/tuanchris/dune-weaver/main/setup-pi.sh | bash
  7. #
  8. # OR from existing clone:
  9. # git clone https://github.com/tuanchris/dune-weaver --single-branch
  10. # cd dune-weaver
  11. # bash setup-pi.sh
  12. #
  13. # Options:
  14. # --no-wifi-fix Skip WiFi stability fix
  15. # --no-hotspot Skip autohotspot setup
  16. # --help Show help
  17. #
  18. set -e
  19. # Colors for output
  20. RED='\033[0;31m'
  21. GREEN='\033[0;32m'
  22. YELLOW='\033[1;33m'
  23. BLUE='\033[0;34m'
  24. NC='\033[0m' # No Color
  25. # Default options
  26. FIX_WIFI=true # Applied by default for stability
  27. SETUP_HOTSPOT=true # Autohotspot for first-time WiFi setup
  28. INSTALL_DIR="$HOME/dune-weaver"
  29. REPO_URL="https://github.com/tuanchris/dune-weaver"
  30. # Parse arguments
  31. while [[ $# -gt 0 ]]; do
  32. case $1 in
  33. --no-wifi-fix)
  34. FIX_WIFI=false
  35. shift
  36. ;;
  37. --no-hotspot)
  38. SETUP_HOTSPOT=false
  39. shift
  40. ;;
  41. --help|-h)
  42. echo "Dune Weaver Raspberry Pi Setup Script"
  43. echo ""
  44. echo "One-command install:"
  45. echo " curl -fsSL https://raw.githubusercontent.com/tuanchris/dune-weaver/main/setup-pi.sh | bash"
  46. echo ""
  47. echo "Or from existing clone:"
  48. echo " cd ~/dune-weaver && bash setup-pi.sh [OPTIONS]"
  49. echo ""
  50. echo "Options:"
  51. echo " --no-wifi-fix Skip WiFi stability fix (applied by default)"
  52. echo " --no-hotspot Skip autohotspot setup"
  53. echo " --help, -h Show this help message"
  54. exit 0
  55. ;;
  56. *)
  57. echo -e "${RED}Unknown option: $1${NC}"
  58. echo "Use --help for usage information"
  59. exit 1
  60. ;;
  61. esac
  62. done
  63. # Helper functions
  64. print_step() {
  65. echo -e "\n${BLUE}==>${NC} ${GREEN}$1${NC}"
  66. }
  67. print_warning() {
  68. echo -e "${YELLOW}Warning:${NC} $1"
  69. }
  70. print_error() {
  71. echo -e "${RED}Error:${NC} $1"
  72. }
  73. print_success() {
  74. echo -e "${GREEN}$1${NC}"
  75. }
  76. # Install system dependencies
  77. install_system_deps() {
  78. print_step "Installing system dependencies..."
  79. sudo apt update
  80. sudo DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \
  81. python3-venv python3-pip python3-dev \
  82. gcc g++ make swig unzip wget \
  83. libjpeg-dev zlib1g-dev \
  84. libgpiod-dev gpiod \
  85. nginx git vim
  86. print_success "System dependencies installed"
  87. }
  88. # Install lgpio C library from source (not packaged in Trixie)
  89. install_lgpio() {
  90. # Check if already installed
  91. if ldconfig -p | grep -q liblgpio; then
  92. echo "lgpio C library already installed"
  93. return
  94. fi
  95. print_step "Building lgpio C library from source..."
  96. local tmpdir
  97. tmpdir=$(mktemp -d)
  98. cd "$tmpdir"
  99. wget -q https://github.com/joan2937/lg/archive/master.zip
  100. unzip -q master.zip
  101. cd lg-master
  102. make
  103. sudo make install
  104. cd /
  105. rm -rf "$tmpdir"
  106. # joan2937/lg installs to /usr/local/lib/ but pip's build links
  107. # against /usr/lib/aarch64-linux-gnu/ — symlink so the linker finds it
  108. local syslib="/usr/lib/aarch64-linux-gnu"
  109. if [[ -f /usr/local/lib/liblgpio.so ]] && [[ ! -f "$syslib/liblgpio.so" ]]; then
  110. sudo ln -sf /usr/local/lib/liblgpio.so "$syslib/liblgpio.so"
  111. sudo ln -sf /usr/local/lib/liblgpio.a "$syslib/liblgpio.a" 2>/dev/null || true
  112. fi
  113. sudo ldconfig
  114. print_success "lgpio C library installed"
  115. }
  116. # Install Node.js 20 via nodesource
  117. install_nodejs() {
  118. print_step "Installing Node.js..."
  119. if command -v node &> /dev/null; then
  120. local node_version
  121. node_version=$(node --version)
  122. echo "Node.js already installed: $node_version"
  123. # Check if version is 20+
  124. local major
  125. major=$(echo "$node_version" | sed 's/v//' | cut -d. -f1)
  126. if [[ "$major" -ge 20 ]]; then
  127. print_success "Node.js version is sufficient"
  128. return
  129. fi
  130. echo "Upgrading to Node.js 20..."
  131. fi
  132. curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
  133. sudo DEBIAN_FRONTEND=noninteractive apt install -y nodejs
  134. print_success "Node.js $(node --version) installed"
  135. }
  136. # Check if running on Raspberry Pi
  137. check_raspberry_pi() {
  138. print_step "Checking system compatibility..."
  139. if [[ ! -f /proc/device-tree/model ]]; then
  140. print_warning "Could not detect Raspberry Pi model. Continuing anyway..."
  141. return
  142. fi
  143. MODEL=$(tr -d '\0' < /proc/device-tree/model)
  144. echo "Detected: $MODEL"
  145. # Check for 64-bit OS
  146. ARCH=$(uname -m)
  147. if [[ "$ARCH" != "aarch64" && "$ARCH" != "arm64" ]]; then
  148. print_error "64-bit OS required. Detected: $ARCH"
  149. echo "Please reinstall Raspberry Pi OS (64-bit) using Raspberry Pi Imager"
  150. exit 1
  151. fi
  152. print_success "64-bit OS detected ($ARCH)"
  153. }
  154. # Disable WLAN power save
  155. disable_wlan_powersave() {
  156. print_step "Disabling WLAN power save for better stability..."
  157. # Check if already disabled
  158. if iwconfig wlan0 2>/dev/null | grep -q "Power Management:off"; then
  159. echo "WLAN power save already disabled"
  160. return
  161. fi
  162. # Create config to persist across reboots
  163. sudo tee /etc/NetworkManager/conf.d/wifi-powersave-off.conf > /dev/null << 'EOF'
  164. [connection]
  165. wifi.powersave = 2
  166. EOF
  167. # Also try immediate disable
  168. sudo iwconfig wlan0 power off 2>/dev/null || true
  169. print_success "WLAN power save disabled"
  170. }
  171. # Apply WiFi stability fix
  172. apply_wifi_fix() {
  173. print_step "Applying WiFi stability fix..."
  174. CMDLINE_FILE="/boot/firmware/cmdline.txt"
  175. if [[ ! -f "$CMDLINE_FILE" ]]; then
  176. CMDLINE_FILE="/boot/cmdline.txt"
  177. fi
  178. if [[ ! -f "$CMDLINE_FILE" ]]; then
  179. print_warning "Could not find cmdline.txt, skipping WiFi fix"
  180. return
  181. fi
  182. # Check if fix already applied
  183. if grep -q "brcmfmac.feature_disable=0x82000" "$CMDLINE_FILE"; then
  184. echo "WiFi fix already applied"
  185. return
  186. fi
  187. # Backup and apply fix
  188. sudo cp "$CMDLINE_FILE" "${CMDLINE_FILE}.backup"
  189. sudo sed -i 's/$/ brcmfmac.feature_disable=0x82000/' "$CMDLINE_FILE"
  190. print_success "WiFi fix applied. A reboot is recommended after setup."
  191. NEEDS_REBOOT=true
  192. }
  193. # Update system packages
  194. update_system() {
  195. print_step "Updating system packages..."
  196. sudo apt update
  197. sudo DEBIAN_FRONTEND=noninteractive apt full-upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
  198. print_success "System updated"
  199. }
  200. # Verify we're in the dune-weaver directory
  201. ensure_repo() {
  202. print_step "Setting up dune-weaver repository..."
  203. # Check if we're already in the dune-weaver directory
  204. if [[ -f "main.py" ]] && [[ -f "requirements.txt" ]]; then
  205. INSTALL_DIR="$(pwd)"
  206. print_success "Using existing repo at $INSTALL_DIR"
  207. return
  208. fi
  209. # Check if repo exists in home directory
  210. if [[ -d "$INSTALL_DIR" ]] && [[ -f "$INSTALL_DIR/main.py" ]]; then
  211. print_success "Found existing repo at $INSTALL_DIR"
  212. cd "$INSTALL_DIR"
  213. echo "Pulling latest changes..."
  214. git pull
  215. return
  216. fi
  217. # Clone the repository
  218. print_step "Cloning dune-weaver repository..."
  219. git clone "$REPO_URL" --single-branch "$INSTALL_DIR"
  220. cd "$INSTALL_DIR"
  221. print_success "Cloned to $INSTALL_DIR"
  222. }
  223. # Deploy native (venv + systemd + nginx)
  224. deploy_native() {
  225. print_step "Setting up Python virtual environment..."
  226. cd "$INSTALL_DIR"
  227. # Create venv
  228. python3 -m venv .venv
  229. source .venv/bin/activate
  230. # Install dependencies
  231. print_step "Installing Python packages..."
  232. pip install --upgrade pip
  233. pip install -r requirements.txt
  234. # Build frontend
  235. print_step "Building frontend..."
  236. cd "$INSTALL_DIR/frontend"
  237. npm ci
  238. npm run build
  239. cd "$INSTALL_DIR"
  240. # Configure nginx
  241. print_step "Configuring nginx..."
  242. sudo cp "$INSTALL_DIR/nginx/dune-weaver.conf" /etc/nginx/sites-available/dune-weaver.conf
  243. sudo sed -i "s|INSTALL_DIR_PLACEHOLDER|$INSTALL_DIR|g" /etc/nginx/sites-available/dune-weaver.conf
  244. sudo ln -sf /etc/nginx/sites-available/dune-weaver.conf /etc/nginx/sites-enabled/dune-weaver.conf
  245. sudo rm -f /etc/nginx/sites-enabled/default
  246. sudo nginx -t
  247. sudo systemctl restart nginx
  248. sudo systemctl enable nginx
  249. # Create systemd service
  250. print_step "Creating systemd service..."
  251. sudo cp "$INSTALL_DIR/dune-weaver.service" /etc/systemd/system/dune-weaver.service
  252. sudo sed -i "s|USER_PLACEHOLDER|$USER|g" /etc/systemd/system/dune-weaver.service
  253. sudo sed -i "s|INSTALL_DIR_PLACEHOLDER|$INSTALL_DIR|g" /etc/systemd/system/dune-weaver.service
  254. # Enable and start service
  255. sudo systemctl daemon-reload
  256. sudo systemctl enable dune-weaver
  257. sudo systemctl start dune-weaver
  258. # Create sudoers entry for passwordless systemctl commands
  259. print_step "Configuring sudo permissions..."
  260. sudo tee /etc/sudoers.d/dune-weaver > /dev/null << EOF
  261. $USER ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart dune-weaver
  262. $USER ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop dune-weaver
  263. $USER ALL=(ALL) NOPASSWD: /usr/bin/systemctl start dune-weaver
  264. $USER ALL=(ALL) NOPASSWD: /usr/bin/systemctl poweroff
  265. $USER ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
  266. EOF
  267. sudo chmod 0440 /etc/sudoers.d/dune-weaver
  268. print_success "Native deployment complete!"
  269. }
  270. # Install dw CLI command
  271. install_cli() {
  272. print_step "Installing 'dw' command..."
  273. # Copy dw script to /usr/local/bin
  274. sudo cp "$INSTALL_DIR/dw" /usr/local/bin/dw
  275. sudo chmod +x /usr/local/bin/dw
  276. print_success "'dw' command installed"
  277. }
  278. # Setup autohotspot
  279. setup_autohotspot() {
  280. print_step "Setting up autohotspot..."
  281. if [[ ! -f "$INSTALL_DIR/wifi/setup-wifi.sh" ]]; then
  282. print_warning "wifi/setup-wifi.sh not found, skipping autohotspot setup"
  283. return
  284. fi
  285. bash "$INSTALL_DIR/wifi/setup-wifi.sh"
  286. print_success "Autohotspot setup complete"
  287. }
  288. # Get IP address
  289. get_ip_address() {
  290. # Try multiple methods to get IP
  291. IP=$(hostname -I 2>/dev/null | awk '{print $1}')
  292. if [[ -z "$IP" ]]; then
  293. IP=$(ip route get 1 2>/dev/null | awk '{print $7}' | head -1)
  294. fi
  295. if [[ -z "$IP" ]]; then
  296. IP="<your-pi-ip>"
  297. fi
  298. echo "$IP"
  299. }
  300. # Print final instructions
  301. print_final_instructions() {
  302. IP=$(get_ip_address)
  303. HOSTNAME=$(hostname)
  304. echo ""
  305. echo -e "${GREEN}============================================${NC}"
  306. echo -e "${GREEN} Dune Weaver Setup Complete!${NC}"
  307. echo -e "${GREEN}============================================${NC}"
  308. echo ""
  309. echo -e "Access the web interface at:"
  310. echo -e " ${BLUE}http://$IP${NC}"
  311. echo -e " ${BLUE}http://$HOSTNAME.local${NC}"
  312. echo ""
  313. echo "Manage with the 'dw' command:"
  314. echo " dw logs View live logs"
  315. echo " dw restart Restart Dune Weaver"
  316. echo " dw update Pull latest and restart"
  317. echo " dw stop Stop Dune Weaver"
  318. echo " dw status Show status"
  319. echo " dw wifi help WiFi and hotspot management"
  320. echo " dw help Show all commands"
  321. echo ""
  322. if [[ "$SETUP_HOTSPOT" == "true" ]]; then
  323. echo -e "${BLUE}Autohotspot:${NC} If no known WiFi is found on boot,"
  324. echo "a 'Dune Weaver' hotspot will be created automatically."
  325. echo "Connect to it and open the app to configure WiFi."
  326. echo ""
  327. fi
  328. if [[ "$NEEDS_REBOOT" == "true" ]]; then
  329. print_warning "A reboot is recommended to apply WiFi fixes"
  330. read -p "Reboot now? (y/N) " -n 1 -r
  331. echo
  332. if [[ $REPLY =~ ^[Yy]$ ]]; then
  333. sudo reboot
  334. fi
  335. fi
  336. }
  337. # Main installation flow
  338. main() {
  339. echo -e "${GREEN}"
  340. echo " ____ __ __ "
  341. echo " | _ \ _ _ _ __ ___\ \ / /__ __ ___ _____ _ __ "
  342. echo " | | | | | | | '_ \ / _ \\ \ /\ / / _ \/ _\` \ \ / / _ \ '__|"
  343. echo " | |_| | |_| | | | | __/ \ V V / __/ (_| |\ V / __/ | "
  344. echo " |____/ \__,_|_| |_|\___| \_/\_/ \___|\__,_| \_/ \___|_| "
  345. echo -e "${NC}"
  346. echo "Raspberry Pi Setup Script"
  347. echo ""
  348. echo "Install directory: $INSTALL_DIR"
  349. echo ""
  350. # Run setup steps
  351. check_raspberry_pi
  352. install_system_deps
  353. install_nodejs
  354. ensure_repo
  355. update_system
  356. disable_wlan_powersave
  357. if [[ "$FIX_WIFI" == "true" ]]; then
  358. apply_wifi_fix
  359. fi
  360. if [[ "$SETUP_HOTSPOT" == "true" ]]; then
  361. setup_autohotspot
  362. fi
  363. install_lgpio
  364. deploy_native
  365. install_cli
  366. print_final_instructions
  367. }
  368. # Run main
  369. main