只需不到十元 用 RP2040 DIY 一条 YubiKey FIDO2 物理密钥

我曾经有考虑过购买一条 Yubikey 来作为 U2F 验证用途,但是鉴于最便宜的 YubiKey 5C NFC 都要$58 美刀,所以还是忍住没下手(错过了 Cloudflare 曾经的 10 刀优惠 :()

直到昨天我发现了一个开源项目 Pico FIDO(曾名 Pico Keys)可以把 ESP32-S3/RP2035/RP2040 变成一条类似 YubiKey 的实体密钥,刚好我剩下一块微雪的 RP2040-Zero 开发板就利用上了

RP2040 在购物平台上的价格平均在 10 元左右,算是很划算了,但是需要注意 RP2040 内储存的密钥在被物理接触时是可以被盗取的,而其余两个则无此风险,严格注意安全的需要注意了


刷写固件

要给 RP 系列开发板刷固件很简单,以我手上这块为例,只需要按着板上的 BOOT 按钮再连接 USB 线,电脑就能识别到一个盘符,名称是 RPI-RP2

GitHub上下载最新的发行版固件(我用的是 pico_fido_pico-7.6.uf2),直接拖到开发板盘里就能自动刷固件,安装完成后会自动重启

重启后你的开发板就变成了一个可以用于 FIDO2 验证的物理密钥,可以直接在需要 U2F 的网站添加使用,也可以到https://webauthn.io测试登录

这个项目曾经有一个很方便的设置网页,但是在前几个月变成了一个收费€29 欧元的 PicoKey App,我是不会付费的,且不设置也能正常使用,就没有多折腾了


使用密钥

这里以 Vaultwarden 为例,任意支持 Passkey 登录的平台理论上都适用

在 Vaultwarden 后台左侧的设置>安全里找到 Two-step login 选项,找到 Passkey 选项,输入密码库的主密码后便能添加密钥(这里如果唤醒的浏览器插件内置的 Passkey 选择,找到插件的“使用实体密钥”或类似选项)

首次使用密钥需要设置一个密码,以后每次认证都需要输入一次,设置密码后按 BOOT 按键来认证

然后再到 Master password 页,打开 Log in with passkey,这样以后登录便无需输入邮箱

配置后每次登录的流程为: 点击使用 Passkey 登录>输入密码>按实体认证按钮(在 Vaultwarden 还需要再输入一遍主密码)


封面图是我把背景打印出来再把板子放上去拍照而来 😛

为解决晚上睡觉途中突然亮灯 再次购入小米人体感应器一枚

继两天前在 Home Assistant 配置了人来灯亮(以及人走灯灭),这两天晚上都因为检测范围过大导致多次意外亮起,虽然也只是几分钟就灭了但也确实降低了我的睡眠质量,我能想到的解决方法只有两个:在睡前把灯带关闭,或者再购入一个感应器来检测我是否在床,最后我感觉后者合理一点(还是懒啊)


我把新买的感应器放到了床头的位置对着我的头,那么理论上只要我在床上就能检测到,配对上网关,再到 HA 里刷新设备,往自动亮灯的自动化里加上一个条件:

- condition: template
            value_template: >-
              {% set t =
              states('event.xiaomi_cn_blt_3_xxx_pir1g_motion_detected_e_2_1008')
              | as_datetime %} {{ t is not none and (now() - t).total_seconds()
              > 180 }}

当距离上一次检测到我在床的 3 分钟后才允许亮灯,希望不会再把我弄醒

2026.04.10 更新:这两晚都没有再在睡觉途中亮了 🙂


另外,我之前买的 COB 灯带也到了,COB 灯带的 LED 排的比传统灯带(SMD)更密集,所以看起来没有一粒粒的点光

我把灯带黏到床底后才发现由于我买的智能控制器只有开关,没有调压/亮度功能,最高亮度太亮了!没有我想要的效果所以我打算不把它加到人来灯亮的自动化里,而是额外新建一个按钮开关灯的配置

提醒各位如果想买灯带记得控制器要买带调光功能的


给博客加上了归档页面,链接在页眉的菜单内,方便索引文章~

Home Assistant 配合小米人体传感器 2s 实现人走关灯

我的需求为当离开房间 5 分钟后自动关闭灯光,于是买了一个小米的人体传感器 2s,这个传感器比其他的设备多了一个光照亮度检测,可以顺带实现晚上起夜自动亮床边灯带

但是由于我贪便宜买的这个是人体传感器,而不是人在传感器,导致有些时候会误判关灯(人在传感器内有个雷达,比较适合于静止的人;人体则适合移动/路过的)

另外当在 Home Assistant 内配合小米插件时,Motion detected 这个 entity 只有”上一次触发有人的时间“这个状态,而没有像在米家内的”隔一段时间无人“的状态,而我的想法是每隔一分钟判断上一次触发过了多久,超过了 300 秒就执行关灯(走后五分钟关灯方便临时离开的情况)

{% set t = states('event.xiaomi_cn_blt_3_xxx_pir1g_motion_detected_e_2_1008') | as_datetime %} {{ t is not none and (now() - t).total_seconds() > 300 }} #记得修改 event id

于是最终的自动化是:

alias: Turn Off Light When No Motion
description: ""
triggers:
  - minutes: /1
    trigger: time_pattern
conditions: []
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: >-
              {% set t = states('event.xiaomi_cn_blt_3_xxx_pir1g_motion_detected_e_2_1008')  | as_datetime %} {{ t is not none and (now() - t).total_seconds()
              > 300 }}
        sequence:
          - action: light.turn_off
            target:
              entity_id: light.xxx #灯的 entity
mode: single

如果你也打算买一个类似的设备,大多是需要搭配小米自家的网关的,而且必须有蓝牙 Mesh 协议,有些旧的网关只支持 Zigbee 协议是不能连接上的

另外,我买的人体传感器 2s 是需要一颗 CR2450 电池的,需要注意

最划算的方案是买一个二手的小爱音箱,记得跟卖家确认支持的协议再下单

Home Assistant 绑定米家账号后怎么刷新设备列表

问题

在 Home Assistant 使用小米官方插件并绑定账号(初始化)后,再在米家 APP 内添加设备,HA 内没有自动添加上设备,需要手动刷新设备列表

这里有一个之前写的绑定米家设备的教程,不知不觉过了两年啦


解决方法

在 HA 左下角设置>设备与服务>XiaoMi Home 里点击齿轮图标

然后在弹出的窗口滑到上方,勾选更新设备列表

在页面下方点击下一步,再勾选确认后等待几秒就能看到新添加的设备了


后记:这个 GitHub 文档里是有写的,但是明显我没有看到,在设置里找了很久才找到 🙁

如何解决 Matomo 统计的历史数据突然变成 0 访客

问题

今天打开博客的 Matomo 统计,发现 2 月其中两天的访客人数突然就变成了 0 人,我很确定在前天看的时候是有访客的,隔了两天再看已经消失了


解决方法

搜索发现,遇到这种情况只是归档数据出了问题,原始的访客日志还是在的,所以只需要重新建立一遍归档就能修复,重建归档的方法主要有两种:直接删除归档的数据表和用插件把数据表设置成“无效”,这里用后者

首先到 Matomo 插件市场里下载Invalidate Reports,在页面最底部的橘色按钮下载 WordPress 版本的插件,在 WP 里上传插件压缩包安装插件

然后到 Matomo 统计的设置>系统>设置为无效报告里把从上个月开始的数据设置为无效(根据需要调整)

然后等待下一次计划任务触发(默认一小时一次)归档数据就会重建了,或者也可以在 WP 插件的诊断>Troubleshooting 里手动点击 存档报告 按钮重建

利用 n8n 工作流实现定时检测友链状态

博客的友链不时会出现死链,网站打不开等的状态,本来想着用 UptimeRobot 这类网站监控工具来监控但是手动同步友链列表又比较麻烦,所以就改为用 n8n 工作流来定时自动获取友情链接列表,再自动逐一检测状态并筛选失效链接推送,用了点时间弄出来一个工作流,如果你有类似需求可以参考看看 🙂


n8n 工作流

先看截图:

逐一说明:

  1. 定时触发:每 10 小时触发运行
  2. HTTP 请求:发送请求到博客的友链页面(https://wuminboke.site/misc),失败重试三遍;这里需要注意如果你的博客开了 CF 5 秒盾或者类似检测,需要调整一下 WAF 规则
  3. JS 代码:提取<li>里的<a>标签,并且排除掉博客自身的域名:
    const html = $input.first().json.data
    const regex = /<li[^>]*>\s*<a\s+[^>]*href=["']?([^"'\s>]+)["']?[^>]*>/gi;
    const excludedDomains = ['wuminboke.site']; //排除自己的博客域名
    const hrefs = [];
    let match;
    while ((match = regex.exec(html)) !== null) {
      const href = match[1];
      const isExcluded = excludedDomains.some(domain => href.includes(domain));
      if (!isExcluded) {
        hrefs.push(href);
      }
    }
    return hrefs.map(href => ({ json: { href } }));
  4. HTTP 请求:逐一发送 GET 请求到 {{ $json.href }} ,并且把设置里的 On Error 改为 Continue (using error output),错误时继续
  5. 如果:把上一步的 Error 拉到如果,配置 {{ $input.all().length }} # is equal to 0,判断有无错误
  6. JS 代码:如果上一步“如果”的结果是“否”,那么把错误的网址和错误码拼起来:
    const items = $input.all();
    const lines = items.map(item => {
      const href = item.json.href;
      const status = item.json.error?.status ? ` ${item.json.error.status}` : '';
      return `${status}\n ${href}`;
    });
    const message = `检测完成 (共有${lines.length}个错误)\n\n` + lines.join('\n\n');
    return [{ json: { message } }];
  7. TG 推送:利用 TG 机器人发送通知(我在消息前还加了时间,但是不是必要的);在@BotFather 创建机器人获取 Token 并用@userinfobot 获取账号的聊天 ID

不想自己托管 n8n 的可以找找那些免费 Docker 容器服务,比如 ClawCloud 绑定 GitHub 后就有免费的额度可以用 😛

WordPress 优化 – 把博客的图片挂载到免费的 Cloudflare R2 储存桶

Cloudflare 的 R2 储存桶提供了免费的 10GB 空间再加上 100 万次 A 类操作(写入),1000 万次的 B 类操作(读取),小博客存放图片够用了,不过是需要绑定付款方式的,不过无需绑卡的方案也有不少,比如 IBM 云有免费的 25GB 对象储存,具体还请自行搜索,这里就不多叙述了 🙂


创建储存桶

首先登录 Cloudflare,在左侧菜单的 Storage & databases 里找到 R2 object storage,第一次使用需要输入付款方式(不超过免费额度是不会扣钱的),我是直接用 PayPal 绑定一遍再解绑,避免意外

进入到 R2 页面后,点击 Create bucket 创建储存桶

在下一页的 Bucket name 里给储存桶命名,其余的选项保持默认即可

创建完成后会跳转到储存桶详情页,点击 Settings

找到 Custom Domains,绑定一个子域名,如 images.example.com ,CF 会自动创建一条 DNS 记录(所以这个子域不能是已经解析过的)

接下来需要创建一条 API Token,回到 R2 主页面,在页面右下角找到 API Tokens,点击右方的管理按钮

在下一页选择 Create User API token 并设置权限为 Read & Write(读+写),Specify bucket 就选择刚刚创建的储存桶

创建后 CF 会显示 Access Key,Secret Key,以及 Endpoint,把这三项信息记下来,稍后配置插件时要用到

到这里,在 CF 中的配置已经完成


安装配置插件

转到 WP 后台,在安装插件里搜索Advanced Media Offloader,安装并启用插件

在后台左侧找到 Media Offloader,进入到设置页面

依次填入信息后保存:

  • Access Key
  • Secret Key
  • Endpoint
  • Bucket 名称
  • 自定义域名

保存后进入到 WP 的媒体库,随便上传一张图片作测试,上传后右键图片复制图片地址,如配置正确的话图片的网址会类似于:https://images.example.com/2026/03/xxxxx.png,代表插件已经成功配置

本文所有图片都是利用这个方法挂载到 R2 的,可以参考看看


我到现在才发现原来 CF 全称是 Cloudflare,一直以为是 CloudFlare 🙁

写到一半发现可读性有点差,改了一遍还是有些奇怪,抱歉 😥

如何使用 Rclone 下载 Google Drive 分享链接内的文件

一般如果要下载别人用 Google Drive 谷歌网盘分享的文件链接时,只能先压缩成.zip 压缩包再下载,下点小文件还好,一旦要下载动辄几十 GB 的文件时就力不从心了

不过我们可以利用 Rclone 同步工具,登录谷歌账号来进行下载


解决方法

这里以 Windows 环境为例,首先到 Rclone 的官网下载最新版,如果慢的话可以到 Github 下载

解压缩获得 rclone.exe,在文件目录 Shift+右键打开终端,执行 .\rclone.exe config 命令登录,依次输入:

  1. 新配置 > n
  2. 输入配置名称 > remote
  3. 选择 Google Drive > drive
  4. 用户端 ID > (Enter)
  5. 用户端密钥 > (Enter)
  6. 选择权限 > 1
  7. 账号保存文件 > (Enter)
  8. 浏览器登录 > y

这里会弹出浏览器,正常登录账号,登录后回到 Rclone 窗口继续:

  1. 团队盘 > n
  2. 是否保存配置 > y

到这里我们成功建立了一个名为 remote 的配置文件

然后在浏览器用同一个账号打开一遍共享链接,文件夹会被自动保存到“与我共享”里

回到 Rclone,用以下命令下载:

.\rclone.exe copy "remote:刚刚保存的文件夹/文件名称" "要下载到的本机目录" -P --drive-shared-with-me

最后能看到文件正在下载

 

WordPress 优化 – 后台禁用 Ctrl+K 快捷键控制面板

WordPress 在 6.9 版本更新后在仪表盘后台加入了一项新的功能——控制面板(Command Palette)

但是这东西我是越看越难受啊,所以我想把它禁用掉


解决方法

要禁用掉控制面板很简单,只需要在主题的 functions.php 内加入以下代码:

// 禁用 CTRL + K
function disable_command_palette() {

	wp_deregister_script( 'wp-commands' );
	wp_dequeue_script( 'wp-commands' ); 

}
add_action('admin_enqueue_scripts', 'disable_command_palette');

保存后刷新页面,再按 Ctrl+K 已经没有控制面板了


6.9 的更新可以看这篇博客:WordPress 6.9 发布,开启 AI 时代的内容创作新体验!,不知道啥时候开始几乎所有东西都加上了 AI!虽然 WordPress 这次更新只是实验性的加上了 Abilities API 和 MCP 服务器,但是这在我看来是“踏出了第一步”,以后会有更多的 AI 功能引入 🙁

不用自己瞎折腾了,WordPress 7.0 要加 AI 功能,底层直接整合 AI 核心基础架构

通过 Portainer 更新 Docker 容器镜像/重新拉取镜像

背景

我想把 Portainer 管理的 Immich 图库容器更新到新版本(v2.5.6),所以需要重新拉取容器镜像

这篇教程同样适用于其他容器的更新

解决方法

首先登录 Portainer 后台,打开左侧菜单里的 Containers,在容器列表内找到要更新的容器

这里我要更新 immich_server 这个容器,所以点击容器名称进入到详情页面

在详情页点击 Stop 按钮停止容器后点击 Recreate 按钮

下一步在弹出的提示窗中把 Re-pull image 选项启用,再次点击 Recreate 按钮

注意:容器内如有非持久化数据,更新后会丢失

最后,等待更新完成