跳转至

Home Assistant 自动化 Trace 定位助手

纯前端:粘贴 automation YAML(可选 + trace JSON),工具按「trigger → condition → action → mode」四步静态定位卡在哪一步,并给每步该查什么。数据不出浏览器,适合排查「跑一半就停」「动作时灵时不灵」。

自动化最常见的两类抱怨——"不触发"和"动作时灵时不灵"——真正卡住的地方其实就四处:trigger 没触发、condition 没过、action 报错、mode 把新触发卡住了。HA 的 Trace 面板能看到每一步真实结果,但字段多、不熟的人容易看花。这个工具把"该按什么顺序查"固化成一张四步路径,先把你 YAML 里缺 trigger、action 实体写错这类结构问题标出来,再指你最可疑的那一步。

四步定位:卡在哪一步

步骤 卡在这的特征 该查什么
① trigger 日志里根本没这条自动化的"触发"记录 缺 trigger / 实体写错 / for 没到时 / 类型不合法
② condition 触发了但 action 没跑,Trace 里某条 condition 标"失败" 哪条 condition 没过(时间/状态/模板);常是值写反或时区不对
③ action Trace 里 action 步骤标"错误" action: 的实体名拼写、服务名(domain.service)写错、模板变量未定义
④ mode 高频率触发时只跑第一条 mode: single 上一条没跑完就忽略新触发;看要不要改 restart/queued

Trace 定位工具

粘 automation YAML(可整段或单条),需要的话再粘 HA 导出的 trace JSON,点「定位」:

(function () { 'use strict'; function esc(s) { return String(s).replace(/&/g, '&').replace(//g, '>'); } function lineCount(raw) { return raw.split(/\r?\n/).length; } // 粗略扫 YAML 是否含某类顶层/子层键(不精确解析,启发式) function hasAny(raw, names) { for (var i = 0; i < names.length; i++) { var re = new RegExp('\\b' + names[i] + '\\s*:', 'i'); if (re.test(raw)) { return true; } } return false; } function findMode(raw) { var m = raw.match(/\bmode\s*:\s*([A-Za-z]+)\s*$/m) || raw.match(/\bmode\s*:\s*([A-Za-z]+)\s*$/m); var m2 = raw.match(/\bmode\s*:\s*([A-Za-z]+)\s*([^\n]*)/); if (m2) { return m2[1]; } return null; } function locate() { var out = document.getElementById('trace-out'); if (!out) { return; } var raw = (document.getElementById('trace-yaml').value || '').trim(); var tj = (document.getElementById('trace-json').value || '').trim(); if (!raw) { out.innerHTML = '

先粘 automation YAML,再点「定位」。

'; return; } var lines = raw.split(/\r?\n/); var report = []; var foundMode = findMode(raw); var hasTrigger = hasAny(raw, ['trigger','triggers']); var hasCond = hasAny(raw, ['condition','conditions']); var hasAction = hasAny(raw, ['action','actions']); // ① trigger 层 if (!hasTrigger) { report.push({ step: 1, level: 'fail', text: '缺 trigger / triggers:这条自动化永远不会触发。先补 trigger,再谈后面三步。' }); } else { report.push({ step: 1, level: 'ok', text: '有 trigger。日志里若完全没有"触发"记录,查实体名对不对、for 有没有到、trigger 类型是否合法。' }); } // ② condition 层 if (hasCond) { report.push({ step: 2, level: 'warn', text: '含 condition。"动作时灵时不灵"最常见的卡点:看 Trace 里哪条 condition 标"失败"——时间条件(after/before)和模板变量最常写反。' }); } else { report.push({ step: 2, level: 'ok', text: '没有 condition:触发即执行,跳过这步;若怀疑某步没跑,重点看第①③④步。' }); } // ③ action 层 if (!hasAction) { report.push({ step: 3, level: 'fail', text: '缺 action / actions:即使触发也没有动作。补 action。' }); } else { report.push({ step: 3, level: 'ok', text: '有 action。Trace 里 action 步骤若标"错误",查:实体名拼写、服务名是否 domain.service、模板变量是否未定义。' }); } // ④ mode 层 if (foundMode === 'single') { report.push({ step: 4, level: 'warn', text: 'mode: single——上一条没跑完时,新触发会被忽略(日志会打 Already running)。高频触发场景改 restart(重头来)或 queued(排队)。' }); } else if (foundMode === 'queued') { report.push({ step: 4, level: 'ok', text: 'mode: queued——新触发排队等前一条跑完。若发现"跑不过来/堆积",看是不是 delay 太长。' }); } else if (foundMode === 'parallel') { report.push({ step: 4, level: 'ok', text: 'mode: parallel——每次触发都并发起一个新实例。多实例互不干扰,但注意延迟动作会不会打架。' }); } else { report.push({ step: 4, level: 'info', text: '未显式写 mode,默认按 single 处理。高频场景建议显式指定。' }); } // trace JSON(可选) var traceNote = null; if (tj) { try { var data = JSON.parse(tj); // HA trace 常见结构:steps 是对象,键是 step 名;或 run 里有 var steps = null; if (data && data.steps) { steps = data.steps; } if (steps) { var keys = Object.keys(steps); if (keys.length) { var last = keys[keys.length - 1]; var lv = ''; try { lv = steps[last] && steps[last].result ? String(steps[last].result) : ''; } catch (e) {} traceNote = 'trace 里共 ' + keys.length + ' 步,最后停在「' + last + '」。这步就是真正卡住的地方,重点看它。' + (lv ? '(result:' + esc(lv.slice(0, 40)) + ')' : ''); } else { traceNote = 'trace 的 steps 为空——说明这条自动化根本没跑起来,回到第①步查 trigger。'; } } else { traceNote = 'trace JSON 解析成功,但没找到 steps 字段——确认粘的是 HA 导出的完整 trace,不是某一步的片段。'; } } catch (e) { traceNote = 'trace JSON 解析失败(不是合法 JSON)。HA 的 trace 导出是 JSON,先确认复制完整再粘;没有也留空,按 YAML 排查顺序走即可。'; } } // 渲染 var html = ''; if (traceNote) { html += '

' + esc(traceNote) + '

'; } html += '
    '; for (var i = 0; i < report.length; i++) { var r = report[i]; var mark = r.level === 'fail' ? '✗' : (r.level === 'warn' ? '⚠' : (r.level === 'info' ? 'ⓘ' : '✓')); var color = r.level === 'fail' ? 'var(--md-primary-fg-color)' : (r.level === 'warn' ? 'var(--md-code-fg-color)' : 'var(--md-default-fg-color--light)'); html += '
  1. 第' + r.step + '步 ' + mark + ' ' + esc(r.text) + '
  2. '; } html += '
'; html += '

这是静态启发式定位,不模拟 HA 运行时。哪一步真断了,以 HA Trace 面板与日志为准。

'; out.innerHTML = html; } document.addEventListener('DOMContentLoaded', function () { var btn = document.getElementById('trace-go'); if (btn) { btn.addEventListener('click', locate); } }); })();