打开网易新闻 查看精彩图片

说实话,这玩意儿我去年就在某宝买过一个,当时看它便宜,随手就扔钥匙扣上挂着了。那时候也没想着怎么折腾,就当个单纯的防丢挂件用。直到前两天,我在论坛看到大佬“雪晴科技HTH”推荐这东西,说它能完美接入Home Assistant,而且功能还不少。这下勾起了我的折腾欲,2块9的东西(用了3块优惠券,原价5.9元),要是真能变身智能按钮加蓝牙追踪,那性价比简直逆天了。

什么是 iTag?

iTag 这类产品其实是个通用的 BLE(低功耗蓝牙)标签。外观大多是个小水滴形状,里面藏着一个纽扣电池、一个蜂鸣器和一个物理按键。本质上,它就是一个标准的 BLE GATT 设备。虽然各家厂商出的固件可能稍有不同,但底层逻辑是一样的:通过不同的“特征(Characteristic)”来传输按键信号、电量信息或者触发蜂鸣器。

为什么选它?

对我这种家里还没装智能门锁、还在用机械钥匙的人来说,iTag 简直是刚需。

  1. 1. 无线按钮:它支持单击和双击判定。挂在钥匙上,进门顺手一按就能触发开关灯或者控制指定设备。
  2. 2. 居家/离家判定:利用 ESP32 的蓝牙连接上itag防丢器。只要钥匙在范围内,我就在,可以做自动化离家和回家场景。
  3. 3. 寻物报警:找不到钥匙了?手机点一下,钥匙扣就开始尖叫。
  4. 4. 超低成本:2块9,坏了丢了完全不心疼。
逆向与折腾:从抓包到接入

主要用到了esp32开发板,然后在esphome代码里启用(esp32_ble_tracker)BLE 扫描跟踪组件和(ble_client)BLE 客户端组件,如果家里有esp32芯片的智能设备,可以加几行代码进去就可以实现把itag防丢器接入HA。

折腾的第一步是逆向调试。我用了 Web Bluetooth API 工具连接了 iTag,发现它的按键信号是通过服务 0xFFE0 的特征 0xFFE1 推送的。不同芯片方案(有的用 Ti CC2541,有的用杰理)推送的 HEX 数据不一样,我这个单击发的是 0x01。

最大的坑在于那个模式控制寄存器 0x1802 / 0x2A06。它其实是个 3态互斥寄存器:写入 0x01 是寻物报警,0x00 是按键提示音,0x02 是静音。如果你把它当成普通的开关配置,肯定会打架。所以我最后在 ESPHome 里用了一个 select 选择器来统一管理,效果非常稳。

打开网易新闻 查看精彩图片
打开网易新闻 查看精彩图片

针对双击手感,我还微调了 ESPHome 的 on_multi_click 时间参数。因为人手操作总是有快慢,把判定窗口放宽到 0.6 秒后,识别率几乎达到了 100%。

完整的 ESPHome 配置代码

直接把下面这段代码粘进你的配置里,改下 MAC 地址就能用。我已经把电量查询、寻物开关、ITAG单击双击和ITAG连接状态全都写好了。

substitutions:  device_name: "itag_button"  itag_mac: "FF:******:DF"esphome:  name: esp32kf  friendly_name: esp32kfesp32:  board: esp32dev  framework:    type: esp-idflogger:esp32_ble_tracker:  scan_parameters:    active: true    interval: 150ms    window: 100msble_client:  - mac_address: ${itag_mac}    id: itag_client    on_connect:      then:        - binary_sensor.template.publish:            id: itag_connected_real            state: ON        - ble_client.ble_write:            id: itag_client            service_uuid: 'FFE0'            characteristic_uuid: 'FFE2'            value: [0x00]        - logger.log: "iTag 链路已建立,状态同步为:在家"    on_disconnect:      then:        - binary_sensor.template.publish:            id: itag_connected_real            state: OFF        - logger.log: "iTag 链路已断开,状态同步为:离家"sensor:  - platform: internal_temperature    name: "esp32 Temperature"  - platform: ble_client    type: characteristic    ble_client_id: itag_client    name: "iTag battery"    icon: "mdi:battery"    service_uuid: '180F'    characteristic_uuid: '2A19'    unit_of_measurement: "%"    accuracy_decimals: 0    update_interval: 2hbinary_sensor:  - platform: template    name: "钥匙扣实际状态"    id: itag_connected_real    device_class: connectivity  - platform: template    id: itag_button_pulse    internal: true    on_multi_click:      - timing:          - ON for at most 0.4s          - OFF for at least 0.4s        then:          - text_sensor.template.publish:              id: last_action              state: "单击 (Single Click)"          - delay: 500ms          - text_sensor.template.publish:              id: last_action              state: "null"      - timing:          - ON for at most 0.4s          - OFF for at most 0.4s          - ON for at most 0.4s        then:          - text_sensor.template.publish:              id: last_action              state: "双击 (Double Click)"          - delay: 500ms          - text_sensor.template.publish:              id: last_action              state: "null"select:  - platform: template    name: "钥匙扣工作模式"    id: itag_work_mode    icon: "mdi:tune-vertical"    optimistic: true    restore_value: true    options:      - "静音模式"      - "按键提示音"      - "寻物蜂鸣"    set_action:      - logger.log:          format: "【模式切换】iTag 当前设为: %s"          args: ["x.c_str()"]      - if:          condition:            lambda: 'return x == "静音模式";'          then:            - ble_client.ble_write:                id: itag_client                service_uuid: '1802'                characteristic_uuid: '2A06'                value: [0x02]      - if:          condition:            lambda: 'return x == "按键提示音";'          then:            - ble_client.ble_write:                id: itag_client                service_uuid: '1802'                characteristic_uuid: '2A06'                value: [0x00]      - if:          condition:            lambda: 'return x == "寻物蜂鸣";'          then:            - ble_client.ble_write:                id: itag_client                service_uuid: '1802'                characteristic_uuid: '2A06'                value: [0x01]text_sensor:  - platform: ble_client    ble_client_id: itag_client    service_uuid: 'FFE0'    id: djkj    characteristic_uuid: 'FFE1'    internal: true    notify: true    on_notify:      then:        - if:            condition:              lambda: 'return !x.empty() && ((uint8_t)x[0]) == 0x01;'            then:              - logger.log: "【iTag】捕获到硬件按键按下事件!"              - binary_sensor.template.publish:                  id: itag_button_pulse                  state: ON              - delay: 50ms              - binary_sensor.template.publish:                  id: itag_button_pulse                  state: OFF  - platform: template    name: "钥匙扣最后动作"    id: last_action    icon: "mdi:gesture-tap"
总结

折腾完之后,我发现这小玩意儿比我想象中要稳。虽然它只是个 2.9 元的硬件,但配合 ESPHome 和 HA 的自动化,能玩出的花样真的很多。如果你手里也有吃灰的防丢器,不妨动手试一试,这绝对是入坑智能家居成本最低、乐趣最高的方式之一了。