- Um engenheiro diagnosticado com diabetes tipo 1 em 2020 criou um sistema pessoal de monitoramento baseado em Go para tratar o controle da glicose como resposta a incidentes
- Como o Libre não tem API nem SDK oficial, era difícil integrar diretamente, mas ele encontrou um caminho via Miao Miao e o app Tomato para enviar dados de glicose para um endpoint externo a cada 2 minutos
- Com um echo server em Go e deploy no Encore, ele verificou o formato das requisições do Tomato, converteu o valor de SGV e o exibiu em um dashboard do Grafana como um medidor de glicose em tempo real
- Um bot no Telegram registra anotações de contexto como refeições, exercícios e aplicações de insulina, e um cron executado a cada 5 minutos cria um incidente no incident.io quando a glicose cai abaixo do limite
- Com alertas, escalonamento e relatórios, esse fluxo funciona como um sistema de observabilidade de saúde pessoal para que episódios de glicose baixa ou lacunas de medição não precisem ser enfrentados sozinho
Tratando diabetes tipo 1 como um problema de resposta a incidentes
- Diabetes tipo 1 é uma doença autoimune em que o pâncreas produz pouca ou nenhuma insulina, necessária para transformar carboidratos em energia
- A insulina precisa ser reposta por injeção, e a dose varia conforme a comida ingerida e vários outros fatores
- O tipo 1 não é uma doença causada por estilo de vida, e atualmente não há cura
- A glicose no sangue precisa ser monitorada continuamente
- Se ficar alta demais por muito tempo, pode causar danos aos órgãos e reduzir a expectativa de vida
- Se ficar baixa demais, pode ser fatal mesmo em pouco tempo
- Quando a glicose cai demais, pode não ser possível comer ou beber com segurança, exigindo ajuda de outra pessoa
- Sempre que olha para uma comida ou bebida, é preciso calcular a quantidade de carboidratos e a dose de insulina necessária, e até um exercício leve, como uma caminhada curta, exige considerar a quantidade de insulina ativa no corpo
- Situações em que a visibilidade da glicose desaparece podem ser tratadas de forma parecida com um sistema de produção em estado de observabilidade zero
- Em um ambiente de trabalho, isso significaria declarar um incidente e só fechá-lo quando a visibilidade fosse restaurada
- Glicose baixa é o primeiro sinal de que algo está errado e exige ação imediata
Encontrando um caminho de dados com Libre, Miao Miao e Tomato
- O dispositivo Libre, preso ao braço, permite ler a glicose aproximando o celular
- O aparelho precisa ser trocado a cada 2 semanas
- Se a glicose cair abaixo de certa linha vermelha, é preciso consumir carboidratos
- Se sair da faixa verde, pode ser necessário considerar ações como aplicar insulina
- Exercício, ganho de peso e doenças alteram a sensibilidade à insulina, então o controle da glicose vira um alvo em constante movimento
- Se o Libre apresentar erro, não é possível ler a glicose no aparelho nem receber alertas de níveis perigosos
- O Libre é um dispositivo fechado, então é difícil obter os dados diretamente sem API ou SDK
- O Miao Miao é acoplado sobre o Libre, faz leituras aproximadamente a cada 2 minutos e envia o resultado para o app Tomato
- Mesmo quando o app oficial do Libre mostra erro, o caminho via Miao Miao continua publicando dados
- O app oficial aparentemente trata mudanças bruscas de glicose como outliers e deixa de enviar esses dados, mas o usuário queria poder avaliá-los por conta própria
- O app Tomato tinha uma função para publicar a glicose como eventos do Google Calendar a cada 5 minutos, o que permitia ver a glicose em uma complication do Apple Watch
Verificando as requisições do Tomato com um echo server em Go
- Nas configurações do app Tomato havia um campo para informar uma URL de sincronização de dados para o Nightscout
- Ao colocar o endereço de um servidor próprio em vez da URL do Nightscout, o seguinte caminho apareceu nos logs do gateway
/id/e1d67817-4591-4e8e-9bca-58a07a1087d8/api/v1/devicestatus
- Para inspecionar o corpo da requisição, ele escreveu um echo server simples em Go
- Para deploy e execução, usou encore.dev, o que permitiu operar o sistema de monitoramento gratuitamente
// encore:api public raw method=POST path=/id/:id/api/v1/devicestatus
func Echo(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", req.Header.Get("Content-Type"))
if _, err := io.Copy(w, req.Body); err != nil {
http.Error(w, "Failed to echo request", http.StatusInternalServerError)
return
}
}
- Um echo server é um servidor que devolve exatamente os dados enviados pelo cliente, útil para resolver problemas de rede ou testar o comportamento de aplicações
- Da mesma forma, ele inspecionou outras requisições e obteve a resposta com os dados de glicose
- date: 1696171541297
sgV: 73
delta: 0
sysTime: 1696171541381
dateString: "2023-10-01T14:45:41.297Z"
_id: "dOUXaI8HcaulCGrQfxe23UE0"
type: "sgv"
device: "Tomato"
direction: "Flat"
sgV é o valor da glicose, e ele o divide por 18 para usar a unidade adotada no Reino Unido
- A resposta também inclui data, hora e direção da tendência
- Essa chamada acontece a cada 2 minutos
Dashboard no Grafana e medidor de glicose
- O valor recebido da glicose foi configurado como uma Gauge metric
- Gauge é uma métrica que representa um único valor que pode subir ou descer, usada para medir valores variáveis como uso de memória, número de requisições simultâneas ou temperatura
- O valor da glicose é registrado após a conversão com
newValue / 18
var BloodSugar = metrics.NewGauge[float64]{
name: "blood_sugar",
metrics.GaugeConfig{},
}
BloodSugar.Set(float64(newValue) / 18)
if err := insertReading(ctx, newValue); err != nil {
rlog.Error(msg: "failed to insert blood sugar, proceeding", keysAndValues: "db_err", err)
}
- Os dados de glicose também são armazenados no Postgres em modo best effort
- Se o salvamento no banco falhar, ele apenas registra no log e segue em frente para não quebrar o restante da lógica
- O dashboard do Grafana mostra em tempo real o medidor de glicose, atualizado a cada 2 minutos
- A faixa ideal de glicose é, idealmente, entre 4 e 9
- Compartilhar o dashboard ou deixar um monitor em casa permite verificar rapidamente o estado da glicose
Adicionando contexto da glicose com um bot no Telegram
- Só o valor da glicose não basta; como em monitoramento de sistemas complexos, o contexto é importante
- Se a glicose subir logo após uma refeição, isso pode não ser motivo imediato de preocupação se a insulina já foi aplicada
- O Grafana suporta anotações, mas entrar no sistema e registrar anotações manualmente gera atrito
- Então ele criou um bot no Telegram para adicionar anotações via webhook sempre que recebe uma mensagem
// encore:api public raw method=POST path=/webhook
func Webhook(w http.ResponseWriter, req *http.Request) {
body, err := io.ReadAll(req.Body)
if err != nil {
http.Error(w, "could not read request body", http.StatusBadRequest)
return
}
defer req.Body.Close()
var update Update
err = json.Unmarshal(body, &update)
if err != nil {
http.Error(w, "could not unmarshal JSON", http.StatusBadRequest)
return
}
}
- O webhook recebe a requisição, valida a mensagem, faz o unmarshal do JSON e depois devolve o resultado do processamento ao Telegram
- Após validar a mensagem, ele faz o parse de inteiros e chama o serviço de anotações
- O serviço de anotações adiciona tags conforme o tipo de atividade
func getTags(ctx context.Context, activity string) []string {
a := strings.ToLower(activity)
switch a {
case "walk", "run", "ran", "gym":
return []string{"exercise"}
case "eat", "ate":
return []string{"food eaten"}
case "inject":
return []string{"inject"}
default:
return []string{"other"}
}
}
- No Grafana, é possível definir cores por tag e configurar consultas de anotações
- Quando contexto como refeição, exercício ou aplicação de insulina aparece no gráfico, fica mais fácil para ele e para quem vê o dashboard interpretar a situação da glicose
Transformando glicose baixa em incidente
- A visibilidade da glicose já existia, mas sem alertas era difícil reagir em tempo real
- Seguindo a orientação recebida da enfermeira, quando a glicose cai abaixo de 4, o tratamento deve começar
- Ele escreveu um pequeno cron que verifica a medição a cada 5 minutos e dispara um incidente se o valor estiver abaixo do limite inferior predefinido
var _ = cron.NewJob(
id: "monitor-blood",
cron.JobConfig{
Title: "monitor blood to check if there is reason to open an incident",
Every: 5 * cron.Minute,
Endpoint: BloodIncidentCron,
},
)
if r < BloodLowerLimit {
if err := triggerIncident(ctx); err != nil {
return fmt.Errorf("failed to trigger an incident: %w", err)
}
}
- Ele também adicionou ao bot do Telegram um código para abrir manualmente um incidente com a mensagem
"i need help"
- Quando um incidente é disparado, outro microserviço cria o incidente no incident.io
- O incident.io foi escolhido por ser a ferramenta mais parecida com o sistema interno de incidentes da Cloudflare e por ser escrita inteiramente em Go
payload := Payload{
IdempotencyKey: idemKey.String(),
Mode: incidentModeStandard,
Name: fmt.Sprintf("Matt's blood sugar is currently #%s", blood),
SeverityID: incidentSeverityCritical,
Summary: "Matt's blood sugar is low!",
Visibility: "public",
}
- Usar uma ferramenta real de incidentes permite configurar políticas de escalonamento e ver relatórios como o tempo passado em estado de incidente
- O workflow do incident.io permite configurar regras semelhantes ao Zapier e enviar alertas por SMS aos inscritos
- No workflow de exemplo, quando um incidente é aberto, ele recebe um SMS; se não for encerrado em 20 minutos, há escalonamento automático para a parceira ou irmãos
Relatórios e melhorias futuras
- Com uma ferramenta de incidentes, é possível ver em relatórios com que frequência incidentes de glicose baixa ocorreram ao longo do tempo
- O gráfico de exemplo usa dados de demonstração para o blog; felizmente, os dados reais são bem menos dramáticos
- Se os eventos de glicose baixa estiverem aumentando, isso pode indicar que o tratamento atual não está adequado e servir de base para conversar com o médico
- As melhorias atuais ou planejadas incluem:
- Encerramento automático: hoje ele fecha incidentes manualmente para revisar os dados, mas como às vezes demorou para encerrar e isso gerou escalonamentos, seria possível fechar automaticamente após cerca de 15 minutos de glicose estável
- Conversar com os dados: ele guarda anos de dados de glicose e quer treinar um LLM para fazer perguntas como “por que minha glicose sempre cai às 15h?” e usar também outros dados, como os do Google Calendar
- Expandir os gráficos: os gráficos atuais são simples, mas poderiam incluir indicadores como hba1c para avaliar o sucesso do controle do diabetes
- Reforçar a prevenção de falhas: como o sistema é crítico, quanto mais falhas puder evitar, melhor
Um sistema operacional pessoal de saúde construído com código
- No início do diagnóstico de tipo 1, havia o medo de que a doença tornasse a vida muito mais difícil
- Este projeto ajuda a entender, gerenciar e monitorar a condição de tipo 1
- Ao tratar o controle da glicose como um sistema distribuído, ele construiu um sistema operacional pessoal com observabilidade, alertas, incidentes, escalonamento e relatórios
- Saber programar permitiu automatizar até certo ponto o gerenciamento de uma condição que, até a década de 1920, equivalia a uma sentença de morte
1 comentários
Opiniões no Hacker News
Convivo com diabetes há 16 anos, e gosto do tom do autor; é admirável. Quando você se sentir triste ou com raiva por causa da doença, seria bom reler este texto e lembrar do momento atual, em que você tinha certeza de que conseguiria lidar com tudo
Não é fácil. Nem mentalmente, nem na prática; no meu caso, por vários anos foi algo próximo de “alegremente fora de controle”
Ainda ontem, pela primeira vez na minha vida com diabetes, eu vi 36 horas dentro da faixa-alvo e chorei de verdade. Foram 36 horas 100% no verde. Talvez eu finalmente tenha entendido
Alguns anos depois, minha qualidade de vida saltou para algo como 60%, graças a uma microbomba conectada a um CGM, ao compartilhamento de experiências com outras pessoas com diabetes, à instalação de uma VM com NightScout para ver a glicemia e a tendência no celular, no relógio, em widgets no desktop, no despertador e até na geladeira, e a contar carboidratos com precisão alemã, aplicando o bolus exatamente 15 minutos antes da refeição. Eu realmente detesto viver assim, mas funciona
Aguente firme. Não perca o foco
O estilo de escrita é muito bom e foi prazeroso de ler. Por coincidência, trabalho com testes de invasão, e um dos meus colegas tem diabetes tipo 1
Ver o panorama deste texto e as complexidades decorrentes disso me fez ter ainda mais empatia por esse colega, que supera essas dificuldades todos os dias e ainda assim é uma das pessoas mais gentis e acessíveis da equipe
Mas, depois de ler este texto, dá para sentir de verdade o nível de autodisciplina necessário para viver assim
Claro, também pode ser o tipo de trabalho que tenta invadir sistemas, como em testes de invasão; fiquei curioso se é isso que você faz
Recentemente li sobre um caso em que todos esperavam que uma pessoa fosse promovida, mas ela não foi. Seu único “pecado” foi comentar brevemente com um colega que estava exausta porque o carro vivia quebrando e precisava de consertos frequentes
A promoção exigiria algum deslocamento, e o gerente teria dito: “Eu ia promovê-la, mas ouvi dizer que o carro dela não está em boas condições”
Depois que o Matt fez essa apresentação na GopherConSG, conversamos sobre o problema de não sermos donos dos nossos próprios dados. Achei marcante como a questão é complexa por causa das muitas responsabilidades legais do lado do provedor de dados, ou seja, a empresa que monitora a glicemia, e dá para entender por que as grandes empresas hesitam em abrir esses dados
Por outro lado, parece bastante injusto que o usuário não consiga acessar dados que são legitimamente seus e que pode usar para tomar ações reais
Ontem criei algo relacionado: um plugin para tmux que mostra dados de glicemia como ícone de status no terminal
Também tenho diabetes tipo 1 e uso Dexcom como CGM; por enquanto fiz funcionar para esse ambiente, mas quero dar suporte a outros dispositivos e adicionar mais recursos. Se houver interesse, está aqui
https://github.com/Cian911/tmux-xdrip
A ideia do Miao Miao é interessante. Como pessoa com diabetes tipo 1, o Freestyle Libre é um dispositivo incrível que realmente mudou minha vida, mas o software é bem ruim
Reportei um bug há mais de um ano, e a Abbott ainda não reconheceu que é um bug. Isso apesar de haver muitos relatos iguais online e nas avaliações do app no Google Play
Para quem tiver interesse ou puder ajudar a pressionar a Abbott a reconhecer: o bug é que adicionar o LibreLink à lista de apps exceção do modo Não Perturbe não funciona; assim que o modo Não Perturbe é ativado, aparece imediatamente um alerta dizendo que “os alarmes não podem ser usados”. Em ambientes de trabalho, ativar o Não Perturbe também desliga os alarmes de glicemia, o que é inconveniente
Um assunto um pouco relacionado: há 6 meses fui diagnosticado com apneia obstrutiva do sono e preciso usar uma máquina CPAP, que sopra ar a uma pressão constante durante o sono para impedir o fechamento das vias aéreas. Não chega a ser missão crítica como diabetes tipo 1, mas essas máquinas registram muito mais logs do que eu esperava
Ao ler este texto, fiquei me perguntando se existe algo hackeado de forma parecida para monitoramento da apneia do sono, ou para casos mais graves
Já conheço o OSCAR e sou muito grato por ele, mas também queria saber o que mais existe
1: https://www.sleepfiles.com/OSCAR/
No pior momento do exame do sono, foram registrados cerca de 48 eventos por minuto em que eu parava de respirar. Quando a língua enrola para dentro das vias aéreas, respirar fica bem difícil
É interessante, mas fiquei curioso se há algum motivo para não terem analisado os softwares open source dessa área, como Nightscout, xDrip etc.
Não é um problema novo; muita gente já resolveu isso com soluções bem mais ricas em recursos e que funcionam bem
Scott Hanselman fala há anos em seu blog e no YouTube sobre diabetes tipo 1 e vários hacks tecnológicos. Antigamente, ele até fez um hack para mostrar a glicemia no prompt do terminal: https://www.youtube.com/watch?v=_meKUIm9NwA
Os trechos “durante esse período, não era possível medir a glicemia com o dispositivo, e eu não recebia alertas mesmo se a glicose atingisse níveis perigosos. Esse erro tinha o hábito de aparecer nos momentos mais estressantes” e “ao investigar, pareceu que o app principal não gostava de receber dados em que o nível de glicose mudava de repente e parecia um outlier” soam como um bug que ameaça a vida
Tecnicamente, decisões de tratamento não devem ser tomadas apenas com dados de CGM
No caso dos sensores Dexcom, alguns apps conseguem obter os valores brutos e contornar esse processo. No Libre, pelo menos no Libre 3, já vi ele preencher os dados posteriormente em certas situações quando conclui que aquilo não foi uma leitura incorreta, mas sim que a glicose estava mudando rapidamente
Não é baseado em tecnologia, mas também existem cães de alerta para diabetes
Tenho aprendido recentemente que cães bem treinados têm tido bastante sucesso em complementar sistemas tecnológicos de monitoramento e alerta, avisando seus tutores