Glucose Broadcasts

Android apps can receive every minute a glucose value from Juggluco.

The glucose broadcast sends every minute the latest “Stream” glucose value. To receive previous glucose values, history values or scans or entered amounts of insulin and carbohydrate etc, you can make use the web server in Juggluco.

Juggluco can imitate xDrip's glucose broadcast com.eveningoutpost.dexdrip.BgEstimate or send a new broadcast: glucodata.Minute.

Apps targeting Android 11 or higher will only receive this broadcast when they have added the following to their AndroidManifest:

 <queries>
    <package android:name="tk.glucodata"/>
 </queries>

The application id of the app should also be selected in Juggluco's left menu->Settings, "xDrip broadcast" or "Glucodata broadcast".

Juggluco
Glucodata Broadcast set to send to mijn.apps.glucosedata in Juggluco settings

See a Java example app or a Kotlin example app receiving the glucodata.Minute broadcast and writing the output to logcat.

In Juggluco and in Kerfstok, rate of change near zero (-0.8<rate<0.8) is displayed as zero, thus showing a horizontal arrow (Steady). This is done to prevent an upward arrow, when in reality the glucose value is falling and the other way around. This threshold is not applied to the rate of change in the exported values, the eXport/xDrip/Nightscout webserver in Juggluco and the glucose broadcasts.

To AndroidManifest.xml you add:

    <application>
[...]
     <receiver android:name="mijn.apps.showglucose.Receiver" android:enabled="true" android:exported="true">
          <intent-filter>
               <action android:name="glucodata.Minute"/>
            </intent-filter>
        </receiver>
    </application>
    <queries>
    <package android:name="tk.glucodata"/>
    </queries>

And you create the class derived from BroadcastReceiver, here called "mijn.apps.showglucose.Receiver", consisting of onReceive that reads the values out of a Bundle.

package mijn.apps.showglucose;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import java.text.DateFormat;
import java.util.Date;

public class Receiver extends BroadcastReceiver {
    private static final String ACTION = "glucodata.Minute";
    private static final String ALARM = "glucodata.Minute.Alarm"; //Alarm indicator, see showalarm  
    private static final String GLUCOSECUSTOM = "glucodata.Minute.glucose"; //Glucose in units of settings
    private static final String LOG_ID = "Receiver";
    private static final String MGDL = "glucodata.Minute.mgdl"; //Glucose in mg/dL
    private static final String RATE = "glucodata.Minute.Rate"; // Rate of change, see label functions
    private static final String SERIAL = "glucodata.Minute.SerialNumber"; //Serial number of sensor
    private static final String TIME = "glucodata.Minute.Time"; //Time in msec
    static DateFormat dateformat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);


    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (!action.equals(ACTION)) {
            Log.e(LOG_ID, "action=" + action + " != " + ACTION);
            return;
        }
        Bundle extras = intent.getExtras();
        String name = extras.getString(SERIAL);
        int mgdl = extras.getInt(MGDL);
        float glucose = extras.getFloat(GLUCOSECUSTOM);
        float rate = extras.getFloat(RATE);
        int alarm = extras.getInt(ALARM);
        long time = extras.getLong(TIME);
        showalarm(alarm);
        System.out.println(name + " glucose=" + glucose + "(mgdL=" + mgdl + ") rate=" + rate + " (libreLabel=" + librelabel(rate) + ", dexcomlabel="+getdexcomlabel(rate)+") time=" + dateformat.format(new Date(time)));
    }

static  String getdexcomlabel(float rate ) {
        if (rate >= 3.5f) return "DoubleUp";
        if (rate >= 2.0f) return "SingleUp";
        if (rate >= 1.0f) return "FortyFiveUp";
        if (rate > -1.0f) return "Flat";
        if (rate > -2.0f) return "FortyFiveDown";
        if (rate > -3.5f) return "SingleDown";
        if(java.lang.Float.isNaN(rate)) return "";
        return "DoubleDown";
    }


static     String librelabel(float rate) {
        if (rate <= -2.0f) {
            return "Falling Quickly".intern();
        }
        if (rate <= -1.0f) {
            return "Falling".intern();
        }
        if (rate <= 1.0f) {
            return "Steady".intern();
        }
        if (rate <= 2.0f) {
            return "Rising".intern();
        }
        if (Float.isNaN(rate)) {
            return "Undetermined".intern();
        }
        return "Rising Quickly".intern();
    }

/*
If alarm&8 is not zero, the alarm goes off. The alarm goes off only once and then waits the number of minutes set with “Minutes deactivated” before going off again.
During the deactivation, alarm&7 still shows whether the glucose value is too high or too low. Whether the glucose value is above the maximally measurable value or below the minimally measurable value is shown independent of whether the alarm is set or not.
*/

    void showalarm(int alarm) {
        if ((alarm & 8) != 0) {
            System.out.print("Alarm ");
        }
        int withoutalarm = alarm & 7;
        switch (withoutalarm) {
            case 4:
                System.out.println("Hightest");
                return;
            case 5:
                System.out.println("Lowest");
                return;
            case 6:
                System.out.println("too high");
                return;
            case 7:
                System.out.println("too low");
                return;
            default:
                return;
        }
    }

}