diff --git a/README.md b/README.md index 7d8d348..f764729 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PIGWay Device Agent v3.7.0 +# PIGWay Device Agent v3.7.1 [简体中文](README.zh-CN.md) | English @@ -7,6 +7,8 @@ PIGWay Device Agent is a lightweight local monitoring service for Raspberry Pi a ### Installation +See the [Trixie fresh-install guide](docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.md) for Raspberry Pi OS Lite 64-bit based on Debian 13. The Agent itself does not access GPIO or I2C; OLED, RGB, and fan control belong to the independent hardware plugin. + ```bash sudo ./install.sh # Explicitly enable the API: diff --git a/README.zh-CN.md b/README.zh-CN.md index dfac179..a755bee 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,4 +1,4 @@ -# PIGWay Device Agent v3.7.0 +# PIGWay Device Agent v3.7.1 简体中文 | [English](README.md) @@ -7,6 +7,8 @@ ## 安装 +Raspberry Pi OS Lite 64-bit(Debian 13 Trixie)的全新安装与兼容性说明见 [Trixie 全新安装指南](docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.zh-CN.md)。监控 Agent 本身不访问 GPIO/I²C;OLED、RGB 和风扇由独立硬件插件负责。 + ```bash sudo ./install.sh # 显式启用 API: diff --git a/SHA256SUMS b/SHA256SUMS index deda07d..402bd06 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -1,11 +1,14 @@ -7ed31dd9c626c8d3b576fb7976e7c75f2ed1e6f32cbc73c517e608831e440c6e README.md -ddb6c2fb89001f0a7c2736534cc0b125ee4563ea2fc4f8d001752f5866f81e6f README.zh-CN.md -0f1ebcb0e9fa63744fcf0c0045fe4db3fa6eedcf22c333b46d6079d9105fad5c app/pigway_pi_control.py -f91af58e7dd752fa97c168ae419e14e6ee5099ea58d8252568f7ce72affd219f app/pigway_pi_control_api.py +ecf1884bb89b86ed4573d4d922a971abde43772bf7b2ffa6a999a12fbfb72caf .gitignore +8e5eee4f796e1dcf9a9b4960541672fc10aae6615e96e7e2296297dcab1535d7 README.md +94851d7e4a635ae41f41d7b7e89ddba6c0b2c53e201415fc3e3f233a82a9eb8e README.zh-CN.md +aeb34068c0b9dd3afc8f1c3dd388cb658b010df37f171643d2f508fbbf853980 app/pigway_pi_control.py +641591fd59c71dc22b54e0b844d3192e46b1f8a924b59f32f5ec9c5354f9a75a app/pigway_pi_control_api.py 3a8a368bbf299eb2d6cccc968a4aacf070058bddebdc5176083a97ac579d720d app/plugin_api.py -60665659712a0c7311e7bfa8fd09b300b0ea164cf53746feda91c7cc2a28145d config/pigway-pi-control.conf +11a75ded35d7d16512a734814d85fe2f61101044d9de9352679be73355772008 config/pigway-pi-control.conf e3bf3e7070ea62c0526791bed922688c3fb49fee36ca9c375d080119968feb72 docs/HARDWARE_PLUGIN_API.md +2030b90b8c01d02c414a1fb44c96c84527b6cafcc8a6ead5634c5e9dcd5dad4b docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.md +75984d9dea668c04d35f79d7c454d93b835825ef2eff607b760294237016aa8f docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.zh-CN.md 5223c7b71c8d18b3dd9b946a24f398b80afd0bf115e1273bac0f01df8e2ce46d install.sh 5e16c1d5d592fa3e129cde0077da6b089784c82f452205a349d4824f48eb3a66 systemd/pigway-pi-control-api.service -35ec68e1c3040e7affad43961fbb77e9e29f3474b92aed1005502629f9acb549 systemd/pigway-pi-control.service +272545c6f821a850caa545361f4f93e3218436c7fbc1b3dd01b83ac442e7793e systemd/pigway-pi-control.service 9f59e7313ee58e687ae6dd29d175c67082674254ac5c5f54168b2735b64c20a4 uninstall.sh diff --git a/app/pigway_pi_control.py b/app/pigway_pi_control.py index cc16b1d..0a0e96a 100755 --- a/app/pigway_pi_control.py +++ b/app/pigway_pi_control.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import time,socket,subprocess,configparser,signal,re,argparse,json,os,platform from pathlib import Path -APP_VERSION="3.7.0" +APP_VERSION="3.7.1" import sys sys.path.append("/usr/local/share/pigway-pi-control") from shutil import disk_usage @@ -105,6 +105,7 @@ page=0; page_since=time.monotonic() cpu_hi=mem_hi=0 net_ok=link_ok=True net_iface=""; net_kind="NET"; net_metric="DOWN"; ip_label="IP4"; ip="NO IP" +link_failure_since=internet_failure_since=None wifi_weak_since=None; wifi_weak_active=False last_net=last_watch=0 watch_state=[] @@ -202,6 +203,8 @@ SENSOR_INTERVAL=cf("timing","sensor_interval",1.0) NETWORK_INTERVAL=cf("timing","network_interval",5.0) SERVICE_INTERVAL=cf("timing","service_interval",5.0) HEALTH_INTERVAL=cf("timing","health_interval",1.0) +STARTUP_GRACE_SECONDS=max(0.0,cf("timing","startup_grace_seconds",15.0)) +NETWORK_FAILURE_SECONDS=max(0.0,cf("timing","network_failure_seconds",15.0)) CPU_HIGH=cf("alerts","cpu_high",90.0) CPU_RECOVER=cf("alerts","cpu_recover",75.0) @@ -433,6 +436,7 @@ if args.diagnose: raise SystemExit(diagnose()) watch_state=[] last_sensor=last_status=0.0 last_net=last_watch=last_health=0.0 +monitor_ready_at=time.monotonic()+STARTUP_GRACE_SECONDS health={}; fs_readonly=False agent={"last_sensor_ok":0.0,"last_network_ok":0.0,"last_watch_ok":0.0,"last_health_ok":0.0,"loop_errors":0} event_state={} @@ -455,9 +459,23 @@ try: c=cpu_pct(); t=temp_c(); m=mem_pct(); d=disk_pct() latest=(c,t,m,d); last_sensor=now; agent["last_sensor_ok"]=now - if now-last_net >= NETWORK_INTERVAL: + if now>=monitor_ready_at and now-last_net >= NETWORK_INTERVAL: old_id=(net_iface,ip) - net_iface,net_kind,net_metric,ip_label,ip=primary_network(); link_ok=ip!="NO IP"; net_ok=internet() if link_ok else False + net_iface,net_kind,net_metric,ip_label,ip=primary_network() + raw_link_ok=ip!="NO IP"; raw_net_ok=internet() if raw_link_ok else False + if raw_link_ok: + link_failure_since=None;link_ok=True + else: + if link_failure_since is None:link_failure_since=now + link_ok=now-link_failure_since>=NETWORK_FAILURE_SECONDS + link_ok=not link_ok + if not raw_link_ok: + internet_failure_since=None;net_ok=True + elif raw_net_ok: + internet_failure_since=None;net_ok=True + else: + if internet_failure_since is None:internet_failure_since=now + net_ok=not (now-internet_failure_since>=NETWORK_FAILURE_SECONDS) new_id=(net_iface,ip) if new_id!=old_id: log("INFO","NET_SWITCH",old_if=old_id[0],old_ip=old_id[1],new_if=net_iface,new_ip=ip) @@ -465,7 +483,7 @@ try: transition("internet_down",link_ok and not net_ok,"INTERNET_DOWN","INTERNET_RECOVERED",iface=net_iface,ip=ip) last_net=now; agent["last_network_ok"]=now - if now-last_watch >= SERVICE_INTERVAL: + if now>=monitor_ready_at and now-last_watch >= SERVICE_INTERVAL: watch_state=[check_watch(w) for w in watches] for w,ok in zip(watches,watch_state): transition("watch:"+w[0]+":"+w[1],not ok,"WATCH_DOWN","WATCH_RECOVERED",name=w[1],target=w[2]) diff --git a/app/pigway_pi_control_api.py b/app/pigway_pi_control_api.py index 32c6811..6dd8f98 100644 --- a/app/pigway_pi_control_api.py +++ b/app/pigway_pi_control_api.py @@ -10,7 +10,7 @@ from pathlib import Path from urllib.parse import parse_qs,urlencode,urlparse from urllib.request import Request,urlopen -APP_VERSION="3.7.0" +APP_VERSION="3.7.1" CFG=Path("/etc/pigway-pi-control.conf") STATUS=Path("/run/pigway-pi-control/status.json") TOKEN=Path("/etc/pigway-pi-control-api.token") diff --git a/config/pigway-pi-control.conf b/config/pigway-pi-control.conf index bab5077..343ef6d 100644 --- a/config/pigway-pi-control.conf +++ b/config/pigway-pi-control.conf @@ -42,6 +42,14 @@ service_interval = 5 # 推荐:1。当前电源状态及时更新告警与插件快照;历史状态仅记录一次日志。 health_interval = 1 +# 开机后等待网络与被监控服务完成启动,再开始网络/服务告警检查。 +# 这不会延迟温度、电源、磁盘等本机健康检查。 +startup_grace_seconds = 15 + +# 网络或互联网必须连续失败达到此时间才产生告警。 +# 成功样本会立即清除等待状态,避免开机和链路切换瞬间误报。 +network_failure_seconds = 15 + [alerts] # ---------------- CPU ---------------- @@ -131,11 +139,12 @@ pi_act_led = off # 修改后: # sudo systemctl restart pigway-pi-control # -# 默认监控 SSH 和向日葵核心后台服务。 +# 默认只监控 Raspberry Pi OS 自带的 SSH 服务。 +# 向日葵、Tailscale 等可选服务应在实际安装后再显式添加,避免全新系统误报。 SSH = ssh.service -AWESUN = runawesun.service # 以下仅为示例,默认不启用: +# AWESUN = runawesun.service # TAILSCALE = tailscaled.service # DOCKER = docker.service # SAMBA = smbd.service diff --git a/docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.md b/docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.md new file mode 100644 index 0000000..f522db2 --- /dev/null +++ b/docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.md @@ -0,0 +1,71 @@ +# Raspberry Pi OS Lite (Debian 13 Trixie) fresh installation + +English | [简体中文](RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.zh-CN.md) + +## Compatibility + +PIGWay Device Agent v3.7.1 supports Raspberry Pi OS Lite 64-bit based on Debian 13 Trixie and Python 3.13. The Agent reads Linux `/proc`, `/sys`, systemd, network state, and journal data. It has no GPIO, I2C, Pillow, or third-party Python dependency and does not need a virtual environment. + +The Yahboom RGB Cooling HAT is an independent plugin. It uses Debian packages `python3-smbus2` and `python3-pil`, avoiding pip changes to the PEP 668 managed system Python. Its verified interface remains unchanged: + +- I2C bus `/dev/i2c-1`; I2C must be enabled. SPI is not required. +- MCU `0x0D`; 128x32 SSD1306-compatible OLED `0x3C`. +- The fan uses six levels through MCU register `0x08`. It is not a Raspberry Pi GPIO PWM fan and has no verified RPM feedback. +- The plugin is the sole process owner of the MCU, OLED, RGB, and fan. + +Do not guess or change GPIO pins for this board. Other GPIO/PWM fans require their own driver. + +## Fresh installation + +Check the system first: + +```bash +cat /etc/os-release +python3 --version +raspi-config nonint get_i2c +ls -l /dev/i2c-1 +ss -ltnp +``` + +`raspi-config nonint get_i2c` must return `0`. Enabling I2C changes boot configuration and normally requires a reboot, so confirm SSH key access and stable power first: + +```bash +sudo raspi-config nonint do_i2c 0 +sudo reboot +``` + +Install the Agent first. Its API is opt-in: + +```bash +git clone https://tea.pigway.com/way/pigway-device-agent.git +cd pigway-device-agent +sudo ./install.sh --disable-api +``` + +To use Device Console, verify the port and explicitly enable the API: + +```bash +sudo ./install.sh --enable-api +sudo ss -ltnp | grep ':6001 ' +sudo cat /etc/pigway-pi-control-api.token +``` + +Install the hardware plugin after `/dev/i2c-1` is available: + +```bash +git clone https://tea.pigway.com/way/pigway-yahboom-cooling-hat.git +cd pigway-yahboom-cooling-hat +sudo ./install.sh --enable-integration +``` + +Register its Unix socket under `[plugins]` in `/etc/pigway-pi-control.conf`, then restart only the Agent: + +```ini +yahboom-cooling-hat = /run/pigway-plugins/yahboom-cooling-hat/api.sock +``` + +## Validation + +Validate both systemd services, Agent diagnostics, boot journal, and plugin status. Physically verify the four-line OLED and 10-second rotation, RGB alerts, and temperature-dependent fan levels. Plugin shutdown requests full fan speed and driver close never writes fan OFF. Since the board has no tachometer feedback, requested level is not measured RPM. + +The Agent API is disabled by default and the plugin uses a local Unix socket, so the default installation opens no TCP port and does not conflict with Tailscale or an API gateway. diff --git a/docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.zh-CN.md b/docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.zh-CN.md new file mode 100644 index 0000000..49e91b7 --- /dev/null +++ b/docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.zh-CN.md @@ -0,0 +1,88 @@ +# Raspberry Pi OS Lite(Debian 13 Trixie)全新安装 + +[English](RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.md) | 简体中文 + +## 兼容性结论 + +PIGWay Device Agent v3.7.1 兼容 Raspberry Pi OS Lite 64-bit、Debian 13 Trixie 与 Python 3.13。Agent 只读取 Linux 的 `/proc`、`/sys`、systemd、网络状态和 journal,不依赖 GPIO、I²C、Pillow 或第三方 Python 包,也不需要 venv。 + +Yahboom RGB Cooling HAT 已拆分为独立插件。插件使用 Debian 系统包 `python3-smbus2` 与 `python3-pil`,不会通过 pip 修改受 PEP 668 管理的系统 Python。硬件接口保持原实现: + +- I²C 总线:`/dev/i2c-1`,需要启用 I²C;不需要 SPI。 +- 控制 MCU:`0x0D`;OLED:`0x3C`,SSD1306 兼容 128×32。 +- 风扇通过 MCU 寄存器 `0x08` 的六档控制,不使用 Raspberry Pi GPIO PWM 引脚,也没有可验证的 RPM 反馈。 +- RGB 由 MCU 寄存器控制;插件是 MCU、OLED、RGB 与风扇的唯一进程所有者。 + +因此不能把这块散热板当作直连 GPIO 风扇,也不应猜测或修改 GPIO 引脚。其他 GPIO/PWM 风扇需要独立驱动。 + +## 安装前检查 + +```bash +cat /etc/os-release +python3 --version +raspi-config nonint get_i2c +ls -l /dev/i2c-1 +ss -ltnp +``` + +`raspi-config nonint get_i2c` 返回 `0` 才表示 I²C 已启用。启用 I²C 会修改启动配置,并且通常需要重启;远程操作前应确认 SSH 公钥登录与供电稳定。 + +```bash +sudo raspi-config nonint do_i2c 0 +sudo reboot +``` + +重连后确认 `/dev/i2c-1` 存在。散热板插件安装器会安装诊断工具并检查 `0x0D`、`0x3C`,不会改 SSH、网络或 Wi-Fi。 + +## 安装顺序 + +先安装通用监控 Agent: + +```bash +git clone https://tea.pigway.com/way/pigway-device-agent.git +cd pigway-device-agent +sudo ./install.sh --disable-api +``` + +默认只监控 `ssh.service`,不会假定已安装向日葵或 Tailscale。需要 NAS 上的 Device Console 时,先确认端口未占用,再显式开启 API: + +```bash +sudo ./install.sh --enable-api +sudo ss -ltnp | grep ':6001 ' +sudo cat /etc/pigway-pi-control-api.token +``` + +再安装独立硬件插件: + +```bash +git clone https://tea.pigway.com/way/pigway-yahboom-cooling-hat.git +cd pigway-yahboom-cooling-hat +sudo ./install.sh --enable-integration +``` + +最后在 `/etc/pigway-pi-control.conf` 的 `[plugins]` 中登记: + +```ini +yahboom-cooling-hat = /run/pigway-plugins/yahboom-cooling-hat/api.sock +``` + +然后仅重启 Agent: + +```bash +sudo systemctl restart pigway-pi-control.service +``` + +## 验收 + +```bash +systemctl --no-pager --full status pigway-pi-control.service +systemctl --no-pager --full status pigway-cooling-hat.service +sudo /usr/local/sbin/pigway-pi-control --diagnose +sudo journalctl -u pigway-pi-control.service -b --no-pager +sudo journalctl -u pigway-cooling-hat.service -b --no-pager +curl --unix-socket /run/pigway-plugins/yahboom-cooling-hat/api.sock http://localhost/v1/status +``` + +实机还需肉眼确认 OLED 四行主页与10秒轮播、RGB 告警颜色/效果,并通过温度变化确认风扇档位。停止插件时会请求风扇全速,驱动关闭时不会写风扇 OFF;硬件无转速反馈,因此只能确认请求档位,不能声称测得真实 RPM。 + +API 默认关闭,不占用 TCP 端口。插件只使用本机 Unix socket,不会与 Tailscale 或 API 网关争用端口。两个服务都由 systemd 独立管理,异常退出自动重启,任一项目停止不会停止另一个项目。 diff --git a/systemd/pigway-pi-control.service b/systemd/pigway-pi-control.service index 6117fd1..13e89f2 100644 --- a/systemd/pigway-pi-control.service +++ b/systemd/pigway-pi-control.service @@ -1,7 +1,7 @@ [Unit] Description=PIGWay system monitor -After=local-fs.target systemd-modules-load.service -Before=multi-user.target +After=local-fs.target systemd-modules-load.service network-online.target ssh.service +Wants=network-online.target [Service] Type=simple