数据源如何工作
- 一个数据源对应一张卡片。 数据源会指定它所填充的卡片。一张卡片最多有一个数据源,一个组织跨其所有项目最多有 200 个。
- 按计划运行。 日历数据源每 5 分钟同步一次,所有数据源都可设置为 1 分钟到 24 小时之间。数据源在创建或更改后几秒内运行,
source refresh可立即同步。 - 同步失败绝不会改动卡片。 卡片保留最后一份有效文档,数据源的状态会说明出了什么问题。失败会自动重试,每次失败后等待更久。
- 只写入变化。 同步发现内容与卡片已有内容相同时,不会写入任何内容,也不会唤醒屏幕。
- 同步免费。 文档与其他写入一样,计入卡片数据存储。
- 凭据经过加密。 能授予访问权限的地址会加密存储,只回显其协议和主机,绝不会出现在日志、错误或项目事件日志中。
- 在您选定的期间内保留最后一次正常的数据。 数据源无法刷新时,其卡片会在该数据源的过期时长(
--stale-after,1 分钟到 7 天;场馆预订数据源为 30 分钟)内保留最后一次正常的数据,之后数据在所有屏幕(包括离线屏幕)上过期,依赖它的页面会切换到其后的页面。未设置时长时,最后一次正常的数据会保留到数据源再次刷新。参见数据源无法刷新时。
从预设开始
预设是为人们常放到屏幕上的服务准备好的现成数据源。选一个,填几个字段,再加上该服务签发的凭据,screenRIG 就会创建数据源并把返回结果映射到卡片。预设是 json 数据源,所有套餐都包含。
| 预设 | 卡片 | 需要什么 |
|---|---|---|
slack-channel | workplace-posts, message-wall | 频道 ID,以及您工作区内部 Slack 应用的机器人令牌(xoxb-…):在 api.slack.com/apps 添加范围 channels:history 和 groups:history,安装应用,然后把它添加到频道 |
bonusly | kudos | 带有 recognition:read 的 Bonusly 访问令牌,由 Global 或 Tech 管理员在 Company、Integrations、API & Tokens 中创建 |
facebook-page | social-wall | 主页 ID、要显示的名称,以及在 Graph API 测试工具中获取的、带有 pages_read_engagement 和 pages_show_list 的主页访问令牌 |
instagram-business | social-wall | Instagram 账号 ID、要显示的名称,以及关联 Facebook 主页的访问令牌,需带有 instagram_basic、pages_show_list 和 pages_read_engagement |
google-reviews | reviews | 地点 ID,以及已启用 Places API (New) 的 Google Cloud API 密钥 |
google-travel-times | travel-times | 出发地址、一到八个目的地、驾车或公共交通,以及已启用 Routes API 的 Google Cloud API 密钥 |
greenhouse-jobs | now-hiring | 来自 boards.greenhouse.io/TOKEN 的看板令牌。无需凭据:看板是公开的 |
lever-jobs | now-hiring | 来自 jobs.lever.co/NAME 的站点名称和区域。无需凭据 |
ashby-jobs | now-hiring | 来自 jobs.ashbyhq.com/NAME 的看板名称。无需凭据 |
github-actions | build-board | 所有者、仓库和分支,以及对该仓库 Actions 具有只读访问权限的细粒度个人访问令牌,在 github.com/settings/personal-access-tokens 创建 |
控制台、您的智能体和 API 都会列出这些预设,并附上每个字段的帮助说明和示例,以及获取各项凭据的步骤:GET /api/source-presets 返回全部预设,GET /api/source-presets/NAME 返回其中一个(见 API 参考的数据源分组)。创建数据源时,用 preset、settings 和 secrets 代替 kind 和 config。请把凭据放在请求文件中,不要放在命令行上,并把 BOT_TOKEN 替换为令牌:
{
"name": "Announcements",
"preset": "slack-channel",
"target": {"card_id": "crd_CARD"},
"settings": {"channel": "C0123456789", "channel_label": "#announcements"},
"secrets": {"token": "BOT_TOKEN"}
}curl -sS https://api.screenrig.ai/api/sources/test \
-H "Authorization: Bearer $SCREENRIG_TOKEN" -H "Content-Type: application/json" -d @source.jsonPOST /api/sources/test 会输出卡片将显示的文档、卡片是否接受它以及发现的情况,并且不写入任何内容。POST /api/sources 保存同一个文件,带新 settings 的 PATCH /api/sources/ID 会基于预设重新构建数据源。
- 您的账号,您的条款。 预设使用您创建的凭据读取您自己的账号,并遵守该服务的条款,卡片会显示这些条款要求的内容:Google 评价会带上 Google 标识和每位评价者的姓名。
- 计划与过期。 每个预设都有自己的刷新间隔和过期时长,例如 Slack 为 1 分钟和 15 分钟,Google 评价为 6 小时和 24 小时。数据源可以选择其他间隔,但不能比预设的最小值更短。同步失败时,卡片会保留最后的数据,直到过期时长结束,然后变为空白。
- 费用由服务方计收。 Google 评价每刷新一次,就是您 Google Cloud 项目上的一次 Place Details 请求;Google 行程时间每刷新一次,每个目的地就是一个路线元素:四个目的地每 15 分钟刷新,每月约 11,500 个元素。
- Slack 需要内部应用。 不要分发它:Slack 会把在其 Marketplace 之外分发的应用限制为每分钟一次请求。应用只读取它被添加到的频道,卡片显示的是人们自己发的消息,不包括加入通知、机器人发布的内容或频道通知。
创建数据源
先创建卡片,然后预览,最后保存。
screenrig card create --type events --file events.json
screenrig source test --kind ics --card crd_CARD \
--set 'calendars=[{"key":"lobby"}]' --set time_zone=America/Vancouver \
--secret calendar_url.lobby=@lobby.url
screenrig source create --kind ics --card crd_CARD --name "Lobby calendar" \
--set 'calendars=[{"key":"lobby"}]' --set time_zone=America/Vancouver \
--secret calendar_url.lobby=@lobby.url
screenrig source show src_SOURCEsource test 会获取该地址,按卡片的需要整理,并用卡片校验结果,不保存也不写入任何内容。它会显示文档、卡片是否接受,以及过程中发现的情况。请通过文件 (@FILE) 或标准输入 (-) 提供地址,使其不会留在 shell 历史记录中。
| 命令 | 作用 |
|---|---|
source list, source show ID | 项目的数据源,附带状态、设置和已遮蔽的密钥 |
source create | 保存数据源,并在几秒内运行 |
source update ID | 更改名称、卡片、设置(--set KEY=VALUE 或 --settings JSON)、密钥(--secret、--unset-secret)、计划(--refresh 10m),或 --enable 与 --disable |
source test | 预览数据源(无论是否已保存),不写入任何内容 |
source refresh ID | 立即同步并显示结果 |
source delete ID | 删除数据源;卡片保留其最后一份文档 |
--settings 以 JSON、@FILE 或 - 的形式接收数据源的完整配置。CLI 的 --config 选项指定的是其凭据文件,与数据源无关。
数据源无法刷新时
数据源写入每份文档时都附带一个截止时间:写入后加上过期时长,并在数据源持续刷新时不断顺延。数据源正常时,截止时间会一直向后移动。数据源失败后,卡片会一直显示最后一次正常的数据直到截止时间,之后数据在所有屏幕(包括离线屏幕)上过期。
- 设置时长:在
source create或source update上使用--stale-after 30m,范围 1 分钟到 7 天,且至少为刷新间隔的两倍。--stale-after none会保留最后一次正常的数据直到下次刷新。场馆预订数据源(momentus)除非另行指定,保留 30 分钟;其他类型保留到下次刷新。 - 回退到您自己的内容。
visibility.data条件指向该文档(房间则为momentus-room-{screen.field.room})的页面在数据存在时显示,数据过期后让位给下一页。请在它之后放一个带有您品牌的普通页面作为备用。 - 查看。
source show会列出stale_after、data_expires_at,并在数据源持续失败且数据已过期时列出stale: true。仪表板的数据源页面显示同样的内容:失败的数据源显示为“失败中”,数据已过期的显示为“已过期”。 - 接收通知。 针对条件
source_failed(刷新失败)和source_stale(数据已过期)的通知,会像针对屏幕一样发送电子邮件,或发布到 Slack、Teams、PagerDuty 或 Telegram:screenrig notifications create --email owner --conditions source_failed,source_stale --hold 5。通知会写明数据源并链接其页面,在刷新成功后解除,并且--tag永远不会匹配数据源。
状态与错误
每个数据源都会显示状态:上次运行、上次成功和上次更改卡片的时间,下次运行的时间,连续失败的同步次数,以及最近的错误。
| 状态 | 含义 |
|---|---|
pending | 数据源尚未运行 |
ok | 最近一次获取成功,且卡片接受了该文档 |
error | 最近一次同步失败;卡片保留最后一份有效文档 |
disabled | 计划已停止 |
ok 不判断上游数据是否正确或最新,也不判断是否有屏幕在显示该卡片。
| 错误代码 | 发生了什么 |
|---|---|
unreachable | screenRIG 无法访问该地址。请使用 80 或 443 端口上的公共 http(s) 地址 |
timeout | 该地址在 20 秒内没有响应 |
too_large | 响应大于 5 MB |
http_status | 该地址返回了 HTTP 错误。404 表示该地址已不再正确 |
auth_failed | 该地址拒绝了凭据;下次尝试将等待一小时 |
not_shared | Google 表格没有共享:Google 返回了登录页、401 或 403。请共享给“知道链接的任何人”,或发布到网络 |
parse_failed | 响应不是日历。登录页面会被当作网页而不是日历:请使用该日历的 ICS 地址 |
rate_limited | 该服务正在限制请求;下次同步将等待 |
mapping_failed | 映射无法在消息所指的位置读取响应,例如找不到数组的 rows_path |
card_data_invalid | 卡片在消息所指出的路径处拒绝了该文档 |
target_missing | 卡片已被删除。请将数据源指向另一张卡片 |
payment_required | 项目存储已满,因此文档没有写入 |
config_invalid | 某项设置或已保存的密钥已无法使用。请重新输入 |
internal | 同步在 screenRIG 内部失败,将会重试 |
创建或更改数据源时,可能返回 source_target_taken(该卡片已有数据源)、source_target_unsupported(该类型无法填充这张卡片;响应会列出可以填充的卡片)、source_secret_required(缺少密钥,或密钥是为另一个地址输入的,需要重新输入)、source_limit_reached 和 source_busy(同步正在运行;请在间隔后重试)。当地址无法读取时,source test 会返回 source_test_failed 并附上原因。
日历
日历数据源读取 iCalendar (ICS) 地址:Google 日历、Outlook、iCloud 和 Nextcloud 为日历提供的私密地址,以及任何 webcal:// 链接。它会用所有日历汇总的日程填充 events 卡片,或按每个日历一个房间填充 study-rooms 卡片。
| 设置 | 含义 |
|---|---|
calendars | 1 到 60 个日历,每个为 {key, name, floor, capacity}。键由小写字母、数字、- 和 _ 组成;名称、楼层和容量用于描述一个房间 |
days | 从今天起显示多少天,1 到 31(默认 7) |
time_zone | IANA 时区,例如 America/Vancouver,用于确定一天的边界,以及订阅源未标注时区的时间 |
private_title | 私密事件显示的标题(默认 Private) |
include, exclude | 按标题保留或隐藏事件的关键词:参见按关键词保留或隐藏事件 |
每个日历有一个密钥 calendar_url.KEY,其中保存它的地址。
- 重复事件(每日、每周、每月和每年的规则、例外、被移动和被取消的场次)以及时区都在服务器上展开,因此事件在夏令时变化后仍保持原来的时刻,卡片也不会重复日历自身的规则。Outlook 使用的 Windows 时区名称也能识别。
- 私密事件(
CLASS:PRIVATE或CONFIDENTIAL)只显示时间,使用私密标题,不显示组织者和地点。该规则只针对标记为私密的事件,不评判其他事件会透露什么。 - 空闲时间(标记为透明或空闲的事件)会保留在日程中,但会从只计算占用时间的会议室看板中去除。已取消的事件会被丢弃,暂定事件按占用计算。
- 全天事件显示为日期;没有结束时间的事件不显示结束时间。
- 警告。 当订阅源未指明时区、使用了 screenRIG 无法识别的时区,或某个事件的重复次数超过 500、事件总数超过 5,000 时,同步仍会成功,状态中会列出警告。
- 多个日历一起同步:只要有一个失败,整次同步就失败,错误信息会指出是哪个日历,因此看板不会显示不完整的内容。
按关键词保留或隐藏事件
ics、microsoft365、google_calendar 和 momentus 数据源有两个可选设置 include 和 exclude,各是 1 到 20 个关键词的列表,每个关键词 1 到 100 个字符。当事件标题至少包含一个 include 关键词(设置了 include 时),且不包含任何 exclude 关键词时,该事件会保留。关键词在标题内匹配,不区分大小写和重音符号,因此 café 和 CAFE 互相匹配。
- 仅限标题。 地点、组织者和说明从不参与匹配。没有标题的事件按
Untitled event匹配。 - 私密事件按其自身的标题匹配,显示的仍是私密标题。私密标题本身从不参与匹配,所以
exclude: ["private"]不会移除任何内容。 - 数据源写入的所有内容都读取过滤后的日历:卡片以及每个按键存储的会议室文档。过滤只移除事件,绝不移除日历或会议室,空闲事件与其他事件同样处理。
- 状态会统计被排除的数量,记为
filtered,为 0 时也会显示。当时间窗口内有事件而关键词把它们全部排除时,同步仍会成功,并给出提示检查关键词的警告,卡片显示空列表。source test也会显示同样的内容。
screenrig source create --kind ics --card crd_EVENTS --name "Public events" \
--set 'calendars=[{"key":"main"}]' --set 'include=["open house","tour"]' --set 'exclude=["cancelled"]' \
--secret calendar_url.main=@calendar.url工作表、CSV 和 JSON
sheets、csv 或 json 数据源读取一张表或一个 JSON 文档,并通过映射生成卡片的数据,因此可以填充任何卡片:用价目表生成菜单,用指标表生成 KPI 看板,用积分榜表生成表格。映射是数据源设置中的 JSON 模板。映射生成的内容是否被接受由卡片自己的规则决定,source test 会输出文档并说明卡片是否接受,因此您可以不断修改映射直到被接受。这些数据源每 5 分钟同步一次,最短每分钟一次。
screenrig source test --kind sheets --card crd_MENU --settings @menu-sheet.json
screenrig source create --kind sheets --card crd_MENU --name "Cafe menu" --settings @menu-sheet.json
screenrig source create --kind json --card crd_KPI --name "Daily numbers" \
--settings @kpi-api.json --secret token=@api.token| 设置 | 类型 | 含义 |
|---|---|---|
url | 全部 | 工作表的链接,或 CSV 文件或 JSON 文档的地址:80 或 443 端口上的公共 http(s) 地址 |
mapping | 全部 | 生成卡片数据的模板。必填 |
key_template | 全部 | 包含 {screen.field.NAME} 占位符的可选键;映射返回由 {fields,document} 组成的 documents 数组 |
sheet | sheets | 标签页的名称,用于读取链接所指标签页以外的标签页 |
header_row | sheets, csv | true(默认)表示第一行是列名;false表示每一行都是数据,列名为 Column 1、Column 2 |
key_column | sheets, csv | 其单元格为各行命名的列,使映射可以按名称查找行 |
delimiter | csv | ,、; 或 tab。默认使用第一行中出现最多的那个 |
auth | csv, json | none(默认)、bearer、header、basic、oauth2 或 token_request:参见请求、登录和分页 |
header_name | csv, json | 当 auth 为 header 时,承载令牌的标头,例如 X-API-Key |
rows_path | json | 行数组在响应中的位置,例如 data.items |
key_field | json | 每一行中为其命名的成员。请与 rows_path 一起设置 |
共享 Google 表格
sheets 数据源通过 Google 的 CSV 导出读取工作表。它不需要登录,也不需要访问您的 Google 账号,但工作表必须允许任何拿到链接的人读取。
- 在 Google 表格中选择共享,将常规访问权限设为“知道链接的任何人”,角色设为“查看者”。若要改为发布,请选择文件、共享、发布到网络。
- 将浏览器地址栏中的链接复制到
url。指向某个标签页的链接会保留该标签页。若要按名称读取其他标签页,请设置sheet;已发布的链接不接受sheet。 - 用
source test预览。工作表的表头行会成为映射读取的列键。
未共享的工作表会返回登录页、401 或 403。此时状态为 not_shared,消息会指出需要更改的设置:共享、常规访问权限、知道链接的任何人、查看者,或者文件、共享、发布到网络。请让工作表的所有者共享它。not_shared 只判断这三种响应,不会将列与您的映射进行比较。
需要令牌的地址
公共地址上的 CSV 文件或 JSON 文档不需要令牌。对于受保护的地址,请将 auth 设为 bearer(screenRIG 发送 Authorization: Bearer TOKEN),或设为 header 并配合 header_name(发送 HEADER_NAME: TOKEN),并将令牌作为密钥 token 提供:
screenrig source create --kind csv --card crd_TABLE --name "Standings" \
--settings @standings.json --secret token=@api.token令牌以加密方式存储,只以提示形式返回:有 12 个或更多字符时为最后四个字符,否则为 …。它只会发送到地址自身的来源,即协议、主机和端口。重定向到其他站点时不会携带令牌,未重新输入令牌就把数据源指向另一个来源会被拒绝,并返回 source_secret_required。只更改路径或查询参数时会保留令牌。关于 basic、OAuth 2 和令牌请求,以及带正文、请求头或分页的请求,参见请求、登录和分页。header_name 不能是 Host、Cookie、Content-Length、Connection、Transfer-Encoding、Upgrade、Proxy- 标头,也不能是 screenRIG 自己设置的标头。
请求、登录和分页
csv 或 json 数据源会向其 url 发送 GET 请求。这些设置可以发送正文或额外的请求头、登录、读取多页,并把今天的日期放进请求。每个请求有 20 秒,整次运行有 60 秒。
| 设置 | 含义 |
|---|---|
method | GET(默认)或 POST。有 body 或 graphql 时会变成 POST |
body | JSON 对象或数组,或原样发送的文本。占位符填入后最多 16 KiB |
body_format | json(默认)或 form。表单正文是由文本、数字和布尔字段组成的对象,或类似 a=1&b={{today}} 的文本;值会进行 URL 编码 |
graphql | {query, variables},以 JSON POST 发送。有错误且没有数据的响应是 parse_failed;数据旁带有错误只是警告 |
headers | 最多 8 个值不是密钥的额外请求头。Authorization 会被拒绝,header_name 拒绝的任何请求头也一样:请使用 auth 或请求头密钥 |
time_zone | 日期占位符使用的 IANA 时区。未设置时按 UTC 读取,状态中会列出警告 |
pagination | 读取多页,详见下文 |
format | 仅限 json 类型:auto(默认)、json、csv 或 xml |
名为 header.NAME 的密钥会作为请求头 NAME 发送,因此有两个凭据请求头的 API 会使用 header.X-Client-Id 和 header.X-Api-Key。名为 query.NAME 的密钥会以 NAME=value 的形式添加到地址中。每种最多 8 个,两种都加密封存且不会显示,预览中查询密钥显示为 NAME=***。
| 身份验证 | 设置和密钥 |
|---|---|
none | 默认值 |
bearer | 密钥 token,以 Authorization: Bearer 发送 |
header | header_name 和密钥 token |
basic | username(不含冒号)和密钥 password,以 Authorization: Basic 发送 |
oauth2 | 客户端凭据:token_url、client_id、可选的 scope 和 audience、token_auth(body 为默认值,或 basic)以及密钥 client_secret。screenRIG 向 token_url 请求令牌,并以 Authorization: Bearer 发送 |
token_request | 返回令牌的登录:token_url、token_body(其字符串可把已存储的密钥读取为 {{secrets.NAME}} 的对象)、token_body_format(json 或 form)、token_path(令牌在响应中的位置,如 token.accessToken),以及可选的 token_expires_path、token_ttl(60 到 86,400 秒,默认 3,600 秒)和 header_name(原样携带令牌的请求头;不设置时为 Authorization: Bearer) |
screenRIG 只在内存中保留令牌,绝不写入存储,并在其过期前一分钟续期。地址以 401 拒绝令牌时,它会重新请求一次新令牌。令牌地址的拒绝(HTTP 400、401 或 403)是 auth_failed,令牌地址返回的任何内容都不会被引用。
{
"url": "https://api.example.com/v2/orders/summary",
"method": "POST",
"body": {"from": "{{today-7d}}", "to": "{{today}}"},
"time_zone": "America/Vancouver",
"auth": "oauth2",
"token_url": "https://login.example.com/oauth/token",
"client_id": "screen-signs",
"scope": "orders.read",
"mapping": {"body": "{{body.total}} orders this week"}
}screenrig source create --kind json --card crd_BOARD --name "Orders" \
--settings @orders.json --secret client_secret=@client.secret日期占位符可填入地址、正文和 headers:{{now}}(2026-11-01T17:30:00Z)、{{today}}(2026-11-01)和 {{unix}}(秒),都可带可选偏移量,如 {{now-90m}}、{{now+7d}} 或 {{today-1d}}(单位为 m、h 和 d;{{today}} 只接受天)。它们按 time_zone 读取,未设置则按 UTC。每次运行都会根据时钟填入,因此明天的运行请求的就是明天的日期。
分页。 pagination 是 {type, max_pages},其中 max_pages 为 1 到 20(默认 5)。json 数据源需设置 rows_path,使各页的行合并到同一个数组,csv 数据源则合并表的行。
| 类型 | 如何找到下一页 |
|---|---|
link_header | Link 头中的 rel="next" 地址 |
next_url | path,响应中存放下一个地址的位置(仅 json) |
cursor | param 和 path:游标以 param 放入地址,或用 "in": "body" 放入正文,游标缺失或为空时读取结束(仅 json) |
page | param、start(默认 1)以及带 size 的 size_param:页码放入地址,或用 "in": "body" 放入正文,读到没有行或少于 size 行的页面时结束 |
下一个地址必须停留在数据源自己的来源,因此密钥绝不会离开它。重复的页面,或超过 max_pages 的页数,会让读取带着警告结束,所有页面合计最多 5 MB。
XML 和 RSS。 以 < 开头的响应,或设置了 format xml 的响应,会按 XML 读取。body 保存根元素:属性为 @name,含子元素的元素文本为 #text,重复的元素是列表,名称保留其前缀(item["media:thumbnail"]["@url"])。RSS 或 Atom feed 还会得到 body.items,每个条目在有的情况下带有 title、link、published、summary(纯文本,最多 1,000 个字符)和 image。DOCTYPE 绝不会被跟随,也不会从中获取任何内容。
source test 会在 request 中显示发出的第一个请求:已填入的占位符、以 NAME=*** 表示的查询密钥、Accept 和 Content-Type 之外的请求头名称(不含值)、正文的前 1 KiB 以及读取的页数。
数据源读取什么
- CSV 与工作表。普通 CSV:字节顺序标记、带引号的字段、成对的引号、引号内的换行以及 CRLF 行尾都能读取。单元格会去掉首尾空白,没有内容的行会被丢弃。表头会变成小写、去掉重音的键,其他字符的每一段连续字符变成一个
_,因此Sold out变为sold_out;空表头变为column_3(即其位置),重复的变为_2、_3。不是 UTF-8 文本的文件(例如电子表格文件)会得到parse_failed:请将其另存为 UTF-8 的 CSV。 - JSON。数字保留原有的位数。设置
rows_path后,数据源会在那里查找行数组;找不到数组的路径会在rows_path处得到mapping_failed,空数组则是没有行的表。 - 限制。表最多读取 10,000 行和 200 列。超出部分同步仍会成功,状态会列出一条警告,说明省略了多少行或列。
- 如果该地址返回的是网页而不是数据,结果为
parse_failed。
映射
映射是一个 JSON 模板。包含 {{item.price}} 这类占位符的字符串是文本,恰好只含一个占位符的字符串会保留值的类型。内容为空的路径在文本中为空,作为单独的值则被省略,卡片随后会指出缺少什么。路径可以从哪里开始取决于类型:
| 类型 | 起点 | 包含 |
|---|---|---|
sheets, csv | rows | 每一行数据,按列键组成的对象 |
sheets, csv | columns | [{key, name}],按工作表顺序 |
sheets, csv | by_key | 有 key_column 时:按该列单元格去掉首尾空白的文本索引各行 |
json | body | 整个响应 |
json | by_key | 有 rows_path 和 key_field 时:按该成员去掉首尾空白的文本、数字或布尔值索引各行 |
请优先使用 by_key,而不是 rows[2] 这样的位置:通过键找到的行,在有人给表排序或在上方插入行后仍然能找到。带空格的键写作 by_key["Net sales"].value。如果两行共用一个键,第一行优先,状态会列出一条警告,统计重复的数量。任何警告都不会引用单元格内容。
单元格是文本,因此需要转换:{"$number": "{{item.price}}"} 可读取 1,234.50、$4.50、3,50 € 和 12%(用逗号作小数点时加上 "$decimal": ","),{"$bool": "{{item.sold_out}}"} 可读取 true、yes、y、1 和 x。$each 遍历列表,并提供 item 和 index;$group 配合 $by 按首次出现的顺序对行分组,并提供 group.key 和 group.items;$text 配合 $max 截断文本。模板会在保存数据源时检查。
用包含 Section、Item、Price 和 Sold out 列的表生成的菜单会按分区对行分组:
{
"url": "https://docs.google.com/spreadsheets/d/SHEET_ID/edit#gid=0",
"mapping": {
"sections": {
"$group": "{{rows}}", "$by": "{{item.section}}",
"$template": {
"name": "{{group.key}}",
"items": {
"$each": "{{group.items}}",
"$template": {
"name": "{{item.item}}",
"price": { "$number": "{{item.price}}" },
"soldOut": { "$bool": "{{item.sold_out}}" }
}
}
}
}
}
}用包含 Metric、Value、Unit 和 Change 列的表生成的 KPI 看板按名称查找每个指标,因此对表排序不会移动任何内容:
{
"url": "https://docs.google.com/spreadsheets/d/SHEET_ID/edit#gid=0",
"key_column": "Metric",
"mapping": {
"tiles": [
{ "label": "Revenue", "value": { "$number": "{{by_key.Revenue.value}}" },
"unit": "{{by_key.Revenue.unit}}", "delta": { "$number": "{{by_key.Revenue.change}}" } },
{ "label": "Orders", "value": { "$number": "{{by_key.Orders.value}}" },
"delta": { "$number": "{{by_key.Orders.change}}" } }
]
}
}表格卡片会列出每一行:
{
"mapping": {
"rows": {
"$each": "{{rows}}", "$limit": 50,
"$template": {
"team": "{{item.team}}",
"points": { "$number": "{{item.points}}" },
"played": { "$number": "{{item.played}}" }
}
}
}
}要查看数据源读到了什么,请用映射 {"columns": "{{columns}}", "rows": "{{rows}}"} 测试。卡片会拒绝该文档,但预览仍会输出它,包含每个键和每一行。
这些数据源上的 ok 表示地址已响应,且卡片接受了映射生成的文档。它并不表示工作表或响应包含映射所期望的行:消失的列会使其成员被省略,缺少必需成员的文档会被卡片拒绝。
计算值
映射还可以在服务器上计算真假值和数字,因此只测试相等的页面条件(如带 equals: true 的 visibility.data)也能跟随数字变化。卡片 webhook 和叠加层触发器的映射也有同样的辅助函数。
| 函数 | 结果 | 写法 |
|---|---|---|
$gt, $gte, $lt, $lte | 真或假,两个值都按数字读取 | {"$lt": ["{{body.stock}}", 5]} |
$between | 当值在两个边界之内(含边界)时为真 | {"$between": ["{{body.temp}}", 18, 24]} |
$eq, $ne | 数字按值比较(5、"5" 和 "5.00" 相等),其他内容按精确文本比较,区分大小写 | {"$eq": ["{{body.status}}", "open"]} |
$contains | 文本包含某段文本(忽略大小写),或列表包含某个值 | {"$contains": ["{{body.title}}", "closed"]} |
$in | 值是列表中的一项 | {"$in": ["{{body.tier}}", ["gold", "silver"]]} |
$and, $or, $not | 组合条件 | {"$and": [{"$gt": ["{{body.a}}", 1]}, {"$not": {"$eq": ["{{body.s}}", "x"]}}]} |
$sum, $avg, $min, $max | 来自列表的一个数字 | {"$sum": "{{rows}}", "$of": "{{item.amount}}"} |
$count | 有多少元素,或有多少元素通过 $where | {"$count": "{{body.queue}}", "$where": {"$gt": ["{{item.wait}}", 5]}} |
- 无法进行的比较为假,并给出指明位置而从不显示值的警告,因此缺失、为空或非数字的值绝不会让页面显示。
$bool是唯一把缺失值当作假的读取器。 - 列表。
$of从每个元素读取一个占位符,可使用item和index,$where保留满足条件的元素。为空或不是数字的元素会被跳过,并由一条警告统一计数。没有可相加的内容时,$sum和$count为 0,$avg、$min和$max会省略其成员。 - 数字。 使用逗号作小数点的值(
3,50、1.234,50)需要"$decimal": ","。$avg四舍五入到 2 位小数,"$round"可设置 0 到 6 位。$sum、$min和$max是精确值。
来自 JSON 响应的低库存标记。当页面的 visibility.data 用 path /low_stock 和 equals true 指向该文档时,该页面只在库存偏低时显示:
{
"sku": "{{body.sku}}",
"stock": {"$number": "{{body.stock}}"},
"low_stock": {"$lt": ["{{body.stock}}", 5]}
}来自表格的、低于各自补货水平的任意商品:
{"low_stock": {"$gt": [{"$count": "{{rows}}", "$where": {"$lt": ["{{item.stock}}", "{{item.reorder_at}}"]}}, 0]}}按键存储的文档
JSON、CSV 和 Sheets 源接受 key_template,例如 room-{screen.field.room}。将数据源映射为由 {"fields":{"room":"A101"},"document":{"body":"Room A101"}} 对象组成的 documents 数组。卡片 Webhook 使用 --key-template 生成相同的输出,也可以不经映射直接接收外层对象。
{
"key_template": "room-{screen.field.room}",
"mapping": {
"documents": {
"$each": "{{rows}}",
"$template": {
"fields": {"room": "{{item.room}}"},
"document": {"body": "{{item.title}}"}
}
}
}
}放置所属的 crd_ 实例并设置 data: "room-{screen.field.room}";每个屏幕只读取由自身 room 字段生成的键。所有输出都使用实例的卡片架构。同一数据源供不同类型的卡片使用时,请分别设置实例、映射和前缀。
使用 source test 预览。无效文档、缺失字段和重复键会在写入前导致整批被拒绝。存储故障可能留下部分已写入的文档:请使用相同的 Idempotency-Key 重发完全相同的 Webhook 正文,或刷新源。被省略的键保留原有数据和到期时间;Webhook 不设置到期时间。使用 --clear-key-template 清除 Webhook 的模板。
文件和表格的小技巧
- OneDrive 和 SharePoint。 共享链接会打开预览页面,数据源会把它当作网页读取(
parse_failed)。在 CSV 文件的链接后加上?download=1,如果链接已有?,则加&download=1,这样链接就会直接返回文件。链接必须无需登录即可使用。 - 按名称查找行。 把
key_column设为给每一行命名的列(按其表头,不区分大小写),并把行读取为by_key.NAME.column:对表格排序或插入行不会改变任何内容。与任何列都不匹配的名称会在key_column处产生mapping_failed,重复的键会保留第一行,并给出统计重复数的警告。
会议室
会议室数据源读取会议室日历,并填充 room 卡片(一间会议室的门牌)或 room-board 卡片(一栋楼的所有会议室)。它也可以填充 events 日程或 study-rooms 卡片,日历地址(ICS)同样可以填充这些卡片。
卡片根据所持有的预订,每秒在屏幕上算出会议室是空闲、占用还是接下来是什么,因此即使屏幕离线,门牌也始终正确。会议室数据源只读取:卡片只显示预订,不接受输入,所以不会从屏幕上预订、结束或签到。
| 设置 | 含义 |
|---|---|
rooms | 1 到 60 间会议室,每间包含一个 key(小写字母、数字、- 和 _)、会议室的地址(Microsoft 365 用 email,Google 用 calendar_id),以及可选的 name、floor 和 capacity。room 卡片只接受一间会议室 |
days | 从今天起显示多少天,1 到 31(默认 7) |
time_zone | IANA 时区,例如 America/Los_Angeles,用于确定每天的界限和全天事件。不设置时,每天从 UTC 午夜开始,状态会列出一条警告 |
private_title | 私密事件显示的标题(默认 Private) |
Microsoft 365 会议室邮箱
类型 microsoft365 通过您自己的 Microsoft Entra 应用读取 Exchange Online 会议室邮箱,因此应用、它的密钥以及它能读取的内容都由您掌控。另有两项设置:tenant,即目录 ID 或已验证的域名,例如 contoso.com(common、organizations 和 consumers 会被拒绝);client_id,即应用的应用程序(客户端)ID。密钥 client_secret 是应用的客户端密钥的值。
- 在 Microsoft Entra 管理中心注册一个不带重定向 URI 的单租户应用,添加客户端密钥(复制其值,而不是密钥 ID),添加 Microsoft Graph 应用程序权限
Calendars.Read,并授予管理员同意。 - 把应用限制在会议室邮箱范围内。在应用程序访问策略加以限制之前,
Calendars.Read可访问组织内的每个邮箱。把会议室放进启用邮件的安全组,然后在 Exchange Online PowerShell 中运行下面的前两条命令;对于组内的会议室,Test-ApplicationAccessPolicy会返回Granted。Exchange 的应用程序 RBAC 也能以同样方式限制应用。 - 除非另行设置,会议室邮箱会把会议主题替换为组织者的姓名。若要显示会议标题,请对每间会议室运行第三条命令。
- 先预览,再保存。
source test会登录并读取每间会议室,报告读取到的会议室和预订、每间会议室的数量;如果应用还拥有Place.Read.All,还会报告组织列出的会议室数量。
# Exchange Online PowerShell
New-ApplicationAccessPolicy -AppId CLIENT_ID -PolicyScopeGroupId rooms@example.com -AccessRight RestrictAccess -Description "screenRIG room signs"
Test-ApplicationAccessPolicy -AppId CLIENT_ID -Identity boardroom@example.com
Set-CalendarProcessing -Identity boardroom@example.com -DeleteSubject $false -AddOrganizerToSubject $false
# screenRIG (source create takes the same options, plus --name)
screenrig source test --kind microsoft365 --card crd_ROOM \
--set tenant=contoso.com --set client_id=CLIENT_ID --set time_zone=America/Los_Angeles \
--set 'rooms=[{"key":"boardroom","email":"boardroom@example.com","name":"Boardroom","floor":"Level 4","capacity":12}]' \
--secret client_secret=@secret.txt- 映射。私密和机密会议显示私密标题,不显示组织者。标记为空闲或在其他地点办公的会议,以及会议室已拒绝的会议,视为空闲时间;暂定会议计为占用;已取消的会议会被丢弃。全天事件是会议室所在时区的日期。
- 频率。会议室数据源每 2 分钟同步一次。登录约一小时有效,并会重复使用。
- 错误。会引用 Microsoft 返回的错误代码和文本,例如客户端密钥错误时的
AADSTS7000215;应用无法读取的会议室会使同步失败,并在错误中点名该会议室。 - 改用连接。 把
connection_id设为项目的 Microsoft 连接(两种模式均可),以代替tenant、client_id和client_secret。这样数据源不保存任何密钥,并按连接的方式登录。
Google Workspace 资源日历
类型 google_calendar 通过您自己的 Google 服务账号读取资源日历和会议室日历。密钥 service_account_key 是 Google 签发的原样密钥文件,其提示是账号的地址。可选设置 subject 指定在全网域授权下该账号所代表的用户。
- 在 Google Cloud 中启用 Google Calendar API,创建服务账号,并为其下载 JSON 密钥。
- 让账号可以访问这些会议室。要么把每个会议室日历共享给该账号的地址,并授予“查看所有活动详情”权限;要么在管理控制台中为该账号启用全网域授权,范围为
https://www.googleapis.com/auth/calendar.events.readonly,并把subject设为能看到这些会议室的用户。 - 会议室的
calendar_id是该资源在管理控制台“建筑物和资源”下的日历 ID,以@resource.calendar.google.com结尾。先用source test预览,再保存。
screenrig source test --kind google_calendar --card crd_ROOM \
--set time_zone=America/Los_Angeles \
--set 'rooms=[{"key":"boardroom","calendar_id":"CALENDAR_ID","name":"Boardroom"}]' \
--secret service_account_key=@key.json- 映射。私密和机密事件显示私密标题,不显示组织者。标记为透明的事件以及会议室已拒绝的事件视为空闲时间,已取消的事件会被丢弃。全天事件保留其日期。
- 错误。会引用 Google 返回的错误文本。账号看不到的日历会在错误中点名,并说明如何共享。
- 改用连接。 把
connection_id设为项目的 Google 连接,以代替service_account_key。使用 screenRIG 的应用时,数据源读取批准它的账号能看到的日历,因此该账号必须能看到数据源列出的每个会议室日历,并且不允许使用subject。
限制与隐私
room卡片保留最早的 200 条预订,room-board卡片每间会议室保留 60 条。超出时,状态会列出一条警告,说明有多少条被舍弃。- 卡片的文档只包含卡片会绘制的内容,因为每个显示卡片的屏幕都会保留其文档。
room卡片只有在showOrganizer开启时才会收到组织者。room-board卡片从不会收到组织者,并且除非showTitles开启,否则会收到私密标题以代替每个会议标题。对这两项设置的更改会在下一次同步时到达卡片。私密会议在两种情况下都使用私密标题。 - 这些类型的消息会引用 Microsoft 或 Google 返回的错误文本,绝不会包含密钥。
场馆的预订系统
类型 momentus 使用您的管理员在 Momentus 中创建的 API 用户(Main Menu,Api Users),读取 Momentus Enterprise(以前以 Ungerboeck 和 EBMS 名称销售的系统)的活动安排(房间的每次使用及其日期和时间)和空间。它可以填充活动目录的 events 卡片、大厅或走廊的 room-board 卡片,或单个房间的 room 卡片;为每个房间写入一份文档后,每个屏幕都显示自己房间的预订。活动前不久发生变更的房间会在刷新间隔(默认一分钟)内显示。
| 设置 | 含义 |
|---|---|
base_url | 您的 Momentus Web 服务器的 https 地址,例如 https://momentus.example.com/prod;API 从 {base_url}/api/v1/... 读取 |
organization | 读取数据所用的 Momentus 组织代码 |
api_user | API 用户的 ID |
time_zone | 场馆的 IANA 时区。Momentus 保存的是本地日期和时间,因此请务必设置;未设置时,时间按 UTC 读取,状态会列出警告 |
days | 从今天起读取多少天,1 到 31(默认 7) |
rooms | {key, space, name?, floor?, capacity?, area?} 的可选列表;space 是 Momentus 空间代码。不设置时,Momentus 列出的每个空间都是一个房间(键为小写的代码) |
space_filter, function_filter | 可选的 Momentus 搜索表达式,用于限定读取的空间或活动安排 |
exclude_status_codes | 要排除的活动安排状态代码,例如已取消的。状态代码由您的场馆自行定义,因此没有默认值 |
include, exclude | 按数据源显示的标题保留或隐藏功能的关键词,与日历相同 |
两个密文:api_key 是 API 用户的一个密钥 GUID,api_secret 是其密文 GUID。两者都加密保存,不会再次显示;如果数据源指向其他服务器、组织或 API 用户,需要重新输入。
screenrig source test --kind momentus --card crd_EVENTS \
--set base_url=https://momentus.example.com/prod --set organization=10 \
--set api_user=venue-api --set time_zone=America/Los_Angeles \
--set 'exclude_status_codes=["XX"]' \
--secret api_key=@key.guid --secret api_secret=@secret.guid
screenrig source create --kind momentus --card crd_EVENTS --name "Venue events" \
--settings @momentus.json --secret api_key=@key.guid --secret api_secret=@secret.guidsource test 会读取 Momentus,整理文档并对照卡片检查,然后显示读取到的房间和活动(functions、functions_other_spaces、events_KEY)。不会保存或写入任何内容。
- 活动安排如何转换。 活动安排的
Space决定房间,Description作为标题(title_field可指定其备用描述之一),房间名称作为地点。没有开始时间的活动安排是全天活动。跨多个日期且带时间的活动安排会在每个日期显示这些时间;结束早于开始的则跨夜进行。 - 私密标题。 除非开启
show_titles,否则room-board显示私密标题,与其他房间类型相同。 - 错误。 由于服务器是您自己的,Momentus 自身的错误文本不会被引用:
auth_failed表示 API 用户、密钥或密文有误,或无权读取活动安排和空间;config_invalid表示筛选条件不是有效的 Momentus 搜索;parse_failed表示base_url指向的是网页而不是 API。
每个房间一块标牌
设置 room_key_prefix(例如 momentus-room-)后,数据源还会按该前缀加房间键,为每个房间写入一份 room 文档。为每个房间的屏幕设置其房间,并放置一张 room 卡片,其 data 键为该前缀加屏幕自己的字段:
screenrig screen update scr_BALLROOM --field room=ballroom-a在此播放列表图元中使用内联目录类型 room:
{"id":"door","primitive":"card","card":"room","layout":"full","content_fit":"fill","params":{"room":"{screen.field.room}"},"data":"momentus-room-{screen.field.room}"}crd_ 图元使用实例自身的 params 和 major,请省略这些字段。可选的 data 用于选择文档键或每屏幕键模板。源的 --card crd_ID 仍指定其同步目标。对于页面,将 visibility.data.key 设为 momentus-room-{screen.field.room},并在其后添加一个无条件的备用页面。
后端会在每个屏幕的清单中填入该字段,因此一份播放列表即可让每个屏幕显示自己的房间;没有房间字段的屏幕在该卡片上不显示任何内容。area_key_prefix 对走廊屏幕同样适用:每个房间的 area 将其分组,该区域房间的 room-board 会按前缀加区域键写入(area_card 可以是 events)。screen_field 和 area_field 指定下方检查所读取的屏幕字段(默认分别为 room 和 area)。
检查映射
screenrig source rooms src_ID(以及仪表板中数据源页面的“房间映射”部分)会列出数据源提供但没有任何屏幕显示的房间,以及房间缺失或指定了 Momentus 中不存在的房间的屏幕:
screenrig source rooms src_VENUE当屏幕在该字段上有值,或屏幕自己的播放列表放置了读取该键的卡片时,它会出现在报告中。每个屏幕的状态为 mapped、missing(播放列表读取该字段而屏幕没有值)或 unknown(值没有指向任何房间;报告会指出可能想指的房间,例如仅大小写不同)。使用 screenrig screen update scr_ID --field room=ROOM_KEY 修正屏幕。报告读取数据源最后一次成功刷新时的房间,不会获取任何内容;列出 rooms 的数据源在首次刷新之前就已知道这些房间。
安全
能授予访问权限的地址就是凭据。screenRIG 会加密存储它,只回显协议和主机,并且仅在运行同步时使用。数据源只读取 80 或 443 端口上的公共 http(s) 地址,最多跟随三次重定向;无法解析到公共主机的地址、被拒绝的连接和握手失败看起来完全一样,因此无法利用数据源探测网络。任何消息都不会引用地址或收到的响应。数据源只读取:绝不会写入日历。