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 refresha 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ção | Cartão | O que ela precisa |
|---|---|---|
slack-channel | workplace-posts, message-wall | O 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 |
bonusly | kudos | Um token de acesso do Bonusly com recognition:read, que um administrador Global ou Tech cria em Company, Integrations, API & Tokens |
facebook-page | social-wall | O 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-business | social-wall | O 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-reviews | reviews | O ID do local e uma chave de API do Google Cloud com a Places API (New) ativada |
google-travel-times | travel-times | Um 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-jobs | now-hiring | O token do quadro de boards.greenhouse.io/TOKEN. Sem credencial: o quadro é público |
lever-jobs | now-hiring | O nome do site de jobs.lever.co/NAME e a região. Sem credencial |
ashby-jobs | now-hiring | O nome do quadro de jobs.ashbyhq.com/NAME. Sem credencial |
github-actions | build-board | O 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.jsonPOST /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_SOURCEsource 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.
| Comando | O que faz |
|---|---|
source list, source show ID | As fontes do projeto, com status, configurações e segredos mascarados |
source create | Salva uma fonte e a executa em poucos segundos |
source update ID | Altera 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 test | Faz uma prévia de uma fonte, salva ou não, sem gravar nada |
source refresh ID | Sincroniza na hora e mostra o resultado |
source delete ID | Remove 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 30memsource createousource update, de 1 minuto a 7 dias e pelo menos o dobro do intervalo de atualização.--stale-after nonemanté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.datanomeia 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 showmostrastale_after,data_expires_ate, 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) esource_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--tagnunca 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.
| Estado | Significado |
|---|---|
pending | A fonte ainda não foi executada |
ok | A última busca foi bem-sucedida e o cartão aceitou o documento |
error | A última sincronização falhou; o cartão mantém o último documento válido |
disabled | O 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 erro | O que aconteceu |
|---|---|
unreachable | O screenRIG não conseguiu acessar o endereço. Use um endereço http(s) público na porta 80 ou 443 |
timeout | O endereço não respondeu em 20 segundos |
too_large | A resposta é maior que 5 MB |
http_status | O endereço respondeu com um erro HTTP. Um 404 significa que o endereço não está mais correto |
auth_failed | O endereço recusou as credenciais; a próxima tentativa espera uma hora |
not_shared | Uma 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_failed | A 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_limited | O serviço está limitando as solicitações; a próxima sincronização espera |
mapping_failed | O mapeamento não conseguiu ler a resposta no lugar que a mensagem nomeia, como um rows_path que não encontra matriz |
card_data_invalid | O cartão recusou o documento no caminho indicado pela mensagem |
target_missing | O cartão foi excluído. Aponte a fonte para outro cartão |
payment_required | O armazenamento do projeto está cheio, então o documento não foi gravado |
config_invalid | Uma configuração ou um segredo armazenado não pode mais ser usado. Informe-o novamente |
internal | A 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ção | Significado |
|---|---|
calendars | De 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 |
days | Quantos dias exibir a partir de hoje, de 1 a 31 (padrão 7) |
time_zone | Um fuso horário IANA como America/Vancouver, usado para o limite do dia e para horários que um feed deixa sem fuso |
private_title | O título exibido para eventos privados (padrão Private) |
include, exclude | Palavras-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:PRIVATEouCONFIDENTIAL) 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 testmostra 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.urlPlanilhas, 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ção | Tipos | Significado |
|---|---|---|
url | todos | O 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 |
mapping | todos | O modelo que monta os dados do cartão. Obrigatório |
key_template | todos | Chave opcional com marcadores {screen.field.NAME}; a transformação retorna um array documents de {fields,document} |
sheet | sheets | O nome de uma guia, para ler uma guia diferente da que o link nomeia |
header_row | sheets, csv | true (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_column | sheets, csv | A coluna cujas células nomeiam as linhas, para que o mapeamento possa buscar uma linha pelo nome |
delimiter | csv | ,, ; ou tab. Por padrão, o que a primeira linha mais usa |
auth | csv, json | none (padrão), bearer, header, basic, oauth2 ou token_request: veja Solicitações, login e páginas |
header_name | csv, json | Com auth header, o cabeçalho que leva o token, como X-API-Key |
rows_path | json | Onde fica a matriz de linhas na resposta, como data.items |
key_field | json | O 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.
- 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.
- 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, definasheet; um link publicado não aceitasheet. - 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.tokenO 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ção | Significado |
|---|---|
method | GET (padrão) ou POST. Um body ou graphql o transforma em POST |
body | Um objeto ou array JSON, ou texto enviado como está escrito. No máximo 16 KiB depois de preenchidos os marcadores |
body_format | json (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 |
headers | Até 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_zone | Um fuso IANA para os marcadores de data. Sem ele, são lidos em UTC e o status lista um aviso |
pagination | Lê mais de uma página, como descrito abaixo |
format | Somente 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ção | Configurações e segredos |
|---|---|
none | O padrão |
bearer | O segredo token, enviado como Authorization: Bearer |
header | header_name e o segredo token |
basic | username (sem dois-pontos) e o segredo password, enviados como Authorization: Basic |
oauth2 | Credenciais 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_request | Um 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.secretOs 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.
| Tipo | Como a próxima página é encontrada |
|---|---|
link_header | O endereço rel="next" no cabeçalho Link |
next_url | path, o lugar da resposta que contém o próximo endereço (somente json) |
cursor | param 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) |
page | param, 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 queSold outvirasold_out; um cabeçalho vazio viracolumn_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_failedemrows_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:
| Tipo | Ponto de partida | Contém |
|---|---|---|
sheets, csv | rows | Cada linha de dados, um objeto por chave de coluna |
sheets, csv | columns | [{key, name}] na ordem da planilha |
sheets, csv | by_key | Com key_column: as linhas pelo texto aparado da célula dessa coluna |
json | body | A resposta inteira |
json | by_key | Com 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ção | Retorna | Escrita |
|---|---|---|
$gt, $gte, $lt, $lte | Verdadeiro ou falso, lendo os dois valores como números | {"$lt": ["{{body.stock}}", 5]} |
$between | Verdadeiro quando o valor está dentro dos dois limites, inclusive | {"$between": ["{{body.temp}}", 18, 24]} |
$eq, $ne | Nú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"]} |
$contains | Um texto contém um texto, ignorando maiúsculas e minúsculas, ou uma lista contém um valor | {"$contains": ["{{body.title}}", "closed"]} |
$in | O valor é um de uma lista | {"$in": ["{{body.tier}}", ["gold", "silver"]]} |
$and, $or, $not | Combinam condições | {"$and": [{"$gt": ["{{body.a}}", 1]}, {"$not": {"$eq": ["{{body.s}}", "x"]}}]} |
$sum, $avg, $min, $max | Um número a partir de uma lista | {"$sum": "{{rows}}", "$of": "{{item.amount}}"} |
$count | Quantos 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.
$oflê um marcador de cada elemento, comitemeindexdisponíveis, e$wheremanté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,$sume$countvalem 0, e$avg,$mine$maxomitem o seu membro. - Números. Uma vírgula decimal (
3,50,1.234,50) precisa de"$decimal": ",".$avgarredonda para 2 casas, e"$round"define de 0 a 6.$sum,$mine$maxsã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=1ao link de um arquivo CSV, ou&download=1quando 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_columncomo a coluna que dá nome a cada linha, pelo cabeçalho em qualquer caixa, e leia uma linha comoby_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_failedemkey_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ção | Significado |
|---|---|
rooms | De 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 |
days | Quantos dias exibir a partir de hoje, de 1 a 31 (padrão 7) |
time_zone | Um 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_title | O 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.
- 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.Readdo Microsoft Graph e conceda o consentimento do administrador. - Limite o aplicativo às caixas de correio de sala.
Calendars.Readalcanç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-ApplicationAccessPolicyrespondeGrantedpara uma sala do grupo. O RBAC do Exchange para aplicativos limita um aplicativo da mesma forma. - 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.
- Veja a prévia e depois salve.
source testfaz login, lê cada sala e informa as salas e reservas lidas, uma contagem por sala e, se o aplicativo também tiverPlace.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
AADSTS7000215para 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_idcomo uma conexão da Microsoft do projeto, em qualquer um dos dois modos, no lugar detenant,client_ideclient_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.
- No Google Cloud, ative a API Google Agenda, crie uma conta de serviço e baixe uma chave JSON para ela.
- 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.readonlye definasubjectcomo um usuário que consiga ver as salas. - O
calendar_idde 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 comsource teste 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_idcomo uma conexão do Google do projeto no lugar deservice_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, esubjectnão é permitido.
Limites e privacidade
- Um cartão
roomguarda as 200 primeiras reservas e um cartãoroom-boardguarda 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
roomrecebe organizadores somente comshowOrganizerativado. Um cartãoroom-boardnunca recebe organizadores e recebe o título privado no lugar de cada título de reunião, a menos queshowTitlesesteja 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ção | Significado |
|---|---|
base_url | O 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/... |
organization | O código da organização do Momentus de onde os dados são lidos |
api_user | O id do usuário de API |
time_zone | O 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 |
days | Quantos dias ler a partir de hoje, de 1 a 31 (padrão 7) |
rooms | Lista 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_filter | Expressões de busca do Momentus opcionais que limitam os espaços ou as funções lidos |
exclude_status_codes | Có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, exclude | Palavras-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.guidsource 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
Spaceda função escolhe a sala, oDescriptioné o título (title_fieldpode 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-boardmostra o título privado, a menos queshow_titlesesteja ativado, como nos outros tipos de sala. - Erros. O texto próprio do Momentus nunca é citado, porque o servidor é seu:
auth_failedsignifica que o usuário de API, a chave ou o segredo está errado ou não pode ler funções e espaços,config_invalidque um filtro não é uma busca válida do Momentus, eparse_failedquebase_urlchegou 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-aUse 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_VENUEUma 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.