Pular para o conteúdo

API GraphQL

A API da TechFab está disponível para os clientes e parceiros que optaram por adquirir o serviço de infraestrutura gerenciada, e só é acessível através da credencial recebida após a contratação.

A API serve para todos os produtos desenvolvidos pela TechFab e é construída inteiramente em GraphQL. Todas as requisições são enviadas para o endpoint único https://api.techfab.com.br/graphql.

Se você não tem familiaridade com GraphQL, pode ler mais a respeito e aprender no site oficial. O GraphQL permite que vários recursos sejam acessados com uma única requisição, como será mostrado em vários exemplos neste guia. É essencial para aplicações IoT.

Temos disponível um playground para que o desenvolvedor faça testes, ganhe familiaridade com os recursos disponíveis pela API, além de poder consultar a documentação auto-gerada. Está disponível em https://playground.api.techfab.com.br.

Visão geral do Playground

Ao acessar o playground, é necessário providenciar a credencial de acesso na aba Headers na parte inferior, e então clicar no botão re-fetch no lado esquerdo para carregar a documentação e liberar o recurso de auto-completar do editor, conforme a imagem abaixo.

Autenticação

É feita através do cabeçalho Authorization: Basic <sua-credencial>, substituindo o <sua-credencial> pela credencial gerada por nós.

⚠️ Atente-se ao prefixo Basic. Em alguns casos, seu cliente HTTP pode inserir o prefixo Bearer por padrão, fazendo com que a requisição não seja autorizada.

Refere-se às leituras feitas pelos sensores dos dispositivos. Podem ser consumidas de duas formas:

  • Em tempo real através do webhook de telemetria;
  • Histórico de medições anteriores através do resolver measurements.

⚠️ O resolver measurements é projetado para a consulta de dados históricos. Não é recomendado fazer requisições constantes a measurements para verificar a chegada de novas medições (polling). Isso poderá acarretar em maiores custos de assinatura da API ou até o bloqueio temporário do acesso à API. Sempre dê preferência ao webhook para aplicações em tempo real.

Tanto webhooks quanto dados históricos na API possuem os seguintes campos para as medições:

  • thingId: UUID do dispositivo que enviou a medida.
  • time: Unix timestamp em que a medida foi realizada.
  • resource: é o recurso medido pelos sensores, como temperatura, umidade, tensão, corrente e dezenas de outras medidas.
  • unit: unidade do recurso medido.
  • severity: nível de urgência da mensagem. Medidas acima de 0 são consideradas anomalias.
    ValorDescrição
    0Mensagem padrão.
    1Pouco urgente e não necessita ação imediata.
    2Urgente e necessita ação imediata.
    3Crítico e pode representar perda de conexão ou a integridade do dispositivo.
  • alertId: identificador da mensagem de alerta emitido. Costuma vir em mensagens com severity acima de 0. Confira a tabela de alert IDs disponível na documentação do seu produto.
  • value: valor de medidas numéricas. A maioria dos recursos são numéricos.
  • boolValue: valor de medidas booleanas, como switch, que representa o estado ligado ou desligado.
  • stringValue: valor de medidas em texto, como log.
  • dataValue: valor de medidas em dados binários, codificado em string base64.

Confira a documentação do seu produto para conhecer todas as medições disponíveis.

Webhooks são mensagens enviadas por nós diretamente para o seu servidor. Nosso serviço enviará uma requisição POST com as telemetrias dos dispositivos para o endereço desejado no seguinte formato, como mostrado no exemplo abaixo:

{
"id": "123e4567-e89b-12d3-a456-426614174000",
"data": [
{
"thingId": "2542460b-584b-4818-afba-de550f000076",
"resource": "temperature",
"value": 19.75,
"unit": "Cel",
"time": 1687630647
},
{
"thingId": "2542460b-584b-4818-afba-de550f000076",
"resource": "humidity",
"value": 55.52,
"unit": "%RH",
"time": 1687630647
},
{
"thingId": "2542460b-584b-4818-afba-de550f000076",
"resource": "switch",
"boolValue": true,
"time": 1687630646
},
{
"thingId": "2542460b-584b-4818-afba-de550f000076",
"resource": "electricPotential",
"value": 235.2935,
"time": 1687630646,
"severity": 1,
"alertId": 6
},
{
"thingId": "2542460b-584b-4818-afba-de550f000076",
"resource": "config",
"dataValue": "fs+r",
"time": 1687630648
},
{
"thingId": "2542460b-584b-4818-afba-de550f000076",
"resource": "log",
"stringValue": "Finished",
"time": 1687630707
}
]
}

Nosso servidor espera uma resposta de sucesso com o status entre 200 e 299 para que a requisição seja considerada como processada. Caso receba um status igual ou superior a 400, ou mesmo se houver falha na conexão, a requisição será repetida durante 5 minutos até que seja processada com sucesso. Se após este período a requisição ainda não tiver sido processada, ela será descartada.

⚠️ O campo id é o identificador da requisição. Em caso de falhas e novas tentativas de envio, pode ser útil para identificar se as medições já foram processadas.

Para registrar um webhook que receberá telemetrias, a seguinte mutation deve ser executada passando a URL desejada:

mutation SetTelemetryWebhook($url: String!) {
setWebhook(type: TELEMETRY, url: $url)
}

Caso não deseje mais receber telemetria via webhook, a seguinte mutation deve ser executada:

mutation DeleteWebhook {
deleteWebhook(type: TELEMETRY)
}

Pode ser consultado através do resolver measurements, dentro de thing.

measurements {
edges {
node {
resource
value
unit
time
severity
alertId
}
}
}
  • after: busca medidas após o cursor especificado. Útil em paginação.
  • first: busca as primeiras n medidas especificadas.
  • from: busca medidas a partir da data especificada em formato Unix timestamp.
  • to: busca medidas até a data especificada em formato Unix timestamp.
  • isAlert: busca medições de alerta.
  • resources: busca todas as medidas que possuem resource incluídos na lista.
  • severities: busca medições com os níveis de severidade incluídos na lista.
  • sort: parâmetros de ordenamento especificados abaixo.
    • time: ASC e DESC retornam resultados cronologicamente ordenados de forma ascendente e descendente respectivamente.
  • things para listar dispositivos.
  • thing para retornar um dispositivo específico, passando obrigatoriamente o id como argumento.

Mais detalhes podem ser consultados na documentação da API GraphQL no playground.

Novos dispositivos precisam ser registrados como parte da infraestrutura da TechFab através da mutation registerThings, passando uma lista de IDs dos dispositivos a serem ativados, e qual a rede estes dispositivos fazem parte.

mutation RegisterThings($thingIds: [ID!]!, $networkType: String!)

O networkType é especificado pela equipe técnica da TechFab para cada projeto. Entre em contato caso tenha dúvidas.

Dependendo do projeto, novos dispositivos são registrados pela própria equipe técnica da TechFab.

  • thingGroups para listar grupos de dispositivos.
  • thingGroup para retornar um grupo específico, passando obrigatoriamente o id como argumento.

Mais detalhes podem ser consultados na documentação da API GraphQL no playground.

Novos grupos de dispositivos podem ser criados através da mutation createThingGroup, passando um nome e uma lista de IDs de dispositivos, como no exemplo abaixo.

mutation CreateThingGroup($name: String!, $thingIds: [ID!]!) {
name
}

Grupos podem ser excluídos através da mutation deleteThingGroup, passando o ID do grupo a ser removido.

mutation DeleteThingGroup($id: ID!)

Nossa API retorna um número limitado de resultados por consulta. Se for necessário retornar mais resultados, a paginação deve ser usada.

Assim como várias outras APIs, usamos o padrão de connections com edges e nodes indexados por cursores. Segue um exemplo de consulta.

<connection> {
edges {
cursor
node {
...
}
}
pageInfo {
startCursor
endCursor
hasNextPage
}
}

<connection> é qualquer resolver que retorna uma lista de resultados, como things, thingGroups e measurements.

query GetThings($previousCursor: ID) {
things(first: 10, after: $previousCursor) {
pageInfo {
startCursor
endCursor
hasNextPage
}
edges {
cursor
node {
name
measurements { ... }
}
}
}
}

Vamos supor que você fez uma consulta aos 10 primeiros dispositivos, e o endCursor da primeira consulta retornou 652af35c0332d2798e0f971e. Na próxima consulta, você pode passar a variável previousCursor com o valor do endCursor da requisição anterior para então retornar os dados paginados.

{ "previousCursor": "652af35c0332d2798e0f971e" }

Você pode fazer isso repetidamente para retornar uma lista completa. Quando não houver mais resultados disponível, hasNextPage irá retornar false e a iteração pode ser concluída.

ℹ️ Este recurso está em desenvolvimento e pode não estar disponível para todos os clientes.

O estado é um documento JSON que armazena as configurações atuais do dispositivo.

Exemplo de um dispositivo do tipo fotocelula:

{
"manualMode": false,
"lightOn": true,
"defaultBrightness": 80,
"telemetrySchedules": [
{
"resource": "electricPotential",
"trigger": {
"type": "delta",
"value": 2
}
},
{
"resource": "accumulatedPower",
"trigger": {
"type": "cron",
"stringValue": "0 0 0 * * *"
}
}
],
"commandSchedules": [
{
"resource": "brightness",
"trigger": {
"type": "cron",
"stringValue": "0 30 1 * * *"
},
"value": 50
},
{
"resource": "brightness",
"trigger": {
"type": "cron",
"stringValue": "0 0 7 * * *"
},
"value": 40
}
]
}

O estado pode ser aplicado através das seguintes mutations, enviando uma string JSON serializada com as propriedades que se deseja aplicar:

  • Para dispositivos individualmente
    mutation SetThingState($thingId: ID!, $state: AWSJSON!) {
    setThingState(thingId: $thingId, state: $state)
    }
  • Para grupos de dispositivo
    mutation SetThingGroupState($thingGroupId: ID!, $type: String!, $state: AWSJSON!) {
    setThingGroupState(thingGroupId: $thingGroupId, type: $type, state: $state)
    }

    type é o tipo de dispositivo, por exemplo, fotocelula.

Por exemplo, vamos alterar as propriedades manualMode e defaultBrightness do dispositivo. Em seguida, veremos como podemos consultar o estado.

{
"thingId": "ad0ae0cc-e819-48d4-91a0-b93b12332c9f",
"state": "{\"manualMode\":false,\"defaultBrightness\":60}"
}

ℹ️ Cada dispositivo possui propriedades de estado diferentes. Consulte a sua documentação específica do seu dispositivo para saber mais.

O estado dos dispositivos pode ser consultado através do campo state do tipo Thing na API.

Exemplo:

query GetThing($id: ID!) {
thing(id: $id) {
id
type
state {
reported
desired
delta
}
}
}
{
"data": {
"thing": {
"id": "ad0ae0cc-e819-48d4-91a0-b93b12332c9f",
"type": "fotocelula",
"state": {
"reported": "{\"lightOn\":true,\"defaultBrightness\":85,\"telemetrySchedules\":[{\"resource\":\"electricPotential\",\"trigger\":{\"type\":\"delta\",\"value\":2}},{\"resource\":\"accumulatedPower\",\"trigger\":{\"type\":\"cron\",\"stringValue\":\"0 0 0 * * *\"}}],\"commandSchedules\":[{\"resource\":\"brightness\",\"trigger\":{\"type\":\"cron\",\"stringValue\":\"0 30 1 * * *\"},\"value\":50},{\"resource\":\"brightness\",\"trigger\":{\"type\":\"cron\",\"stringValue\":\"0 0 7 * * *\"},\"value\":40}]}",
"desired": "{\"lightOn\":true,\"defaultBrightness\":60,\"telemetrySchedules\":[{\"resource\":\"electricPotential\",\"trigger\":{\"type\":\"delta\",\"value\":2}},{\"resource\":\"accumulatedPower\",\"trigger\":{\"type\":\"cron\",\"stringValue\":\"0 0 0 * * *\"}}],\"commandSchedules\":[{\"resource\":\"brightness\",\"trigger\":{\"type\":\"cron\",\"stringValue\":\"0 30 1 * * *\"},\"value\":50},{\"resource\":\"brightness\",\"trigger\":{\"type\":\"cron\",\"stringValue\":\"0 0 7 * * *\"},\"value\":40}]}",
"delta": "{\"defaultBrightness\":60}"
}
}
}
}

O campo state possui as seguintes propriedades:

  • reported é o estado atual do dispositivo.
  • desired é o estado desejado do dispositivo.
  • delta é a diferença entre o estado reported e desired.

No exemplo acima, alteramos as propriedades manualMode e defaultBrightness. Agora podemos ver que temos um delta com defaultBrightness: 60. Isso significa que o defaultBrightness em reported é diferente do que está em desired, ou seja, foram alteradas na nuvem e estão aguardando serem confirmadas pelo dispositivo. Já a propriedade manualMode não aparece no delta, pois foi definida com o mesmo valor que já estava anteriormente, logo é ignorada.