fix(bot): per-sender cooldown + empty-channel fallback (v1.20.1)

- Replace global _last_reply float with _last_reply_per_sender dict.
  A reply to one node no longer blocks all other senders for 5 s.
  LRU eviction keeps the dict bounded at 200 entries.

- _get_active_channels() now falls back to BotConfig defaults when
  the stored channel set is empty (user never saved a selection).
  Bot was silently deaf on first run despite the panel showing all
  channels pre-checked.

Closes: bot only replies to first sender in multi-node #test session.
This commit is contained in:
pe1hvh
2026-04-16 07:07:50 +02:00
parent ad91b27c62
commit da3a868ec6
43 changed files with 422 additions and 6337 deletions
-238
View File
@@ -1,238 +0,0 @@
#!/usr/bin/env bash
# =============================================================================
# MeshCore Bridge — systemd Service Installer
# =============================================================================
#
# Installs meshcore_bridge as a systemd daemon service.
#
# Usage:
# sudo bash install_scripts/install_bridge.sh # from project root
# cd install_scripts && sudo bash install_bridge.sh # from install_scripts/
# sudo bash install_scripts/install_bridge.sh --uninstall
#
# What this script does:
# 1. Copies meshcore_bridge.py and meshcore_bridge/ to /opt/meshcore-bridge/
# 2. Copies bridge_config.yaml to /etc/meshcore/ (if not already present)
# 3. Creates a systemd service unit file
# 4. Reloads systemd and enables the service
#
# After installation:
# sudo nano /etc/meshcore/bridge_config.yaml # edit configuration
# sudo systemctl start meshcore-bridge # start the service
# sudo systemctl enable meshcore-bridge # auto-start on boot
# sudo systemctl status meshcore-bridge # check status
# journalctl -u meshcore-bridge -f # follow logs
#
# Author: PE1HVH
# SPDX-License-Identifier: MIT
# Copyright: (c) 2026 PE1HVH
# =============================================================================
set -euo pipefail
SERVICE_NAME="meshcore-bridge"
INSTALL_DIR="/opt/meshcore-bridge"
CONFIG_DIR="/etc/meshcore"
CONFIG_FILE="${CONFIG_DIR}/bridge_config.yaml"
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
# ── Resolve project root ──
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ "$(basename "${SCRIPT_DIR}")" == "install_scripts" ]]; then
PROJECT_DIR="$(dirname "${SCRIPT_DIR}")"
else
PROJECT_DIR="${SCRIPT_DIR}"
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
# ── Check root ──
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root (sudo)."
exit 1
fi
# ── Uninstall mode ──
if [[ "${1:-}" == "--uninstall" ]]; then
info "Uninstalling ${SERVICE_NAME}..."
if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then
info "Stopping service..."
systemctl stop "${SERVICE_NAME}"
fi
if systemctl is-enabled --quiet "${SERVICE_NAME}" 2>/dev/null; then
info "Disabling service..."
systemctl disable "${SERVICE_NAME}"
fi
if [[ -f "${SERVICE_FILE}" ]]; then
info "Removing service file..."
rm -f "${SERVICE_FILE}"
systemctl daemon-reload
fi
if [[ -d "${INSTALL_DIR}" ]]; then
info "Removing installation directory..."
rm -rf "${INSTALL_DIR}"
fi
warn "Configuration preserved at ${CONFIG_DIR}/"
info "Uninstall complete."
exit 0
fi
# ── Install mode ──
info "Installing ${SERVICE_NAME}..."
# Verify source files exist
if [[ ! -f "${PROJECT_DIR}/meshcore_bridge.py" ]]; then
error "meshcore_bridge.py not found in ${PROJECT_DIR}"
error "Run this script from the project directory or from install_scripts/."
exit 1
fi
if [[ ! -d "${PROJECT_DIR}/meshcore_bridge" ]]; then
error "meshcore_bridge/ directory not found in ${PROJECT_DIR}"
exit 1
fi
# Detect Python interpreter
PYTHON_BIN=""
for candidate in python3.12 python3.11 python3.10 python3; do
if command -v "${candidate}" &>/dev/null; then
PYTHON_BIN="$(command -v "${candidate}")"
break
fi
done
if [[ -z "${PYTHON_BIN}" ]]; then
error "Python 3.10+ not found. Install Python first."
exit 1
fi
PYTHON_VERSION=$("${PYTHON_BIN}" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
info "Using Python ${PYTHON_VERSION} at ${PYTHON_BIN}"
# Check dependencies
info "Checking dependencies..."
"${PYTHON_BIN}" -c "import yaml" 2>/dev/null || {
error "pyyaml not installed. Run: pip install pyyaml"
exit 1
}
"${PYTHON_BIN}" -c "from meshcore_gui.core.shared_data import SharedData" 2>/dev/null || {
error "meshcore_gui not importable. Ensure it is installed or on PYTHONPATH."
exit 1
}
info "All dependencies satisfied."
# Create install directory
info "Copying files to ${INSTALL_DIR}/..."
mkdir -p "${INSTALL_DIR}"
cp "${PROJECT_DIR}/meshcore_bridge.py" "${INSTALL_DIR}/"
cp -r "${PROJECT_DIR}/meshcore_bridge" "${INSTALL_DIR}/"
chmod +x "${INSTALL_DIR}/meshcore_bridge.py"
# Copy config (preserve existing)
mkdir -p "${CONFIG_DIR}"
if [[ -f "${CONFIG_FILE}" ]]; then
warn "Config already exists at ${CONFIG_FILE} — not overwriting."
warn "New template saved as ${CONFIG_FILE}.new"
cp "${PROJECT_DIR}/bridge_config.yaml" "${CONFIG_FILE}.new"
else
info "Installing config template at ${CONFIG_FILE}"
cp "${PROJECT_DIR}/bridge_config.yaml" "${CONFIG_FILE}"
fi
# Detect meshcore_gui location for PYTHONPATH
MESHCORE_GUI_DIR=""
MESHCORE_GUI_PARENT=$("${PYTHON_BIN}" -c "
import meshcore_gui, os
print(os.path.dirname(os.path.dirname(meshcore_gui.__file__)))
" 2>/dev/null || true)
if [[ -n "${MESHCORE_GUI_PARENT}" ]]; then
MESHCORE_GUI_DIR="${MESHCORE_GUI_PARENT}"
info "meshcore_gui found at ${MESHCORE_GUI_DIR}"
fi
# Build PYTHONPATH
PYTHONPATH_VALUE="${INSTALL_DIR}"
if [[ -n "${MESHCORE_GUI_DIR}" ]]; then
PYTHONPATH_VALUE="${INSTALL_DIR}:${MESHCORE_GUI_DIR}"
fi
# Create systemd service file
info "Creating systemd service..."
cat > "${SERVICE_FILE}" << EOF
[Unit]
Description=MeshCore Bridge — Cross-Frequency Message Bridge Daemon
Documentation=file://${INSTALL_DIR}/BRIDGE.md
After=network.target
Wants=network.target
[Service]
Type=simple
ExecStart=${PYTHON_BIN} ${INSTALL_DIR}/meshcore_bridge.py --config=${CONFIG_FILE}
WorkingDirectory=${INSTALL_DIR}
Environment="PYTHONPATH=${PYTHONPATH_VALUE}"
# Restart policy
Restart=on-failure
RestartSec=10
StartLimitIntervalSec=300
StartLimitBurst=5
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home /var/log
PrivateTmp=true
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=${SERVICE_NAME}
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd
info "Reloading systemd daemon..."
systemctl daemon-reload
# Copy documentation
if [[ -f "${PROJECT_DIR}/BRIDGE.md" ]]; then
cp "${PROJECT_DIR}/BRIDGE.md" "${INSTALL_DIR}/"
fi
# ── Summary ──
echo
info "============================================="
info " Installation complete!"
info "============================================="
echo
info "Files installed:"
info " Application: ${INSTALL_DIR}/"
info " Config: ${CONFIG_FILE}"
info " Service: ${SERVICE_FILE}"
echo
info "Next steps:"
info " 1. Edit configuration: sudo nano ${CONFIG_FILE}"
info " 2. Start the service: sudo systemctl start ${SERVICE_NAME}"
info " 3. Enable auto-start: sudo systemctl enable ${SERVICE_NAME}"
info " 4. Check status: sudo systemctl status ${SERVICE_NAME}"
info " 5. Follow logs: journalctl -u ${SERVICE_NAME} -f"
info " 6. Open dashboard: http://localhost:9092"
echo
info "To uninstall: sudo bash install_scripts/install_bridge.sh --uninstall"
-233
View File
@@ -1,233 +0,0 @@
#!/usr/bin/env bash
# ============================================================================
# MeshCore Observer — systemd Service Installer
# ============================================================================
#
# Installs a systemd service for the MeshCore Observer daemon.
# Automatically detects the venv and current user.
#
# Usage:
# bash install_scripts/install_observer.sh # from project root
# cd install_scripts && bash install_observer.sh # from install_scripts/
#
# Optional:
# bash install_scripts/install_observer.sh --uninstall
#
# Requirements:
# - meshcore-gui project with venv/ directory
# - nicegui and pyyaml installed in the venv
# - sudo access (for systemd)
#
# Author: PE1HVH
# SPDX-License-Identifier: MIT
# Copyright: (c) 2026 PE1HVH
# ============================================================================
set -euo pipefail
# ── Colors ──
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
SERVICE_NAME="meshcore-observer"
# ── Resolve project root ──
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ "$(basename "${SCRIPT_DIR}")" == "install_scripts" ]]; then
PROJECT_DIR="$(dirname "${SCRIPT_DIR}")"
else
PROJECT_DIR="${SCRIPT_DIR}"
fi
# ── Uninstall mode ──
if [[ "${1:-}" == "--uninstall" ]]; then
info "Removing ${SERVICE_NAME} service..."
sudo systemctl stop "${SERVICE_NAME}" 2>/dev/null || true
sudo systemctl disable "${SERVICE_NAME}" 2>/dev/null || true
sudo rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
sudo systemctl daemon-reload
sudo systemctl reset-failed 2>/dev/null || true
ok "Service removed"
exit 0
fi
# ── Detect environment ──
info "Detecting environment..."
if [[ ! -f "${PROJECT_DIR}/meshcore_observer.py" ]] && [[ ! -d "${PROJECT_DIR}/meshcore_observer" ]]; then
error "Cannot find meshcore_observer.py or meshcore_observer/ in ${PROJECT_DIR}
Run this script from the project directory or from install_scripts/."
fi
CURRENT_USER="$(whoami)"
VENV_PYTHON="${PROJECT_DIR}/venv/bin/python"
# Check venv
if [[ ! -x "${VENV_PYTHON}" ]]; then
# Try parent directory venv (observer may be in meshcore-gui project)
PARENT_VENV="$(dirname "${PROJECT_DIR}")/venv/bin/python"
if [[ -x "${PARENT_VENV}" ]]; then
VENV_PYTHON="${PARENT_VENV}"
warn "Using parent directory venv: ${VENV_PYTHON}"
else
error "Virtual environment not found at: ${VENV_PYTHON}
Create it first:
python3 -m venv venv
source venv/bin/activate
pip install nicegui pyyaml"
fi
fi
# ── Check dependencies ──
info "Checking dependencies..."
"${VENV_PYTHON}" -c "import nicegui" 2>/dev/null || {
error "nicegui not installed in venv. Run:
source venv/bin/activate
pip install nicegui"
}
"${VENV_PYTHON}" -c "import yaml" 2>/dev/null || {
error "pyyaml not installed in venv. Run:
source venv/bin/activate
pip install pyyaml"
}
ok "All dependencies satisfied"
# ── Detect NODE_PATH for meshcore-decoder (MQTT auth) ──
NODE_PATH_VALUE=""
if command -v node &>/dev/null; then
NPM_GLOBAL="$(npm root -g 2>/dev/null || true)"
if [[ -n "${NPM_GLOBAL}" ]] && [[ -d "${NPM_GLOBAL}" ]]; then
NODE_PATH_VALUE="${NPM_GLOBAL}"
info "Node.js global modules: ${NPM_GLOBAL}"
fi
fi
# ── Optional settings ──
WEB_PORT="${WEB_PORT:-9093}"
ARCHIVE_DIR="${ARCHIVE_DIR:-~/.meshcore-gui/archive}"
DEBUG_ON="${DEBUG_ON:-}"
if [[ -z "${DEBUG_ON}" ]]; then
read -rp "Enable debug logging? [y/N] " dbg
if [[ "${dbg}" == "y" || "${dbg}" == "Y" ]]; then
DEBUG_ON="yes"
else
DEBUG_ON="no"
fi
fi
DEBUG_FLAG=""
if [[ "${DEBUG_ON}" == "yes" ]]; then
DEBUG_FLAG="--debug-on"
fi
# ── Config file ──
CONFIG_FLAG=""
CONFIG_FILE="${PROJECT_DIR}/observer_config.yaml"
if [[ -f "${CONFIG_FILE}" ]]; then
CONFIG_FLAG="--config=${CONFIG_FILE}"
info "Using config: ${CONFIG_FILE}"
else
info "No observer_config.yaml found — using defaults"
fi
# ── Summary ──
echo ""
echo "═══════════════════════════════════════════════════"
echo " MeshCore Observer — Service Installer"
echo "═══════════════════════════════════════════════════"
echo " Project dir: ${PROJECT_DIR}"
echo " User: ${CURRENT_USER}"
echo " Python: ${VENV_PYTHON}"
echo " Archive dir: ${ARCHIVE_DIR}"
echo " Web port: ${WEB_PORT}"
echo " Config: ${CONFIG_FILE}"
echo " Debug: ${DEBUG_ON}"
if [[ -n "${NODE_PATH_VALUE}" ]]; then
echo " NODE_PATH: ${NODE_PATH_VALUE}"
fi
echo "═══════════════════════════════════════════════════"
echo ""
read -rp "Continue? [y/N] " confirm
if [[ "${confirm}" != "y" && "${confirm}" != "Y" ]]; then
info "Aborted."
exit 0
fi
# ── Install systemd service ──
info "Installing systemd service..."
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
# Build optional Environment line for NODE_PATH
ENV_LINE=""
if [[ -n "${NODE_PATH_VALUE}" ]]; then
ENV_LINE="Environment=\"NODE_PATH=${NODE_PATH_VALUE}\""
fi
sudo tee "${SERVICE_FILE}" > /dev/null << SERVICE_EOF
[Unit]
Description=MeshCore Observer — Read-Only Archive Monitor Dashboard
[Service]
Type=simple
User=${CURRENT_USER}
WorkingDirectory=${PROJECT_DIR}
ExecStart=${VENV_PYTHON} meshcore_observer.py ${CONFIG_FLAG} --port=${WEB_PORT} ${DEBUG_FLAG}
Restart=on-failure
RestartSec=30
${ENV_LINE}
[Install]
WantedBy=multi-user.target
SERVICE_EOF
sudo systemctl daemon-reload
sudo systemctl enable "${SERVICE_NAME}"
ok "${SERVICE_NAME}.service installed and enabled"
# ── Done ──
echo ""
echo "═══════════════════════════════════════════════════"
echo -e " ${GREEN}Installation complete!${NC}"
echo "═══════════════════════════════════════════════════"
echo ""
echo " Commands:"
echo " sudo systemctl start ${SERVICE_NAME} # Start"
echo " sudo systemctl stop ${SERVICE_NAME} # Stop"
echo " sudo systemctl restart ${SERVICE_NAME} # Restart"
echo " sudo systemctl status ${SERVICE_NAME} # Status"
echo " journalctl -u ${SERVICE_NAME} -f # Live logs"
echo ""
echo " Dashboard: http://localhost:${WEB_PORT}"
echo ""
echo " Uninstall:"
echo " bash install_scripts/install_observer.sh --uninstall"
echo ""
echo "═══════════════════════════════════════════════════"
# Optionally start immediately
echo ""
read -rp "Start service now? [y/N] " start_now
if [[ "${start_now}" == "y" || "${start_now}" == "Y" ]]; then
sudo systemctl start "${SERVICE_NAME}"
sleep 2
if systemctl is-active --quiet "${SERVICE_NAME}"; then
ok "Service is running!"
echo ""
info "View live logs: journalctl -u ${SERVICE_NAME} -f"
else
warn "Service could not start. Check logs:"
echo " journalctl -u ${SERVICE_NAME} --no-pager -n 20"
fi
fi
+99 -44
View File
@@ -1,17 +1,32 @@
#!/usr/bin/env bash
# ============================================================================
# MeshCore GUI — Serial Installer
# MeshCore GUI — Serial Installer (multi-instance)
# ============================================================================
#
# Installs a systemd service for the serial-based MeshCore GUI.
# Automatically detects paths and the current user.
# The service name is derived from the serial port, so multiple instances
# can coexist on the same machine.
#
# Usage:
# bash install_scripts/install_serial.sh # from project root
# cd install_scripts && bash install_serial.sh # from install_scripts/
#
# Optional:
# bash install_scripts/install_serial.sh --uninstall
# Optional env vars:
# SERIAL_PORT=/dev/ttyUSB0 Serial device (will prompt if omitted)
# WEB_PORT=8081 NiceGUI web port (default: 8081)
# BAUD=115200 Baud rate (default: 115200)
# SERIAL_CX_DLY=0.1 Serial connect delay (default: 0.1)
# DEBUG_ON=yes|no Enable debug logging (will prompt if omitted)
#
# Examples — two instances on the same machine:
# SERIAL_PORT=/dev/ttyUSB0 WEB_PORT=8081 bash install_scripts/install_serial.sh
# SERIAL_PORT=/dev/ttyUSB1 WEB_PORT=8082 bash install_scripts/install_serial.sh
#
# Uninstall a specific instance:
# SERIAL_PORT=/dev/ttyUSB0 bash install_scripts/install_serial.sh --uninstall
#
# List all installed instances:
# bash install_scripts/install_serial.sh --list
#
# Requirements:
# - meshcore-gui project with venv/ directory
@@ -26,7 +41,7 @@ RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
@@ -41,15 +56,66 @@ else
PROJECT_DIR="${SCRIPT_DIR}"
fi
# ── List mode ──
if [[ "${1:-}" == "--list" ]]; then
echo ""
echo "Installed MeshCore GUI instances:"
echo "─────────────────────────────────────────────────"
found=0
for f in /etc/systemd/system/meshcore-gui-*.service; do
[[ -f "$f" ]] || continue
name="$(basename "$f" .service)"
status="$(systemctl is-active "$name" 2>/dev/null || echo inactive)"
port="$(grep -oP '(?<=--port=)\S+' "$f" 2>/dev/null || echo '?')"
device="$(grep -oP '(?<=ExecStart=.{60,200} )/dev/\S+' "$f" 2>/dev/null | head -1 || echo '?')"
echo " ${name}"
echo " device : ${device}"
echo " port : ${port}"
echo " status : ${status}"
echo ""
found=1
done
if [[ $found -eq 0 ]]; then
echo " (none found)"
fi
echo "─────────────────────────────────────────────────"
exit 0
fi
# ── Resolve serial port (needed for service name) ──
SERIAL_PORT="${SERIAL_PORT:-}"
if [[ -z "${SERIAL_PORT}" ]]; then
echo ""
echo -e "${YELLOW}Serial device not specified.${NC}"
echo "You can specify it in two ways:"
echo ""
echo " 1. As an environment variable:"
echo " SERIAL_PORT=/dev/ttyACM0 bash $0"
echo ""
echo " 2. Enter manually:"
read -rp " Serial device (e.g. /dev/ttyACM0 or /dev/ttyUSB0): " SERIAL_PORT
echo ""
fi
if [[ -z "${SERIAL_PORT}" ]]; then
error "No serial device specified. Aborted."
fi
# Derive a safe service name from the device path
# e.g. /dev/ttyUSB1 → meshcore-gui-ttyUSB1
DEVICE_SLUG="$(basename "${SERIAL_PORT}")"
SERVICE_NAME="meshcore-gui-${DEVICE_SLUG}"
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
# ── Uninstall mode ──
if [[ "${1:-}" == "--uninstall" ]]; then
info "Removing meshcore-gui service..."
sudo systemctl stop meshcore-gui 2>/dev/null || true
sudo systemctl disable meshcore-gui 2>/dev/null || true
sudo rm -f /etc/systemd/system/meshcore-gui.service
info "Removing ${SERVICE_NAME}..."
sudo systemctl stop "${SERVICE_NAME}" 2>/dev/null || true
sudo systemctl disable "${SERVICE_NAME}" 2>/dev/null || true
sudo rm -f "${SERVICE_FILE}"
sudo systemctl daemon-reload
sudo systemctl reset-failed 2>/dev/null || true
ok "Service removed"
ok "Service '${SERVICE_NAME}' removed"
exit 0
fi
@@ -85,25 +151,6 @@ else
error "Cannot determine entry point."
fi
# Serial port (env or prompt)
SERIAL_PORT="${SERIAL_PORT:-}"
if [[ -z "${SERIAL_PORT}" ]]; then
echo ""
echo -e "${YELLOW}Serial device not specified.${NC}"
echo "You can specify it in two ways:"
echo ""
echo " 1. As an environment variable:"
echo " SERIAL_PORT=/dev/ttyACM0 bash $0"
echo ""
echo " 2. Enter manually:"
read -rp " Serial device (e.g. /dev/ttyACM0 or /dev/ttyUSB0): " SERIAL_PORT
echo ""
fi
if [[ -z "${SERIAL_PORT}" ]]; then
error "No serial device specified. Aborted."
fi
# Optional settings
BAUD="${BAUD:-115200}"
SERIAL_CX_DLY="${SERIAL_CX_DLY:-0.1}"
@@ -132,6 +179,11 @@ if ! id -nG "${CURRENT_USER}" | grep -qw "dialout"; then
warn " (then log out/in)"
fi
# Warn if this service already exists
if [[ -f "${SERVICE_FILE}" ]]; then
warn "Service '${SERVICE_NAME}' already exists and will be overwritten."
fi
# Summary
echo ""
echo "═══════════════════════════════════════════════════"
@@ -146,6 +198,7 @@ echo " Baudrate: ${BAUD}"
echo " CX delay: ${SERIAL_CX_DLY}"
echo " Web port: ${WEB_PORT}"
echo " Debug: ${DEBUG_ON}"
echo " Service name: ${SERVICE_NAME}"
echo "═══════════════════════════════════════════════════"
echo ""
read -rp "Continue? [y/N] " confirm
@@ -187,11 +240,10 @@ ok "Python files are syntactically correct"
# ── Step 3: Install systemd service ──
info "Step 3/3: Installing systemd service..."
SERVICE_FILE="/etc/systemd/system/meshcore-gui.service"
sudo tee "${SERVICE_FILE}" > /dev/null << SERVICE_EOF
[Unit]
Description=MeshCore GUI (Serial)
Description=MeshCore GUI (${SERIAL_PORT})
[Service]
Type=simple
@@ -206,8 +258,8 @@ WantedBy=multi-user.target
SERVICE_EOF
sudo systemctl daemon-reload
sudo systemctl enable meshcore-gui
ok "meshcore-gui.service installed and enabled"
sudo systemctl enable "${SERVICE_NAME}"
ok "'${SERVICE_NAME}' installed and enabled"
# ── Done ──
echo ""
@@ -216,14 +268,17 @@ echo -e " ${GREEN}Installation complete!${NC}"
echo "═══════════════════════════════════════════════════"
echo ""
echo " Commands:"
echo " sudo systemctl start meshcore-gui # Start"
echo " sudo systemctl stop meshcore-gui # Stop"
echo " sudo systemctl restart meshcore-gui # Restart"
echo " sudo systemctl status meshcore-gui # Status"
echo " journalctl -u meshcore-gui -f # Live logs"
echo " sudo systemctl start ${SERVICE_NAME}"
echo " sudo systemctl stop ${SERVICE_NAME}"
echo " sudo systemctl restart ${SERVICE_NAME}"
echo " sudo systemctl status ${SERVICE_NAME}"
echo " journalctl -u ${SERVICE_NAME} -f"
echo ""
echo " Uninstall:"
echo " bash install_scripts/install_serial.sh --uninstall"
echo " All instances:"
echo " bash install_scripts/install_serial.sh --list"
echo ""
echo " Uninstall this instance:"
echo " SERIAL_PORT=${SERIAL_PORT} bash install_scripts/install_serial.sh --uninstall"
echo ""
echo "═══════════════════════════════════════════════════"
@@ -231,14 +286,14 @@ echo "════════════════════════
echo ""
read -rp "Start service now? [y/N] " start_now
if [[ "${start_now}" == "y" || "${start_now}" == "Y" ]]; then
sudo systemctl start meshcore-gui
sudo systemctl start "${SERVICE_NAME}"
sleep 2
if systemctl is-active --quiet meshcore-gui; then
if systemctl is-active --quiet "${SERVICE_NAME}"; then
ok "Service is running!"
echo ""
info "View live logs: journalctl -u meshcore-gui -f"
info "View live logs: journalctl -u ${SERVICE_NAME} -f"
else
warn "Service could not start. Check logs:"
echo " journalctl -u meshcore-gui --no-pager -n 20"
echo " journalctl -u ${SERVICE_NAME} --no-pager -n 20"
fi
fi
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
# ============================================================================
# MeshCore GUI — Virtual Environment Setup
# ============================================================================
#
# Creates a venv and installs core Python dependencies.
#
# Usage:
# bash install_scripts/install_venv.sh # from project root
# cd install_scripts && bash install_venv.sh # from install_scripts/
#
# ============================================================================
set -euo pipefail
# ── Resolve project root ──
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ "$(basename "${SCRIPT_DIR}")" == "install_scripts" ]]; then
PROJECT_DIR="$(dirname "${SCRIPT_DIR}")"
else
PROJECT_DIR="${SCRIPT_DIR}"
fi
cd "${PROJECT_DIR}"
echo "Creating virtual environment in ${PROJECT_DIR}/venv ..."
python3 -m venv venv
source venv/bin/activate
pip install nicegui meshcore bleak meshcoredecoder
echo "Done. Activate with: source ${PROJECT_DIR}/venv/bin/activate"