675 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			675 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/bin/bash
 | 
						||
 | 
						||
# Turmli Bar Calendar Tool - Raspberry Pi Zero Deployment Script
 | 
						||
# Deploys the application as a systemd service without Docker
 | 
						||
# Optimized for low resource usage on Raspberry Pi Zero
 | 
						||
 | 
						||
set -e  # Exit on error
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Configuration
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
APP_NAME="turmli-calendar"
 | 
						||
APP_DIR="/opt/turmli-calendar"
 | 
						||
SERVICE_NAME="turmli-calendar.service"
 | 
						||
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}"
 | 
						||
APP_USER="pi"
 | 
						||
APP_PORT="8000"
 | 
						||
PYTHON_VERSION="3"
 | 
						||
REPO_DIR="$(dirname "$(readlink -f "$0")")"
 | 
						||
 | 
						||
# Colors for output
 | 
						||
RED='\033[0;31m'
 | 
						||
GREEN='\033[0;32m'
 | 
						||
YELLOW='\033[1;33m'
 | 
						||
BLUE='\033[0;34m'
 | 
						||
MAGENTA='\033[0;35m'
 | 
						||
CYAN='\033[0;36m'
 | 
						||
NC='\033[0m' # No Color
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Helper Functions
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
print_header() {
 | 
						||
    echo
 | 
						||
    echo -e "${MAGENTA}╔════════════════════════════════════════════╗${NC}"
 | 
						||
    echo -e "${MAGENTA}║  Turmli Calendar - Raspberry Pi Deployment ║${NC}"
 | 
						||
    echo -e "${MAGENTA}╚════════════════════════════════════════════╝${NC}"
 | 
						||
    echo
 | 
						||
}
 | 
						||
 | 
						||
print_section() {
 | 
						||
    echo
 | 
						||
    echo -e "${CYAN}━━━ $1 ━━━${NC}"
 | 
						||
    echo
 | 
						||
}
 | 
						||
 | 
						||
print_success() {
 | 
						||
    echo -e "${GREEN}✓${NC} $1"
 | 
						||
}
 | 
						||
 | 
						||
print_error() {
 | 
						||
    echo -e "${RED}✗${NC} $1"
 | 
						||
}
 | 
						||
 | 
						||
print_warning() {
 | 
						||
    echo -e "${YELLOW}⚠${NC} $1"
 | 
						||
}
 | 
						||
 | 
						||
print_info() {
 | 
						||
    echo -e "${BLUE}ℹ${NC} $1"
 | 
						||
}
 | 
						||
 | 
						||
check_root() {
 | 
						||
    if [[ $EUID -ne 0 ]]; then
 | 
						||
        print_error "This script must be run as root (use sudo)"
 | 
						||
        exit 1
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
check_os() {
 | 
						||
    if ! grep -q "Raspbian\|Raspberry Pi OS" /etc/os-release 2>/dev/null; then
 | 
						||
        print_warning "This script is optimized for Raspberry Pi OS"
 | 
						||
        read -p "Continue anyway? (y/N): " -n 1 -r
 | 
						||
        echo
 | 
						||
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
 | 
						||
            exit 1
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# System Checks and Prerequisites
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
check_system() {
 | 
						||
    print_section "System Check"
 | 
						||
 | 
						||
    # Check if running as root
 | 
						||
    check_root
 | 
						||
 | 
						||
    # Check OS
 | 
						||
    check_os
 | 
						||
 | 
						||
    # Check available memory
 | 
						||
    local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
 | 
						||
    if [ "$mem_total" -lt 400000 ]; then
 | 
						||
        print_warning "Low memory detected ($(($mem_total/1024))MB). This is expected for Pi Zero."
 | 
						||
    fi
 | 
						||
 | 
						||
    # Check available disk space
 | 
						||
    local disk_free=$(df /opt 2>/dev/null | tail -1 | awk '{print $4}')
 | 
						||
    if [ "$disk_free" -lt 500000 ]; then
 | 
						||
        print_warning "Low disk space available. Need at least 500MB free."
 | 
						||
        print_info "Current free space: $(($disk_free/1024))MB"
 | 
						||
    fi
 | 
						||
 | 
						||
    # Check /tmp space
 | 
						||
    local tmp_free=$(df /tmp 2>/dev/null | tail -1 | awk '{print $4}')
 | 
						||
    if [ "$tmp_free" -lt 200000 ]; then
 | 
						||
        print_warning "/tmp has limited space. Will use alternative temp directory."
 | 
						||
    fi
 | 
						||
 | 
						||
    # Check Python
 | 
						||
    if ! command -v python${PYTHON_VERSION} &> /dev/null; then
 | 
						||
        print_error "Python ${PYTHON_VERSION} is not installed"
 | 
						||
        return 1
 | 
						||
    fi
 | 
						||
    print_success "Python $(python${PYTHON_VERSION} --version 2>&1 | cut -d' ' -f2) installed"
 | 
						||
 | 
						||
    # Check pip
 | 
						||
    if ! python${PYTHON_VERSION} -m pip --version &> /dev/null; then
 | 
						||
        print_warning "pip not found, installing..."
 | 
						||
        apt-get update
 | 
						||
        apt-get install -y python3-pip
 | 
						||
    fi
 | 
						||
    print_success "pip installed"
 | 
						||
 | 
						||
    # Check git (optional, for updates)
 | 
						||
    if command -v git &> /dev/null; then
 | 
						||
        print_success "git installed (optional)"
 | 
						||
    else
 | 
						||
        print_info "git not installed (optional, needed for updates)"
 | 
						||
    fi
 | 
						||
 | 
						||
    return 0
 | 
						||
}
 | 
						||
 | 
						||
install_dependencies() {
 | 
						||
    print_section "Installing System Dependencies"
 | 
						||
 | 
						||
    # Update package list
 | 
						||
    print_info "Updating package list..."
 | 
						||
    apt-get update
 | 
						||
 | 
						||
    # Install system dependencies
 | 
						||
    print_info "Installing system packages..."
 | 
						||
    apt-get install -y \
 | 
						||
        python3-dev \
 | 
						||
        python3-venv \
 | 
						||
        python3-pip \
 | 
						||
        build-essential \
 | 
						||
        libffi-dev \
 | 
						||
        libssl-dev \
 | 
						||
        libxml2-dev \
 | 
						||
        libxslt1-dev \
 | 
						||
        curl \
 | 
						||
        systemd
 | 
						||
 | 
						||
    # Clean apt cache to free up space
 | 
						||
    apt-get clean
 | 
						||
    apt-get autoremove -y
 | 
						||
 | 
						||
    print_success "System dependencies installed"
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Application Setup
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
create_app_directory() {
 | 
						||
    print_section "Creating Application Directory"
 | 
						||
 | 
						||
    # Create directory
 | 
						||
    if [ -d "$APP_DIR" ]; then
 | 
						||
        print_warning "Directory $APP_DIR already exists"
 | 
						||
        read -p "Remove existing installation? (y/N): " -n 1 -r
 | 
						||
        echo
 | 
						||
        if [[ $REPLY =~ ^[Yy]$ ]]; then
 | 
						||
            print_info "Removing existing installation..."
 | 
						||
            systemctl stop ${SERVICE_NAME} 2>/dev/null || true
 | 
						||
            rm -rf "$APP_DIR"
 | 
						||
        else
 | 
						||
            print_error "Installation cancelled"
 | 
						||
            exit 1
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
 | 
						||
    mkdir -p "$APP_DIR"
 | 
						||
    print_success "Created $APP_DIR"
 | 
						||
}
 | 
						||
 | 
						||
copy_application_files() {
 | 
						||
    print_section "Copying Application Files"
 | 
						||
 | 
						||
    # Copy necessary files
 | 
						||
    print_info "Copying application files..."
 | 
						||
    cp -v "$REPO_DIR/main.py" "$APP_DIR/"
 | 
						||
 | 
						||
    # Use RPi-optimized requirements if available, otherwise fallback to standard
 | 
						||
    if [ -f "$REPO_DIR/requirements-rpi.txt" ]; then
 | 
						||
        print_info "Using Raspberry Pi optimized requirements"
 | 
						||
        cp -v "$REPO_DIR/requirements-rpi.txt" "$APP_DIR/requirements.txt"
 | 
						||
    else
 | 
						||
        cp -v "$REPO_DIR/requirements.txt" "$APP_DIR/"
 | 
						||
    fi
 | 
						||
 | 
						||
    # Copy logo if exists
 | 
						||
    if [ -f "$REPO_DIR/Vektor-Logo.svg" ]; then
 | 
						||
        cp -v "$REPO_DIR/Vektor-Logo.svg" "$APP_DIR/"
 | 
						||
    fi
 | 
						||
 | 
						||
    # Create static directory
 | 
						||
    mkdir -p "$APP_DIR/static"
 | 
						||
    if [ -d "$REPO_DIR/static" ]; then
 | 
						||
        cp -r "$REPO_DIR/static/"* "$APP_DIR/static/" 2>/dev/null || true
 | 
						||
    fi
 | 
						||
 | 
						||
    # Copy logo to static directory
 | 
						||
    if [ -f "$APP_DIR/Vektor-Logo.svg" ]; then
 | 
						||
        cp "$APP_DIR/Vektor-Logo.svg" "$APP_DIR/static/logo.svg"
 | 
						||
    fi
 | 
						||
 | 
						||
    # Create empty cache file
 | 
						||
    touch "$APP_DIR/calendar_cache.json"
 | 
						||
 | 
						||
    # Set ownership
 | 
						||
    chown -R ${APP_USER}:${APP_USER} "$APP_DIR"
 | 
						||
 | 
						||
    print_success "Application files copied"
 | 
						||
}
 | 
						||
 | 
						||
setup_python_environment() {
 | 
						||
    print_section "Setting Up Python Environment"
 | 
						||
 | 
						||
    # Create virtual environment to isolate dependencies
 | 
						||
    print_info "Creating Python virtual environment..."
 | 
						||
    sudo -u ${APP_USER} python${PYTHON_VERSION} -m venv "$APP_DIR/venv"
 | 
						||
 | 
						||
    # Upgrade pip in virtual environment
 | 
						||
    print_info "Upgrading pip..."
 | 
						||
    sudo -u ${APP_USER} "$APP_DIR/venv/bin/pip" install --upgrade pip wheel setuptools
 | 
						||
 | 
						||
    # Set temporary directory to avoid filling up /tmp (which is in RAM on Pi)
 | 
						||
    export TMPDIR="$APP_DIR/tmp"
 | 
						||
    mkdir -p "$TMPDIR"
 | 
						||
    chown ${APP_USER}:${APP_USER} "$TMPDIR"
 | 
						||
 | 
						||
    # Install requirements
 | 
						||
    print_info "Installing Python dependencies (this may take a while on Pi Zero)..."
 | 
						||
    print_warning "This process might take 10-20 minutes on a Raspberry Pi Zero"
 | 
						||
 | 
						||
    # Install with optimizations for low memory and ARM architecture
 | 
						||
    sudo -u ${APP_USER} TMPDIR="$TMPDIR" "$APP_DIR/venv/bin/pip" install \
 | 
						||
        --no-cache-dir \
 | 
						||
        --prefer-binary \
 | 
						||
        --no-build-isolation \
 | 
						||
        --no-deps \
 | 
						||
        -r "$APP_DIR/requirements.txt"
 | 
						||
 | 
						||
    # Install dependencies separately to handle failures better
 | 
						||
    sudo -u ${APP_USER} TMPDIR="$TMPDIR" "$APP_DIR/venv/bin/pip" install \
 | 
						||
        --no-cache-dir \
 | 
						||
        --prefer-binary \
 | 
						||
        --no-build-isolation \
 | 
						||
        -r "$APP_DIR/requirements.txt"
 | 
						||
 | 
						||
    # Clean up temp directory
 | 
						||
    rm -rf "$TMPDIR"
 | 
						||
 | 
						||
    print_success "Python environment set up"
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Systemd Service Setup
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
create_systemd_service() {
 | 
						||
    print_section "Creating Systemd Service"
 | 
						||
 | 
						||
    # Create service file
 | 
						||
    cat > "$SERVICE_FILE" << EOF
 | 
						||
[Unit]
 | 
						||
Description=Turmli Bar Calendar Tool
 | 
						||
Documentation=https://github.com/turmli/bar-calendar-tool
 | 
						||
After=network.target network-online.target
 | 
						||
Wants=network-online.target
 | 
						||
 | 
						||
[Service]
 | 
						||
Type=simple
 | 
						||
User=${APP_USER}
 | 
						||
Group=${APP_USER}
 | 
						||
WorkingDirectory=${APP_DIR}
 | 
						||
Environment="PATH=/opt/turmli-calendar/venv/bin:/home/turmli/.local/bin:/usr/local/bin:/usr/bin:/bin"
 | 
						||
Environment="PYTHONPATH=${APP_DIR}"
 | 
						||
Environment="TZ=Europe/Berlin"
 | 
						||
 | 
						||
# Use virtual environment Python with uvicorn
 | 
						||
ExecStart=${APP_DIR}/venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port ${APP_PORT} --workers 1 --log-level info
 | 
						||
 | 
						||
# Restart policy
 | 
						||
Restart=always
 | 
						||
RestartSec=10
 | 
						||
StartLimitInterval=200
 | 
						||
StartLimitBurst=5
 | 
						||
 | 
						||
# Resource limits for Raspberry Pi Zero
 | 
						||
MemoryMax=256M
 | 
						||
CPUQuota=75%
 | 
						||
 | 
						||
# Security hardening
 | 
						||
PrivateTmp=true
 | 
						||
NoNewPrivileges=true
 | 
						||
ProtectSystem=strict
 | 
						||
ProtectHome=true
 | 
						||
ReadWritePaths=${APP_DIR}/calendar_cache.json ${APP_DIR}/static
 | 
						||
 | 
						||
# Logging
 | 
						||
StandardOutput=journal
 | 
						||
StandardError=journal
 | 
						||
SyslogIdentifier=${APP_NAME}
 | 
						||
 | 
						||
[Install]
 | 
						||
WantedBy=multi-user.target
 | 
						||
EOF
 | 
						||
 | 
						||
    print_success "Service file created at $SERVICE_FILE"
 | 
						||
 | 
						||
    # Reload systemd
 | 
						||
    systemctl daemon-reload
 | 
						||
    print_success "Systemd configuration reloaded"
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Service Management Functions
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
start_service() {
 | 
						||
    print_section "Starting Service"
 | 
						||
 | 
						||
    systemctl enable ${SERVICE_NAME}
 | 
						||
    systemctl start ${SERVICE_NAME}
 | 
						||
 | 
						||
    # Wait for service to start
 | 
						||
    sleep 3
 | 
						||
 | 
						||
    if systemctl is-active --quiet ${SERVICE_NAME}; then
 | 
						||
        print_success "Service started successfully"
 | 
						||
 | 
						||
        # Test if application is responding
 | 
						||
        sleep 2
 | 
						||
        if curl -s -f "http://localhost:${APP_PORT}/api/events" > /dev/null 2>&1; then
 | 
						||
            print_success "Application is responding on port ${APP_PORT}"
 | 
						||
        else
 | 
						||
            print_warning "Service is running but not responding yet (may still be starting)"
 | 
						||
        fi
 | 
						||
    else
 | 
						||
        print_error "Failed to start service"
 | 
						||
        print_info "Check logs with: journalctl -u ${SERVICE_NAME} -n 50"
 | 
						||
        return 1
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
stop_service() {
 | 
						||
    print_section "Stopping Service"
 | 
						||
 | 
						||
    if systemctl is-active --quiet ${SERVICE_NAME}; then
 | 
						||
        systemctl stop ${SERVICE_NAME}
 | 
						||
        print_success "Service stopped"
 | 
						||
    else
 | 
						||
        print_info "Service is not running"
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
restart_service() {
 | 
						||
    print_section "Restarting Service"
 | 
						||
 | 
						||
    systemctl restart ${SERVICE_NAME}
 | 
						||
    sleep 2
 | 
						||
 | 
						||
    if systemctl is-active --quiet ${SERVICE_NAME}; then
 | 
						||
        print_success "Service restarted successfully"
 | 
						||
    else
 | 
						||
        print_error "Failed to restart service"
 | 
						||
        return 1
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Status and Monitoring
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
show_status() {
 | 
						||
    print_section "Service Status"
 | 
						||
 | 
						||
    # Show service status
 | 
						||
    systemctl status ${SERVICE_NAME} --no-pager
 | 
						||
 | 
						||
    echo
 | 
						||
    print_section "Application Health"
 | 
						||
 | 
						||
    # Check if responding
 | 
						||
    if curl -s -f "http://localhost:${APP_PORT}/api/events" > /dev/null 2>&1; then
 | 
						||
        print_success "Application is healthy and responding"
 | 
						||
 | 
						||
        # Get event count
 | 
						||
        local events=$(curl -s "http://localhost:${APP_PORT}/api/events" | python3 -c "import sys, json; data=json.load(sys.stdin); print(len(data.get('events', [])))" 2>/dev/null || echo "unknown")
 | 
						||
        print_info "Calendar has $events events"
 | 
						||
    else
 | 
						||
        print_error "Application is not responding"
 | 
						||
    fi
 | 
						||
 | 
						||
    # Show resource usage
 | 
						||
    echo
 | 
						||
    print_section "Resource Usage"
 | 
						||
 | 
						||
    local pid=$(systemctl show ${SERVICE_NAME} -p MainPID --value)
 | 
						||
    if [ "$pid" != "0" ]; then
 | 
						||
        if [ -f "/proc/$pid/status" ]; then
 | 
						||
            local mem_usage=$(grep VmRSS /proc/$pid/status | awk '{print $2/1024 " MB"}')
 | 
						||
            print_info "Memory usage: $mem_usage"
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
 | 
						||
    # Show recent logs
 | 
						||
    echo
 | 
						||
    print_section "Recent Logs"
 | 
						||
    journalctl -u ${SERVICE_NAME} -n 20 --no-pager
 | 
						||
}
 | 
						||
 | 
						||
show_logs() {
 | 
						||
    print_section "Application Logs"
 | 
						||
    journalctl -u ${SERVICE_NAME} -f
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Update Function
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
update_application() {
 | 
						||
    print_section "Updating Application"
 | 
						||
 | 
						||
    # Stop service
 | 
						||
    stop_service
 | 
						||
 | 
						||
    # Backup current installation
 | 
						||
    print_info "Creating backup..."
 | 
						||
    cp -r "$APP_DIR" "${APP_DIR}.backup.$(date +%Y%m%d_%H%M%S)"
 | 
						||
 | 
						||
    # Copy new files
 | 
						||
    print_info "Copying updated files..."
 | 
						||
    cp -v "$REPO_DIR/main.py" "$APP_DIR/"
 | 
						||
 | 
						||
    # Use RPi-optimized requirements if available
 | 
						||
    if [ -f "$REPO_DIR/requirements-rpi.txt" ]; then
 | 
						||
        cp -v "$REPO_DIR/requirements-rpi.txt" "$APP_DIR/requirements.txt"
 | 
						||
    else
 | 
						||
        cp -v "$REPO_DIR/requirements.txt" "$APP_DIR/"
 | 
						||
    fi
 | 
						||
 | 
						||
    if [ -f "$REPO_DIR/Vektor-Logo.svg" ]; then
 | 
						||
        cp -v "$REPO_DIR/Vektor-Logo.svg" "$APP_DIR/"
 | 
						||
        cp "$APP_DIR/Vektor-Logo.svg" "$APP_DIR/static/logo.svg"
 | 
						||
    fi
 | 
						||
 | 
						||
    # Update dependencies with temp directory
 | 
						||
    print_info "Updating Python dependencies..."
 | 
						||
    export TMPDIR="$APP_DIR/tmp"
 | 
						||
    mkdir -p "$TMPDIR"
 | 
						||
    chown ${APP_USER}:${APP_USER} "$TMPDIR"
 | 
						||
 | 
						||
    sudo -u ${APP_USER} TMPDIR="$TMPDIR" "$APP_DIR/venv/bin/pip" install \
 | 
						||
        --no-cache-dir \
 | 
						||
        --prefer-binary \
 | 
						||
        --no-build-isolation \
 | 
						||
        -r "$APP_DIR/requirements.txt"
 | 
						||
 | 
						||
    # Clean up temp directory
 | 
						||
    rm -rf "$TMPDIR"
 | 
						||
 | 
						||
    # Set ownership
 | 
						||
    chown -R ${APP_USER}:${APP_USER} "$APP_DIR"
 | 
						||
 | 
						||
    # Restart service
 | 
						||
    start_service
 | 
						||
 | 
						||
    print_success "Application updated successfully"
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Uninstall Function
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
uninstall() {
 | 
						||
    print_section "Uninstalling Application"
 | 
						||
 | 
						||
    print_warning "This will remove the Turmli Calendar application"
 | 
						||
    read -p "Are you sure? (y/N): " -n 1 -r
 | 
						||
    echo
 | 
						||
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
 | 
						||
        print_info "Uninstall cancelled"
 | 
						||
        return
 | 
						||
    fi
 | 
						||
 | 
						||
    # Stop and disable service
 | 
						||
    systemctl stop ${SERVICE_NAME} 2>/dev/null || true
 | 
						||
    systemctl disable ${SERVICE_NAME} 2>/dev/null || true
 | 
						||
 | 
						||
    # Remove service file
 | 
						||
    rm -f "$SERVICE_FILE"
 | 
						||
    systemctl daemon-reload
 | 
						||
 | 
						||
    # Remove application directory
 | 
						||
    rm -rf "$APP_DIR"
 | 
						||
 | 
						||
    print_success "Application uninstalled"
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Performance Optimization for Pi Zero
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
optimize_for_pi_zero() {
 | 
						||
    print_section "Optimizing for Raspberry Pi Zero"
 | 
						||
 | 
						||
    # Disable unnecessary services to free up resources
 | 
						||
    print_info "Checking for unnecessary services..."
 | 
						||
 | 
						||
    local services_to_check="bluetooth cups avahi-daemon triggerhappy"
 | 
						||
    for service in $services_to_check; do
 | 
						||
        if systemctl is-enabled --quiet "$service" 2>/dev/null; then
 | 
						||
            print_info "Consider disabling $service to free resources"
 | 
						||
        fi
 | 
						||
    done
 | 
						||
 | 
						||
    # Configure swap if needed
 | 
						||
    local swap_total=$(grep SwapTotal /proc/meminfo | awk '{print $2}')
 | 
						||
    if [ "$swap_total" -lt 100000 ]; then
 | 
						||
        print_warning "Low swap space detected. Consider increasing swap size."
 | 
						||
        print_info "Edit /etc/dphys-swapfile and set CONF_SWAPSIZE=256"
 | 
						||
    fi
 | 
						||
 | 
						||
    # Set CPU governor for better performance
 | 
						||
    if [ -f /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor ]; then
 | 
						||
        local governor=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)
 | 
						||
        if [ "$governor" != "performance" ]; then
 | 
						||
            print_info "Current CPU governor: $governor"
 | 
						||
            print_info "Consider setting to 'performance' for better response times"
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
 | 
						||
    print_success "Optimization check complete"
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Main Installation Function
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
install() {
 | 
						||
    print_header
 | 
						||
 | 
						||
    # Run checks
 | 
						||
    check_system || exit 1
 | 
						||
 | 
						||
    # Install dependencies
 | 
						||
    install_dependencies
 | 
						||
 | 
						||
    # Setup application
 | 
						||
    create_app_directory
 | 
						||
    copy_application_files
 | 
						||
    setup_python_environment
 | 
						||
 | 
						||
    # Setup service
 | 
						||
    create_systemd_service
 | 
						||
 | 
						||
    # Optimize for Pi Zero
 | 
						||
    optimize_for_pi_zero
 | 
						||
 | 
						||
    # Start service
 | 
						||
    start_service
 | 
						||
 | 
						||
    # Show final status
 | 
						||
    print_section "Installation Complete!"
 | 
						||
    print_success "Turmli Calendar is now running"
 | 
						||
    echo
 | 
						||
    print_info "Access the calendar at:"
 | 
						||
    print_info "  http://$(hostname -I | cut -d' ' -f1):${APP_PORT}"
 | 
						||
    print_info "  http://localhost:${APP_PORT}"
 | 
						||
    echo
 | 
						||
    print_info "Useful commands:"
 | 
						||
    print_info "  sudo systemctl status ${SERVICE_NAME}  - Check status"
 | 
						||
    print_info "  sudo systemctl restart ${SERVICE_NAME} - Restart service"
 | 
						||
    print_info "  sudo journalctl -u ${SERVICE_NAME} -f  - View logs"
 | 
						||
    print_info "  sudo $0 status                         - Show detailed status"
 | 
						||
    echo
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Command Line Interface
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
print_usage() {
 | 
						||
    echo "Usage: sudo $0 {install|start|stop|restart|status|logs|update|uninstall|optimize}"
 | 
						||
    echo
 | 
						||
    echo "Commands:"
 | 
						||
    echo "  install    - Full installation (first time setup)"
 | 
						||
    echo "  start      - Start the service"
 | 
						||
    echo "  stop       - Stop the service"
 | 
						||
    echo "  restart    - Restart the service"
 | 
						||
    echo "  status     - Show service status and health"
 | 
						||
    echo "  logs       - Follow application logs"
 | 
						||
    echo "  update     - Update application from current directory"
 | 
						||
    echo "  uninstall  - Remove application completely"
 | 
						||
    echo "  optimize   - Check and suggest optimizations for Pi Zero"
 | 
						||
    echo
 | 
						||
    echo "Examples:"
 | 
						||
    echo "  sudo $0 install           # Initial installation"
 | 
						||
    echo "  sudo $0 status            # Check if running properly"
 | 
						||
    echo "  sudo $0 logs              # View real-time logs"
 | 
						||
    echo
 | 
						||
    echo "Configuration:"
 | 
						||
    echo "  Port: ${APP_PORT}"
 | 
						||
    echo "  Directory: ${APP_DIR}"
 | 
						||
    echo "  Service: ${SERVICE_NAME}"
 | 
						||
}
 | 
						||
 | 
						||
# ============================================================================
 | 
						||
# Main Script Entry Point
 | 
						||
# ============================================================================
 | 
						||
 | 
						||
case "$1" in
 | 
						||
    install)
 | 
						||
        check_root
 | 
						||
        install
 | 
						||
        ;;
 | 
						||
    start)
 | 
						||
        check_root
 | 
						||
        start_service
 | 
						||
        ;;
 | 
						||
    stop)
 | 
						||
        check_root
 | 
						||
        stop_service
 | 
						||
        ;;
 | 
						||
    restart)
 | 
						||
        check_root
 | 
						||
        restart_service
 | 
						||
        ;;
 | 
						||
    status)
 | 
						||
        show_status
 | 
						||
        ;;
 | 
						||
    logs)
 | 
						||
        show_logs
 | 
						||
        ;;
 | 
						||
    update)
 | 
						||
        check_root
 | 
						||
        update_application
 | 
						||
        ;;
 | 
						||
    uninstall)
 | 
						||
        check_root
 | 
						||
        uninstall
 | 
						||
        ;;
 | 
						||
    optimize)
 | 
						||
        check_root
 | 
						||
        optimize_for_pi_zero
 | 
						||
        ;;
 | 
						||
    *)
 | 
						||
        print_header
 | 
						||
        print_usage
 | 
						||
        exit 1
 | 
						||
        ;;
 | 
						||
esac
 | 
						||
 | 
						||
exit 0
 |