选择器
选择器用于标识 HTTP 请求或 WebSocket命令的目标设备和会话。
设备选择器 — URL 路径
适用于所有设备级 HTTP 路由,格式为 /{device_type}/{id_or_index}/…。
| 路由示例 | 选择器的含义 |
|---|---|
inverse3/0 | 按索引获取第一个Inverse3(从 0 开始) |
inverse3/A14 | 设备 ID 为 A14 的Inverse3 |
inverse3/* | 所有Inverse3 设备(通配符) |
*inverse/* | 所有 Inverse 系列设备(Inverse3、Inverse3x、Minverse) |
inverse3:left/0 | 按索引取第一个左手Inverse3 |
*inverse:right/0 | 按索引获取第一个右手Inverse 系列设备 |
verse_grip/0 | 按索引获取第一个有线 VerseGrip |
wireless_verse_grip/* | 所有无线 VerseGrip |
*verse_grip/* | 所有 VerseGrip 系列设备(有线、无线、定制) |
*verse_grip:custom/0 | 按索引获取第一个自定义 VerseGrip(等同于 custom_verse_grip/0) |
类过滤器在索引解析之前进行
<type>:<class>/<N> 选择器会先应用过滤器,然后对过滤后的集合建立索引。<N> 指的是过滤后列表中的位置,而不是原始设备列表中的位置:
*inverse:left/0— 第 0 个左手 Inverse 系列设备。如果连接了三个 Inverse 设备(右、左、右),则会解析为第二个设备, 而不是原始列表中的索引0再次筛选为“必须是左手”。inverse3:right/0— 第 0 个右手 Inverse3。*inverse:left/3— 如果连接的左手 Inverse 设备少于四个,则返回404,即使系统中的 Inverse 设备总数超过四个。
通配符选择器也遵循这一规则:*inverse/* 和 *verse_grip/* 将匹配该系列中的每一个设备,针对这些选择器的 POST / DELETE 该请求将把操作应用到每一个所有匹配项,而不仅仅是第一个。
规则:
GET将拒绝通配符选择器(含义不明确)→400POST和DELETE接受通配符——该操作将应用于所有匹配项404响应使用no device matches selector '<sel>'的格式,其中<sel>针对收到的原始选择器表达式
支持的 {device_type} 值: inverse3、verse_grip、wireless_verse_grip,
或系列通配符 *inverse、*verse_grip。
会话选择器 — ?session=<expr> 查询参数
会话级 HTTP 端点(basis、mount、preset、filters、navigation、SDF)需要 ?session=<expr> 查询参数,用于指定其操作所属的会话上下文。
| 表达式 | 含义 |
|---|---|
| (省略) | 所有会话(仅允许用于 DELETE) |
#123 或 123 | ID 为 123 的会话 |
:0 | 根据索引获取第一个会话 |
:-1 | 根据索引获取最后一个会话 |
profile_name | 使用该配置文件的第一个会话(可能存在歧义) |
profile_name:0 | 指定 profile 且索引为 0 的第一个会话 |
:default:0 | profile 指 default 且索引为 0 的第一个会话 |
co.haply.hub::*:0 | 个人资料 以 co.haply.hub:: 以(glob 通配符)开头的第一个会话 |
:co.haply.hub::*:-1 | co.haply.hub:: 命名空间中的最后一个会话 |
示例:
GET /inverse3/0/config/navigation?session=:default:0
配置文件 名称通配符
profile 模式支持两个 glob 通配符:
| 元字符 | 含义 |
|---|---|
* | 匹配任意字符序列(包括空序列) |
? | 正好匹配一个字符 |
所有其他字符——包括 :、.、-、[、]——均按字面匹配(不支持字符类,例如 [abc])。匹配区分大小写。
单独的 *(或 all)保留其原意,即所有会话。若要按名称匹配 * 开头的 profile,请至少包含一个其他字符(例如 *foo)。
常用模式:
co.haply.hub::*—co.haply.hub::命名空间中的任意配置文件*-update— 任意以-update结尾的个人资料co.haply.*:*— 命名空间以co.haply.以任意 profile 开头的defaul?— 任意以defaul开头,共 7 个字符的个人简介
通配符模式本身存在歧义(可能匹配多个会话)。若要在 GET / POST 在端点上使用时,请在末尾添加 :<index> 为消除歧义:
# First session in the Haply Hub namespace
GET /inverse3/0/config/basis?session=co.haply.hub::*:0
# Last session in a Unity namespace
GET /inverse3/0/config/mount?session=:co.haply.unity::*:-1
在 GET/POST 请求中,不带索引的歧义通配符会被 400 拒绝。但在明确支持多会话匹配的路由上则允许使用——GET /sessions/<sel>(返回所有匹配项)和 DELETE 端点(适用于所有匹配项)。
会话选择器 — URL 路径
/sessions/{selector} 端点接受与路径参数相同的表达式——用于查询单个会话或验证其是否存在:
GET /sessions # list all active sessions
GET /sessions/:default:0 # one session by profile + index
GET /sessions/:-1 # last active session
GET /sessions/#42 # session id 42
GET /sessions/co.haply.hub::* # every session in the co.haply.hub:: namespace
GET /sessions/co.haply.hub::*:0 # first session in that namespace
这在功能上等同于 GET /sessions?session=<selector>;这两条路由调用的都是同一个处理函数。GET /sessions/<sel> 是少数几个允许省略的 :<index> 后缀,是接受歧义通配符模式的端点之一——它会以与 GET /sessions 该列表结构将返回所有匹配项。
上述所有选择器形式——包括 #42 以及带 * 的 profile 模式——都会被标准 HTTP 客户端(Python requests、libhv、curl、fetch)原样传输。无需在客户端进行 URL 编码。在 shell 中,请对包含 # 或 * 参数需加引号(例如 --session "#42"、--session "co.haply.hub::*:0"),以防止 shell 将其视为注释,或对其进行本地文件的 glob 展开。
一个例外:在路径中,? glob 字符是 URL 查询分隔符。在路径形式的选择器中(例如 /sessions/defaul?:0),它必须编码为 %3F,即 GET /sessions/defaul%3F:0。在查询表达式中的选择器(?session=defaul?:0)中,只有第一个 ? 将查询部分分隔开来,后续的 ? 字符将原样传递。
在 WebSocket 连接中,你就是对话本身——你的 configure 和 commands 该条目将自动指向当前会话中的设备。只有 超文本传输协定 只有在请求时才需要会话选择器(包括跨会话远程控制,参见 会话)。