mirror of
https://github.com/rightup/pyMC_Repeater.git
synced 2026-03-28 17:43:06 +01:00
264 lines
12 KiB
Bash
264 lines
12 KiB
Bash
#!/usr/bin/env bash
|
||
# pyMC Repeater - Proxmox LXC Installer
|
||
# Creates an LXC container with USB passthrough and installs pyMC Repeater
|
||
#
|
||
# Usage (run on the Proxmox host):
|
||
# bash -c "$(curl -fsSL https://raw.githubusercontent.com/rightup/pyMC_Repeater/main/scripts/proxmox-install.sh)"
|
||
#
|
||
# License: MIT
|
||
# Source: https://github.com/rightup/pyMC_Repeater
|
||
|
||
set -euo pipefail
|
||
|
||
# ── Defaults ───────────────────────────────────────────────────────────────
|
||
REPO="https://github.com/rightup/pyMC_Repeater.git"
|
||
BRANCH="feat/newRadios"
|
||
CT_TEMPLATE="debian-12-standard"
|
||
CT_RAM=1024
|
||
CT_SWAP=512
|
||
CT_DISK=4
|
||
CT_CORES=2
|
||
CT_HOSTNAME="pymc-repeater"
|
||
CT_BRIDGE="vmbr0"
|
||
CT_STORAGE="local-lvm"
|
||
CT_TEMPLATE_STORAGE="local"
|
||
CH341_VID="1a86"
|
||
CH341_PID="5512"
|
||
|
||
# ── Colors ─────────────────────────────────────────────────────────────────
|
||
RD="\033[01;31m" GN="\033[1;92m" YW="\033[33m" BL="\033[36m" BLD="\033[1m" CL="\033[m"
|
||
|
||
msg_info() { echo -e " ${BL}ℹ${CL} ${1}"; }
|
||
msg_ok() { echo -e " ${GN}✓${CL} ${1}"; }
|
||
msg_warn() { echo -e " ${YW}⚠${CL} ${1}"; }
|
||
msg_error() { echo -e " ${RD}✗${CL} ${1}"; }
|
||
|
||
header() {
|
||
clear
|
||
echo -e "${BLD}"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo " pyMC Repeater - Proxmox LXC Installer"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo -e "${CL}"
|
||
}
|
||
|
||
cleanup() {
|
||
local exit_code=$?
|
||
if [ $exit_code -ne 0 ] && [ -n "${CTID:-}" ] && pct status "$CTID" &>/dev/null; then
|
||
echo ""
|
||
read -p " Delete the failed container ${CTID}? [y/N]: " -r
|
||
if [[ "$REPLY" =~ ^[Yy]$ ]]; then
|
||
pct stop "$CTID" 2>/dev/null || true
|
||
pct destroy "$CTID" 2>/dev/null || true
|
||
msg_ok "Container ${CTID} removed"
|
||
fi
|
||
fi
|
||
}
|
||
trap cleanup EXIT
|
||
|
||
# ── Preflight checks ──────────────────────────────────────────────────────
|
||
header
|
||
|
||
if ! command -v pct &>/dev/null; then
|
||
msg_error "This script must be run on a Proxmox VE host."
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$EUID" -ne 0 ]; then
|
||
msg_error "Please run as root"
|
||
exit 1
|
||
fi
|
||
|
||
msg_ok "Running on Proxmox host as root"
|
||
|
||
# Check for CH341
|
||
echo ""
|
||
if lsusb -d "${CH341_VID}:${CH341_PID}" &>/dev/null; then
|
||
msg_ok "CH341 USB device detected"
|
||
else
|
||
msg_warn "CH341 USB device not found — plug it in before starting the repeater"
|
||
fi
|
||
|
||
# ── Interactive settings ──────────────────────────────────────────────────
|
||
echo ""
|
||
echo -e "${BLD}Container Settings${CL} (press Enter for defaults):"
|
||
echo ""
|
||
|
||
read -p " Hostname [${CT_HOSTNAME}]: " -r input; CT_HOSTNAME="${input:-$CT_HOSTNAME}"
|
||
read -p " RAM in MB [${CT_RAM}]: " -r input; CT_RAM="${input:-$CT_RAM}"
|
||
read -p " Disk in GB [${CT_DISK}]: " -r input; CT_DISK="${input:-$CT_DISK}"
|
||
read -p " CPU cores [${CT_CORES}]: " -r input; CT_CORES="${input:-$CT_CORES}"
|
||
read -p " Bridge [${CT_BRIDGE}]: " -r input; CT_BRIDGE="${input:-$CT_BRIDGE}"
|
||
|
||
AVAILABLE_STORAGES=$(pvesm status -content rootdir 2>/dev/null | awk 'NR>1 {print $1}' || echo "local-lvm")
|
||
echo " Available storages: ${AVAILABLE_STORAGES}"
|
||
read -p " Storage [${CT_STORAGE}]: " -r input; CT_STORAGE="${input:-$CT_STORAGE}"
|
||
read -p " Git branch [${BRANCH}]: " -r input; BRANCH="${input:-$BRANCH}"
|
||
read -sp " Root password [pymc]: " CT_PASSWORD; echo
|
||
CT_PASSWORD="${CT_PASSWORD:-pymc}"
|
||
|
||
# ── Get next CTID ─────────────────────────────────────────────────────────
|
||
CTID=$(pvesh get /cluster/nextid)
|
||
|
||
# ── Confirmation ──────────────────────────────────────────────────────────
|
||
echo ""
|
||
echo -e "${BLD}Summary:${CL}"
|
||
echo " CTID: ${CTID} Host: ${CT_HOSTNAME} RAM: ${CT_RAM}MB Disk: ${CT_DISK}GB"
|
||
echo " Cores: ${CT_CORES} Storage: ${CT_STORAGE} Bridge: ${CT_BRIDGE} Branch: ${BRANCH}"
|
||
echo " Mode: privileged (required for USB passthrough)"
|
||
echo ""
|
||
read -p " Proceed? [Y/n]: " -r
|
||
[[ "${REPLY:-Y}" =~ ^[Nn]$ ]] && { msg_warn "Aborted"; exit 0; }
|
||
|
||
# ── Download template ─────────────────────────────────────────────────────
|
||
echo ""
|
||
msg_info "Downloading Debian 12 template..."
|
||
TEMPLATE_FILE=$(pveam available -section system 2>/dev/null | grep "${CT_TEMPLATE}" | sort -t- -k4 -V | tail -1 | awk '{print $2}')
|
||
[ -z "$TEMPLATE_FILE" ] && { msg_error "Template not found. Run: pveam update"; exit 1; }
|
||
|
||
pveam list "$CT_TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE_FILE" || \
|
||
pveam download "$CT_TEMPLATE_STORAGE" "$TEMPLATE_FILE"
|
||
msg_ok "Template ready"
|
||
|
||
# ── Create container ──────────────────────────────────────────────────────
|
||
msg_info "Creating LXC container ${CTID}..."
|
||
pct create "$CTID" "${CT_TEMPLATE_STORAGE}:vztmpl/${TEMPLATE_FILE}" \
|
||
--hostname "$CT_HOSTNAME" \
|
||
--memory "$CT_RAM" \
|
||
--swap "$CT_SWAP" \
|
||
--cores "$CT_CORES" \
|
||
--rootfs "${CT_STORAGE}:${CT_DISK}" \
|
||
--net0 "name=eth0,bridge=${CT_BRIDGE},ip=dhcp" \
|
||
--unprivileged 0 \
|
||
--features nesting=1 \
|
||
--onboot 1 \
|
||
--start 0 \
|
||
--password "$CT_PASSWORD" \
|
||
--ostype debian
|
||
msg_ok "Container created"
|
||
|
||
# ── USB passthrough ───────────────────────────────────────────────────────
|
||
msg_info "Configuring USB passthrough..."
|
||
cat >> "/etc/pve/lxc/${CTID}.conf" <<'EOF'
|
||
|
||
# CH341 USB passthrough for pyMC Repeater
|
||
lxc.cgroup2.devices.allow: c 189:* rwm
|
||
lxc.mount.entry: /dev/bus/usb dev/bus/usb none bind,optional,create=dir 0 0
|
||
EOF
|
||
msg_ok "USB passthrough configured"
|
||
|
||
# ── Host udev rule ────────────────────────────────────────────────────────
|
||
msg_info "Installing CH341 udev rule on host..."
|
||
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="5512", MODE="0666"' \
|
||
> /etc/udev/rules.d/99-ch341.rules
|
||
udevadm control --reload-rules
|
||
udevadm trigger --subsystem-match=usb --action=change
|
||
msg_ok "Host udev rule installed"
|
||
|
||
# ── Start container & wait for network ────────────────────────────────────
|
||
msg_info "Starting container..."
|
||
pct start "$CTID"
|
||
sleep 3
|
||
for _ in $(seq 1 30); do
|
||
pct exec "$CTID" -- ping -c1 -W1 8.8.8.8 &>/dev/null && break
|
||
sleep 1
|
||
done
|
||
msg_ok "Container running with network"
|
||
|
||
# ── Bootstrap: install git, clone repo ────────────────────────────────────
|
||
msg_info "Installing git inside container..."
|
||
pct exec "$CTID" -- bash -c "
|
||
export DEBIAN_FRONTEND=noninteractive
|
||
|
||
# Fix locale warnings
|
||
apt-get update -qq
|
||
apt-get install -y locales >/dev/null 2>&1
|
||
sed -i 's/# en_US.UTF-8/en_US.UTF-8/' /etc/locale.gen
|
||
locale-gen >/dev/null 2>&1
|
||
echo 'LANG=en_US.UTF-8' > /etc/default/locale
|
||
|
||
apt-get install -y git whiptail >/dev/null 2>&1
|
||
|
||
# Enable auto-login on console (no password prompt in Proxmox web console)
|
||
mkdir -p /etc/systemd/system/container-getty@1.service.d
|
||
cat > /etc/systemd/system/container-getty@1.service.d/override.conf <<'AUTOLOGIN'
|
||
[Service]
|
||
ExecStart=
|
||
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM
|
||
AUTOLOGIN
|
||
systemctl daemon-reload
|
||
|
||
# Login banner with system info
|
||
cat > /etc/profile.d/pymc-motd.sh <<'MOTD'
|
||
#!/bin/sh
|
||
HOSTNAME=\$(hostname)
|
||
IP=\$(hostname -I | awk '{print \$1}')
|
||
OS=\$(. /etc/os-release && echo \"\$NAME\")
|
||
VER=\$(. /etc/os-release && echo \"\$VERSION_ID\")
|
||
echo \"\"
|
||
echo \" pyMC Repeater LXC Container\"
|
||
echo \" 🌐 GitHub: https://github.com/rightup/pyMC_Repeater\"
|
||
echo \"\"
|
||
echo \" 🖥️ OS: \$OS - Version: \$VER\"
|
||
echo \" 🏠 Hostname: \$HOSTNAME\"
|
||
echo \" 💡 IP Address: \$IP\"
|
||
echo \" 📡 Dashboard: http://\$IP:8000\"
|
||
echo \"\"
|
||
echo \" Management: cd /opt/pymc_repeater && bash manage.sh\"
|
||
echo \"\"
|
||
MOTD
|
||
chmod +x /etc/profile.d/pymc-motd.sh
|
||
"
|
||
msg_ok "Git installed, locale fixed, console auto-login enabled"
|
||
|
||
msg_info "Cloning pyMC_Repeater (branch: ${BRANCH})..."
|
||
pct exec "$CTID" -- bash -c "git clone --branch ${BRANCH} ${REPO} /root/pyMC_Repeater"
|
||
msg_ok "Repository cloned"
|
||
|
||
# Pre-seed config with CH341 radio type and correct GPIO pins
|
||
pct exec "$CTID" -- bash -c "
|
||
mkdir -p /etc/pymc_repeater
|
||
if [ -f /root/pyMC_Repeater/config.yaml.example ]; then
|
||
cp /root/pyMC_Repeater/config.yaml.example /etc/pymc_repeater/config.yaml
|
||
# Set radio type to CH341
|
||
sed -i 's/^radio_type: sx1262$/radio_type: sx1262_ch341/' /etc/pymc_repeater/config.yaml
|
||
# Replace Pi BCM GPIO pins with CH341 GPIO pin numbers (0-7)
|
||
sed -i 's/cs_pin: 21/cs_pin: 0/' /etc/pymc_repeater/config.yaml
|
||
sed -i 's/reset_pin: 18/reset_pin: 2/' /etc/pymc_repeater/config.yaml
|
||
sed -i 's/busy_pin: 20/busy_pin: 4/' /etc/pymc_repeater/config.yaml
|
||
sed -i 's/irq_pin: 16/irq_pin: 6/' /etc/pymc_repeater/config.yaml
|
||
sed -i 's/rxen_pin: -1/rxen_pin: 1/' /etc/pymc_repeater/config.yaml
|
||
# Enable TCXO and DIO2 RF switch for E22 module
|
||
sed -i 's/use_dio3_tcxo: false/use_dio3_tcxo: true/' /etc/pymc_repeater/config.yaml
|
||
sed -i 's/use_dio2_rf: false/use_dio2_rf: true/' /etc/pymc_repeater/config.yaml
|
||
fi
|
||
"
|
||
|
||
# ── Run manage.sh install ─────────────────────────────────────────────────
|
||
msg_info "Running manage.sh install (this will take several minutes)..."
|
||
echo ""
|
||
# Use lxc-attach with a pty so manage.sh gets an interactive terminal
|
||
lxc-attach -n "$CTID" -- bash -c "cd /root/pyMC_Repeater && TERM=xterm bash manage.sh install"
|
||
echo ""
|
||
msg_ok "manage.sh install completed"
|
||
|
||
# ── Get container IP ──────────────────────────────────────────────────────
|
||
sleep 2
|
||
CT_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}')
|
||
|
||
# ── Done ──────────────────────────────────────────────────────────────────
|
||
echo ""
|
||
echo -e "${BLD}"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo " ✓ pyMC Repeater Installation Complete!"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo -e "${CL}"
|
||
echo -e " Container: ${GN}${CTID}${CL} (${CT_HOSTNAME})"
|
||
echo -e " IP Address: ${GN}${CT_IP:-unknown}${CL}"
|
||
echo -e " Dashboard: ${GN}http://${CT_IP:-<ip>}:8000${CL}"
|
||
echo ""
|
||
echo " Next: open the dashboard and complete the setup wizard"
|
||
echo " Management: pct enter ${CTID}, then: cd /opt/pymc_repeater && bash manage.sh"
|
||
echo ""
|
||
echo "═══════════════════════════════════════════════════════════════"
|