diff --git a/apps/widbgjs/ChangeLog b/apps/widbgjs/ChangeLog
new file mode 100644
index 000000000..7b83706bf
--- /dev/null
+++ b/apps/widbgjs/ChangeLog
@@ -0,0 +1 @@
+0.01: First release
diff --git a/apps/widbgjs/README.md b/apps/widbgjs/README.md
new file mode 100644
index 000000000..dbb8cd9e4
--- /dev/null
+++ b/apps/widbgjs/README.md
@@ -0,0 +1,29 @@
+# Prerequisites
+For this widget to work and to get data from the phone, you need:
+- An Android phone
+ - with xDrip and the helper app installed.
+ - the Gadgetbridge app (bangle version) for the Android phone
+- A BangleJS
+ - With this widget installed
+
+# Widget
+
+## How to use it
+Make sure you have all the prerequisites from above.
+
+The watch should automatically start displaying values, if there is an arrow visible behind the value, the value is within the not-expired-yet time range changeable in the settings standard is 15 minutes. (I will probably change this in the future, to strike through the text to make expired values clearer).
+
+## Settings
+In the settings, you can:
+- Disable/hide the widget
+- Change the unit from mmol/L to mg/dL
+- Set a time at which old BG values expire
+
+
+# Developer
+Developed by Phil Roggenbuck (phrogg)
+
+
+# Disclaimer
+As well as xdrip you should not use this app to make medical decisions!
+
diff --git a/apps/widbgjs/metadata.json b/apps/widbgjs/metadata.json
new file mode 100644
index 000000000..0639b5a51
--- /dev/null
+++ b/apps/widbgjs/metadata.json
@@ -0,0 +1,22 @@
+{
+ "id": "widbgjs",
+ "name": "Blood Glucose Widget (xdrip)",
+ "shortName":"BG Widget",
+ "icon": "screenshot.png",
+ "screenshots": [{"url":"screenshot.png"}],
+ "version":"0.01",
+ "type": "widget",
+ "supports": ["BANGLEJS", "BANGLEJS2"],
+ "readme": "README.md",
+ "allow_emulator":true,
+ "description": "Displays the current blood glucose received from xdrip and send over via a helper app on the watch.",
+ "tags": "widget",
+ "storage": [
+ {"name":"widbgjs.wid.js","url":"widget.js"},
+ {"name":"widbgjs.settings.js","url":"settings.js"}
+ ],
+ "data": [
+ {"name":"widbgjs.json"},
+ {"name":"widbgjs.settings.json"}
+ ]
+ }
diff --git a/apps/widbgjs/screenshot.png b/apps/widbgjs/screenshot.png
new file mode 100644
index 000000000..b6e092af2
Binary files /dev/null and b/apps/widbgjs/screenshot.png differ
diff --git a/apps/widbgjs/settings.js b/apps/widbgjs/settings.js
new file mode 100644
index 000000000..f2eb190bc
--- /dev/null
+++ b/apps/widbgjs/settings.js
@@ -0,0 +1,54 @@
+(function (back) {
+ const SAVEFILE = "wpbgjs.settings.json";
+
+ // initialize with default settings...
+ let s = {
+ 'unitIsMmol': true,
+ 'expireThreshold': 600000,
+ 'hide': false
+ };
+ // ...and overwrite them with any saved values
+ // This way saved values are preserved if a new version adds more settings
+ const storage = require('Storage');
+ const d = storage.readJSON(SAVEFILE, 1) || {};
+ const saved = d.settings || {};
+ for (const key in saved) {
+ s[key] = saved[key];
+ }
+
+ function save() {
+ d.settings = s;
+ storage.write(SAVEFILE, d);
+ WIDGETS['widbgjs'].draw();
+ }
+
+ E.showMenu({
+ '': { 'title': 'BG widget' },
+ 'Unit': {
+ value: s.unitIsMmol,
+ format: () => (s.unitIsMmol ? 'mmol/L' : 'mg/dL'),
+ onchange: () => {
+ s.unitIsMmol = !s.unitIsMmol;
+ save();
+ },
+ },
+ 'Exp. BG': {
+ value: s.expireThreshold,
+ min: 18000, step: 60000,
+ format: s => (s ? s / 60000 + ' min' : '0'),
+ onchange: (g) => {
+ s.expireThreshold = g;
+ save();
+ },
+ },
+ 'Hide Widget': {
+ value: s.hide,
+ format: () => (s.hide ? 'Yes' : 'No'),
+ onchange: () => {
+ s.hide = !s.hide;
+ save();
+ },
+ },
+ '< Back': back,
+ });
+});
\ No newline at end of file
diff --git a/apps/widbgjs/widget.js b/apps/widbgjs/widget.js
new file mode 100644
index 000000000..1a1df002d
--- /dev/null
+++ b/apps/widbgjs/widget.js
@@ -0,0 +1,143 @@
+//WIDGETS = {}; // <-- for development only
+
+(() => {
+ // persistant vals
+ let storedData;
+ let settings;
+
+ function loadSettings() { // stolen from https://github.com/espruino/BangleApps/blob/master/apps/widpedom/widget.js
+ const d = require('Storage').readJSON("widbgjs.settings.json", 1) || {};
+ settings = Object.assign({
+ 'unitIsMmol': true,
+ 'expireThreshold': 600000,
+ 'reloadInterval': 5 * 60000,
+ 'hide': false
+ }, d || {});
+ return d;
+ }
+
+ function loadVals() {
+ try {
+ const d = require('Storage').readJSON("widbgjs.json", 1) || {};
+ storedData = Object.assign({
+ 'bg': null,
+ 'bgTimeStamp': null,
+ 'bgDirection': null
+ }, d || {});
+ return d;
+ } catch(e) {
+ Bangle.removeFile("widbgjs.json");
+ }
+ return null;
+ }
+
+ function calculateRotation(bgDirection) {
+ var a = 90;
+ // get the arrow right (https://github.com/StephenBlackWasAlreadyTaken/NightWatch/blob/6de1d3775c6e447177c12f387f647628cc8e24ce/mobile/src/main/java/com/dexdrip/stephenblack/nightwatch/Bg.java)
+ switch (bgDirection) {
+ case ("DoubleDown"):
+ g.setColor("#f00");
+ a = 180;
+ break;
+ case ("SingleDown"):
+ a = 180;
+ break;
+ case ("DoubleUp"):
+ g.setColor("#f00");
+ a = 0;
+ break;
+ case ("SingleUp"):
+ a = 0;
+ break;
+ case ("FortyFiveUp"):
+ a = 45;
+ break;
+ case ("FortyFiveDown"):
+ a = 135;
+ break;
+ case ("Flat"):
+ a = 90;
+ break;
+ }
+ // turn the arrow thanks to (https://forum.espruino.com/conversations/344607/)
+ const p180 = Math.PI / 180;
+ // a is defined above
+ var r = 21;
+ var x = r * Math.sin(a * p180);
+ var y = r * Math.cos(a * p180);
+
+ return a * p180;
+ }
+
+ function getBG(bg) {
+ var tmp = null;
+
+ try {
+ if (storedData.bg !== null) {
+ tmp = bg;
+
+ if (settings.unitIsMmol) {
+ tmp /= 18;
+ tmp = tmp.toFixed(1);
+ }
+ }
+
+ } catch (e) { }
+ return tmp;
+ }
+
+ function isBgTooOld(bgTimeStamp) {
+ var currTimeInMilli = new Date().valueOf();
+
+ try {
+ if (bgTimeStamp === null) {
+ return true;
+ }
+
+ if (currTimeInMilli - settings.expireThreshold <= bgTimeStamp) {
+ return false;
+ }
+ } catch (e) { }
+ return true;
+ }
+
+ function draw() {
+ loadSettings();
+ try {
+ if (settings.hide) return;
+ } catch (e) { }
+ loadVals();
+
+ outpt = getBG(storedData.bg);
+
+ if (outpt === null) { // this means no value has been received yet
+ outpt = "BG";
+ bgTimeStamp = "0";
+ }
+
+ // prepare to write on the screen
+ g.reset().clearRect(this.x, this.y, this.x + this.width, this.y + 23); // erase background
+ g.setFont('Vector', 22);
+ g.setColor(g.theme.fg);
+
+ // check if the value is too old
+ if (!isBgTooOld(storedData.bgTimeStamp)) {
+ g.drawImage(atob("FBQBAGAADwAB+AA/wAduAGZgAGAABgAAYAAGAABgAAYAAGAABgAAYAAGAABgAAYAAGAABgA="), this.x + 60, this.y + 9, { rotate: calculateRotation(storedData.bgDirection) });
+ }
+ g.setColor(g.theme.fg).drawString(outpt, this.x + 5, this.y);
+ }
+
+ setInterval(function () {
+ WIDGETS["widbgjs"].draw(WIDGETS["widbgjs"]);
+ }, 5 * 60000); // update every 5 minutes (%* 60000
+
+
+ // add your widget
+ WIDGETS["widbgjs"] = {
+ area: "tl",
+ width: 72,
+ draw: draw
+ };
+})();
+
+//Bangle.drawWidgets(); // <-- for development only