feat: make plugin integration opt-in
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo ./install.sh
|
sudo ./install.sh
|
||||||
|
# 允许监控接入(默认关闭):
|
||||||
|
sudo ./install.sh --enable-integration
|
||||||
# 只安装,不启动/启用新服务:
|
# 只安装,不启动/启用新服务:
|
||||||
sudo ./install.sh --no-start
|
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 采用正常灯效配置,默认全灭。
|
无论是否接入监控,插件都自行采集 CPU、内存、温度、磁盘和主网络状态,OLED 主页显示本机数据,风扇执行本地温控,RGB 采用正常灯效配置,默认全灭。
|
||||||
温度传感器读取失败时请求风扇全速;停止插件时也请求全速并关闭 OLED/RGB,避免因退出显示程序而取消散热。硬件故障时不能保证执行成功,错误会记录日志。风扇无转速反馈,只报告设置档位,不伪造 RPM。
|
温度传感器读取失败时请求风扇全速;停止插件时也请求全速并关闭 OLED/RGB,避免因退出显示程序而取消散热。硬件故障时不能保证执行成功,错误会记录日志。风扇无转速反馈,只报告设置档位,不伪造 RPM。
|
||||||
|
|
||||||
## 接入监控
|
## 可选接入监控
|
||||||
|
|
||||||
|
安装时可选择允许接入,默认否;非交互安装默认关闭。关闭时不创建 API socket,本机采集、显示和温控继续运行。也可修改 `/etc/pigway-cooling-hat.conf` 的 `[integration] enabled = true/false` 并重启插件。`--disable-integration` 显式关闭。
|
||||||
|
|
||||||
|
|
||||||
可选接入 [PIGWay Pi Control 监控平台](https://tea.pigway.com/way/pigway-pi-control),接收服务、进程和系统健康告警。双方独立安装、独立运行;不接入也可正常使用本地功能。
|
可选接入 [PIGWay Pi Control 监控平台](https://tea.pigway.com/way/pigway-pi-control),接收服务、进程和系统健康告警。双方独立安装、独立运行;不接入也可正常使用本地功能。
|
||||||
|
|
||||||
监控 Web → 系统配置 → 硬件插件中发现并手动接入。协议是本机 Unix socket 上的 HTTP API,默认不开放 TCP 端口。目录 0750、socket 0660,只有 root 和被明确加入 `pigway-hardware` 组的账户能访问。不要随意授予组成员资格。
|
[独立 Web 管理中心](https://tea.pigway.com/way/pigway-web-manager) → 系统配置 → 硬件插件中发现并手动接入。协议是本机 Unix socket 上的 HTTP API,默认不开放 TCP 端口。目录 0750、socket 0660,只有 root 和被明确加入 `pigway-hardware` 组的账户能访问。不要随意授予组成员资格。
|
||||||
|
|
||||||
监控发送完整、带版本和有效期的状态;不发送动画帧或 MCU 指令。插件自己仲裁 OLED 页和 RGB 效果。告警页有页码并轮播主页,相同 RGB 效果不因切页重启。只有一个有效监控来源可以持有显示状态,重复/乱序版本会拒绝。状态超过 10 秒未更新(由客户端声明,允许3–30秒),旧告警失效,OLED 回到本机主页、RGB 回到正常灯效,风扇继续本地温控。主动断开行为相同。监控快照中的 system/network 字段保留 API v1 兼容,但不会覆盖插件的本地数据。采样失败显示 --,不伪装为零。
|
监控发送完整、带版本和有效期的状态;不发送动画帧或 MCU 指令。插件自己仲裁 OLED 页和 RGB 效果。告警页有页码并轮播主页,相同 RGB 效果不因切页重启。只有一个有效监控来源可以持有显示状态,重复/乱序版本会拒绝。状态超过 10 秒未更新(由客户端声明,允许3–30秒),旧告警失效,OLED 回到本机主页、RGB 回到正常灯效,风扇继续本地温控。主动断开行为相同。监控快照中的 system/network 字段保留 API v1 兼容,但不会覆盖插件的本地数据。采样失败显示 --,不伪装为零。
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -1,13 +1,13 @@
|
|||||||
1331f33332c6c33ecfc3e91d8734ee3ff7755ac1c2ac337fb2e5c367072c1ea7 README.md
|
2b17b2e0a9bce7f9af8cb6ab5905e01721dddf7513ffd1dfc8c73120dfccbe23 README.md
|
||||||
30646b607d69dcbddbd5b41119a22f63ffb8de22929d1d0f9ff3715326a5bca6 install.sh
|
93bde984cdce2326601ac808fe55022fe68d9d624b9dcf4ff8b023613418b3c4 install.sh
|
||||||
cd6bb5137dd5922e48866f6daf4b949a16f8fad321e77addd5b4f75181bc4248 uninstall.sh
|
cd6bb5137dd5922e48866f6daf4b949a16f8fad321e77addd5b4f75181bc4248 uninstall.sh
|
||||||
1c7836f80b0845fbc5c4b8570b12d736e2a3c442b1ceb5d35d48cdd518e4ad50 app/drivers/yahboom.py
|
1c7836f80b0845fbc5c4b8570b12d736e2a3c442b1ceb5d35d48cdd518e4ad50 app/drivers/yahboom.py
|
||||||
215a62acc0a5c87303d6c52c0a2585b87dfb5cdbafbe9e9e733523d48a6a9812 app/migrate.py
|
215a62acc0a5c87303d6c52c0a2585b87dfb5cdbafbe9e9e733523d48a6a9812 app/migrate.py
|
||||||
72c46162c33f9587c6ccb01ad53fbaaf4ecec8cd21014b909e87e8707e253e36 app/oled_font_5x7.bin
|
72c46162c33f9587c6ccb01ad53fbaaf4ecec8cd21014b909e87e8707e253e36 app/oled_font_5x7.bin
|
||||||
1803eebf7917291587204db85f74e2009e87f3a7b27a194b1aba245aa49b442b app/runtime.py
|
1803eebf7917291587204db85f74e2009e87f3a7b27a194b1aba245aa49b442b app/runtime.py
|
||||||
e5153007a3562dc48468c6cee74de55de35844fc1e0e4d0cf00feb696991e14f app/service.py
|
af4a97339c805b5471149da29699d465cb76b20a8f1da7ad8af8e32d4a2fc827 app/service.py
|
||||||
0bd9cf86e5c8c5b36ded2dc957a38ad50ec23ccc69ba91c9968d3f76391ef1c9 app/settings.py
|
413548a4b38ac3ad32712c92df5e7cd4e9ef2603872ca201d3e4df8c50dbad4a app/settings.py
|
||||||
0e1cd04786652bb0a54589b43050937c1bdc6cf1ec6a549d611cf7229c29761a config/pigway-cooling-hat.conf
|
9bcac51c4e3a3d409885a3041ac121a20a48596bc47ee769c7af0b8f3772fa8d config/pigway-cooling-hat.conf
|
||||||
c373cdde2b937bbc58d4b227f3a1d2923f0816c13f1e34548ed1f3b26c101ed5 systemd/pigway-cooling-hat.service
|
c373cdde2b937bbc58d4b227f3a1d2923f0816c13f1e34548ed1f3b26c101ed5 systemd/pigway-cooling-hat.service
|
||||||
73b7922be93bf57169589755137dc5ec7ec7c8059c85c2bf2ee53a344e534d7e docs/HARDWARE_PLUGIN_API.md
|
e3bf3e7070ea62c0526791bed922688c3fb49fee36ca9c375d080119968feb72 docs/HARDWARE_PLUGIN_API.md
|
||||||
7ca33e7a4361673d153b5e6212447d53091e223ee05ec24411083d6c052f2894 app/local_state.py
|
7ca33e7a4361673d153b5e6212447d53091e223ee05ec24411083d6c052f2894 app/local_state.py
|
||||||
|
|||||||
+11
-1
@@ -42,7 +42,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
if self.path=='/v1/descriptor':self.send(200,descriptor())
|
if self.path=='/v1/descriptor':self.send(200,descriptor())
|
||||||
elif self.path=='/v1/status':self.send(200,c.status())
|
elif self.path=='/v1/status':self.send(200,c.status())
|
||||||
elif self.path=='/v1/config':
|
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'})
|
else:self.send(404,{'error':'not found'})
|
||||||
|
|
||||||
def do_PUT(self):
|
def do_PUT(self):
|
||||||
@@ -53,6 +53,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
elif self.path=='/v1/config':
|
elif self.path=='/v1/config':
|
||||||
updates=validated(body.get('updates',{}))
|
updates=validated(body.get('updates',{}))
|
||||||
if not updates:raise ValueError('no configuration 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:
|
with c.lock:
|
||||||
value={s:dict(v) for s,v in c.config.items()}
|
value={s:dict(v) for s,v in c.config.items()}
|
||||||
for section,fields in updates.items():value[section].update(fields)
|
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=path.with_name(path.name+'.bak.'+str(time.time_ns()))
|
||||||
backup.write_bytes(path.read_bytes());backup.chmod(0o600)
|
backup.write_bytes(path.read_bytes());backup.chmod(0o600)
|
||||||
save(path,value);c.config=value
|
save(path,value);c.config=value
|
||||||
|
|
||||||
log('CONFIG_UPDATED',sections=','.join(updates));self.send(200,{'ok':True})
|
log('CONFIG_UPDATED',sections=','.join(updates));self.send(200,{'ok':True})
|
||||||
else:self.send(404,{'error':'not found'})
|
else:self.send(404,{'error':'not found'})
|
||||||
except (ValueError,TypeError,KeyError) as exc:self.send(400,{'error':str(exc)})
|
except (ValueError,TypeError,KeyError) as exc:self.send(400,{'error':str(exc)})
|
||||||
@@ -104,6 +106,14 @@ def main():
|
|||||||
controller.stop()
|
controller.stop()
|
||||||
controller.driver=None
|
controller.driver=None
|
||||||
path.unlink(missing_ok=True)
|
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:
|
try:
|
||||||
with Server(str(path),Handler) as server:
|
with Server(str(path),Handler) as server:
|
||||||
path.chmod(0o660);server.controller=controller;server.config_path=Path(args.config)
|
path.chmod(0o660);server.controller=controller;server.config_path=Path(args.config)
|
||||||
|
|||||||
@@ -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,"降档回差")):
|
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("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","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("oled","page_seconds",10,"轮播周期","Page interval",minimum=1,maximum=120,unit="s")
|
||||||
option("rgb","alert_mode","breathe","告警模式","Alert mode","enum",choices=["breathe","flash"])
|
option("rgb","alert_mode","breathe","告警模式","Alert mode","enum",choices=["breathe","flash"])
|
||||||
|
|||||||
@@ -109,3 +109,8 @@ memory_breathe_color = purple
|
|||||||
storage_breathe_color = white
|
storage_breathe_color = white
|
||||||
network_breathe_color = blue
|
network_breathe_color = blue
|
||||||
service_breathe_color = cyan
|
service_breathe_color = cyan
|
||||||
|
|
||||||
|
[integration]
|
||||||
|
# 默认不开启接入 API;true 允许本机监控通过 Unix socket 接入。修改后重启插件。
|
||||||
|
# Local features work independently. Restart after changing this setting.
|
||||||
|
enabled = false
|
||||||
|
|||||||
@@ -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
|
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
|
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.
|
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
@@ -3,11 +3,14 @@ set -euo pipefail
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
NO_START=0
|
NO_START=0
|
||||||
IMPORT=''
|
IMPORT=''
|
||||||
|
INTEGRATION=''
|
||||||
while [ "$#" -gt 0 ]; do
|
while [ "$#" -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
--enable-integration) INTEGRATION=true; shift ;;
|
||||||
|
--disable-integration) INTEGRATION=false; shift ;;
|
||||||
--no-start) NO_START=1; shift ;;
|
--no-start) NO_START=1; shift ;;
|
||||||
--import-monitor-config) [ "$#" -ge 2 ] || exit 2; IMPORT="$2"; shift 2 ;;
|
--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 ;;
|
*) echo "ERROR: unknown option: $1" >&2; exit 2 ;;
|
||||||
esac
|
esac
|
||||||
done
|
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; }
|
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.
|
# Validate a possible migration before changing installed files.
|
||||||
if [ -n "$IMPORT" ]; then PYTHONDONTWRITEBYTECODE=1 python3 app/migrate.py "$IMPORT" --check; fi
|
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
|
getent group pigway-hardware >/dev/null || groupadd --system pigway-hardware
|
||||||
mkdir -p /usr/local/lib/pigway-cooling-hat/drivers
|
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/
|
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)"
|
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
|
PYTHONDONTWRITEBYTECODE=1 python3 app/migrate.py "$IMPORT" --output /etc/pigway-cooling-hat.conf
|
||||||
fi
|
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/
|
install -m 0644 systemd/pigway-cooling-hat.service /etc/systemd/system/
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
if [ "$NO_START" -eq 0 ]; then
|
if [ "$NO_START" -eq 0 ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user