@ajclarkson/homerun

中文文档
0.1.0发布于 2026/11/08MITTypeScriptES Module构建来源已验证近 30 天下载 2978

家庭助理的脚本自动化框架

中文使用文档
文档目录11 节
  1. 问题
  2. 如何运作
  3. 示例
  4. 测试
  5. 触发和行动
  6. 观察性
  7. 宣布的理由、目录和历史
  8. 运行
  9. HTTP API 密码
  10. 建筑
  11. 许可证

全垒打

TypeScript中的打字可测试的家庭助理自动化.

状况:1.0前. API可能会改变. 构建发布为 npm @ajclarkson/homerun (单位:千美元)edge 标签,每推一次 main和作为多克的形象, ghcr.io/ajclarkson/homerun:edge。 。 。 .

Homerun是一个以纯函数取代HA自动化和节点-RED流量的框架. 每个自动化都是上下文构建器和还原器——没有副作用,没有隐藏状态,没有YAML. 管线处理可观察性,错位隔离,动作执行.


问题

家庭助理自动化是YAML. 节点-RED流量为JSON blobs. 两者都难以测试,难以审查,也无法进行打字检查. 凌晨2点,一个糟糕的部署 静静地打破了你的灯.

Homerun带来了与应用代码相同的学科:纯函数,打入输入,以及以毫秒为单位运行的测试套件.


如何运作

每个自动化都遵循同样的管道:

Event → context() → reduce() → actions[]

context() 从 HA 状态缓存中读取并返回打入的快照——如果缺少所需的输入,则提前中止。 这是唯一触及外部国家的地方.

reduce() 是一个纯函数。 它收到上下文,并发回附有声明行动清单的决定。 没有Aync 没有副作用 没有HA的电话.

输油管 处理其他所有事项:关联ID、可观察事件、错误隔离和动作执行.


示例

import { defineAutomation, abort, requireState, requireNumericState } from '@ajclarkson/homerun';

export default defineAutomation({
  id: 'kitchen:lighting',
  location: 'kitchen',
  subsystem: 'lighting',

  triggers: [
    { type: 'state_changed', entity: 'binary_sensor.kitchen_motion' },
    { type: 'on_start' },
  ],

  context(state) {
    if (requireState(state, 'input_boolean.kitchen_lights_enabled') !== 'on') {
      return abort('automation_disabled');
    }
    return {
      motion: requireState(state, 'binary_sensor.kitchen_motion') === 'on',
      lux: requireNumericState(state, 'sensor.kitchen_lux'),
      luxThreshold: requireNumericState(state, 'input_number.kitchen_lux_threshold'),
    };
  },

  reduce(ctx) {
    const shouldLight = ctx.motion && ctx.lux < ctx.luxThreshold;
    return {
      decision: shouldLight ? 'lights_on' : 'lights_off',
      actions: [
        {
          type: 'ha.call_service',
          domain: 'light',
          service: shouldLight ? 'turn_on' : 'turn_off',
          target: { entity_id: 'light.kitchen_ceiling' },
        },
      ],
    };
  },
});

requireState 财务报告和审定财务报表 requireNumericState 中止运行,如果一个实体不存在或非数字存在,则将丢失的实体记录下来,因此一个已死传感器永远不会沉默地变成一个错误的决定. 决定 conditions 默认到完整上下文,所以已发布的事件会解释自己.

这样的文件会进入您的自动化目录; 见 消费者设置。 。 。 .


测试

因为 reduce() 是纯函数,测试只是函数调用。 在没有家庭助理连接的情况下测试整个自动化,使用测试助手:

import { describe, it, expect } from 'vitest';
import { testAutomation } from '@ajclarkson/homerun/testing';
import kitchenLights from './kitchen-lights';

describe('kitchen lighting', () => {
  it('turns lights on when motion is detected below the lux threshold', () => {
    const result = testAutomation(kitchenLights, {
      event: { type: 'state_changed', entity_id: 'binary_sensor.kitchen_motion', new_state: { state: 'on' } },
      state: {
        'input_boolean.kitchen_lights_enabled': { state: 'on' },
        'binary_sensor.kitchen_motion': { state: 'on' },
        'sensor.kitchen_lux': { state: '20' },
        'input_number.kitchen_lux_threshold': { state: '40' },
      },
    });
    expect(result.decision).toBe('lights_on');
    expect(result.actions[0]).toMatchObject({ service: 'turn_on' });
  });
});

没有嘲弄,没有HA连接,没有Aync. testAbort 财务报告和审定财务报表 testUnavailable 覆盖中止路径。 见 自动化 API。 。 。 .


触发和行动

触发当火
state_changed实体的状态或属性变化. 过滤器为 to; 延迟使用 duration。 。 。 .
schedule疯狂的表达火.
on_start系统准备就绪,国家缓存人员已满员.
timer_expired前一个定时器 timer.start 动作已过期.
button按钮实体发出 a single_press, (中文(简体) ), double_press,或 hold 手势.
mqtt_in发送到 MQTT 主题的消息 .
行动效果
ha.call_service家庭助理服务
mqtt.publish发布到 MQTT 主题
timer.start启动( 或重新启动) 一个命名的计时器
timer.cancel取消命名的计时器

从 reduce() 作为平面数组;运行时间在决定事件发布后执行. 完整类型和语义 : 自动化 API。 。 。 .


观察性

每个管道运行都会向MQTT发布一个事件——无论是决定,中止,还是扔出——每个动作都会发布自己的起步和结果事件. A 类 correlation_id 在事件源头做薄荷 让你追踪到一个HA状态的变化 到服务电话 由此产生.

{
  "schema": "home.events.v2",
  "correlation_id": "b3d2f1a0-...",
  "root_correlation_id": "b3d2f1a0-...",
  "automation_id": "kitchen:lighting",
  "location": "kitchen",
  "subsystem": "lighting",
  "event_type": "decision",
  "trigger": { "type": "state_changed", "entity_id": "binary_sensor.kitchen_motion", "to": "on" },
  "decision": "lights_on",
  "conditions": { "motion": true, "lux": 20, "luxThreshold": 40 },
  "actions": [{ "type": "ha.call_service", "domain": "light", "service": "turn_on" }],
  "hasAction": true,
  "timestamp": "2026-06-23T18:00:00.000Z"
}

Schema,主题,指令包跟踪和Prometheus 度量衡: 观察性。 。 。 .


宣布的理由、目录和历史

默认的决定 reason 是一个自由字符串。 相反,自动化可以宣布它可能给出的原因,因此消费者(一个仪表板,一个通知器)可以在不解析字符串的情况下解释发生的事情. 这里的一切都是选入的:没有自动化的 reasons 行为和出版都和以前一样.

export default defineAutomation({
  id: 'kitchen:lighting',
  location: 'kitchen',
  subsystem: 'lighting',
  triggers: [{ type: 'state_changed', entity: 'binary_sensor.kitchen_motion' }],
  reasons: {
    lights_on: { outcome: 'acted' },
    lux_high: { outcome: 'declined', facts: ['lux', 'threshold'] },
    startup: { outcome: 'ignored' },
  },
  context: (state) => ({ lux: requireNumericState(state, 'sensor.kitchen_lux') }),
  reduce: ({ lux }) =>
    lux > 40
      ? { decision: 'no_action', reason: 'lux_high', facts: { lux, threshold: 40 }, actions: [] }
      : { decision: 'turn_on', reason: 'lights_on', actions: [kitchenLightsOn] },
});
  • outcome 实值 acted (规则改变了什么), declined (它选择不采取行动,一个人会想知道原因)或 ignored (噪声:起步,已入正态). 它被宣布,从来没有从 actions。 。 。 .
  • facts 是输入的句子需要的值:每个理由最多8个按键,每个按字符串、有限数字、布尔或 null它们与 conditions 目的: conditions 是整个上下文的调试堆放 可能自由改变形状, facts 是消费者可以依赖的小型、 命名、 检查的套件.
  • 宣布的理由执行三次。 编译时间 reason 已声明的密钥和 facts 必须准确携带该理由的密钥(额外密钥只在运行时被捕获,因为TypeScript不检查返回对象的多余属性)。 装入时间 defineAutomation 丢入不良结果、事实列表或理由名称(字母、数字和下划线:理由不包含自由文本)。 在运行时,一个未申报的理由,或一个未申报、失踪或非原始事实,从未停止行动:决定是在没有公布的情况下公布的 outcome 或者说 facts 和单独的形状 declaration_error 事件发布到 homerun/events 并登录,与 problem (单位:千美元)undeclared_reason, (中文(简体) ), missing_fact, (中文(简体) ), undeclared_fact 或者说 invalid_fact_value当标识形状时, reason 财务报告和审定财务报表 fact观察力绝不能阻止真正的行动。 宣布无一物不能返回的自动化 facts (一个编译错误). defineAutomation 和登记处(包括热重装)均拒绝错误声明,并拒绝保留地点 dev在装货.
  • 决定事件获得可选 outcome 财务报告和审定财务报表 facts,只用于宣布自动化.

目录. 声明保留于 homerun/catalog (单位:千美元)home.catalog.v1) 启动时和重新装入后,每个条目的 reasons 添加到 GET /automations。只有宣布原因的自动化设备才会出现。 该目录在每次启动时都会被公布,如果无内容则会空出原因,所以保留的副本永远不会被搁置. 消费者可以对照其措辞表检查.

历史. 设定 history 在配置中(默认关闭)保留一个环缓冲器来显示值得询问的事件“这里发生了什么?”

history:
  size: 20        # per location, 1 to 200
  houseSize: 30   # house-wide, 1 to 200

每个存储条目重新发布,最先更新,保留在 homerun/{location}/history 财务报告和审定财务报表 homerun/history (单位:千美元)home.history.v1) (中文(简体) ). 记得:已宣布 acted 财务报告和审定财务报表 declined (或,对于未宣布的自动化、有行动的决定),除 disabled,则失败动作(失败) action_result 或一个 action_ack_timeout) (中文(简体) ). 条目只结构化:理由键、事实、a trigger 概要和a detail 记录abort_kind编号 :entity,或 failure编号 :action编号 :entity。 留存的再版是和最新状态( 250ms) 相接的, 并被冲出关闭状态 。 启动时, 缓冲器会从保留的主题中重新播种, 因此历史可以幸存到重启中; 保存错误的信息会被记录下来, 而缓冲器开始为空 。 种子等待 1s 由经纪人发送保留的信息. dev 作为地点名称保留(登记时被拒)。 书目和历史都遵循 events.enabled: 当是时 false,既不是出版(也不是原始历史,也不是保存),与MQTT流的其他部分一样。 在干跑模式下,每个话题都移动到下面 homerun/dev/。 。 。 .

两种有效载荷的JSON Schemas装入 schemas/ 并随包出版. 每一个有效载荷在公布之前都要对他们进行验证.


运行

npm install @ajclarkson/homerun

家常读作 configuration.yaml (路径从 HOMERUN_CONFIG)与您的家用助理 URL 和令牌, MQTT 经纪人和自动化目录. 秘密可以生活在一个 secrets.yaml 旁边,并被引用 !secret。 。 。 . configuration.yaml.example 显示每个选项.

设定 options.dry_run: true 在不采取任何行动的情况下运行整个管道——环境、减少、可观察性.

设置您的自动回放, 从您的家庭助理中生成实体类型, 运行运行时间包含在 消费者设置。 。 。 .

要研究框架本身 复制并使用 npm run dev (从源头跑出,热量重载)和 npm test。 。 。 .


HTTP API 密码

服务器在端口显示 HTTP API 7070 默认(server.port 在配置中.

方法路径说明
GET/health/live总是返回 200 { status: "live" } 用于活性探测器.
GET/health/ready回返 200 当HA状态的缓存被装入,自动化设备被装入时, 503 开始的时候。 反应包括 entities, (中文(简体) ), automations 计数,以及 dry_run: true 如果运行在干燥模式中.
GET/automations列出所有注册自动化及其 id, (中文(简体) ), location, (中文(简体) ), subsystem类型,加 reasons 给那些宣布他们有罪的人.
POST/automations/:id/trigger手动点火 on_start 用于给定自动化的事件.
POST/reload重新扫描自动化目录并重载已更改的文件 .
GET/events所有编审中的决定和行动活动的服务器发送事件流实时.

建筑

HAClient ──────────────────────────────────────┐
  └─ state_changed (per entity, with corr. ID) │
                                               ▼
Scheduler (cron / on_start) ──────► TriggerEngine.dispatch()
TimerManager (setTimeout)  ──────►        │
                                          ▼
                                   matchAndFire()
                                          │
                              ┌───────────▼──────────┐
                              │    Pipeline Runner    │
                              │  context → reduce     │
                              │  → validate → fanout  │
                              └──────────┬────────────┘
                                         │
                          ┌──────────────┴──────────────┐
                          ▼                             ▼
                   Observability                  ActionRuntime
                 (MQTT events)           (HA calls / timers / MQTT)

许可证

麻省理工学院

版本与发布记录

@ajclarkson/homerun 共发布 119 个版本,首次发布于 2026/07/11;过去 12 个月发布了 119 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.1.0latest2026/11/08今天
0.1.0-edge.f10ba12edge2026/11/08今天
0.1.0-edge.710bb4a2026/11/071 天前
0.1.0-edge.826524a2026/11/026 天前
0.0.1-edge.0a3c3ff2026/11/026 天前
0.0.1-edge.8c93a272026/11/026 天前

关于 @ajclarkson/homerun 的常见问题

@ajclarkson/homerun 是什么?

家庭助理的脚本自动化框架

如何安装 @ajclarkson/homerun?

在项目目录运行 npm install @ajclarkson/homerun,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 homerun、homerun-generate-ha-types、homerun-generate-ha-services,可以用 npx homerun 直接运行。

@ajclarkson/homerun 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@ajclarkson/homerun 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@ajclarkson/homerun 可以商用吗?

@ajclarkson/homerun 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@ajclarkson/homerun 的下载量有多少?

根据 npm 官方下载统计,@ajclarkson/homerun 在 2026/10/05 – 2026/11/03 这 30 天内被下载 2,978 次,日均约 99 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@ajclarkson/homerun 还在维护吗?

最新版本 0.1.0 发布于 2026/11/08(今天);至今共发布 119 个版本,其中过去 12 个月发布了 119 个;首次发布于 2026/07/11。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。