diff --git a/README.md b/README.md index f2278e5..675d22c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,22 @@ This is the repository holding the menu-based software code, which you may choos See [here](https://github.com/EnhancedRadioDevices/Explorer-HAT) for more details on the Explorer HAT hardware. +You can set your preferred auto-updating status screen using the following setting in your `~/myopenaps/preferences.json`: + +`"status_screen": "bigbgstatus"` will display the big BG status screen (no graph). + +`"status_screen": "off"` will turn the auto-updating screen off. + + +By default, the auto-updating status script will invert the display about 50% of the time, to prevent burn-in on the OLED screen. You can turn this off with the following setting in your `~/myopenaps/preferences.json`: + +`"wearOLEDevenly": "off"` + +Or you can have it invert the display from 8pm to 8am with: + +`"wearOLEDevenly": "nightandday"` + + ## Example screen outputs (Note: these are examples. The latest code may yield different menu items and screen displays) ### Status screen: diff --git a/config/menus/menu.json b/config/menus/menu.json index a0a1d67..11dfac7 100644 --- a/config/menus/menu.json +++ b/config/menus/menu.json @@ -4,8 +4,11 @@ "menu": [ { "label": "Status Graph", - "command": "node scripts/status.js", - "emit": "nothing" + "emit": "showgraphstatus" + }, + { + "label": "Big BG Status", + "emit": "showbigBGstatus" }, { "label": "Set Temp Target", @@ -54,8 +57,7 @@ }, { "label": "Unicorn Logo", - "command": "node scripts/unicorn.js", - "emit": "nothing" + "emit": "showlogo" } ] }, diff --git a/index.js b/index.js index 51b62ed..482e28d 100644 --- a/index.js +++ b/index.js @@ -10,21 +10,22 @@ const i2c = require('i2c-bus'); const path = require('path'); const pngparse = require('pngparse'); const extend = require('extend'); +var fs = require('fs'); var i2cBus = i2c.openSync(1); +var openapsDir = "/root/myopenaps"; //if you're using a nonstandard OpenAPS directory, set that here. NOT RECOMMENDED. + // setup the display var displayConfig = require('./config/display.json'); displayConfig.i2cBus = i2cBus; -var display = require('./lib/display/ssd1306')(displayConfig); -// display the logo -pngparse.parseFile('./static/unicorn.png', function(err, image) { - if(err) - throw err - display.clear(); - display.oled.drawBitmap(image.data); -}); +try { + var display = require('./lib/display/ssd1306')(displayConfig); + displayImage('./static/unicorn.png'); //display logo +} catch (e) { + console.warn("Could not setup display:", e); +} // setup battery voltage monitor var voltageConfig = require('./config/voltage.json') @@ -44,8 +45,36 @@ socketServer .on('warning', (warn) => { console.log('socket-server warning: ', warn.reason) }) +.on('displaystatus', function () { + if (display) { + var preferences; + fs.readFile(openapsDir+'/preferences.json', function (err, data) { + if (err) throw err; + preferences = JSON.parse(data); + if (preferences.status_screen == "bigbgstatus") { + bigBGStatus(display, openapsDir); + } else if (preferences.status_screen == "off") { + //don't auto-update the screen if it's turned off + } else { + graphStatus(display, openapsDir); //default to graph status + } + }); + } +}) +function displayImage(pathToImage) { + pngparse.parseFile(pathToImage, function(err, image) { + if(err) + throw err + display.clear(); + display.oled.drawBitmap(image.data); + }); +} +// load up graphical status scripts +const graphStatus = require('./scripts/status.js'); +const bigBGStatus = require('./scripts/big_bg_status.js'); +// if you want to add your own status display script, it will be easiest to replace one of the above! // setup the menus var buttonsConfig = require('./config/buttons.json'); @@ -64,6 +93,15 @@ var hidMenu = require('./lib/hid-menu/hid-menu')(buttonsConfig, menuConfig); hidMenu .on('nothing', function () { }) +.on('showgraphstatus', function () { + graphStatus(display, openapsDir); +}) +.on('showbigBGstatus', function () { + bigBGStatus(display, openapsDir); +}) +.on('showlogo', function () { + displayImage('./static/unicorn.png'); +}) .on('showvoltage', function () { voltage() .then(function (v) { @@ -85,16 +123,18 @@ hidMenu // display the current menu on the display function showMenu(menu) { - display.clear(); - var text = ''; + if (display) { + display.clear(); + var text = ''; - var p = menu.getParentSelect(); - text += p ? '[' + p.label + ']\n' : ''; - var c = menu.getCurrentSelect(); - menu.getActiveMenu().forEach(function (m) { - text += (m.selected ? '>' : ' ') + m.label + '\n'; - }); + var p = menu.getParentSelect(); + text += p ? '[' + p.label + ']\n' : ''; + var c = menu.getCurrentSelect(); + menu.getActiveMenu().forEach(function (m) { + text += (m.selected ? '>' : ' ') + m.label + '\n'; + }); -// console.log(text); - display.write(text); + // console.log(text); + display.write(text); + } } diff --git a/lib/socket-server/socket-server.js b/lib/socket-server/socket-server.js index dfffc5d..1a9307b 100644 --- a/lib/socket-server/socket-server.js +++ b/lib/socket-server/socket-server.js @@ -91,6 +91,11 @@ module.exports = function (config) { try { switch (cmd.command) { + case 'status': + emitter.emit('displaystatus'); + socketServer.clientWrite(client, 200, 'Success', 'HAT Display Updated'); + break; + case 'read_voltage': require('./commands/read_voltage')(config) .then((response) => { diff --git a/scripts/big_bg_status.js b/scripts/big_bg_status.js new file mode 100644 index 0000000..a426bf8 --- /dev/null +++ b/scripts/big_bg_status.js @@ -0,0 +1,166 @@ +var fs = require('fs'); +var font = require('oled-font-5x7'); + +// Rounds value to 'digits' decimal places +function round(value, digits) +{ + if (! digits) { digits = 0; } + var scale = Math.pow(10, digits); + return Math.round(value * scale) / scale; +} + +function convert_bg(value, profile) +{ + if (profile != null && profile.out_units == "mmol/L") + { + return round(value / 18, 1).toFixed(1); + } + else + { + return Math.round(value); + } +} + +function stripLeadingZero(value) +{ + var re = /^(-)?0+(?=[\.\d])/; + return value.toString().replace( re, '$1'); +} + +module.exports = bigbgstatus; + +// +//Start of status display function +// + +function bigbgstatus(display, openapsDir) { + +display.oled.clearDisplay(true); //clear the buffer + +//Parse all the .json files we need +try { + var profile = JSON.parse(fs.readFileSync(openapsDir+"/settings/profile.json")); +} catch (e) { + // Note: profile.json is optional as it's only needed for mmol conversion for now. Print an error, but not return + console.error("Status screen display error: could not parse profile.json: ", e); +} +try { + var batterylevel = JSON.parse(fs.readFileSync(openapsDir+"/monitor/edison-battery.json")); +} catch (e) { + console.error("Status screen display error: could not parse edison-battery.json: ", e); +} +try { + var suggested = JSON.parse(fs.readFileSync(openapsDir+"/enact/suggested.json")); +} catch (e) { + console.error("Status screen display error: could not parse suggested.json: ", e); +} +try { + var bg = JSON.parse(fs.readFileSync(openapsDir+"/monitor/glucose.json")); +} catch (e) { + console.error("Status screen display error: could not parse glucose.json: ", e); +} +try { + var iob = JSON.parse(fs.readFileSync(openapsDir+"/monitor/iob.json")); +} catch (e) { + console.error("Status screen display error: could not parse iob.json: ", e); +} +try { + var cob = JSON.parse(fs.readFileSync(openapsDir+"/monitor/meal.json")); +} catch (e) { + console.error("Status screen display error: could not parse meal.json: ", e); +} +try { + var stats = fs.statSync("/tmp/pump_loop_success"); +} catch (e) { + console.error("Status screen display error: could not find pump_loop_success"); +} + +if(batterylevel) { + //Process and display battery gauge + display.oled.drawLine(116, 57, 127, 57, 1, false); //top + display.oled.drawLine(116, 63, 127, 63, 1, false); //bottom + display.oled.drawLine(116, 57, 116, 63, 1, false); //left + display.oled.drawLine(127, 57, 127, 63, 1, false); //right + display.oled.drawLine(115, 59, 115, 61, 1, false); //iconify + var batt = Math.round(batterylevel.battery / 10); + display.oled.fillRect(127-batt, 58, batt, 5, 1, false); //fill battery gauge +} + +//calculate timeago for BG +if(bg && profile) { + var startDate = new Date(bg[0].date); + var endDate = new Date(); + var minutes = Math.round(( (endDate.getTime() - startDate.getTime()) / 1000) / 60); + if (bg[0].delta) { + var delta = Math.round(bg[0].delta); + } else if (bg[1] && bg[0].date - bg[1].date > 200000 ) { + var delta = Math.round(bg[0].glucose - bg[1].glucose); + } else if (bg[2] && bg[0].date - bg[2].date > 200000 ) { + var delta = Math.round(bg[0].glucose - bg[2].glucose); + } else if (bg[3] && bg[0].date - bg[3].date > 200000 ) { + var delta = Math.round(bg[0].glucose - bg[3].glucose); + } else { + var delta = 0; + } + + //display BG number, add plus sign if delta is positive + display.oled.setCursor(0,0); + if (delta >= 0) { + display.oled.writeString(font, 3, ""+convert_bg(bg[0].glucose, profile), 1, false, 0, false); + display.oled.writeString(font, 1, "+"+stripLeadingZero(convert_bg(delta, profile)), 1, false, 0, false); + display.oled.writeString(font, 2, " "+minutes+"m", 1, false, 0, false); + } else { + display.oled.writeString(font, 3, ""+convert_bg(bg[0].glucose, profile), 1, false, 0, false); + display.oled.writeString(font, 1, ""+stripLeadingZero(convert_bg(delta, profile)), 1, false, 0, false); + display.oled.writeString(font, 2, " "+minutes+"m", 1, false, 0, false); + } +} + +//calculate timeago for last successful loop +if(stats) { + var date = new Date(stats.mtime); + var hour = date.getHours(); + hour = (hour < 10 ? "0" : "") + hour; + var min = date.getMinutes(); + min = (min < 10 ? "0" : "") + min; + + //display last loop time + display.oled.setCursor(0,57); + display.oled.writeString(font, 1, "Last loop at: "+hour+":"+min, 1, false, 0, false); +} + +//parse and render COB/IOB +if(iob && cob) { + display.oled.setCursor(0,23); + display.oled.writeString(font, 1, "IOB:", 1, false, 0, false); + display.oled.writeString(font, 2, " "+iob[0].iob+'U', 1, false, 0, false); + display.oled.setCursor(0,39); + display.oled.writeString(font, 1, "COB:", 1, false, 0, false); + display.oled.writeString(font, 2, " "+cob.mealCOB+'g', 1, false, 0, false); +} + +display.oled.dimDisplay(true); //dim the display +display.oled.update(); // write buffer to the screen + +fs.readFile(openapsDir+"/preferences.json", function (err, data) { + if (err) throw err; + preferences = JSON.parse(data); + if (preferences.wearOLEDevenly.includes("off")) { + display.oled.invertDisplay(false); + } + else if (preferences.wearOLEDevenly.includes("nightandday") && (hour >= 20 || hour <= 8)) { + display.oled.invertDisplay(false); + } + else if (preferences.wearOLEDevenly.includes("nightandday") && (hour <= 20 && hour >= 8)) { + display.oled.invertDisplay(true); + } + else { + display.oled.invertDisplay((endDate % 2 == 1)); + } +}); + + // +}//End of status display function + // + + diff --git a/scripts/getip.sh b/scripts/getip.sh index 132510e..32eaf3b 100755 --- a/scripts/getip.sh +++ b/scripts/getip.sh @@ -1,5 +1,6 @@ #!/bin/bash -IP=$(ip -f inet -o addr show wlan0|cut -d\ -f 7 | cut -d/ -f 1) -echo -e "Current IP Address:\n$IP\n" -echo -e "To connect: ssh\npi@$IP\n" +wlan0IP=$(ip -f inet -o addr show wlan0|cut -d\ -f 7 | cut -d/ -f 1) +bnep0IP=$(ip -f inet -o addr show bnep0|cut -d\ -f 7 | cut -d/ -f 1) +echo -e "Current WiFi IP:\n$wlan0IP\n" +echo -e "Current BT IP:\n$bnep0IP\n" diff --git a/scripts/status.js b/scripts/status.js index 462ed68..c40693c 100644 --- a/scripts/status.js +++ b/scripts/status.js @@ -1,13 +1,5 @@ - -'use strict'; - -const i2c = require('i2c-bus'); -const path = require('path'); -const extend = require('extend'); -var os = require('os'); var fs = require('fs'); var font = require('oled-font-5x7'); -var i2cBus = i2c.openSync(1); // Rounds value to 'digits' decimal places function round(value, digits) @@ -35,160 +27,225 @@ function stripLeadingZero(value) return value.toString().replace( re, '$1'); } -// setup the display -var displayConfig = require('/root/src/openaps-menu/config/display.json'); -displayConfig.i2cBus = i2cBus; -var display = require('/root/src/openaps-menu/lib/display/ssd1306')(displayConfig); +module.exports = graphicalStatus; + +// +//Start of status display function +// + +function graphicalStatus(display, openapsDir) { + +display.oled.clearDisplay(true); //clear display buffer //Parse all the .json files we need try { - var profile = JSON.parse(fs.readFileSync("/root/myopenaps/settings/profile.json")); + var profile = JSON.parse(fs.readFileSync(openapsDir+"/settings/profile.json")); } catch (e) { - // Note: profile.json is optional as it's only needed for mmol conversion for now. Print an error, but not return - console.error("Could not parse profile.json: ", e); + console.error("Status screen display error: could not parse profile.json: ", e); } try { - var batterylevel = JSON.parse(fs.readFileSync("/root/myopenaps/monitor/edison-battery.json")); + var batterylevel = JSON.parse(fs.readFileSync(openapsDir+"/monitor/edison-battery.json")); } catch (e) { - console.error("Could not parse edison-battery.json: ", e); + console.error("Status screen display error: could not parse edison-battery.json: ", e); } - -if(batterylevel) { - //Process and display battery gauge - display.oled.drawLine(115, 57, 127, 57, 1); //top - display.oled.drawLine(115, 63, 127, 63, 1); //bottom - display.oled.drawLine(115, 57, 115, 63, 1); //left - display.oled.drawLine(127, 57, 127, 63, 1); //right - display.oled.drawLine(114, 59, 114, 61, 1); //iconify - var batt = Math.round(127 - (batterylevel.battery / 10)); - display.oled.fillRect(batt, 58, 126, 62, 1); //fill battery gauge -} - -//Create and render clock -function displayClock() { - var date = new Date(); - var hour = date.getHours(); - hour = (hour < 10 ? "0" : "") + hour; - var min = date.getMinutes(); - min = (min < 10 ? "0" : "") + min; - display.oled.setCursor(83, 57); - display.oled.writeString(font, 1, hour+":"+min, 1, true); -} - -displayClock(); - -//bg graph -display.oled.drawLine(5, 51, 5, 21, 1); -display.oled.drawLine(5, 51, 127, 51, 1); -//targets high and low -display.oled.drawLine(2, 30, 5, 30, 1); -display.oled.drawLine(2, 40, 5, 40, 1); - try { - var suggested = JSON.parse(fs.readFileSync("/root/myopenaps/enact/suggested.json")); + var status = JSON.parse(fs.readFileSync(openapsDir+"/monitor/status.json")); } catch (e) { - return console.error("Could not parse suggested.json: ", e); + console.error("Status screen display error: could not parse status.json: ", e); } try { - var bg = JSON.parse(fs.readFileSync("/root/myopenaps/monitor/glucose.json")); + var suggested = JSON.parse(fs.readFileSync(openapsDir+"/enact/suggested.json")); } catch (e) { - return console.error("Could not parse glucose.json: ", e); -} -//render BG graph -var numBGs = (suggested.predBGs != undefined) ? (72) : (120); //fill the whole graph with BGs if there are no predictions -var date = new Date(); -var zerotime = date.getTime() - ((numBGs * 5) * 600); -var zero_x = numBGs + 5; -for (var i = 0; i < numBGs; i++) { - if (bg[i] != null) { - var x = 2 + zero_x + Math.round(((((bg[i].date - zerotime)/1000)/60)/5)); - var y = Math.round( 21 - ( ( bg[i].glucose - 250 ) / 8 ) ); - //left and right boundaries - if ( x < 5 ) x = 5; - if ( x > 127 ) x = 127; - //upper and lower boundaries - if ( y < 21 ) y = 21; - if ( y > 51 ) y = 51; - display.oled.drawPixel([x, y, 1]); - // if we have multiple data points within 3m, look further back to fill in the graph - if ( bg[i-1] && bg[i-1].date - bg[i].date < 200000 ) { - numBGs++; - } - } + console.error("Status screen display error: could not parse suggested.json: ", e); } - -//render predictions, only if we have them -if (suggested.predBGs != undefined) { - //render line between actual BG and predicted - x = zero_x + 3; - display.oled.drawLine(x, 51, x, 21, 1); - //render predictions - var predictions = [suggested.predBGs.IOB, suggested.predBGs.ZT, suggested.predBGs.UAM, suggested.predBGs.COB]; - for (i = 0; i <= 48; i++) { - x++; - for(var n = 0; n <=3 && (predictions[n] != undefined); n++) { - y = Math.round( 21 - ( (predictions[n][i] - 250 ) / 8) ); - //right boundary - if ( x > 127 ) x = 127; - //upper and lower boundaries - if ( y < 21 ) y = 21; - if ( y > 51 ) y = 51; - display.oled.drawPixel([x, y, 1]); - } - } -} -//calculate timeago for BG -var startDate = new Date(bg[0].date); -var endDate = new Date(); -var minutes = Math.round(( (endDate.getTime() - startDate.getTime()) / 1000) / 60); -if (bg[0].delta) { - var delta = Math.round(bg[0].delta); -} else if (bg[1] && bg[0].date - bg[1].date > 200000 ) { - var delta = Math.round(bg[0].glucose - bg[1].glucose); -} else if (bg[2] && bg[0].date - bg[2].date > 200000 ) { - var delta = Math.round(bg[0].glucose - bg[2].glucose); -} else if (bg[3] && bg[0].date - bg[3].date > 200000 ) { - var delta = Math.round(bg[0].glucose - bg[3].glucose); -} else { - var delta = 0; -} - -//display BG number, add plus sign if delta is positive -display.oled.setCursor(0,57); -if (delta >= 0) { - display.oled.writeString(font, 1, "BG:"+convert_bg(bg[0].glucose, profile)+"+"+stripLeadingZero(convert_bg(delta, profile))+" "+minutes+"m", 1, true); -} else { - display.oled.writeString(font, 1, "BG:"+convert_bg(bg[0].glucose, profile)+""+stripLeadingZero(convert_bg(delta, profile))+" "+minutes+"m", 1, true); +try { + var bg = JSON.parse(fs.readFileSync(openapsDir+"/monitor/glucose.json")); +} catch (e) { + console.error("Status screen display error: could not parse glucose.json: ", e); } - try { - var temp = JSON.parse(fs.readFileSync("/root/myopenaps/monitor/last_temp_basal.json")); + var temp = JSON.parse(fs.readFileSync(openapsDir+"/monitor/last_temp_basal.json")); + var statusStats = fs.statSync(openapsDir+"/monitor/last_temp_basal.json"); } catch (e) { - return console.error("Could not parse last_temp_basal.json: ", e); + console.error("Status screen display error: could not parse last_temp_basal.json: ", e); } - -//calculate timeago for status -var stats = fs.statSync("/root/myopenaps/monitor/last_temp_basal.json"); -startDate = new Date(stats.mtime); -endDate = new Date(); -minutes = Math.round(( (endDate.getTime() - startDate.getTime()) / 1000) / 60); - -//render current temp basal -display.oled.setCursor(0,0); -var tempRate = Math.round(temp.rate*10)/10; -display.oled.writeString(font, 1, "TB: "+temp.duration+'m '+tempRate+'U/h '+'('+minutes+'m ago)', 1); - try { - var iob = JSON.parse(fs.readFileSync("/root/myopenaps/monitor/iob.json")); + var iob = JSON.parse(fs.readFileSync(openapsDir+"/monitor/iob.json")); } catch (e) { - return console.error("Could not parse iob.json: ", e); + console.error("Status screen display error: could not parse iob.json: ", e); } - try { - var cob = JSON.parse(fs.readFileSync("/root/myopenaps/monitor/meal.json")); + var cob = JSON.parse(fs.readFileSync(openapsDir+"/monitor/meal.json")); } catch (e) { - return console.error("Could not parse meal.json: ", e); + console.error("Status screen display error: could not parse meal.json: ", e); } -//parse and render COB/IOB -display.oled.setCursor(0,8); -display.oled.writeString(font, 1, "COB: "+cob.mealCOB+"g IOB: "+iob[0].iob+'U', 1, true); + +//Process and display battery gauge +if(batterylevel) { + display.oled.drawLine(116, 57, 127, 57, 1, false); //top + display.oled.drawLine(116, 63, 127, 63, 1, false); //bottom + display.oled.drawLine(116, 57, 116, 63, 1, false); //left + display.oled.drawLine(127, 57, 127, 63, 1, false); //right + display.oled.drawLine(115, 59, 115, 61, 1, false); //make it look like a battery + var batt = Math.round(batterylevel.battery / 10); + display.oled.fillRect(127-batt, 58, batt, 5, 1, false); //fill battery gauge +} + +//display reason for not looping, and move the graph to make room for the message +var yOffset = 0; //offset for graph, if we need to move it +if (status && suggested) { + var notLoopingReason = suggested.reason; + display.oled.setCursor(0,16); + if (status.suspended == true) { + display.oled.writeString(font, 1, "PUMP SUSPENDED", 1, false, 0, false); + yOffset = 3; + } + else if (status.bolusing == true) { + display.oled.writeString(font, 1, "PUMP BOLUSING", 1, false, 0, false); + yOffset = 3; + } + else if (notLoopingReason.includes("CGM is calibrating")) { + display.oled.writeString(font, 1, "CGM calib./???/noisy", 1, false, 0, false); + yOffset = 3; + } + else if (notLoopingReason.includes("CGM data is unchanged")) { + display.oled.writeString(font, 1, "CGM data unchanged", 1, false, 0, false); + yOffset = 3; + } + else if (notLoopingReason.includes("BG data is too old")) { + display.oled.writeString(font, 1, "BG data too old", 1, false, 0, false); + yOffset = 3; + } +//add more on-screen warnings/messages, maybe some special ones for xdrip-js users? +} + +//display current target(s) +if (profile) { + var targetLow = Math.round( (21+yOffset) - ( ( profile.bg_targets.targets[0].low - 250 ) / 8 ) ); + var targetHigh = Math.round( (21+yOffset) - ( ( profile.bg_targets.targets[0].high - 250 ) / 8 ) ); + display.oled.drawLine(2, targetHigh, 5, targetHigh, 1, false); + display.oled.drawLine(2, targetLow, 5, targetLow, 1, false); +} + +if (bg) { + //render BG graph + var numBGs = (suggested.predBGs != undefined) ? (72) : (120); //fill the whole graph with BGs if there are no predictions + var date = new Date(); + var zerotime = date.getTime() - ((numBGs * 5) * 600); + var zero_x = numBGs + 5; + for (var i = 0; i < numBGs; i++) { + if (bg[i] != null) { + var x = zero_x + Math.round(((((bg[i].date - zerotime)/1000)/60)/5)); + var y = Math.round( (21+yOffset) - ( ( bg[i].glucose - 250 ) / 8 ) ); + //left and right boundaries + if ( x < 5 ) x = 5; + if ( x > 127 ) x = 127; + //upper and lower boundaries + if ( y < (21+yOffset) ) y = (21+yOffset); + if ( y > (51+yOffset) ) y = (51+yOffset); + display.oled.drawPixel([x, y, 1, false]); + // if we have multiple data points within 3m, look further back to fill in the graph + if ( bg[i-1] && bg[i-1].date - bg[i].date < 200000 ) { + numBGs++; + } + } + } + + //calculate timeago for BG + var startDate = new Date(bg[0].date); + var endDate = new Date(); + var minutes = Math.round(( (endDate.getTime() - startDate.getTime()) / 1000) / 60); + if (bg[0].delta) { + var delta = Math.round(bg[0].delta); + } else if (bg[1] && bg[0].date - bg[1].date > 200000 ) { + var delta = Math.round(bg[0].glucose - bg[1].glucose); + } else if (bg[2] && bg[0].date - bg[2].date > 200000 ) { + var delta = Math.round(bg[0].glucose - bg[2].glucose); + } else if (bg[3] && bg[0].date - bg[3].date > 200000 ) { + var delta = Math.round(bg[0].glucose - bg[3].glucose); + } else { + var delta = 0; + } + //display BG number and timeago, add plus sign if delta is positive + display.oled.setCursor(0,57); + if (delta >= 0) { + display.oled.writeString(font, 1, "BG:"+convert_bg(bg[0].glucose, profile)+"+"+stripLeadingZero(convert_bg(delta, profile))+" "+minutes+"m", 1, false, 0, false); + } else { + display.oled.writeString(font, 1, "BG:"+convert_bg(bg[0].glucose, profile)+""+stripLeadingZero(convert_bg(delta, profile))+" "+minutes+"m", 1, false, 0, false); + } +} + +//render predictions on the graph, but only if we have them +if (bg && suggested && suggested.predBGs != undefined) { + //render line between actual BG and predicted + x = zero_x + 1; + display.oled.drawLine(x, 51+yOffset, x, 21+yOffset, 1, false); + //render predictions + var predictions = [suggested.predBGs.IOB, suggested.predBGs.ZT, suggested.predBGs.UAM, suggested.predBGs.COB]; + for (i = 0; i <= 48; i++) { + x++; + for(var n = 0; n <=3 && (predictions[n] != undefined); n++) { + y = Math.round( (21+yOffset) - ( (predictions[n][i] - 250 ) / 8) ); + //right boundary + if ( x > 127 ) x = 127; + //upper and lower boundaries + if ( y < (21+yOffset) ) y = (21+yOffset); + if ( y > (51+yOffset) ) y = (51+yOffset); + display.oled.drawPixel([x, y, 1, false]); + } + } +} + +//display current temp basal and how long ago it was set, on the first line of the screen +if (statusStats && temp) { + startDate = new Date(statusStats.mtime); + endDate = new Date(); + var minutesAgo = Math.round(( (endDate.getTime() - startDate.getTime()) / 1000) / 60); + //display current temp basal + display.oled.setCursor(0,0); + var tempRate = Math.round(temp.rate*10)/10; + display.oled.writeString(font, 1, "TB: "+temp.duration+'m '+tempRate+'U/h '+'('+minutesAgo+'m ago)', 1, false, 0, false); +} + +//display current COB and IOB, on the second line of the screen +if (iob && cob) { + display.oled.setCursor(0,8); + display.oled.writeString(font, 1, "COB: "+cob.mealCOB+"g IOB: "+iob[0].iob+'U', 1, false, 0, false); +} + +//display bg graph axes +display.oled.drawLine(5, 51+yOffset, 5, 21+yOffset, 1, false); +display.oled.drawLine(5, 51+yOffset, 127, 51+yOffset, 1, false); + +//render clock +var clockDate = new Date(); +var clockHour = clockDate.getHours(); +clockHour = (clockHour < 10 ? "0" : "") + clockHour; +var clockMin = clockDate.getMinutes(); +clockMin = (clockMin < 10 ? "0" : "") + clockMin; +display.oled.setCursor(83, 57); +display.oled.writeString(font, 1, clockHour+":"+clockMin, 1, false, 0, false); + +display.oled.dimDisplay(true); //dim the display +display.oled.update(); //write buffer to the screen + +fs.readFile(openapsDir+"/preferences.json", function (err, data) { + if (err) throw err; + preferences = JSON.parse(data); + if (preferences.wearOLEDevenly.includes("off")) { + display.oled.invertDisplay(false); + } + else if (preferences.wearOLEDevenly.includes("nightandday") && (clockHour >= 20 || clockHour <= 8)) { + display.oled.invertDisplay(false); + } + else if (preferences.wearOLEDevenly.includes("nightandday") && (clockHour <= 20 && clockHour >= 8)) { + display.oled.invertDisplay(true); + } + else { + display.oled.invertDisplay((endDate % 2 == 1)); + } +}); + + // +}//End of status display function + // diff --git a/scripts/status.sh b/scripts/status.sh new file mode 100755 index 0000000..3115173 --- /dev/null +++ b/scripts/status.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +command -v socat >/dev/null 2>&1 || { echo >&2 "I require socat but it's not installed. Aborting."; exit 1; } + +RESPONSE=`echo '{"command":"status"}' | socat -,ignoreeof ~/src/openaps-menu/socket-server.sock | sed -n 's/.*"response":\([^}]*\)}/\1/p'` +echo $RESPONSE + +#./getvoltage.sh | sed -n 's/.*"response":\([^}]*\)}/\1/p' diff --git a/scripts/unicorn.js b/scripts/unicorn.js deleted file mode 100644 index 68aed35..0000000 --- a/scripts/unicorn.js +++ /dev/null @@ -1,25 +0,0 @@ - -'use strict'; - - -const i2c = require('i2c-bus'); -const path = require('path'); -const pngparse = require('pngparse'); -const extend = require('extend'); - -var i2cBus = i2c.openSync(1); - -// setup the display -var displayConfig = require('../config/display.json'); -displayConfig.i2cBus = i2cBus; -var display = require('../lib/display/ssd1306')(displayConfig); - -// display the logo -pngparse.parseFile('./static/unicorn.png', function(err, image) { - if(err) - throw err - display.oled.drawBitmap(image.data); -}); - -//dim the display -display.oled.dimDisplay(true);