Documentação

Mantenha cartões atualizados a partir de um endereço externo

Uma fonte de dados é um endereço que o screenRIG lê em um horário. Ela grava o que encontra nos dados de um cartão, de modo que o cartão se mantém atualizado sem que ninguém republique e cada tela que mostra o cartão busca o documento de novo quando ele muda. Uma agenda do Google, do Outlook, do iCloud ou do Nextcloud vira uma pauta ou um quadro de salas, uma planilha ou uma API vira um cardápio ou um painel de indicadores, e uma predefinição transforma um canal do Slack, as avaliações do Google ou um mural de vagas em um cartão.

Como funciona uma fonte

  • Uma fonte, um cartão. Uma fonte indica o cartão que ela preenche. Um cartão tem no máximo uma fonte, e uma organização tem até 200 em todos os seus projetos.
  • Ela roda em um agendamento. Uma fonte de calendário sincroniza a cada 5 minutos, e cada fonte pode ser ajustada entre 1 minuto e 24 horas. Uma fonte roda segundos depois de ser criada ou alterada, e source refresh a sincroniza na hora.
  • Uma sincronização com falha nunca altera o cartão. O cartão mantém o último documento válido, e o status da fonte diz o que deu errado. As falhas são repetidas automaticamente, com espera maior após cada uma.
  • Só as mudanças são gravadas. Quando uma sincronização encontra o que o cartão já contém, nada é gravado e as telas não são acordadas.
  • As sincronizações são gratuitas. Os documentos contam no armazenamento de dados de cartões como qualquer outra gravação.
  • As credenciais ficam criptografadas. Um endereço que concede acesso é armazenado criptografado, devolvido apenas com o esquema e o host, e nunca aparece em um log, em um erro nem no registro de eventos do projeto.
  • Ela mantém os últimos dados bons pelo período que você escolher. Quando uma fonte não consegue atualizar, o card dela mantém os últimos dados bons pelo período de expiração da fonte (--stale-after, de 1 minuto a 7 dias; uma fonte de reservas de local os mantém por 30 minutos); depois os dados expiram em todas as telas, inclusive as offline, e as páginas que dependem deles recorrem às páginas seguintes. Sem período, os últimos dados bons ficam até a fonte atualizar de novo. Veja Quando uma fonte não consegue atualizar.

Comece com uma predefinição

Uma predefinição é uma fonte pronta para um serviço que as pessoas costumam colocar em uma tela. Escolha uma, preencha alguns campos, adicione a credencial que o serviço emite, e o screenRIG monta a fonte e mapeia a resposta para o cartão. As predefinições são fontes json, que todos os planos incluem.

PredefiniçãoCartãoO que ela precisa
slack-channelworkplace-posts, message-wallO ID do canal e o token de bot (xoxb-…) de um aplicativo interno do Slack no seu workspace: em api.slack.com/apps adicione os escopos channels:history e groups:history, instale o aplicativo e depois adicione-o ao canal
bonuslykudosUm token de acesso do Bonusly com recognition:read, que um administrador Global ou Tech cria em Company, Integrations, API & Tokens
facebook-pagesocial-wallO ID da página, o nome a exibir e um token de acesso da página com pages_read_engagement e pages_show_list do Explorador da API Graph
instagram-businesssocial-wallO ID da conta do Instagram, o nome a exibir e o token de acesso da página do Facebook vinculada, com instagram_basic, pages_show_list e pages_read_engagement
google-reviewsreviewsO ID do local e uma chave de API do Google Cloud com a Places API (New) ativada
google-travel-timestravel-timesUm endereço de partida, de um a oito destinos, de carro ou de transporte público, e uma chave de API do Google Cloud com a Routes API ativada
greenhouse-jobsnow-hiringO token do quadro de boards.greenhouse.io/TOKEN. Sem credencial: o quadro é público
lever-jobsnow-hiringO nome do site de jobs.lever.co/NAME e a região. Sem credencial
ashby-jobsnow-hiringO nome do quadro de jobs.ashbyhq.com/NAME. Sem credencial
github-actionsbuild-boardO proprietário, o repositório e o branch, e um token de acesso pessoal de escopo detalhado com acesso somente leitura às Actions desse repositório, de github.com/settings/personal-access-tokens

O dashboard, seu agente e a API listam as predefinições, com a linha de ajuda e o exemplo de cada campo e os passos para obter cada credencial: GET /api/source-presets devolve todas e GET /api/source-presets/NAME devolve uma (o grupo Fontes de dados da referência da API). Crie uma fonte a partir de uma predefinição com preset, settings e secrets no lugar de kind e config. Mantenha a credencial no arquivo da solicitação, não na linha de comando, e substitua BOT_TOKEN pelo 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.json

POST /api/sources/test mostra o documento que o cartão exibiria, se o cartão o aceita e o que foi notado, e não grava nada. POST /api/sources salva o mesmo arquivo, e PATCH /api/sources/ID com novos settings monta a fonte de novo a partir da predefinição.

  • Sua conta, seus termos. Uma predefinição lê a sua própria conta com uma credencial que você cria, sob os termos do serviço, e o cartão mostra o que esses termos pedem: as avaliações do Google aparecem com a marca do Google e o nome de cada avaliador.
  • Horário e expiração. Cada predefinição define seu próprio intervalo e seu próprio período de expiração, como um minuto e 15 minutos para o Slack, ou 6 horas e 24 horas para as avaliações do Google. Uma fonte pode escolher outro intervalo, nunca um mais curto que o mínimo da sua predefinição. Quando uma sincronização falha, o cartão mantém seus últimos dados até o período de expiração passar e depois fica em branco.
  • Cobrança no serviço. Cada atualização das avaliações do Google é uma solicitação Place Details no seu projeto do Google Cloud, e cada atualização dos tempos de viagem do Google é um elemento de rota para cada destino: quatro destinos a cada 15 minutos dão cerca de 11.500 elementos por mês.
  • O Slack exige um aplicativo interno. Não o distribua: o Slack limita a uma solicitação por minuto um aplicativo distribuído fora do seu Marketplace. O aplicativo lê apenas os canais aos quais foi adicionado, e o cartão mostra as mensagens próprias das pessoas, não entradas, publicações de bots nem avisos do canal.

Criar uma fonte

Crie primeiro o cartão, depois faça uma prévia e então salve.

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_SOURCE

source test busca o endereço, ajusta-o para o cartão e confere o resultado com o cartão, sem salvar nem gravar nada. Mostra o documento, se o cartão o aceita e o que notou no caminho. Informe o endereço em um arquivo (@FILE) ou pela entrada padrão (-) para que ele não fique no histórico do seu shell.

ComandoO que faz
source list, source show IDAs fontes do projeto, com status, configurações e segredos mascarados
source createSalva uma fonte e a executa em poucos segundos
source update IDAltera o nome, o cartão, as configurações (--set KEY=VALUE ou --settings JSON), os segredos (--secret, --unset-secret), o agendamento (--refresh 10m) ou --enable e --disable
source testFaz uma prévia de uma fonte, salva ou não, sem gravar nada
source refresh IDSincroniza na hora e mostra o resultado
source delete IDRemove a fonte; o cartão mantém o último documento

--settings recebe toda a configuração da fonte como JSON, @FILE ou -. A opção --config da CLI indica o arquivo de credenciais dela e não tem relação com as fontes.

Quando uma fonte não consegue atualizar

Uma fonte grava cada documento com um prazo: o período de expiração depois da gravação, renovado enquanto a fonte continua atualizando. Enquanto a fonte funciona, o prazo vai se movendo. Quando ela falha, o card continua mostrando os últimos dados bons até o prazo, e então os dados expiram em todas as telas, inclusive as offline.

  • Defina o período com --stale-after 30m em source create ou source update, de 1 minuto a 7 dias e pelo menos o dobro do intervalo de atualização. --stale-after none mantém os últimos dados bons até a próxima atualização. Uma fonte de reservas de local (momentus) os mantém por 30 minutos, salvo indicação em contrário; os outros tipos os mantêm até a próxima atualização.
  • Recorrer ao seu próprio conteúdo. Uma página cuja condição visibility.data nomeia o documento (para uma sala, momentus-room-{screen.field.room}) aparece enquanto os dados existem e cede lugar à página seguinte quando eles expiram. Deixe depois dela uma página comum, com a sua marca, como reserva.
  • Ler. source show mostra stale_after, data_expires_at e, quando os dados expiraram enquanto a fonte continua falhando, stale: true. A página Fontes de dados do painel mostra o mesmo: uma fonte com falha aparece como Falhando, e uma cujos dados expiraram como Desatualizada.
  • Ser avisado. Uma notificação nas condições source_failed (uma atualização falhou) e source_stale (os dados expiraram) envia e-mail ou publica no Slack, Teams, PagerDuty ou Telegram, como para uma tela: screenrig notifications create --email owner --conditions source_failed,source_stale --hold 5. Um aviso nomeia a fonte e liga à página dela, some quando uma atualização funciona, e --tag nunca corresponde a uma fonte.

Status e erros

Cada fonte mostra o status dela: quando rodou pela última vez, teve sucesso pela última vez e alterou o cartão pela última vez, quando rodará de novo, quantas sincronizações seguidas falharam e o último erro.

EstadoSignificado
pendingA fonte ainda não foi executada
okA última busca foi bem-sucedida e o cartão aceitou o documento
errorA última sincronização falhou; o cartão mantém o último documento válido
disabledO agendamento está parado

ok não avalia se os dados de origem estão corretos ou atualizados, nem se uma tela exibe o cartão.

Código de erroO que aconteceu
unreachableO screenRIG não conseguiu acessar o endereço. Use um endereço http(s) público na porta 80 ou 443
timeoutO endereço não respondeu em 20 segundos
too_largeA resposta é maior que 5 MB
http_statusO endereço respondeu com um erro HTTP. Um 404 significa que o endereço não está mais correto
auth_failedO endereço recusou as credenciais; a próxima tentativa espera uma hora
not_sharedUma planilha do Google não está compartilhada: o Google respondeu com uma página de login, um 401 ou um 403. Compartilhe-a com Qualquer pessoa com o link, ou publique-a na web
parse_failedA resposta não é um calendário. Uma página de login é lida como página da web, não como calendário: use o endereço ICS do calendário
rate_limitedO serviço está limitando as solicitações; a próxima sincronização espera
mapping_failedO mapeamento não conseguiu ler a resposta no lugar que a mensagem nomeia, como um rows_path que não encontra matriz
card_data_invalidO cartão recusou o documento no caminho indicado pela mensagem
target_missingO cartão foi excluído. Aponte a fonte para outro cartão
payment_requiredO armazenamento do projeto está cheio, então o documento não foi gravado
config_invalidUma configuração ou um segredo armazenado não pode mais ser usado. Informe-o novamente
internalA sincronização falhou dentro do screenRIG e será repetida

Criar ou alterar uma fonte pode retornar source_target_taken (o cartão já tem uma fonte), source_target_unsupported (o tipo não pode preencher esse cartão; a resposta lista os cartões possíveis), source_secret_required (falta um segredo, ou ele foi informado para outro endereço e precisa ser informado de novo), source_limit_reached e source_busy (uma sincronização está em andamento; tente novamente após o intervalo). source test retorna source_test_failed com o motivo quando o endereço não pode ser lido.

Calendários

Uma fonte de calendário lê endereços iCalendar (ICS): o endereço secreto que Google Agenda, Outlook, iCloud e Nextcloud fornecem para um calendário, e qualquer link webcal://. Ela preenche um cartão events com uma agenda de todos os calendários, ou um cartão study-rooms com uma sala por calendário.

ConfiguraçãoSignificado
calendarsDe 1 a 60 calendários, cada um com {key, name, floor, capacity}. A chave aceita letras minúsculas, dígitos, - e _; nome, andar e capacidade descrevem uma sala
daysQuantos dias exibir a partir de hoje, de 1 a 31 (padrão 7)
time_zoneUm fuso horário IANA como America/Vancouver, usado para o limite do dia e para horários que um feed deixa sem fuso
private_titleO título exibido para eventos privados (padrão Private)
include, excludePalavras-chave que mantêm ou ocultam eventos pelo título: veja Manter ou ocultar eventos por palavra-chave

Cada calendário tem um segredo, calendar_url.KEY, que guarda o endereço dele.

  • Eventos recorrentes (regras diárias, semanais, mensais e anuais, exceções, ocorrências movidas e canceladas) e fusos horários são expandidos no servidor, de modo que um evento mantém o horário no horário de verão e um cartão nunca repete as regras do calendário. Os nomes de fuso horário do Windows usados pelo Outlook são reconhecidos.
  • Eventos privados (CLASS:PRIVATE ou CONFIDENTIAL) mostram apenas os horários, com o título privado e sem organizador nem local. A regra cobre os marcados como privados e não avalia o que os demais eventos revelam.
  • Tempo livre (eventos marcados como transparentes ou livres) é mantido em uma agenda e omitido em um painel de salas, onde só conta o tempo ocupado. Eventos cancelados são descartados e os provisórios contam como ocupados.
  • Eventos de dia inteiro aparecem como datas; um evento sem fim não mostra fim.
  • Avisos. Quando um feed não informa fuso horário, usa um que o screenRIG não reconhece, ou contém mais de 500 ocorrências de um evento ou mais de 5.000 eventos, a sincronização é concluída e o status mostra um aviso.
  • Vários calendários são sincronizados juntos: se um falha, a sincronização inteira falha e o erro nomeia o calendário, de modo que um painel nunca mostra um conjunto parcial.

Manter ou ocultar eventos por palavra-chave

As fontes ics, microsoft365, google_calendar e momentus aceitam duas configurações opcionais, include e exclude, cada uma uma lista de 1 a 20 palavras-chave de 1 a 100 caracteres. Um evento permanece quando o título contém pelo menos uma palavra-chave de include (quando include está definido) e nenhuma das de exclude. Uma palavra-chave corresponde dentro de um título sem diferenciar maiúsculas de minúsculas nem acentos, então café e CAFE correspondem entre si.

  • Somente o título. O local, o organizador e a descrição nunca são comparados. Um evento sem título é comparado como Untitled event.
  • Eventos privados são comparados pelo próprio título e continuam mostrando o título privado. O título privado em si nunca é comparado, então exclude: ["private"] não remove nada.
  • Tudo o que a fonte grava lê as agendas filtradas: o cartão e cada documento de sala por chave. Um filtro remove apenas eventos, nunca uma agenda ou uma sala, e eventos livres contam como os demais.
  • O status conta o que ficou de fora como filtered, inclusive 0. Quando a janela tem eventos e as palavras-chave não deixam nenhum, a sincronização é bem-sucedida com um aviso para conferi-las, e o cartão mostra uma lista vazia. source test mostra o mesmo.
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

Planilhas, CSV e JSON

Uma fonte sheets, csv ou json lê uma tabela ou um documento JSON e monta com ele os dados de um cartão por meio de um mapeamento, de modo que preenche qualquer cartão: um cardápio a partir de uma lista de preços, um painel de indicadores a partir de uma planilha de métricas, uma tabela a partir de uma planilha de classificação. O mapeamento é um modelo JSON nas configurações da fonte. As regras do próprio cartão decidem se o que o mapeamento monta é aceito, e source test mostra o documento e se o cartão o aceita, então você edita o mapeamento até que aceite. Essas fontes sincronizam a cada 5 minutos, e a cada minuto no mínimo.

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
ConfiguraçãoTiposSignificado
urltodosO link da planilha, ou o endereço do arquivo CSV ou do documento JSON: um endereço http(s) público na porta 80 ou 443
mappingtodosO modelo que monta os dados do cartão. Obrigatório
key_templatetodosChave opcional com marcadores {screen.field.NAME}; a transformação retorna um array documents de {fields,document}
sheetsheetsO nome de uma guia, para ler uma guia diferente da que o link nomeia
header_rowsheets, csvtrue (padrão) quando a primeira linha contém os nomes das colunas; false quando todas as linhas são dados, chamadas Column 1, Column 2
key_columnsheets, csvA coluna cujas células nomeiam as linhas, para que o mapeamento possa buscar uma linha pelo nome
delimitercsv,, ; ou tab. Por padrão, o que a primeira linha mais usa
authcsv, jsonnone (padrão), bearer, header, basic, oauth2 ou token_request: veja Solicitações, login e páginas
header_namecsv, jsonCom auth header, o cabeçalho que leva o token, como X-API-Key
rows_pathjsonOnde fica a matriz de linhas na resposta, como data.items
key_fieldjsonO membro de cada linha que a nomeia. Defina-o junto com rows_path

Compartilhar uma planilha do Google

Uma fonte sheets lê a planilha pela exportação CSV do Google. Ela não precisa de login nem de acesso à sua conta do Google, e a planilha precisa poder ser lida por qualquer pessoa que tenha o link.

  1. No Google Planilhas, escolha Compartilhar, defina o Acesso geral como Qualquer pessoa com o link e a função como Leitor. Para publicar em vez disso, escolha Arquivo, Compartilhar, Publicar na Web.
  2. Copie o link da barra de endereços do navegador para url. Um link para uma guia mantém essa guia. Para ler outra guia pelo nome, defina sheet; um link publicado não aceita sheet.
  3. Faça uma prévia com source test. A linha de cabeçalho da planilha vira as chaves de coluna que o mapeamento lê.

Uma planilha que não está compartilhada responde com uma página de login, um 401 ou um 403. O status é então not_shared, e a mensagem nomeia a configuração a mudar: Compartilhar, Acesso geral, Qualquer pessoa com o link, Leitor, ou Arquivo, Compartilhar, Publicar na Web. Peça ao proprietário da planilha que a compartilhe. not_shared julga somente essas três respostas. Ele não compara as colunas com o seu mapeamento.

Endereços com token

Um arquivo CSV ou um documento JSON em um endereço público não precisa de token. Para um protegido, defina auth como bearer (o screenRIG envia Authorization: Bearer TOKEN) ou como header com header_name (ele envia HEADER_NAME: TOKEN), e informe o token como o segredo token:

screenrig source create --kind csv --card crd_TABLE --name "Standings" \
  --settings @standings.json --secret token=@api.token

O token é armazenado criptografado e mostrado de volta apenas como uma dica: seus últimos quatro caracteres quando tem 12 ou mais, caso contrário …. Ele é enviado somente à origem do próprio endereço: o esquema, o host e a porta. Um redirecionamento para outro site não é seguido com o token, e apontar a fonte para outra origem sem informar o token de novo é recusado com source_secret_required. Mudar apenas o caminho ou a consulta mantém o token. Para basic, OAuth 2 e solicitações de token, e para uma solicitação com corpo, cabeçalhos ou páginas, veja Solicitações, login e páginas. header_name não pode ser Host, Cookie, Content-Length, Connection, Transfer-Encoding, Upgrade nem um cabeçalho Proxy-, nem um cabeçalho que o próprio screenRIG define.

Solicitações, login e páginas

Uma fonte csv ou json envia um GET para a sua url. Estas configurações enviam um corpo ou cabeçalhos extras, fazem login, leem mais de uma página e colocam a data de hoje em uma solicitação. Cada solicitação tem 20 segundos e uma execução inteira tem 60.

ConfiguraçãoSignificado
methodGET (padrão) ou POST. Um body ou graphql o transforma em POST
bodyUm objeto ou array JSON, ou texto enviado como está escrito. No máximo 16 KiB depois de preenchidos os marcadores
body_formatjson (padrão) ou form. Um corpo de formulário é um objeto de campos de texto, número e booleanos, ou um texto como a=1&b={{today}}; os valores são codificados para URL
graphql{query, variables}, enviado como um POST JSON. Uma resposta com erros e sem dados é parse_failed; erros junto com dados são um aviso
headersAté 8 cabeçalhos extras cujos valores não são segredos. Authorization é recusado, e também qualquer cabeçalho que header_name recuse: use auth ou um segredo de cabeçalho
time_zoneUm fuso IANA para os marcadores de data. Sem ele, são lidos em UTC e o status lista um aviso
paginationLê mais de uma página, como descrito abaixo
formatSomente o tipo json: auto (padrão), json, csv ou xml

Um segredo chamado header.NAME é enviado como o cabeçalho NAME, então uma API com dois cabeçalhos de credencial usa header.X-Client-Id e header.X-Api-Key. Um segredo chamado query.NAME é adicionado ao endereço como NAME=value. Cada tipo aceita até 8, ambos são armazenados selados e nunca mostrados, e a visualização mostra um segredo de consulta como NAME=***.

AutenticaçãoConfigurações e segredos
noneO padrão
bearerO segredo token, enviado como Authorization: Bearer
headerheader_name e o segredo token
basicusername (sem dois-pontos) e o segredo password, enviados como Authorization: Basic
oauth2Credenciais de cliente: token_url, client_id, scope e audience opcionais, token_auth (body, o padrão, ou basic) e o segredo client_secret. O screenRIG pede um token a token_url e o envia como Authorization: Bearer
token_requestUm login que devolve um token: token_url, token_body (um objeto cujas strings podem ler segredos armazenados como {{secrets.NAME}}), token_body_format (json ou form), token_path (onde o token está na resposta, como token.accessToken) e, opcionalmente, token_expires_path, token_ttl (de 60 a 86.400 segundos, 3.600 por padrão) e header_name (o cabeçalho que leva o token como está; sem ele, Authorization: Bearer)

O screenRIG mantém um token na memória, nunca no armazenamento, e o renova um minuto antes de expirar. Quando o endereço recusa um token com 401, ele pede um novo uma vez. Uma recusa no endereço do token (HTTP 400, 401 ou 403) é auth_failed, e nada do que o endereço do token diz é citado.

{
  "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

Os marcadores de data preenchem o endereço, o corpo e headers: {{now}} (2026-11-01T17:30:00Z), {{today}} (2026-11-01) e {{unix}} (segundos), cada um com um deslocamento opcional como {{now-90m}}, {{now+7d}} ou {{today-1d}} (unidades m, h e d; {{today}} aceita só dias). Eles são lidos em time_zone, ou em UTC. Cada execução os preenche a partir do relógio, então a execução de amanhã pede a data de amanhã.

Páginas. pagination é {type, max_pages}, com max_pages de 1 a 20 (5 por padrão). Uma fonte json define rows_path para que as linhas das páginas se juntem em um único array, e uma fonte csv junta as linhas da tabela.

TipoComo a próxima página é encontrada
link_headerO endereço rel="next" no cabeçalho Link
next_urlpath, o lugar da resposta que contém o próximo endereço (somente json)
cursorparam e path: o cursor vai no endereço como param, ou no corpo com "in": "body", e a leitura termina quando o cursor está ausente ou vazio (somente json)
pageparam, start (1 por padrão) e size_param com size: o número da página vai no endereço ou, com "in": "body", no corpo, e a leitura termina em uma página sem linhas ou com menos de size

O próximo endereço deve ficar na origem da própria fonte, para que um segredo nunca saia dela. Uma página repetida, ou mais páginas que max_pages, encerra a leitura com um aviso, e todas as páginas juntas somam no máximo 5 MB.

XML e RSS. Uma resposta que começa com <, ou format xml, é lida como XML. body contém o elemento raiz: atributos são @name, o texto de um elemento que tem filhos é #text, elementos repetidos são listas e os nomes mantêm o prefixo (item["media:thumbnail"]["@url"]). Um feed RSS ou Atom também recebe body.items, cada entrada com title, link, published, summary (texto simples, até 1.000 caracteres) e image, quando a entrada os tem. Um DOCTYPE nunca é seguido e nada é buscado dele.

source test mostra em request a primeira solicitação como foi enviada: os marcadores preenchidos, os segredos de consulta como NAME=***, os nomes dos cabeçalhos além de Accept e Content-Type sem os valores, o primeiro KiB do corpo e o número de páginas lidas.

O que uma fonte lê

  • CSV e planilhas. CSV comum: marca de ordem de bytes, campos entre aspas, aspas duplicadas, quebras de linha dentro de aspas e fins de linha CRLF são todos lidos. As células são aparadas e as linhas sem conteúdo são descartadas. Um cabeçalho vira uma chave em minúsculas, sem acentos, com cada sequência de outros caracteres virando um único _, de modo que Sold out vira sold_out; um cabeçalho vazio vira column_3 (a sua posição) e uma repetição vira _2, _3. Um arquivo que não é texto UTF-8, como um arquivo de planilha, é parse_failed: salve-o como CSV em UTF-8.
  • JSON. Os números mantêm seus dígitos. Com rows_path, a fonte procura ali a matriz de linhas; um caminho que não encontra matriz é mapping_failed em rows_path, e uma matriz vazia é uma tabela sem linhas.
  • Limites. Uma tabela é lida até 10.000 linhas e 200 colunas. Além disso, a sincronização tem êxito e o status lista um aviso que conta as linhas ou colunas deixadas de fora.
  • Uma página da web no endereço, em vez dos dados, é parse_failed.

O mapeamento

O mapeamento é um modelo JSON. Uma string com marcadores como {{item.price}} é texto, e uma string que é exatamente um marcador mantém o tipo do valor. Um caminho que não contém nada fica vazio em um texto e omite um valor isolado, de modo que o cartão passa a nomear o que lhe falta. De onde um caminho pode partir depende do tipo:

TipoPonto de partidaContém
sheets, csvrowsCada linha de dados, um objeto por chave de coluna
sheets, csvcolumns[{key, name}] na ordem da planilha
sheets, csvby_keyCom key_column: as linhas pelo texto aparado da célula dessa coluna
jsonbodyA resposta inteira
jsonby_keyCom rows_path e key_field: as linhas pelo texto, número ou booleano aparado desse membro

Prefira by_key a uma posição como rows[2]: uma linha encontrada pela chave continua sendo encontrada quando alguém ordena a planilha ou insere uma linha acima dela. Escreva uma chave com espaços como by_key["Net sales"].value. Se duas linhas compartilham uma chave, a primeira vence e o status lista um aviso que conta as repetições. Nenhum aviso cita uma célula.

As células são texto, então converta-as: {"$number": "{{item.price}}"} lê 1,234.50, $4.50, 3,50 € e 12% (adicione "$decimal": "," para vírgula decimal), e {"$bool": "{{item.sold_out}}"} lê true, yes, y, 1 e x. $each percorre uma lista com item e index no escopo, $group com $by agrupa as linhas na ordem da primeira aparição com group.key e group.items no escopo, e $text com $max corta o texto. O modelo é verificado quando a fonte é salva.

Um cardápio a partir de uma planilha com as colunas Section, Item, Price e Sold out agrupa as linhas por seção:

{
  "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}}" }
          }
        }
      }
    }
  }
}

Um painel de indicadores a partir de uma planilha com as colunas Metric, Value, Unit e Change procura cada métrica pelo nome, então ordenar a planilha não move nada:

{
  "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}}" } }
    ]
  }
}

Um cartão de tabela lista todas as linhas:

{
  "mapping": {
    "rows": {
      "$each": "{{rows}}", "$limit": 50,
      "$template": {
        "team": "{{item.team}}",
        "points": { "$number": "{{item.points}}" },
        "played": { "$number": "{{item.played}}" }
      }
    }
  }
}

Para ver o que uma fonte lê, teste com o mapeamento {"columns": "{{columns}}", "rows": "{{rows}}"}. O cartão recusa esse documento, e a prévia o mostra mesmo assim, com cada chave e cada linha.

ok em uma dessas fontes significa que o endereço respondeu e o cartão aceitou o documento gerado pelo mapeamento. Isso não diz que a planilha ou a resposta contém as linhas que o seu mapeamento espera: uma coluna que desaparece deixa de fora o seu membro, e o cartão recusa um documento ao qual falta um membro obrigatório.

Valores calculados

Um mapeamento também pode calcular verdadeiro ou falso e números no servidor, então uma condição de página que só testa igualdade, como visibility.data com equals: true, pode acompanhar um número. Os mapeamentos de webhooks de cartão e de gatilhos de sobreposição têm as mesmas funções.

FunçãoRetornaEscrita
$gt, $gte, $lt, $lteVerdadeiro ou falso, lendo os dois valores como números{"$lt": ["{{body.stock}}", 5]}
$betweenVerdadeiro quando o valor está dentro dos dois limites, inclusive{"$between": ["{{body.temp}}", 18, 24]}
$eq, $neNúmeros por valor (5, "5" e "5.00" correspondem), qualquer outra coisa como texto exato, com maiúsculas e minúsculas{"$eq": ["{{body.status}}", "open"]}
$containsUm texto contém um texto, ignorando maiúsculas e minúsculas, ou uma lista contém um valor{"$contains": ["{{body.title}}", "closed"]}
$inO valor é um de uma lista{"$in": ["{{body.tier}}", ["gold", "silver"]]}
$and, $or, $notCombinam condições{"$and": [{"$gt": ["{{body.a}}", 1]}, {"$not": {"$eq": ["{{body.s}}", "x"]}}]}
$sum, $avg, $min, $maxUm número a partir de uma lista{"$sum": "{{rows}}", "$of": "{{item.amount}}"}
$countQuantos elementos há, ou quantos passam em $where{"$count": "{{body.queue}}", "$where": {"$gt": ["{{item.wait}}", 5]}}
  • Uma comparação que não pode ser feita é falsa, com um aviso que nomeia o lugar e nunca o valor, então um valor ausente, vazio ou não numérico nunca ativa uma página. $bool é o único leitor que trata um valor ausente como falso.
  • Listas. $of lê um marcador de cada elemento, com item e index disponíveis, e $where mantém os elementos para os quais uma condição vale. Um elemento vazio ou que não é um número é ignorado, e um aviso os conta. Sem nada para somar, $sum e $count valem 0, e $avg, $min e $max omitem o seu membro.
  • Números. Uma vírgula decimal (3,50, 1.234,50) precisa de "$decimal": ",". $avg arredonda para 2 casas, e "$round" define de 0 a 6. $sum, $min e $max são exatos.

Um sinalizador de estoque baixo a partir de uma resposta JSON. Uma página cujo visibility.data nomeia o documento com path /low_stock e equals true só aparece enquanto o estoque está baixo:

{
  "sku": "{{body.sku}}",
  "stock": {"$number": "{{body.stock}}"},
  "low_stock": {"$lt": ["{{body.stock}}", 5]}
}

A partir de uma planilha, qualquer produto abaixo do seu próprio nível de reposição:

{"low_stock": {"$gt": [{"$count": "{{rows}}", "$where": {"$lt": ["{{item.stock}}", "{{item.reorder_at}}"]}}, 0]}}

Documentos por chave

As fontes JSON, CSV e Sheets aceitam key_template, por exemplo room-{screen.field.room}. Transforme o feed em um array documents de objetos {"fields":{"room":"A101"},"document":{"body":"Room A101"}}. Um webhook de cartão usa --key-template com a mesma saída, ou recebe o objeto envolvente diretamente sem transformação.

{
  "key_template": "room-{screen.field.room}",
  "mapping": {
    "documents": {
      "$each": "{{rows}}",
      "$template": {
        "fields": {"room": "{{item.room}}"},
        "document": {"body": "{{item.title}}"}
      }
    }
  }
}

Posicione a instância crd_ correspondente com data: "room-{screen.field.room}"; cada tela lê apenas a chave formada a partir de seu próprio campo room. Todas as saídas usam o esquema de cartão da instância. Use instâncias, transformações e prefixos separados para diferentes tipos de cartão que leem o mesmo feed.

Veja a prévia com source test. Documentos inválidos, campos ausentes e chaves duplicadas fazem o lote ser recusado antes da gravação. Uma falha de armazenamento pode deixar uma parte gravada: reenvie o mesmo corpo do webhook com o mesmo Idempotency-Key, ou atualize a fonte. As chaves omitidas mantêm os dados e a validade anteriores; os webhooks não definem validade. Limpe o modelo de um webhook com --clear-key-template.

Dicas para arquivos e planilhas

  • OneDrive e SharePoint. Um link de compartilhamento abre uma página de visualização, que uma fonte lê como uma página da web (parse_failed). Adicione ?download=1 ao link de um arquivo CSV, ou &download=1 quando ele já tem um ?, para que o link devolva o próprio arquivo. O link precisa funcionar sem login.
  • Procure linhas pelo nome. Defina key_column como a coluna que dá nome a cada linha, pelo cabeçalho em qualquer caixa, e leia uma linha como by_key.NAME.column: ordenar a planilha ou inserir uma linha não muda nada. Um nome que não corresponde a nenhuma coluna dá mapping_failed em key_column, e uma chave repetida mantém a primeira linha com um aviso que conta as repetições.

Salas de reunião

Uma fonte de salas lê agendas de salas de reunião e preenche um cartão room, a placa de porta de uma sala, ou um cartão room-board, todas as salas de um prédio. Ela também pode preencher uma agenda events ou um cartão study-rooms, e um endereço de agenda (ICS) preenche os mesmos cartões.

Os cartões calculam na tela, a cada segundo, se uma sala está livre, em uso ou o que vem a seguir, a partir das reservas que guardam: uma placa continua correta mesmo com a tela offline. Uma fonte de salas só lê: um cartão mostra reservas e não aceita entrada, então nada é reservado, encerrado ou registrado a partir de uma tela.

ConfiguraçãoSignificado
roomsDe 1 a 60 salas, cada uma com uma key (letras minúsculas, dígitos, - e _), o endereço da sala (email no Microsoft 365, calendar_id no Google) e, opcionalmente, name, floor e capacity. Um cartão room aceita exatamente uma sala
daysQuantos dias exibir a partir de hoje, de 1 a 31 (padrão 7)
time_zoneUm fuso IANA como America/Los_Angeles, usado para o limite do dia e para eventos de dia inteiro. Sem ele, o dia começa à meia-noite UTC e o status mostra um aviso
private_titleO título exibido para eventos privados (padrão Private)

Caixas de correio de sala do Microsoft 365

O tipo microsoft365 lê caixas de correio de sala do Exchange Online com seu próprio aplicativo do Microsoft Entra, de modo que o aplicativo, o segredo dele e o que ele pode ler continuam sendo seus. Mais duas configurações: tenant, o ID do diretório ou um domínio verificado como contoso.com (common, organizations e consumers são recusados), e client_id, o ID do aplicativo (cliente). O segredo client_secret é o valor do segredo do cliente do aplicativo.

  1. No centro de administração do Microsoft Entra, registre um aplicativo de locatário único sem URI de redirecionamento, adicione um segredo do cliente (copie o valor dele, não o ID do segredo), adicione a permissão de aplicativo Calendars.Read do Microsoft Graph e conceda o consentimento do administrador.
  2. Limite o aplicativo às caixas de correio de sala. Calendars.Read alcança todas as caixas de correio da organização até que uma política de acesso de aplicativo a restrinja. Coloque as salas em um grupo de segurança habilitado para email e execute os dois primeiros comandos abaixo no Exchange Online PowerShell; Test-ApplicationAccessPolicy responde Granted para uma sala do grupo. O RBAC do Exchange para aplicativos limita um aplicativo da mesma forma.
  3. As caixas de correio de sala trocam o assunto de uma reunião pelo nome do organizador, a menos que se configure o contrário. Para mostrar os títulos das reuniões, execute o terceiro comando para cada sala.
  4. Veja a prévia e depois salve. source test faz login, lê cada sala e informa as salas e reservas lidas, uma contagem por sala e, se o aplicativo também tiver Place.Read.All, quantas salas a organização lista.
# 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
  • Mapeamento. Reuniões privadas e confidenciais mostram o título privado e nenhum organizador. Reuniões marcadas como livres ou trabalhando em outro lugar, e as que a sala recusou, são tempo livre; as provisórias contam como ocupadas; as canceladas são descartadas. Eventos de dia inteiro são datas no fuso horário da sala.
  • Frequência. Uma fonte de salas sincroniza a cada 2 minutos. O login dura cerca de uma hora e é reutilizado.
  • Erros. O código e o texto de erro da Microsoft são citados, por exemplo AADSTS7000215 para um segredo do cliente incorreto, e uma sala que o aplicativo não consegue ler faz a sincronização falhar e é nomeada.
  • Uma conexão no lugar. Defina connection_id como uma conexão da Microsoft do projeto, em qualquer um dos dois modos, no lugar de tenant, client_id e client_secret. A fonte então não guarda nenhum segredo e faz login como a conexão faz.

Agendas de recursos do Google Workspace

O tipo google_calendar lê agendas de recursos e de salas com sua própria conta de serviço do Google. O segredo service_account_key é o arquivo de chave como o Google o emite, e a dica dele é o endereço da conta. A configuração opcional subject nomeia o usuário em nome de quem a conta age na delegação em todo o domínio.

  1. No Google Cloud, ative a API Google Agenda, crie uma conta de serviço e baixe uma chave JSON para ela.
  2. Dê à conta acesso às salas. Ou compartilhe cada agenda de sala com o endereço da conta e a permissão Ver todos os detalhes do evento, ou ative a delegação em todo o domínio para a conta no console de administração com o escopo https://www.googleapis.com/auth/calendar.events.readonly e defina subject como um usuário que consiga ver as salas.
  3. O calendar_id de uma sala é o ID da agenda do recurso no console de administração, em Prédios e recursos; ele termina em @resource.calendar.google.com. Veja a prévia com source test e depois salve.
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
  • Mapeamento. Eventos privados e confidenciais mostram o título privado e nenhum organizador. Eventos marcados como transparentes, e os que a sala recusou, são tempo livre; os cancelados são descartados. Eventos de dia inteiro mantêm as datas.
  • Erros. O texto de erro do Google é citado. Uma agenda que a conta não consegue ver é nomeada no erro, com a forma de compartilhá-la.
  • Uma conexão no lugar. Defina connection_id como uma conexão do Google do projeto no lugar de service_account_key. Com o aplicativo do screenRIG, a fonte lê as agendas que a conta que a aprovou pode ver, então essa conta precisa ver cada agenda de sala que a fonte lista, e subject não é permitido.

Limites e privacidade

  • Um cartão room guarda as 200 primeiras reservas e um cartão room-board guarda 60 por sala. Se houver mais, o status mostra um aviso com quantas ficaram de fora.
  • O documento do cartão contém apenas o que o cartão exibe, porque cada tela que mostra um cartão guarda o documento dele. Um cartão room recebe organizadores somente com showOrganizer ativado. Um cartão room-board nunca recebe organizadores e recebe o título privado no lugar de cada título de reunião, a menos que showTitles esteja ativado. Uma alteração em qualquer uma das duas configurações chega ao cartão na próxima sincronização. Uma reunião privada leva o título privado nos dois casos.
  • As mensagens desses tipos citam o texto de erro que a Microsoft ou o Google respondeu, e nunca um segredo.

O sistema de reservas de um local

O tipo momentus lê as funções (cada uso de uma sala, com datas e horários) e os espaços de uma instalação do Momentus Enterprise, o sistema vendido antes como Ungerboeck e EBMS, com um usuário de API que o administrador cria no Momentus (Main Menu, Api Users). Ele preenche um card events para um diretório de eventos, um card room-board para um saguão ou corredor, ou um card room para uma sala, e com um documento por sala, cada tela mostra as reservas da própria sala. Uma sala que muda pouco antes de um evento aparece dentro do intervalo de atualização, um minuto por padrão.

ConfiguraçãoSignificado
base_urlO endereço https do seu servidor web do Momentus, como https://momentus.example.com/prod; a API é lida a partir de {base_url}/api/v1/...
organizationO código da organização do Momentus de onde os dados são lidos
api_userO id do usuário de API
time_zoneO fuso IANA do local. O Momentus guarda datas e horários locais, então defina-o; sem ele, os horários são lidos como UTC e o status mostra um aviso
daysQuantos dias ler a partir de hoje, de 1 a 31 (padrão 7)
roomsLista opcional de {key, space, name?, floor?, capacity?, area?}; space é o código de espaço do Momentus. Sem ela, cada espaço que o Momentus lista é uma sala (chave: o código em minúsculas)
space_filter, function_filterExpressões de busca do Momentus opcionais que limitam os espaços ou as funções lidos
exclude_status_codesCódigos de status de função a omitir, como os cancelados. Os códigos de status são do seu local, então não há valor padrão
include, excludePalavras-chave que mantêm ou ocultam funções pelo título que a fonte mostra, como nas agendas

Dois segredos: api_key, um dos GUIDs de chave do usuário de API, e api_secret, o GUID secreto dele. Ambos são guardados selados, nunca mais exibidos e informados de novo se a fonte apontar para outro servidor, organização ou usuário de 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.guid

source test lê o Momentus, monta o documento e o confere com o card, e mostra as salas e os eventos lidos (functions, functions_other_spaces, events_KEY). Nada é salvo nem gravado.

  • No que uma função se torna. O Space da função escolhe a sala, o Description é o título (title_field pode nomear uma de suas descrições alternativas) e o nome da sala é o local. Uma função sem horário de início é um evento de dia inteiro. Uma função em várias datas com horários mostra esses horários em cada data; a que termina antes de começar vai pela noite.
  • Títulos privados. Um room-board mostra o título privado, a menos que show_titles esteja ativado, como nos outros tipos de sala.
  • Erros. O texto próprio do Momentus nunca é citado, porque o servidor é seu: auth_failed significa que o usuário de API, a chave ou o segredo está errado ou não pode ler funções e espaços, config_invalid que um filtro não é uma busca válida do Momentus, e parse_failed que base_url chegou a uma página web e não à API.

Uma sinalização para cada sala

Defina room_key_prefix (por exemplo momentus-room-) e a fonte também grava um documento room para cada sala sob esse prefixo seguido da chave da sala. Dê à tela de cada sala a sua sala e coloque um card room cuja chave data seja o prefixo seguido do campo próprio da tela:

screenrig screen update scr_BALLROOM --field room=ballroom-a

Use o tipo de catálogo inline room nesta primitiva da playlist:

{"id":"door","primitive":"card","card":"room","layout":"full","content_fit":"fill","params":{"room":"{screen.field.room}"},"data":"momentus-room-{screen.field.room}"}

As primitivas crd_ usam params e major da própria instância; omita esses campos. O data opcional seleciona uma chave de documento ou um modelo de chave por tela. O --card crd_ID da fonte continua indicando seu destino de sincronização. Para a página, defina visibility.data.key como momentus-room-{screen.field.room} e adicione depois uma página alternativa sem condições.

O backend preenche o campo no manifesto de cada tela, então uma única playlist mostra a cada tela a própria sala, e uma tela sem campo de sala não mostra nada para esse card. area_key_prefix faz o mesmo para um display de corredor: o area de cada sala a agrupa, e um room-board das salas dessa área é gravado sob o prefixo seguido da chave da área (area_card pode ser events). screen_field e area_field nomeiam os campos de tela que a verificação abaixo lê (room e area por padrão).

Conferir o mapeamento

screenrig source rooms src_ID (e a seção Mapeamento de salas na página da fonte no painel) lista as salas que a fonte oferece e que nenhuma tela mostra, e as telas cuja sala está ausente ou indica uma sala que o Momentus não tem:

screenrig source rooms src_VENUE

Uma tela aparece no relatório quando tem um valor para o campo, ou quando a própria playlist dela coloca um card que lê a chave. Cada uma é mapped, missing (a playlist lê o campo e a tela não tem nenhum) ou unknown (o valor não nomeia nenhuma sala; o relatório diz a sala que provavelmente se quis dizer, como as mesmas letras em outra caixa). Corrija uma tela com screenrig screen update scr_ID --field room=ROOM_KEY. O relatório lê as salas da última atualização boa da fonte e não busca nada; uma fonte que lista suas rooms as conhece antes da primeira atualização.

Segurança

Um endereço que concede acesso é uma credencial. O screenRIG o armazena criptografado, devolve apenas o esquema e o host, e o usa somente para executar a sincronização. As fontes leem endereços http(s) públicos na porta 80 ou 443 e seguem no máximo três redirecionamentos; um endereço que não resolve para um host público, uma conexão recusada e uma falha de handshake aparecem todos do mesmo jeito, de modo que uma fonte não serve para explorar uma rede. Nenhuma mensagem cita um endereço nem a resposta recebida. As fontes só leem: nunca gravam em um calendário.