Files
moduleair_pro_4g/installation.sh
2025-06-24 12:15:23 +02:00

710 lines
22 KiB
Bash

#!/bin/bash
# ModuleAir Pro 4G - Complete Installation Script
# Author: ModuleAir Team
# Description: Automated deployment script for Raspberry Pi with colored output
set -e # Exit on any error
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
# Function to print colored messages
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_header() {
echo -e "${PURPLE}================================${NC}"
echo -e "${WHITE}$1${NC}"
echo -e "${PURPLE}================================${NC}"
}
print_step() {
echo -e "${CYAN}[STEP]${NC} $1"
}
# Check if running as root
check_root() {
if [[ $EUID -eq 0 ]]; then
print_error "This script should not be run as root. Run as regular user with sudo privileges."
exit 1
fi
}
# Check if sudo is available
check_sudo() {
print_step "Checking sudo privileges..."
if ! sudo -n true 2>/dev/null; then
print_error "This script requires sudo privileges. Please run: sudo visudo"
exit 1
fi
print_success "Sudo privileges confirmed"
}
# Pre-configure packages to avoid prompts
pre_configure_packages() {
print_step "Pre-configuring packages to avoid prompts..."
# Pre-configure common package questions
# For any package that uses debconf
echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections
# For timezone data (if tzdata gets updated)
echo 'tzdata tzdata/Areas select Europe' | sudo debconf-set-selections
echo 'tzdata tzdata/Zones/Europe select Paris' | sudo debconf-set-selections
# For keyboard configuration
echo 'keyboard-configuration keyboard-configuration/layoutcode string us' | sudo debconf-set-selections
# For any service restart prompts
echo '* libraries/restart-without-asking boolean true' | sudo debconf-set-selections
print_success "Package pre-configuration completed"
}
# System update
update_system() {
print_step "Updating system packages..."
# Set non-interactive mode for apt
export DEBIAN_FRONTEND=noninteractive
sudo apt update -y
print_success "System packages updated"
}
# Install dependencies
install_dependencies() {
print_step "Installing system dependencies..."
# Ensure non-interactive mode
export DEBIAN_FRONTEND=noninteractive
local packages=(
"git"
"apache2"
"php"
"php-sqlite3"
"sqlite3"
"python3"
"python3-pip"
"python3-dev"
"build-essential"
"cmake"
"libsqlite3-dev"
"i2c-tools"
"python3-smbus"
"python3-serial"
"python3-requests"
"python3-schedule"
"python3-rpi.gpio"
)
for package in "${packages[@]}"; do
print_status "Installing $package..."
if sudo DEBIAN_FRONTEND=noninteractive apt install -y -q "$package"; then
print_success "$package installed"
else
print_error "Failed to install $package"
exit 1
fi
done
print_success "All system dependencies installed"
}
# Install Python packages
install_python_packages() {
print_step "Installing Python packages..."
local pip_packages=(
"pymodbus"
"pyserial"
"requests"
"schedule"
"gpiozero"
"smbus2"
"adafruit-circuitpython-bme280"
"sensirion-shdlc-sfa3x"
"crcmod"
"psutil"
)
for package in "${pip_packages[@]}"; do
print_status "Installing Python package: $package..."
if sudo pip3 install "$package" --break-system-packages; then
print_success "$package installed"
else
print_warning "Failed to install $package (may already be installed)"
fi
done
print_success "Python packages installation completed"
}
# Clone repository
clone_repository() {
print_step "Cloning ModuleAir Pro 4G repository..."
# Remove existing directory if it exists
if [ -d "/var/www/moduleair_pro_4g" ]; then
print_warning "Directory /var/www/moduleair_pro_4g already exists - removing..."
sudo rm -rf /var/www/moduleair_pro_4g
fi
# Clone the repository
if sudo git clone http://gitea.aircarto.fr/PaulVua/moduleair_pro_4g.git /var/www/moduleair_pro_4g; then
print_success "Repository cloned successfully"
else
print_error "Failed to clone repository"
exit 1
fi
# Set proper permissions
sudo chown -R www-data:www-data /var/www/moduleair_pro_4g
sudo chmod -R 755 /var/www/moduleair_pro_4g
print_success "Repository permissions set"
}
# Configure Apache
configure_apache() {
print_step "Configuring Apache web server..."
# Backup original config
sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf.backup
print_status "Original Apache config backed up"
# Update DocumentRoot
sudo sed -i 's|DocumentRoot /var/www/html|DocumentRoot /var/www/moduleair_pro_4g|' /etc/apache2/sites-available/000-default.conf
# Enable PHP if not already enabled
sudo a2enmod php* 2>/dev/null || true
# Restart Apache
sudo systemctl restart apache2
sudo systemctl enable apache2
print_success "Apache configured and restarted"
}
# System optimizations (disable audio, isolate CPU)
system_optimizations() {
print_step "Applying system optimizations..."
# Find config.txt location
local config_file="/boot/firmware/config.txt"
if [ ! -f "$config_file" ]; then
config_file="/boot/config.txt"
fi
if [ ! -f "$config_file" ]; then
print_error "Could not find config.txt file"
return 1
fi
# Disable onboard audio
print_status "Disabling onboard audio..."
if grep -q "^dtparam=audio=" "$config_file"; then
# Replace existing audio parameter
sudo sed -i 's/^dtparam=audio=.*/dtparam=audio=off/' "$config_file"
print_success "Audio parameter updated to off"
else
# Add audio parameter if not exists
echo "dtparam=audio=off" | sudo tee -a "$config_file" > /dev/null
print_success "Audio disabled in config.txt"
fi
# Blacklist audio module
print_status "Blacklisting audio module..."
echo "blacklist snd_bcm2835" | sudo tee /etc/modprobe.d/blacklist.conf > /dev/null
print_success "Audio module blacklisted"
# Find cmdline.txt location
local cmdline_file="/boot/firmware/cmdline.txt"
if [ ! -f "$cmdline_file" ]; then
cmdline_file="/boot/cmdline.txt"
fi
if [ ! -f "$cmdline_file" ]; then
print_error "Could not find cmdline.txt file"
return 1
fi
# Add CPU isolation
print_status "Setting up CPU isolation..."
if ! grep -q "isolcpus=3" "$cmdline_file"; then
# Read current cmdline and append isolcpus
local current_cmdline=$(cat "$cmdline_file")
echo "${current_cmdline} isolcpus=3" | sudo tee "$cmdline_file" > /dev/null
print_success "CPU core 3 isolated"
else
print_warning "CPU isolation already configured"
fi
# Check if audio module is currently loaded
if lsmod | grep -q snd_bcm2835; then
print_warning "Audio module is currently loaded. It will be disabled after reboot."
else
print_success "Audio module is not loaded"
fi
print_success "System optimizations completed"
}
# Enable hardware interfaces
enable_hardware() {
print_step "Enabling hardware interfaces..."
# Enable I2C using raspi-config
print_status "Enabling I2C interface..."
if sudo raspi-config nonint do_i2c 0; then
print_success "I2C enabled via raspi-config"
else
print_warning "Failed to enable I2C via raspi-config"
fi
# Enable SPI using raspi-config
print_status "Enabling SPI interface..."
if sudo raspi-config nonint do_spi 0; then
print_success "SPI enabled via raspi-config"
else
print_warning "Failed to enable SPI via raspi-config"
fi
# Enable Serial/UART using raspi-config
print_status "Enabling Serial interface..."
if sudo raspi-config nonint do_serial 0; then
print_success "Serial enabled via raspi-config"
else
print_warning "Failed to enable Serial via raspi-config"
fi
# Check if config.txt exists for additional UART configurations
local config_file="/boot/firmware/config.txt"
if [ ! -f "$config_file" ]; then
config_file="/boot/config.txt"
fi
if [ ! -f "$config_file" ]; then
print_error "Could not find config.txt file"
exit 1
fi
print_status "Using config file: $config_file"
# Add additional UART configurations for multiple ports
local uart_configs=(
"dtoverlay=uart2"
"dtoverlay=uart3"
"dtoverlay=uart4"
"dtoverlay=uart5"
)
for config in "${uart_configs[@]}"; do
if ! grep -q "^$config" "$config_file"; then
echo "$config" | sudo tee -a "$config_file"
print_success "Added: $config"
else
print_warning "$config already configured"
fi
done
print_success "Hardware interfaces configuration completed"
}
# Setup sudo authorization
setup_sudo_authorization() {
print_step "Setting up sudo authorization..."
SUDOERS_FILE="/etc/sudoers"
# First, fix any existing syntax errors
if sudo visudo -c 2>&1 | grep -q "syntax error"; then
print_warning "Syntax error detected in sudoers file. Attempting to fix..."
# Remove the problematic line if it exists
sudo sed -i '/www-data ALL=(ALL) NOPASSWD: \/usr\/bin\/python3 \* www-data/d' "$SUDOERS_FILE"
fi
# Add proper sudo rules (each on a separate line)
if ! sudo grep -q "/usr/bin/nmcli" "$SUDOERS_FILE"; then
# Create a temporary file with the new rules
cat <<EOF | sudo tee /tmp/sudoers_additions > /dev/null
# ModuleAir Pro 4G sudo rules
ALL ALL=(ALL) NOPASSWD: /usr/bin/nmcli, /usr/sbin/reboot
www-data ALL=(ALL) NOPASSWD: /usr/bin/git pull
www-data ALL=(ALL) NOPASSWD: /usr/bin/ssh
www-data ALL=(ALL) NOPASSWD: /usr/bin/python3 *
www-data ALL=(ALL) NOPASSWD: /bin/systemctl *
www-data ALL=(ALL) NOPASSWD: /var/www/moduleair_pro_4g/*
EOF
# Validate the temporary file
if sudo visudo -c -f /tmp/sudoers_additions; then
# Append to sudoers if valid using tee with sudo
cat /tmp/sudoers_additions | sudo EDITOR='tee -a' visudo >/dev/null 2>&1
print_success "Sudo authorization added"
else
print_error "Failed to add sudo rules - syntax validation failed"
sudo rm -f /tmp/sudoers_additions
return 1
fi
# Clean up
sudo rm -f /tmp/sudoers_additions
else
print_warning "Sudo authorization already set. Skipping."
fi
# Validate sudoers file after changes
if ! sudo visudo -c; then
print_error "Sudoers file has syntax errors! Please fix manually with 'sudo visudo'"
return 1
fi
print_success "Sudo authorization configured successfully"
}
# Set device permissions
set_permissions() {
print_step "Setting device permissions..."
# Set UART permissions
sudo chmod 666 /dev/ttyAMA* 2>/dev/null || print_warning "Some UART devices may not be available yet"
# Set I2C permissions
sudo chmod 666 /dev/i2c-* 2>/dev/null || print_warning "I2C devices may not be available yet"
# Add user to dialout group for serial access
sudo usermod -a -G dialout "$USER"
sudo usermod -a -G i2c "$USER"
sudo usermod -a -G gpio "$USER"
print_success "Device permissions set"
}
# Setup RTC save to DB service
setup_rtc_service() {
print_step "Setting up RTC save to DB service..."
# Create the service file
cat << 'EOF' | sudo tee /etc/systemd/system/rtc_save_to_db.service > /dev/null
[Unit]
Description=RTC Save to DB Script
After=network.target
[Service]
ExecStart=/usr/bin/python3 /var/www/moduleair_pro_4g/RTC/save_to_db.py
Restart=always
RestartSec=1
User=root
WorkingDirectory=/var/www/moduleair_pro_4g
StandardOutput=append:/var/www/moduleair_pro_4g/logs/rtc_save_to_db.log
StandardError=append:/var/www/moduleair_pro_4g/logs/rtc_save_to_db_errors.log
[Install]
WantedBy=multi-user.target
EOF
print_success "RTC service file created"
# Create logs directory if it doesn't exist
sudo mkdir -p /var/www/moduleair_pro_4g/logs
sudo chown -R www-data:www-data /var/www/moduleair_pro_4g/logs
print_status "Logs directory created/verified"
# Reload systemd daemon
print_status "Reloading systemd daemon..."
sudo systemctl daemon-reload
# Enable the service
print_status "Enabling RTC service..."
if sudo systemctl enable rtc_save_to_db.service; then
print_success "RTC service enabled"
else
print_error "Failed to enable RTC service"
return 1
fi
# Start the service
print_status "Starting RTC service..."
if sudo systemctl start rtc_save_to_db.service; then
print_success "RTC service started"
else
print_warning "Failed to start RTC service - may need reboot"
fi
# Check service status
print_status "Checking RTC service status..."
if systemctl is-active --quiet rtc_save_to_db.service; then
print_success "RTC service is running"
else
print_warning "RTC service is not running - will start after reboot"
fi
return 0
}
# Setup cron jobs
setup_cron_jobs() {
print_step "Setting up cron jobs..."
if [ -f "/var/www/moduleair_pro_4g/cron_jobs" ]; then
if sudo crontab "/var/www/moduleair_pro_4g/cron_jobs"; then
print_success "Cron jobs set up successfully"
else
print_error "Failed to set up cron jobs"
return 1
fi
else
print_warning "Cron jobs file not found. Skipping."
fi
}
# Create databases
create_databases() {
print_step "Creating databases..."
if [ -f "/var/www/moduleair_pro_4g/sqlite/create_db.py" ]; then
if sudo /usr/bin/python3 "/var/www/moduleair_pro_4g/sqlite/create_db.py"; then
print_success "Databases created successfully"
else
print_error "Failed to create databases"
return 1
fi
else
print_warning "Database creation script not found"
fi
}
# Fix database permissions
fix_database_permissions() {
print_step "Fixing database permissions..."
# Set full permissions for sqlite directory
if [ -d "/var/www/moduleair_pro_4g/sqlite" ]; then
sudo chmod 777 /var/www/moduleair_pro_4g/sqlite/
print_success "Set permissions for sqlite directory"
else
print_warning "sqlite directory not found"
fi
# Set full permissions for sensors.db
if [ -f "/var/www/moduleair_pro_4g/sqlite/sensors.db" ]; then
sudo chmod 777 /var/www/moduleair_pro_4g/sqlite/sensors.db
print_success "Set permissions for sensors.db"
else
print_warning "sensors.db not found"
fi
print_success "Database permissions fixed"
}
# Set configuration
set_configuration() {
print_step "Setting configuration..."
if [ -f "/var/www/moduleair_pro_4g/sqlite/set_config_smart.py" ]; then
if sudo /usr/bin/python3 "/var/www/moduleair_pro_4g/sqlite/set_config_smart.py"; then
print_success "Configuration set successfully"
else
print_error "Failed to set configuration"
return 1
fi
else
print_warning "Configuration script not found"
fi
}
# Setup systemd services
setup_services() {
print_step "Setting up systemd services..."
if [ -f "/var/www/moduleair_pro_4g/services/setup_services.sh" ]; then
sudo chmod +x /var/www/moduleair_pro_4g/services/setup_services.sh
if sudo /var/www/moduleair_pro_4g/services/setup_services.sh; then
print_success "Systemd services configured"
else
print_error "Failed to setup systemd services"
exit 1
fi
else
print_warning "setup_services.sh not found - services will need to be configured manually"
fi
}
# Create startup script for permissions (runs at boot)
create_boot_script() {
print_step "Creating boot permission script..."
cat << 'EOF' | sudo tee /usr/local/bin/moduleair-permissions.sh > /dev/null
#!/bin/bash
# ModuleAir Pro 4G Boot Permissions Script
sleep 5 # Wait for devices to initialize
chmod 666 /dev/ttyAMA* 2>/dev/null || true
chmod 666 /dev/i2c-* 2>/dev/null || true
EOF
sudo chmod +x /usr/local/bin/moduleair-permissions.sh
# Create systemd service for boot permissions
cat << 'EOF' | sudo tee /etc/systemd/system/moduleair-permissions.service > /dev/null
[Unit]
Description=ModuleAir Pro 4G Device Permissions
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/moduleair-permissions.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable moduleair-permissions.service
print_success "Boot permission script created and enabled"
}
# Final status check
final_status() {
print_header "INSTALLATION COMPLETE"
print_step "Checking service status..."
# Check Apache
if systemctl is-active --quiet apache2; then
print_success "Apache2 is running"
else
print_warning "Apache2 is not running"
fi
# Check for ModuleAir services
local services=(
"moduleair-npm-data.timer"
"moduleair-co2-data.timer"
"moduleair-sara-data.timer"
"moduleair-bme-data.timer"
"moduleair-boot.service"
"rtc_save_to_db.service"
)
for service in "${services[@]}"; do
if systemctl is-enabled --quiet "$service" 2>/dev/null; then
print_success "Service enabled: $service"
else
print_warning "Service not found: $service"
fi
done
print_header "INSTALLATION SUMMARY"
echo -e "${GREEN}${NC} System packages installed"
echo -e "${GREEN}${NC} Python packages installed"
echo -e "${GREEN}${NC} Repository cloned"
echo -e "${GREEN}${NC} Directory structure created"
echo -e "${GREEN}${NC} Apache web server configured"
echo -e "${GREEN}${NC} Hardware interfaces enabled"
echo -e "${GREEN}${NC} System optimizations applied"
echo -e "${GREEN}${NC} Device permissions set"
echo -e "${GREEN}${NC} Sudo authorization configured"
echo -e "${GREEN}${NC} Databases created"
echo -e "${GREEN}${NC} Configuration set"
echo -e "${GREEN}${NC} Cron jobs configured"
echo -e "${GREEN}${NC} Matrix binaries verified"
echo -e "${GREEN}${NC} Systemd services configured"
echo -e "${GREEN}${NC} RTC save to DB service installed"
echo -e "${GREEN}${NC} Boot scripts created"
print_header "NEXT STEPS"
echo -e "${YELLOW}1.${NC} ${RED}REBOOT REQUIRED${NC} - Hardware interfaces need reboot to activate:"
echo -e " ${CYAN}sudo reboot${NC}"
echo -e ""
echo -e "${YELLOW}2.${NC} After reboot, verify I2C and UART devices:"
echo -e " ${CYAN}ls -la /dev/i2c-* /dev/ttyAMA*${NC}"
echo -e ""
echo -e "${YELLOW}3.${NC} Check service status:"
echo -e " ${CYAN}systemctl list-timers | grep moduleair${NC}"
echo -e " ${CYAN}systemctl status rtc_save_to_db.service${NC}"
echo -e ""
echo -e "${YELLOW}4.${NC} Test RTC module (after reboot):"
echo -e " ${CYAN}python3 /var/www/moduleair_pro_4g/RTC/read.py${NC}"
echo -e ""
echo -e "${YELLOW}5.${NC} Access web interface at:"
echo -e " ${CYAN}http://$(hostname -I | awk '{print $1}')${NC}"
echo -e ""
echo -e "${YELLOW}6.${NC} Check logs if needed:"
echo -e " ${CYAN}tail -f /var/www/moduleair_pro_4g/logs/*.log${NC}"
echo -e " ${CYAN}tail -f /var/www/moduleair_pro_4g/logs/rtc_save_to_db.log${NC}"
echo -e ""
echo -e "${YELLOW}7.${NC} Check cron jobs:"
echo -e " ${CYAN}sudo crontab -l${NC}"
echo -e ""
echo -e "${YELLOW}8.${NC} Verify audio is disabled (after reboot):"
echo -e " ${CYAN}lsmod | grep snd_bcm2835${NC} (should return nothing)"
echo -e ""
echo -e "${YELLOW}9.${NC} Verify CPU isolation (after reboot):"
echo -e " ${CYAN}cat /proc/cmdline | grep isolcpus${NC}"
echo -e ""
print_warning "I2C and UART devices will NOT work until after reboot!"
print_warning "Audio and CPU isolation changes require reboot to take effect!"
}
# Main installation function
main() {
print_header "MODULEAIR PRO 4G INSTALLATION"
print_status "Starting automated installation..."
# Set non-interactive mode globally
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a
check_root
check_sudo
pre_configure_packages
update_system
install_dependencies
install_python_packages
clone_repository
configure_apache
enable_hardware
system_optimizations
set_permissions
setup_sudo_authorization
# Database and configuration setup
create_databases
set_configuration
fix_database_permissions # Fix permissions after database creation
setup_cron_jobs
setup_services
setup_rtc_service # New function for RTC service
create_boot_script
final_status
print_header "INSTALLATION COMPLETED SUCCESSFULLY"
print_success "ModuleAir Pro 4G has been installed successfully!"
print_warning "Please reboot the system to complete the installation."
}
# Run main function
main "$@"