diff --git a/apps.json b/apps.json index 7c705bc6d..bd8731ff5 100644 --- a/apps.json +++ b/apps.json @@ -2033,5 +2033,30 @@ {"name":"widancs.wid.js","url":"ancs.min.js"}, {"name":"widancs.settings.js","url":"settings.js"} ] + }, + { + "id": "cprassist", + "name":"CPR Assist", + "icon":"cprassist-icon.png", + "version": "0.01", + "readme": "README.md", + "description": "Provides assistance while performing a CPR", + "tags": "tool,firstaid", + "allow_emulator": true, + "storage": [ + { + "name": "cprassist.app.js", + "url": "cprassist.js" + }, + { + "name": "cprassist.img", + "url": "cprassist-icon.js", + "evaluate": true + }, + { + "name": "cprassist.settings.js", + "url": "settings.js" + } + ] } ] diff --git a/apps/cprassist/ChangeLog b/apps/cprassist/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/cprassist/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/cprassist/README.md b/apps/cprassist/README.md new file mode 100644 index 000000000..569f61149 --- /dev/null +++ b/apps/cprassist/README.md @@ -0,0 +1,24 @@ +# CPR Assist + +Provides assistance while performing a CPR + +## Usage + +The app alternates between the phases for +chest compression and rescue breaths in an in an endless loop. +In the chest compression phase the the watch will provide a +buzz at a rate of 100 rpm for 30 repetitions. +A longer buzz introduces an interval of 4 seconds to perform +2 rescue breaths. +A ratio of chest compressions to rescue breaths is also +displayed in the bottom of the screen. + +The number of repetitions for chest compression and +rescue breaths, the rpm rate and the duration of the +rescue breath phase can be adjusted in the settings. +See e.g. [CPR on Wikipedia](https://en.wikipedia.org/wiki/Cardiopulmonary_resuscitation) +for futher information and updates on the recommendations. + +## Attributions + +Icon source: https://commons.wikimedia.org/wiki/File:ISO_7010_E003_-_First_aid_sign.svg diff --git a/apps/cprassist/cprassist-icon.js b/apps/cprassist/cprassist-icon.js new file mode 100644 index 000000000..ec4667a8c --- /dev/null +++ b/apps/cprassist/cprassist-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwhC/AH4Arg93AB1wC/dxkQACi4XRuf/AAU3C/4X/C+sTmYABn4XD+YICmIXJl4TDAA/yC/4X/C+LXXAAdzC4c3BQgX/C/4X0uMiAAUXC6IAKC+wA/AH4AkA==")) diff --git a/apps/cprassist/cprassist-icon.png b/apps/cprassist/cprassist-icon.png new file mode 100644 index 000000000..c39962aaa Binary files /dev/null and b/apps/cprassist/cprassist-icon.png differ diff --git a/apps/cprassist/cprassist.js b/apps/cprassist/cprassist.js new file mode 100644 index 000000000..862ae54d6 --- /dev/null +++ b/apps/cprassist/cprassist.js @@ -0,0 +1,79 @@ +const SETTINGS_FILE = 'cprassist.settings.json'; +const SHORT_BUZZ_PERIOD = 80; +const LONG_BUZZ_PERIOD = 800; + +Bangle.setLCDTimeout(undefined); // do not deaktivate display while running this app + +let settings; + +function setting(key) { + const DEFAULTS = { + 'compression_count': 30, + 'breath_count': 2, + 'compression_rpm': 100, + 'breath_period_sec': 4 + }; + if (!settings) { + const storage = require("Storage"); + settings = storage.readJSON(SETTINGS_FILE, 1) || {}; + } + return (key in settings) + ? settings[key] + : DEFAULTS[key]; +} + +let counter = setting('compression_count'); + +function provideFeedback() { + let period = counter > 0 + ? SHORT_BUZZ_PERIOD + : LONG_BUZZ_PERIOD; + try { + Bangle.buzz(period, 1.0); + } catch(err) { + } +} + +function drawHeart() { + g.fillCircle(40, 92, 12); + g.fillCircle(60, 92, 12); + g.fillPoly([29, 98, 50, 120, 71, 98]); +} + +function updateScreen() { + const colors = [0xFFFF, 0x9492]; + g.reset().clearRect(0, 50, 250, 150); + if (counter > 0) { + g.setFont("Vector", 40).setFontAlign(0, 0); + g.setColor(colors[counter%2]); + drawHeart(); + g.drawString(counter + "", g.getWidth()/2, 100); + } else { + g.setFont("Vector", 20).setFontAlign(0, 0); + g.drawString("RESCUE", g.getWidth()/2, 70); + g.drawString("BREATHS", g.getWidth()/2, 120); + } +} + +function tick() { + provideFeedback(); + updateScreen(); + if (counter == 0) { + var reset = function() { + counter = setting('compression_count'); + clearInterval(interval); + interval = setInterval(tick, 60000/setting('compression_rpm')); + }; + clearInterval(interval); + interval = setInterval(reset, setting('breath_period_sec')*1000); + } + counter -= 1; +} + +interval = setInterval(tick, 60000/setting('compression_rpm')); + +g.clear(1).setFont("6x8"); +g.drawString(setting('compression_count') + ' / ' + setting('breath_count'), 30, 200); + +Bangle.loadWidgets(); +Bangle.drawWidgets(); diff --git a/apps/cprassist/settings.js b/apps/cprassist/settings.js new file mode 100644 index 000000000..5776baa0b --- /dev/null +++ b/apps/cprassist/settings.js @@ -0,0 +1,64 @@ +// This file should contain exactly one function, which shows the app's settings +/** + * @param {function} back Use back() to return to settings menu + */ +(function(back) { + const SETTINGS_FILE = 'cprassist.settings.json'; + + // initialize with default settings... + let s = { + 'compression_count': 30, + 'breath_count': 2, + 'compression_rpm': 100, + 'breath_period_sec': 4 + }; + // ...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 saved = storage.readJSON(SETTINGS_FILE, 1) || {}; + for (const key in saved) { + s[key] = saved[key]; + } + + // creates a function to safe a specific setting + function save(key) { + return function(value) { + s[key] = value; + storage.write(SETTINGS_FILE, s); + }; + } + + const menu = { + '': { 'title': 'CPR Assist' }, + '< Back': back, + 'chest compr.': { + value: s.compression_count, + min: 1, + max: 200, + step: 1, + onchange: save('compression_count'), + }, + 'rescue breaths': { + value: s.breath_count, + min: 0, + max: 100, + step: 1, + onchange: save('breath_count'), + }, + 'rpm': { + value: s.compression_rpm, + min: 1, + max: 200, + step: 10, + onchange: save('compression_rpm'), + }, + 'breaths period': { + value: s.breath_period_sec, + min: 1, + max: 60, + step: 1, + onchange: save('breath_period_sec'), + } + }; + E.showMenu(menu); +});