-
-
-
-
Hi user136708
I tried out cliockJS2Enhanced but could not find the sunrise / sunset code or display.
BTW the way. I think if you update to the latest firmware you will ge able to get the function mode to work again. But I was thinking about taking that out as its a bit confusing - better to keep the App simple. There's no real need to be able to power the heart rate sensor on / off in this App. If you want heart rate you should use a different App etc.
In Bangle 2 - I think the way this should work is swipe left / right to cycle forwards or backwards through the different info displays. And maybe sunrise and sunset should be options in that list.
There is also a much more elegant way to code cycling through these displays which results in a lot less code and variables. See gpstouch app for how I did this - which I copied from another clock that quoted cliock as inspiration. I might spend an evening revisting the cliock to improve the code.
I like what you did to make the TIME easier to read. -
Hi user136708, @Gordon
I tried out your cliockJS2Enhanced clock as I wanted to see what you had done with sunrise / sunset.
I did some work on cliock back in Januarry I added the infoMode and FunctionMode lines. I cant claim to be the original coder of CLIOCK - but I did add the info mode line concept.@Gordon - I was not able to get infoMode to cycle. There no longer appears to be a BTN1 event.
I think this problem got introduced in v0.14 with your Fix BTN1 change.
Basically I would expect to be able to tap the top right edge of the bangle 2 and for the info mode display to cycle. This no longer happens on both watches.functionMode still works as tapping in botton left will bring up the HRM On/Off control.
Does the BTN1 fix require a specific version of firmware to work.
Update: Looks like I need a firmware update
https://github.com/espruino/BangleApps/issues/853
Update: Updated firmware and BTN1 works again. -
What do you mean by: I had to fully rebuild my watch ?
I was updating apps using the update button in the App Loader - but 5 apps failed to upload with an error message about missing a file.
The watch rebooted and stayed at the 'checking storage ....' message.
I waited for maybe 2-3 minutes as I have experienced this once before and you asked if I had waited more than 30 seconds.I then did a BTN long press reboot and same thing happenned - boot hung at checking storage...
I then connected the App Loader and attempted 'install default apps'.
It went through the 'Erasing Phase' but then failed to install the default apps.At which point I manually installed the default Apps one by one; starting with boot loader, launcher and a clock. This is what I mean by had to rebuild my watch.
My Kickstart Bangle 2 has come now so I have two Bangle 2s. So I might play around and see if I can find a sure fire way of reproducing this. I am pretty certain that both times I have used the UpdateApps button rather than choosing to upgrade one by one; but could be wrong. I'll need to get both watches in the same config state - same firmware same apps, same versions first though.
-
Hi user136708,
Rather than writing the GPS lat lon into cliock it would be better to write an app called my location. All mylocation will do is get a gps fix and write lat/lon to mylocation. Json. Then every app that needs your main location can read that. I had thought of doing this myself as I want to add sunrise and sunset to one of my clocks.
Also I wrote a library for gps, called geoutils. Its in gps touch app. I would be interested in extending that with the sunrise sunset calculations based on the value stored in mylocation. Json.
-
I had to fully rebuild my watch (default Apps failed) after 5 apps failed to upgrade.
When the watch rebooted it got stuck saying 'checking storage' after the boot screen.
I could connect through the App Loader.
Tried to install default apps, erased apps then failed to install.
Installed Bootloader, Launcher and a couple of Apps and recovered.I had the same thing happen a while ago on a Bangle 2, could not find the thread though.
-
-
-
-
-
@Gordon - here's my code - have a play with it and let me know what you think.
I'm enjoying the Health App a lot more since I have done this.function getSettings() { return require("Storage").readJSON("health.json",1)||{}; } function setSettings(s) { require("Storage").writeJSON("health.json",s); } function menuMain() { swipe_enabled = false; E.showMenu({ "":{title:"Health Tracking"}, "< Back":()=>load(), "Step Counting":()=>menuStepCount(), "Movement":()=>menuMovement(), "Heart Rate":()=>menuHRM(), "Settings":()=>menuSettings() }); } function menuSettings() { swipe_enabled = false; var s=getSettings(); E.showMenu({ "":{title:"Health Tracking"}, "< Back":()=>menuMain(), "Heart Rt":{ value : 0|s.hrm, min : 0, max : 2, format : v=>["Off","10 mins","Always"][v], onchange : v => { s.hrm=v;setSettings(s); } } }); } function menuStepCount() { swipe_enabled = false; E.showMenu({ "":{title:"Step Counting"}, "< Back":()=>menuMain(), "per hour":()=>stepsPerHour(), "per day":()=>stepsPerDay() }); } function menuMovement() { swipe_enabled = false; E.showMenu({ "":{title:"Movement"}, "< Back":()=>menuMain(), "per hour":()=>movementPerHour(), "per day":()=>movementPerDay(), }); } function menuHRM() { swipe_enabled = false; E.showMenu({ "":{title:"Heart Rate"}, "< Back":()=>menuMain(), "per hour":()=>hrmPerHour(), "per day":()=>hrmPerDay(), }); } function stepsPerHour() { E.showMessage("Loading..."); var data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.steps); console.log(data); g.clear(1); Bangle.drawWidgets(); g.reset(); Bangle.setUI("updown", ()=>menuStepCount()); barChart("HOUR", data); } function stepsPerDay() { E.showMessage("Loading..."); var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps); console.log(data); g.clear(1); Bangle.drawWidgets(); g.reset(); Bangle.setUI("updown", ()=>menuStepCount()); barChart("DAY", data); } function hrmPerHour() { E.showMessage("Loading..."); var data = new Uint16Array(24); var cnt = new Uint8Array(23); require("health").readDay(new Date(), h=>{ data[h.hr]+=h.bpm; if (h.bpm) cnt[h.hr]++; }); data.forEach((d,i)=>data[i] = d/cnt[i]); g.clear(1); Bangle.drawWidgets(); g.reset(); Bangle.setUI("updown", ()=>menuHRM()); barChart("HOUR", data); } function hrmPerDay() { E.showMessage("Loading..."); var data = new Uint16Array(31); var cnt = new Uint8Array(31); require("health").readDailySummaries(new Date(), h=>{ data[h.day]+=h.bpm; if (h.bpm) cnt[h.day]++; }); data.forEach((d,i)=>data[i] = d/cnt[i]); g.clear(1); Bangle.drawWidgets(); g.reset(); Bangle.setUI("updown", ()=>menuHRM()); barChart("DAY", data); } function movementPerHour() { E.showMessage("Loading..."); var data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.movement); g.clear(1); Bangle.drawWidgets(); g.reset(); Bangle.setUI("updown", ()=>menuMovement()); barChart("HOUR", data); } function movementPerDay() { E.showMessage("Loading..."); var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement); g.clear(1); Bangle.drawWidgets(); g.reset(); Bangle.setUI("updown", ()=>menuMovement()); barChart("DAY", data); } // Bar Chart Code const w = g.getWidth(); const h = g.getHeight(); // find the max value in the array, using a loop due to array size function max(arr) { var m = -Infinity; for(var i=0; i< arr.length; i++) if(arr[i] > m) m = arr[i]; return m; } // find the end of the data, the array might be for 31 days but only have 2 days in it function get_data_length(arr) { var nlen = arr.length; for(var i = arr.length - 1; i > 0 && arr[i] == 0; i--) nlen--; return nlen; } var data_len; var chart_index; var chart_max_datum; var chart_label; var chart_data; var swipe_enabled = false; function barChart(label, dt) { data_len = get_data_length(dt); chart_index = Math.max(data_len - 5, -5); // choose initial index that puts the last day on the end chart_max_datum = max(dt); // find highest bar, for scaling chart_label = label; chart_data = dt; drawBarChart(); swipe_enabled = true; } function drawBarChart() { const bar_bot = 140; const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre var bar_top; var bar; g.setColor(g.theme.bg); g.fillRect(0,24,w,h); for (bar = 1; bar < 10; bar++) { if (bar == 5) { g.setFont('6x8', 2); g.setFontAlign(0,-1) g.setColor(g.theme.fg); g.drawString(chart_label + " " + (chart_index + bar -1) + " " + chart_data[chart_index + bar - 1], g.getWidth()/2, 150); g.setColor("#00f"); } else { g.setColor("#0ff"); } // draw a fake 0 height bar if chart_index is outside the bounds of the array if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len) bar_top = bar_bot - 100 * (chart_data[chart_index + bar - 1]) / chart_max_datum; else bar_top = bar_bot; g.fillRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); g.setColor(g.theme.fg); g.drawRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); } } function next_bar() { chart_index = Math.min(data_len - 5, chart_index + 1); } function prev_bar() { chart_index = Math.max(-4, chart_index - 1); } Bangle.on('swipe', dir => { if (!swipe_enabled) return; if (dir == 1) prev_bar(); else next_bar(); drawBarChart(); }); Bangle.loadWidgets(); Bangle.drawWidgets(); menuMain();
-
I guess one question is whether the extra granularity of data displayed by the health app is useful?
Not quite sure what you mean here. I think you mean - is the by hour display useful. Then yes I think its great to look at the day by hour and confirm or challenge the mental picture that has been built up in your mind as to how active you were across a 24 hour perioid.
-
Ok - I will post my latest code for the health.app.js below.
I have only tested on Bangle JS 2 for now.A few questions arise
1) Regarding the retrieval of steps per day. The array that gets retrieved has a 0 at index 0.On Thursday 11th Nov - looks like this:
DAY 0 1 2 3 4 5 6 7 8 9 10 DATA [0, 6, 33, 122, 2027, 7703, 11378, 7649, 4105, 3246, 5559, ....
Day 11 has yet to conclude so we have 10 days of data.
So basically the zero element is a dummy value of 0 for day data.
When dsiplaying the chart we want to display from day 1 to last day of month.The hour data hour has significance for the 0 index - in that it is the first hour of the day from 00:00 - when displaying the chart we will want to display from hour 0-23.
I think it would be more consistant to retrieve the data with the zero element always being significant - this way the chart labelling can be adjusted by an offset of 1 or 0 - rather than having to skip the first entry for day data or split into a new array starting at element 1. Hope that makes sense.
2) Movement Data for Days is strange ?
Basically as in the two charts below.
Today my general movements, sitting and walking was the same as yesterday.
Each day is getting 255 for the day - where as hour 8 this morning was 356.I guess what I am asking is - what is the unit of movement in an hour ?
What is the unit for of movement for a day and how does that relate to the hours movement ?
I am also seeing approx 90-100 movements during sleeping hours.
Rolling over in bed should not score as movement.I think movement should be defined in terms of breaking the 8 steps in 8 threshold.
So how many minutes was I actively walking in that hour ? This is a good idea as the point
about healthy movement is that you have to stand for so many minutes per hour, feel the force of gravity on your bones etc. By breaking the step threshold we can say you were standing for that time. We should not be scoring movement when sitting on a sofa or sleeping.This means if you sit at a desk for an hour you score 0 BUT if you get up for 5 minutes walk to the bathroom for a toilet break you score 5. So if you stand every 20 minutes for 2 minutes you score 6 in that hour. A full hours walking scores 60. Being active for every minute of 8 hours would score 480. A full days mounting climbing for 8 hours could be made to register as 100 movement points for the day. So an average commute day is going to look around 60 minutes of activity or a 12.5 point day. This would give a good explanable baseline for activity.
-
// test data //var data = new Uint16Array([1023, 1020, 5300, 3178, 1500, 10600, 5700, 1008, 5090, 1010, 6100, 712]); // OR pull data from the health app var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps); const w = g.getWidth(); const h = g.getHeight(); // find the max value in the array, using a loop due to array size function max(arr) { var m = -Infinity; for(var i=0; i< arr.length; i++) if(arr[i] > m) m = arr[i]; return m; } // find the end of the data, the array might be for 31 days but only have 2 days in it function get_data_length(arr) { var nlen = arr.length; for(var i = arr.length - 1; i > 0 && arr[i] == 0; i--) nlen--; return nlen; } // choose initial index that puts the last day on the end const len = get_data_length(data); var index = Math.max(len - 5, -5); const max_datum = max(data); // find highest bar, for scaling function draw() { const bar_bot = 140; const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre var bar_top; var bar; g.setColor(g.theme.bg); g.fillRect(0,24,w,h); for (bar = 1; bar < 10; bar++) { if (bar == 5) { g.setFont('6x8', 2); g.setFontAlign(0,-1) g.setColor(g.theme.fg); g.drawString("DAY " + (index + bar) + " " + data[index + bar - 1], g.getWidth()/2, 150); g.setColor("#00f"); } else { g.setColor("#0ff"); } // draw a fake 0 height bar if index is outside the bounds of the array if ((index + bar - 1) >= 0 && (index + bar - 1) < len) bar_top = bar_bot - 100 * (data[index + bar - 1]) / max_datum; else bar_top = bar_bot; g.fillRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); g.setColor(g.theme.fg); g.drawRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); } } function next() { index = Math.min(len - 5, index + 1); //console.log("next="+index); } function prev() { index = Math.max(-4, index - 1); //console.log("prev="+index); } Bangle.on('swipe', dir => { if (dir == 1) prev(); else next(); draw(); }); // handle switch display on by pressing BTN1 Bangle.on('lcdPower', function(on) { if (on) draw(); }); draw(); Bangle.loadWidgets(); Bangle.drawWidgets(); Bangle.setUI("clock"); // returns to launcher for now
-
@Gordon
I have been experimenting with a bar chart format that is a bit more readable for the Health App.
I'm finding the existing charts too small to read.In the Day 9 example 1 - you can see the chart loads and highlights the previous day in the centre of the screen. This is the last day in the history.
In the Day 7 example, I have swiped right twice to scroll back one day at a time and see the exact step count day by day. Of course you can swipe left again and reverse. In this way you could review a whole month. The day under view is always displayed in the centre of the screen as blue. The data is displayed below the chart. I actually nicked the idea from an android app that works this way. I also like the fact that the charts have attractive colors. It makes it feel a lot more polished as a user experience.
I have done some example code - but not sure how to make it model (wait for a BTN1 press in Bangle 2). Somehow this could be added to the graph module or just embedded in the health app.
-
-
@labarks - how is the launcher coming along. I'm interested to see what it looks like. What name have you given it?
-
-
-
-
-
@Gordon what would be needed to do this?