From 401fa113ce628075b72c0de87b71045f2961d2cb Mon Sep 17 00:00:00 2001 From: LucienStorm Date: Wed, 1 Oct 2025 21:28:20 -0500 Subject: [PATCH] Add files via upload --- apps/BMOface/ChangeLog | 6 + apps/BMOface/README.md | 21 ++ apps/BMOface/app-icon.js | 1 + apps/BMOface/app.js | 427 +++++++++++++++++++++++++++++++++++ apps/BMOface/metadata.json | 23 ++ apps/BMOface/screenshot1.png | Bin 0 -> 5579 bytes apps/BMOface/screenshot2.png | Bin 0 -> 4259 bytes apps/BMOface/screenshot3.png | Bin 0 -> 7377 bytes apps/BMOface/screenshot4.png | Bin 0 -> 3893 bytes apps/BMOface/settings.js | 37 +++ 10 files changed, 515 insertions(+) create mode 100644 apps/BMOface/ChangeLog create mode 100644 apps/BMOface/README.md create mode 100644 apps/BMOface/app-icon.js create mode 100644 apps/BMOface/app.js create mode 100644 apps/BMOface/metadata.json create mode 100644 apps/BMOface/screenshot1.png create mode 100644 apps/BMOface/screenshot2.png create mode 100644 apps/BMOface/screenshot3.png create mode 100644 apps/BMOface/screenshot4.png create mode 100644 apps/BMOface/settings.js diff --git a/apps/BMOface/ChangeLog b/apps/BMOface/ChangeLog new file mode 100644 index 0000000000..c0a67da148 --- /dev/null +++ b/apps/BMOface/ChangeLog @@ -0,0 +1,6 @@ +0.01: Initial release with BMO character +0.02: Added Finn and Jake characters +0.03: Added settings menu for character selection +0.04: Added temperature unit toggle (C/F) +0.05: Fixed settings menu crash, added charging status indicators +0.06: Fixed "Invalid Settings!" error with proper settings file handling \ No newline at end of file diff --git a/apps/BMOface/README.md b/apps/BMOface/README.md new file mode 100644 index 0000000000..3da7d765c3 --- /dev/null +++ b/apps/BMOface/README.md @@ -0,0 +1,21 @@ +BMO Face (Cartoon Face) + +A playful Bangle.js watchface inspired by BMO. Shows time at the top center, temperature (upper-left), heart rate (above steps), and steps (bottom-right). When the watch locks, the face goes to sleep with a light gray background and a -_- expression. + +Features +- Time centered at top using `7x11Numeric7Seg` +- Temperature upper-left +- Steps bottom-right, heart rate just above +- Locked mode: LCD-like gray with `-_-` sleeping face +- Widgets hidden by default; swipe to reveal + +Testing lock state +Use in emulator console: +```javascript +Bangle.setLocked(true); +Bangle.setLocked(false); +``` + +Attribution +Character inspiration: BMO from Adventure Time + diff --git a/apps/BMOface/app-icon.js b/apps/BMOface/app-icon.js new file mode 100644 index 0000000000..40971b3052 --- /dev/null +++ b/apps/BMOface/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("2GwgNVACmqACgr/Ff4rKqkAioGIgArI1EAlQHEgEKAYQrTqlViIrH0WqwQEBlWoGAIIB1GqkUqFY1BqtFEgMVEwIICAgMUFakoK44rCoArILYIrJ0EqFY4FBFaZXLFZKDLFY4ADFaIADV6aDLV5KDLFaWogAgB1WAgQKEFZNUgAgBqtAgIKEFb4ABK4ImDK5oABQYQmBQZwrKqsAbZArCbYIrF1UAbZArCoArHAAQrSAAQr/FZavHFZqvIFb9UgD8BABQpG1EAEwIAKFbgAOEJYr40ECExcAFY1AgIlKqCMEDAWADwgAG0EAhQrFgEAFZVAgEFFYoVBa5Q4BMobWDa5YrH1AVBJQYrcBgKRDFf4r/FdLbH1QVBlQrRJQQqJFZIeBFROq0BkEDwZ1DAA9QMggeDJIYAIHAgmKABQmLABIr/Ff4r/FcUAgAECoAEDFcFUFYMBqtQAgMFFZv+Aom/FZtQv/4FAMAg//worN/4AFFZtAv4VFwIrjh4UE/iIBV5u/FRYrHQIUfAYTcHbbYrDAAgr/Ff4ACbZ4r/qArGgoriqgrqqorGiorjWAquHFbosFBg4reQwSAHFcQALFf4r/Ff4r/FfuKkEqFdEg1BXboEEgArSwEIgArCotUDoMFK6GK1AdBhRXQFYIBBqIrSAIOiV4coFZ9UFahSKFckCQZ1AigrDgIwKV4koEwQvB0ArMihXEAYNQFZkoK4gDBxArLolEFY1EFZeIxArF0CDMoNQFYNRFYVQQZmC0ArB0QrCxCvPgkFFYVEFY1QiivGhEKFYS0DAAeglArDAA1UcAgACoqLCJRCHBFY2KW4QrJWgYrwgi9BUZMCwEqFYsIXoLPJgNAioHEolEoorKAA+IxGKFZQAHoNEGYIrRwWIL4IrRV4JXTV4JXTFYKvLFZKvLAA9QijkBFaOglGoFaQADFaIADFf4r/Fd4A==")) \ No newline at end of file diff --git a/apps/BMOface/app.js b/apps/BMOface/app.js new file mode 100644 index 0000000000..072649a3de --- /dev/null +++ b/apps/BMOface/app.js @@ -0,0 +1,427 @@ +const storage = require('Storage'); + +require("Font6x12").add(Graphics); +require("Font8x12").add(Graphics); +require("Font7x11Numeric7Seg").add(Graphics); + +function bigThenSmall(big, small, x, y) { + g.setFont("7x11Numeric7Seg", 2); + g.drawString(big, x, y); + x += g.stringWidth(big); + g.setFont("8x12"); + g.drawString(small, x, y); +} + +function getBackgroundImage() { + // Cartoon face background - we'll create this + return null; // Placeholder for now +} + +function drawSmileShape(x, y, width, height, thickness) { + // New approach: stamp small circles along an ellipse arc to get + // naturally rounded ends (no polygons changed elsewhere) + var startAngle = Math.PI / 5; + var endAngle = (4 * Math.PI) / 5; + var step = Math.PI / 40; // small, keeps change minimal + var rx = width/1.57; // match previous horizontal scale + var ry = height/2; + var r = Math.max(1, thickness/2); + for (var a=startAngle; a<=endAngle; a+=step) { + var px = x + rx * Math.cos(a); + var py = y + ry * Math.sin(a); + g.fillCircle(px, py, r); + } +} + +function drawLightningBolt(x, y, width, height) { + // Draw lightning bolt using two opposing acute triangles + // x, y = center point + // width = how wide the bolt is + // height = how tall the bolt is + g.setColor(0x000000); + + var halfWidth = width / 2.5; + var halfHeight = height / 1; + + // Upper triangle (pointing down-right) + var upperTriangle = [ + x, y - halfHeight, // Top center point + x + halfWidth, y, // Right middle point + x - halfWidth/2, y + halfHeight/2 // Left lower point + ]; + g.fillPoly(upperTriangle); + + // Lower triangle (pointing up-left) + var lowerTriangle = [ + x, y + halfHeight, // Bottom center point + x - halfWidth, y, // Left middle point + x + halfWidth/2, y - halfHeight/2 // Right upper point + ]; + g.fillPoly(lowerTriangle); +} + +function drawFinnFace() { + var isCharging = Bangle.isCharging(); + + // White squared bottom for hood (behind everything) + g.setColor(0xFFFFFF); // White + g.fillRect(2, 100, 168, 180); // Squared hood bottom (x1, y1, x2, y2) + + // White hood ears + g.setColor(0xFFFFFF); + g.fillCircle(30, 20, 17); // Left ear (x, y, radius) + g.fillCircle(140, 20, 17); // Right ear (x, y, radius) + + // White hood behind face + g.setColor(0xFFFFFF); // White + g.fillCircle(85, 82, 85); // Hood circle (x, y, radius) + + // Finn's face (flesh colored circle) + g.setColor(0.95, 0.8, 0.7); // Flesh color + g.fillCircle(85, 80, 70); // Face circle (x, y, radius) + + // Outlines + g.setColor(0x000000); + g.drawCircle(85, 82, 70); // Face outline + g.drawCircle(85, 85, 85); // Hood outline + + if (isCharging) { + // Lightning bolt eyes when charging + drawLightningBolt(32, 55, 12, 20); // Left lightning bolt + drawLightningBolt(139, 55, 12, 20); // Right lightning bolt + } else { + // Normal circular eyes + g.setColor(0x000000); + g.fillCircle(32, 55, 10); // Left eye (x, y, radius) + g.fillCircle(139, 55, 10); // Right eye (x, y, radius) + } + + // Curved smile using arc + g.setColor(0x000000); + // Draw curved smile: center at (85, 100), radius 20, from 0.2*PI to 0.8*PI + var smilePoints = []; + for (var angle = 0.2 * Math.PI; angle <= 0.8 * Math.PI; angle += 0.1) { + var x = 85 + 20 * Math.cos(angle); + var y = 100 + 20 * Math.sin(angle); + smilePoints.push(x, y); + } + g.drawPoly(smilePoints); +} + +function drawBMOFace() { + var isCharging = Bangle.isCharging(); + + if (isCharging) { + // Lightning bolt eyes when charging + drawLightningBolt(32, 55, 12, 20); // Left lightning bolt (x, y, width, height) + drawLightningBolt(139, 55, 12, 20); // Right lightning bolt (x, y, width, height) + } else { + // Normal circular eyes + g.setColor(0x000000); + g.fillCircle(32, 55, 10); // Left eye - moved up and left + g.fillCircle(139, 55, 10); // Right eye - moved up and left + } + + // BMO mouth structure - all elements follow the same calculated curve + // Black mouth outline + g.setColor(0x424242); + drawSmileShape(85, 86, 40, 20, 29); // Black smile outline + + // Inside of mouth (dark green) + g.setColor(0x225c27); // Dark green + drawSmileShape(85, 85, 43, 20, 20); // Dark green inside smile + + // Tongue (medium green) + g.setColor(0x474747); // Medium green + drawSmileShape(85, 99, 40, 10, 6); // Green tongue smile + + // Curved white tooth line (smile) + g.setColor(0xFFFFFF); + drawSmileShape(85, 80, 50, 12, 4); // White tooth line smile +} + +function drawJakeFace() { + var isCharging = Bangle.isCharging(); + + // Black circles behind Jake's eyes + g.setColor(0x000000); + g.fillCircle(45, 63, 30); // Left black eye background (x, y, radius) + g.fillCircle(115, 63, 30); // Right black eye background (x, y, radius) + + // Jake's white eyes on top of black circles + g.setColor(0xFFFFFF); // White + g.fillCircle(50, 60, 25); // Left eye (x, y, radius) + g.fillCircle(120, 60, 25); // Right eye (x, y, radius) + + // Eye outlines + g.setColor(0x000000); + g.drawCircle(50, 60, 25); // Left eye outline + g.drawCircle(120, 60, 25); // Right eye outline + + if (isCharging) { + // Lightning bolt eyes when charging (inside the white circles) + drawLightningBolt(50, 60, 8, 15); // Left lightning bolt (x, y, width, height) + drawLightningBolt(120, 60, 8, 15); // Right lightning bolt (x, y, width, height) + } + + // Jake's jowls - horizontal pointed oval (like an eye shape) + g.setColor(0xFFFF00); // Yellow + g.fillEllipse(130, 120, 45, 65); // Main jowl oval (center x, center y, width, height) + + // Jowl outline + g.setColor(0x000000); + g.drawEllipse(130, 120, 45, 65); // Main jowl outline (center x, center y, width, height) + g.drawEllipse(45, 130, 70, 77); // Left droop outline + g.drawEllipse(105, 130, 130, 77); // Right droop outline + + g.setColor(0xFFFF00); + g.fillEllipse(47, 125, 68, 75); // Inner left droop oval (center x, center y, width, height) + g.fillEllipse(107, 125, 128, 75); // Inner right droop oval (center x, center y, width, height) + + // Black horizontal oval nose + g.setColor(0x000000); + g.fillEllipse(105, 105, 68, 80); // Nose oval (center x, center y, width, height) +} + +function drawCartoonFace() { + var settings = require("Storage").readJSON("bmoface.settings.json", 1) || {}; + var character = settings.character || "BMO"; + + if (character === "Finn") { + drawFinnFace(); + } else if (character === "Jake") { + drawJakeFace(); + } else { + drawBMOFace(); // Default BMO face + } +} + +// schedule a draw for the next minute +var drawTimeout; +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + +function clearIntervals() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; +} + +function drawClock() { + g.setFont("7x11Numeric7Seg", 3); + g.setColor(0, 0, 0); // Black text directly on green background + // Top-center time + var t = require("locale").time(new Date(), 1); + var tx = (g.getWidth() - g.stringWidth(t)) / 2; + g.drawString(t, tx, 8); + g.setFont("8x12", 2); + g.drawString(require("locale").dow(new Date(), 2).toUpperCase(), 18, 130); + g.setFont("8x12"); + g.drawString(require("locale").month(new Date(), 2).toUpperCase(), 80, 126); + g.setFont("8x12", 2); + const time = new Date().getDate(); + g.drawString(time < 10 ? "0" + time : time, 78, 137); +} + +function drawBattery() { + bigThenSmall(E.getBattery(), "%", 146, 8); +} + +function getTemperature(){ + try { + var temperature = E.getTemperature(); + var settings = require("Storage").readJSON("bmoface.settings.json", 1) || {}; + var useFahrenheit = settings.tempUnit === "F"; + + if (useFahrenheit) { + temperature = (temperature * 9/5) + 32; + return Math.round(temperature) + "F"; + } else { + var formatted = require("locale").temp(temperature).replace(/[^\d-]/g, ''); + return formatted; + } + } catch(ex) { + print(ex) + return "?" + } +} + +function getSteps() { + var steps = Bangle.getHealthStatus("day").steps; + steps = Math.round(steps/1000); + return steps + "k"; +} + +function drawBorders() { + // Top border - thin dark teal/green line + g.setColor(0.1, 0.4, 0.3); // Dark teal/green + g.fillRect(0, 0, g.getWidth(), 6); + + // Bottom border - thicker bar (no progress indicator) + g.fillRect(0, g.getHeight() - 8, g.getWidth(), g.getHeight()); +} + +function draw() { + queueDraw(); + + // Clear to character-appropriate background + g.clear(); + var settings = require("Storage").readJSON("bmoface.settings.json", 1) || {}; + var character = settings.character || "BMO"; + + if (character === "Finn") { + g.setColor(0.5, 0.7, 1.0); // Light blue for Finn + } else if (character === "Jake") { + g.setColor(1.0, 1.0, 0.0); // Yellow for Jake + } else { + g.setColor(0.35, 0.78, 0.45); // Light green for BMO + } + g.fillRect(0, 0, g.getWidth(), g.getHeight()); + + // Draw borders (only for BMO, not Finn or Jake) + if (character === "BMO") { + drawBorders(); + } + + // Draw cartoon face + drawCartoonFace(); + + // Draw AdvCasio information like the original + g.setColor(0x000000); // Black text + + g.setFontAlign(-1,-1); + g.setFont("8x12", 2); + // Temperature - upper left + g.drawString(getTemperature(), 6, 6); + + // Steps - bottom right + var stepsStr = getSteps(); + var sx = g.getWidth() - g.stringWidth(stepsStr) - 6; + var sy = g.getHeight() - g.getFontHeight() - 6; + g.drawString(stepsStr, sx, sy); + + // Heart rate just above steps + var hr = Bangle.getHealthStatus().bpm || Bangle.getHealthStatus("last").bpm; + var hrStr = (hr && isFinite(hr)) ? String(Math.round(hr)) : "--"; + var hx = g.getWidth() - g.stringWidth(hrStr) - 6; + var hy = sy - g.getFontHeight() - 2; + g.drawString(hrStr, hx, hy); + + g.setFontAlign(-1,-1); + drawClock(); + drawBattery(); + + // Hide widgets + for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} +} + +// Draw the sleeping overlay version when locked +function drawLockedScreen() { + // Light gray background like LCD + g.clear(); + g.setColor(0.8, 0.8, 0.8); + g.fillRect(0, 0, g.getWidth(), g.getHeight()); + + // Draw borders + drawBorders(); + + var isCharging = Bangle.isCharging(); + + var settings = require("Storage").readJSON("bmoface.settings.json", 1) || {}; + var character = settings.character || "BMO"; + + if (isCharging) { + // Lightning bolt eyes when charging (even when locked) + if (character === "Jake") { + // Jake's lightning bolts in white eye circles + g.setColor(0xFFFFFF); // White eye background + g.fillCircle(50, 60, 25); // Left eye + g.fillCircle(120, 60, 25); // Right eye + g.setColor(0x000000); + g.drawCircle(50, 60, 25); // Left eye outline + g.drawCircle(120, 60, 25); // Right eye outline + drawLightningBolt(50, 60, 8, 15); // Left lightning bolt + drawLightningBolt(120, 60, 8, 15); // Right lightning bolt + } else { + // BMO/Finn lightning bolts + drawLightningBolt(32, 55, 12, 20); // Left lightning bolt (x, y, width, height) + drawLightningBolt(139, 55, 12, 20); // Right lightning bolt (x, y, width, height) + } + } else { + // Sleeping face: horizontal slits + g.setColor(0x000000); + if (character === "Jake") { + // Jake's sleeping eyes in white circles + g.setColor(0xFFFFFF); // White eye background + g.fillCircle(50, 60, 25); // Left eye + g.fillCircle(120, 60, 25); // Right eye + g.setColor(0x000000); + g.drawCircle(50, 60, 25); // Left eye outline + g.drawCircle(120, 60, 25); // Right eye outline + // Horizontal slits inside the white circles + g.fillRect(40, 60, 60, 63); // left slit + g.fillRect(110, 60, 130, 63); // right slit + } else { + // BMO/Finn sleeping eyes + g.fillRect(22, 55, 42, 58); // left slit: y fixed by height of 3 px + g.fillRect(129, 55, 149, 58); // right slit + } + } + + // Mouth slit centered (always horizontal when locked) + g.setColor(0x000000); + g.fillRect(60, 90, 120, 83); + + // Redraw information in black at same positions + g.setColor(0x000000); + g.setFontAlign(-1,-1); + g.setFont("8x12", 2); + g.drawString(getTemperature(), 6, 6); + + var stepsStr = getSteps(); + var sx = g.getWidth() - g.stringWidth(stepsStr) - 6; + var sy = g.getHeight() - g.getFontHeight() - 6; + g.drawString(stepsStr, sx, sy); + + var hr = Bangle.getHealthStatus().bpm || Bangle.getHealthStatus("last").bpm; + var hrStr = (hr && isFinite(hr)) ? String(Math.round(hr)) : "--"; + var hx = g.getWidth() - g.stringWidth(hrStr) - 6; + var hy = sy - g.getFontHeight() - 2; + g.drawString(hrStr, hx, hy); + + g.setFontAlign(-1,-1); + drawClock(); + drawBattery(); + + // Keep widgets hidden + for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} +} + +Bangle.on("lcdPower", (on) => { + if (on) { + draw(); + } else { + clearIntervals(); + } +}); + +Bangle.on("lock", (locked) => { + clearIntervals(); + if (locked) { + drawLockedScreen(); + } else { + draw(); + } +}); + +Bangle.setUI("clock"); + +// Load widgets, but don't show them +Bangle.loadWidgets(); +require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe +g.clear(); +draw(); \ No newline at end of file diff --git a/apps/BMOface/metadata.json b/apps/BMOface/metadata.json new file mode 100644 index 0000000000..0427b2821c --- /dev/null +++ b/apps/BMOface/metadata.json @@ -0,0 +1,23 @@ +{ + "id": "bmoface", + "name": "BMO Face", + "shortName": "BMO", + "version": "1.0.6", + "description": "A watch face inspired by BMO that shows time, temp, steps and HR. Sleeps to -_- when locked.", + "icon": "app.png", + "tags": "clock", + "type": "clock", + "supports": ["BANGLEJS", "BANGLEJS2"], + "allow_emulator": true, + "readme": "README.md", + "screenshots": [ + {"url": "screenshot1.png"}, + {"url": "screenshot2.png"}, + {"url": "screenshot3.png"}, + {"url": "screenshot4.png"} + ], + "storage": [ + { "name": "bmoface.app.js", "url": "app.js" }, + { "name": "bmoface.settings.js", "url": "settings.js" } + ] +} \ No newline at end of file diff --git a/apps/BMOface/screenshot1.png b/apps/BMOface/screenshot1.png new file mode 100644 index 0000000000000000000000000000000000000000..b12ff07ad2104530f9338ac0d1ad755d7512f230 GIT binary patch literal 5579 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 Dl5u7? literal 0 HcmV?d00001 diff --git a/apps/BMOface/screenshot2.png b/apps/BMOface/screenshot2.png new file mode 100644 index 0000000000000000000000000000000000000000..5834256113ee3b5ab3ff54662d1ef2aaf6843b25 GIT binary patch literal 4259 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 literal 0 HcmV?d00001 diff --git a/apps/BMOface/screenshot3.png b/apps/BMOface/screenshot3.png new file mode 100644 index 0000000000000000000000000000000000000000..0cc3870a380aeb86a050a50ddec1c52e9169f517 GIT binary patch literal 7377 zcmZu$cQ~7G)JBZfs17Suy7Z@26tzdtp{-FJYKtADM5#?8I!R#g$A`^L{yF1 zHDXm_ZxsX~l6>j+{rmm#j_ZBi=REhh&pG#Vz2|*vZe}FFE6&Tt#wKw0&drCcYx%$9 z7&q&TshxLVV>`)q_ol()VD|N__z&N&oPHV7Pbcm82!E2gE-tK;9RA{+`O$Mx(;*6V zVV#ja$lTtsyawMhOr8JYun+}IF6sHT(Hz61Zz3-zHqt5m&VzFiyeFFVqm~Lhwo*4A zPG^ENo)&VsKyJersz?2dhx}{hLO-`Vn+HdqB<;KXk&D~|E83#!I|AGhFoBFo4#|Re zx_H^PC|MpjMhtS-h|yE%t}Pf1v52(Bf8!dIqcX z<-kjaLVQ~bb=)|hz#uD)e%!h-4nITNt2s;z8u;I<9^99(o!|%4#w%5ioKmQsNS`gA z_}Wr`!ndL$)>;yAS*wA@$0IJBx4I#4ANXU#js3}sz{~sbbghpw&^+6)3Z@S9X1wY! z*%%qh_*`1svj&_aXz6w|KpiDW4W(1-mM}I=-?bYE8Rmxnu3r8+eI&QA)|Zka?o&!h-gY{EMO{$mfqpAX&zotJ$ixhXHZwg0rNsAY`?+HaXfI6 zZ$7T%9^A84C0B_pch@Owoj)k{>QjtBzke7^Fp{WG ze{oG_m*$wxpWUva<2wKVrK1EV=$mge3xn002JD#n>?sz%!5qx}O18UE2oUK=`%TXLZPnXOQ9Cuk)UMjhNa$EXisz9GLOqH>dAR z%vi<(Q7=7E$9MNFFNDfz4>xSYN3hkFRoq_R2x-H+3VtaZEn9ybihwn00zPMf&Kevi z+NPZjS}Z%+^}+la=-bPn>Q`+Y`Z}*UtpuRGjnyjw zH;HNc(jKYxpt-QI6jak#4%fYD;_73Fr#FXa8I%$l@H>U^W4sKjijh9lv)Ow4)Kh`(!j*(taJEQA1%y7nFF}hN1#r#(pwhsT$`0%|^^ygYM>spH>+)Mbq zJyqmBc(d!>qI-t6MRoQ&gp4w*{pc=bcWXvGpURW*V5x7yD%S(ehK*8N0N51tlkgu^ z%&T~lM269bdxkm2_Zp~5Krz=kwb|Btx}awtd_sgAIu90Jo z+NK9lfj_1*xxcA8OId~f6^DCF{&7^R2l@X7M}ZPi`JC{l)2Cz51fJ)yZ4klwvS%+Y zsjNFE9)7K*ATKVK$sZ85Vx+pHcivB^^yKVyq4?KNTlKl!A5c~(ra*+3tX=*t$toZP z{2{JkR5Vb`{Euv1UVHu4BK*CIjZ z(|8ukNwL{GCkKykDIjiL7uy>hh1*3++w?qG);G9@k%+|OsZq@k&M z{QPnTgjwz_SJiWG9!Y7dL)@xATl!u9y^4wb znR_%>k=)jJwl6ufx^5)a+dEU8Li&I*KrPByZqDmV+_`@%G{j_i^luzaO46;X(MG*J zZntiC|B&$3?z}?H)GV*G1#Accrm=X`MCF5rr&cUR71km z_QlvD@)q~&nrW`nDbD{L)J!zDFT?W}K4s_MmJQc0=|~2>Ytq-hQei#I4c| zE4VVo**iZy^g2*Q$PwoL)(T<#pWJt65HTkut3)QEg5rrVccX(H;S>NnhgVsKNWdgGC0m_O;I0`3qxP#IwTo9A>AyZe{?S$*{<6rJ!;u=UrHs9N!^ zUNIzU55q@xfj;>Q8Rsucs+|O|l)Fx>aS#$<8wY7S3+_G>in2XaYX12z@#Pt1TYxna zgQp4agJ?IEqGK)Mx}9wnehl*|x;c{0N{DkQaAV#+zvc9+^msg)@#|z6-K60E{7GY& zd|7?^t2V)M7z@?tuc!(5EgO%v9Tyzx>(p z>)%#VFg}Ar6X{-v{#9sE$;2S$rhD%{FhHzUOX1>r{MH!_KHNZ7VoiXgV)5ErmTET% zj2&yVS+=mXz^?D!y*XNwcwd zG}q_^uZ>{qiyO;~M{l>#Vn3lFhKjz%;KlSh8Il&Rtyz^=-glMl2J#oP1cL=eA0oi# z0h1?1gE@1xdjewiNW z=e`Ub^dI@TWq!uC7Cxf@q&kgimOuK${%%^hBrBzFX!UzGe1qq}nm)j%!%8T<^<(^u zWuA5_S2mX;nG*?MgmRzAnmHA&GeLAmFS$NS5`o9Qx zt3(&*#_DE)TVG0tR#qdl?rK9vPyi8@ec5PKyVY}Cv{6qnFRSfR^P*YU!a~n@%x7ia zjN|d+qnv|WD-9b3H3pZ}S}&!I&+STYJ}W+tWlhGF`ZXe&96lUnnCWRZ%Gh3+H~f1s z0>_#9E$h`IX8b)nDDAV#HCbx7eU()=x@>K`YkpcIl1Rx++L)NpZG~C`b38WLnP_1_sH0-wR~`ObjRJ6a-TdT{d8t`%rXF4A z9yGI?l)(Aq~!LyTwMa$_<;htQtbKbX9JMhLOQ`~ zBiDK%_|@Mfxuy2yNxl^cx)R?HjgDc)pCnYPWH6^AzvL}Q**!un{~Zs0t6QckvBb#- zaul8}U?^#s#|jLh0@H6T2sGT=G}mol8`N*R4kep&ziyvSHMm>vn4I*1Yp4%!)LD|# z*389Z*UsNY1~S%o?`V||Zx0)+r?{1z4zUN4rn7BVgl|0tx@>3w%Sv!>ROpxn_6|rX0!bQ*juVpkOHi%9tWo0PC!Rz6Od@l$w8x ztCNdHh(+siE7dF?L0PESXQMFC&J6$h!pE7NeAz3&JEI%gvFqIw7Dw|GJu9pF5gFJO z%51jTwSK(|Hy_kJw`_B&uO}%2XeAz~6-b*8=|S3B9AazR0xXn<*d5Hh?LH*?+TW^d zW?iE0S$x4FdWHaqF#fY)aS6wRtnE>u3P~mhHAtqbEDs%ASEdC&of7F&zP5Y@e~wgc zg_W|oLIFtBJ+X*_J3hM)2MZAp)$9IewH0jceD_J{Na;COZ;`F+ySnS~+eD`76gHxw zixtu9EVK`yVA)+ERz!cE*|VSjUax6d`}z@gjbr*O+PQ#daoWGR!sJj;kKQC_Qb?K- zOvdo-tZ1z%_Pa$dbW%2eVw9CrX=ou%-EfAw=qN zD5N8%*P-=DAUw_Y(k6Nah1!c^^o{p=oj`UT4^2l5n0Ld(AuTeC{C^C^w8&J-$;ArP zxWH$XA@I67)XSGDJX$sUB*@EZc9&n0K=rEA9dMvVnXLQ1|GKHAC?Bv;or&Wx?g5HP zTT{AtXFFF}?A_)LtotQ=O3<{3c-vA&b5~f;+E< zXR3>i;1`Z_!z{d3?O_d*D z{?6oKk@qlPrGaBUtZ@f1U|q1mZs#?>DB=tcONeqo7Y7d+Ufj~m)mmD>b0l3c(r)1J z(2}PC)4nD5)1h5#M}`=BkYBDkV#cmk_z$|A(?96V``!og3`Ga8lC_?-je4YRWQ5Gq zKK-zB@sJCWPcC~Oo~s52EC$qJk(VQPm_73ATdBTbADgZzZ=F9ONk~Ya&915U4a!#y zU}NJC{nrcl^mwj_&1`p0Zid0KQZy5tl1IMr_qs)LlFGLguXRy;tb{rupge1VHY2K; z-){?bXc(a*SWG%;^ZaAdk;P;(XmGdVa?6ax9Fn1c>TtFskWi@v_z zMuz#Pe5h$>ULU=A_(|v{AQtu-w1|*xTAcV>ssg8^qOvr<&!)6QsOl&W{9`KyAPcP0 zC@xd8&j~36E0Ea5{9a2I~y+^#Ttu z&~F*yST8^1bD(Y?X3~&@5++VleQYlxd7?||{QMmQTOmx{KMxkDvV9tm7h*!jp~*Jm zIjW0rN29%vRRICT<*%qc!+Xdn!Ii@~YL&$3H74)O8yv3ZMT=!yFDvP(A_@+5kG1ur z%wS{k09rq%$8pgt?E!~7@jz76-o{TOm({lLN+ywy5tNZT3oR=fBS!{2mt)MF7 z9aP{mzmD5LY%F~tF4=x*c)DFbY~~>sR(=aQc3MSOw1>%Z0Z(!8Pf8h{){?n>Q0))ETSp#t(g@pDc)1COcRZBAN|_Y80wf z%2)`Yz`Aa)^O=?96Ue`l{aUeu*8cn|t6R*S9?LM2OsI6Ur(?hd6pVRxGynkKXk~A z@zO=vuW&v$i)FTJ;nNW$yv@~Sc$Xa{tKte%Uzlx#YI&d}F?wW!X?Grdhm)2`Oo38?g|i}y`;*GXZkYWUj*C^mvxOfT zGS3`mx0Wt>Q9_le>`n%A%Tv#Vc8bt$`cB#s?u-W!1E)wm_`B}}0I}$yH36LBZCac4 zxs!Lu^~2kQWW%j56u&uJB?)9x)apaU<#laSrUQ44uHNK+^D?&u>b7FL=K7>?}>v5y;G#5qq!T2w?WM%m#141w1rX{U(Xf23Hx}#i{#*$5R|~B_z`@b);7t_AHC23owmliJ^E&OW{C${(AZrR5n^r;ht>>p z34eR8N|O0ePm+%~)^+OTPWFkQ1~-BG5rAgplb!0TsV2HS{vl`Y12TCC50VuZoEhF3 zWe;S+EWpi5xS#Ls5|DR5eOw>T3?!bO)H@FYb_ez2Gap-W`0g~_TEp30TO_@5GAR|M z%;tpMS+&RaI8+)MB%AaFj#p@M|f9~;O zPhHSCdn&0U1+)uqGx>If@W6jhuiZc`)j3+hc`3*N8(WpsFrHS@s#ozu4-CMtFEsL4X9?cWY3T_2(fzhZ@ zf{?@`o7QfiM$<~gixyj}t70z3{1pq-JHc;npBAx4P~rV}xf+tPkO9BNS-9J9 zxg(76Be)o`JbC(TzJcS9Cip|??J&Qf9>DQK&h(mRwAd%*4Ws2@+}w(V!V-|P+M>8F7YwaG^DNk^luH39<+PI> zRY{mHs{fovKq>X+Cw_%Fq-8+d2R#4OV4v5(unrU#`^goz&8y}|gD`KuR>fWS5FTmp4-%SwDGsuZ0Dv3 zI%LIOX$TR3|H9iSYWFSA0s!tV(wo+eulcSL9)K}J5-MLywG=X$0>Xbp8`VG-f!*SP{dj!xnbyqPvOAS4X`;N%qkgEtJdsD> zT8#6h2i5bx2iEQTjKD7oNHMdhN~6uuTQ}fgGsNlL+Xz*_B%7$Vi0@8W+gd%_f%7Re z?OwxF3?U$7IZL$5DOC6V!gdC-t32f42;rzNxRgf@7PBX%m{Psz&>dE-O<;w0Zw6cH zeWe{R>Kb|4L!xdNg>A&zr};_8a)7mxv)lb>vhGe6=P>Sw+CwGjj8_)7kb>Wr-%VAB z78Vm3KSIer-o=xL9K?^g`Do$@%s2d!ug7QfpCh@vZwP&~%P9Y8Ikll%700VEdklAZ z$V;c$R_A}gxad&xIeV%S9wXOYId?Y@Y6aI5RM*o`{ThTyVz^_~6iF^EU_5E(Pv*lN zHkk}iT~{GCeXjdpS@Buw#a=@6X;~+i!$_VOQ{zV;sR~c+&!-#|5fc&V{Nb+oD$2yO zRq)jw8zVRGNA@4bc!U)?4G~?9X2R7blyvHQRE+J$G++1|x|V*$P+-%fSk=d=+SgOc z_o$P7*T<~T{5e{;$eUP-9lGyqIr&87CPgPb!HdJEtDi*Wo`jA z)Syr-n-ZnML)$(we8n*#*Z-{mJ{+MbO~0=%Yh7EzRNpX+Wp(@0;)h@z?3&k#TYlF~ z0nEdFm=JsZOA2%vL6f3-;c2(6?a)3QIqQ%`P=ozf&*seuAFParxnYk!nCRA|AcH`9 za%%)y5NEgr_qX`{bASsHqh>2(uV-y(SRVN!veeAWhyzRyzFKADn5b-4+0yNXUe+p} z&Gl6kjYfR(JJmp|n)?{34G!Yqbsh2lblLVJ6sX~L2_IaoMICoA>!Z-=B9%S0#uN~J zmR;7snVFin`6V%vd6S!#-bk+U^0l&GJ!Ki4a?XmHZ-S`(Arw3ALww;lU`{cHU^@QDz zrGox(2fAT~Ct`35%oYB10fp|tHFpE47faQHx=NLa`60LLQ zaPY*@dIv3bdvfx)c3NGP++zCNZ_EeENP%9ii?icSO^a#sl zug~RzGf~x>Bcss{qK+bx_!8pV7UKeZvTk*g55#7=JH?2bsCkX&XC>bMB)hiBQZeeY z{;QBcfrdUMrtP4I_60!qaVYUvwhRZ_fM=;A{m*Z(%M#?h6tHgYveElpe1bcD=&?u8 zzt`sIW`c42M>$JsrHJ5ZxZ89`G0%~1>|((DFflP!T%ChLDBlX-6a>?76<_rI?Y a0Gn)HKeQfn^%(03+ud7cH@_J=M*R=X_#iw0 literal 0 HcmV?d00001 diff --git a/apps/BMOface/screenshot4.png b/apps/BMOface/screenshot4.png new file mode 100644 index 0000000000000000000000000000000000000000..f2ba4fd1d34371dae054c0cbbc4fd17375c781bd GIT binary patch literal 3893 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 DPcdvu literal 0 HcmV?d00001 diff --git a/apps/BMOface/settings.js b/apps/BMOface/settings.js new file mode 100644 index 0000000000..3ab35cefd1 --- /dev/null +++ b/apps/BMOface/settings.js @@ -0,0 +1,37 @@ +(function(back) { + var FILE = "bmoface.settings.json"; + + // Load settings with proper defaults + var settings = Object.assign({ + character: "BMO", + tempUnit: "F" + }, require('Storage').readJSON(FILE, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + // Show the menu + E.showMenu({ + "" : { "title" : "BMO Face Settings" }, + "< Back" : back, + 'Character': { + value: 0 | ["BMO", "Finn", "Jake"].indexOf(settings.character), + min: 0, max: 2, + format: v => ["BMO", "Finn", "Jake"][v], + onchange: v => { + settings.character = ["BMO", "Finn", "Jake"][v]; + writeSettings(); + } + }, + 'Temperature Unit': { + value: settings.tempUnit === "F" ? 1 : 0, + min: 0, max: 1, + format: v => v ? "Fahrenheit" : "Celsius", + onchange: v => { + settings.tempUnit = v ? "F" : "C"; + writeSettings(); + } + } + }); +}); \ No newline at end of file