Files
pyMC_Repeater/manage.sh
T
Lloyd 599e4628d9 Changes include:
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.
2026-01-18 20:15:50 +00:00

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