В первой статье я рассказывал, как появился проект дневника диабета и почему обычных приложений нам оказалось недостаточно. Но сам дневник — это только верхушка айсберга. Любая аналитика начинается с данных. А значит, сначала нужно научиться их получать.
Современный диабетик использует сразу несколько устройств и сервисов. Сенсор непрерывного мониторинга глюкозы, приложение производителя, иногда инсулиновую помпу, иногда обычные шприц-ручки. Каждый производитель строит собственную экосистему, и объединять их между собой никто не собирается.
Мне хотелось получить единую систему, которая автоматически собирает показатели сахара, хранит историю и предоставляет данные уже моему приложению. При этом было важно не ломать привычный сценарий использования официальных приложений.
Именно поэтому архитектура проекта получилась такой.
Общая архитектура
Все внешние сервисы для меня являются исключительно источниками данных. Они отвечают за работу с медицинскими устройствами и облачной синхронизацией. Django отвечает за совершенно другую задачу — объединение информации, хранение истории и предоставление единого API всему остальному проекту. Home Assistant вообще не работает напрямую ни с Libre, ни с Medtrum. Он знает только о моем API и использует его для автоматизаций.
Получается довольно простая схема.

Главное требование — не мешать работе официальных приложений
Для меня это было принципиально. Телефон дочери работает так, как задумал производитель. Сенсор подключен. Официальное приложение показывает график. Приходят штатные уведомления. Я не хотел заменять эту экосистему. Поэтому интеграция строилась через учетную запись наблюдателя (родителя).
И LibreLinkUp, и Medtrum позволяют получать данные удаленно именно таким способом. В результате ребенок продолжает пользоваться официальным приложением, а моя система просто получает копию данных через предусмотренный производителем механизм.
Интеграция Medtrum
С Medtrum все оказалось довольно приятно. После авторизации доступен полноценный веб-интерфейс с отчетами. Достаточно было открыть инструменты разработчика браузера, чтобы увидеть, как именно строятся запросы.
Оказалось, что используется вполне классический REST API. После входа сервер устанавливает сессионные cookies, а дальнейшая работа происходит уже внутри этой HTTP-сессии. В Django это реализуется достаточно естественно. Создается HTTP-сессия. Проходит авторизация. Сохраняются cookies. После чего этой же сессией выполняются запросы к API.

Больше всего мне понравилось то, как Medtrum предоставляет историю. API позволяет сразу получать данные за произвольный диапазон дат. Это означает, что первая синхронизация выполняется очень быстро. Не нужно последовательно получать тысячи отдельных измерений. Достаточно запросить историю за нужный период и сохранить ее в собственной базе. После этого система переходит в обычный режим и периодически забирает только новые записи. Единственное, что оказалось неожиданностью — чувствительность сервера к HTTP-заголовкам. Если набор заголовков отличается от того, который использует официальный веб-клиент, сервер может отклонить запрос, даже если авторизация выполнена успешно. Поэтому при разработке интеграции инструменты разработчика браузера стали практически обязательными.
Интеграция Libre оказалась совсем другой
После Medtrum казалось, что Libre будет устроена примерно так же. Но это оказалось не так.
Первым делом я посмотрел LibreView. Однако довольно быстро выяснилось, что веб-интерфейс предназначен прежде всего для просмотра отчетов. Для человека это удобно, а вот автоматически получать поток измерений практически невозможно. Поэтому выбор пал на LibreLinkUp. Это официальное приложение для наблюдателей, которое позволяет родителям удаленно получать текущие показатели сахара. Именно оно идеально подходило под архитектуру моего проекта.
Но дальше начались сложности.
В отличие от Medtrum, здесь уже не было веб-клиента, который можно было бы спокойно изучить через браузер. Фактически единственным клиентом являлось Android-приложение. Поэтому пришлось анализировать сетевое взаимодействие приложения, чтобы понять последовательность запросов и разобраться, как именно происходит обмен данными с сервером. В результате удалось восстановить всю последовательность взаимодействия и реализовать собственного клиента для Django. Но внутри все оказалось совершенно иначе. Вместо классических cookies здесь используется токенная авторизация. Сначала необходимо пройти аутентификацию. Получить токен. Использовать его в последующих запросах. При необходимости обновлять.

Как и в случае с Medtrum, сервер оказался довольно чувствительным к HTTP-заголовкам. Поэтому пришлось максимально точно воспроизводить поведение официального клиента.
Почему все данные сначала попадают в Django
Может возникнуть вопрос. Зачем вообще сохранять информацию у себя? Почему Home Assistant сразу не получает данные из Libre? Или почему не обращаться к API производителей каждый раз при открытии страницы? Причин несколько.
Во-первых, все производители используют разные API.
Во-вторых, они могут менять их независимо от моего приложения.
В-третьих, данные нужны не только для отображения текущего сахара.
На их основе строятся графики. Работает статистика. Формируются отчеты. Позже появилась аналитика. А сейчас постепенно развивается модуль прогнозирования. Поэтому собственной базой данных я фактически отделил свое приложение от внешних сервисов.
Если завтра появится еще один производитель CGM, мне достаточно написать еще один адаптер, а вся остальная система продолжит работать без изменений.
Home Assistant появился совсем по другой причине
Изначально Home Assistant не планировался как часть проекта. Но постепенно захотелось получать более гибкие уведомления.
Например:
предупредить о быстром падении сахара;
сообщить голосом через Алису;
отправить push на телефон;
использовать данные в сценариях умного дома.
Именно здесь Home Assistant оказался идеальным решением. Я создал в нем виртуальный датчик. Этот датчик через определенные интервалы обращается к API моего Django-приложения. Получает текущее значение сахара. Получает направление тренда. И обновляет собственное состояние. Для Home Assistant это выглядит как обычный датчик температуры или влажности. А значит, дальше можно использовать всю мощь платформы. Создавать автоматизации. Настраивать сложные условия. Озвучивать предупреждения через Алису. Отправлять push-уведомления. Подключать любые другие устройства умного дома.

Именно это мне больше всего нравится в таком подходе. Home Assistant ничего не знает о Libre или Medtrum. Он знает только один источник данных — мое API. Поэтому расширять систему становится значительно проще.
Что получилось в итоге
Со временем я понял, что самым важным компонентом проекта оказался вовсе не дневник и даже не интеграции с медицинскими сервисами. Главным элементом стало собственное хранилище данных. Именно оно позволило объединить разные источники информации в единую модель, независимо от того, каким способом эти данные были получены. Сегодня вся система строится вокруг одного потока данных:

Теперь для остальных компонентов проекта уже не имеет значения, откуда пришли данные. Они работают с единым API и единым форматом хранения. Именно эта архитектура впоследствии позволила без серьезных изменений добавить автоматический расчет показателей, аналитику и первые эксперименты с машинным обучением.
Что будет в следующей статье
Когда система научилась автоматически получать данные, возникла новая задача. Недостаточно просто знать уровень сахара. Важно понимать, почему он изменился.
Как учитывать хлебные единицы?
Почему одинаковое количество углеводов в разных продуктах влияет на сахар по-разному?
Как учитывать время усвоения пищи, гликемический индекс и действие различных типов инсулина?
Об этом и пойдет речь в следующей статье, где мы разберем математическую модель, лежащую в основе расчетов диабетического дневника.