release: support Raspberry Pi OS Trixie

This commit is contained in:
way
2026-10-11 00:06:22 +08:00
parent b0998555d9
commit fc27079126
9 changed files with 210 additions and 17 deletions
+3 -1
View File
@@ -1,4 +1,4 @@
# PIGWay Device Agent v3.7.0 # PIGWay Device Agent v3.7.1
[简体中文](README.zh-CN.md) | English [简体中文](README.zh-CN.md) | English
@@ -7,6 +7,8 @@ PIGWay Device Agent is a lightweight local monitoring service for Raspberry Pi a
### Installation ### 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 ```bash
sudo ./install.sh sudo ./install.sh
# Explicitly enable the API: # Explicitly enable the API:
+3 -1
View File
@@ -1,4 +1,4 @@
# PIGWay Device Agent v3.7.0 # PIGWay Device Agent v3.7.1
简体中文 | [English](README.md) 简体中文 | [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 ```bash
sudo ./install.sh sudo ./install.sh
# 显式启用 API: # 显式启用 API:
+9 -6
View File
@@ -1,11 +1,14 @@
7ed31dd9c626c8d3b576fb7976e7c75f2ed1e6f32cbc73c517e608831e440c6e README.md ecf1884bb89b86ed4573d4d922a971abde43772bf7b2ffa6a999a12fbfb72caf .gitignore
ddb6c2fb89001f0a7c2736534cc0b125ee4563ea2fc4f8d001752f5866f81e6f README.zh-CN.md 8e5eee4f796e1dcf9a9b4960541672fc10aae6615e96e7e2296297dcab1535d7 README.md
0f1ebcb0e9fa63744fcf0c0045fe4db3fa6eedcf22c333b46d6079d9105fad5c app/pigway_pi_control.py 94851d7e4a635ae41f41d7b7e89ddba6c0b2c53e201415fc3e3f233a82a9eb8e README.zh-CN.md
f91af58e7dd752fa97c168ae419e14e6ee5099ea58d8252568f7ce72affd219f app/pigway_pi_control_api.py aeb34068c0b9dd3afc8f1c3dd388cb658b010df37f171643d2f508fbbf853980 app/pigway_pi_control.py
641591fd59c71dc22b54e0b844d3192e46b1f8a924b59f32f5ec9c5354f9a75a app/pigway_pi_control_api.py
3a8a368bbf299eb2d6cccc968a4aacf070058bddebdc5176083a97ac579d720d app/plugin_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 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 5223c7b71c8d18b3dd9b946a24f398b80afd0bf115e1273bac0f01df8e2ce46d install.sh
5e16c1d5d592fa3e129cde0077da6b089784c82f452205a349d4824f48eb3a66 systemd/pigway-pi-control-api.service 5e16c1d5d592fa3e129cde0077da6b089784c82f452205a349d4824f48eb3a66 systemd/pigway-pi-control-api.service
35ec68e1c3040e7affad43961fbb77e9e29f3474b92aed1005502629f9acb549 systemd/pigway-pi-control.service 272545c6f821a850caa545361f4f93e3218436c7fbc1b3dd01b83ac442e7793e systemd/pigway-pi-control.service
9f59e7313ee58e687ae6dd29d175c67082674254ac5c5f54168b2735b64c20a4 uninstall.sh 9f59e7313ee58e687ae6dd29d175c67082674254ac5c5f54168b2735b64c20a4 uninstall.sh
+22 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import time,socket,subprocess,configparser,signal,re,argparse,json,os,platform import time,socket,subprocess,configparser,signal,re,argparse,json,os,platform
from pathlib import Path from pathlib import Path
APP_VERSION="3.7.0" APP_VERSION="3.7.1"
import sys import sys
sys.path.append("/usr/local/share/pigway-pi-control") sys.path.append("/usr/local/share/pigway-pi-control")
from shutil import disk_usage from shutil import disk_usage
@@ -105,6 +105,7 @@ page=0; page_since=time.monotonic()
cpu_hi=mem_hi=0 cpu_hi=mem_hi=0
net_ok=link_ok=True net_ok=link_ok=True
net_iface=""; net_kind="NET"; net_metric="DOWN"; ip_label="IP4"; ip="NO IP" 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 wifi_weak_since=None; wifi_weak_active=False
last_net=last_watch=0 last_net=last_watch=0
watch_state=[] watch_state=[]
@@ -202,6 +203,8 @@ SENSOR_INTERVAL=cf("timing","sensor_interval",1.0)
NETWORK_INTERVAL=cf("timing","network_interval",5.0) NETWORK_INTERVAL=cf("timing","network_interval",5.0)
SERVICE_INTERVAL=cf("timing","service_interval",5.0) SERVICE_INTERVAL=cf("timing","service_interval",5.0)
HEALTH_INTERVAL=cf("timing","health_interval",1.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_HIGH=cf("alerts","cpu_high",90.0)
CPU_RECOVER=cf("alerts","cpu_recover",75.0) CPU_RECOVER=cf("alerts","cpu_recover",75.0)
@@ -433,6 +436,7 @@ if args.diagnose: raise SystemExit(diagnose())
watch_state=[] watch_state=[]
last_sensor=last_status=0.0 last_sensor=last_status=0.0
last_net=last_watch=last_health=0.0 last_net=last_watch=last_health=0.0
monitor_ready_at=time.monotonic()+STARTUP_GRACE_SECONDS
health={}; fs_readonly=False 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} 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={} event_state={}
@@ -455,9 +459,23 @@ try:
c=cpu_pct(); t=temp_c(); m=mem_pct(); d=disk_pct() 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 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) 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) new_id=(net_iface,ip)
if new_id!=old_id: 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) 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) 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 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] watch_state=[check_watch(w) for w in watches]
for w,ok in zip(watches,watch_state): 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]) transition("watch:"+w[0]+":"+w[1],not ok,"WATCH_DOWN","WATCH_RECOVERED",name=w[1],target=w[2])
+1 -1
View File
@@ -10,7 +10,7 @@ from pathlib import Path
from urllib.parse import parse_qs,urlencode,urlparse from urllib.parse import parse_qs,urlencode,urlparse
from urllib.request import Request,urlopen from urllib.request import Request,urlopen
APP_VERSION="3.7.0" APP_VERSION="3.7.1"
CFG=Path("/etc/pigway-pi-control.conf") CFG=Path("/etc/pigway-pi-control.conf")
STATUS=Path("/run/pigway-pi-control/status.json") STATUS=Path("/run/pigway-pi-control/status.json")
TOKEN=Path("/etc/pigway-pi-control-api.token") TOKEN=Path("/etc/pigway-pi-control-api.token")
+11 -2
View File
@@ -42,6 +42,14 @@ service_interval = 5
# 推荐:1。当前电源状态及时更新告警与插件快照;历史状态仅记录一次日志。 # 推荐:1。当前电源状态及时更新告警与插件快照;历史状态仅记录一次日志。
health_interval = 1 health_interval = 1
# 开机后等待网络与被监控服务完成启动,再开始网络/服务告警检查。
# 这不会延迟温度、电源、磁盘等本机健康检查。
startup_grace_seconds = 15
# 网络或互联网必须连续失败达到此时间才产生告警。
# 成功样本会立即清除等待状态,避免开机和链路切换瞬间误报。
network_failure_seconds = 15
[alerts] [alerts]
# ---------------- CPU ---------------- # ---------------- CPU ----------------
@@ -131,11 +139,12 @@ pi_act_led = off
# 修改后: # 修改后:
# sudo systemctl restart pigway-pi-control # sudo systemctl restart pigway-pi-control
# #
# 默认监控 SSH 和向日葵核心后台服务。 # 默认只监控 Raspberry Pi OS 自带的 SSH 服务。
# 向日葵、Tailscale 等可选服务应在实际安装后再显式添加,避免全新系统误报。
SSH = ssh.service SSH = ssh.service
AWESUN = runawesun.service
# 以下仅为示例,默认不启用: # 以下仅为示例,默认不启用:
# AWESUN = runawesun.service
# TAILSCALE = tailscaled.service # TAILSCALE = tailscaled.service
# DOCKER = docker.service # DOCKER = docker.service
# SAMBA = smbd.service # SAMBA = smbd.service
@@ -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.
@@ -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 独立管理,异常退出自动重启,任一项目停止不会停止另一个项目。
+2 -2
View File
@@ -1,7 +1,7 @@
[Unit] [Unit]
Description=PIGWay system monitor Description=PIGWay system monitor
After=local-fs.target systemd-modules-load.service After=local-fs.target systemd-modules-load.service network-online.target ssh.service
Before=multi-user.target Wants=network-online.target
[Service] [Service]
Type=simple Type=simple