setup-pi.sh 11 KB

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