commit
ba8c1c39cb
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial Medical Information application!
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Medical Information
|
||||
|
||||
This app displays basic medical information, and provides a common way to set up the `medicalinfo.json` file, which other apps can use if required.
|
||||
|
||||
## Medical information JSON file
|
||||
|
||||
When the app is loaded from the app loader, a file named `medicalinfo.json` is loaded along with the javascript etc.
|
||||
The file has the following contents:
|
||||
|
||||
```
|
||||
{
|
||||
"bloodType": "",
|
||||
"height": "",
|
||||
"weight": "",
|
||||
"medicalAlert": [ "" ]
|
||||
}
|
||||
```
|
||||
|
||||
## Medical information editor
|
||||
|
||||
Clicking on the download icon of `Medical Information` in the app loader invokes the editor.
|
||||
The editor downloads and displays the current `medicalinfo.json` file, which can then be edited.
|
||||
The edited `medicalinfo.json` file is uploaded to the Bangle by clicking the `Upload` button.
|
||||
|
||||
## Creator
|
||||
|
||||
James Taylor ([jt-nti](https://github.com/jt-nti))
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwg+7kUiCykCC4MgFykgDIIXUAQgAMiMRiREBC4YABkILBCxEBC4pHCC4kQFxIXEAAgXCGBERif/+QXHl//mIXJj//+YXHn//+IXL/8yCwsjBIIXNABIX/C63d7oDB+czmaPPC7hHR/oWBAAPfC65HRC7qnXX/4XDABAXkIIQAFI5wXXL/5f/L/5fvC9sTC5cxC5IAOC48BCxsQC44wOCxAArA"))
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
const medicalinfo = require('medicalinfo').load();
|
||||
// const medicalinfo = {
|
||||
// bloodType: "O+",
|
||||
// height: "166cm",
|
||||
// weight: "73kg"
|
||||
// };
|
||||
|
||||
function hasAlert(info) {
|
||||
return (Array.isArray(info.medicalAlert)) && (info.medicalAlert[0]);
|
||||
}
|
||||
|
||||
// No space for widgets!
|
||||
// TODO: no padlock widget visible so prevent screen locking?
|
||||
|
||||
g.clear();
|
||||
const bodyFont = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
|
||||
g.setFont(bodyFont);
|
||||
|
||||
const title = hasAlert(medicalinfo) ? "MEDICAL ALERT" : "Medical Information";
|
||||
var lines = [];
|
||||
|
||||
lines = g.wrapString(title, g.getWidth() - 10);
|
||||
var titleCnt = lines.length;
|
||||
if (titleCnt) lines.push(""); // add blank line after title
|
||||
|
||||
if (hasAlert(medicalinfo)) {
|
||||
medicalinfo.medicalAlert.forEach(function (details) {
|
||||
lines = lines.concat(g.wrapString(details, g.getWidth() - 10));
|
||||
});
|
||||
lines.push(""); // add blank line after medical alert
|
||||
}
|
||||
|
||||
if (medicalinfo.bloodType) {
|
||||
lines = lines.concat(g.wrapString("Blood group: " + medicalinfo.bloodType, g.getWidth() - 10));
|
||||
}
|
||||
if (medicalinfo.height) {
|
||||
lines = lines.concat(g.wrapString("Height: " + medicalinfo.height, g.getWidth() - 10));
|
||||
}
|
||||
if (medicalinfo.weight) {
|
||||
lines = lines.concat(g.wrapString("Weight: " + medicalinfo.weight, g.getWidth() - 10));
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
|
||||
// TODO: display instructions for updating medical info if there is none!
|
||||
|
||||
E.showScroller({
|
||||
h: g.getFontHeight(), // height of each menu item in pixels
|
||||
c: lines.length, // number of menu items
|
||||
// a function to draw a menu item
|
||||
draw: function (idx, r) {
|
||||
// FIXME: in 2v13 onwards, clearRect(r) will work fine. There's a bug in 2v12
|
||||
g.setBgColor(idx < titleCnt ? g.theme.bg2 : g.theme.bg).
|
||||
setColor(idx < titleCnt ? g.theme.fg2 : g.theme.fg).
|
||||
clearRect(r.x, r.y, r.x + r.w, r.y + r.h);
|
||||
g.setFont(bodyFont).drawString(lines[idx], r.x, r.y);
|
||||
}
|
||||
});
|
||||
|
||||
// Show launcher when button pressed
|
||||
setWatch(() => load(), process.env.HWVERSION === 2 ? BTN : BTN3, { repeat: false, edge: "falling" });
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 713 B |
|
|
@ -0,0 +1,135 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css" />
|
||||
|
||||
<style type="text/css">
|
||||
.alert {
|
||||
padding: 20px;
|
||||
background-color: #f44336; /* Red */
|
||||
color: white;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="info"></div>
|
||||
|
||||
<button id="btnReload" class="btn btn-primary">Reload from watch</button>
|
||||
<button id="btnUpload" class="btn btn-primary">Upload to watch</button>
|
||||
<button id="btnDownload" class="btn btn-primary">Download</button>
|
||||
|
||||
<pre id="medicalinfo" contenteditable></pre>
|
||||
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
<script>
|
||||
const medicalInfoFile = "medicalinfo.json";
|
||||
|
||||
function errorFormat() {
|
||||
var date = new Date();
|
||||
var error =
|
||||
'<p class="alert">' +
|
||||
date.toUTCString() +
|
||||
" : Wrong format, it should be JSON" +
|
||||
"</p>";
|
||||
return error;
|
||||
}
|
||||
|
||||
function getEditableContent() {
|
||||
return document.getElementById("medicalinfo").innerHTML.replace(/<[^>]*>/g, '');;
|
||||
}
|
||||
|
||||
function isJsonString(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
} catch (e) {
|
||||
console.log(str)
|
||||
console.log(e)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function uploadFile(fileid, contents) {
|
||||
Puck.write(
|
||||
`\x10(function() {
|
||||
require("Storage").write("${fileid}",'${contents}');
|
||||
Bluetooth.print("OK");
|
||||
})()\n`,
|
||||
(ret) => {
|
||||
console.log("uploadFile", ret);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/* Load settings JSON file from the watch.
|
||||
*/
|
||||
function loadMedicalInfo() {
|
||||
document.getElementById("info").innerHTML = "";
|
||||
Util.showModal("Loading...");
|
||||
Puck.eval(`require('Storage').readJSON("${medicalInfoFile}")`, (data) => {
|
||||
document.getElementById("medicalinfo").innerHTML = JSON.stringify(
|
||||
data,
|
||||
null,
|
||||
2
|
||||
);
|
||||
Util.hideModal();
|
||||
});
|
||||
}
|
||||
/* Save settings as a JSON file on the watch.
|
||||
*/
|
||||
function uploadMedicalInfo() {
|
||||
document.getElementById("info").innerHTML = "";
|
||||
Util.showModal("Uploading...");
|
||||
let medicalInfoJson = getEditableContent();
|
||||
if (isJsonString(medicalInfoJson)) {
|
||||
let shortMedicalInfoJson = JSON.stringify(JSON.parse(medicalInfoJson));
|
||||
uploadFile(medicalInfoFile, shortMedicalInfoJson);
|
||||
} else {
|
||||
document.getElementById("info").innerHTML = errorFormat();
|
||||
}
|
||||
Util.hideModal();
|
||||
}
|
||||
|
||||
function downloadMedicalInfo() {
|
||||
document.getElementById("info").innerHTML = "";
|
||||
Util.showModal("Downloading...");
|
||||
let medicalInfoJson = getEditableContent();
|
||||
if (isJsonString(medicalInfoJson)) {
|
||||
var a = document.createElement("a"),
|
||||
file = new Blob([medicalInfoJson], { type: "application/json" });
|
||||
var url = URL.createObjectURL(file);
|
||||
a.href = url;
|
||||
a.download = medicalInfoFile;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 0);
|
||||
} else {
|
||||
document.getElementById("info").innerHTML = errorFormat();
|
||||
}
|
||||
Util.hideModal();
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById("btnUpload")
|
||||
.addEventListener("click", function () {
|
||||
uploadMedicalInfo();
|
||||
});
|
||||
document
|
||||
.getElementById("btnDownload")
|
||||
.addEventListener("click", function () {
|
||||
downloadMedicalInfo();
|
||||
});
|
||||
document
|
||||
.getElementById("btnReload")
|
||||
.addEventListener("click", function () {
|
||||
loadMedicalInfo();
|
||||
});
|
||||
function onInit() {
|
||||
loadMedicalInfo();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
const storage = require('Storage');
|
||||
|
||||
exports.load = function () {
|
||||
const medicalinfo = storage.readJSON('medicalinfo.json') || {
|
||||
bloodType: "",
|
||||
height: "",
|
||||
weight: "",
|
||||
medicalAlert: [""]
|
||||
};
|
||||
|
||||
// Don't return anything unexpected
|
||||
const expectedMedicalinfo = [
|
||||
"bloodType",
|
||||
"height",
|
||||
"weight",
|
||||
"medicalAlert"
|
||||
].filter(key => key in medicalinfo)
|
||||
.reduce((obj, key) => (obj[key] = medicalinfo[key], obj), {});
|
||||
|
||||
return expectedMedicalinfo;
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"bloodType": "",
|
||||
"height": "",
|
||||
"weight": "",
|
||||
"medicalAlert": [ "" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{ "id": "medicalinfo",
|
||||
"name": "Medical Information",
|
||||
"version":"0.01",
|
||||
"description": "Provides 'medicalinfo.json' used by various health apps, as well as a way to edit it from the App Loader",
|
||||
"icon": "app.png",
|
||||
"tags": "health,medical",
|
||||
"type": "app",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"screenshots": [{"url":"screenshot_light.png"}],
|
||||
"interface": "interface.html",
|
||||
"storage": [
|
||||
{"name":"medicalinfo.app.js","url":"app.js"},
|
||||
{"name":"medicalinfo.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"medicalinfo","url":"lib.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"medicalinfo.json","url":"medicalinfo.json"}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
|
|
@ -1 +1,2 @@
|
|||
0.01: Initial Medical Alert Widget!
|
||||
0.02: Use Medical Information app for medical alert text, and to display details
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ Implemented:
|
|||
- Basic medical alert logo and message
|
||||
- Only display bottom widget on clocks
|
||||
- High contrast colours depending on theme
|
||||
- Configure medical alert text (using Medical Information app)
|
||||
- Show details when touched (using Medical Information app)
|
||||
|
||||
Future:
|
||||
|
||||
- Configure when to show bottom widget (always/never/clocks)
|
||||
- Configure medical alert text
|
||||
- Show details when touched
|
||||
|
||||
## Creator
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
{ "id": "widmeda",
|
||||
"name": "Medical Alert Widget",
|
||||
"shortName":"Medical Alert",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "Display a medical alert in the bottom widget section.",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"dependencies" : { "medicalinfo":"app" },
|
||||
"tags": "health,medical,tools,widget",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
|
|
|||
|
|
@ -1,30 +1,47 @@
|
|||
(() => {
|
||||
function getAlertText() {
|
||||
const medicalinfo = require("medicalinfo").load();
|
||||
const alertText = ((Array.isArray(medicalinfo.medicalAlert)) && (medicalinfo.medicalAlert[0])) ? medicalinfo.medicalAlert[0] : "";
|
||||
return (g.wrapString(alertText, g.getWidth()).length === 1) ? alertText : "MEDICAL ALERT";
|
||||
}
|
||||
|
||||
// Top right star of life logo
|
||||
WIDGETS["widmedatr"]={
|
||||
WIDGETS["widmedatr"] = {
|
||||
area: "tr",
|
||||
width: 24,
|
||||
draw: function() {
|
||||
draw: function () {
|
||||
g.reset();
|
||||
g.setColor("#f00");
|
||||
g.drawImage(atob("FhYBAAAAA/AAD8AAPwAc/OD/P8P8/x/z/n+/+P5/wP58A/nwP5/x/v/n/P+P8/w/z/Bz84APwAA/AAD8AAAAAA=="), this.x + 1, this.y + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Bottom medical alert message
|
||||
WIDGETS["widmedabl"]={
|
||||
// Bottom medical alert text
|
||||
WIDGETS["widmedabl"] = {
|
||||
area: "bl",
|
||||
width: Bangle.CLOCK?Bangle.appRect.w:0,
|
||||
draw: function() {
|
||||
width: Bangle.CLOCK ? Bangle.appRect.w : 0,
|
||||
draw: function () {
|
||||
// Only show the widget on clocks
|
||||
if (!Bangle.CLOCK) return;
|
||||
|
||||
g.reset();
|
||||
g.setBgColor(g.theme.dark ? "#fff" : "#f00");
|
||||
g.setColor(g.theme.dark ? "#f00" : "#fff");
|
||||
g.setFont("Vector",18);
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("Vector", 16);
|
||||
g.setFontAlign(0, 0);
|
||||
g.clearRect(this.x, this.y, this.x + this.width - 1, this.y + 23);
|
||||
g.drawString("MEDICAL ALERT", this.width / 2, this.y + ( 23 / 2 ));
|
||||
|
||||
const alertText = getAlertText();
|
||||
g.drawString(alertText, this.width / 2, this.y + (23 / 2));
|
||||
}
|
||||
};
|
||||
|
||||
Bangle.on("touch", (_, c) => {
|
||||
const bl = WIDGETS.widmedabl;
|
||||
const tr = WIDGETS.widmedatr;
|
||||
if ((bl && c.x >= bl.x && c.x < bl.x + bl.width && c.y >= bl.y && c.y <= bl.y + 24)
|
||||
|| (tr && c.x >= tr.x && c.x < tr.x + tr.width && c.y >= tr.y && c.y <= tr.y + 24)) {
|
||||
load("medicalinfo.app.js");
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
Loading…
Reference in New Issue