diff --git a/SHA256SUMS b/SHA256SUMS
index 6cd5134..ceef624 100644
--- a/SHA256SUMS
+++ b/SHA256SUMS
@@ -7,5 +7,5 @@ e83c52906953a723fc79208aecf08372a875e0bc2f74b4283329a475744f8984 README.md
3da8b5d94e2206bffc19e1c4fa493b31880c9a90fd6a02546461fe54a7255dbd config/pigway-pi-control.conf
20c584762cae3a50a498df25e11272c3c191a62c9ce823e63f8224d14637b1d4 systemd/pigway-pi-control-api.service
35ec68e1c3040e7affad43961fbb77e9e29f3474b92aed1005502629f9acb549 systemd/pigway-pi-control.service
-51ffc5a0f92ef926791352c4dbb6d23b3e610340bb0d0b666696f0af329c4d02 web/index.html
+217474cf1fea5aebe54dea8b4c3434a7e3560d4fac8ad51d7d8faa9c7819a4f2 web/index.html
73b7922be93bf57169589755137dc5ec7ec7c8059c85c2bf2ee53a344e534d7e docs/HARDWARE_PLUGIN_API.md
diff --git a/web/index.html b/web/index.html
index b91fbc7..eef440c 100644
--- a/web/index.html
+++ b/web/index.html
@@ -67,7 +67,7 @@ function ownerName(id){const [type,suffix]=String(id||'').split('|',2),name=owne
function field(section,key,value,extra=''){const id=`${section}.${key}`,opts=options[id],rule=numberRules[id],label=tr(labels[id]||key),help=tr(hints[id]||''),unit=tr(units[id]||'');let control;if(opts)control=`${opts.map(([v,l])=>`${tr(l)} `).join('')} `;else{const input=` `;control=unit?`
${input}${unit}
`:input}return``}
function pluginText(zh,en){return LANG==='zh'?zh:en}
function renderPlugins(){return `${pluginText('仅发现插件不会接管插件的设备能力。接入后发送监控状态;暂停或断开联动不会停止插件的本地功能。','Discovery does not take control of plugin device capabilities. Connecting sends monitoring state; pausing or disconnecting the link does not stop the plugin’s local functions.')}
${tr('刷新列表')}
`}
-function fanMetric(plugins){const fans=Object.entries(plugins).filter(([,p])=>p.online&&p.capabilities?.fan&&p.status?.available!==false).map(([id,p])=>{const st=p.status;let value='';if(p.capabilities.fan.rpm&&typeof st.fan_rpm==='number'&&Number.isFinite(st.fan_rpm)&&st.fan_rpm>=0)value=st.fan_rpm+' RPM';else if(st.fan_name&&st.fan_name!=='UNAVAILABLE')value=pluginText('档位 ','level ')+(st.fan_name==='OFF'?pluginText('关闭','Off'):st.fan_name);return value?{name:p.name||id,value}:null}).filter(Boolean);return fans.length?` · ${fans.map(f=>esc(pluginText('风扇','Fan')+(fans.length>1?' '+f.name:'')+' · '+f.value)).join(' · ')} `:''}
+function fanMetric(plugins){const fans=Object.entries(plugins).filter(([,p])=>p.online&&p.capabilities?.fan&&p.status?.available!==false).map(([id,p])=>{const st=p.status;let value='';if(p.capabilities.fan.rpm&&typeof st.fan_rpm==='number'&&Number.isFinite(st.fan_rpm)&&st.fan_rpm>=0)value=st.fan_rpm+' RPM';else if(st.fan_name&&st.fan_name!=='UNAVAILABLE')value=({OFF:pluginText('关闭','Off'),L1:pluginText('低速','Low'),L2:pluginText('中低速','Medium-low'),L3:pluginText('中速','Medium'),L4:pluginText('高速','High'),MAX:pluginText('全速','Full speed')})[st.fan_name]||st.fan_name;return value?{name:p.name||id,value}:null}).filter(Boolean);return fans.length?` · ${fans.map(f=>esc(pluginText('风扇','Fan')+(fans.length>1?' '+f.name:'')+' '+f.value)).join(' · ')} `:''}
function pluginSummary(plugins,host){if(!Object.keys(plugins).length)return `${pluginText('风扇与硬件插件','Fan and hardware plugins')} ${pluginText('尚未接入硬件插件,风扇状态不可用。独立安装并启动插件后,可在插件管理中接入。','No hardware plugin connected; fan status is unavailable. Install and start a plugin independently, then connect it in plugin management.')}
${pluginText('管理插件','Manage plugin')} `;return Object.entries(plugins).map(([id,p])=>{const st=p.status||{},cap=p.capabilities||{},details=[];if(p.online){if(cap.display)details.push('OLED '+esc(ownerName(st.oled_page||'')));if(cap.rgb)details.push('RGB '+esc(rgbName(st.rgb_mode||'')))}return `${esc(p.name||id)} · ${p.online?tr('在线'):tr('离线')} ${details.join(' · ')}${pluginText('管理插件','Manage plugin')}
`}).join('')}
let pluginRows=[],pluginDraft=null,pluginSelected='';