circlesclock 0.19: Remove old code and fixing clkinfo handling (fix HRM and other items that change)

pull/2372/head
Gordon Williams 2022-12-07 09:58:45 +00:00
parent 3e5c0538a9
commit 973b8e1565
4 changed files with 66 additions and 638 deletions

View File

@ -32,3 +32,4 @@
Use widget_utils if available
0.17: Load circles from clkinfo
0.18: Improved clkinfo handling and using it for the weather circle
0.19: Remove old code and fixing clkinfo handling (fix HRM and other items that change)

View File

@ -18,7 +18,6 @@ let settings = Object.assign(
storage.readJSON("circlesclock.default.json", true) || {},
storage.readJSON(SETTINGS_FILE, true) || {}
);
//TODO deprecate this (and perhaps use in the clkinfo module)
// Load step goal from health app and pedometer widget as fallback
if (settings.stepGoal == undefined) {
@ -31,17 +30,7 @@ if (settings.stepGoal == undefined) {
}
}
let timerHrm; //TODO deprecate this
let drawTimeout;
/*
* Read location from myLocation app
*/
function getLocation() {
return storage.readJSON("mylocation.json", 1) || undefined;
}
let location = getLocation();
let showWidgets = settings.showWidgets || false;
let circleCount = settings.circleCount || 3;
let showBigWeather = settings.showBigWeather || false;
@ -106,39 +95,9 @@ let circleItemNum = [
2, // circle3
3, // circle4
];
let weatherCircleNum = 0;
let weatherCircleDataNum = 0;
let weatherCircleCondNum = 0;
let weatherCircleTempNum = 0;
function hideWidgets() {
/*
* we are not drawing the widgets as we are taking over the whole screen
* so we will blank out the draw() functions of each widget and change the
* area to the top bar doesn't get cleared.
*/
if (WIDGETS && typeof WIDGETS === "object") {
for (let wd of WIDGETS) {
wd.draw = () => {};
wd.area = "";
}
}
}
function draw() {
g.clear(true);
let widgetUtils;
try {
widgetUtils = require("widget_utils");
} catch (e) {
}
if (!showWidgets) {
if (widgetUtils) widgetUtils.hide(); else hideWidgets();
} else {
if (widgetUtils) widgetUtils.show();
Bangle.drawWidgets();
}
g.reset().clearRect(Bangle.appRect);
g.setColor(colorBg);
g.fillRect(0, widgetOffset, w, h2 + 22);
@ -180,117 +139,18 @@ function draw() {
if (icon) g.drawImage(icon, w - 48, h1, {scale:0.75});
}
drawCircle(1);
drawCircle(2);
drawCircle(3);
if (circleCount >= 4) drawCircle(4);
// FIXME do we really need to redraw circles every time?
for (var i=1;i<=circleCount;i++)
drawCircle(i);
queueDraw();
}
function drawCircle(index) {
let type = settings['circle' + index];
if (!type) type = defaultCircleTypes[index - 1];
let w = getCircleXPosition(type);
switch (type) {
case "weather":
drawWeather(w);
break;
case "sunprogress":
case "sunProgress":
drawSunProgress(w);
break;
//TODO those are going to be deprecated, keep for backwards compatibility for now
//ideally all data should come from some clkinfo
case "steps":
drawSteps(w);
break;
case "stepsDist":
drawStepsDistance(w);
break;
case "hr":
drawHeartRate(w);
break;
case "battery":
drawBattery(w);
break;
case "temperature":
drawTemperature(w);
break;
case "pressure":
drawPressure(w);
break;
case "altitude":
drawAltitude(w);
break;
//end deprecated
case "empty":
// we draw nothing here
return;
default:
drawClkInfo(index, w);
}
}
// serves as cache for quicker lookup of circle positions
let circlePositionsCache = [];
/*
* Looks in the following order if a circle with the given type is somewhere visible/configured
* 1. circlePositionsCache
* 2. settings
* 3. defaultCircleTypes
*
* In case 2 and 3 the circlePositionsCache will be updated
*/
function getCirclePosition(type) {
if (circlePositionsCache[type] >= 0) {
return circlePositionsCache[type];
}
for (let i = 1; i <= circleCount; i++) {
let setting = settings['circle' + i];
if (setting == type) {
circlePositionsCache[type] = i - 1;
return i - 1;
}
}
for (let i = 0; i < defaultCircleTypes.length; i++) {
if (type == defaultCircleTypes[i] && (!settings || settings['circle' + (i + 1)] == undefined)) {
circlePositionsCache[type] = i;
return i;
}
}
return undefined;
}
function getCircleXPosition(type) {
let circlePos = getCirclePosition(type);
if (circlePos != undefined) {
return circlePosX[circlePos];
}
return undefined;
}
function isCircleEnabled(type) {
return getCirclePosition(type) != undefined;
}
function getCircleColor(type) {
let pos = getCirclePosition(type);
let color = settings["circle" + (pos + 1) + "color"];
function getCircleColor(index) {
let color = settings["circle" + index + "color"];
if (color && color != "") return color;
}
function getCircleIconColor(type, color, percent) {
let pos = getCirclePosition(type);
let colorizeIcon = settings["circle" + (pos + 1) + "colorizeIcon"] == true;
if (colorizeIcon) {
return getGradientColor(color, percent);
} else {
return "";
}
}
function getGradientColor(color, percent) {
if (isNaN(percent)) percent = 0;
if (percent > 1) percent = 1;
@ -311,422 +171,15 @@ function getGradientColor(color, percent) {
return color;
}
function getImage(graphic, color) {
if (!color || color == "") {
return graphic;
function getCircleIconColor(index, color, percent) {
let colorizeIcon = settings["circle" + index + "colorizeIcon"] == true;
if (colorizeIcon) {
return getGradientColor(color, percent);
} else {
return {
width: 16,
height: 16,
bpp: 1,
transparent: 0,
buffer: E.toArrayBuffer(graphic),
palette: new Uint16Array([colorBg, g.toColor(color)])
};
return "";
}
}
function drawWeather(w) {
if (!w) w = getCircleXPosition("weather");
let weatherInfo = menu[weatherCircleNum];
let weatherCond = weatherCircleCondNum >= 0? weatherInfo.items[weatherCircleCondNum]: undefined;
let weatherData = weatherCircleDataNum >= 0? weatherInfo.items[weatherCircleDataNum]: undefined;
let weatherTemp = weatherCircleTempNum >= 0? weatherInfo.items[weatherCircleTempNum]: undefined;
let color = getCircleColor("weather");
let percent = 0;
let data = settings.weatherCircleData;
let tempString = "?", icon = undefined;
let scale = 16/24; //our icons are 16x16 while clkinfo's are 24x24
if(weatherCond) {
weatherCond.show()
weatherCond.hide()
let data = weatherCond.get()
if(settings.legacyWeatherIcons) { //may disappear in future
icon = getWeatherIconByCode(data.v);
scale = 1;
} else
icon = data.img;
}
if(weatherTemp) {
weatherTemp.show()
weatherTemp.hide()
tempString = weatherTemp.get().text;
}
drawCircleBackground(w);
if(weatherData) {
weatherData.show();
weatherData.hide();
let data = weatherData.get();
if(weatherData.hasRange) percent = (data.v-data.min) / (data.max-data.min);
drawGauge(w, h3, percent, color);
}
drawInnerCircleAndTriangle(w);
writeCircleText(w, tempString);
if(icon) {
g.setColor(getCircleIconColor("weather", color, percent))
.drawImage(icon, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: scale});
} else {
g.drawString("?", w, h3 + radiusOuter);
}
}
function drawWeatherOld(w) {
if (!w) w = getCircleXPosition("weather");
let weather = getWeather();
let tempString = weather ? locale.temp(weather.temp - 273.15) : undefined;
let code = weather ? weather.code : -1;
drawCircleBackground(w);
let color = getCircleColor("weather");
let percent;
let data = settings.weatherCircleData;
switch (data) {
case "humidity":
let humidity = weather ? weather.hum : undefined;
if (humidity >= 0) {
percent = humidity / 100;
drawGauge(w, h3, percent, color);
}
break;
case "wind":
if (weather) {
let wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
if (wind[1] >= 0) {
if (wind[2] == "kmh") {
wind[1] = windAsBeaufort(wind[1]);
}
// wind goes from 0 to 12 (see https://en.wikipedia.org/wiki/Beaufort_scale)
percent = wind[1] / 12;
drawGauge(w, h3, percent, color);
}
}
break;
case "empty":
break;
}
drawInnerCircleAndTriangle(w);
writeCircleText(w, tempString ? tempString : "?");
if (code > 0) {
let icon = getWeatherIconByCode(code);
if (icon) g.drawImage(getImage(icon, getCircleIconColor("weather", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
} else {
g.drawString("?", w, h3 + radiusOuter);
}
}
function drawSunProgress(w) {
if (!w) w = getCircleXPosition("sunprogress");
let percent = getSunProgress();
// sunset icons:
let sunSetDown = atob("EBCBAAAAAAABgAAAAAATyAZoBCB//gAAAAAGYAPAAYAAAAAA");
let sunSetUp = atob("EBCBAAAAAAABgAAAAAATyAZoBCB//gAAAAABgAPABmAAAAAA");
drawCircleBackground(w);
let color = getCircleColor("sunprogress");
drawGauge(w, h3, percent, color);
drawInnerCircleAndTriangle(w);
let icon = sunSetDown;
let text = "?";
let times = getSunData();
if (times != undefined) {
let sunRise = Math.round(times.sunrise.getTime() / 1000);
let sunSet = Math.round(times.sunset.getTime() / 1000);
if (!isDay()) {
// night
if (now > sunRise) {
// after sunRise
let upcomingSunRise = sunRise + 60 * 60 * 24;
text = formatSeconds(upcomingSunRise - now);
} else {
text = formatSeconds(sunRise - now);
}
icon = sunSetUp;
} else {
// day, approx sunrise tomorrow:
text = formatSeconds(sunSet - now);
icon = sunSetDown;
}
}
writeCircleText(w, text);
g.drawImage(getImage(icon, getCircleIconColor("sunprogress", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
}
/*
* Deprecated but nice as references for clkinfo
*/
function drawSteps(w) {
if (!w) w = getCircleXPosition("steps");
let steps = getSteps();
drawCircleBackground(w);
let color = getCircleColor("steps");
let percent;
let stepGoal = settings.stepGoal;
if (stepGoal > 0) {
percent = steps / stepGoal;
if (stepGoal < steps) percent = 1;
drawGauge(w, h3, percent, color);
}
drawInnerCircleAndTriangle(w);
writeCircleText(w, shortValue(steps));
g.drawImage(getImage(atob("EBCBAAAACAAcAB4AHgAeABwwADgGeAZ4AHgAMAAAAHAAIAAA"), getCircleIconColor("steps", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
}
function drawStepsDistance(w) {
if (!w) w = getCircleXPosition("stepsDistance");
let steps = getSteps();
let stepDistance = settings.stepLength;
let stepsDistance = Math.round(steps * stepDistance);
drawCircleBackground(w);
let color = getCircleColor("stepsDistance");
let percent;
let stepDistanceGoal = settings.stepDistanceGoal;
if (stepDistanceGoal > 0) {
percent = stepsDistance / stepDistanceGoal;
if (stepDistanceGoal < stepsDistance) percent = 1;
drawGauge(w, h3, percent, color);
}
drawInnerCircleAndTriangle(w);
writeCircleText(w, shortValue(stepsDistance));
g.drawImage(getImage(atob("EBCBAAAACAAcAB4AHgAeABwwADgGeAZ4AHgAMAAAAHAAIAAA"), getCircleIconColor("stepsDistance", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
}
function drawHeartRate(w) {
if (!w) w = getCircleXPosition("hr");
let heartIcon = atob("EBCBAAAAAAAeeD/8P/x//n/+P/w//B/4D/AH4APAAYAAAAAA");
drawCircleBackground(w);
let color = getCircleColor("hr");
let percent;
if (hrtValue != undefined) {
let minHR = settings.minHR;
let maxHR = settings.maxHR;
percent = (hrtValue - minHR) / (maxHR - minHR);
if (isNaN(percent)) percent = 0;
drawGauge(w, h3, percent, color);
}
drawInnerCircleAndTriangle(w);
writeCircleText(w, hrtValue != undefined ? hrtValue : "-");
g.drawImage(getImage(heartIcon, getCircleIconColor("hr", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
}
function drawBattery(w) {
if (!w) w = getCircleXPosition("battery");
let battery = E.getBattery();
let powerIcon = atob("EBCBAAAAA8ADwA/wD/AP8A/wD/AP8A/wD/AP8A/wD/AH4AAA");
drawCircleBackground(w);
let color = getCircleColor("battery");
let percent;
if (battery > 0) {
percent = battery / 100;
drawGauge(w, h3, percent, color);
}
drawInnerCircleAndTriangle(w);
if (Bangle.isCharging()) {
color = colorGreen;
} else {
if (settings.batteryWarn != undefined && battery <= settings.batteryWarn) {
color = colorRed;
}
}
writeCircleText(w, battery + '%');
g.drawImage(getImage(powerIcon, getCircleIconColor("battery", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
}
function drawTemperature(w) {
if (!w) w = getCircleXPosition("temperature");
getPressureValue("temperature").then((temperature) => {
drawCircleBackground(w);
let color = getCircleColor("temperature");
let percent;
if (temperature) {
let min = -40;
let max = 85;
percent = (temperature - min) / (max - min);
drawGauge(w, h3, percent, color);
}
drawInnerCircleAndTriangle(w);
if (temperature)
writeCircleText(w, locale.temp(temperature));
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("temperature", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
});
}
function drawPressure(w) {
if (!w) w = getCircleXPosition("pressure");
getPressureValue("pressure").then((pressure) => {
drawCircleBackground(w);
let color = getCircleColor("pressure");
let percent;
if (pressure && pressure > 0) {
let minPressure = 950;
let maxPressure = 1050;
percent = (pressure - minPressure) / (maxPressure - minPressure);
drawGauge(w, h3, percent, color);
}
drawInnerCircleAndTriangle(w);
if (pressure)
writeCircleText(w, Math.round(pressure));
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("pressure", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
});
}
function drawAltitude(w) {
if (!w) w = getCircleXPosition("altitude");
getPressureValue("altitude").then((altitude) => {
drawCircleBackground(w);
let color = getCircleColor("altitude");
let percent;
if (altitude) {
let min = 0;
let max = 10000;
percent = (altitude - min) / (max - min);
drawGauge(w, h3, percent, color);
}
drawInnerCircleAndTriangle(w);
if (altitude)
writeCircleText(w, locale.distance(Math.round(altitude)));
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("altitude", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
});
}
function shortValue(v) {
if (isNaN(v)) return '-';
if (v <= 999) return v;
if (v >= 1000 && v < 10000) {
v = Math.floor(v / 100) * 100;
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
}
if (v >= 10000) {
v = Math.floor(v / 1000) * 1000;
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
}
}
function getSteps() {
if (Bangle.getHealthStatus) {
return Bangle.getHealthStatus("day").steps;
}
if (WIDGETS && WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom.getSteps();
}
return 0;
}
function getPressureValue(type) {
return new Promise((resolve) => {
if (Bangle.getPressure) {
if (!pressureLocked) {
pressureLocked = true;
if (pressureCache && pressureCache[type]) {
resolve(pressureCache[type]);
}
Bangle.getPressure().then(function(d) {
pressureLocked = false;
if (d) {
pressureCache = d;
if (d[type]) {
resolve(d[type]);
}
}
}).catch(() => {});
} else {
if (pressureCache && pressureCache[type]) {
resolve(pressureCache[type]);
}
}
}
});
}
/*
* end deprecated
*/
var menu = null;
function reloadMenu() {
menu = clock_info.load();
for(var i=1; i<5; i++)
if(settings['circle'+i].includes("/")) {
let parts = settings['circle'+i].split("/");
let infoName = parts[0], itemName = parts[1];
let infoNum = menu.findIndex(e=>e.name==infoName);
let itemNum = 0; //get first if dynamic
if(!menu[infoNum].dynamic)
itemNum = menu[infoNum].items.findIndex(it=>it.name==itemName);
circleInfoNum[i-1] = infoNum;
circleItemNum[i-1] = itemNum;
} else if(settings['circle'+i] == "weather") {
weatherCircleNum = menu.findIndex(e=>e.name.toLowerCase() == "weather");
weatherCircleDataNum = menu[weatherCircleNum].items.findIndex(it=>it.name==settings.weatherCircleData);
weatherCircleCondNum = menu[weatherCircleNum].items.findIndex(it=>it.name=="condition");
weatherCircleTempNum = menu[weatherCircleNum].items.findIndex(it=>it.name=="temperature");
}
}
//reload periodically for changes?
reloadMenu();
function drawEmpty(img, w, color) {
drawGauge(w, h3, 0, color);
drawInnerCircleAndTriangle(w);
@ -736,45 +189,32 @@ function drawEmpty(img, w, color) {
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
}
function drawClkInfo(index, w) {
function drawCircle(index) {
var info = menu[circleInfoNum[index-1]];
var type = settings['circle'+index];
if (!w) w = getCircleXPosition(type);
var w = circlePosX[index-1];
drawCircleBackground(w);
const color = getCircleColor(type);
var item = info.items[circleItemNum[index-1]];
const color = getCircleColor(index);
var item = info && info.items[circleItemNum[index-1]];
if(!info || !item) {
drawEmpty(info? info.img : null, w, color);
return;
}
item.show();
item.hide();
var data=item.get();
var img = data.img;
var percent = 1; //fill up if no range
var txt = data.text;
var txt = ""+data.text;
if (txt.endsWith(" bpm")) txt=txt.slice(0,-4); // hack for heart rate - remove the 'bpm' text
if(!img) img = info.img;
if(item.hasRange) percent = (data.v-data.min) / (data.max-data.min);
if(data.short) txt = data.short;
drawGauge(w, h3, percent, color);
drawInnerCircleAndTriangle(w);
writeCircleText(w, txt);
g.setColor(getCircleIconColor(type, color, percent))
g.setColor(getCircleIconColor(index, color, percent))
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
}
/*
* wind goes from 0 to 12 (see https://en.wikipedia.org/wiki/Beaufort_scale)
*/
function windAsBeaufort(windInKmh) {
let beaufort = [2, 6, 12, 20, 29, 39, 50, 62, 75, 89, 103, 118];
let l = 0;
while (l < beaufort.length && beaufort[l] < windInKmh) {
l++;
}
return l;
}
/*
* Choose weather icon to display based on weather conditition code
@ -823,7 +263,9 @@ function getWeatherIconByCode(code, big) {
case 8:
switch (code) {
case 800:
return isDay() ? weatherSunny : weatherMoon;
var hr = (new Date()).getHours();
var isDay = (hr>6) && (hr<=18); // fixme we don't want to include ALL of suncalc just to choose one icon
return isDay ? weatherSunny : weatherMoon;
case 801:
return weatherPartlyCloudy;
case 802:
@ -836,16 +278,6 @@ function getWeatherIconByCode(code, big) {
}
}
function isDay() {
let times = getSunData();
if (times == undefined) return true;
let sunRise = Math.round(times.sunrise.getTime() / 1000);
let sunSet = Math.round(times.sunset.getTime() / 1000);
return (now > sunRise && now < sunSet);
}
function formatSeconds(s) {
if (s > 60 * 60) { // hours
return Math.round(s / (60 * 60)) + "h";
@ -856,45 +288,6 @@ function formatSeconds(s) {
return "<1m";
}
function getSunData() {
if (location != undefined && location.lat != undefined) {
let SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
// get today's sunlight times for lat/lon
return SunCalc ? SunCalc.getTimes(new Date(), location.lat, location.lon) : undefined;
}
return undefined;
}
/*
* Calculated progress of the sun between sunrise and sunset in percent
*
* Taken from rebble app and modified
*/
function getSunProgress() {
let times = getSunData();
if (times == undefined) return 0;
let sunRise = Math.round(times.sunrise.getTime() / 1000);
let sunSet = Math.round(times.sunset.getTime() / 1000);
if (isDay()) {
// during day
let dayLength = sunSet - sunRise;
if (now > sunRise) {
return (now - sunRise) / dayLength;
} else {
return (sunRise - now) / dayLength;
}
} else {
// during night
if (now < sunRise) {
let prevSunSet = sunSet - 60 * 60 * 24;
return 1 - (sunRise - now) / (sunRise - prevSunSet);
} else {
let upcomingSunRise = sunRise + 60 * 60 * 24;
return (upcomingSunRise - now) / (upcomingSunRise - sunSet);
}
}
}
/*
* Draws the background and the grey circle
@ -961,18 +354,54 @@ function getWeather() {
return jsonWeather && jsonWeather.weather ? jsonWeather.weather : undefined;
}
var menu = clock_info.load();
for(var i=1; i<5; i++) {
var circleType = settings['circle'+i];
if(circleType.includes("/")) {
let parts = circleType.split("/");
let infoName = parts[0], itemName = parts[1];
let infoNum = menu.findIndex(e=>e.name==infoName);
if (infoNum<0) infoNum=0; // not found!
let itemNum = 0; //get first if dynamic
if(!menu[infoNum].dynamic)
itemNum = menu[infoNum].items.findIndex(it=>it.name==itemName);
if (itemNum<0) itemNum=0;
circleInfoNum[i-1] = infoNum;
circleItemNum[i-1] = itemNum;
let menuItem = menu[infoNum].items[itemNum];
if (menuItem) {
(function(i){
menuItem.on('redraw', function() {
drawCircle(i);
});
})(i);
menuItem.show();
}
} else { // empty?
circleInfoNum[i-1] = -1;
circleItemNum[i-1] = -1;
}
}
Bangle.setUI({
mode : "clock",
remove : function() {
/*remove : function() {
THIS CLOCK IS NOT YET ABLE TO UNLOAD ALL OF ITSELF.
DO NOT UNCOMMENT THIS WITOUT FIXING IT
OR THERE WILL BE HUGE MEMORY LEAKS
// Called to unload all of the clock app
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
// must call clkinfo.hide here for any showing clkinfo
delete Graphics.prototype.setFontRobotoRegular50NumericOnly;
delete Graphics.prototype.setFontRobotoRegular21;
}});
}*/
});
Bangle.loadWidgets();
if (!showWidgets) require("widget_utils").hide();
else Bangle.drawWidgets();
// schedule a draw for the next second or minute
function queueDraw() {

View File

@ -1,7 +1,7 @@
{ "id": "circlesclock",
"name": "Circles clock",
"shortName":"Circles clock",
"version":"0.18",
"version":"0.19",
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
"icon": "app.png",
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],

View File

@ -12,17 +12,15 @@
storage.write(SETTINGS_FILE, settings);
}
//const valuesCircleTypes = ["empty", "steps", "stepsDist", "hr", "battery", "weather", "sunprogress", "temperature", "pressure", "altitude", "timer"];
//const namesCircleTypes = ["empty", "steps", "distance", "heart", "battery", "weather", "sun", "temperature", "pressure", "altitude", "timer"];
var valuesCircleTypes = ["empty","weather", "sunprogress"];
var namesCircleTypes = ["empty","weather", "sun"];
var valuesCircleTypes = ["empty"];
var namesCircleTypes = ["empty"];
clock_info.load().forEach(e=>{
if(e.dynamic) {
valuesCircleTypes = valuesCircleTypes.concat([e.name+"/"]);
namesCircleTypes = namesCircleTypes.concat([e.name]);
} else {
let values = e.items.map(i=>e.name+"/"+i.name);
let names =e.name=="Bangle" ? e.items.map(i=>i.name) : values;
let names = e.name=="Bangle" ? e.items.map(i=>i.name) : values;
valuesCircleTypes = valuesCircleTypes.concat(values);
namesCircleTypes = namesCircleTypes.concat(names);
}