卡片
浏览卡片类型,创建实例并更新其实时数据。播放列表放置返回的卡片 ID。
screenrig cards list --category ops
screenrig cards show gauge
screenrig card create --type gauge --file gauge-params.json
screenrig card list
screenrig card show crd_01EXAMPLEGAUGE000000000
screenrig card params crd_01EXAMPLEGAUGE000000000 --file gauge-params.json
screenrig card data get crd_01EXAMPLEGAUGE000000000
screenrig card data put crd_01EXAMPLEGAUGE000000000 --file gauge-data.json --ttl 30m --expect-rev 1
screenrig card preview crd_01EXAMPLEGAUGE000000000 --output gauge.png
screenrig card delete crd_01EXAMPLEGAUGE000000000| 参数 | 类型 | 必填 | 默认 | 备注 |
|---|---|---|---|---|
cards list --tag TAG | string | 否 | 无 | 匹配目录标签。 |
cards list --category CATEGORY | string | 否 | 无 | 匹配目录类别。与 --tag 同用时,卡片必须同时匹配两者。 |
cards show <id> | string | 是 | 无 | 来自 cards list 的目录卡片 ID,例如 gauge。 |
card create --type ID | string | 是 | 无 | 来自 cards list 的目录卡片 ID。 |
card create --major N | integer | 否 | 当前主版本 | 固定目录的某个主版本。 |
card create --file FILE | 路径 | 是 | 无 | 静态参数,最大 1 MiB 的 JSON 对象,或用 - 从 stdin 读取。 |
<id> | string | 是 | 无 | card show、card params、card delete 和 card data 接受的 crd_ 卡片 ID。 |
card params --file FILE | 路径 | 是 | 无 | 新的静态参数,读取方式同 card create。它们立即替换旧参数。 |
card data put --file FILE | 路径 | 是 | 无 | 数据文档,JSON 对象,或用 - 从 stdin 读取。它会替换整个文档。 |
card data put --ttl DURATION | string | 否 | 无 | 经过这么长时间后清空数据,离线屏幕也包括在内:30m、12h 或 1d,范围从 1s 到 365d。不能与 --expires-at 同时使用。 |
card data put --expires-at INSTANT | string | 否 | 无 | 在此 ISO 8601 时间戳(带时区)清空数据。两个选项都不带的写入会清除原有的过期时间。 |
card data put --expect-rev REVISION | string | 否 | 无 | 仅当数据仍处于此版本(由 card data get 显示)时才写入。过期的值会返回 revision_conflict(退出码 6)。 |
card preview <type|id> | string | 是 | 无 | 目录中的卡片 ID,或 crd_ 卡片 ID,用于以该实例自己的参数和数据绘制它。 |
card preview --output FILE | 路径 | 是 | 无 | 把 PNG 写到这里。会自动创建父目录。 |
card preview --params FILE | 路径 | 否 | 无 | 静态属性,JSON 对象。 |
card preview --data FILE | 路径 | 否 | 无 | 动态文档,JSON 对象。 |
card preview --feed FILE | 路径 | 否 | 无 | 主机 feed 文档,JSON 对象。卡片需要而您省略的 feed 会被报告。 |
card preview --width PX / --height PX | integer | 否 | 1920 / 1080 | 图像尺寸,1 到 8192。 |
card preview --layout LAYOUT | string | 否 | full | full、free、center、wide、header、footer、corner、chip 或 strip:要绘制的布局。 |
card preview --anchor ANCHOR | string | 否 | 无 | corner 或 chip 布局的角,或 strip 的边。 |
card preview --now ISO | string | 否 | 当前时间 | 将时钟冻结在此 ISO 8601 时刻。 |
card preview --timezone ZONE | string | 否 | 本机的 | 按此 IANA 时区绘制,例如 America/Vancouver。 |
cards show 会显示卡片的 params_schema 和 data_schema、静态与动态属性、层级、布局、feed,以及可复制到播放列表页面的示例。card create 返回实例的 crd_ ID;响应丢失后重新运行相同命令,会重用已保存的幂等键。
card data put 会立即替换文档,无需重新发布播放列表。卡片自身的校验会拒绝它无法显示的文档(card_data_invalid),到期后,若无人续期,所有屏幕上的数据都会清空。card delete 会删除该实例及其数据,只要有播放列表放置了它,就返回 resource_conflict(退出码 5)。
card preview 在本地 Chromium 或 Chrome 中绘制卡片并写出 PNG,不会发布任何内容。如果没有安装常见浏览器,请把 SCREENRIG_BROWSER 设为浏览器的路径。data.file 是写出的路径,data.unrendered 列出预览无法绘制的内容,例如您没有提供的主机 feed。发布前请先打开图片查看。内置卡片介绍每张卡片的布局、属性和 feed。
卡片 webhook
卡片 webhook 是写入一张卡片数据文档的 URL,因此 CMS、销售点或脚本等外部系统只需一次 HTTP POST 即可向卡片推送数据。kv webhook create、list、rotate-secret 和 delete 用 --card CARD_ID 代替 --app-id,二者不能同时使用;下方的 update 和 deliveries 仅适用于卡片。每张卡片最多有 20 个 webhook。
kv webhook create --card CARD_ID [--description TEXT] [--map FILE|-] [--key-template TEMPLATE] [--signing]
[--verify VENDOR [--verify-secret-file FILE|-] [--verify-header NAME] [--verify-encoding hex|base64]
[--verify-timestamp-header NAME] [--verify-tolerance SECONDS]] [--handshake VENDOR]
kv webhook list --card CARD_ID
kv webhook rotate-secret <webhook_id> --card CARD_ID [--signing]
kv webhook delete <webhook_id> --card CARD_ID创建卡片 webhook 不需要键。--map FILE(或用于 stdin 的 -)是映射,--key-template 写入按键存储的文档,--signing 开启签名,--verify 和 --handshake 接受服务商自己的请求(服务商签名和握手)。响应包含 data.secret,如果 webhook 签名,还包含 data.signing_secret,各仅显示一次。
发送方的认证方式与应用 webhook 相同:使用 srkw_… 密钥的 Authorization: Bearer <secret>,或以 webhook ID 为用户名的 HTTP Basic,因此 https://kvw_ID:srkw_…@api.screenrig.ai/… 适合只能填写 URL 的发送方。请求体最大 1 MiB,为 JSON 或 application/x-www-form-urlencoded;重复的表单名会变成数组。没有映射时,发送的对象就是卡片的数据文档。
映射是一个 JSON 模板,用于根据请求体构建文档。包含占位符(如 {{body.alert.title}} 或 {{body.items[0].name}})的字符串是文本,恰好只有一个占位符的字符串会保留值的类型。路径以 body 或 query(URL 的查询参数)开头,不含任何内容的路径在文本中为空,单独出现时会省略该值。$each 映射数组,并提供 item 和 index,$group 配合 $by 按首次出现的顺序分组,$number 读取 1,234.50、$3.50、3,50 € 或 12%(逗号作小数点时添加 "$decimal": ","),$bool 读取 true、yes、y、1 和 x,$text 配合 $max 截断文本。模板在保存时检查:invalid_request 会指出失败的位置。
{
"title": "{{body.alert.title}}",
"body": "{{body.alert.message}}",
"items": { "$each": "{{body.items}}", "$limit": 20, "$template": { "text": "{{item.name}}" } }
}curl https://api.screenrig.ai/api/cards/crd_01EXAMPLEBOARD0000000000/kv-webhooks/kvw_01EXAMPLEWEBHOOK00000/write \
-H "Authorization: Bearer $CARD_WEBHOOK_SECRET" \
-H "Content-Type: application/json" \
--data '{"alert":{"title":"Fire drill","message":"Leave by the east door"},"items":[{"name":"Floor 2"}]}'--key-template TEMPLATE 以按键存储的文档取代卡片自己的文档。模板是带有 {screen.field.NAME} 令牌的卡片数据键,例如 room-{screen.field.room};映射后的对象(没有映射时为提交的正文)只包含由 {fields, document} 对象组成的 documents 数组:每个 fields 对象填充模板,每个 document 写入它所生成的键下。把同一模板作为 data 的页面项会让每个屏幕显示自己字段对应的文档。所有文档在写入任何一个之前都会先检查,重复的键会被拒绝。存储故障可能使一批文档只写入一部分:请用相同的正文和相同的 Idempotency-Key 重试。请求中省略的键保留原有数据,webhook 不设置过期时间。按键存储的文档展示了一个映射。
每次投递都会按卡片自己的数据检查:card_data_invalid 指出路径,且不写入任何内容。与卡片当前内容相同的文档会返回 unchanged 且不写入,因此不会唤醒任何屏幕。被接受的投递不计入智能体请求上限,一个 webhook 每分钟最多接受 120 次投递,Idempotency-Key 标头会对发送方的重试去重 24 小时。
错误的地址、错误或缺失的密钥、已停用的 webhook、缺失或错误的签名以及每个 GET 都得到相同的 404 响应,因此链接预览或猜测不会泄露任何信息。kv webhook deliveries 会说明原因。
使用 --signing 时,每次发送还必须携带 ScreenRig-Signature: t=<unix seconds>,v1=<hex>,其中 v1 是 t、一个点和原始请求体在签名密钥 whsec_… 下的 HMAC-SHA256,且 t 必须在服务器时钟的五分钟以内。这是 screenRIG 自身 webhook 签名的格式。
服务商签名和握手
服务商各自用自己的方式给请求签名,有些根本无法发送您的密钥。卡片 webhook 可以用服务商的签名代替密钥来验证,并响应服务商的设置握手,因此 GitHub、Stripe、Shopify、Slack、Linear、Vercel、Asana、Microsoft Graph、monday.com、Trello 和 Azure Event Grid 可以直接向它发送,无需中转。其他任何用 HMAC 签名的发送方同样可用。用 kv webhook create 配合 --verify 和 --handshake 进行设置,用 kv webhook update 进行更改。
--verify VENDOR接受服务商自己的签名,--verify-secret-file FILE(用于 stdin 时为-,不能作为参数)保存服务商显示的密钥。对于generic_hmac,--verify-header NAME指定携带签名的请求头,--verify-encoding为hex(默认)或base64,--verify-timestamp-header NAME指定携带 Unix 时间的请求头,--verify-tolerance SECONDS表示该时间与服务器时钟的最大偏差(默认 300)。带有服务商有效签名的请求会被接受,无需 webhook 自己的srkw_密钥,该密钥仍可并行使用。服务商密钥加密封存,只显示为set和其最后四个字符的hint。在kv webhook update中,不带--verify-secret-file的--verify会在服务商不变时保留已存储的密钥,--clear-verify会移除验证及其密钥。--verify和--signing互相排斥。--handshake VENDOR是microsoft_graph、monday、trello或azure_event_grid,--clear-handshake会将其移除。Slack 和 Asana 通过--verify响应各自的握手。
--verify | 标题 | 签名的内容 | 时间戳 |
|---|---|---|---|
github | X-Hub-Signature-256 | 正文的 HMAC-SHA256 | 无 |
stripe | Stripe-Signature | 时间戳、一个点和正文的 HMAC-SHA256;任何一个 v1 匹配即可 | 5 分钟 |
shopify | X-Shopify-Hmac-Sha256 | 正文的 HMAC-SHA256,base64 编码 | 无 |
slack | X-Slack-Signature | v0:、时间戳、一个冒号和正文的 HMAC-SHA256 | X-Slack-Request-Timestamp,5 分钟 |
linear | Linear-Signature | 正文的 HMAC-SHA256 | 正文中的 webhookTimestamp,1 分钟 |
vercel | x-vercel-signature | 正文的 HMAC-SHA1 | 无 |
asana | X-Hook-Signature | 以握手的 X-Hook-Secret 为密钥的正文 HMAC-SHA256 | 无 |
generic_hmac | 您指定的 --verify-header | 正文的 HMAC-SHA256;设置了 --verify-timestamp-header 时则是时间戳、一个点和正文的 HMAC-SHA256 | --verify-tolerance |
每个 HMAC 的密钥都是服务商所显示的密钥文本(Stripe 的包含 whsec_)。签名按正文到达时的原样以恒定时间验证,并在请求消耗写入额度之前完成,而且先于其时间戳进行判断:timestamp_stale 始终表示密钥是正确的。正文可以是 JSON、表单或 multipart/form-data,其中文本字段为 body.FIELD,文件只计数、不读取。
| 服务商 | screenRIG 的响应 | 后续请求 |
|---|---|---|
Slack(--verify slack) | 带有其 challenge 的已签名 url_verification;用 curl 测试的人可以改为发送 webhook 的密钥 | 用签名密钥签名 |
Asana(--verify asana) | 握手的 X-Hook-Secret,它会加密封存并作为每个 X-Hook-Signature 的密钥。再次收到相同的密钥视为重试;不同的密钥会被拒绝,并返回 handshake_invalid | X-Hook-Signature |
Microsoft Graph(--handshake microsoft_graph) | 将解码后的 ?validationToken= 作为 text/plain 返回 | 携带 webhook 的密钥 |
monday.com(--handshake monday) | 它发送的 challenge,以 JSON 原样返回 | 携带 webhook 的密钥 |
Trello(--handshake trello) | HEAD 检查仅针对此 webhook,返回 200 且不带正文 | 携带 webhook 的密钥 |
Azure Event Grid(--handshake azure_event_grid) | {"validationResponse": "CODE"} | 携带 webhook 的密钥 |
握手只会针对为该服务商设置的 webhook 响应,在要求任何凭据之前,针对属于该握手的请求,并且已停用的 webhook 绝不会响应。它不写入任何内容,从不计费,也不消耗速率额度。Microsoft Graph、monday.com、Trello 和 Azure Event Grid 不发送 screenRIG 验证的请求头,因此它们的投递只有携带 webhook 的密钥才会被接受。Azure Event Grid 把它作为标记为机密的静态投递头 Authorization: Bearer srkw_… 携带,其事件以数组形式到达,所以映射读取 body[0]。
| 服务商 | 在服务商的设置中 |
|---|---|
| GitHub | Settings、Webhooks、Add webhook:Payload URL 填 webhook 的 url,内容类型填 application/json,密钥相同。GitHub 的 ping 带有签名,会被接受 |
| Stripe | Workbench、Webhooks:添加 url 并显示其签名密钥。Stripe 轮换密钥期间会为每个密钥发送一个 v1,任一个都会被接受;之后用 kv webhook update --verify stripe --verify-secret-file FILE 替换已存储的密钥 |
| Shopify | 用 url 以 JSON 格式订阅。密钥是应用的客户端密钥 |
| Slack | Events API 的 Request URL 是 url,密钥是应用的签名密钥。斜杠命令会发送表单:{{body.text}}、{{body.user_name}} |
| Linear | 在 API 设置中用 url 创建 webhook,并复制其签名密钥。正文中的 webhookTimestamp 必须与服务器时钟相差不超过一分钟 |
| Vercel | Team settings、Webhooks:用 url 创建一个。Vercel 只会显示一次密钥 |
| Asana | 用 --verify asana 创建 webhook:它还没有密钥,会以 secret_pending 拒绝事件。然后创建 Asana webhook,把 target 设为 url,握手会设置密钥。要换用新密钥,先带 --clear-verify 运行 kv webhook update,再用 --verify asana 重新设置 |
| Microsoft Graph | 创建订阅,把 notificationUrl 设为 url |
| monday.com | 用 url 运行 create_webhook |
| Trello | POST /1/webhooks,把 callbackURL 设为 url |
| Azure Event Grid | 创建事件订阅,把 url 作为其 webhook 端点,并添加上面的投递头 |
echo '{"title":"{{body.repository.full_name}}","body":"{{body.head_commit.message}}"}' |
screenrig kv webhook create --card crd_01EXAMPLEBOARD0000000000 --description "Pushes" \
--verify github --verify-secret-file github.secret --map -
用自己的 HMAC 签名的发送方,需要指定请求头、编码,如果签名时间戳,还要指定时间戳请求头:
echo '{"title":"{{body.sensor}}","body":"{{body.state}}"}' |
screenrig kv webhook create --card crd_01EXAMPLEBOARD0000000000 --verify generic_hmac --verify-secret-file acme.secret \
--verify-header X-Acme-Signature --verify-encoding base64 --verify-timestamp-header X-Acme-Time --verify-tolerance 120 \
--map -
ts=$(date +%s); body='{"sensor":"door","state":"open"}'
sig=$(printf '%s.%s' "$ts" "$body" | openssl dgst -sha256 -hmac "$SECRET" -binary | openssl base64 -A)
curl "$URL" -H "Content-Type: application/json" -H "X-Acme-Time: $ts" -H "X-Acme-Signature: $sig" -d "$body"
每次失败都和其他任何拒绝返回相同的 404。kv webhook deliveries 会显示原因,握手的结果显示为 handshake:
| 原因 | 含义 |
|---|---|
signature_missing | 已设置服务商验证,而请求既没有携带密钥,也没有携带服务商的请求头 |
signature_invalid | 请求头格式错误,或与正文不匹配 |
timestamp_missing | 签名需要时间戳,而请求没有携带 |
timestamp_stale | 签名有效,但其时间戳超出时间窗口 |
secret_pending | Asana 的验证仍在等待其握手 |
handshake_invalid | 该请求是某个服务商的握手,无法响应 |
kv webhook update#
更改卡片 webhook:其描述、映射、按键存储的文档、启用状态、签名、服务商验证或握手。
kv webhook update <webhook_id> --card CARD_ID [--description TEXT] [--map FILE|-] [--clear-map]
[--key-template TEMPLATE] [--clear-key-template] [--enable|--disable] [--signing|--no-signing]
[--verify VENDOR [--verify-secret-file FILE|-] [--verify-header NAME] [--verify-encoding hex|base64]
[--verify-timestamp-header NAME] [--verify-tolerance SECONDS]] [--clear-verify]
[--handshake VENDOR] [--clear-handshake]| 参数 | 类型 | 必填 | 默认 | 备注 |
|---|---|---|---|---|
<webhook_id> | string | 是 | 无 | kvw_ Webhook ID。 |
--card CARD_ID | string | 是 | 无 | crd_ 卡片 ID。 |
--description TEXT | string | 否 | 无 | 向它发送内容的系统,最多 200 字节。不得包含凭证。 |
--map FILE | 路径 | 否 | 无 | 用从文件读取的 JSON 对象替换映射,或用 - 从 stdin 读取。不能与 --clear-map 同时使用。 |
--clear-map | boolean | 否 | 关闭 | 移除映射:发送的对象即成为文档。 |
--key-template TEMPLATE | string | 否 | 无 | 设置为文档生成键的键模板,例如 room-{screen.field.room}。不能与 --clear-key-template 同时使用。 |
--clear-key-template | boolean | 否 | 关闭 | 恢复为写入卡片自己的文档。 |
--enable / --disable | boolean | 否 | 关闭 | 在使用 --enable 之前,--disable 对每次发送的响应都与错误密钥相同。 |
--signing / --no-signing | boolean | 否 | 关闭 | --signing 要求带签名请求头,并仅显示一次签名密钥;已经签名的 webhook 保留其密钥,用 kv webhook rotate-secret --signing 替换。--no-signing 不再要求。不能与 --verify 同时使用。 |
--verify VENDOR | string | 否 | 无 | 替换服务商验证:github、stripe、shopify、slack、linear、vercel、asana 或 generic_hmac。不带 --verify-secret-file 时,若服务商不变则保留已存储的密钥,因此可单独更改请求头或时间窗口;换用其他服务商则需提供它自己的密钥。不能与 --clear-verify 同时使用。 |
--verify-secret-file FILE | 路径 | 否 | 已存储的密钥 | 从文件读取服务商的密钥,或用 - 从 stdin 读取(不能作为参数)。asana 不需要密钥。只有一个选项可以读取 stdin。 |
--verify-header NAME | string | 配合 generic_hmac | 无 | 携带签名的请求头,1 到 100 个字母、数字、- 或 _。 |
--verify-encoding ENCODING | string | 否 | hex | 配合 generic_hmac:hex 或 base64;可以以 sha256= 开头。 |
--verify-timestamp-header NAME | string | 否 | 无 | 配合 generic_hmac:携带 Unix 时间的请求头,单位为秒,或为 13 位数字的毫秒。 |
--verify-tolerance SECONDS | integer | 否 | 300 | 配合 --verify-timestamp-header:时间戳与服务器时钟的最大偏差,1 到 86400。 |
--clear-verify | boolean | 否 | 关闭 | 不再接受服务商的签名,并清除其密钥。Asana 验证只有在先 --clear-verify、再次 --verify asana 之后才会接受新的握手。 |
--handshake VENDOR | string | 否 | 无 | 设置 URL 所响应的握手:microsoft_graph、monday、trello 或 azure_event_grid。不能与 --clear-handshake 同时使用。 |
--clear-handshake | boolean | 否 | 关闭 | 不再响应服务商的握手。 |
至少需要一项更改。
screenrig kv webhook update kvw_01EXAMPLEWEBHOOK00000 --card crd_01EXAMPLEBOARD0000000000 --map alert-map.json
screenrig kv webhook update kvw_01EXAMPLEWEBHOOK00000 --card crd_01EXAMPLEBOARD0000000000 --verify stripe --verify-secret-file stripe.secret
screenrig kv webhook update kvw_01EXAMPLEWEBHOOK00000 --card crd_01EXAMPLEBOARD0000000000 --clear-verify信封就是该 webhook。data.signing_secret 只在开启签名的那次响应中出现一次。更新被中断或结果不明时,请再次运行相同的命令:它会重用已保存的幂等键。
kv webhook deliveries#
读取卡片 webhook 过去七天的投递日志,最新的在前。
kv webhook deliveries <webhook_id> --card CARD_ID [--limit N]| 参数 | 类型 | 必填 | 默认 | 备注 |
|---|---|---|---|---|
<webhook_id> | string | 是 | 无 | kvw_ Webhook ID。 |
--card CARD_ID | string | 是 | 无 | crd_ 卡片 ID。 |
--limit N | number | 否 | 100 | 返回多少条投递记录,1 到 100。 |
screenrig kv webhook deliveries kvw_01EXAMPLEWEBHOOK00000 --card crd_01EXAMPLEBOARD0000000000每一项包含 at、outcome(written、unchanged、refused、invalid 或 handshake)、reason(credentials、signature(表示 ScreenRig-Signature 失败)、disabled、body、mapping、card_data_invalid、rate_limited、internal,或上表中的服务商原因)、detail、status、bytes、files(multipart 请求中的文件部分,会被忽略)、revision 和 request_id。不会存储正文、请求头或凭据,向错误地址发送的请求不会留下任何记录。