const TIMER_IDX = "lcars"; const SETTINGS_FILE = "lcars.setting.json"; const locale = require('locale'); const storage = require('Storage') const widget_utils = require('widget_utils'); let settings = { alarm: -1, dataRow1: "Steps", dataRow2: "HRM", dataRow3: "Battery", speed: "kph", fullscreen: false, themeColor1BG: "#FF9900", themeColor2BG: "#FF00DC", themeColor3BG: "#0094FF", disableAlarms: false, disableData: false, }; let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; for (const key in saved_settings) { settings[key] = saved_settings[key] } /* * Colors to use */ let color1 = settings.themeColor3BG; let color2 = settings.themeColor1BG; let color3 = settings.themeColor2BG; let cWhite = "#FFFFFF"; let cBlack = "#000000"; let cGrey = "#424242"; /* * Global lcars variables */ let lcarsViewPos = 0; // let hrmValue = 0; var plotMonth = false; function convert24to16(input) { let RGB888 = parseInt(input.replace(/^#/, ''), 16); let r = (RGB888 & 0xFF0000) >> 16; let g = (RGB888 & 0xFF00) >> 8; let b = RGB888 & 0xFF; r = (r * 249 + 1014) >> 11; g = (g * 253 + 505) >> 10; b = (b * 249 + 1014) >> 11; let RGB565 = 0; RGB565 = RGB565 | (r << 11); RGB565 = RGB565 | (g << 5); RGB565 = RGB565 | b; return "0x"+RGB565.toString(16); } var color1C = convert24to16(color1);//Converting colors to the correct format. var color2C = convert24to16(color2); var color3C = convert24to16(color3); /* * Requirements and globals */ var colorPalette = new Uint16Array([//Used to change the color of the image if the user selects a color that is diffrent than the default. 0x0000, // not used color2C, // second color3C, // third 0x0000, // not used color1C, // first 0x0000, // not used 0x0000, // not used 0x0000, // not used 0x0000, // not used 0x0000, // not used 0x0000, // not used 0x0000, // not used 0x0000, // not used 0x0000, // not used 0x0000, // not used 0x0000 // not used ],0,1); var bgLeftFullscreen = { width : 27, height : 176, bpp : 3, transparent : 0, buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAoUP7y/KH5QGDoQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyDCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4"))), palette: colorPalette }; var bgLeftNotFullscreen = { width : 27, height : 152, bpp : 3, transparent : 0, buffer : require("heatshrink").decompress((atob("/4AB+VJkmSAQV///+BAtJn//5IIFkmf/4IGyVP/gIGpMnF41PHIImGF4ImHJoQmGJoIdK8hNHNY47C/JNGBIJZGyYJBQA5GCKH5Q/KAQAy0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5Q1GShlJChQLCCg5TCHw5TMAD35FAoIIkgJB8hGGv/8Mg8/+QIFp4cB5IRGBIIvI/4IFybyCF4wTCDp5NBHZZiGz4JBLJKAGk4JBO4xQ/KGQA8UP7y/KH5QnAHih/eX5Q/GQ4JCGRJlKCgxTDBAwgCCg5TCHwxTCNA4A=="))), palette: colorPalette }; var bgRightFullscreen = { width : 27, height : 176, bpp : 3, transparent : 0, buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAnUP7y/KH4yGeVYAJ0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkp4CS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A=="))), palette: colorPalette }; var bgRightNotFullscreen = { width : 27, height : 152, bpp : 3, transparent : 0, buffer : require("heatshrink").decompress((atob("yVJkgCCyf/AAPJBAYCBk4JB8gUFyVP//yBAoCB//5BAwUCAAIUHAAIgGChopGv5TIn5TIz4yLKYxxC/iGI/xxGKH5Q/agwAx0hGF34JB6RGFr4JB9JkFl4JB+gdFy4JB/QdFpYJB/odFkqrCS4xGCWoyhCKH5QuDoxQCDpI7GDoJZGHYIRGLIQvGO4QvGMQRNJADv+GIqTC/5PGz4JBJ41JBIPJCg2TD4QLGn4JB/gUaHwRTGHwRTHBIRTGNAQyJ8gyI+QdFp4JB/IdFk5lLKH5QvAHih/eX5Q/KE4A8UP7y/KH5QGDpg7HJoxZCCIx3CJowmCF4yACJoyJC/4A="))), palette: colorPalette }; var bgLeft = settings.fullscreen ? bgLeftFullscreen : bgLeftNotFullscreen; var bgRight= settings.fullscreen ? bgRightFullscreen : bgRightNotFullscreen; var iconEarth = { width : 50, height : 50, bpp : 3, buffer : require("heatshrink").decompress(atob("AFtx48ECBsDwU5k/yhARLjgjBjlzAQMQEZcIkOP/fn31IEZgCBnlz58cEpM4geugEgwU/8+WNZJHDuHHvgmBCQ8goEOnVgJoMnyV58mACItHI4X8uAFBuVHnnz4BuGxk4////Egz3IkmWvPgNw8f/prB//BghTC+AjE7848eMjNnzySBwUJkmf/BuGuPDAQIjBiPHhhTCSQnjMo0ITANJn44Dg8MuFBggCCiFBcAJ0Bv5xEh+ITo2OhHkyf/OIQdBWwVHhgjBNwUE+fP/5EEgePMoYLBhMgyVJk/+BQQdC688I4XxOIc8v//NAvr+QEBj/5NwKVBy1/QYUciPBhk1EAJrC+KeC489QYaMBgU/8BNB9+ChEjz1Jkn/QYMBDQIgCcYTCCiP/nlzJQmenMAgV4//uy/9wRaB/1J8iVCcAfHjt9TYYICnhKCgRKBw159/v//r927OIeeoASBDQccvv3791KYVDBYPLJQeCnPnz//AAP6ocEjEkXgMgJQtz79fLAP8KYkccAcJ8Gf/f/xu/cAMQ4eP5MlyQRCMolx40YsOGBAPfnnzU4KVDpKMBvz8Dh0/8me7IICgkxJQXPIgZTD58sEgcJk+eNoONnFBhk4/5uB/pcDg5KD+4mEv4CBXISVDhEn31/8/+mH7x//JQK5CAAMB4JBCnnxJQf/+fJEgkAa4L+CAQOOjMn/1bXIRxDJQXx58f//Hhlz/88EgsChMgz/Zs/+nfkyV/8huDOI6SD498NwoACi1Z8+S/Plz17/+QCI7jC+ZxBmfPnojIAAMDcYWSp//2wRJEwq2GABECjMgNYwAmA=")) }; var iconSaturn = { width : 50, height : 50, bpp : 3, transparent : 1, buffer : require("heatshrink").decompress(atob("AH4A/AEkQuPHCJ0ChEAwARNjAjBjgjOhs06Q2OEYVx4ARMhEggUMkANIDoIgBoEEgEBNxJEC6ZrBAAMwNxAjDNYcHNxIjB7dtEwIHBwRoKj158+cuPEjlwCRAjC23bpu0wRNDAAsHEYWeEwaSJ6YjCAQUNSRQjEzxQBWZMNEYlsmg2JWAIjCz95SoJuJggjDtuw6dMG5JKCz998wFBJRVNEYW0yaVBJRNhJQN9+4pCzhKJmBKC4YpB/fINxIgCzFxSoQ3J4ENm3CAQPb98wbpEcAQMYWwKYBNxMDXgc2/fv3g2IEAOAgAjBjy5CEhEMfYICBgfPnjdLjj+CgMHiC3JknDhhoINw4jCAB0IJQIANR4QjPAH4A/AFA")) }; var iconMoon = { width : 50, height : 50, bpp : 3, transparent : 1, buffer : require("heatshrink").decompress(atob("AH4AQjlx44CCCZsg8eOkHDwAQKEYgmPhEgEQM48AOIgMHEYoCB4ATI8UAmH/x04JoRuJsImHuBKLn37EwZuIgEQOI8cEpXj/yYBhE8+YNGgkYoJxITBUPnAaC///nC+FjBuIOJZEB8YeCh/8AoYACoMEEAnEjhQDPQJKJ/DCDAoi5DoLdHAoMQgLjFWYPOnngh02IwXzwDjEgPGEYS8BI4MBYoSVG4fP/nghkAgZrDkngJQqSG4gvBg4sBQgkImHihEAWwP8ZBMBEYl5/+cSoVAGQIUFh04weJn///0gj/OEw5KEz45BzhuCTYQAEgePB4IACAoJuBnAQEa4XHjxKB//xFgWHJQsCRgMDEonipwjENwUBDQNx8+evvn/hTDLw3igE+EgZxB8UOXIvEJQUfEYOfv53DEQkgga5BJQvzx84cAj+CDoNh8/eEYJKDuCSEcocnEon+/7xEgFBIIcfB4Mf/IICXI2DgDdBAAn758gCIq5Dv4zBvJuIOIfjEgvP/ARHgwdCB4P3AoTdFAAk4EYk8SQgAFTALaDSQwAGh08//vnDmBABYmEEZYAzA==")) }; var iconMars = { width : 50, height : 50, bpp : 3, transparent : 1, buffer : require("heatshrink").decompress(atob("AH4ATjlwCJ+Dh0wwAQMg0cuPHjFhCZkDps0yVJkmQCBMEjFx42atOmzQmLhMkEYQCCCREQoOGEYmmzB0IEY4CBkARGoJKBEYQCEzgSGkGSpAjDyYCCphuGiFhJQgCD8ASFgRHGAQKbB6BuHJRGeOIsINxEk6dNmARDgMEjQjHAQPnVQojIyZKB6YSDNwK5FAQt54BuDXJIjBEwK5EgxKKXgq5BJRdgXIojJAQJKMcAM0EwM2JUApDoCVFExa7FkGCgAmIkAREEwUEjAmHCIgABhEggQmFpACBCIojBEwRQCzVhwkQU4YADgQmBwQCCI4IFBCAojFAQojGJQQjDAQgRGEZICBEo4gFyUIkilFJQUYEAZrBAQMYNw5KDSQSbCNwwABgOGEwgCBsPACQ5xGwdNnARJcAVh48evvnCJK8Chs+/fv33gCRcB48cuPHCBYA/ADAA==")) }; var iconSatellite = { width : 50, height : 50, bpp : 3, transparent : 2, buffer : require("heatshrink").decompress(atob("pMkyQC/ATGXhIRPyNl0gmPjlwCJ9ly1aCJ1c+fHJR1Hy1ZJR1I+fPnlx6QRLpe+/JKBr5KMuYjBJQMdCJce/fvJQW0CJUlEYQCBSpvvJQbXJjl0NwnzNxGQwEOnHhgF78+WqQyIrFx48cAQXz4ShJgAABh0+8cP//9LJEhg4jDuP3//0LhGQgYlBgeAn///5cIy8MuAmDCIP/9I4HkmCEYMOgHfCQWkCI0cuBuDgF/CIP+CI1Ny1IkeAgHANwIAB/QRFrj7BhkxEwQRC/4RFpbXDgSVBg4RCSorXDI4MJAQMfCIP8cwImDn37fwN58+kwHgLgSVFub7CI4NyBAJKDLgkuEYX78+evKtCLg0jEYRKC58JMoRcFkwjDJQTFDl65EkojEAQMdcwn/+gFC3YjEJQLXEpYRDWwQmEdI6SHAQO0CJUkx4jDF4gCIJQgRMXIjCEARIjCCJ2XEYPKCJqJBJQIROcAUpCJ0kybaDARtdCKAC2kAA=")) }; var iconCharging = { width : 50, height : 50, bpp : 3, transparent : 5, buffer : require("heatshrink").decompress(atob("23btugAwUBtoICARG0h048eODQYCJ6P/AAUCCJfbo4SDxYRLtEcuPHjlwgoRJ7RnIloUHoYjDAQfAExEAwUIkACEkSAIEYwCBhZKH6EIJI0CJRFHEY0BJRWBSgf//0AJRYSE4BKLj4SE8BKLv4RD/hK/JS2AXY0gXwRKG4cMmACCJQMAg8csEFJQsBAwfasEAm379u0gFbcBfHzgFBz1xMQZKBjY/D0E2+BOChu26yVEEYdww+cgAFCg+cgIfB6RKF4HbgEIkGChEAthfCJQ0eEAIjBBAMxk6GCJQtgtyVBwRKBAQMbHAJKGXIIFCgACBhl54qVG2E+EAJKBJoWAm0WJQ6SCXgdxFgMLJQvYjeAEAUwFIUitEtJQ14NwUHgEwKYZKGwOwNYX7XgWCg3CJQ5rB4MevPnAoPDJRJrCgEG/ECAoNsJRUwoEesIIBiJKI3CVDti/CJRKVDiJHBSo0YsOGjED8AjBcAcIgdhcAXAPIUAcAYIBcA4dBAQUG8BrBgBuCgOwcBEeXIK2BBAIFBgRqBGoYAChq8CcYUE4FbUYOACQsHzgjDgwFBCIImBAQsDtwYD7cAloRI22B86YBw5QBgoRJ7dAgYEDCJaeBJoMcsARMAQNoJIIRE6A")) }; var iconWarning = { width : 50, height : 50, bpp : 3, transparent : 1, buffer : require("heatshrink").decompress(atob("kmSpIC/AWMyoQIFsmECJFJhMmA4QXByVICIwODAQ4RRFIQGD5JVLkIGDzJqMyAGDph8MiRKGyApEAoZKFyYIDQwMkSQNkQZABBhIIOOJRuEL5gRIAUKACVQMhmUSNYNDQYJTBBwYFByGTkOE5FJWYNMknCAQKYCiaSCpmGochDoSYBhMwTAZrChILBhmEzKPBF4ImBTAREBDoMmEwJVDoYjBycJFgWEJQRuLJQ1kmQCCjJlCBYbjCagaDBwyDBmBuBF4TjJAUQKINBChCDQxZBcZIIQF4NIgEAgKSDiQmEVQKMBoARBAAMCSQLLBVoxqKL4gaCChVCNwoRKOIo4CJIgABBoSMHpIRFgDdJOIJUBCAUJRgJuEAQb+DIIgRIAX4C/ASOQA")) }; // Font to use: // Graphics.prototype.setFontAntonioMedium = function(scale) { // Actual height 20 (19 - 0) g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAA//mP/5gAAAAAAAAAAAAA/gAMAAAAAA/gAPAAAEIIBP+H/8D+IYBP+H/8D+IABCAAwIAfnwP8+PHh448eP3+B4fAAAAAAAH/AD/4AwGAMBgD/4Af8GAAPgAPgAfgAfAAfAA+AAOP/AH/4BgGAYBgH/4A/8AAAAAAAAAQAA/B+f4/+GMPhjv/4/h8Dg/gAcYwAAPwADgAAAAAAAAB//8///sAAaAACAAAMAAb//+f//AAAAAAAbAAGwAA4AA/wADgABsAAbAAAAAAAgAAMAAPwAD8AAMAADAAAAAAAAAAHAAB/AAOAAAAAAAAMAADAAAwAAMAACAAAAAAAAAABgAAYAAAAAAAAA4AD+AP+A/4A/gAOAAAAAAAAAH//j//8wADMAAz//8f/+AAAAAAAMAADAABgAA//+P//gAAAAAAAAAAAAAfgfP4fzAfswfDP/gx/gMAAAHgPj4D8wMDMHAz//8f3+AAEAAAAADwAH8APzA/AwP//j//4AAwAAAD/Hw/x+MwBjOAYz/+Mf/AAAAAAAH//j//8wYDMGAz9/8fP+AAcDAAAwAAMAfjB/4z/wP+AD4AAwAAAAOB/f4///MHAzBwM///H9/gAAAAAAH/Pj/78wGDMBgz//8f/+AAAAAAADhwA4cAAAAAAAAAAAAAADh/A4fgAAAAOAAHwABsAA7gAccAGDAAAAANgADYAA2AANgADYAA2AAAAAAAABgwAccADuAAbAAHwAA4AAAAHwAD8c4/POMHAD/wAfwAAAAAAAAD/wD//B4B4Y/HMf8zMBMyATMwczP+M4BzHwcgf+AA+AAAAAAD4A/+P/8D+DA/4wH/+AB/4AAeAAAAAAA//+P//jBgYwYGP//j//4PH4AAAAAAAf/+P//zgAcwADP4fz+P4Ph8AAAAAAA//+P//jAAYwAGPADj//4P/4AAAAAAA//+P//jBgYwYGMGBgAAAAAAP//j//4wYAMGADBgAAAAAAAA//w///PAHzAQM4MHP7/x+/8AAAAAAD//4//+AGAABgAAYAP//j//4AAAAAAAAAA//+P//gAAAAAAAAAAAHwAB+AABgAAY//+P//AAAAAAAAAAD//4//+APgAf+Afj8PgPjAAYAAAAAAD//4//+AABgAAYAAGAAAAAAA//+P//j/gAD/wAB/gAP4B/4P/AD//4//+AAAAAAAAAAP//j//4P4AAfwAA/g//+P//gAAAAAAAAAA//g//+PAHjAAY4AOP//h//wAAAAAAD//4//+MDADAwA4cAP/AB/gAAAAAAAA//g//+PAHjAAc4APv//5//yAAAAAAD//4//+MGADBgA48AP//h+f4AAAAAAB+Pw/z+MOBjBwY/P+Hx/AAHgwAAMAAD//4//+MAADAAAAAAP//D//4AAOAABgAA4//+P//AAAAwAAP8AD//AA/+AAfgP/4//gPwAAAAA+AAP/4Af/4AD+A//j/wA/wAD/+AA/4B/+P/+D+AAAAAMADj8P4P/4A/4B//w+A+MABgAAA4AAPwAB/gAB/+A//j/gA+AAMAAAAAYwB+MH/jf+Y/8GPwBjAAAAAAP//7//+wABsAAYAAAAAAPAAD/gAH/gAD/gAD4AACAAADAAGwABv//7//+AAAA=="), 32, atob("BQUHCAgVCQQFBQkHBQcFBwgICAgICAgICAgFBQcHBwgPCQkJCQcHCQoFCQkHDQoJCQkJCAYJCQ0ICAcGBwY="), 20+(scale<<8)+(1<<16)); }; Graphics.prototype.setFontAntonioLarge = function(scale) { // Actual height 39 (39 - 1) g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAAAAPgAAAAAB8AAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAD8AAAAAH/gAAAAP/8AAAAf//gAAA///AAAB//+AAAD//8AAAH//4AAAP//wAAAB//gAAAAP/AAAAAB+AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///AAAf////8AP/////4B//////Af/////8D8AAAAfgeAAAAA8DwAAAAHgeAAAAA8D//////gf/////8B//////AP/////wAf////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAHgAAAAAA8AAAAAAPgAAAAAB4AAAAAAf/////gP/////8B//////gP/////8B//////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAD/+AAP8A//wAP/gP/+AH/8D//wD//gfgAA//8DwAAf+HgeAAP/A8DwAH/gHgfgP/wA8D///4AHgP//+AA8A///AAHgB//AAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AA/gAD/AAH/gA/4AA/+AP/AAH/4D/4AA//gfgA4AB8DwAPAAHgeAB4AA8DwAPgAHgfAD+AB8D//////gP/////4B//5//+AD/+H//gAH/AH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAP/AAAAAP/4AAAAP//AAAAP/x4AAAf/wPAAAf/gB4AAf/AAPAAP/AAB4AB//////gP/////8B//////gP/////8AAAAAPAAAAAAB4AAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//wD/AB///Af+AP//4D/4B///Af/gP//4B/8B4D4AAPgPAeAAA8B4DwAAHgPAfAAB8B4D////gPAf///4B4B////APAD///gAAAD//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///AAAP////4AH/////wB//////Af/////8D8APAA/geADwAB8DwAeAAHgeADwAA8D4AeAAPgf/j+AH8B/8f///gP/h///4Af8H//+AAPgP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAAPAAAAAAB4AAAABgPAAAA/8B4AAB//gPAAD//8B4AH///gPAH///8B4P//+AAPH//wAAB///gAAAP//AAAAB/+AAAAAP+AAAAAB+AAAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4A/+AAf/w//+AP//v//4B//////Af/////8D4AfwAPgeAB8AA8DwAHAAHgeAB8AA8D4Af4APgf/////8B//////AP//v//4A//4//8AA/4A/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAD//+D/gB///4f+AP///j/4D///8f/gfAAHgB8DwAA8AHgeAAHgA8DwAA8AHgfgAHgB8D//////gP/////4A/////+AD/////gAD////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAfgAAB+AD8AAAPwAfgAAB+AD8AAAPwAfgAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("DBATExMTExMTExMTCw=="), 45+(scale<<8)+(1<<16)); }; /* * Draw watch face */ var drawTimeout; function queueDraw() { // Faster updates during alarm to ensure that it is // shown correctly... var timeout = isAlarmEnabled() ? 10000 : 60000; if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = setTimeout(function() { drawTimeout = undefined; draw(); }, timeout - (Date.now() % timeout)); } /** * This function plots a data row in LCARS style. * Note: It can be called async and therefore, the text alignment and * font is set each time the function is called. */ function printRow(text, value, y, c){ g.setFontAntonioMedium(); g.setFontAlign(-1,-1,0); // Print background g.setColor(c); g.setFontAlign(-1,-1,0); g.fillRect(80, y-2, 165 ,y+18); g.fillCircle(163, y+8, 10); g.setColor(cBlack); g.drawString(text, 135, y); // Plot text width = g.stringWidth(value); g.setColor(cBlack); g.fillRect(130-width-8, y-2, 130, y+18); g.setColor(c); g.setFontAlign(1,-1,0); g.drawString(value, 126, y); } function drawData(key, y, c){ try{ _drawData(key, y, c); } catch(ex){ // Show last error - next try hopefully works. } } function _drawData(key, y, c){ key = key.toUpperCase() var text = key; var value = "ERR"; var should_print= true; if(key == "STEPS"){ text = "STEP"; value = getSteps(); } else if(key == "BATTERY"){ text = "BAT"; value = E.getBattery() + "%"; } else if (key == "VREF"){ value = E.getAnalogVRef().toFixed(2) + "V"; } else if(key == "HRM"){ value = Math.round(Bangle.getHealthStatus("last").bpm); } else if (key == "TEMP"){ var weather = getWeather(); value = weather.temp; } else if (key == "HUMIDITY"){ text = "HUM"; var weather = getWeather(); value = weather.hum; } else if (key == "WIND"){ text = "WND"; var weather = getWeather(); value = weather.wind; } else if (key == "ALTITUDE"){ should_print= false; text = "ALT"; // Immediately print something - avoid that its empty printRow(text, "", y, c); Bangle.getPressure().then(function(data){ if(data && data.altitude){ value = Math.round(data.altitude); printRow(text, value, y, c); } }) } else if(key == "CORET"){ value = locale.temp(parseInt(E.getTemperature())); } // Print for all datapoints that are not async if(should_print){ printRow(text, value, y, c); } } function drawHorizontalBgLine(color, x1, x2, y, h){ g.setColor(color); for(var i=0; i{ data[h.day]+=h.bpm; if (h.bpm) cnt[h.day]++; }); require("graph").drawBar(g, data, { axes : true, minx: 1, gridx : 5, gridy : 100, width : 140, height : 50, x: 5, y: 25 }); // Plot step graph var data = new Uint16Array(32); health.readDailySummaries(new Date(), h=>data[h.day]+=h.steps/1000); var gridY = parseInt(Math.max.apply(Math, data)/2); gridY = gridY <= 0 ? 1 : gridY; require("graph").drawBar(g, data, { axes : true, minx: 1, gridx : 5, gridy : gridY, width : 140, height : 50, x: 5, y: 115 }); g.setFontAlign(1, 1, 0); g.setFontAntonioMedium(); g.setColor(cWhite); if(settings.fullscreen){ g.drawString("M-HRM", 154, 27); g.drawString("M-STEPS [K]", 154, 115); } else { g.drawString("MONTH", 154, 115); } // Plot day } else { var data = new Uint16Array(24); var cnt = new Uint8Array(24); health.readDay(new Date(), h=>{ data[h.hr]+=h.bpm; if (h.bpm) cnt[h.hr]++; }); require("graph").drawBar(g, data, { axes : true, minx: 1, gridx : 4, gridy : 100, width : 140, height : 50, x: 5, y: 25 }); // Plot step graph var data = new Uint16Array(24); health.readDay(new Date(), h=>data[h.hr]+=h.steps); var gridY = parseInt(Math.max.apply(Math, data)/1000)*1000; gridY = gridY <= 0 ? 1000 : gridY; require("graph").drawBar(g, data, { axes : true, minx: 1, gridx : 4, gridy : gridY, width : 140, height : 50, x: 5, y: 115 }); g.setFontAlign(1, 1, 0); g.setFontAntonioMedium(); g.setColor(cWhite); if(settings.fullscreen){ g.drawString("D-HRM", 154, 27); g.drawString("D-STEPS", 154, 115); } else { g.drawString("DAY", 154, 115); } } } function draw(){ // Queue draw first to ensure that its called in one minute again. queueDraw(); // Next draw the watch face g.reset(); g.clearRect(0, 0, g.getWidth(), g.getHeight()); // Draw current lcars position if(lcarsViewPos == 0){ drawPosition0(); } else if (lcarsViewPos == 1) { drawPosition1(); } // After drawing the watch face, we can draw the widgets if(settings.fullscreen){ widget_utils.hide(); } else { Bangle.drawWidgets(); } } /* * Step counter via widget */ function getSteps() { var steps = 0; try{ if (WIDGETS.wpedom !== undefined) { steps = WIDGETS.wpedom.getSteps(); } else if (WIDGETS.activepedom !== undefined) { steps = WIDGETS.activepedom.getSteps(); } else { steps = Bangle.getHealthStatus("day").steps; } } catch(ex) { // In case we failed, we can only show 0 steps. } return steps; } function getWeather(){ var weatherJson; try { weatherJson = storage.readJSON('weather.json'); var weather = weatherJson.weather; // Temperature weather.temp = locale.temp(weather.temp-273.15); // Humidity weather.hum = weather.hum + "%"; // Wind const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/); var speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934; weather.wind = Math.round(wind[1] * speedFactor); return weather } catch(ex) { // Return default } return { temp: " ? ", hum: " ? ", txt: " ? ", wind: " ? ", wdir: " ? ", wrose: " ? " }; } /* * Handle alarm */ function isAlarmEnabled(){ try{ var alarm = require('sched'); var alarmObj = alarm.getAlarm(TIMER_IDX); if(alarmObj===undefined || !alarmObj.on){ return false; } return true; } catch(ex){ } return false; } function getAlarmMinutes(){ if(!isAlarmEnabled()){ return -1; } var alarm = require('sched'); var alarmObj = alarm.getAlarm(TIMER_IDX); return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000)); } function increaseAlarm(){ try{ var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0; var alarm = require('sched') alarm.setAlarm(TIMER_IDX, { timer : (minutes+5)*60*1000, }); alarm.reload(); } catch(ex){ } } function decreaseAlarm(){ try{ var minutes = getAlarmMinutes(); minutes -= 5; var alarm = require('sched') alarm.setAlarm(TIMER_IDX, undefined); if(minutes > 0){ alarm.setAlarm(TIMER_IDX, { timer : minutes*60*1000, }); } alarm.reload(); } catch(ex){ } } /* * Listeners */ Bangle.on('lcdPower',on=>{ if (on) { // Whenever we connect to Gadgetbridge, reading data from // health failed. Therefore, we update only partially... drawInfo(); drawState(); } else { // stop draw timer if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; } }); Bangle.on('lock', function(isLocked) { drawInfo(); }); Bangle.on('charging',function(charging) { drawState(); }); function feedback(){ Bangle.buzz(40, 0.3); } // Touch gestures to control clock. We don't use swipe to be compatible with the bangle ecosystem Bangle.on('touch', function(btn, e){ var left = parseInt(g.getWidth() * 0.2); var right = g.getWidth() - left; var upper = parseInt(g.getHeight() * 0.2); var lower = g.getHeight() - upper; var is_left = e.x < left; var is_right = e.x > right; var is_upper = e.y < upper; var is_lower = e.y > lower; if(!settings.disableData){ if(is_left && lcarsViewPos == 1){ feedback(); lcarsViewPos = 0; draw(); return; } else if(is_right && lcarsViewPos == 0){ feedback(); lcarsViewPos = 1; draw(); return; } } if(lcarsViewPos == 0){ if(is_upper && !settings.disableAlarms){ feedback(); increaseAlarm(); drawState(); return; } if(is_lower && !settings.disableAlarms){ feedback(); decreaseAlarm(); drawState(); return; } } else if (lcarsViewPos == 1 && (is_upper || is_lower) && plotMonth != is_lower){ feedback(); plotMonth = is_lower; draw(); return; } }); /* * Lets start widgets, listen for btn etc. */ // Show launcher when middle button pressed Bangle.setUI("clock"); Bangle.loadWidgets(); // Clear the screen once, at startup and draw clock g.setTheme({bg:"#000",fg:"#fff",dark:true}).clear(); draw();