找回密码
 立即注册

微信扫码登录

搜索
查看: 37|回复: 0

[基础教程] 实现HomKit、HA一键切换OpenClash节点

[复制链接]

2

主题

4

回帖

55

积分

注册会员

积分
55
金钱
49
HASS币
0
发表于 3 天前 | 显示全部楼层 |阅读模式
用过劣质✈的基本都懂:
  • 节点多(几十个甚至上百个)
  • 经常有一半是失效的
  • 网络不通了才发现节点挂了
  • 每次都要:
    • 打开 OpenWrt
    • 进 OpenClash
    • 打开 yacd 面板
    • 找策略组换有效节点


说实话,非常烦。
所以我的目标只有一个:
能不能像选 Wi-Fi 一样,随手切一下 Clash 节点?
最终效果(先说结论)我现在的使用体验是:
  • 📱 Home Assistant / HomeKit 里直接一个「节点选择」
  • 🔄 OPenClash/ HomeKit/ HA 三端实时同步
  • 🔁 换机场、换订阅 不用改配置


一句话总结:
教程开始
把 OpenClash 的“策略组选择”搬到了 Home Assistant / HomeKit

1.首先打开OpenClash复制secret密钥和管理地址


2.上方导航栏选择:覆写设置->开发者选项->在exit0之前输入代码

# === 强制 API 控制器和Secret (针对 HA/yacd) ===
ruby_edit "$CONFIG_FILE" "['external-controller']" "'0.0.0.0:9090'"
ruby_edit "$CONFIG_FILE" "['secret']" "'kKMeQaxb'"
ruby_edit "$CONFIG_FILE" "['allow-lan']" "true"
ruby_edit "$CONFIG_FILE" "['bind-address']" "'*'"
(1)很多机场默认配置是 127.0.0.1:9090(只允许路由器本机访问),HA/手机/电脑访问就“打不开”,所以改为0.0.0.0:9090
(2)secret: API 的访问密码 HA / yacd 访问必须带 Authorization: Bearer kKMeQaxb(你的secret密钥)

3.保存脚本,重启 OpenClash(或重新加载配置)



4.输入192.168.31.254:9090/proxies确认能响应成功返回{"message":"Unauthorized"}


Step 2:修改HA的 configuration.yaml
1.点击设置->加载项->搜索 File editor 工具 添加到侧边栏方便使用(docker版本自行查找下载方法)
2.打开文件管理工具没有configuration.yaml去左上角文件夹搜索路径是/homeassistant/configuration.yaml
3.粘贴在原有代码下面即可


# ===== OpenClash 控制面板 =====
rest_command:
  openclash_select_node:
    # ===== IP替换成自己的OpenClash管理地址 =====
    url: "http://192.168.31.254:9090/proxies/{{ group | urlencode }}"
    method: PUT
    headers:
      # ===== kKMeQaxb 替换成自己的Secret密钥 =====
      Authorization: "Bearer kKMeQaxb"
      Content-Type: "application/json"
    payload: >
      {"name":"{{ node }}"}

rest:
    # ===== 需替换 =====
  - resource: "http://192.168.31.254:9090/proxies"
    method: GET
    headers:
      # ===== 需替换 =====
      Authorization: "Bearer kKMeQaxb"
      # ===== HA组件刷新时间,按需更改 =====
    scan_interval: 3
    sensor:
      - name: Clash Proxies JSON
        value_template: "{{ value_json.proxies | length if value_json is defined and value_json.proxies is defined else 0 }}"
        json_attributes:
          - proxies
          
template:
  - sensor:
      - name: Clash Control Group
        state: >
          {% set p = state_attr('sensor.clash_proxies_json','proxies') %}
          {% if not p %}unknown{% else %}
            {# 这些 type 代表“不是机场节点”,不计入真实节点数 #}
            {% set non_leaf_types = ['Selector','URLTest','Fallback','Direct','Reject','RejectDrop','Pass','Compatible'] %}
            {% set best = namespace(name='unknown', score=-1) %}

            {% for g,v in p.items() %}
              {% if v is mapping and v.get('type') == 'Selector' and v.get('all') is iterable %}
                {% set score = 0 %}
                {% for n in v.get('all') %}
                  {% if n in p and (p[n].get('type') not in non_leaf_types) %}
                    {% set score = score + 1 %}
                  {% endif %}
                {% endfor %}

                {# 如果你无论如何都不想选 GLOBAL,可以在这里直接跳过 #}
                
                {% if g == 'GLOBAL' %}{% set score = -1 %}{% endif %}

                {% if score > best.score %}
                  {% set best.name = g %}
                  {% set best.score = score %}
                {% endif %}
              {% endif %}
            {% endfor %}

            {{ best.name }}
          {% endif %}

      - name: Clash Nodes List
        state: "ok"
        attributes:
          nodes: >
            {% set p = state_attr('sensor.clash_proxies_json','proxies') %}
            {% set g = states('sensor.clash_control_group') %}
            {% if p and g not in ['unknown','unavailable'] and g in p and 'all' in p[g] %}
              {{ p[g]['all'] | list }}
            {% else %}
              []
            {% endif %}

      - name: Clash 当前节点
        state: >
          {% set p = state_attr('sensor.clash_proxies_json','proxies') %}
          {% set g = states('sensor.clash_control_group') %}
          {% if p and g not in ['unknown','unavailable'] and g in p and 'now' in p[g] %}
            {{ p[g]['now'] }}
          {% else %}
            unknown
          {% endif %}

# ===== HA下拉选择组件 =====
input_select:
  clash_node:
    name: Clash 节点选择
    options:
      - 同步中...
(需要修改处已注释,按需填写)

4.右上角保存之后,重启HA-选择重新启动系统
Step 3:设置自动化同步节点状态
1.重启后点击设置->自动化-创建新的自动化

2.如图按照步骤添加三个自动化代码如下:
alias: OpenClash-选择下拉框切换节点
 triggers:
   - entity_id: input_select.clash_node
     trigger: state
 conditions:
   - condition: template
     value_template: |
       {{ states('sensor.clash_control_group') not in ['unknown','unavailable']
          and trigger.to_state.state not in ['unknown','unavailable','同步中...'] }}
 actions:
   - data:
       group: "{{ states('sensor.clash_control_group') }}"
       node: "{{ states('input_select.clash_node') }}"
     action: rest_command.openclash_select_node
alias: OpenClash-同步节点列表到下拉框
triggers:
  - entity_id: sensor.clash_proxies_json
    trigger: state
actions:
  - target:
      entity_id: input_select.clash_node
    data:
      options: |
        {{ state_attr('sensor.clash_nodes_list','nodes') or ['同步中...'] }}
    action: input_select.set_options
alias: OpenClash-回写当前节点到下拉框
 triggers:
  - entity_id: sensor.clash_proxies_json
     trigger: state
 conditions:
   - condition: template
     value_template: |
       {{ states('sensor.clash_control_group') not in ['unknown','unavailable']
          and states('sensor.clash_dang_qian_jie_dian') not in ['unknown','unavailable'] }}
 actions:
   - delay: "00:00:01"
   - variables:
       cur: "{{ states('sensor.clash_dang_qian_jie_dian') }}"
       opts: "{{ state_attr('input_select.clash_node','options') or [] }}"
       sel: "{{ states('input_select.clash_node') }}"
   - choose:
       - conditions:
           - condition: template
             value_template: "{{ cur in opts and cur != sel }}"
         sequence:
           - target:
               entity_id: input_select.clash_node
             data:
               option: "{{ cur }}"
             action: input_select.select_option
3.成功创建自动化并启用
4.测试是否成功获取数据
选择开发者工具->状态->搜索 sensor.clash_proxies_json 看是否有数据,有数据说明离成功了
Step 4:测试HA--OpenClash 之间节点切换功能
1.将刚才添加的Clash名字相关的所有组件放到桌面上,测试是否能成功切换节点 (1)OpenClash-->HA 测试yacd控制面板切换节点,HA组件成功显示,延迟3S(上方configuration.yamlscan_interval: 3 属性设置)
结果:成功切换
(2)HA-->OpenClash 测试HA组件下拉列表切换节点,网页刷新yacd控制面板成功显示
结果:成功切换
Step 5:将组件接入HomeKit
1.设置->设备与服务->HomeKit Bridge->右上角齿轮选择组件
包含模式





exclude





include

按照自己喜好来,我不喜欢乱的,我就选include

2.组件选择InputSelect->搜索clash->选择“clash节点选择”配件->重新加载
3.查看HomeKit app,配件成功导入 InputSelect类型的开关
Step 6:测试HA--OpenClash--HomeKit 之间节点切换功能
(1)OpenClash-->HA-->HomeKit测试yacd控制面板切换节点,HA组件,HomeKit 配件成功显示
结果:成功切换

  (2)HomeKit-->OpenClash-->HA 测试HomeKit开关切换节点,yacd控制面板和HA成功显示
  结果:成功切换

Step 7:测试切换机场HA--OpenClash--HomeKit 是否同步显示
测试结果:没毛
但是就是切换✈HomeKit有延迟,在一个机场不动只切换节点没延迟
教程结束
以后终于不用再到网页切换节点了!,HA真方便!





回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian ( 晋ICP备17001384号-1 )

GMT+8, 2026-1-23 20:44 , Processed in 0.322287 second(s), 9 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表