#!/bin/sh # MMPM - MagicMirror Package Manager controller # Manages MMPM inside MagicMirror2 LXC container # Copyright (C) 2024-2026 CyberMind.fr CONFIG="mmpm" LXC_NAME="magicmirror2" MMPM_REPO="https://github.com/Bee-Mar/mmpm.git" usage() { cat <<'EOF' Usage: mmpmctl [options] Commands: install Install MMPM in MagicMirror2 container update Update MMPM to latest version status Show MMPM status logs Show MMPM logs (use -f to follow) shell Open shell in container (MMPM context) service-run Internal: run MMPM GUI under procd service-stop Stop MMPM GUI Module Management (via MMPM): search Search for modules list List installed modules install Install a module remove Remove a module upgrade [module] Upgrade module(s) Examples: mmpmctl install mmpmctl search weather mmpmctl install MMM-WeatherChart mmpmctl list MMPM Web GUI: http://:7891 EOF } log_info() { echo "[INFO] $*"; } log_warn() { echo "[WARN] $*" >&2; } log_error() { echo "[ERROR] $*" >&2; } uci_get() { uci -q get ${CONFIG}.$1; } uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; } load_config() { port="$(uci_get main.port || echo 7891)" address="$(uci_get main.address || echo 0.0.0.0)" enabled="$(uci_get main.enabled || echo 0)" } # Check if MagicMirror2 LXC is running check_mm2_running() { if ! lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"; then log_error "MagicMirror2 container not running" log_error "Start it first: /etc/init.d/magicmirror2 start" return 1 fi return 0 } # MMPM binary path inside container MMPM_BIN="/usr/local/bin/mmpm" # Node.js PATH inside container (for npm) NODE_PATH="/opt/nodejs/node-v24.13.0/bin:/usr/local/bin:/usr/bin:/bin" # Check if MMPM is installed in container is_mmpm_installed() { lxc-attach -n "$LXC_NAME" -- test -x "$MMPM_BIN" } # Run MMPM command with proper PATH (for npm access) run_mmpm() { lxc-attach -n "$LXC_NAME" -- sh -c "export PATH=$NODE_PATH:\$PATH && $MMPM_BIN $*" } # Install MMPM in container cmd_install() { check_mm2_running || return 1 if is_mmpm_installed; then log_info "MMPM is already installed" return 0 fi log_info "Installing MMPM in MagicMirror2 container..." # Install dependencies and MMPM via pip lxc-attach -n "$LXC_NAME" -- sh -c ' # Update package lists apt-get update 2>/dev/null || apk update 2>/dev/null || true # Install Python and pip if not present if ! command -v pip3 >/dev/null 2>&1; then apt-get install -y python3 python3-pip python3-venv 2>/dev/null || \ apk add python3 py3-pip 2>/dev/null || true fi # Install MMPM via pip (--break-system-packages for Debian Trixie PEP 668) pip3 install --upgrade --break-system-packages mmpm 2>/dev/null || \ pip3 install --upgrade mmpm 2>/dev/null || \ pip install --upgrade --break-system-packages mmpm 2>/dev/null || \ pip install --upgrade mmpm # Initialize MMPM database mmpm db --yes 2>/dev/null || true ' || { log_error "Failed to install MMPM" return 1 } log_info "MMPM installed successfully" log_info "Enable and start the GUI: /etc/init.d/mmpm enable && /etc/init.d/mmpm start" } # Update MMPM cmd_update() { check_mm2_running || return 1 if ! is_mmpm_installed; then log_error "MMPM not installed. Run 'mmpmctl install' first" return 1 fi log_info "Updating MMPM..." lxc-attach -n "$LXC_NAME" -- pip3 install --upgrade --break-system-packages mmpm 2>/dev/null || \ lxc-attach -n "$LXC_NAME" -- pip3 install --upgrade mmpm || { log_error "Failed to update MMPM" return 1 } log_info "MMPM updated successfully" } # Show status cmd_status() { load_config echo "=== MMPM Status ===" echo "" if ! lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"; then echo "MagicMirror2 container: NOT RUNNING" echo "MMPM: N/A (container not running)" return fi echo "MagicMirror2 container: RUNNING" if is_mmpm_installed; then local version=$(run_mmpm version 2>/dev/null | head -1 || echo "unknown") echo "MMPM installed: YES (v$version)" else echo "MMPM installed: NO" echo "" echo "Install with: mmpmctl install" return fi echo "" echo "=== GUI Status ===" # Check if GUI is running via pm2 local ui_status=$(run_mmpm ui --status 2>/dev/null | grep "mmpm.ui" | grep -c "online" || echo "0") if [ "$ui_status" != "0" ]; then local gui_url=$(run_mmpm ui --url 2>/dev/null | grep -oE 'http://[^[:space:]]+' || echo "") echo "GUI Status: RUNNING (pm2)" echo "GUI URL: $gui_url" else echo "GUI Status: NOT RUNNING" echo "Start with: /etc/init.d/mmpm start" fi } # Show logs cmd_logs() { if [ "$1" = "-f" ]; then logread -f -e mmpm else logread -e mmpm | tail -100 fi } # Start MMPM GUI (via pm2) cmd_service_run() { check_mm2_running || return 1 if ! is_mmpm_installed; then log_error "MMPM not installed" return 1 fi log_info "Starting MMPM GUI..." # Check if UI is installed, if not install it local ui_status=$(run_mmpm ui --status 2>/dev/null | grep -c "mmpm.ui" || echo "0") if [ "$ui_status" = "0" ]; then log_info "Installing MMPM UI..." run_mmpm ui install --yes 2>/dev/null || true fi # Start MMPM GUI via pm2 run_mmpm ui --start } # Stop MMPM GUI cmd_service_stop() { check_mm2_running || return 1 if ! is_mmpm_installed; then return 0 fi log_info "Stopping MMPM GUI..." run_mmpm ui --stop 2>/dev/null || true } # Search modules cmd_search() { local query="$1" check_mm2_running || return 1 if ! is_mmpm_installed; then log_error "MMPM not installed" return 1 fi if [ -z "$query" ]; then log_error "Search query required" return 1 fi run_mmpm search "$query" } # List installed modules cmd_list() { check_mm2_running || return 1 if ! is_mmpm_installed; then log_error "MMPM not installed" return 1 fi run_mmpm list --installed } # Install module via MMPM cmd_module_install() { local module="$1" check_mm2_running || return 1 if ! is_mmpm_installed; then log_error "MMPM not installed" return 1 fi if [ -z "$module" ]; then log_error "Module name required" return 1 fi log_info "Installing module: $module" run_mmpm install "$module" --yes } # Remove module via MMPM cmd_module_remove() { local module="$1" check_mm2_running || return 1 if ! is_mmpm_installed; then log_error "MMPM not installed" return 1 fi if [ -z "$module" ]; then log_error "Module name required" return 1 fi log_info "Removing module: $module" run_mmpm remove "$module" --yes } # Upgrade modules cmd_upgrade() { local module="$1" check_mm2_running || return 1 if ! is_mmpm_installed; then log_error "MMPM not installed" return 1 fi if [ -n "$module" ]; then log_info "Upgrading module: $module" run_mmpm upgrade "$module" --yes else log_info "Upgrading all modules..." run_mmpm upgrade --yes fi } # Open shell in container cmd_shell() { check_mm2_running || return 1 lxc-attach -n "$LXC_NAME" -- /bin/sh } # Main entry point case "${1:-}" in install) shift; cmd_install "$@" ;; update) shift; cmd_update "$@" ;; status) shift; cmd_status "$@" ;; logs) shift; cmd_logs "$@" ;; shell) shift; cmd_shell "$@" ;; service-run) shift; cmd_service_run "$@" ;; service-stop) shift; cmd_service_stop "$@" ;; search) shift; cmd_search "$@" ;; list) shift; cmd_list "$@" ;; install-module) shift; cmd_module_install "$@" ;; remove) shift; cmd_module_remove "$@" ;; upgrade) shift; cmd_upgrade "$@" ;; help|--help|-h|'') usage ;; *) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;; esac