diff --git a/apps/agenda/ChangeLog b/apps/agenda/ChangeLog index 99c44a2b1..9e7151e1e 100644 --- a/apps/agenda/ChangeLog +++ b/apps/agenda/ChangeLog @@ -12,3 +12,4 @@ 0.11: Setting to use "Today" and "Yesterday" instead of dates Added dynamic, short and range fields to clkinfo 0.12: Added color field and updating clkinfo periodically (running events) +0.13: Show day of the week in date diff --git a/apps/agenda/agenda.js b/apps/agenda/agenda.js index 8afca95a9..6d2b783fd 100644 --- a/apps/agenda/agenda.js +++ b/apps/agenda/agenda.js @@ -34,8 +34,9 @@ function getDate(timestamp) { return new Date(timestamp*1000); } function formatDay(date) { + let formattedDate = Locale.dow(date,1) + " " + Locale.date(date).replace(/\d\d\d\d/,""); if (!settings.useToday) { - return Locale.date(date); + return formattedDate; } const dateformatted = date.toISOString().split('T')[0]; // yyyy-mm-dd const today = new Date(Date.now()).toISOString().split('T')[0]; // yyyy-mm-dd @@ -46,7 +47,7 @@ function formatDay(date) { if (dateformatted == tomorrow) { return /*LANG*/"Tomorrow "; } - return Locale.date(date); + return formattedDate; } } function formatDateLong(date, includeDay, allDay) { @@ -58,7 +59,7 @@ function formatDateLong(date, includeDay, allDay) { return shortTime; } function formatDateShort(date, allDay) { - return formatDay(date).replace(/\d\d\d\d/,"")+(allDay?"":Locale.time(date,1)+Locale.meridian(date)); + return formatDay(date)+(allDay?"":Locale.time(date,1)+Locale.meridian(date)); } var lines = []; @@ -75,25 +76,29 @@ function showEvent(ev) { if (titleCnt) lines.push(""); // add blank line after title if(start.getDay() == end.getDay() && start.getMonth() == end.getMonth()) includeDay = false; - if(includeDay || ev.allDay) { + if(includeDay && ev.allDay) { + //single day all day (average to avoid getting previous day) lines = lines.concat( - /*LANG*/"Start:", + g.wrapString(formatDateLong(new Date((start+end)/2), includeDay, ev.allDay), g.getWidth()-10)); + } else if(includeDay || ev.allDay) { + lines = lines.concat( + /*LANG*/"Start"+":", g.wrapString(formatDateLong(start, includeDay, ev.allDay), g.getWidth()-10), - /*LANG*/"End:", + /*LANG*/"End"+":", g.wrapString(formatDateLong(end, includeDay, ev.allDay), g.getWidth()-10)); } else { lines = lines.concat( - g.wrapString(Locale.date(start), g.getWidth()-10), + g.wrapString(formatDateShort(start,true), g.getWidth()-10), g.wrapString(/*LANG*/"Start"+": "+formatDateLong(start, includeDay, ev.allDay), g.getWidth()-10), g.wrapString(/*LANG*/"End"+": "+formatDateLong(end, includeDay, ev.allDay), g.getWidth()-10)); } if(ev.location) - lines = lines.concat(/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10)); - if(ev.description) + lines = lines.concat("",/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10)); + if(ev.description && ev.description.trim()) lines = lines.concat("",g.wrapString(ev.description, g.getWidth()-10)); if(ev.calName) - lines = lines.concat(/*LANG*/"Calendar"+": ", g.wrapString(ev.calName, g.getWidth()-10)); - lines = lines.concat(["",/*LANG*/"< Back"]); + lines = lines.concat("",/*LANG*/"Calendar"+": ", g.wrapString(ev.calName, g.getWidth()-10)); + lines = lines.concat("",/*LANG*/"< Back"); E.showScroller({ h : g.getFontHeight(), // height of each menu item in pixels c : lines.length, // number of menu items @@ -120,7 +125,7 @@ function showList() { CALENDAR = CALENDAR.filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000); } if(CALENDAR.length == 0) { - E.showMessage("No events"); + E.showMessage(/*LANG*/"No events"); return; } E.showScroller({ diff --git a/apps/agenda/metadata.json b/apps/agenda/metadata.json index 88dd2c1bc..737568cb5 100644 --- a/apps/agenda/metadata.json +++ b/apps/agenda/metadata.json @@ -1,7 +1,7 @@ { "id": "agenda", "name": "Agenda", - "version": "0.12", + "version": "0.13", "description": "Simple agenda", "icon": "agenda.png", "screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}], diff --git a/apps/assistedgps/ChangeLog b/apps/assistedgps/ChangeLog index ff2de6f67..92f04dc6d 100644 --- a/apps/assistedgps/ChangeLog +++ b/apps/assistedgps/ChangeLog @@ -2,3 +2,4 @@ 0.02: Update to work with Bangle.js 2 0.03: Select GNSS systems to use for Bangle.js 2 0.04: Now turns GPS off after upload +0.05: Fix regression in 0.04 that caused AGPS data not to get loaded diff --git a/apps/assistedgps/custom.html b/apps/assistedgps/custom.html index 75a4ecf32..716865983 100644 --- a/apps/assistedgps/custom.html +++ b/apps/assistedgps/custom.html @@ -158,7 +158,7 @@ var chunk = bin.substr(i,chunkSize); js += `\x10Serial1.write(atob("${btoa(chunk)}"))\n`; } - js = "\x10setTimeout(() => Bangle.setGPSPower(0,'agps'), 1000);\n"; // turn GPS off after a delay + js += "\x10setTimeout(() => Bangle.setGPSPower(0,'agps'), 1000);\n"; // turn GPS off after a delay return js; } diff --git a/apps/assistedgps/metadata.json b/apps/assistedgps/metadata.json index ac9fe5725..d2e7334c4 100644 --- a/apps/assistedgps/metadata.json +++ b/apps/assistedgps/metadata.json @@ -1,7 +1,7 @@ { "id": "assistedgps", "name": "Assisted GPS Updater (AGPS)", - "version": "0.04", + "version": "0.05", "description": "Downloads assisted GPS (AGPS) data to Bangle.js for faster GPS startup and more accurate fixes. **No app will be installed**, this just uploads new data to the GPS chip.", "sortorder": -1, "icon": "app.png", diff --git a/apps/legoremote/ChangeLog b/apps/legoremote/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/legoremote/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/legoremote/README.md b/apps/legoremote/README.md new file mode 100644 index 000000000..a95b7b154 --- /dev/null +++ b/apps/legoremote/README.md @@ -0,0 +1,28 @@ +# LEGO Remote control + +This app allows you to control LEGO models from Bangle.js + +Right now the only supported control device is the Mould King M-0006 +Bluetooth remote for LEGO Power Functions: http://www.espruino.com/LEGO+Power+Functions+Clone + +LEGO Power Functions does not have an official Bluetooth remote controller. Hopefully +in the future this app will be able to support other types of remote (see below). + +## Usage + +Run the app, and ensure you're not connected to your watch via Bluetooth +(a warning will pop up if so). + +Now press the arrow keys on the screen to control the robot. + +It is expected that the robot is controlled by two motors, one on the left +side (connected to the `A` output) and one on the right (connected to the `B` output). + +## Future additions + +In the future it would be great to add: + +* Recording a series of movements and playing them back +* Support for official LEGO bluetooth remotes (via [Pybricks](https://pybricks.com/)) +* Support for different robot styles and configurations +* Using the Bangle's compass (or even GPS) to allow better robot control. diff --git a/apps/legoremote/app-icon.js b/apps/legoremote/app-icon.js new file mode 100644 index 000000000..850e0eda6 --- /dev/null +++ b/apps/legoremote/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4X/8H5A4NH/4ABJf4A/AFMC1Uq1QABhWqBYYJDAAegBYWqxWogWohQBCBYWpqsVqoABytVBYUC0u5rf5rf1q/xtQLBhWW/t//2/AYVKBYWX/u//z3B3//rRGCr/1/+H/v9/1/BYn2/lT+v6/oXDlF/4//9/78/5F4epv/X//f7/3+v9I4Wp38b/9v7//+4LD0P/HgN/7f/EgMoBYOlJ4IACDAP1O4QLH6ibCBYI7Br/+qf/iwLC1NIquhq2lquprWAWQVVoNVgtU0NVlQLCZQ7XDbgLWJEgOCdgLZBdwgA/AH4AaA")) diff --git a/apps/legoremote/app.js b/apps/legoremote/app.js new file mode 100644 index 000000000..1c76a54a8 --- /dev/null +++ b/apps/legoremote/app.js @@ -0,0 +1,70 @@ +var lego = require("mouldking"); +lego.start(); +E.on('kill', () => { + // return to normal Bluetooth advertising + NRF.setAdvertising({},{showName:true}); +}); +// You must leave one second after 'start' to allow the remote to be paired + +var arrowIcon = atob("IiiBAAAAwAAAAPwAAAB/gAAAP/AAAB/+AAAP/8AAB//4AAP//wAA///gAH///AA///8AH///4A////wH////gf////D////8f////5/////n////+f////4AP/8AAA//wAAD//AAAP/8AAA//wAAD//AAAH/8AAAf/wAAB//AAAH/8AAAf/gAAB/+AAAH/4AAAf/gAAB/+AAAH/4AAAP/gAAA/+AAAD/wAAAD8AA"); +var controlState = ""; + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +var R = Bangle.appRect; +// we'll divide up into 3x3 +function getBoxCoords(x,y) { + return { + x : R.x + R.w*x/3, + y : R.y + R.h*y/3 + }; +} + +function draw() { + g.reset().clearRect(R); + var c, ninety = Math.PI/2; + var colOn = "#f00", colOff = g.theme.fg; + c = getBoxCoords(1.5, 0.5); + g.setColor(controlState=="up"?colOn:colOff).drawImage(arrowIcon,c.x,c.y,{rotate:0}); + c = getBoxCoords(2.5, 1.5); + g.setColor(controlState=="right"?colOn:colOff).drawImage(arrowIcon,c.x,c.y,{rotate:ninety}); + c = getBoxCoords(0.5, 1.5); + g.setColor(controlState=="left"?colOn:colOff).drawImage(arrowIcon,c.x,c.y,{rotate:-ninety}); + c = getBoxCoords(1.5, 1.5); + g.setColor(controlState=="down"?colOn:colOff).drawImage(arrowIcon,c.x,c.y,{rotate:ninety*2}); + if (NRF.getSecurityStatus().connected) { + c = getBoxCoords(1.5, 2.5); + g.setFontAlign(0,0).setFont("6x8").drawString("WARNING:\nBluetooth Connected\nYou must disconnect\nbefore LEGO will work",c.x,c.y); + } +} +draw(); +NRF.on('connect', draw); +NRF.on('disconnect', draw); + +function setControlState(s) { + controlState = s; + var c = {}; + var speed = 3; + if (s=="up") c={a:-speed,b:-speed}; + if (s=="down") c={a:speed,b:speed}; + if (s=="left") c={a:speed,b:-speed}; + if (s=="right") c={a:-speed,b:speed}; + draw(); + lego.set(c); +} + +Bangle.on('drag',e => { + var x = Math.floor(E.clip((e.x - R.x) * 3 / R.w,0,2.99)); + var y = Math.floor(E.clip((e.y - R.y) * 3 / R.h,0,2.99)); + if (!e.b) { + setControlState(""); + return; + } + if (y==0) { // top row + if (x==1) setControlState("up"); + } else if (y==1) { + if (x==0) setControlState("left"); + if (x==1) setControlState("down"); + if (x==2) setControlState("right"); + } +}); diff --git a/apps/legoremote/app.png b/apps/legoremote/app.png new file mode 100644 index 000000000..727049e2b Binary files /dev/null and b/apps/legoremote/app.png differ diff --git a/apps/legoremote/metadata.json b/apps/legoremote/metadata.json new file mode 100644 index 000000000..c86251860 --- /dev/null +++ b/apps/legoremote/metadata.json @@ -0,0 +1,14 @@ +{ "id": "legoremote", + "name": "LEGO Remote control", + "shortName":"LEGO Remote", + "version":"0.01", + "description": "Use your Bangle.js to control LEGO models. See the README for compatibility", + "icon": "app.png", + "tags": "toy,lego,bluetooth", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"legoremote.app.js","url":"app.js"}, + {"name":"legoremote.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/mtnclock/ChangeLog b/apps/mtnclock/ChangeLog new file mode 100644 index 000000000..cbf5c7f40 --- /dev/null +++ b/apps/mtnclock/ChangeLog @@ -0,0 +1,2 @@ +0.01: New App! +0.02: Get weather from weather.json diff --git a/apps/mtnclock/README.md b/apps/mtnclock/README.md index 58538509d..441754b83 100644 --- a/apps/mtnclock/README.md +++ b/apps/mtnclock/README.md @@ -4,7 +4,7 @@ Based on the Pebble watchface Weather Land. Mountain Pass Clock changes depending on time (day/night) and weather conditions. -This clock requires Gadgetbridge and an app that Gadgetbridge can use to get the current weather from OpenWeatherMap (e.g. Weather Notification). To set up Gadgetbridge and weather, see https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Weather. +This clock requires Gadgetbridge and an app that Gadgetbridge can use to get the current weather from OpenWeatherMap (e.g. Weather Notification), or a Bangle app that will update weather.json such as OWM Weather. To set up Gadgetbridge and weather, see https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Weather. The scene will change according to the following OpenWeatherMap conditions: clear, cloudy, overcast, lightning, drizzle, rain, fog and snow. Each weather condition has night/day scenes. diff --git a/apps/mtnclock/app.js b/apps/mtnclock/app.js index 28ba25882..c6adc7706 100644 --- a/apps/mtnclock/app.js +++ b/apps/mtnclock/app.js @@ -323,11 +323,28 @@ function setWeather() { draw(a); } +function readWeather() { + var weatherJson = require("Storage").readJSON('weather.json', 1); + // save updated weather data if available and it has been an hour since last updated + if (weatherJson !== undefined && (data.time === undefined || (data.time + 3600000) < weatherJson.weather.time)) { + data = { + time: weatherJson.weather.time, + temp: weatherJson.weather.temp, + code: weatherJson.weather.code + }; + require("Storage").writeJSON('mtnclock.json', data); + } +} + const _GB = global.GB; global.GB = (event) => { if (event.t==="weather") { - data = event; - require("Storage").write('mtnclock.json', event); + data = { + temp: event.temp, + code: event.code, + time: Date.now() + }; + require("Storage").writeJSON('mtnclock.json', data); setWeather(); } if (_GB) setTimeout(_GB, 0, event); @@ -340,11 +357,13 @@ function queueDraw() { if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = setTimeout(function() { drawTimeout = undefined; + readWeather(); setWeather(); queueDraw(); }, 60000 - (Date.now() % 60000)); } queueDraw(); +readWeather(); setWeather(); Bangle.setUI("clock"); diff --git a/apps/mtnclock/metadata.json b/apps/mtnclock/metadata.json index a3a173069..82a0cccab 100644 --- a/apps/mtnclock/metadata.json +++ b/apps/mtnclock/metadata.json @@ -2,7 +2,7 @@ "id": "mtnclock", "name": "Mountain Pass Clock", "shortName": "Mtn Clock", - "version": "0.01", + "version": "0.02", "description": "A clock that changes scenery based on time and weather.", "readme":"README.md", "icon": "app.png", diff --git a/apps/qcenter/ChangeLog b/apps/qcenter/ChangeLog index 366b0ff86..436949dc9 100644 --- a/apps/qcenter/ChangeLog +++ b/apps/qcenter/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Fix fast loading on swipe to clock 0.03: Adds a setting for going back to clock on a timeout +0.04: Fix timeouts closing fast loaded apps \ No newline at end of file diff --git a/apps/qcenter/app.js b/apps/qcenter/app.js index f012fdda8..bd22c87e1 100644 --- a/apps/qcenter/app.js +++ b/apps/qcenter/app.js @@ -111,6 +111,7 @@ let layout = new Layout({ remove: ()=>{ Bangle.removeListener("swipe", onSwipe); Bangle.removeListener("touch", updateTimeout); + if (timeout) clearTimeout(timeout); delete Graphics.prototype.setFont8x12; } }); diff --git a/apps/qcenter/metadata.json b/apps/qcenter/metadata.json index 630fd2bef..cd3e350a4 100644 --- a/apps/qcenter/metadata.json +++ b/apps/qcenter/metadata.json @@ -2,7 +2,7 @@ "id": "qcenter", "name": "Quick Center", "shortName": "QCenter", - "version": "0.03", + "version": "0.04", "description": "An app for quickly launching your favourite apps, inspired by the control centres of other watches.", "icon": "app.png", "tags": "", diff --git a/apps/tempgraph/ChangeLog b/apps/tempgraph/ChangeLog index 58dd75a19..74f2d7255 100644 --- a/apps/tempgraph/ChangeLog +++ b/apps/tempgraph/ChangeLog @@ -1,2 +1,3 @@ 0.01: 3/Feb/2023 Added 'Temperature Graph' app to depository. - +0.02: 4/Feb/2023 Rewrote the widget handling after discovering there's a 'widget_utils' module to properly hide and show them. +0.03: 4/Feb/2023 Fixed number error in timesData array. diff --git a/apps/tempgraph/app.js b/apps/tempgraph/app.js index a0bb3016b..b8dd231ae 100644 --- a/apps/tempgraph/app.js +++ b/apps/tempgraph/app.js @@ -1,395 +1,394 @@ -// Temperature Graph -// BangleJS Script - -Bangle.setBarometerPower(true,"tempgraph"); -Bangle.loadWidgets(); -var wids=WIDGETS; -var widsOn=true; -var rm=null; -var gt=null; -var dg=null; -var Layout=require("Layout"); -var C=true; -var temp,tempMode,readErrCnt,watchButton2; - -var graph=require("Storage").readJSON("tempgraph.json",true); -if(graph==undefined) { - graph=[]; -} - -var timesData=[ - // dur=duration, u=time units, d=divisions on graph, s=seconds per unit. - {dur:10,u:"Mins",d:5,s:60}, - {dur:20,u:"Mins",d:4,s:60}, - {dur:30,u:"Mins",d:3,s:60}, - {dur:40,u:"Mins",d:4,s:60}, - {dur:1,u:"Hr",d:4,s:3600}, - {dur:2,u:"Hrs",d:4,s:3600}, - {dur:3,u:"Hrs",d:3,s:3600}, - {dur:4,u:"Hrs",d:4,s:3600}, - {dur:6,u:"Hrs",d:6,s:3600}, - {dur:8,u:"Hrs",d:4,s:3600}, - {dur:12,u:"Hrs",d:6,s:3600}, - {dur:16,u:"Hrs",d:4,s:3600}, - {dur:20,u:"Hrs",d:5,s:3600}, - {dur:1,u:"Day",d:4,s:3600}, - {dur:2,u:"Days",d:4,s:86400}, - {dur:3,u:"Days",d:3,s:86400}, - {dur:4,u:"Days",d:4,s:86400}, - {dur:5,u:"Days",d:5,s:86400}, - {dur:6,u:"Days",d:6,s:86400}, - {dur:7,u:"Days",d:7,s:86400} -]; -var times=[]; -for(n=0;n{ - temp=p.temperature; - if(tempMode=="drawGraph"&&graph.length>0&&Math.abs(graph[graph.length-1].temp-temp)>10&&readErrCnt<2){ - // A large change in temperature may be a reading error. ie. A 0C or less reading after - // a 20C reading. So if this happens, the reading is repeated up to 2 times to hopefully - // skip such errors. - readErrCnt++; - print("readErrCnt "+readErrCnt); - return; - } - clearInterval(gt); - readErrCnt=0; - switch (tempMode){ - case "showTemp": - showT(); - break; - case "drawGraph": - var date=new Date(); - var dateStr=require("locale").date(date).trim(); - var hrs=date.getHours(); - var mins=date.getMinutes(); - var secs=date.getSeconds(); - graph.push({ - temp:temp, - date:dateStr, - hrs:hrs, - mins:mins, - secs:secs - }); - if(graph.length==1){ - graph[0].dur=durInd; - } - require("Storage").writeJSON("tempgraph.json", graph); - if(graph.length==150){ - clearInterval(dg); - } - drawG(); - } - }); -} - -function getTemp(){ - readErrCnt=0; - gt = setInterval(getT,800); -} - -function setButton(){ - var watchButton=setWatch(function(){ - clearInterval(gt); - clearInterval(dg); - clearWatch(watchButton); - Bangle.removeListener("touch",screenTouch); - openMenu(); - },BTN); - Bangle.on('touch',screenTouch); -} - -function setButton2(){ - watchButton2=setWatch(function(){ - clearWatch(watchButton2); - openMenu(); - },BTN); -} - -function zPad(n){ - return n.toString().padStart(2,0); -} - -function screenTouch(n,ev){ - if(ev.y>23&&ev.y<152){ - C=C==false; - drawG(false); - } -} - -function drawG(){ - function cf(t){ - if(C){ - return t; - } - return getF(t); - } - drawWids(); - var top=1; - var bar=21; - var barBot=175-22; - if(widsOn){ - top=25; - bar=bar+24; - barBot=barBot-24; - } - var low=graph[0].temp; - var hi=low; - for(n=0;nt){ - low=t; - } - if(hi10){ - div=5; - } - if(C){ - g.setColor(1,0,0); - }else{ - g.setColor(0,0,1); - } - var step=(barBot-bar)/((tempHi-tempLow)/div); - for(n=0;nexit()}, - ],lazy:true - }); - drawWids(); - messageLO.render(); -} - -function showT(){ - tempLO.lab1.label=tempLO.lab3.label; - tempLO.lab2.label=tempLO.lab4.label; - tempLO.lab3.label=tempLO.lab5.label; - tempLO.lab4.label=tempLO.lab6.label; - tempLO.lab5.label=temp.toFixed(2)+"C"; - tempLO.lab6.label=getF(temp).toFixed(2)+"F"; - tempLO.render(); -} - -function exit(){ - clearWatch(watchButton2); - openMenu(); -} - -function showTemp(){ - tempMode="showTemp"; - setButton2(); - tempLO=new Layout({ - type:"v",c:[ - {type:"h",c:[ - {type:"txt",pad:5,col:"#f77",font:"6x8:2",label:" ",id:"lab1"}, - {type:"txt",pad:5,col:"#77f",font:"6x8:2",label:" ",id:"lab2"} - ]}, - {type:"h",c:[ - {type:"txt",pad:5,col:"#f77",font:"6x8:2",label:" ",id:"lab3"}, - {type:"txt",pad:5,col:"#77f",font:"6x8:2",label:" ",id:"lab4"} - ]}, - {type:"h",c:[ - {type:"txt",pad:5,col:"#f00",font:"6x8:2",label:" ",id:"lab5"}, - {type:"txt",pad:5,col:"#00f",font:"6x8:2",label:" ",id:"lab6"} - ]}, - {type:"h",c:[ - {type:"btn",pad:2,font:"6x8:2",label:"Temp",cb:l=>getTemp()}, - {type:"btn",pad:2,font:"6x8:2",label:"Exit",cb:l=>exit()} - ]} - ] - },{lazy:true}); - tempLO.render(); - getTemp(); -} - -var menu={ - "":{ - "title":" Temp. Graph" - }, - - "Widgets":{ - value:widsOn, - format:vis=>vis?"Hide":"Show", - onchange:vis=>{ - widsOn=vis; - refreshMenu(); - } - }, - - "Duration":{ - value:times.indexOf(duration), - min:0,max:times.length-1,step:1,wrap:true, - format:tim=>times[tim], - onchange:(dur)=>{ - duration=times[dur]; - } - }, - - "Draw Graph":function(){ - E.showMenu(); - drawGraph(); - }, - - "Show Graph" : function(){ - E.showMenu(); - if(graph.length>0){ - showGraph(); - }else{ - message("No graph to\nshow as no\ngraph has been\ndrawn yet."); - } - }, - - "Save Graph" : function(){ - E.showMenu(); - if(graph.length>0){ - saveGraph(); - }else{ - message("No graph to\nsave as no\ngraph has been\ndrawn yet."); - } - }, - - "Save Data" : function(){ - E.showMenu(); - if(graph.length>0){ - saveData(); - }else{ - message("No data to\nsave as no\ngraph has been\ndrawn yet."); - } - }, - - "Show Temp":function(){ - E.showMenu(); - showTemp(); - } -}; - -openMenu(); +// Temperature Graph +// BangleJS Script + +Bangle.setBarometerPower(true,"tempgraph"); +Bangle.loadWidgets(); +var widsOn=true; +var rm=null; +var gt=null; +var dg=null; +var Layout=require("Layout"); +var C=true; +var temp,tempMode,readErrCnt,watchButton2; + +var graph=require("Storage").readJSON("tempgraph.json",true); +if(graph==undefined) { + graph=[]; +} + +var timesData=[ + // dur=duration, u=time units, d=divisions on graph, s=seconds per unit. + {dur:10,u:"Mins",d:5,s:60}, + {dur:20,u:"Mins",d:4,s:60}, + {dur:30,u:"Mins",d:3,s:60}, + {dur:40,u:"Mins",d:4,s:60}, + {dur:1,u:"Hr",d:4,s:3600}, + {dur:2,u:"Hrs",d:4,s:3600}, + {dur:3,u:"Hrs",d:3,s:3600}, + {dur:4,u:"Hrs",d:4,s:3600}, + {dur:6,u:"Hrs",d:6,s:3600}, + {dur:8,u:"Hrs",d:4,s:3600}, + {dur:12,u:"Hrs",d:6,s:3600}, + {dur:16,u:"Hrs",d:4,s:3600}, + {dur:20,u:"Hrs",d:5,s:3600}, + {dur:1,u:"Day",d:4,s:86400}, + {dur:2,u:"Days",d:4,s:86400}, + {dur:3,u:"Days",d:3,s:86400}, + {dur:4,u:"Days",d:4,s:86400}, + {dur:5,u:"Days",d:5,s:86400}, + {dur:6,u:"Days",d:6,s:86400}, + {dur:7,u:"Days",d:7,s:86400} +]; +var times=[]; +for(n=0;n{ + temp=p.temperature; + if(tempMode=="drawGraph"&&graph.length>0&&Math.abs(graph[graph.length-1].temp-temp)>10&&readErrCnt<2){ + // A large change in temperature may be a reading error. ie. A 0C or less reading after + // a 20C reading. So if this happens, the reading is repeated up to 2 times to hopefully + // skip such errors. + readErrCnt++; + print("readErrCnt "+readErrCnt); + return; + } + clearInterval(gt); + readErrCnt=0; + switch (tempMode){ + case "showTemp": + showT(); + break; + case "drawGraph": + var date=new Date(); + var dateStr=require("locale").date(date).trim(); + var hrs=date.getHours(); + var mins=date.getMinutes(); + var secs=date.getSeconds(); + graph.push({ + temp:temp, + date:dateStr, + hrs:hrs, + mins:mins, + secs:secs + }); + if(graph.length==1){ + graph[0].dur=durInd; + } + require("Storage").writeJSON("tempgraph.json", graph); + if(graph.length==150){ + clearInterval(dg); + } + drawG(); + } + }); +} + +function getTemp(){ + readErrCnt=0; + gt = setInterval(getT,800); +} + +function setButton(){ + var watchButton=setWatch(function(){ + clearInterval(gt); + clearInterval(dg); + clearWatch(watchButton); + Bangle.removeListener("touch",screenTouch); + openMenu(); + },BTN); + Bangle.on('touch',screenTouch); +} + +function setButton2(){ + watchButton2=setWatch(function(){ + clearWatch(watchButton2); + openMenu(); + },BTN); +} + +function zPad(n){ + return n.toString().padStart(2,0); +} + +function screenTouch(n,ev){ + if(ev.y>23&&ev.y<152){ + C=C==false; + drawG(false); + } +} + +function drawG(){ + function cf(t){ + if(C){ + return t; + } + return getF(t); + } + drawWids(); + var top=1; + var bar=21; + var barBot=175-22; + if(widsOn){ + top=25; + bar=bar+24; + barBot=barBot-24; + } + var low=graph[0].temp; + var hi=low; + for(n=0;nt){ + low=t; + } + if(hi10){ + div=5; + } + if(C){ + g.setColor(1,0,0); + }else{ + g.setColor(0,0,1); + } + var step=(barBot-bar)/((tempHi-tempLow)/div); + for(n=0;nexit()}, + ],lazy:true + }); + drawWids(); + messageLO.render(); +} + +function showT(){ + tempLO.lab1.label=tempLO.lab3.label; + tempLO.lab2.label=tempLO.lab4.label; + tempLO.lab3.label=tempLO.lab5.label; + tempLO.lab4.label=tempLO.lab6.label; + tempLO.lab5.label=temp.toFixed(2)+"C"; + tempLO.lab6.label=getF(temp).toFixed(2)+"F"; + tempLO.render(); +} + +function exit(){ + clearWatch(watchButton2); + openMenu(); +} + +function showTemp(){ + tempMode="showTemp"; + setButton2(); + tempLO=new Layout({ + type:"v",c:[ + {type:"h",c:[ + {type:"txt",pad:5,col:"#f77",font:"6x8:2",label:" ",id:"lab1"}, + {type:"txt",pad:5,col:"#77f",font:"6x8:2",label:" ",id:"lab2"} + ]}, + {type:"h",c:[ + {type:"txt",pad:5,col:"#f77",font:"6x8:2",label:" ",id:"lab3"}, + {type:"txt",pad:5,col:"#77f",font:"6x8:2",label:" ",id:"lab4"} + ]}, + {type:"h",c:[ + {type:"txt",pad:5,col:"#f00",font:"6x8:2",label:" ",id:"lab5"}, + {type:"txt",pad:5,col:"#00f",font:"6x8:2",label:" ",id:"lab6"} + ]}, + {type:"h",c:[ + {type:"btn",pad:2,font:"6x8:2",label:"Temp",cb:l=>getTemp()}, + {type:"btn",pad:2,font:"6x8:2",label:"Exit",cb:l=>exit()} + ]} + ] + },{lazy:true}); + tempLO.render(); + getTemp(); +} + +var menu={ + "":{ + "title":" Temp. Graph" + }, + + "Widgets":{ + value:widsOn, + format:vis=>vis?"Hide":"Show", + onchange:vis=>{ + widsOn=vis; + refreshMenu(); + } + }, + + "Duration":{ + value:times.indexOf(duration), + min:0,max:times.length-1,step:1,wrap:true, + format:tim=>times[tim], + onchange:(dur)=>{ + duration=times[dur]; + } + }, + + "Draw Graph":function(){ + E.showMenu(); + drawGraph(); + }, + + "Show Graph" : function(){ + E.showMenu(); + if(graph.length>0){ + showGraph(); + }else{ + message("No graph to\nshow as no\ngraph has been\ndrawn yet."); + } + }, + + "Save Graph" : function(){ + E.showMenu(); + if(graph.length>0){ + saveGraph(); + }else{ + message("No graph to\nsave as no\ngraph has been\ndrawn yet."); + } + }, + + "Save Data" : function(){ + E.showMenu(); + if(graph.length>0){ + saveData(); + }else{ + message("No data to\nsave as no\ngraph has been\ndrawn yet."); + } + }, + + "Show Temp":function(){ + E.showMenu(); + showTemp(); + } +}; + +openMenu(); diff --git a/apps/tempgraph/metadata.json b/apps/tempgraph/metadata.json index 6772429a5..63d4feddd 100644 --- a/apps/tempgraph/metadata.json +++ b/apps/tempgraph/metadata.json @@ -1,7 +1,7 @@ { "id": "tempgraph", "name": "Temperature Graph", "shortName":"Temp Graph", - "version":"0.01", + "version":"0.03", "description": "An app for recording the temperature for time periods ranging from 10 minutes to 7 days.", "icon": "app.png", "type": "app", diff --git a/apps/widbat/ChangeLog b/apps/widbat/ChangeLog index 5986ecf3f..59c58af32 100644 --- a/apps/widbat/ChangeLog +++ b/apps/widbat/ChangeLog @@ -6,3 +6,4 @@ 0.07: Move CHARGING variable to more readable string 0.08: Ensure battery updates every 60s even if LCD was on at boot and stays on 0.09: Misc speed/memory tweaks +0.10: Color changes due to the battery level diff --git a/apps/widbat/metadata.json b/apps/widbat/metadata.json index 993310eb2..5f0d1b7d1 100644 --- a/apps/widbat/metadata.json +++ b/apps/widbat/metadata.json @@ -1,7 +1,7 @@ { "id": "widbat", "name": "Battery Level Widget", - "version": "0.09", + "version": "0.10", "description": "Show the current battery level and charging status in the top right of the clock", "icon": "widget.png", "type": "widget", diff --git a/apps/widbat/widget.js b/apps/widbat/widget.js index a8a0c5382..7fce16335 100644 --- a/apps/widbat/widget.js +++ b/apps/widbat/widget.js @@ -31,7 +31,11 @@ x+=16; } g.setColor(g.theme.fg).fillRect(x,y+2,x+s-4,y+21).clearRect(x+2,y+4,x+s-6,y+19).fillRect(x+s-3,y+10,x+s,y+14); - g.setColor("#0f0").fillRect(x+4,y+6,x+4+E.getBattery()*(s-12)/100,y+17); + var battery = E.getBattery(); + if(battery < 20) {g.setColor("#f00");} + else if (battery < 50) {g.setColor("#ff0");} + else {g.setColor("#0f0");} + g.fillRect(x+4,y+6,x+4+battery*(s-12)/100,y+17); }}; setWidth(); })() diff --git a/lang/it_IT.json b/lang/it_IT.json index 310af8580..72d7dbb51 100644 --- a/lang/it_IT.json +++ b/lang/it_IT.json @@ -200,7 +200,15 @@ "Green": "Verde", "Blue": "Blu", "Black": "Nero", - "Show Week Number": "Mostra numero settimana" + "Show Week Number": "Mostra numero settimana", + "Calendar": "Calendario", + "Start": "Inizio", + "End": "Fine", + "Location": "Posizione", + "No location": "Nessuna posizione", + "No events": "Nessun evento", + "Today": "Oggi", + "Tomorrow": "Domani" }, "//2": "App-specific overrides", "alarm": {