#!/bin/bash set -euo pipefail cd "$(dirname "$0")" for required in app/pigway_pi_control.py app/pigway_pi_control_api.py app/oled_font_5x7.bin web/index.html config/pigway-pi-control.conf systemd/pigway-pi-control.service systemd/pigway-pi-control-api.service; do if [ ! -f "$required" ]; then echo "ERROR: required file missing: $required" >&2 exit 1 fi done echo "[1/8] 检查 Raspberry Pi I2C..." [ -e /dev/i2c-1 ] || { echo "错误:/dev/i2c-1 不存在。先 sudo raspi-config -> Interface Options -> I2C -> Enable,然后重启。"; exit 1; } # A running copy of this project is expected during an upgrade. Any other # process holding the shared bus could race OLED/MCU writes, so report it and # let the user decide how that process should be managed. i2c_conflict_pids() { local allowed_pid pid allowed_pid="$(systemctl show -p MainPID --value pigway-pi-control.service 2>/dev/null || true)" for pid in $(fuser /dev/i2c-1 2>/dev/null || true); do case "$pid" in ''|*[!0-9]*) continue ;; esac [ "$pid" = "$$" ] && continue [ "$pid" = "$allowed_pid" ] && continue kill -0 "$pid" 2>/dev/null && echo "$pid" done } I2C_CONFLICTS="$(i2c_conflict_pids)" if [ -n "$I2C_CONFLICTS" ]; then echo "ERROR: /dev/i2c-1 is already in use by another process:" >&2 while read -r pid; do ps -p "$pid" -o pid=,user=,comm=,args= >&2 || true done <<< "$I2C_CONFLICTS" echo "Stop or reconfigure the conflicting OLED/LED controller, then run the installer again." >&2 exit 1 fi echo "[2/8] 等待 APT/DPKG..." for i in $(seq 1 60); do if ! fuser /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; then break; fi [ "$i" = 60 ] && { echo "APT 锁等待超时,请稍后重试。"; exit 1; } echo "软件包管理器忙,等待 5 秒... ($i/60)" sleep 5 done echo "[3/8] 安装依赖..." apt-get update apt-get install -y python3 python3-pil python3-smbus2 i2c-tools echo "[4/8] 检查硬件地址..." SCAN="$(i2cdetect -y 1)"; echo "$SCAN" echo "$SCAN" | grep -qi '3c' || echo "警告:未扫描到 OLED 0x3C" echo "$SCAN" | grep -qi '0d' || echo "警告:未扫描到控制 MCU 0x0D" echo "[5/8] 安装程序与配置..." mkdir -p /usr/local/share/pigway-pi-control install -m 0644 app/oled_font_5x7.bin /usr/local/share/pigway-pi-control/oled_font_5x7.bin mkdir -p /usr/local/share/pigway-pi-control/web install -m 0644 web/index.html /usr/local/share/pigway-pi-control/web/index.html install -m 0755 app/pigway_pi_control.py /usr/local/sbin/pigway-pi-control install -m 0755 app/pigway_pi_control_api.py /usr/local/sbin/pigway-pi-control-api # Keep the complete existing configuration, including comments and custom sections. # The merge below adds only missing documented options. if [ -f /etc/pigway-pi-control.conf ]; then cp /etc/pigway-pi-control.conf "/etc/pigway-pi-control.conf.bak.$(date +%Y%m%d-%H%M%S)" else install -m 0644 config/pigway-pi-control.conf /etc/pigway-pi-control.conf fi # Preserve the existing file while exposing new defaults. Existing values win. python3 - /etc/pigway-pi-control.conf <<'PY' import re,sys from pathlib import Path path=Path(sys.argv[1]) text=path.read_text() sections={ "led":( ("cpu_r",255),("cpu_g",0),("cpu_b",0), ("power_r",255),("power_g",40),("power_b",0), ("memory_r",255),("memory_g",0),("memory_b",255), ("storage_r",255),("storage_g",255),("storage_b",255), ("network_r",0),("network_g",0),("network_b",255), ("service_r",0),("service_g",255),("service_b",96), ("warning_flash_on_ms",1500),("warning_flash_off_ms",1500), ("critical_flash_on_ms",750),("critical_flash_off_ms",750), ("emergency_flash_on_ms",300),("emergency_flash_off_ms",300), ), "api":(("bind","0.0.0.0"),("port",6001),("log_limit",200)), } for section,defaults in sections.items(): match=re.search(rf"(?m)^\[{re.escape(section)}\]\s*$",text) if match: next_section=re.search(r"(?m)^\[.+\]\s*$",text[match.end():]) end=match.end()+(next_section.start() if next_section else len(text)-match.end()) chunk=text[match.end():end] else: text=text.rstrip()+f"\n\n[{section}]\n" match=re.search(rf"(?m)^\[{re.escape(section)}\]\s*$",text) end=len(text); chunk="" missing=[(key,value) for key,value in defaults if not re.search(rf"(?m)^\s*{re.escape(key)}\s*=",chunk)] if missing: note="# Added by installer; existing user values are preserved.\n" block=note+"".join(f"{key} = {value}\n" for key,value in missing) text=text[:end].rstrip()+"\n"+block+"\n\n"+text[end:].lstrip("\n") path.write_text(text) PY if [ ! -s /etc/pigway-pi-control-api.token ]; then umask 077 python3 - <<'PY' > /etc/pigway-pi-control-api.token import secrets print(secrets.token_urlsafe(32)) PY fi chmod 0600 /etc/pigway-pi-control-api.token install -m 0644 systemd/pigway-pi-control.service /etc/systemd/system/pigway-pi-control.service install -m 0644 systemd/pigway-pi-control-api.service /etc/systemd/system/pigway-pi-control-api.service echo "[6/8] 启用/重启服务..." systemctl daemon-reload systemctl enable pigway-pi-control.service pigway-pi-control-api.service >/dev/null systemctl restart pigway-pi-control.service systemctl restart pigway-pi-control-api.service echo "[7/8] 安装后自检..." FAIL=0 check() { local label="$1"; shift; if "$@"; then printf " %-30s PASS\n" "$label"; else printf " %-30s FAIL\n" "$label"; FAIL=1; fi; } APP=/usr/local/sbin/pigway-pi-control check "Version 3.6.0" grep -Fq 'APP_VERSION="3.6.0"' "$APP" check "OLED physical 128x32" grep -Fq 'W,H=128,32' "$APP" check "OLED MUX 0x1F" grep -Fq '0xA8,0x1F' "$APP" check "OLED pages 0..3" grep -Fq 'cmd(0x22);cmd(0);cmd(3)' "$APP" check "UI CPU/TMP/DSK" grep -Fq 'f"TMP {temp:.1f}C"' "$APP" check "UI IP4/IP6" grep -Fq 'ip_label' "$APP" check "UI right column x=72" grep -Fq 'draw_text_5x7(pix,72,y,right[row])' "$APP" no_other_i2c_owner() { [ -z "$(i2c_conflict_pids)" ]; } check "No other I2C owner" no_other_i2c_owner check "Service running" systemctl is-active --quiet pigway-pi-control.service check "API/Web service running" systemctl is-active --quiet pigway-pi-control-api.service check "API/Web version 3.6.0" grep -Fq 'APP_VERSION="3.6.0"' /usr/local/sbin/pigway-pi-control-api check "Web UI installed" test -s /usr/local/share/pigway-pi-control/web/index.html check "P0 health monitor" grep -Fq 'throttled_state' "$APP" check "Diagnostic command" grep -Fq -- '--diagnose' "$APP" if [ "$FAIL" -ne 0 ]; then echo "错误:安装后自检未全部通过。"; exit 1; fi echo "[8/8] 状态..." systemctl --no-pager --full status pigway-pi-control.service || true systemctl --no-pager --full status pigway-pi-control-api.service || true echo echo "完成。程序:/usr/local/sbin/pigway-pi-control" echo "配置:/etc/pigway-pi-control.conf" echo "Web/API:http://$(hostname -I | awk '{print $1}'):$(awk -F= '/^port[[:space:]]*=/{gsub(/[[:space:]]/,"",$2);print $2;exit}' /etc/pigway-pi-control.conf)" echo "配置写入 token:sudo cat /etc/pigway-pi-control-api.token"