using System.Collections.Generic;
using Newtonsoft.Json.Converters;
using System.Globalization;
using System.Net.Http.Headers;
using System.Threading.Tasks;
public static string apiRegion = "";
public static HttpClient httpClient = new HttpClient(new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate });
static async Task Main(string[] args)
UserResponse? userResponse = null;
var logonDetails = GetContent();
Console.WriteLine("Processing");
var req = CreateRequest(HttpMethod.Post, "llu/auth/login", "");
req.Content = logonDetails;
userResponse = await SendRequest<UserResponse>(req, "Logging In");
ArgumentNullException.ThrowIfNull(userResponse);
if (!userResponse?.Data?.Redirect ?? false)
apiRegion = "-" + userResponse?.Data?.Region;
ArgumentNullException.ThrowIfNull(userResponse);
while (userResponse != null && userResponse?.Status == 4)
var req = CreateRequest(HttpMethod.Post, $"auth/continue/{userResponse?.Data?.Step?.Type}", userResponse.GetToken());
userResponse = await SendRequest<UserResponse>(req, "Step");
ArgumentNullException.ThrowIfNull(userResponse);
var loggedInUserID = userResponse?.Data?.User?.ID;
await SendRequest<GlucoseHistoryResponse>(CreateRequest(HttpMethod.Get, "glucoseHistory?numPeriods=5&period=1", userResponse.GetToken()));
var req2 = CreateRequest(HttpMethod.Get, "llu/connections", userResponse.GetToken());
req2.Headers.Add("patientid", userResponse?.Data?.User?.ID);
var connections = await SendRequest<Connections>(req2, "llu/connections");
ArgumentNullException.ThrowIfNull(connections);
ArgumentNullException.ThrowIfNull(connections.Data);
if ((connections?.Data?.Length ?? 0) == 0)
throw new Exception("No followers found");
if (connections != null && connections?.Data != null)
foreach (var connection in connections.Data)
await DisplayLog(connection.patientId, userResponse.GetToken(), connection.GetDisplayName());
await DisplayGraph(connection.patientId, userResponse.GetToken(), connection.GetDisplayName());
Console.WriteLine("Finished");
public static async Task DisplayGraph(string? userID, string token, string name)
ArgumentNullException.ThrowIfNull(userID);
var request = CreateRequest(HttpMethod.Get, $"llu/connections/{userID}/graph", token);
var graph = await SendRequest<GraphResponse>(request, $"Graph Request for {name}");
graph?.Data?.Connection?.glucoseMeasurement?.DumpObject($"Latest values for {name}", 1);
private static async Task DisplayLog(string? userID, string token, string name)
ArgumentNullException.ThrowIfNull(userID);
var request = CreateRequest(HttpMethod.Get, $"/llu/connections/{userID}/logbook", token);
await SendRequest<LogResponse>(request);
static StringContent GetContent()
var email = Util.GetPassword("LibreviewUserName");
var password = Util.GetPassword("LibreViewPassword");
Console.WriteLine("Enter LibreView user Name");
var email = Console.ReadLine();
Console.WriteLine("Enter LibreView Password");
var password = Console.ReadLine() ;
var content = "{\"email\": \"" + email + "\", \"password\": \"" + password + "\"}";
return new StringContent(content)
ContentType = new MediaTypeHeaderValue("application/json")
private static HttpRequestMessage CreateRequest(HttpMethod method, string? path, string? token = null)
ArgumentNullException.ThrowIfNull(path);
var url = Uri.IsWellFormedUriString(path, UriKind.Absolute) ? path : $"https://api{apiRegion}.libreview.io/{path}";
if (path.ToLower().StartsWith("http"))
Uri.IsWellFormedUriString(path, UriKind.Absolute);
var request = new HttpRequestMessage
RequestUri = new Uri(url),
{ "product" , "llu.android" },
{ "Accept" , "application/json" },
{ "cache-control" , "no-cache" },
{ "accept-encoding" , "gzip" },
{ "connection" , "Keep-Alive" },
{ "user-agent" , "Mozilla/5.0 (Windows NT 10.0; rv:129.0) Gecko/20100101 Firefox/129.0" }
if (!String.IsNullOrEmpty(token))
request.Headers.Add("Authorization", $"Bearer {token}");
private static void DumpResponse(HttpResponseMessage response, string rawJson, string? title)
var jo = JsonConvert.DeserializeObject(rawJson, Converter.Settings);
(new { response.StatusCode, response, rawJson, jo }).DumpObject(title, 0);
Console.WriteLine(title);
Console.WriteLine(response.StatusCode);
Console.WriteLine(rawJson);
private static async Task<T?> SendRequest<T>(HttpRequestMessage request, string info = "")
ArgumentNullException.ThrowIfNull(request);
info = request?.RequestUri?.ToString() ?? "";
using var response = await httpClient.SendAsync(request ?? new HttpRequestMessage());
var json = await response.Content.ReadAsStringAsync();
DumpResponse(response, json, request?.RequestUri?.ToString());
ArgumentNullException.ThrowIfNull(json);
var o = JsonConvert.DeserializeObject<T>(json, Converter.Settings);
public partial class AuthTicket
public string Token { get; set; } = "";
[JsonProperty("expires")]
public long Expires { get; set; }
[JsonProperty("duration")]
public long Duration { get; set; }
public DateTime PossibleExpiryDate
return DateTime.UnixEpoch.AddSeconds(Expires);
public TimeSpan PossibleDuration
return TimeSpan.FromMilliseconds(Duration);
public partial class Step
public string? Type { get; set; }
[JsonProperty("componentName")]
public string? ComponentName { get; set; }
public Props? Props { get; set; }
public partial class Props
[JsonProperty("reaccept")]
public bool? Reaccept { get; set; }
[JsonProperty("titleKey")]
public string? TitleKey { get; set; }
public string? Type { get; set; }
internal static class Converter
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
public class UserResponse
public int Status { get; set; }
public UserResponseData? Data { get; set; }
public class UserResponseData
[JsonProperty("redirect")]
public bool Redirect { get; set; }
public string? Region { get; set; }
public UserResponseUser? User { get; set; }
public Step? Step { get; set; }
[JsonProperty("messages")]
public Messages1? Messages { get; set; }
[JsonProperty("notifications")]
public Notifications? Notifications { get; set; }
[JsonProperty("authTicket")]
public AuthTicket? AuthTicket { get; set; }
[JsonProperty("invitations")]
public object? Invitations { get; set; }
[JsonProperty("trustedDeviceToken")]
public string? TrustedDeviceToken { get; set; }
public partial class User
public string? ID { get; set; }
public class UserResponseUser
public string? ID { get; set; }
[JsonProperty("accountType")]
public string? AccountType { get; set; }
[JsonProperty("country")]
public string? Country { get; set; }
[JsonProperty("uiLanguage")]
public string? UiLanguage { get; set; }
public string? firstName { get; set; }
public string? lastName { get; set; }
public string? middleInitial { get; set; }
public string? email { get; set; }
public string? communicationLanguage { get; set; }
public string? uom { get; set; }
public string? dateFormat { get; set; }
public string? timeFormat { get; set; }
public int[]? emailDay { get; set; }
public UserResponseSystem? system { get; set; }
public Details? details { get; set; }
public Twofactor? twoFactor { get; set; }
public int created { get; set; }
public int lastLogin { get; set; }
public Programs? programs { get; set; }
public int dateOfBirth { get; set; }
public Dictionary<string, Practice>? practices { get; set; }
public Dictionary<string, Device>? devices { get; set; }
public Consents? consents { get; set; }
public DateTime calculatedLastLogin => DateTime.UnixEpoch.AddSeconds(this.lastLogin);
public DateTime calculatedDateOfBirth => DateTime.UnixEpoch.AddSeconds(this.dateOfBirth);
public DateTime calculatedCreated => DateTime.UnixEpoch.AddSeconds(this.created);
public string GetDisplayName()
return $"{firstName} {middleInitial} {lastName}".Replace(" ", " ");
public class UserResponseSystem
public Messages? messages { get; set; }
public int firstUsePhoenix { get; set; }
public int firstUsePhoenixReportsDataMerged { get; set; }
public int lluGettingStartedBanner { get; set; }
public int lluNewFeatureModal { get; set; }
public string? lvWebPostRelease { get; set; }
public int webRegister { get; set; }
public int welcomeScreen { get; set; }
public DateTime calculated_firstUsePhoenix => DateTime.UnixEpoch.AddSeconds(this.firstUsePhoenix);
public DateTime calculated_firstUsePhoenixReportsDataMerged => DateTime.UnixEpoch.AddSeconds(this.firstUsePhoenixReportsDataMerged);
public DateTime calculated_lluGettingStartedBanner => DateTime.UnixEpoch.AddSeconds(this.lluGettingStartedBanner);
public DateTime calculated_lluNewFeatureModal => DateTime.UnixEpoch.AddSeconds(this.lluNewFeatureModal);
public DateTime calculated_webRegister => DateTime.UnixEpoch.AddSeconds(this.webRegister);
public DateTime calculated_welcomeScreen => DateTime.UnixEpoch.AddSeconds(this.welcomeScreen);
public string? primaryMethod { get; set; }
public string? primaryValue { get; set; }
public string? secondaryMethod { get; set; }
public string? secondaryValue { get; set; }
public string? id { get; set; }
public string? practiceId { get; set; }
public string? name { get; set; }
public string? address1 { get; set; }
public string? city { get; set; }
public string? state { get; set; }
public string? zip { get; set; }
public string? phoneNumber { get; set; }
public object? records { get; set; }
public string? id { get; set; }
public string? nickname { get; set; }
public string? sn { get; set; }
public int type { get; set; }
public int uploadDate { get; set; }
public DateTime calculatedUploadDate => DateTime.UnixEpoch.AddSeconds(this.uploadDate);
public Llu? llu { get; set; }
public Realworldevidence? realWorldEvidence { get; set; }
public int policyAccept { get; set; }
public int touAccept { get; set; }
public DateTime calculated_policyAccept => DateTime.UnixEpoch.AddSeconds(this.policyAccept);
public DateTime calculated_touAccept => DateTime.UnixEpoch.AddSeconds(this.touAccept);
public class Realworldevidence
public int policyAccept { get; set; }
public bool declined { get; set; }
public int touAccept { get; set; }
public History[]? history { get; set; }
public DateTime calculated_policyAccept => DateTime.UnixEpoch.AddSeconds(this.policyAccept);
public DateTime calculated_touAccept => DateTime.UnixEpoch.AddSeconds(this.touAccept);
public int policyAccept { get; set; }
public bool declined { get; set; }
public DateTime calculated_policyAccept => DateTime.UnixEpoch.AddSeconds(this.policyAccept);
public int unread { get; set; }
public class Notifications
public int unresolved { get; set; }
[JsonProperty("operation")]
public string? Operation { get; set; }
public Args? Args { get; set; }
public object? Error { get; set; }
[JsonProperty("totalSteps")]
public int TotalSteps { get; set; }
[JsonProperty("currentStep")]
public int CurrentStep { get; set; }
public string? Url { get; set; }
public class ExportRequest
[JsonProperty("captchaResponse")]
public string? CaptchaResponse { get; set; }
public string? Type { get; set; }
public ExportRequest(string captchaResponse)
this.CaptchaResponse = captchaResponse;
public class ExportResponse
public int Status { get; set; }
public ExportResponseData? Data { get; set; }
public AuthTicket? Ticket { get; set; }
public object? Error { get; set; }
public class ExportResponseData
public string? Url { get; set; }
public string? Ws { get; set; }
public string? Lp { get; set; }
public class GraphResponse
public int Status { get; set; }
public GraphResponseData? Data { get; set; }
public AuthTicket? Ticket { get; set; }
public class GraphResponseData
[JsonProperty("connection")]
public Connection? Connection { get; set; }
[JsonProperty("activeSensors")]
public ActiveSensor[]? ActiveSensors { get; set; }
[JsonProperty("graphData")]
public GlucoseMeasurement[]? GraphData { get; set; }
public int Status { get; set; }
public GlucoseMeasurement[]? Data { get; set; }
public AuthTicket? Ticket { get; set; }
public int Status { get; set; }
public Connection[]? Data { get; set; }
public AuthTicket? Ticket { get; set; }
public string? id { get; set; }
public string? patientId { get; set; }
public string? country { get; set; }
public int status { get; set; }
public string? firstName { get; set; }
public string? lastName { get; set; }
public int targetLow { get; set; }
public int targetHigh { get; set; }
public int uom { get; set; }
public ActiveSensor? sensor { get; set; }
public AlarmRules? alarmRules { get; set; }
public GlucoseMeasurement? glucoseMeasurement { get; set; }
public GlucoseItem? glucoseItem { get; set; }
public object? glucoseAlarm { get; set; }
public PatientDevice? patientDevice { get; set; }
public int created { get; set; }
public string GetDisplayName()
return $"{firstName} {lastName}";
public DateTime calculatedCreatedDate => DateTime.UnixEpoch.AddSeconds(this.created);
public class ActiveSensor
public string? deviceId { get; set; }
public string? sn { get; set; }
public int a { get; set; }
public int w { get; set; }
public int pt { get; set; }
public bool s { get; set; }
public bool lj { get; set; }
public bool c { get; set; }
public H? h { get; set; }
public F? f { get; set; }
public L? l { get; set; }
public Nd? nd { get; set; }
public int p { get; set; }
public int r { get; set; }
public Std? std { get; set; }
public bool on { get; set; }
public int th { get; set; }
public float thmm { get; set; }
public int d { get; set; }
public float f { get; set; }
public int th { get; set; }
public int thmm { get; set; }
public int d { get; set; }
public int tl { get; set; }
public float tlmm { get; set; }
public bool on { get; set; }
public int th { get; set; }
public float thmm { get; set; }
public int d { get; set; }
public int tl { get; set; }
public float tlmm { get; set; }
public int i { get; set; }
public int r { get; set; }
public int l { get; set; }
public class GlucoseMeasurement
public string? FactoryTimestamp { get; set; }
public string? Timestamp { get; set; }
public int type { get; set; }
public int ValueInMgPerDl { get; set; }
public int TrendArrow { get; set; }
public object? TrendMessage { get; set; }
public int MeasurementColor { get; set; }
public int GlucoseUnits { get; set; }
public float Value { get; set; }
public bool isHigh { get; set; }
public bool isLow { get; set; }
public string? FactoryTimestamp { get; set; }
public string? Timestamp { get; set; }
public int type { get; set; }
public int ValueInMgPerDl { get; set; }
public int TrendArrow { get; set; }
public object? TrendMessage { get; set; }
public int MeasurementColor { get; set; }
public int GlucoseUnits { get; set; }
public float Value { get; set; }
public bool isHigh { get; set; }
public bool isLow { get; set; }
public class PatientDevice
public string? did { get; set; }
public int dtid { get; set; }
public string? v { get; set; }
public bool l { get; set; }
public int ll { get; set; }
public bool h { get; set; }
public int hl { get; set; }
public int u { get; set; }
public FixedLowAlarmValues? fixedLowAlarmValues { get; set; }
public bool alarms { get; set; }
public int fixedLowThreshold { get; set; }
public class FixedLowAlarmValues
public int mgdl { get; set; }
public float mmoll { get; set; }
public class GlucoseHistoryResponse
public int status { get; set; }
public GlucoseHistoryData? data { get; set; }
public AuthTicket? ticket { get; set; }
public class GlucoseHistoryData
public int lastUpload { get; set; }
public int lastUploadCGM { get; set; }
public int lastUploadPro { get; set; }
public int reminderSent { get; set; }
public List<int>? devices { get; set; }
public List<Period>? periods { get; set; }
public DateTime calculatedlastUpload => DateTime.UnixEpoch.AddSeconds(this.lastUpload);
public DateTime calculatedlastUploadCGM => DateTime.UnixEpoch.AddSeconds(this.lastUploadCGM);
public class GlucoseHistoryRange
public double maxGlucoseRange { get; set; }
public double minGlucoseRange { get; set; }
public double maxGlucoseValue { get; set; }
public List<object>? blocks { get; set; }
public int dateEnd { get; set; }
public int dateStart { get; set; }
public bool noData { get; set; }
public string? dataType { get; set; }
public double avgGlucose { get; set; }
public string? serialNumber { get; set; }
public string? deviceId { get; set; }
public int deviceType { get; set; }
public object? mergeableDevices { get; set; }
public int hypoEvents { get; set; }
public int avgTestsPerDay { get; set; }
public int daysOfData { get; set; }
public GlucoseHistoryRange? data { get; set; }
public DateTime calculatedDateStart => DateTime.UnixEpoch.AddSeconds(this.dateStart);
public DateTime calculatedDateEnd => DateTime.UnixEpoch.AddSeconds(this.dateEnd);
class WebClientWithAutomaticDecompression : System.Net.WebClient
protected override System.Net.WebRequest GetWebRequest(Uri address)
System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)base.GetWebRequest(address);
request.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip;
public static class Extensions
public static void DumpObject(this object? o, string? title, int level)
public static string GetToken(this UserResponse? ur)
return ur?.Data?.AuthTicket?.Token ?? "";