`;
vars.forEach(v => {
const typeIcon = { 'bool': '
', 'int': '
', 'float': '
', 'string': '
' }[v.type] || '
';
- const displayValue = v.value !== undefined ? String(v.value).substring(0, 30) : '-';
+ const rawName = v.name || '';
+ const safeName = escapeHtml(rawName);
+ const displayMeta = v.value !== undefined ? String(v.value).substring(0, 30) : (v.type || '-');
+ const metaLabel = v.value !== undefined
+ ? (typeof t === 'function' ? t('sshPage.varTableValue') : '当前值')
+ : (typeof t === 'function' ? t('common.type') : '类型');
html += `
-
+ onclick="selectVariable(this.dataset.name)">
${typeIcon}
-
${v.name}
-
当前值: ${displayValue}${displayValue.length >= 30 ? '...' : ''}
+
${safeName}
+
${escapeHtml(metaLabel)}: ${escapeHtml(displayMeta)}${displayMeta.length >= 30 ? '...' : ''}
`;
diff --git a/components/ts_webui/web/js/lang/en-US.js b/components/ts_webui/web/js/lang/en-US.js
index a746bdb..dd6a32e 100644
--- a/components/ts_webui/web/js/lang/en-US.js
+++ b/components/ts_webui/web/js/lang/en-US.js
@@ -113,6 +113,7 @@ if (typeof i18n !== 'undefined') i18n.registerLanguage('en-US', {
secondsAgo: ' seconds ago',
minutesAgo: ' minutes ago',
hoursAgo: ' hours ago',
+ daysAgo: ' days ago',
// Timezone settings
setTimezone: 'Set Timezone',
timezone: 'Timezone',
@@ -2733,6 +2734,19 @@ if (typeof i18n !== 'undefined') i18n.registerLanguage('en-US', {
speedAdjust: 'Speed Adjust',
editTempCurve: 'Edit Temperature Curve',
manualModeHint: 'Switch to manual mode to adjust',
+ autoStateIdle: 'Idle',
+ autoStateBaseline: 'Baseline',
+ autoStateActive: 'Adaptive',
+ autoStateGuard: 'Guard',
+ autoStateStale: 'Stale',
+ autoStateUnknown: 'Unknown',
+ guardTempShort: 'Guard',
+ predictedTempShort: 'Predicted',
+ autoHelpTitle: 'What makes Auto mode different?',
+ autoHelpIntro: 'Auto mode adjusts fan speed based on the current cooling condition of the device, instead of only following a fixed curve.',
+ autoHelpCurveDiff: 'Curve mode works like a fixed rule table: when temperature reaches a point, the fan uses the matching speed. It is stable and predictable when you want to define the cooling behavior yourself.',
+ autoHelpAdaptive: 'Auto mode still uses your curve as the starting point, then watches how temperature is changing. If the device is heating up quickly, it speeds up earlier; when temperature is steadily falling, it lowers speed gradually.',
+ autoHelpSafety: 'The goal is to control temperature swings before they get too high. Light workloads stay quieter, while heavy workloads or abnormal temperature data put device protection first.',
// Curve management modal
curveManagement: 'Fan Curve Management',
selectFan: 'Select Fan',
@@ -2783,6 +2797,11 @@ if (typeof i18n !== 'undefined') i18n.registerLanguage('en-US', {
weightedTemp: 'Weighted Temp',
weightSumWarning: 'Weight sum should be 1.0 (current: {sum})',
boundVarCount: 'Bound: {count}',
+ boundVarsStale: 'Bound: {count} (all stale)',
+ boundVarsPartialStale: 'Bound: {fresh}/{count} fresh',
+ boundVarsInvalid: 'Bound: {count} (no valid temp)',
+ boundVarsStaleShort: 'Temperature variables stale',
+ boundVarsInvalidShort: 'No valid temperature data',
bindAll: 'Bind All',
unbindAll: 'Unbind All',
selectVariableWeightHint: 'Add float variables with weights (0-1), sum should be 1.0',
diff --git a/components/ts_webui/web/js/lang/zh-CN.js b/components/ts_webui/web/js/lang/zh-CN.js
index 98b1254..4256320 100644
--- a/components/ts_webui/web/js/lang/zh-CN.js
+++ b/components/ts_webui/web/js/lang/zh-CN.js
@@ -113,6 +113,7 @@ if (typeof i18n !== 'undefined') i18n.registerLanguage('zh-CN', {
secondsAgo: '秒前',
minutesAgo: '分钟前',
hoursAgo: '小时前',
+ daysAgo: '天前',
// 时区设置
setTimezone: '设置时区',
timezone: '时区',
@@ -2718,6 +2719,19 @@ if (typeof i18n !== 'undefined') i18n.registerLanguage('zh-CN', {
speedAdjust: '转速调节',
editTempCurve: '编辑温度曲线',
manualModeHint: '切换到手动模式后可调节',
+ autoStateIdle: '待机',
+ autoStateBaseline: '基线',
+ autoStateActive: '自适应',
+ autoStateGuard: '保护',
+ autoStateStale: '失效',
+ autoStateUnknown: '未知',
+ guardTempShort: '保护',
+ predictedTempShort: '预测',
+ autoHelpTitle: '自动模式有什么不同?',
+ autoHelpIntro: '自动模式会根据设备当前的散热状态实时调节风扇,而不是只按一条固定曲线运行。',
+ autoHelpCurveDiff: '曲线模式更像一张固定规则表:温度到多少,风扇就转到多少。它稳定、可预期,适合你想手动定义散热策略的场景。',
+ autoHelpAdaptive: '自动模式会在曲线的基础上继续判断温度变化趋势。如果设备正在快速升温,它会提前加速;如果温度稳定下降,它才会慢慢把转速降下来。',
+ autoHelpSafety: '这样做的目的,是让风扇不只是看到温度后再反应,而是尽量提前压住温度波动。日常轻载时保持更安静,高负载或温度异常时优先保护设备安全。',
// 曲线管理模态框
curveManagement: '风扇曲线管理',
selectFan: '选择风扇',
@@ -2768,6 +2782,11 @@ if (typeof i18n !== 'undefined') i18n.registerLanguage('zh-CN', {
weightedTemp: '加权温度',
weightSumWarning: '权重总和建议为 1.0(当前: {sum})',
boundVarCount: '已绑定: {count} 个',
+ boundVarsStale: '已绑定: {count} 个(全部过期)',
+ boundVarsPartialStale: '已绑定: {fresh}/{count} 有效',
+ boundVarsInvalid: '已绑定: {count} 个(无有效温度)',
+ boundVarsStaleShort: '温度变量已过期',
+ boundVarsInvalidShort: '温度变量无有效数据',
bindAll: '绑定全部',
unbindAll: '解绑全部',
selectVariableWeightHint: '添加多个浮点变量并设置权重(0-1),权重总和建议为 1.0',
diff --git a/docs/API_REFERENCE.md b/docs/API_REFERENCE.md
index 3d5f3f6..75971a6 100644
--- a/docs/API_REFERENCE.md
+++ b/docs/API_REFERENCE.md
@@ -203,10 +203,16 @@ POST /api/v1/config/delete
- 自动化引擎数据源
- 命令参数动态替换
-### var.get
+### automation.variables.get
获取变量值。
-**请求**: `GET /api/v1/var/get?name=cpu_temp.extracted`
+**请求**:
+```json
+POST /api/v1/automation.variables.get
+{
+ "name": "cpu_temp.extracted"
+}
+```
**响应**:
```json
@@ -214,11 +220,7 @@ POST /api/v1/config/delete
"code": 0,
"data": {
"name": "cpu_temp.extracted",
- "value": "45000",
- "type": "string",
- "source": "ssh",
- "timestamp": 1706355600000,
- "persistent": false
+ "value": "45000"
}
}
```
@@ -231,16 +233,16 @@ POST /api/v1/config/delete
}
```
-### var.set
+### automation.variables.set
设置变量值。
**请求**:
-```
-POST /api/v1/var/set
+```json
+POST /api/v1/automation.variables.set
{
"name": "my_variable",
"value": "hello world",
- "persistent": false
+ "create_only": false
}
```
@@ -248,41 +250,35 @@ POST /api/v1/var/set
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 是 | 变量名(最大 64 字符) |
-| `value` | string | 是 | 变量值(最大 256 字符) |
-| `persistent` | bool | 否 | 是否持久化到 NVS(默认 false) |
+| `value` | bool/int/float/string/null | 是 | 变量值 |
+| `create_only` | bool | 否 | 仅在不存在时创建,不覆盖已有变量 |
**响应**:
```json
{
"code": 0,
- "message": "Variable set successfully"
+ "message": "Variable set successfully",
+ "data": {
+ "name": "my_variable",
+ "value": "hello world"
+ }
}
```
-### var.delete
-删除变量。
+### automation.variables.list
+列出自动化变量,支持按前缀或来源过滤。默认返回变量值和更新时间元数据;资源敏感的选择器类 UI 可传 `include_value:false, include_meta:false`。
**请求**:
-```
-POST /api/v1/var/delete
-{
- "name": "my_variable"
-}
-```
-
-**响应**:
```json
+POST /api/v1/automation.variables.list
{
- "code": 0,
- "message": "Variable deleted"
+ "prefix": "agx.",
+ "source_id": "agx",
+ "include_value": true,
+ "include_meta": true
}
```
-### var.list
-列出所有变量或指定前缀的变量。
-
-**请求**: `GET /api/v1/var/list` 或 `GET /api/v1/var/list?prefix=cpu_temp`
-
**响应**:
```json
{
@@ -291,28 +287,28 @@ POST /api/v1/var/delete
"count": 3,
"variables": [
{
- "name": "cpu_temp.extracted",
- "value": "45000",
- "type": "string",
- "source": "ssh",
- "timestamp": 1706355600000,
- "persistent": false
+ "name": "agx.tj.temp",
+ "value": 72.5,
+ "type": "float",
+ "persistent": false,
+ "readonly": false,
+ "last_change_ms": 123456,
+ "last_update_ms": 123900,
+ "age_ms": 250,
+ "stale": false,
+ "source_id": "agx"
},
{
"name": "cpu_temp.status",
- "value": "success",
+ "value": "running",
"type": "string",
- "source": "ssh",
- "timestamp": 1706355600000,
- "persistent": false
- },
- {
- "name": "cpu_temp.exit_code",
- "value": "0",
- "type": "number",
- "source": "ssh",
- "timestamp": 1706355600000,
- "persistent": false
+ "persistent": false,
+ "readonly": false,
+ "last_change_ms": 123000,
+ "last_update_ms": 123900,
+ "age_ms": 250,
+ "stale": false,
+ "source_id": "cpu_temp"
}
]
}
@@ -323,9 +319,10 @@ POST /api/v1/var/delete
| 类型 | 说明 | 示例 |
|------|------|------|
-| `string` | 字符串(默认) | `"hello"` |
-| `number` | 数值(存储为字符串) | `"45000"` |
-| `bool` | 布尔值 | `"true"`, `"false"` |
+| `string` | 字符串 | `"hello"` |
+| `int` | 整数 | `45000` |
+| `float` | 浮点数 | `72.5` |
+| `bool` | 布尔值 | `true`, `false` |
### 变量来源
@@ -513,8 +510,24 @@ POST /api/v1/device/power
"code": 0,
"data": {
"fans": [
- {"id": 0, "running": true, "duty": 50, "rpm": 2500},
- {"id": 1, "running": true, "duty": 60, "rpm": 2800}
+ {
+ "id": 0,
+ "mode": "auto",
+ "duty": 50,
+ "target_duty": 55,
+ "rpm": 2500,
+ "temp": 58.2,
+ "running": true,
+ "control_temperature": 58.2,
+ "guard_temperature": 72.5,
+ "predicted_temperature": 64.0,
+ "slope_c_per_min": 0.8,
+ "controller_gain": 0.75,
+ "cooling_response": 0.4,
+ "auto_state": "active",
+ "guard_active": false,
+ "temp_stale": false
+ }
]
}
}
@@ -553,14 +566,23 @@ POST /api/v1/device/fan/speed
"fans": [
{
"id": 0,
- "mode": "curve",
+ "mode": "auto",
"duty": 30,
"target_duty": 25,
"rpm": 0,
"temperature": 35.5,
"enabled": true,
"running": true,
- "fault": false
+ "fault": false,
+ "control_temperature": 35.5,
+ "guard_temperature": 52.3,
+ "predicted_temperature": 58.0,
+ "slope_c_per_min": 1.2,
+ "controller_gain": 0.75,
+ "cooling_response": 0.4,
+ "auto_state": "active",
+ "guard_active": false,
+ "temp_stale": false
}
],
"temperature": 35.5,
@@ -570,6 +592,8 @@ POST /api/v1/device/fan/speed
}
```
+`control_temperature`、`guard_temperature`、`predicted_temperature`、`slope_c_per_min`、`controller_gain`、`cooling_response`、`auto_state`、`guard_active`、`temp_stale` 是自动模式的状态字段;非 `auto` 模式下这些字段仅表示上一次自动控制快照或默认值,不参与当前调速。
+
### fan.mode
设置风扇工作模式。
@@ -696,11 +720,15 @@ POST /api/v1/fan.save
{
"code": 0,
"data": {
- "current_temp": 35.5,
- "preferred_source": "variable",
+ "initialized": true,
+ "manual_mode": false,
"active_source": "variable",
+ "preferred_source": "variable",
"bound_variable": "agx.cpu_temp",
- "manual_mode": false
+ "temperature_01c": 355,
+ "temperature_c": 35.5,
+ "valid": true,
+ "timestamp_ms": 123900
}
}
```
@@ -742,6 +770,9 @@ POST /api/v1/temp.bind
- 每个 `weight` 取值范围为 `0.0 ~ 1.0`
- 所有权重之和必须大于 `0`
- 旧格式 `variable` 仍然兼容,等价于单变量且权重为 `1.0`
+- 只有所有正权重绑定变量都 fresh 且数值有效时才返回 `weighted_temp_c`
+- 任一正权重绑定变量过期或无效时,响应会标记 `partial_stale: true`
+- `weighted_temp_c` 是绑定变量的加权温度;`temperature_c` / `valid` 始终表示当前活动温度源状态
**响应示例**:
```json
@@ -750,9 +781,30 @@ POST /api/v1/temp.bind
"data": {
"bound_variable": "agx.cpu_temp",
"bound_variables": [
- { "name": "agx.cpu_temp", "weight": 0.4, "value": 52.3 },
- { "name": "agx.gpu_temp", "weight": 0.6, "value": 49.8 }
+ {
+ "name": "agx.cpu_temp",
+ "weight": 0.4,
+ "value": 52.3,
+ "last_update_ms": 123456,
+ "age_ms": 250,
+ "stale": false,
+ "valid": true
+ },
+ {
+ "name": "agx.gpu_temp",
+ "weight": 0.6,
+ "value": 49.8,
+ "last_update_ms": 123460,
+ "age_ms": 246,
+ "stale": false,
+ "valid": true
+ }
],
+ "bound_valid_count": 2,
+ "bound_total_count": 2,
+ "bound_valid_weight": 1.0,
+ "bound_total_weight": 1.0,
+ "partial_stale": false,
"weighted_temp_c": 50.8,
"active_source": "variable",
"temperature_c": 50.8,
diff --git a/docs/COMMAND_REFERENCE.md b/docs/COMMAND_REFERENCE.md
index ee12ae0..d8f3d68 100644
--- a/docs/COMMAND_REFERENCE.md
+++ b/docs/COMMAND_REFERENCE.md
@@ -196,20 +196,16 @@ fan --load
temp --status
temp --status --json
-# 获取当前温度
-temp --get
+# 列出温度源
+temp --providers
+temp --providers --json
# 设置测试温度(调试用)
-temp --manual --enable --value 45
-temp --manual --disable # 禁用测试温度
-
-# 绑定温度变量
-temp --bind --variable "agx.cpu_temp"
-temp --unbind # 解除绑定
+temp --set --value 45
# 切换温度模式
-temp --select --source variable
-temp --select --source sensor
+temp --mode --value manual
+temp --mode --value auto
```
### LED 控制命令
diff --git a/docs/DEVELOPMENT_PROGRESS.md b/docs/DEVELOPMENT_PROGRESS.md
index 9f3db3e..8e4c8eb 100644
--- a/docs/DEVELOPMENT_PROGRESS.md
+++ b/docs/DEVELOPMENT_PROGRESS.md
@@ -1732,12 +1732,13 @@ POST /api/v1/temp.manual
# 设置占空比限制
fan --limits --id 0 --min 10 --max 100
-# 绑定温度变量
-temp --bind --variable "agx.cpu_temp"
+# 查看温度源
+temp --providers
# 设置测试温度
-temp --manual --enable --value 45
-temp --manual --disable
+temp --set --value 45
+temp --mode --value manual
+temp --mode --value auto
```
### WebUI 改进
@@ -2279,7 +2280,11 @@ WebSocket 消息支持 `var_name` 字段:
| `automation.variables.get` | 获取变量值 | `name` |
| `automation.variables.set` | 设置变量 | `name`, `value`, `type` |
| `automation.variables.delete` | 删除变量 | `name` |
-| `automation.variables.list` | 列出所有变量 | `prefix` (可选) |
+| `automation.variables.list` | 列出变量 | `prefix`, `source_id`, `include_value`, `include_meta` (可选) |
+
+`automation.variables.list` 默认返回变量值和更新时间元数据;资源敏感的选择器类 UI 应传
+`include_value:false, include_meta:false`。需要展示变量新鲜度时使用 `last_update_ms`、`age_ms`
+和 `stale`,不要再依赖旧的 `updated_at` 字段。
> **注意**:原 `var.*` API 已废弃(2026-01-27)。