From 6ca8262b513d8e056564b8ef5e493f942fc24f23 Mon Sep 17 00:00:00 2001 From: capybara1 <39344016+capybara1@users.noreply.github.com> Date: Fri, 26 Jun 2020 22:56:54 +0200 Subject: [PATCH] Added first version of CRP Assist app --- apps.json | 25 ++++++++++ apps/cprassist/ChangeLog | 1 + apps/cprassist/README.md | 24 +++++++++ apps/cprassist/cprassist-icon.js | 1 + apps/cprassist/cprassist-icon.png | Bin 0 -> 4037 bytes apps/cprassist/cprassist.js | 79 ++++++++++++++++++++++++++++++ apps/cprassist/settings.js | 64 ++++++++++++++++++++++++ 7 files changed, 194 insertions(+) create mode 100644 apps/cprassist/ChangeLog create mode 100644 apps/cprassist/README.md create mode 100644 apps/cprassist/cprassist-icon.js create mode 100644 apps/cprassist/cprassist-icon.png create mode 100644 apps/cprassist/cprassist.js create mode 100644 apps/cprassist/settings.js 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 0000000000000000000000000000000000000000..c39962aaaf641c1e3d907d9d1855330fbb74c45b GIT binary patch literal 4037 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 rGobwpFvIe`e@6ZeGc1n)06 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); +});