release: merge Yahboom Cooling HAT v1.0.0

This commit is contained in:
way
2026-09-27 23:17:46 +08:00
7 changed files with 60 additions and 12 deletions
+9 -4
View File
@@ -1,4 +1,4 @@
# PIGWay Cooling HAT
# PIGWay Yahboom Cooling HAT
独立运行的本机硬件插件。当前驱动:Yahboom RGB Cooling HAT(MCU 0x0D、OLED 0x3C、128×32)。提供本机状态采集、OLED 展示、风扇温控和 RGB 灯效。无需安装监控主程序即可使用。
@@ -6,6 +6,8 @@
```bash
sudo ./install.sh
# 允许监控接入(默认关闭):
sudo ./install.sh --enable-integration
# 只安装,不启动/启用新服务:
sudo ./install.sh --no-start
```
@@ -22,11 +24,14 @@ curl --unix-socket /run/pigway-plugins/yahboom-cooling-hat/api.sock http://local
无论是否接入监控,插件都自行采集 CPU、内存、温度、磁盘和主网络状态,OLED 主页显示本机数据,风扇执行本地温控,RGB 采用正常灯效配置,默认全灭。
温度传感器读取失败时请求风扇全速;停止插件时也请求全速并关闭 OLED/RGB,避免因退出显示程序而取消散热。硬件故障时不能保证执行成功,错误会记录日志。风扇无转速反馈,只报告设置档位,不伪造 RPM。
## 接入监控
## 可选接入监控
可选接入 [PIGWay Pi Control 监控平台](https://tea.pigway.com/way/pigway-pi-control),接收服务、进程和系统健康告警。双方独立安装、独立运行;不接入也可正常使用本地功能。
安装时可选择允许接入,默认否;非交互安装默认关闭。关闭时不创建 API socket,本机采集、显示和温控继续运行。也可修改 `/etc/pigway-cooling-hat.conf` 的 `[integration] enabled = true/false` 并重启插件。`--disable-integration` 显式关闭。
监控 Web → 系统配置 → 硬件插件中发现并手动接入。协议是本机 Unix socket 上的 HTTP API,默认不开放 TCP 端口。目录 0750、socket 0660,只有 root 和被明确加入 `pigway-hardware` 组的账户能访问。不要随意授予组成员资格。
可选接入 [PIGWay Device Agent 监控平台](https://tea.pigway.com/way/pigway-device-agent),接收服务、进程和系统健康告警。双方独立安装、独立运行;不接入也可正常使用本地功能。
[独立 Web 管理中心](https://tea.pigway.com/way/pigway-device-console) → 系统配置 → 硬件插件中发现并手动接入。协议是本机 Unix socket 上的 HTTP API,默认不开放 TCP 端口。目录 0750、socket 0660,只有 root 和被明确加入 `pigway-hardware` 组的账户能访问。不要随意授予组成员资格。
监控发送完整、带版本和有效期的状态;不发送动画帧或 MCU 指令。插件自己仲裁 OLED 页和 RGB 效果。告警页有页码并轮播主页,相同 RGB 效果不因切页重启。只有一个有效监控来源可以持有显示状态,重复/乱序版本会拒绝。状态超过 10 秒未更新(由客户端声明,允许3–30秒),旧告警失效,OLED 回到本机主页、RGB 回到正常灯效,风扇继续本地温控。主动断开行为相同。监控快照中的 system/network 字段保留 API v1 兼容,但不会覆盖插件的本地数据。采样失败显示 --,不伪装为零。
+6 -6
View File
@@ -1,13 +1,13 @@
1331f33332c6c33ecfc3e91d8734ee3ff7755ac1c2ac337fb2e5c367072c1ea7 README.md
30646b607d69dcbddbd5b41119a22f63ffb8de22929d1d0f9ff3715326a5bca6 install.sh
4c7ba2b738c924561a634befd22032830b480d230c11d7f56eee7ddf72911b60 README.md
93bde984cdce2326601ac808fe55022fe68d9d624b9dcf4ff8b023613418b3c4 install.sh
cd6bb5137dd5922e48866f6daf4b949a16f8fad321e77addd5b4f75181bc4248 uninstall.sh
1c7836f80b0845fbc5c4b8570b12d736e2a3c442b1ceb5d35d48cdd518e4ad50 app/drivers/yahboom.py
215a62acc0a5c87303d6c52c0a2585b87dfb5cdbafbe9e9e733523d48a6a9812 app/migrate.py
72c46162c33f9587c6ccb01ad53fbaaf4ecec8cd21014b909e87e8707e253e36 app/oled_font_5x7.bin
1803eebf7917291587204db85f74e2009e87f3a7b27a194b1aba245aa49b442b app/runtime.py
e5153007a3562dc48468c6cee74de55de35844fc1e0e4d0cf00feb696991e14f app/service.py
0bd9cf86e5c8c5b36ded2dc957a38ad50ec23ccc69ba91c9968d3f76391ef1c9 app/settings.py
0e1cd04786652bb0a54589b43050937c1bdc6cf1ec6a549d611cf7229c29761a config/pigway-cooling-hat.conf
af4a97339c805b5471149da29699d465cb76b20a8f1da7ad8af8e32d4a2fc827 app/service.py
413548a4b38ac3ad32712c92df5e7cd4e9ef2603872ca201d3e4df8c50dbad4a app/settings.py
9bcac51c4e3a3d409885a3041ac121a20a48596bc47ee769c7af0b8f3772fa8d config/pigway-cooling-hat.conf
c373cdde2b937bbc58d4b227f3a1d2923f0816c13f1e34548ed1f3b26c101ed5 systemd/pigway-cooling-hat.service
73b7922be93bf57169589755137dc5ec7ec7c8059c85c2bf2ee53a344e534d7e docs/HARDWARE_PLUGIN_API.md
e3bf3e7070ea62c0526791bed922688c3fb49fee36ca9c375d080119968feb72 docs/HARDWARE_PLUGIN_API.md
7ca33e7a4361673d153b5e6212447d53091e223ee05ec24411083d6c052f2894 app/local_state.py
+11 -1
View File
@@ -42,7 +42,7 @@ class Handler(BaseHTTPRequestHandler):
if self.path=='/v1/descriptor':self.send(200,descriptor())
elif self.path=='/v1/status':self.send(200,c.status())
elif self.path=='/v1/config':
with c.lock:self.send(200,{'config':c.config,'schema':descriptor()['schema'],'notes':descriptor()['notes']})
with c.lock:self.send(200,{'config':{k:v for k,v in c.config.items() if k!='integration'},'schema':{k:v for k,v in descriptor()['schema'].items() if k!='integration'},'notes':descriptor()['notes']})
else:self.send(404,{'error':'not found'})
def do_PUT(self):
@@ -53,6 +53,7 @@ class Handler(BaseHTTPRequestHandler):
elif self.path=='/v1/config':
updates=validated(body.get('updates',{}))
if not updates:raise ValueError('no configuration updates')
if 'integration' in updates:raise ValueError('change integration locally and restart the plugin')
with c.lock:
value={s:dict(v) for s,v in c.config.items()}
for section,fields in updates.items():value[section].update(fields)
@@ -62,6 +63,7 @@ class Handler(BaseHTTPRequestHandler):
backup=path.with_name(path.name+'.bak.'+str(time.time_ns()))
backup.write_bytes(path.read_bytes());backup.chmod(0o600)
save(path,value);c.config=value
log('CONFIG_UPDATED',sections=','.join(updates));self.send(200,{'ok':True})
else:self.send(404,{'error':'not found'})
except (ValueError,TypeError,KeyError) as exc:self.send(400,{'error':str(exc)})
@@ -104,6 +106,14 @@ def main():
controller.stop()
controller.driver=None
path.unlink(missing_ok=True)
if config['integration']['enabled']!='true':
stopped=threading.Event()
signal.signal(signal.SIGTERM,lambda *_:stopped.set())
signal.signal(signal.SIGINT,lambda *_:stopped.set())
log('START',plugin=descriptor()['id'],integration=False,available=controller.driver is not None)
try:stopped.wait()
finally:controller.stop();log('STOP',i2c_errors=controller.i2c_errors)
return
try:
with Server(str(path),Handler) as server:
path.chmod(0o660);server.controller=controller;server.config_path=Path(args.config)
+1
View File
@@ -17,6 +17,7 @@ def option(section,key,default,zh,en,kind="number",minimum=None,maximum=None,cho
for key,value,label in (("start",50,"启动温度"),("level2",53,"二档温度"),("level3",56,"三档温度"),("level4",59,"四档温度"),("max",62,"全速温度"),("hysteresis",1.5,"降档回差")):
option("fan",key,value,label,key.replace('_',' '),minimum=0,maximum=100 if key!="hysteresis" else 20,unit="°C")
option("integration","enabled","false","允许监控接入(重启生效)","Allow monitor integration (restart required)","enum",choices=["false","true"])
option("oled","refresh_seconds",1,"刷新周期","Refresh interval",minimum=.2,maximum=10,unit="s")
option("oled","page_seconds",10,"轮播周期","Page interval",minimum=1,maximum=120,unit="s")
option("rgb","alert_mode","breathe","告警模式","Alert mode","enum",choices=["breathe","flash"])
+5
View File
@@ -109,3 +109,8 @@ memory_breathe_color = purple
storage_breathe_color = white
network_breathe_color = blue
service_breathe_color = cyan
[integration]
# 默认不开启接入 API;true 允许本机监控通过 Unix socket 接入。修改后重启插件。
# Local features work independently. Restart after changing this setting.
enabled = false
+10
View File
@@ -79,3 +79,13 @@ fallback; actual execution still depends on functioning hardware.
Stopping a monitor, pausing its link, or unlinking a plugin does not stop the
plugin service. No API/Web service has a systemd Wants dependency that starts the
other application. Do not run two drivers for the same MCU, GPIO or fan.
## Optional listeners
Agent network API defaults to disabled (`[api] enabled=false`) and has no Web UI.
When enabled, every request requires a device bearer token.
Hardware plugin integration defaults to disabled (`[integration] enabled=false`):
no Unix API socket is created, while local telemetry and hardware workers continue.
Change integration locally and restart the plugin. Monitor-to-plugin linkage does
not require the Agent network API. The independent Web Manager stores registered
device addresses and tokens; it is not implicitly a monitored device.
+18 -1
View File
@@ -3,11 +3,14 @@ set -euo pipefail
cd "$(dirname "$0")"
NO_START=0
IMPORT=''
INTEGRATION=''
while [ "$#" -gt 0 ]; do
case "$1" in
--enable-integration) INTEGRATION=true; shift ;;
--disable-integration) INTEGRATION=false; shift ;;
--no-start) NO_START=1; shift ;;
--import-monitor-config) [ "$#" -ge 2 ] || exit 2; IMPORT="$2"; shift 2 ;;
-h|--help) echo 'Usage: sudo ./install.sh [--no-start] [--import-monitor-config /etc/pigway-pi-control.conf]'; exit 0 ;;
-h|--help) echo 'Usage: sudo ./install.sh [--no-start] [--enable-integration|--disable-integration] [--import-monitor-config /etc/pigway-pi-control.conf]'; exit 0 ;;
*) echo "ERROR: unknown option: $1" >&2; exit 2 ;;
esac
done
@@ -24,6 +27,13 @@ done
python3 -c 'import smbus2, PIL' 2>/dev/null || { apt-get update; apt-get install -y python3 python3-smbus2 python3-pil; }
# Validate a possible migration before changing installed files.
if [ -n "$IMPORT" ]; then PYTHONDONTWRITEBYTECODE=1 python3 app/migrate.py "$IMPORT" --check; fi
if [ -z "$INTEGRATION" ]; then
INTEGRATION=false
if [ -t 0 ]; then
read -r -p 'Allow local monitor integration? [y/N] ' answer
case "$answer" in y|Y|yes|YES) INTEGRATION=true ;; esac
fi
fi
getent group pigway-hardware >/dev/null || groupadd --system pigway-hardware
mkdir -p /usr/local/lib/pigway-cooling-hat/drivers
install -m 0644 app/*.py app/oled_font_5x7.bin /usr/local/lib/pigway-cooling-hat/
@@ -35,6 +45,13 @@ if [ -n "$IMPORT" ]; then
cp /etc/pigway-cooling-hat.conf "/etc/pigway-cooling-hat.conf.bak.$(date +%Y%m%d-%H%M%S)"
PYTHONDONTWRITEBYTECODE=1 python3 app/migrate.py "$IMPORT" --output /etc/pigway-cooling-hat.conf
fi
INTEGRATION="$INTEGRATION" PYTHONDONTWRITEBYTECODE=1 python3 - <<'PYCFG'
import os,sys
from pathlib import Path
sys.path.insert(0,'app')
from settings import load,save
p=Path('/etc/pigway-cooling-hat.conf');c=load(p);c['integration']['enabled']=os.environ['INTEGRATION'];save(p,c)
PYCFG
install -m 0644 systemd/pigway-cooling-hat.service /etc/systemd/system/
systemctl daemon-reload
if [ "$NO_START" -eq 0 ]; then