mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-07-03 00:12:25 +02:00
599e4628d9
Features Neighbour details modal with full info and map view WebSocket support with heartbeat and automatic reconnection Improved signal quality calculations (SNR-based RSSI) Route-based pagination for faster initial loads UI Mobile sidebar tweaks (logout, version info, lazy-loaded charts) Sorting added to the neighbour table Packet view now shows multi-hop paths CAD calibration charts respect light/dark themes Statistics charts now show the full requested time range Performance Reduced polling when WebSocket is active Lazy loading for heavier components Noise floor data capped to keep charts responsive Technical Improved type safety across API responses Contrast improvements for accessibility Cleaner WebSocket and MQTT reconnection handling Additional metrics added to heartbeat stats Bug fixes Corrected noise floor history query Fixed authentication for CAD calibration streams Nothing major required from users — just update and carry on. As always, shout if something looks off.
822 lines
32 KiB
Bash
Executable File
822 lines
32 KiB
Bash
Executable File
#!/bin/bash
|
|
# pyMC Repeater Management Script - Deploy, Upgrade, Uninstall
|
|
|
|
set -e
|
|
|
|
INSTALL_DIR="/opt/pymc_repeater"
|
|
CONFIG_DIR="/etc/pymc_repeater"
|
|
LOG_DIR="/var/log/pymc_repeater"
|
|
SERVICE_USER="repeater"
|
|
SERVICE_NAME="pymc-repeater"
|
|
|
|
# Check if we're running in an interactive terminal
|
|
if [ ! -t 0 ] || [ -z "$TERM" ]; then
|
|
echo "Error: This script requires an interactive terminal."
|
|
echo "Please run from SSH or a local terminal, not via file manager."
|
|
exit 1
|
|
fi
|
|
|
|
# Check if whiptail is available, fallback to dialog
|
|
if command -v whiptail &> /dev/null; then
|
|
DIALOG="whiptail"
|
|
elif command -v dialog &> /dev/null; then
|
|
DIALOG="dialog"
|
|
else
|
|
echo "TUI interface requires whiptail or dialog."
|
|
if [ "$EUID" -eq 0 ]; then
|
|
echo "Installing whiptail..."
|
|
apt-get update -qq && apt-get install -y whiptail
|
|
DIALOG="whiptail"
|
|
else
|
|
echo ""
|
|
echo "Please install whiptail: sudo apt-get install -y whiptail"
|
|
echo "Then run this script again."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Function to show info box
|
|
show_info() {
|
|
$DIALOG --backtitle "pyMC Repeater Management" --title "$1" --msgbox "$2" 12 70
|
|
}
|
|
|
|
# Function to show error box
|
|
show_error() {
|
|
$DIALOG --backtitle "pyMC Repeater Management" --title "Error" --msgbox "$1" 8 60
|
|
}
|
|
|
|
# Function to ask yes/no question
|
|
ask_yes_no() {
|
|
$DIALOG --backtitle "pyMC Repeater Management" --title "$1" --yesno "$2" 10 70
|
|
}
|
|
|
|
# Function to show progress
|
|
show_progress() {
|
|
echo "$2" | $DIALOG --backtitle "pyMC Repeater Management" --title "$1" --gauge "$3" 8 70 0
|
|
}
|
|
|
|
# Function to check if service exists
|
|
service_exists() {
|
|
systemctl list-unit-files | grep -q "^$SERVICE_NAME.service"
|
|
}
|
|
|
|
# Function to check if service is installed
|
|
is_installed() {
|
|
[ -d "$INSTALL_DIR" ] && service_exists
|
|
}
|
|
|
|
# Function to check if service is running
|
|
is_running() {
|
|
systemctl is-active "$SERVICE_NAME" >/dev/null 2>&1
|
|
}
|
|
|
|
# Function to get current version
|
|
get_version() {
|
|
# Try to read from _version.py first (generated by setuptools_scm)
|
|
if [ -f "$INSTALL_DIR/repeater/_version.py" ]; then
|
|
grep "^__version__ = version = " "$INSTALL_DIR/repeater/_version.py" | cut -d"'" -f2 2>/dev/null || echo "unknown"
|
|
elif [ -f "$INSTALL_DIR/pyproject.toml" ]; then
|
|
grep "^version" "$INSTALL_DIR/pyproject.toml" | cut -d'"' -f2 2>/dev/null || echo "unknown"
|
|
else
|
|
echo "not installed"
|
|
fi
|
|
}
|
|
|
|
# Function to get service status for display
|
|
get_status_display() {
|
|
if ! is_installed; then
|
|
echo "Not Installed"
|
|
elif is_running; then
|
|
echo "Running ($(get_version))"
|
|
else
|
|
echo "Installed but Stopped ($(get_version))"
|
|
fi
|
|
}
|
|
|
|
# Main menu
|
|
show_main_menu() {
|
|
local status=$(get_status_display)
|
|
|
|
CHOICE=$($DIALOG --backtitle "pyMC Repeater Management" --title "pyMC Repeater Management" --menu "\nCurrent Status: $status\n\nChoose an action:" 18 70 9 \
|
|
"install" "Install pyMC Repeater" \
|
|
"upgrade" "Upgrade existing installation" \
|
|
"uninstall" "Remove pyMC Repeater completely" \
|
|
"config" "Configure radio settings" \
|
|
"start" "Start the service" \
|
|
"stop" "Stop the service" \
|
|
"restart" "Restart the service" \
|
|
"logs" "View live logs" \
|
|
"status" "Show detailed status" \
|
|
"exit" "Exit" 3>&1 1>&2 2>&3)
|
|
|
|
case $CHOICE in
|
|
"install")
|
|
if is_installed; then
|
|
show_error "pyMC Repeater is already installed!\n\nUse 'upgrade' to update or 'uninstall' first."
|
|
else
|
|
install_repeater
|
|
fi
|
|
;;
|
|
"upgrade")
|
|
if is_installed; then
|
|
upgrade_repeater
|
|
else
|
|
show_error "pyMC Repeater is not installed!\n\nUse 'install' first."
|
|
fi
|
|
;;
|
|
"uninstall")
|
|
if is_installed; then
|
|
uninstall_repeater
|
|
else
|
|
show_error "pyMC Repeater is not installed."
|
|
fi
|
|
;;
|
|
"config")
|
|
configure_radio
|
|
;;
|
|
"start")
|
|
manage_service "start"
|
|
;;
|
|
"stop")
|
|
manage_service "stop"
|
|
;;
|
|
"restart")
|
|
manage_service "restart"
|
|
;;
|
|
"logs")
|
|
clear
|
|
echo "=== Live Logs (Press Ctrl+C to return) ==="
|
|
echo ""
|
|
journalctl -u "$SERVICE_NAME" -f
|
|
;;
|
|
"status")
|
|
show_detailed_status
|
|
;;
|
|
"exit"|"")
|
|
exit 0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Install function
|
|
install_repeater() {
|
|
# Check root
|
|
if [ "$EUID" -ne 0 ]; then
|
|
show_error "Installation requires root privileges.\n\nPlease run: sudo $0"
|
|
return
|
|
fi
|
|
|
|
# Welcome screen
|
|
$DIALOG --backtitle "pyMC Repeater Management" --title "Welcome" --msgbox "\nWelcome to pyMC Repeater Setup\n\nThis installer will configure your Raspberry Pi as a LoRa mesh network repeater.\n\nPress OK to continue..." 12 70
|
|
|
|
# SPI Check
|
|
CONFIG_FILE=""
|
|
if [ -f "/boot/firmware/config.txt" ]; then
|
|
CONFIG_FILE="/boot/firmware/config.txt"
|
|
elif [ -f "/boot/config.txt" ]; then
|
|
CONFIG_FILE="/boot/config.txt"
|
|
fi
|
|
|
|
if [ -n "$CONFIG_FILE" ] && ! grep -q "dtparam=spi=on" "$CONFIG_FILE" 2>/dev/null && ! grep -q "spi_bcm2835" /proc/modules 2>/dev/null; then
|
|
if ask_yes_no "SPI Not Enabled" "\nSPI interface is required but not enabled!\n\nWould you like to enable it now?\n(This will require a reboot)"; then
|
|
echo "dtparam=spi=on" >> "$CONFIG_FILE"
|
|
show_info "SPI Enabled" "\nSPI has been enabled in $CONFIG_FILE\n\nSystem will reboot now. Please run this script again after reboot."
|
|
reboot
|
|
else
|
|
show_error "SPI is required for LoRa radio operation.\n\nPlease enable SPI manually and run this script again."
|
|
return
|
|
fi
|
|
elif [ -z "$CONFIG_FILE" ]; then
|
|
show_error "Could not find config.txt file.\n\nPlease enable SPI manually:\nsudo raspi-config -> Interfacing Options -> SPI -> Enable"
|
|
return
|
|
fi
|
|
|
|
# Get script directory for file copying during installation
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
# Installation progress
|
|
(
|
|
echo "0"; echo "# Creating service user..."
|
|
if ! id "$SERVICE_USER" &>/dev/null; then
|
|
useradd --system --home /var/lib/pymc_repeater --shell /sbin/nologin "$SERVICE_USER"
|
|
fi
|
|
|
|
echo "10"; echo "# Adding user to hardware groups..."
|
|
usermod -a -G gpio,i2c,spi "$SERVICE_USER" 2>/dev/null || true
|
|
usermod -a -G dialout "$SERVICE_USER" 2>/dev/null || true
|
|
|
|
echo "20"; echo "# Creating directories..."
|
|
mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater
|
|
|
|
echo "25"; echo "# Installing system dependencies..."
|
|
apt-get update -qq
|
|
apt-get install -y libffi-dev jq pip python3-rrdtool wget swig build-essential python3-dev
|
|
pip install --break-system-packages setuptools_scm >/dev/null 2>&1 || true
|
|
|
|
# Install mikefarah yq v4 if not already installed
|
|
if ! command -v yq &> /dev/null || [[ "$(yq --version 2>&1)" != *"mikefarah/yq"* ]]; then
|
|
YQ_VERSION="v4.40.5"
|
|
YQ_BINARY="yq_linux_arm64"
|
|
if [[ "$(uname -m)" == "x86_64" ]]; then
|
|
YQ_BINARY="yq_linux_amd64"
|
|
elif [[ "$(uname -m)" == "armv7"* ]]; then
|
|
YQ_BINARY="yq_linux_arm"
|
|
fi
|
|
wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${YQ_BINARY}" && chmod +x /usr/local/bin/yq
|
|
fi
|
|
|
|
echo "28"; echo "# Generating version file..."
|
|
cd "$SCRIPT_DIR"
|
|
# Generate version file using setuptools_scm before copying
|
|
if [ -d .git ]; then
|
|
git fetch --tags 2>/dev/null || true
|
|
# Write the version file that will be copied
|
|
GENERATED_VERSION=$(python3 -m setuptools_scm 2>&1 || echo "unknown (setuptools_scm not available)")
|
|
python3 -c "from setuptools_scm import get_version; get_version(write_to='repeater/_version.py')" 2>&1 || echo " Warning: Could not generate _version.py file"
|
|
echo " Generated version: $GENERATED_VERSION"
|
|
fi
|
|
|
|
echo "29"; echo "# Cleaning old installation files..."
|
|
# Remove old repeater directory to ensure clean install
|
|
rm -rf "$INSTALL_DIR/repeater" 2>/dev/null || true
|
|
# Clean up old Python bytecode
|
|
find "$INSTALL_DIR" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
find "$INSTALL_DIR" -type f -name '*.pyc' -delete 2>/dev/null || true
|
|
|
|
echo "30"; echo "# Installing files..."
|
|
cp -r "$SCRIPT_DIR/repeater" "$INSTALL_DIR/"
|
|
cp "$SCRIPT_DIR/pyproject.toml" "$INSTALL_DIR/"
|
|
cp "$SCRIPT_DIR/README.md" "$INSTALL_DIR/"
|
|
cp "$SCRIPT_DIR/manage.sh" "$INSTALL_DIR/" 2>/dev/null || true
|
|
cp "$SCRIPT_DIR/pymc-repeater.service" "$INSTALL_DIR/" 2>/dev/null || true
|
|
cp "$SCRIPT_DIR/radio-settings.json" /var/lib/pymc_repeater/ 2>/dev/null || true
|
|
cp "$SCRIPT_DIR/radio-presets.json" /var/lib/pymc_repeater/ 2>/dev/null || true
|
|
|
|
echo "45"; echo "# Installing configuration..."
|
|
cp "$SCRIPT_DIR/config.yaml.example" "$CONFIG_DIR/config.yaml.example"
|
|
if [ ! -f "$CONFIG_DIR/config.yaml" ]; then
|
|
cp "$SCRIPT_DIR/config.yaml.example" "$CONFIG_DIR/config.yaml"
|
|
fi
|
|
|
|
echo "55"; echo "# Installing systemd service..."
|
|
cp "$SCRIPT_DIR/pymc-repeater.service" /etc/systemd/system/
|
|
systemctl daemon-reload
|
|
|
|
echo "65"; echo "# Setting permissions..."
|
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater
|
|
chmod 750 "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater
|
|
# Ensure the service user can create subdirectories in their home directory
|
|
chmod 755 /var/lib/pymc_repeater
|
|
# Pre-create the .config directory that the service will need
|
|
mkdir -p /var/lib/pymc_repeater/.config/pymc_repeater
|
|
chown -R "$SERVICE_USER:$SERVICE_USER" /var/lib/pymc_repeater/.config
|
|
|
|
# Configure polkit for passwordless service restart
|
|
mkdir -p /etc/polkit-1/rules.d
|
|
cat > /etc/polkit-1/rules.d/10-pymc-repeater.rules <<'EOF'
|
|
polkit.addRule(function(action, subject) {
|
|
if (action.id == "org.freedesktop.systemd1.manage-units" &&
|
|
action.lookup("unit") == "pymc-repeater.service" &&
|
|
subject.user == "repeater") {
|
|
return polkit.Result.YES;
|
|
}
|
|
});
|
|
EOF
|
|
chmod 0644 /etc/polkit-1/rules.d/10-pymc-repeater.rules
|
|
|
|
echo "75"; echo "# Starting service..."
|
|
systemctl enable "$SERVICE_NAME"
|
|
|
|
echo "90"; echo "# Installation files complete..."
|
|
) | $DIALOG --backtitle "pyMC Repeater Management" --title "Installing" --gauge "Setting up pyMC Repeater..." 8 70 0
|
|
|
|
# Install Python package outside of progress gauge for better error handling
|
|
clear
|
|
echo "=== Installing Python Dependencies ==="
|
|
echo ""
|
|
echo "Installing pymc_repeater and dependencies (including pymc_core from GitHub)..."
|
|
echo "This may take a few minutes..."
|
|
echo ""
|
|
|
|
SCRIPT_DIR="$(dirname "$0")"
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# Suppress pip root user warnings
|
|
export PIP_ROOT_USER_ACTION=ignore
|
|
|
|
# Calculate version from git for setuptools_scm
|
|
if [ -d .git ]; then
|
|
git fetch --tags 2>/dev/null || true
|
|
GIT_VERSION=$(python3 -m setuptools_scm 2>/dev/null || echo "1.0.5")
|
|
export SETUPTOOLS_SCM_PRETEND_VERSION="$GIT_VERSION"
|
|
echo "Installing version: $GIT_VERSION"
|
|
else
|
|
export SETUPTOOLS_SCM_PRETEND_VERSION="1.0.5"
|
|
fi
|
|
|
|
if pip install --break-system-packages --force-reinstall --no-cache-dir --ignore-installed .; then
|
|
echo ""
|
|
echo "✓ Python package installation completed successfully!"
|
|
|
|
# Reload systemd and start the service
|
|
systemctl daemon-reload
|
|
systemctl start "$SERVICE_NAME"
|
|
else
|
|
echo ""
|
|
echo "✗ Python package installation failed!"
|
|
echo "Please check the error messages above and try again."
|
|
read -p "Press Enter to continue..." || true
|
|
fi
|
|
|
|
# Show final results
|
|
sleep 2
|
|
local ip_address=$(hostname -I | awk '{print $1}')
|
|
if is_running; then
|
|
clear
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo " ✓ Installation Completed Successfully!"
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
echo "Service is running on:"
|
|
echo " → http://$ip_address:8000"
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo " NEXT STEP: Complete Web Setup Wizard"
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
echo "Open the web dashboard in your browser to complete setup:"
|
|
echo ""
|
|
echo " 1. Navigate to: http://$ip_address:8000"
|
|
echo " 2. Complete the 5-step setup wizard:"
|
|
echo " • Choose repeater name"
|
|
echo " • Select hardware board"
|
|
echo " • Configure radio settings"
|
|
echo " • Set admin password"
|
|
echo " 3. Log in to your configured repeater"
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
read -p "Press Enter to return to main menu..." || true
|
|
else
|
|
show_error "Installation completed but service failed to start!\n\nCheck logs from the main menu for details."
|
|
fi
|
|
}
|
|
|
|
# Upgrade function
|
|
upgrade_repeater() {
|
|
if [ "$EUID" -ne 0 ]; then
|
|
show_error "Upgrade requires root privileges.\n\nPlease run: sudo $0"
|
|
return
|
|
fi
|
|
|
|
local current_version=$(get_version)
|
|
|
|
if ask_yes_no "Confirm Upgrade" "Current version: $current_version\n\nThis will upgrade pyMC Repeater while preserving your configuration.\n\nContinue?"; then
|
|
|
|
# Show info that upgrade is starting
|
|
show_info "Upgrading" "Starting upgrade process...\n\nThis may take a few minutes.\nProgress will be shown in the terminal."
|
|
|
|
echo "=== Upgrade Progress ==="
|
|
echo "[1/9] Stopping service..."
|
|
systemctl stop "$SERVICE_NAME" 2>/dev/null || true
|
|
|
|
echo "[2/9] Backing up configuration..."
|
|
if [ -d "$CONFIG_DIR" ]; then
|
|
cp -r "$CONFIG_DIR" "$CONFIG_DIR.backup.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
|
|
echo " ✓ Configuration backed up"
|
|
fi
|
|
|
|
echo "[3/9] Updating system dependencies..."
|
|
apt-get update -qq
|
|
|
|
apt-get install -y libffi-dev jq pip python3-rrdtool wget swig build-essential python3-dev
|
|
pip install --break-system-packages setuptools_scm >/dev/null 2>&1 || true
|
|
|
|
# Install mikefarah yq v4 if not already installed
|
|
if ! command -v yq &> /dev/null || [[ "$(yq --version 2>&1)" != *"mikefarah/yq"* ]]; then
|
|
YQ_VERSION="v4.40.5"
|
|
YQ_BINARY="yq_linux_arm64"
|
|
if [[ "$(uname -m)" == "x86_64" ]]; then
|
|
YQ_BINARY="yq_linux_amd64"
|
|
elif [[ "$(uname -m)" == "armv7"* ]]; then
|
|
YQ_BINARY="yq_linux_arm"
|
|
fi
|
|
wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${YQ_BINARY}" && chmod +x /usr/local/bin/yq
|
|
fi
|
|
echo " ✓ Dependencies updated"
|
|
|
|
echo "[3.5/9] Generating version file..."
|
|
SCRIPT_DIR="$(dirname "$0")"
|
|
cd "$SCRIPT_DIR"
|
|
# Generate version file using setuptools_scm before copying
|
|
if [ -d .git ]; then
|
|
git fetch --tags 2>/dev/null || true
|
|
# Write the version file that will be copied
|
|
GENERATED_VERSION=$(python3 -m setuptools_scm 2>&1 || echo "unknown (setuptools_scm not available)")
|
|
python3 -c "from setuptools_scm import get_version; get_version(write_to='repeater/_version.py')" 2>&1 || echo " Warning: Could not generate _version.py file"
|
|
echo " Generated version: $GENERATED_VERSION"
|
|
fi
|
|
echo " ✓ Version file generated"
|
|
|
|
echo "[3.8/9] Cleaning old installation files..."
|
|
# Remove old repeater directory to ensure clean upgrade
|
|
rm -rf "$INSTALL_DIR/repeater" 2>/dev/null || true
|
|
# Clean up old Python bytecode
|
|
find "$INSTALL_DIR" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
find "$INSTALL_DIR" -type f -name '*.pyc' -delete 2>/dev/null || true
|
|
echo " ✓ Old files cleaned"
|
|
|
|
echo "[4/9] Installing new files..."
|
|
cp -r repeater "$INSTALL_DIR/" 2>/dev/null || true
|
|
cp pyproject.toml "$INSTALL_DIR/" 2>/dev/null || true
|
|
cp README.md "$INSTALL_DIR/" 2>/dev/null || true
|
|
cp pymc-repeater.service /etc/systemd/system/ 2>/dev/null || true
|
|
cp radio-settings.json /var/lib/pymc_repeater/ 2>/dev/null || true
|
|
cp radio-presets.json /var/lib/pymc_repeater/ 2>/dev/null || true
|
|
echo " ✓ Files updated"
|
|
|
|
echo "[5/9] Validating and updating configuration..."
|
|
if validate_and_update_config; then
|
|
echo " ✓ Configuration validated and updated"
|
|
else
|
|
echo " ⚠ Configuration validation failed, keeping existing config"
|
|
fi
|
|
|
|
echo "[6/9] Fixing permissions..."
|
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater 2>/dev/null || true
|
|
chmod 750 "$CONFIG_DIR" "$LOG_DIR" 2>/dev/null || true
|
|
chmod 755 /var/lib/pymc_repeater 2>/dev/null || true
|
|
# Pre-create the .config directory that the service will need
|
|
mkdir -p /var/lib/pymc_repeater/.config/pymc_repeater 2>/dev/null || true
|
|
chown -R "$SERVICE_USER:$SERVICE_USER" /var/lib/pymc_repeater/.config 2>/dev/null || true
|
|
# Configure polkit for passwordless service restart
|
|
mkdir -p /etc/polkit-1/rules.d
|
|
cat > /etc/polkit-1/rules.d/10-pymc-repeater.rules <<'EOF'
|
|
polkit.addRule(function(action, subject) {
|
|
if (action.id == "org.freedesktop.systemd1.manage-units" &&
|
|
action.lookup("unit") == "pymc-repeater.service" &&
|
|
subject.user == "repeater") {
|
|
return polkit.Result.YES;
|
|
}
|
|
});
|
|
EOF
|
|
chmod 0644 /etc/polkit-1/rules.d/10-pymc-repeater.rules
|
|
echo " ✓ Permissions updated"
|
|
|
|
echo "[7/9] Reloading systemd..."
|
|
systemctl daemon-reload
|
|
echo " ✓ Systemd reloaded"
|
|
|
|
echo "=== Installing Python Dependencies ==="
|
|
echo ""
|
|
echo "Updating pymc_repeater and dependencies (including pymc_core from GitHub)..."
|
|
echo "This may take a few minutes..."
|
|
echo ""
|
|
|
|
# Install from source directory to properly resolve Git dependencies
|
|
SCRIPT_DIR="$(dirname "$0")"
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# Suppress pip root user warnings
|
|
export PIP_ROOT_USER_ACTION=ignore
|
|
|
|
# Calculate version from git for setuptools_scm
|
|
if [ -d .git ]; then
|
|
git fetch --tags 2>/dev/null || true
|
|
GIT_VERSION=$(python3 -m setuptools_scm 2>/dev/null || echo "1.0.5")
|
|
export SETUPTOOLS_SCM_PRETEND_VERSION="$GIT_VERSION"
|
|
echo "Upgrading to version: $GIT_VERSION"
|
|
else
|
|
export SETUPTOOLS_SCM_PRETEND_VERSION="1.0.5"
|
|
fi
|
|
|
|
# Force reinstall the package and all dependencies for clean upgrade
|
|
if python3 -m pip install --break-system-packages --force-reinstall --no-cache-dir --ignore-installed .; then
|
|
echo ""
|
|
echo "✓ Package and dependencies updated successfully!"
|
|
else
|
|
echo ""
|
|
echo "⚠ Package update failed, but continuing..."
|
|
fi
|
|
|
|
# Note: pymc_core is already reinstalled as part of the full --force-reinstall above
|
|
echo ""
|
|
echo "✓ All packages including pymc_core reinstalled successfully"
|
|
|
|
|
|
echo "[8/9] Starting service..."
|
|
systemctl daemon-reload
|
|
systemctl start "$SERVICE_NAME"
|
|
echo " ✓ Service started"
|
|
|
|
echo "[9/9] Verifying installation..."
|
|
sleep 3 # Give service time to start
|
|
|
|
local new_version=$(get_version)
|
|
|
|
if is_running; then
|
|
echo " ✓ Service is running"
|
|
show_info "Upgrade Complete" "Upgrade completed successfully!\n\nVersion: $current_version → $new_version\n\n✓ Service is running\n✓ Configuration preserved"
|
|
else
|
|
echo " ✗ Service failed to start"
|
|
show_error "Upgrade completed but service failed to start!\n\nVersion updated: $current_version → $new_version\n\nCheck logs from the main menu for details."
|
|
fi
|
|
echo "=== Upgrade Complete ==="
|
|
fi
|
|
}
|
|
|
|
# Radio Configuration function
|
|
configure_radio() {
|
|
# Check if service is running
|
|
if ! is_running; then
|
|
show_error "Service is not running!\n\nPlease start the service first from the main menu."
|
|
return
|
|
fi
|
|
|
|
# Get IP address
|
|
local ip_address=$(hostname -I | awk '{print $1}')
|
|
|
|
# Show info about web-based configuration
|
|
if ask_yes_no "Configure Radio Settings" "Radio configuration is now done through the web interface.\n\nThe web-based setup wizard provides an easy way to:\n\n• Change repeater name\n• Select hardware board\n• Configure radio frequency and settings\n• Update admin password\n\nWeb Dashboard: http://$ip_address:8000/setup\n\nWould you like to open this information?"; then
|
|
clear
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo " Web-Based Radio Configuration"
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
echo "To configure your radio settings:"
|
|
echo ""
|
|
echo " 1. Open a web browser"
|
|
echo " 2. Navigate to: http://$ip_address:8000/setup"
|
|
echo " 3. Complete the setup wizard:"
|
|
echo " • Choose repeater name"
|
|
echo " • Select hardware board"
|
|
echo " • Configure radio settings"
|
|
echo " • Update passwords if needed"
|
|
echo " 4. Service will restart automatically with new settings"
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
echo "Note: The web interface is much easier than the old"
|
|
echo " terminal-based configuration!"
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════════════════════"
|
|
echo ""
|
|
read -p "Press Enter to return to main menu..." || true
|
|
fi
|
|
}
|
|
|
|
# Uninstall function
|
|
uninstall_repeater() {
|
|
if [ "$EUID" -ne 0 ]; then
|
|
show_error "Uninstall requires root privileges.\n\nPlease run: sudo $0"
|
|
return
|
|
fi
|
|
|
|
if ask_yes_no "Confirm Uninstall" "This will completely remove pyMC Repeater including:\n\n- Service and files\n- Configuration (backup will be created)\n- Logs and data\n\nThis action cannot be undone!\n\nContinue?"; then
|
|
(
|
|
echo "0"; echo "# Stopping and disabling service..."
|
|
systemctl stop "$SERVICE_NAME" 2>/dev/null || true
|
|
systemctl disable "$SERVICE_NAME" 2>/dev/null || true
|
|
|
|
echo "20"; echo "# Backing up configuration..."
|
|
if [ -d "$CONFIG_DIR" ]; then
|
|
cp -r "$CONFIG_DIR" "/tmp/pymc_repeater_config_backup_$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
|
|
fi
|
|
|
|
echo "40"; echo "# Removing service files..."
|
|
rm -f /etc/systemd/system/pymc-repeater.service
|
|
systemctl daemon-reload
|
|
|
|
echo "60"; echo "# Removing installation..."
|
|
rm -rf "$INSTALL_DIR"
|
|
rm -rf "$CONFIG_DIR"
|
|
rm -rf "$LOG_DIR"
|
|
rm -rf /var/lib/pymc_repeater
|
|
|
|
echo "80"; echo "# Removing service user..."
|
|
if id "$SERVICE_USER" &>/dev/null; then
|
|
userdel "$SERVICE_USER" 2>/dev/null || true
|
|
fi
|
|
|
|
echo "100"; echo "# Uninstall complete!"
|
|
) | $DIALOG --backtitle "pyMC Repeater Management" --title "Uninstalling" --gauge "Removing pyMC Repeater..." 8 70 0
|
|
|
|
show_info "Uninstall Complete" "\npyMC Repeater has been completely removed.\n\nConfiguration backup saved to /tmp/\n\nThank you for using pyMC Repeater!"
|
|
fi
|
|
}
|
|
|
|
# Service management
|
|
manage_service() {
|
|
local action=$1
|
|
|
|
if [ "$EUID" -ne 0 ]; then
|
|
show_error "Service management requires root privileges.\n\nPlease run: sudo $0"
|
|
return
|
|
fi
|
|
|
|
if ! service_exists; then
|
|
show_error "Service is not installed."
|
|
return
|
|
fi
|
|
|
|
case $action in
|
|
"start")
|
|
systemctl start "$SERVICE_NAME"
|
|
if is_running; then
|
|
show_info "Service Started" "\n✓ pyMC Repeater service has been started successfully."
|
|
else
|
|
show_error "Failed to start service!\n\nCheck logs for details."
|
|
fi
|
|
;;
|
|
"stop")
|
|
systemctl stop "$SERVICE_NAME"
|
|
show_info "Service Stopped" "\n✓ pyMC Repeater service has been stopped."
|
|
;;
|
|
"restart")
|
|
systemctl restart "$SERVICE_NAME"
|
|
if is_running; then
|
|
show_info "Service Restarted" "\n✓ pyMC Repeater service has been restarted successfully."
|
|
else
|
|
show_error "Failed to restart service!\n\nCheck logs for details."
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Show detailed status
|
|
show_detailed_status() {
|
|
local status_info=""
|
|
local version=$(get_version)
|
|
local ip_address=$(hostname -I | awk '{print $1}')
|
|
|
|
status_info="Installation Status: "
|
|
if is_installed; then
|
|
status_info="${status_info}Installed\n"
|
|
status_info="${status_info}Version: $version\n"
|
|
status_info="${status_info}Install Directory: $INSTALL_DIR\n"
|
|
status_info="${status_info}Config Directory: $CONFIG_DIR\n\n"
|
|
|
|
status_info="${status_info}Service Status: "
|
|
if is_running; then
|
|
status_info="${status_info}Running ✓\n"
|
|
status_info="${status_info}Web Dashboard: http://$ip_address:8000\n\n"
|
|
else
|
|
status_info="${status_info}Stopped ✗\n\n"
|
|
fi
|
|
|
|
# Add system info
|
|
status_info="${status_info}System Info:\n"
|
|
status_info="${status_info}- SPI: "
|
|
if grep -q "spi_bcm2835" /proc/modules 2>/dev/null; then
|
|
status_info="${status_info}Enabled ✓\n"
|
|
else
|
|
status_info="${status_info}Disabled ✗\n"
|
|
fi
|
|
|
|
status_info="${status_info}- IP Address: $ip_address\n"
|
|
status_info="${status_info}- Hostname: $(hostname)\n"
|
|
|
|
else
|
|
status_info="${status_info}Not Installed"
|
|
fi
|
|
|
|
show_info "System Status" "$status_info"
|
|
}
|
|
|
|
# Function to validate and update configuration
|
|
validate_and_update_config() {
|
|
local config_file="$CONFIG_DIR/config.yaml"
|
|
local example_file="config.yaml.example"
|
|
local updated_example="$CONFIG_DIR/config.yaml.example"
|
|
|
|
# Copy the new example file
|
|
if [ -f "$example_file" ]; then
|
|
cp "$example_file" "$updated_example"
|
|
else
|
|
echo " ⚠ config.yaml.example not found in source directory"
|
|
return 1
|
|
fi
|
|
|
|
# Check if user config exists
|
|
if [ ! -f "$config_file" ]; then
|
|
echo " ⚠ No existing config.yaml found, copying example"
|
|
cp "$updated_example" "$config_file"
|
|
return 0
|
|
fi
|
|
|
|
# Check if yq is available
|
|
YQ_CMD="/usr/local/bin/yq"
|
|
if ! command -v "$YQ_CMD" &> /dev/null; then
|
|
echo " ⚠ mikefarah yq not found at $YQ_CMD, skipping config merge"
|
|
return 0
|
|
fi
|
|
|
|
# Verify it's the correct yq version
|
|
if [[ "$($YQ_CMD --version 2>&1)" != *"mikefarah/yq"* ]]; then
|
|
echo " ⚠ Wrong yq version detected at $YQ_CMD, skipping config merge"
|
|
return 0
|
|
fi
|
|
|
|
echo " Merging configuration..."
|
|
|
|
# Create backup of user config
|
|
local backup_file="${config_file}.backup.$(date +%Y%m%d_%H%M%S)"
|
|
cp "$config_file" "$backup_file"
|
|
echo " ✓ Backup created: $backup_file"
|
|
|
|
# Merge strategy: user config takes precedence, add missing keys from example
|
|
# This uses yq's multiply merge operator (*) which:
|
|
# - Keeps all values from the right operand (user config)
|
|
# - Adds missing keys from the left operand (example config)
|
|
local temp_merged="${config_file}.merged"
|
|
|
|
if "$YQ_CMD" eval-all '. as $item ireduce ({}; . * $item)' "$updated_example" "$config_file" > "$temp_merged" 2>/dev/null; then
|
|
# Verify the merged file is valid YAML
|
|
if "$YQ_CMD" eval '.' "$temp_merged" > /dev/null 2>&1; then
|
|
mv "$temp_merged" "$config_file"
|
|
echo " ✓ Configuration merged successfully"
|
|
echo " ✓ User settings preserved, new options added"
|
|
return 0
|
|
else
|
|
echo " ✗ Merged config is invalid, restoring backup"
|
|
rm -f "$temp_merged"
|
|
cp "$backup_file" "$config_file"
|
|
return 1
|
|
fi
|
|
else
|
|
echo " ✗ Config merge failed, keeping original"
|
|
rm -f "$temp_merged"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Main script logic
|
|
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
|
|
echo "pyMC Repeater Management Script"
|
|
echo ""
|
|
echo "Usage: $0 [action]"
|
|
echo ""
|
|
echo "Actions:"
|
|
echo " install - Install pyMC Repeater"
|
|
echo " upgrade - Upgrade existing installation"
|
|
echo " uninstall - Remove pyMC Repeater"
|
|
echo " config - Configure radio settings"
|
|
echo " start - Start the service"
|
|
echo " stop - Stop the service"
|
|
echo " restart - Restart the service"
|
|
echo " status - Show status"
|
|
echo " debug - Show debug information"
|
|
echo ""
|
|
echo "Run without arguments for interactive menu."
|
|
exit 0
|
|
fi
|
|
|
|
# Debug mode
|
|
if [ "$1" = "debug" ]; then
|
|
echo "=== Debug Information ==="
|
|
echo "DIALOG: $DIALOG"
|
|
echo "TERM: $TERM"
|
|
echo "TTY: $(tty 2>/dev/null || echo 'not a tty')"
|
|
echo "EUID: $EUID"
|
|
echo "PWD: $PWD"
|
|
echo "Script: $0"
|
|
echo ""
|
|
echo "Testing dialog..."
|
|
$DIALOG --backtitle "pyMC Repeater Management" --title "Test" --msgbox "Dialog test successful!" 8 40
|
|
echo "Dialog test completed."
|
|
exit 0
|
|
fi
|
|
|
|
# Handle command line arguments
|
|
case "$1" in
|
|
"install")
|
|
install_repeater
|
|
exit 0
|
|
;;
|
|
"upgrade")
|
|
upgrade_repeater
|
|
exit 0
|
|
;;
|
|
"uninstall")
|
|
uninstall_repeater
|
|
exit 0
|
|
;;
|
|
"config")
|
|
configure_radio
|
|
exit 0
|
|
;;
|
|
"start"|"stop"|"restart")
|
|
manage_service "$1"
|
|
exit 0
|
|
;;
|
|
"status")
|
|
show_detailed_status
|
|
exit 0
|
|
;;
|
|
esac
|
|
|
|
# Interactive menu loop
|
|
while true; do
|
|
show_main_menu
|
|
done
|