From ac9bc4befd6e42f6ce615251b404452c542a5d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A1=E5=8F=A3=E6=B5=AA=E4=BA=BA?= Date: Sun, 27 Sep 2026 09:54:45 +0800 Subject: [PATCH] fix: align RGB colors with fault domains --- README.md | 2 +- SHA256SUMS | 6 ++--- app/pigway_pi_control.py | 44 +++++++++++++++++++++++------------ config/pigway-pi-control.conf | 7 +++--- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c8f68ea..55620a9 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ CHECK STORAGE ### 5. RGB 告警引擎 -RGB 模式由当前 OLED 告警页决定;OLED 位于主页时则使用最高优先级 active owner。颜色表示故障对象:CPU/供电/降频为黄色,温度为红色,内存为紫色,磁盘为白色,网络/Wi-Fi 为蓝色,服务/进程为青色。速度表示严重等级:Warning 慢呼吸、Critical 快呼吸、Emergency 纯红快速频闪;没有 active alert 时 OFF。模式变化时按 `speed → color → effect` 写入,呼吸动画完全由 MCU 生成,不使用软件 PWM 或逐帧 R/G/B 写入。相邻页面 RGB mode 相同时不关闭、不重启灯效。动画不写循环日志,owner 变化记录 `DISPLAY_OWNER`,实际模式变化才记录 `RGB_MODE`。 +RGB 模式由当前 OLED 告警页决定;OLED 位于主页时则使用最高优先级 active owner。颜色表示故障对象:CPU负载、频率、throttling、温度与散热相关为红色,供电/欠压使用实机校准的橙色闪烁,内存为紫色,存储为白色,网络/Wi-Fi 为蓝色,服务/进程为青色。通常 Warning 慢呼吸、Critical 快呼吸、Emergency 使用对象颜色快速频闪;供电告警例外使用 750ms 亮/灭,避开实机偏绿的 MCU 内置黄色。没有 active alert 时 OFF。模式变化时按已校准的 MCU 协议写入,内置颜色的呼吸完全由 MCU 生成,不使用软件 PWM。相邻页面 RGB mode 相同时不关闭、不重启灯效。动画不写循环日志,owner 变化记录 `DISPLAY_OWNER`,实际模式变化才记录 `RGB_MODE`。 ### 6. 结构化事件日志 diff --git a/SHA256SUMS b/SHA256SUMS index 9ac4ab8..a6d7d65 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -1,7 +1,7 @@ -1dd807bcc8e6711fed829f42e84f8baf4d644e7248a5a39bd17f65d14090e35e README.md +80ac1d84bb2daff8bb4a1182aad9198726f25624e8d253065e71a4eb062857e2 README.md 72c46162c33f9587c6ccb01ad53fbaaf4ecec8cd21014b909e87e8707e253e36 app/oled_font_5x7.bin -e04cd1b7940ce5fa0539586dbaaac654a3234aa6e0306d77d1e4b2b1c918ab9c app/pigway_pi_control.py -7155b4585c8caf60ef8d9a0c0676229b742d86bd938fbd9303ca9bfbb8964be6 config/pigway-pi-control.conf +621889210c68d4eba96d79bd8299ae435fe8f2a057ffc77c3cf86fc8c22a3060 app/pigway_pi_control.py +403f73cd12ea87a3ac08b447d5438e9ceeef0fbde1e54af40304167bde470f1e config/pigway-pi-control.conf 4c2816da08378f2927c746b70ecfa7d5312e1730c08727cd89e8c2801e707054 install.sh 81aea0a89b298b2512fcb9bf6418618ba7a66474d425390cf95a0128c4093474 systemd/pigway-pi-control.service a3d74973cafbabaf58a660a8c83d28347d1c06d9fd2dab3ecfce5b7607f6fee2 uninstall.sh diff --git a/app/pigway_pi_control.py b/app/pigway_pi_control.py index 1f483f3..48cd595 100755 --- a/app/pigway_pi_control.py +++ b/app/pigway_pi_control.py @@ -262,14 +262,21 @@ ROW_Y=[cf("display","row1_y",0,int),cf("display","row2_y",8,int),cf("display","r DARK_PWR=cf("dark_mode","pi_pwr_led",True,lambda x: str(x).strip().lower() in ("off","0","false","no")) DARK_ACT=cf("dark_mode","pi_act_led",True,lambda x: str(x).strip().lower() in ("off","0","false","no")) EMERGENCY_FLASH_MS=cf("led","emergency_flash_ms",300,int) +POWER_BLINK_MS=cf("led","power_blink_ms",750,int) RGB_WRITE_DELAY_MS=max(1,cf("led","write_delay_ms",10,int)) RGB_WARNING_SPEED=max(1,min(3,cf("led","warning_speed",1,int))) RGB_CRITICAL_SPEED=max(1,min(3,cf("led","critical_speed",3,int))) RGB_COLOR_VALUES={"RED":0,"GREEN":1,"BLUE":2,"YELLOW":3,"PURPLE":4,"CYAN":5,"WHITE":6} +RGB_STATIC_VALUES={ + "RED":(0xff,0x00,0x00),"GREEN":(0x00,0xff,0x00),"BLUE":(0x00,0x00,0xff), + "YELLOW":(0xff,0xff,0x00),"PURPLE":(0xff,0x00,0xff),"CYAN":(0x00,0xff,0xff), + "WHITE":(0xff,0xff,0xff),"ORANGE":(0xff,0x28,0x00), +} RGB_OBJECT_COLORS={ - "ROOT_READONLY":"RED", + "ROOT_READONLY":"WHITE", "TEMP_CRITICAL":"RED","TEMP_HIGH":"RED","SOFT_TEMP_LIMIT":"RED", - "POWER_UNDERVOLT":"YELLOW","THROTTLED":"YELLOW","FREQ_CAPPED":"YELLOW","CPU_HIGH":"YELLOW", + "THROTTLED":"RED","FREQ_CAPPED":"RED","CPU_HIGH":"RED", + "POWER_UNDERVOLT":"YELLOW", "MEMORY_HIGH":"PURPLE", "DISK_CRITICAL":"WHITE","DISK_HIGH":"WHITE", "NETWORK_DOWN":"BLUE","INTERNET_DOWN":"BLUE","WIFI_WEAK":"BLUE", @@ -420,21 +427,25 @@ def update_active_alerts(cpu,temp,mem,disk,sample_elapsed=0.0): def rgb_mode_for(request_id,severity): if severity<=0: return "OFF" - color=RGB_OBJECT_COLORS[request_id.split("|",1)[0]] - if severity>=3: return "EMERGENCY_FLASH" + alert_type=request_id.split("|",1)[0] + if alert_type=="POWER_UNDERVOLT": return "ORANGE_CRITICAL_BLINK" + color=RGB_OBJECT_COLORS[alert_type] + if severity>=3: return f"{color}_EMERGENCY_FLASH" return f"{color}_{'FAST' if severity>=2 else 'SLOW'}_BREATHE" -def rgb_emergency(on): +def rgb_flash(mode,on): global _last_rgb - state=("EMERGENCY_FLASH",bool(on)) + color=mode.split("_",1)[0] + channels=RGB_STATIC_VALUES[color] + state=(mode,bool(on)) if _last_rgb==state: return if not on: mcu(0x07,0x00) else: mcu(0x00,0xff); time.sleep(RGB_WRITE_DELAY_MS/1000.0) - mcu(0x01,0xff); time.sleep(RGB_WRITE_DELAY_MS/1000.0) - mcu(0x02,0x00); time.sleep(RGB_WRITE_DELAY_MS/1000.0) - mcu(0x03,0x00) + mcu(0x01,channels[0]); time.sleep(RGB_WRITE_DELAY_MS/1000.0) + mcu(0x02,channels[1]); time.sleep(RGB_WRITE_DELAY_MS/1000.0) + mcu(0x03,channels[2]) _last_rgb=state def render_home(cpu,temp,mem,disk): @@ -614,27 +625,30 @@ class DisplayController: with self.lock: return dict(self.requests[self.current_owner_id]) def _rgb_worker(self): - previous_error=None; applied_mode="OFF"; animation_started=time.monotonic() + previous_error=None; applied_mode="OFF"; applied_flash=False; animation_started=time.monotonic() while not self.stop_event.is_set(): try: with self.lock: wanted_mode=self.current_rgb_mode + wanted_flash=wanted_mode.endswith(("_EMERGENCY_FLASH","_CRITICAL_BLINK")) if wanted_mode!=applied_mode: with bus_lock: if wanted_mode=="OFF": rgb_off() - elif wanted_mode=="EMERGENCY_FLASH": rgb_emergency(True) + elif wanted_flash: rgb_flash(wanted_mode,True) else: rgb_mode(wanted_mode) log("INFO","RGB_MODE",old=applied_mode,new=wanted_mode) applied_mode=wanted_mode; animation_started=time.monotonic() - if applied_mode=="EMERGENCY_FLASH": + applied_flash=applied_mode.endswith(("_EMERGENCY_FLASH","_CRITICAL_BLINK")) + if applied_flash: elapsed=time.monotonic()-animation_started - on=int(elapsed*1000/max(50,EMERGENCY_FLASH_MS))%2==0 - with bus_lock: rgb_emergency(on) + interval=POWER_BLINK_MS if applied_mode=="ORANGE_CRITICAL_BLINK" else EMERGENCY_FLASH_MS + on=int(elapsed*1000/max(50,interval))%2==0 + with bus_lock: rgb_flash(applied_mode,on) if previous_error is not None: log("INFO","RGB_RECOVERED") previous_error=None except Exception as e: if repr(e)!=previous_error: log("ERROR","RGB_ERROR",error=repr(e)) previous_error=repr(e) - delay=0.04 if applied_mode=="EMERGENCY_FLASH" else 0.25 + delay=0.04 if applied_flash else 0.25 self.stop_event.wait(delay) def shutdown(self): diff --git a/config/pigway-pi-control.conf b/config/pigway-pi-control.conf index 0961665..fda14fc 100644 --- a/config/pigway-pi-control.conf +++ b/config/pigway-pi-control.conf @@ -141,15 +141,15 @@ wifi_weak_seconds = 30 [led] # DisplayController 按集中定义的 priority 仲裁 owner。告警页显示时 RGB 同步显示 # 该页对象;主页使用最高优先级 active owner。颜色表示对象,速度表示严重等级: -# CPU/供电/降频 = 黄色 -# 温度 = 红色 +# CPU/频率/温度/散热 = 红色 +# 供电/欠压 = 橙色闪烁(实机校准,避免内置黄色偏绿) # 内存 = 紫色 # 磁盘 = 白色 # 网络/Wi-Fi = 蓝色 # 服务/进程 = 青色 # Warning (1) = 对象颜色慢呼吸 # Critical (2) = 对象颜色快呼吸 -# Emergency (3) = 纯红快速频闪 +# Emergency (3) = 对象颜色快速频闪 # NORMAL_HOME 为 owner 时 RGB OFF。 # # 该旧版 MCU 由内置灯效产生呼吸。仅在模式变化时按 @@ -159,6 +159,7 @@ warning_speed = 1 critical_speed = 3 write_delay_ms = 10 emergency_flash_ms = 300 +power_blink_ms = 750 # v3.3.0 之前的 brightness、*_breathe_seconds、rgb_update_ms、warning_color、 # critical_color 和 *_r/g/b