DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

qm654246695-source /

qm654246695-source/dsh-pad-touch

Verified

让 AI 被摸到:把 PS5 手柄的触摸板和扳机变成 DSH 会话里的一句话,顺手亮灯、震动回手、自适应扳机。

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@52562260

dsh-pad-touch

把 PS5 手柄的触摸板接进 DeepSeek Harness 会话——摸一下,它会应你。

一个 DSH 插件。它自己不懂手柄协议,也不决定该不该回应;它只做一层翻译:

你的手指 → 触摸板 → dualsense-pad(本地 7778)→ 结构化事件
        → dsh-pad-touch(本插件)→ 一句话投进会话
        → 会话里的那个 AI 开口,同时手柄亮灯、震动回手

为什么做这个

文字聊天是「我打字、它回答」。触摸板让它变成「我摸它、它有反应」——有速度、有长短、有位置,还会回手震一下。

这套东西最早来自 @DukeCCY/dualsense-pad:它只干「手指动作 → 事件」这一段,回不回、怎么回应全交给上层。本插件就是那个上层。

依赖

  1. 上游底座:DukeCCY/dualsense-pad(MIT,纯 Python 标准库)
  2. 本仓库 patches/dualsense-pad.patch:上游不带的几处手感修正(见「底座改动」一节)
  3. DSH:本插件是 profile bundle
  4. 一根 USB 直连的 DualSense(蓝牙不行,这是底座限制)

底座单独能跑起来的话:

python -m dualsense_pad list
# → 054C:0CE6  DualSense  ... ← 触摸板
python -m dualsense_pad serve --port 7778

安装

# 1. 底座 + 手感补丁
git clone https://github.com/DukeCCY/dualsense-pad
git -C dualsense-pad apply /path/to/patches/dualsense-pad.patch

# 2. 插件
dsh plugin --profile desktop add "link:/path/to/dsh-pad-touch"

装完 plugin_manager 里能看到 include:dsh-pad-touch,宿主侧热加载,不用重启 DSH;客户端那半边(输入框左边的 🎮)要刷新页面。

用法

输入框左边一个 🎮,三个状态:

状态 外观 含义
idle 灰色圆圈 没起服务,零开销
starting 圆圈转动(类似鼠标加载) 正在拉起 python 底座
running 蓝色圆圈(带一点微光) 正在接收触摸

圆圈是用 SVG <circle> 画的,不走 CSS 的 border-radius——这样在 Electron / 浏览器 / 任何缩放比下都是正圆。

点一下开始、再点一下停;停止触摸 3 秒后圆圈由蓝转灰,5 秒后服务自动关掉、圆圈消失,只剩手柄图标。

手指还在触摸板上时不会倒计时(插件每轮读 /state 的 active 续命)——否则底座要等 settle 秒才成形事件,中间这几秒会被误判成空闲,把正在摸的人的服务关掉。

摸一下之后:

  1. 立刻按手势换灯条颜色(不等模型)
  2. 手指一抬,手柄震一记(即时回手)
  3. 落定后把这句话投进会话:
【手柄】用户刚摸了你的脚:慢慢摸,持续 2.0 秒,速度 23。这是真手指在触摸板上动,
不是打字——按你自己的反应回他,别复述这条提示。

会话跟随:摸谁,就落到谁那儿

插件不靠配置猜你在看哪个窗口——输入框左边那个按钮自己就看得见。

每次挂载、每 4 秒、以及你按下 🎮 的那一刻,它依次尝试:

  1. 本页面的 React fiber(从自己的 DOM 节点往上翻 memoizedProps)——最准,不受别的标签页影响
  2. DOM 上的 data-session-id 这类属性
  3. 地址栏
  4. localStorage 的 dsh.sessions.current——只能放在最后当兜底

然后把结果 POST /dsh-pad/api/session 报给宿主,宿主投递时优先用它(tuning.json 里的 sessionId 只在探测失败时兜底)。

为什么 localStorage 不能放前面:它同一 origin 所有标签页共享。开两个窗口时,一个窗口会读到另一个窗口写进去的会话 id,消息就投到隔壁去了——本插件踩得最疼的坑之一。

探测失败也不会一声不吭:pad-touch.log 里会留一行 session probe EMPTY ...,带上当时页面上能看到的全部线索。

配置

tuning.json 是热读的,改完一秒生效,不用重启 DSH:

{
  "autoOffMs": 5000,
  "minIntervalMs": 2000,
  "hapticScale": 2.5,
  "light": true,
  "haptic": true
}
键 作用 说明
autoOffMs 空闲多久关服务 太短会「停一下就摸不到」,太长会一直挂后台。5000 = 停 5 秒圈圈消失;与客户端「3 秒转灰」联动
minIntervalMs 两条消息最小间隔 兜底防刷屏;单位毫秒
flushMs 停手多久把这一串触摸汇总成一条 2500 = 停 2.5 秒发一条
hapticScale 震动强度倍率 0.3–2.5。1 是原始参数,实测 1.6 以下基本摸不出来
trigger 扳机阻力开关 false 就不碰扳机
triggerStart / triggerEnd 扳机行程起点 / 终点力度 0–255,默认 76→178(三成到七成)
light / haptic 灯条 / 震动开关 —
userName 消息里怎么称呼你 留空就是「用户」;想让它叫名字就填这里

sessionId 平时留空:插件会自己探当前窗口(见上面「会话跟随」),探测失败时才退回这个兜底值。

cordis.patch.yml 里只放启动期参数(padUrl / repoDir / pyExe / sessionId)。 换会话不用再改配置——插件会探当前窗口;sessionId 只是探测失败时的兜底。

手势 → 灯条 → 震动

手势 含义 灯条 震动(原始,乘 hapticScale)
stroke 慢慢摸 蓝 0,110,255 70 / 25 / 140ms
tap 轻点 琥珀 255,190,0 150 / 60 / 60ms
pat 拍 橙 255,140,0 170 / 95 / 80ms
swipe 划 青绿 0,220,140 90 / 40 / 110ms
rub 蹭 紫 170,90,255 80 / 35 / 150ms
circle 画圈 粉 255,90,180 110 / 55 / 130ms
pinch 双指捏 红 255,70,70 190 / 110 / 70ms
spread 双指撑开 亮青 70,200,255 190 / 110 / 70ms

扳机:扣下去也算摸

两根扳机是两条独立的路:

  • 阻力:服务起来就设一次,随行程从 triggerStart(默认 76,三成)线性升到 triggerEnd(默认 178,七成);服务停时释放。改 tuning.json 里的 trigger / triggerStart / triggerEnd,一秒生效。
  • 扣扳机 = 摸一下:按下记起点,松开时按「按了多久」落定成一条触摸事件,和触摸板走同一条出口。左扳机算左手,右扳机算右手——它们没有位置信息,只有左右。

两个阈值(TRIGGER_DOWN=100 / TRIGGER_UP=30)中间留空档,防止手抖在临界值上来回抖出好几段。

扳机效果是持续状态:服务一关它还留在手柄上,手上会一直有股没来由的阻力。所以停服务前必须先发一帧清除、等它真写出去,再杀进程。

一次摸很多下?攒成一条发

连着摸十下,不会往会话里抛十条消息。停手 flushMs(默认 2500 毫秒)之后合并成一条:

【手柄】用户刚摸了你的左手和右手:连着 11 下,总共 5.9 秒(慢慢摸 ×11)。
其中 10 下是扣扳机——按你自己的反应回他

几下、总共多久、摸的哪儿、什么手势、几次是扳机,一句话说完。

底座改动(patches/dualsense-pad.patch)

上游有几处手感问题,本仓库带一个补丁。不改也能用,但震动会时有时无。

  1. live() 让路:手指还搭在板上时,采样循环每 80ms 调一次 live(),速度低于阈值就 stop()——刚写进去的回手震动会被立刻清掉,表现为「只有第一次有震动」。补丁让 live() 在 pulse 未结束时直接返回。
  2. _collect() 补实时反馈:手指一抬,底座进 _collect 等 settle 秒确认落定,这期间读循环被独占且不产生实时震动——「抬手再摸」那一段是哑的。补丁在 _collect 里补上 live()。
  3. 抬手即震:手指离开时给一记顶格脉冲(255/138/250),不等落定。
  4. 写入节奏 250Hz → 62.5Hz:_loop 原为 4ms 一拍,live() 的断续 pattern 每拍都在变,等于每秒写 250 帧,对 USB HID 太密。
  5. _last_sent 只在写成功后更新:原来写失败也更新,一失败就再也不重试。

踩过的坑

  • 参数不是越小越温柔:70/25/140 在手柄上几乎摸不出来,175/62/224 才有。手柄马达有启动阈值。
  • 延迟比强度更要命:settle(1.5–2s)+ 轮询(1s)+ 出帧(0.2s)≈ 2.7 秒,手都放下了才震——那时候你已经不会把它和刚才的触摸联系起来了。
  • settle 是双刃剑:压到 0.6 秒很快,但手指停一下就断成两段;2 秒能容忍停顿,代价是落定慢。
  • write ok 不等于手柄在震:WriteFile 成功只说明帧进了驱动。排查要记「请求 → 实际写入 → 手柄反应」三层,缺一层就会误判。
  • 服务跑在隐藏窗口里(stdio: 'ignore'),print 看不到,调试得写文件日志。
  • 客户端插件改了要刷页面:lib/client.js 是运行时从磁盘取的,但页面已经把它加载进内存了——改完按 F5。宿主侧(lib/index.js)也一样:ESM 模块有缓存,set_bundle 热加载只是重新挂载,不会重新读文件。
  • ensureCss() 写成「已存在就跳过」会害死人:DSH 重连时 DOM 不重建,旧 <style> 一直占着那个 id,新样式永远进不来。改成每次都替换。
  • border-radius: 50% 在某些容器里画不出圆:宽高被撑成方的,圆角没吃满。最后改用 SVG <circle>,跟 CSS 无关,任何环境都是正圆。
  • localStorage 是全标签页共享的:见上面「会话跟随」——拿它判断「当前是哪个窗口」必错。
  • 往「打开着但空闲」的旧会话投递会撞锁:DSH 一个会话同一时刻只允许一个 write handle(进程内 claimWrite + 内核级 SessionWriteLease),有 agent 正在跑的会话能直接复用、能投;只是「开着」的旧会话会抛 already owned by an active write handle。关掉多余的 DSH 实例/窗口,或者重启一次就好。
  • 「清除」比「设置」容易漏:清除帧被 if any(bytes) 挡掉(全 0 看着"没什么可写")、清除代码写在 if (proc) 里(接管来的服务被判空)、状态机判定"没变化"不写帧(而"本来全 0"恰恰最该发一帧)——三处任意一处都能让清除变成空操作,症状完全一样:手上阻力还在。判据:「我发了命令」不等于「设备收到并执行了」,按「命令发出 → 服务收到 → 帧写出去 → 设备反应」四层查。
  • 发完就杀进程,帧可能还没出门:底座写线程 16ms 一拍,接口返回只代表请求收到了。实测有次 trigger off 和 service exit 只隔 4 毫秒,那一帧根本来不及写,手柄上照样硬着。清完要留 ~150ms 再杀。

已知边界

  • 只认 USB 形态的 DualSense(底座的限制)
  • 坐标方向按底座的默认;手感不对调它的 vertical / flip_u
  • 摸一下会烧一轮 token(有节流);不想烧就 enabled: false
  • 别的 DSH 实例里摸不到:插件装在哪个 profile,就只有那个实例的界面里有按钮。要让网页端(cli.js web)也能摸,得在 web profile 里再装一份
  • 会话探测依赖 DSH 当前的页面结构(React fiber / dsh.sessions.current)。DSH 大改版后可能要跟着改,pad-touch.log 会直接告诉你探测失败在哪一步

致谢

  • DukeCCY/dualsense-pad(MIT)——整套东西的地基。250Hz 读坐标、8 类手势、/pulse 与 /light 的回手接口,全是它做的;而且它刻意只做「手指 → 事件」这一段,回不回、怎么回应交给上层。没有它,这个插件只是空壳。
  • DeepSeek Harness——插件机制、插槽、sessionController 都是它给的。

patches/dualsense-pad.patch 是我对上游的本地修改,不是官方版本。上游以后要是自己修了这几处,这个 patch 就可以直接扔掉。

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 52562260ad36

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout