Merge branch 'espruino:master' into master
|
@ -14,3 +14,4 @@ _site
|
|||
.owncloudsync.log
|
||||
Desktop.ini
|
||||
.sync_*.db*
|
||||
*.swp
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
0.07: Clkinfo improvements.
|
||||
0.08: Fix error in clkinfo (didn't require Storage & locale)
|
||||
Fix clkinfo icon
|
||||
0.09: Ensure Agenda supplies an image for clkinfo items
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
agendaItems.items.push({
|
||||
name: "Agenda "+i,
|
||||
get: () => ({ text: title + "\n" + dateStr, img: null}),
|
||||
get: () => ({ text: title + "\n" + dateStr, img: agendaItems.img }),
|
||||
show: function() { agendaItems.items[i].emit("redraw"); },
|
||||
hide: function () {}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "agenda",
|
||||
"name": "Agenda",
|
||||
"version": "0.08",
|
||||
"version": "0.09",
|
||||
"description": "Simple agenda",
|
||||
"icon": "agenda.png",
|
||||
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: Create astrocalc app
|
||||
0.02: Store last GPS lock, can be used instead of waiting for new GPS on start
|
||||
0.03: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
|
||||
0.04: Compatibility with Bangle.js 2, get location from My Location
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
|
||||
const SunCalc = require("suncalc"); // from modules folder
|
||||
const storage = require("Storage");
|
||||
const LAST_GPS_FILE = "astrocalc.gps.json";
|
||||
let lastGPS = (storage.readJSON(LAST_GPS_FILE, 1) || null);
|
||||
const BANGLEJS2 = process.env.HWVERSION == 2; // check for bangle 2
|
||||
|
||||
function drawMoon(phase, x, y) {
|
||||
const moonImgFiles = [
|
||||
|
@ -73,7 +72,7 @@ function drawTitle(key) {
|
|||
*/
|
||||
function drawPoint(angle, radius, color) {
|
||||
const pRad = Math.PI / 180;
|
||||
const faceWidth = 80; // watch face radius
|
||||
const faceWidth = g.getWidth()/3; // watch face radius
|
||||
const centerPx = g.getWidth() / 2;
|
||||
|
||||
const a = angle * pRad;
|
||||
|
@ -141,6 +140,7 @@ function drawData(title, obj, startX, startY) {
|
|||
|
||||
function drawMoonPositionPage(gps, title) {
|
||||
const pos = SunCalc.getMoonPosition(new Date(), gps.lat, gps.lon);
|
||||
const moonColor = g.theme.dark ? {r: 1, g: 1, b: 1} : {r: 0, g: 0, b: 0};
|
||||
|
||||
const pageData = {
|
||||
Azimuth: pos.azimuth.toFixed(2),
|
||||
|
@ -150,59 +150,61 @@ function drawMoonPositionPage(gps, title) {
|
|||
};
|
||||
const azimuthDegrees = parseInt(pos.azimuth * 180 / Math.PI);
|
||||
|
||||
drawData(title, pageData, null, 80);
|
||||
drawData(title, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20);
|
||||
drawPoints();
|
||||
drawPoint(azimuthDegrees, 8, {r: 1, g: 1, b: 1});
|
||||
drawPoint(azimuthDegrees, 8, moonColor);
|
||||
|
||||
let m = setWatch(() => {
|
||||
let m = moonIndexPageMenu(gps);
|
||||
}, BTN3, {repeat: false, edge: "falling"});
|
||||
}, BANGLEJS2 ? BTN : BTN3, {repeat: false, edge: "falling"});
|
||||
}
|
||||
|
||||
function drawMoonIlluminationPage(gps, title) {
|
||||
const phaseNames = [
|
||||
"New Moon", "Waxing Crescent", "First Quarter", "Waxing Gibbous",
|
||||
"Full Moon", "Waning Gibbous", "Last Quater", "Waning Crescent",
|
||||
/*LANG*/"New Moon", /*LANG*/"Waxing Crescent", /*LANG*/"First Quarter", /*LANG*/"Waxing Gibbous",
|
||||
/*LANG*/"Full Moon", /*LANG*/"Waning Gibbous", /*LANG*/"Last Quater", /*LANG*/"Waning Crescent",
|
||||
];
|
||||
|
||||
const phase = SunCalc.getMoonIllumination(new Date());
|
||||
const phaseIdx = Math.round(phase.phase*8);
|
||||
const pageData = {
|
||||
Phase: phaseNames[phase.phase],
|
||||
Phase: phaseNames[phaseIdx],
|
||||
};
|
||||
|
||||
drawData(title, pageData, null, 35);
|
||||
drawMoon(phase.phase, g.getWidth() / 2, g.getHeight() / 2);
|
||||
drawMoon(phaseIdx, g.getWidth() / 2, g.getHeight() / 2);
|
||||
|
||||
let m = setWatch(() => {
|
||||
let m = moonIndexPageMenu(gps);
|
||||
}, BTN3, {repease: false, edge: "falling"});
|
||||
}, BANGLEJS2 ? BTN : BTN3, {repease: false, edge: "falling"});
|
||||
}
|
||||
|
||||
|
||||
function drawMoonTimesPage(gps, title) {
|
||||
const times = SunCalc.getMoonTimes(new Date(), gps.lat, gps.lon);
|
||||
const moonColor = g.theme.dark ? {r: 1, g: 1, b: 1} : {r: 0, g: 0, b: 0};
|
||||
|
||||
const pageData = {
|
||||
Rise: dateToTimeString(times.rise),
|
||||
Set: dateToTimeString(times.set),
|
||||
};
|
||||
|
||||
drawData(title, pageData, null, 105);
|
||||
drawData(title, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20 + 5);
|
||||
drawPoints();
|
||||
|
||||
// Draw the moon rise position
|
||||
const risePos = SunCalc.getMoonPosition(times.rise, gps.lat, gps.lon);
|
||||
const riseAzimuthDegrees = parseInt(risePos.azimuth * 180 / Math.PI);
|
||||
drawPoint(riseAzimuthDegrees, 8, {r: 1, g: 1, b: 1});
|
||||
drawPoint(riseAzimuthDegrees, 8, moonColor);
|
||||
|
||||
// Draw the moon set position
|
||||
const setPos = SunCalc.getMoonPosition(times.set, gps.lat, gps.lon);
|
||||
const setAzimuthDegrees = parseInt(setPos.azimuth * 180 / Math.PI);
|
||||
drawPoint(setAzimuthDegrees, 8, {r: 1, g: 1, b: 1});
|
||||
drawPoint(setAzimuthDegrees, 8, moonColor);
|
||||
|
||||
let m = setWatch(() => {
|
||||
let m = moonIndexPageMenu(gps);
|
||||
}, BTN3, {repease: false, edge: "falling"});
|
||||
}, BANGLEJS2 ? BTN : BTN3, {repease: false, edge: "falling"});
|
||||
}
|
||||
|
||||
function drawSunShowPage(gps, key, date) {
|
||||
|
@ -224,7 +226,7 @@ function drawSunShowPage(gps, key, date) {
|
|||
Degrees: azimuthDegrees
|
||||
};
|
||||
|
||||
drawData(key, pageData, null, 85);
|
||||
drawData(key, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20 + 5);
|
||||
|
||||
drawPoints();
|
||||
|
||||
|
@ -233,7 +235,7 @@ function drawSunShowPage(gps, key, date) {
|
|||
|
||||
m = setWatch(() => {
|
||||
m = sunIndexPageMenu(gps);
|
||||
}, BTN3, {repeat: false, edge: "falling"});
|
||||
}, BANGLEJS2 ? BTN : BTN3, {repeat: false, edge: "falling"});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -273,15 +275,15 @@ function moonIndexPageMenu(gps) {
|
|||
},
|
||||
"Times": () => {
|
||||
m = E.showMenu();
|
||||
drawMoonTimesPage(gps, "Times");
|
||||
drawMoonTimesPage(gps, /*LANG*/"Times");
|
||||
},
|
||||
"Position": () => {
|
||||
m = E.showMenu();
|
||||
drawMoonPositionPage(gps, "Position");
|
||||
drawMoonPositionPage(gps, /*LANG*/"Position");
|
||||
},
|
||||
"Illumination": () => {
|
||||
m = E.showMenu();
|
||||
drawMoonIlluminationPage(gps, "Illumination");
|
||||
drawMoonIlluminationPage(gps, /*LANG*/"Illumination");
|
||||
},
|
||||
"< Back": () => m = indexPageMenu(gps),
|
||||
};
|
||||
|
@ -292,15 +294,15 @@ function moonIndexPageMenu(gps) {
|
|||
function indexPageMenu(gps) {
|
||||
const menu = {
|
||||
"": {
|
||||
"title": "Select",
|
||||
"title": /*LANG*/"Select",
|
||||
},
|
||||
"Sun": () => {
|
||||
/*LANG*/"Sun": () => {
|
||||
m = sunIndexPageMenu(gps);
|
||||
},
|
||||
"Moon": () => {
|
||||
/*LANG*/"Moon": () => {
|
||||
m = moonIndexPageMenu(gps);
|
||||
},
|
||||
"< Exit": () => { load(); }
|
||||
"< Back": () => { load(); }
|
||||
};
|
||||
|
||||
return E.showMenu(menu);
|
||||
|
@ -310,78 +312,9 @@ function getCenterStringX(str) {
|
|||
return (g.getWidth() - g.stringWidth(str)) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* GPS wait page, shows GPS locating animation until it gets a lock, then moves to the Sun page
|
||||
*/
|
||||
function drawGPSWaitPage() {
|
||||
const img = require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AW43GF1wwsFwYwqFwowoFw4wmFxIwdE5YAPF/4vM5nN6YAE5vMF8YtHGIgvhFpQxKF7AuOGA4vXFyAwGF63MFyIABF6xeWMC4UDLwvNGpAJG5gwSdhIIDRBLyWCIgcJHAgJJDoouQF4vMQoICBBJoeGFx6GGACIfHL6YvaX6gvZeCIdFc4gAFXogvGFxgwFDwovQCAguOGAnMMBxeG5guTGAggGGAwNKFySREcA3N5vM5gDBdpQvXEY4AKXqovGGCKbFF7AwPZQwvZGJgtGF7vGdQItG5gSIF7gASF/44WEzgwRF0wwHF1AwFF1QwDF1gvwAH4A/AFAA=="));
|
||||
const str1 = "Astrocalc v0.02";
|
||||
const str2 = "Locating GPS";
|
||||
const str3 = "Please wait...";
|
||||
|
||||
g.clear();
|
||||
g.drawImage(img, 100, 50);
|
||||
g.setFont("6x8", 1);
|
||||
g.drawString(str1, getCenterStringX(str1), 105);
|
||||
g.drawString(str2, getCenterStringX(str2), 140);
|
||||
g.drawString(str3, getCenterStringX(str3), 155);
|
||||
|
||||
if (lastGPS) {
|
||||
lastGPS = JSON.parse(lastGPS);
|
||||
lastGPS.time = new Date();
|
||||
|
||||
const str4 = "Press Button 3 to use last GPS";
|
||||
g.setColor("#d32e29");
|
||||
g.fillRect(0, 190, g.getWidth(), 215);
|
||||
g.setColor("#ffffff");
|
||||
g.drawString(str4, getCenterStringX(str4), 200);
|
||||
|
||||
setWatch(() => {
|
||||
clearWatch();
|
||||
Bangle.setGPSPower(0);
|
||||
m = indexPageMenu(lastGPS);
|
||||
}, BTN3, {repeat: false});
|
||||
}
|
||||
|
||||
g.flip();
|
||||
|
||||
const DEBUG = false;
|
||||
if (DEBUG) {
|
||||
clearWatch();
|
||||
|
||||
const gps = {
|
||||
"lat": 56.45783133333,
|
||||
"lon": -3.02188583333,
|
||||
"alt": 75.3,
|
||||
"speed": 0.070376,
|
||||
"course": NaN,
|
||||
"time":new Date(),
|
||||
"satellites": 4,
|
||||
"fix": 1
|
||||
};
|
||||
|
||||
m = indexPageMenu(gps);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Bangle.on('GPS', (gps) => {
|
||||
if (gps.fix === 0) return;
|
||||
clearWatch();
|
||||
|
||||
if (isNaN(gps.course)) gps.course = 0;
|
||||
require("Storage").writeJSON(LAST_GPS_FILE, JSON.stringify(gps));
|
||||
Bangle.setGPSPower(0);
|
||||
Bangle.buzz();
|
||||
Bangle.setLCDPower(true);
|
||||
|
||||
m = indexPageMenu(gps);
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
Bangle.setGPSPower(1);
|
||||
drawGPSWaitPage();
|
||||
let location = require("Storage").readJSON("mylocation.json",1)||{"lat":51.5072,"lon":0.1276,"location":"London"};
|
||||
indexPageMenu(location);
|
||||
}
|
||||
|
||||
let m;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
{
|
||||
"id": "astrocalc",
|
||||
"name": "Astrocalc",
|
||||
"version": "0.03",
|
||||
"description": "Calculates interesting information on the sun and moon cycles for the current day based on your location.",
|
||||
"version": "0.04",
|
||||
"description": "Calculates interesting information on the sun like sunset and sunrise and moon cycles for the current day based on your location from MyLocation app",
|
||||
"icon": "astrocalc.png",
|
||||
"tags": "app,sun,moon,cycles,tool,outdoors",
|
||||
"supports": ["BANGLEJS"],
|
||||
"tags": "app,sun,moon,cycles,tool",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"dependencies": {"mylocation":"app"},
|
||||
"storage": [
|
||||
{"name":"astrocalc.app.js","url":"astrocalc-app.js"},
|
||||
{"name":"astrocalc.img","url":"astrocalc-icon.js","evaluate":true},
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,9 @@
|
|||
# BTHome Temperature and Pressure
|
||||
|
||||
This app displays temperature and pressure and advertises them over bluetooth using BTHome.io standard (along with battery level)
|
||||
|
||||
This can be used to integrate with [Home Assistant](https://www.home-assistant.io/), so you can use your Bangle as a wireless temperature/pressure sensor.
|
||||
|
||||
More info on the standard at https://bthome.io
|
||||
|
||||
And the data format used is https://bthome.io/format/
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4kA///1N6BIPf//1gMIwdE8sG2me+9Y/8C/2snXsoUNpdnzdt/xj/AH4AYgMRAAUQCyoYSCQNXs1muoFBFyHm1X//+qtwwPiMX1+YmczxP6uIwNFwN6yeDnGDmc504wNFwOpnGYC4OJweaGBsR9WTmYtBmc4GAOuC5ZGBt4SBAAQEBwf2JBcBiupnIuCmedxGTzVRC5cX1AuDnPZF4OKuIXLi3zIoedMgMzn9hC5uICQON5IDBxAXSznYC6RdDPQYXNO4JcB7pdCO56nBnGZ7p6DU5zXBXgSqDa5sAiPqIgOZd4c510RCxQXBi+pRQIXBxODzVxC5hIBvR1DnE505GMGAevzAvC/QuNGAfm1X//+qtwuOGAURq9ms11AoIWOGAQAEFw1EDBwWFggBCkUgAQMigUAAIIAJoABDCgIXQFwYXBCYYBDHAMCEAIkCFgcEAIIKCCoQFCkAhBAQIlCkAsBOoIXCBoIvEAwQTCAYI2BIwgXIF4YXDQwIVCC4YIBMIwfCAQRfGYBSPNC6TBFACgwBACouWAH4AiA="))
|
|
@ -0,0 +1,58 @@
|
|||
// history of temperature/pressure readings
|
||||
var history = [];
|
||||
|
||||
// When we get temperature...
|
||||
function onTemperature(p) {
|
||||
// Average the last 5 temperature readings
|
||||
while (history.length>4) history.shift();
|
||||
history.push(p);
|
||||
var avrTemp = history.reduce((i,h)=>h.temperature+i,0) / history.length;
|
||||
var avrPressure = history.reduce((i,h)=>h.pressure+i,0) / history.length;
|
||||
var t = require('locale').temp(avrTemp).replace("'","°");
|
||||
// Draw
|
||||
var rect = Bangle.appRect;
|
||||
g.reset(1).clearRect(rect.x, rect.y, rect.x2, rect.y2);
|
||||
var x = (rect.x+rect.x2)/2;
|
||||
var y = (rect.y+rect.y2)/2 + 10;
|
||||
g.setFont("6x15").setFontAlign(0,0).drawString("Temperature:", x, y - 65);
|
||||
g.setFontVector(50).setFontAlign(0,0).drawString(t, x, y-25);
|
||||
g.setFont("6x15").setFontAlign(0,0).drawString("Pressure:", x, y+15 );
|
||||
g.setFont("12x20").setFontAlign(0,0).drawString(Math.round(avrPressure)+" hPa", x, y+40);
|
||||
// Set Bluetooth Advertising
|
||||
// https://bthome.io/format/
|
||||
var temp100 = Math.round(avrTemp*100);
|
||||
var pressure100 = Math.round(avrPressure*100);
|
||||
|
||||
Bangle.bleAdvert[0xFCD2] = [ 0x40, /* BTHome Device Information
|
||||
bit 0: "Encryption flag"
|
||||
bit 1-4: "Reserved for future use"
|
||||
bit 5-7: "BTHome Version" */
|
||||
|
||||
0x01, // Battery, 8 bit
|
||||
E.getBattery(),
|
||||
|
||||
0x02, // Temperature, 16 bit
|
||||
temp100&255,temp100>>8,
|
||||
|
||||
0x04, // Pressure, 16 bit
|
||||
pressure100&255,(pressure100>>8)&255,pressure100>>16
|
||||
];
|
||||
NRF.setAdvertising(Bangle.bleAdvert);
|
||||
}
|
||||
|
||||
// Gets the temperature in the most accurate way with pressure sensor
|
||||
function drawTemperature() {
|
||||
Bangle.getPressure().then(p =>{if (p) onTemperature(p);});
|
||||
}
|
||||
|
||||
if (!Bangle.bleAdvert) Bangle.bleAdvert = {};
|
||||
setInterval(function() {
|
||||
drawTemperature();
|
||||
}, 10000); // update every 10s
|
||||
Bangle.loadWidgets();
|
||||
Bangle.setUI({
|
||||
mode : "custom",
|
||||
back : function() {load();}
|
||||
});
|
||||
E.showMessage("Reading temperature...");
|
||||
drawTemperature();
|
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,14 @@
|
|||
{ "id": "bthometemp",
|
||||
"name": "BTHome Temperature and Pressure",
|
||||
"shortName":"BTHome T",
|
||||
"version":"0.01",
|
||||
"description": "Displays temperature and pressure, and advertises them over bluetooth using BTHome.io standard",
|
||||
"icon": "app.png",
|
||||
"tags": "bthome,bluetooth,temperature",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"bthometemp.app.js","url":"app.js"},
|
||||
{"name":"bthometemp.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Tell clock widgets to hide.
|
||||
0.04: Improve current time readability in light theme.
|
||||
0.05: Show calendar colors & improved all day events.
|
||||
0.06: Improved multi-line locations & titles
|
||||
|
|
|
@ -54,13 +54,15 @@ function drawEventBody(event, y) {
|
|||
var yStart = y;
|
||||
if (lines.length > 2) {
|
||||
lines = lines.slice(0,2);
|
||||
lines[1] = lines[1].slice(0,-3)+"...";
|
||||
lines[1] += "...";
|
||||
}
|
||||
g.drawString(lines.join('\n'),10,y);
|
||||
y+=20 * lines.length;
|
||||
if(event.location) {
|
||||
g.drawImage(atob("DBSBAA8D/H/nDuB+B+B+B3Dn/j/B+A8A8AYAYAYAAAAAAA=="),10,y);
|
||||
g.drawString(event.location,25,y);
|
||||
var loclines = g.wrapString(event.location, g.getWidth()-30);
|
||||
if(loclines.length>1) loclines[0] += "...";
|
||||
g.drawString(loclines[0],25,y);
|
||||
y+=20;
|
||||
}
|
||||
if (event.color) {
|
||||
|
@ -131,4 +133,3 @@ var minuteInterval = setInterval(redraw, 60 * 1000);
|
|||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "calclock",
|
||||
"name": "Calendar Clock",
|
||||
"shortName": "CalClock",
|
||||
"version": "0.05",
|
||||
"version": "0.06",
|
||||
"description": "Show the current and upcoming events synchronized from Gadgetbridge",
|
||||
"icon": "calclock.png",
|
||||
"type": "clock",
|
||||
|
|
|
@ -38,3 +38,4 @@
|
|||
Add fast load capability
|
||||
0.21: Remade all icons without a palette for dark theme
|
||||
Now re-adds widgets if they were hidden when fast-loading
|
||||
0.22: Fixed crash if item has no image and cutting long overflowing text
|
||||
|
|
|
@ -183,9 +183,12 @@ let drawCircle = function(index, item, data) {
|
|||
if (txt.endsWith(" bpm")) txt=txt.slice(0,-4); // hack for heart rate - remove the 'bpm' text
|
||||
if(item.hasRange) percent = (data.v-data.min) / (data.max-data.min);
|
||||
if(data.short) txt = data.short;
|
||||
//long text can overflow and we do not draw there anymore..
|
||||
if(txt.length>6) txt = txt.slice(0,5)+"\n"+txt.slice(5,10)
|
||||
drawGauge(w, h3, percent, color);
|
||||
drawInnerCircleAndTriangle(w);
|
||||
writeCircleText(w, txt);
|
||||
if(!img) return; //or get it from the clkinfo?
|
||||
g.setColor(getCircleIconColor(index, color, percent))
|
||||
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "circlesclock",
|
||||
"name": "Circles clock",
|
||||
"shortName":"Circles clock",
|
||||
"version":"0.21",
|
||||
"version":"0.22",
|
||||
"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"}],
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: First release
|
After Width: | Height: | Size: 1012 B |
|
@ -0,0 +1,30 @@
|
|||
(function() {
|
||||
return {
|
||||
name: "Bangle",
|
||||
items: [
|
||||
{ name : "FW",
|
||||
get : () => {
|
||||
let d = new Date();
|
||||
let g = Graphics.createArrayBuffer(24,24,1,{msb:true});
|
||||
g.drawImage(atob("GBjC////AADve773VWmmmmlVVW22nnlVVbLL445VVwAAAADVWAAAAAAlrAAAAAA6sAAAAAAOWAAAAAAlrAD//wA6sANVVcAOWANVVcAlrANVVcA6rANVVcA6WANVVcAlsANVVcAOrAD//wA6WAAAAAAlsAAAAAAOrAAAAAA6WAAAAAAlVwAAAADVVbLL445VVW22nnlVVWmmmmlV"),1,0);
|
||||
return {
|
||||
text : process.env.VERSION,
|
||||
img : g.asImage("string")
|
||||
};
|
||||
},
|
||||
show : function() {
|
||||
this.interval = setTimeout(()=>{
|
||||
this.emit("redraw");
|
||||
this.interval = setInterval(()=>{
|
||||
this.emit("redraw");
|
||||
}, 86400000);
|
||||
}, 86400000 - (Date.now() % 86400000));
|
||||
},
|
||||
hide : function() {
|
||||
clearInterval(this.interval);
|
||||
this.interval = undefined;
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
})
|
|
@ -0,0 +1,13 @@
|
|||
{ "id": "clkinfofw",
|
||||
"name": "Firmware Clockinfo",
|
||||
"version":"0.01",
|
||||
"description": "For clocks that display 'clockinfo', this displays the firmware version string",
|
||||
"icon": "app.png",
|
||||
"type": "clkinfo",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"tags": "clkinfo,firmware",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"clkinfofw.clkinfo.js","url":"clkinfo.js"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"id": "health",
|
||||
"name": "Health Tracking",
|
||||
"shortName": "Health",
|
||||
"version": "0.17",
|
||||
"description": "Logs health data and provides an app to view it",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -13,3 +13,6 @@
|
|||
0.27: BJS2: Changed swipe down to swipe up
|
||||
0.28: Reverted changes to implementation of 0.25
|
||||
0.29: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
|
||||
0.30: BJS2: swipe seems to be working now
|
||||
0.31: Tweaking the swipe option; Added mylocation as a dependency.
|
||||
Remove calls to Bangle.loadWidgets as they are not needed and create warnings
|
|
@ -1,8 +1,16 @@
|
|||
|
||||
# Hanks World Clock - See the time in four locations
|
||||
|
||||
In addition to the main clock and date in your current location, you can add up to three other locations. Great for travel or remote working.
|
||||
Additionally we show the sunset/sunrise and seconds for the current location and the day name is shown in your locale.
|
||||
If watch is locked, seconds get refreshed every 10 seconds.
|
||||
Additionally we show the sunset/sunrise and seconds for the current location. The day name is shown in your locale.
|
||||
Also, you can swipe up to show the current time to a friend.
|
||||
|
||||
All this is configurable:
|
||||
|
||||
- Show seconds only when unlocked (saves battery) / always / do not show seconds
|
||||
- Green color on dark mode (on/off)
|
||||
- Show sun info (on/off) (set your location in the mylocation app)
|
||||
- Rotation degree on swipe (off / 90 / 180 / 270)
|
||||
|
||||

|
||||
|
||||
|
@ -10,9 +18,11 @@ If watch is locked, seconds get refreshed every 10 seconds.
|
|||
|
||||
Location for sun set / rise set with mylocation app.
|
||||
|
||||
Provide names and the UTC offsets for up to three other timezones in the app store. These are stored in a json file on your watch. UTC offsets can be decimal (e.g., 5.5 for India).
|
||||
Provide names and the UTC offsets for up to three other timezones in the App Loader before uploading. These are stored in a json file on your watch. UTC offsets can be decimal (e.g., 5.5 for India).
|
||||
|
||||
The clock does not handle summer time / daylight saving time changes automatically. If one of your three locations changes its UTC offset, you can simply change the setting in the app store and update. Currently the clock only supports 24 hour time format for the additional time zones.
|
||||
The clock does not handle summer time / daylight saving time changes automatically. If one of your three locations changes its UTC offset, you can simply change the setting in the App Launcher and update. Currently the clock only supports 24 hour time format for the additional time zones.
|
||||
|
||||
BangleJS2: Swipe up to rotate screen (Target rotation can be set in the settings). Swipe up again to go back to the default rotation. So you can show the time to a friend real quick or temporarily change orientation for sports etc.
|
||||
|
||||
## Requests
|
||||
|
||||
|
@ -22,5 +32,4 @@ Please use [the Espruino Forum](http://forum.espruino.com/microcosms/1424/) if y
|
|||
|
||||
Created by Hank.
|
||||
|
||||
Based on the great work of "World Clock - 4 time zones". Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock).
|
||||
And Sun Clock [Sun Clock](https://github.com/espruino/BangleApps/tree/master/apps/sunclock)
|
||||
Based on the great work of "World Clock - 4 time zones". Made by [Scott Hale](https://www.github.com/computermacgyver)
|
||||
|
|
|
@ -2,17 +2,19 @@
|
|||
|
||||
// ------- Settings file
|
||||
const SETTINGSFILE = "hworldclock.json";
|
||||
var secondsMode;
|
||||
var showSunInfo;
|
||||
var colorWhenDark;
|
||||
let secondsMode;
|
||||
let showSunInfo;
|
||||
let colorWhenDark;
|
||||
let rotationTarget;
|
||||
// ------- Settings file
|
||||
|
||||
//const BANGLEJS2 = process.env.HWVERSION == 2;
|
||||
const big = g.getWidth()>200;
|
||||
// Font for primary time and date
|
||||
const primaryTimeFontSize = big?6:5;
|
||||
const primaryDateFontSize = big?3:2;
|
||||
require("Font5x9Numeric7Seg").add(Graphics);
|
||||
require("FontTeletext10x18Ascii").add(Graphics);
|
||||
let font5x9 = require("Font5x9Numeric7Seg").add(Graphics);
|
||||
let font10x18 = require("FontTeletext10x18Ascii").add(Graphics);
|
||||
|
||||
// Font for single secondary time
|
||||
const secondaryTimeFontSize = 4;
|
||||
|
@ -24,9 +26,8 @@ const xcol1 = 10;
|
|||
const xcol2 = g.getWidth() - xcol1;
|
||||
|
||||
const font = "6x8";
|
||||
let drag;
|
||||
|
||||
/* TODO: we could totally use 'Layout' here and
|
||||
avoid a whole bunch of hard-coded offsets */
|
||||
|
||||
const xyCenter = g.getWidth() / 2;
|
||||
const xyCenterSeconds = xyCenter + (big ? 85 : 68);
|
||||
|
@ -39,22 +40,27 @@ const yposWorld = big ? 170 : 120;
|
|||
const OFFSET_TIME_ZONE = 0;
|
||||
const OFFSET_HOURS = 1;
|
||||
|
||||
var PosInterval = 0;
|
||||
let PosInterval = 0;
|
||||
|
||||
var offsets = require("Storage").readJSON("hworldclock.settings.json") || [];
|
||||
let offsets = require("Storage").readJSON("hworldclock.settings.json") || [];
|
||||
|
||||
//=======Sun
|
||||
setting = require("Storage").readJSON("setting.json",1);
|
||||
let setting = require("Storage").readJSON("setting.json",1);
|
||||
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
|
||||
SunCalc = require("suncalc"); // from modules folder
|
||||
//https://raw.githubusercontent.com/pebl-hank/BangleApps/master/modules/suncalc.js
|
||||
let SunCalc = require("suncalc"); // from modules folder
|
||||
const LOCATION_FILE = "mylocation.json";
|
||||
var rise = "read";
|
||||
var set = "...";
|
||||
let rise = "read";
|
||||
let set = "...";
|
||||
//var pos = {altitude: 20, azimuth: 135};
|
||||
//var noonpos = {altitude: 37, azimuth: 180};
|
||||
//=======Sun
|
||||
|
||||
var ampm = "AM";
|
||||
let ampm = "AM";
|
||||
|
||||
let defaultRotation = setting.rotate || 0;
|
||||
let currentRotation = defaultRotation;
|
||||
|
||||
|
||||
// TESTING CODE
|
||||
// Used to test offset array values during development.
|
||||
|
@ -89,30 +95,36 @@ const mockOffsets = {
|
|||
// END TESTING CODE
|
||||
|
||||
|
||||
// Load settings
|
||||
function loadMySettings() {
|
||||
// ================ Load settings
|
||||
// Helper function default setting
|
||||
function def (value, def) {return value !== undefined ? value : def;}
|
||||
let def = function(value, def) {
|
||||
return value !== undefined ? value : def;
|
||||
}
|
||||
|
||||
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||
let settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||
secondsMode = def(settings.secondsMode, "when unlocked");
|
||||
showSunInfo = def(settings.showSunInfo, true);
|
||||
colorWhenDark = def(settings.colorWhenDark, "green");
|
||||
}
|
||||
rotationTarget = def(settings.rotationTarget, "90");
|
||||
rotationTarget = parseInt(rotationTarget) || 0;
|
||||
if (rotationTarget == 90) rotationTarget = 1; // very lame, but works for now.
|
||||
if (rotationTarget == 180) rotationTarget = 2;
|
||||
if (rotationTarget == 270) rotationTarget = 3;
|
||||
// ================ Load settings
|
||||
|
||||
|
||||
// Check settings for what type our clock should be
|
||||
var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
|
||||
let _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
|
||||
|
||||
// timeout used to update every minute
|
||||
var drawTimeout;
|
||||
var drawTimeoutSeconds;
|
||||
var secondsTimeout;
|
||||
let drawTimeout;
|
||||
let drawTimeoutSeconds;
|
||||
let secondsTimeout;
|
||||
|
||||
g.setBgColor(g.theme.bg);
|
||||
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
let queueDraw = function() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
|
@ -121,7 +133,7 @@ function queueDraw() {
|
|||
}
|
||||
|
||||
// schedule a draw for the next second
|
||||
function queueDrawSeconds() {
|
||||
let queueDrawSeconds = function() {
|
||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||
drawTimeoutSeconds = setTimeout(function() {
|
||||
drawTimeoutSeconds = undefined;
|
||||
|
@ -130,28 +142,29 @@ function queueDrawSeconds() {
|
|||
}, secondsTimeout - (Date.now() % secondsTimeout));
|
||||
}
|
||||
|
||||
function doublenum(x) {
|
||||
let doublenum = function(x) {
|
||||
return x < 10 ? "0" + x : "" + x;
|
||||
}
|
||||
|
||||
function getCurrentTimeFromOffset(dt, offset) {
|
||||
let getCurrentTimeFromOffset = function(dt, offset) {
|
||||
return new Date(dt.getTime() + offset * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
function updatePos() {
|
||||
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":0,"lon":0,"location":"-"}; //{"lat":53.3,"lon":10.1,"location":"Pattensen"};
|
||||
let updatePos = function() {
|
||||
let coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":0,"lon":0,"location":"-"}; //{"lat":53.3,"lon":10.1,"location":"Pattensen"};
|
||||
if (coord.lat != 0 && coord.lon != 0) {
|
||||
//pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon);
|
||||
times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon);
|
||||
rise = "^" + times.sunrise.toString().split(" ")[4].substr(0,5);
|
||||
set = "v" + times.sunset.toString().split(" ")[4].substr(0,5);
|
||||
//noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon);
|
||||
} else {
|
||||
rise = null;
|
||||
set = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function drawSeconds() {
|
||||
let drawSeconds = function() {
|
||||
// get date
|
||||
let d = new Date();
|
||||
let da = d.toString().split(" ");
|
||||
|
@ -173,16 +186,13 @@ function drawSeconds() {
|
|||
} else {
|
||||
g.setColor(g.theme.fg);
|
||||
}
|
||||
//console.log("---");
|
||||
//console.log(seconds);
|
||||
if (Bangle.isLocked() && secondsMode != "always") seconds = seconds.slice(0, -1) + ':::'; // we use :: as the font does not have an x
|
||||
//console.log(seconds);
|
||||
g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true);
|
||||
queueDrawSeconds();
|
||||
|
||||
}
|
||||
|
||||
function draw() {
|
||||
let draw = function() {
|
||||
// get date
|
||||
let d = new Date();
|
||||
let da = d.toString().split(" ");
|
||||
|
@ -207,7 +217,6 @@ function draw() {
|
|||
}
|
||||
}
|
||||
|
||||
//g.setFont(font, primaryTimeFontSize);
|
||||
g.setFont("5x9Numeric7Seg",primaryTimeFontSize);
|
||||
if (g.theme.dark) {
|
||||
if (colorWhenDark == "green") {
|
||||
|
@ -289,30 +298,58 @@ function draw() {
|
|||
if (secondsMode != "none") queueDrawSeconds();
|
||||
}
|
||||
|
||||
// clean app screen
|
||||
g.clear();
|
||||
|
||||
// Init the settings of the app
|
||||
loadMySettings();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI({
|
||||
mode : "clock",
|
||||
remove : function() {
|
||||
// Called to unload all of the clock app
|
||||
if (PosInterval) clearInterval(PosInterval);
|
||||
PosInterval = undefined;
|
||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||
drawTimeoutSeconds = undefined;
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}});
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
|
||||
// draw immediately at first, queue update
|
||||
|
||||
//if (BANGLEJS2) {
|
||||
let onDrag = e => {
|
||||
if (!drag) { // start dragging
|
||||
drag = {x: e.x, y: e.y};
|
||||
} else if (!e.b) { // released
|
||||
const dx = e.x-drag.x, dy = e.y-drag.y;
|
||||
drag = null;
|
||||
if (Math.abs(dx)>Math.abs(dy)+10) {
|
||||
// horizontal
|
||||
if (dx < dy) {
|
||||
// for later purpose
|
||||
} else {
|
||||
// for later purpose
|
||||
}
|
||||
} else if (Math.abs(dy)>Math.abs(dx)+10) {
|
||||
// vertical
|
||||
if (dx < dy) { //down
|
||||
//g.clear().setRotation(defaultRotation);
|
||||
//currentRotation = defaultRotation;
|
||||
//draw();
|
||||
//Bangle.drawWidgets();
|
||||
} else {
|
||||
if (currentRotation == rotationTarget) {
|
||||
g.clear().setRotation(defaultRotation);
|
||||
currentRotation = defaultRotation;
|
||||
} else {
|
||||
g.clear().setRotation(rotationTarget);
|
||||
currentRotation = rotationTarget;
|
||||
}
|
||||
|
||||
draw();
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
} else {
|
||||
//console.log("tap " + e.x + " " + e.y);
|
||||
if (e.x > 145 && e.y > 145) {
|
||||
// for later purpose
|
||||
}
|
||||
}
|
||||
}
|
||||
}; //);
|
||||
Bangle.on("drag", onDrag);
|
||||
//} <-- BJS2 only } else {
|
||||
//setWatch(xxx, BTN1, { repeat: true, debounce:50 }); // maybe adding this later
|
||||
//setWatch(xxx, BTN3, { repeat: true, debounce:50 });
|
||||
//setWatch(xxx, BTN4, { repeat: true, debounce:50 });
|
||||
//setWatch(xxx, BTN5, { repeat: true, debounce:50 });
|
||||
// }
|
||||
|
||||
|
||||
|
||||
if (!Bangle.isLocked()) { // Initial state
|
||||
|
@ -348,11 +385,10 @@ if (!Bangle.isLocked()) { // Initial state
|
|||
updatePos();
|
||||
}
|
||||
draw(); // draw immediately, queue redraw
|
||||
|
||||
}
|
||||
|
||||
|
||||
Bangle.on('lock',on=>{
|
||||
let onLock = on => {
|
||||
if (!on) { // UNlocked
|
||||
if (showSunInfo) {
|
||||
if (PosInterval != 0) clearInterval(PosInterval);
|
||||
|
@ -388,5 +424,37 @@ Bangle.on('lock',on=>{
|
|||
}
|
||||
draw(); // draw immediately, queue redraw
|
||||
}
|
||||
});
|
||||
};
|
||||
Bangle.on('lock', onLock);
|
||||
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI({
|
||||
mode : "custom",clock:true,
|
||||
remove : function() {
|
||||
// Called to unload all of the clock app
|
||||
g.setRotation(defaultRotation); // bring back default rotation
|
||||
if (typeof PosInterval === "undefined") {
|
||||
console.log("PosInterval is undefined");
|
||||
} else {
|
||||
if (PosInterval) clearInterval(PosInterval);
|
||||
}
|
||||
PosInterval = undefined;
|
||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||
drawTimeoutSeconds = undefined;
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
//if (BANGLEJS2)
|
||||
Bangle.removeListener("drag",onDrag);
|
||||
Bangle.removeListener("onLock",onLock);
|
||||
}});
|
||||
|
||||
|
||||
g.clear().setRotation(defaultRotation); // clean app screen and make sure the default rotation is set
|
||||
draw(); // draw immediately at first, queue update
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
// );
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "hworldclock",
|
||||
"name": "Hanks World Clock",
|
||||
"shortName": "Hanks World Clock",
|
||||
"version": "0.29",
|
||||
"version": "0.31",
|
||||
"description": "Current time zone plus up to three others",
|
||||
"allow_emulator":true,
|
||||
"icon": "app.png",
|
||||
|
@ -12,6 +12,7 @@
|
|||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"custom": "custom.html",
|
||||
"dependencies": {"mylocation":"app"},
|
||||
"storage": [
|
||||
{"name":"hworldclock.app.js","url":"app.js"},
|
||||
{"name":"hworldclock.img","url":"hworldclock-icon.js","evaluate":true},
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
settings.showSunInfo = v;
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rotation": stringInSettings("rotationTarget", ["off", "90", "180", "270"]),
|
||||
};
|
||||
|
||||
E.showMenu(mainmenu);
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkA/4A/AH4AUiIAKCxXzC5c/C5PyC5cvC8PxC5cfLxNEABhgI+gXNp4X3//+9wAK96PJC6/zC5bvKC6//C5YWKC4nUoMUpoXS8lDn/zmlOC6NCA4ckC6Hkl4HD+QwCC5c+LoIsCoSKBMIPjC5tD//0olEp//mgXNmMRiYuBC4JjBBAYAK+MRj//CwIABBAgXkI5AXOiRyBC4J8BkIXN+dEoKnFiNEAYIXNa4sUC59EJAIACkIHBC5iMCoMTn/zmIuBSQIXODAMRAAKqDABikCAAqqBC8i8CAArCBC/n0C49PC5oA/AH4AIA=="))
|
||||
require("heatshrink").decompress(atob("mEw4UA///1NygH+zn/Jf4AJgdVAAnABZ8BBYtABbc1BYtcBYcVBYtUBbcC1QAEwALPgYLFQYoLWgAHBytWAYK0F1Wpv/9tQLH0v//9aBY+XBYPWBY3qz/1r/21YLGv/Vq/9BY3Vv6NB/tXBaMVBYamEBZ1fHYP1BY01r5TB+ruEBYVXNYPVBY9VBYNVBY0FqoiBqtQBY4ACBb0NBYdwBbsBBYdABYoA/AAg="))
|
||||
|
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
@ -82,3 +82,5 @@
|
|||
0.58: Fast load messages without writing to flash
|
||||
Don't write messages to flash until the app closes
|
||||
0.59: Ensure we do write messages if messages app can't be fast loaded (see #2373)
|
||||
0.60: Fix saving of removal messages if UI not open
|
||||
0.61: Fix regression where loading into messages app stops back from working (#2398)
|
||||
|
|
|
@ -370,6 +370,7 @@ function checkMessages(options) {
|
|||
}
|
||||
// If we have a new message, show it
|
||||
if ((toShow||options.showMsgIfUnread) && newMessages.length) {
|
||||
delete newMessages[0].show; // stop us getting stuck here if we're called a second time
|
||||
showMessage(newMessages[0].id);
|
||||
// buzz after showMessage, so being busy during layout doesn't affect the buzz pattern
|
||||
if (global.BUZZ_ON_NEW_MESSAGE) {
|
||||
|
|
|
@ -10,7 +10,15 @@ exports.listener = function(type, msg) {
|
|||
clearTimeout(exports.messageTimeout);
|
||||
delete exports.messageTimeout;
|
||||
}
|
||||
if (msg.t==="remove") return;
|
||||
if (msg.t==="remove") {
|
||||
// we won't open the UI for removed messages, so make sure to delete it from flash
|
||||
if (Bangle.MESSAGES) {
|
||||
// we were waiting for exports.messageTimeout
|
||||
require("messages").apply(msg, Bangle.MESSAGES);
|
||||
if (!Bangle.MESSAGES.length) delete Bangle.MESSAGES;
|
||||
}
|
||||
return require("messages").save(msg); // always write removal to flash
|
||||
}
|
||||
|
||||
const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {};
|
||||
let loadMessages = (Bangle.CLOCK || event.important);
|
||||
|
@ -26,12 +34,12 @@ exports.listener = function(type, msg) {
|
|||
require("messages").save(msg);
|
||||
} else {
|
||||
if (!Bangle.MESSAGES) Bangle.MESSAGES = [];
|
||||
Bangle.MESSAGES.push(msg);
|
||||
require("messages").apply(msg, Bangle.MESSAGES);
|
||||
if (!Bangle.MESSAGES.length) delete Bangle.MESSAGES;
|
||||
}
|
||||
const saveToFlash = () => {
|
||||
// save messages from RAM to flash after all, if we decide not to launch app
|
||||
if (!Bangle.MESSAGES) return;
|
||||
Bangle.MESSAGES.forEach(m => require("messages").save(m));
|
||||
if (Bangle.MESSAGES) Bangle.MESSAGES.forEach(m => require("messages").save(m));
|
||||
delete Bangle.MESSAGES;
|
||||
}
|
||||
msg.handled = true;
|
||||
|
@ -50,6 +58,7 @@ exports.listener = function(type, msg) {
|
|||
if (exports.messageTimeout) clearTimeout(exports.messageTimeout);
|
||||
exports.messageTimeout = setTimeout(function() {
|
||||
delete exports.messageTimeout;
|
||||
if (!Bangle.MESSAGES) return; // message was removed during the delay
|
||||
if (type!=="music") {
|
||||
if (!loadMessages) {
|
||||
// not opening the app, just buzz
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "messagegui",
|
||||
"name": "Message UI",
|
||||
"shortName": "Messages",
|
||||
"version": "0.59",
|
||||
"version": "0.61",
|
||||
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
0.01: Moved message icons from messages into standalone library
|
||||
0.02: Added several new icons and colors
|
||||
0.03: Fix icons broken in 0v02 (#2386)
|
||||
Store all icons in a separate binary file (much faster lookup)
|
||||
|
||||
|
|
After Width: | Height: | Size: 204 B |
After Width: | Height: | Size: 210 B |
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 182 B |
|
@ -0,0 +1,143 @@
|
|||
#!/usr/bin/node
|
||||
|
||||
// Creates lib.js from icons
|
||||
// npm install png-js
|
||||
|
||||
// default icon must come first in icon_names
|
||||
|
||||
var imageconverter = require("../../../webtools/imageconverter.js");
|
||||
var icons = JSON.parse(require("fs").readFileSync(__dirname+"/icon_names.json"));
|
||||
const imgOptions = {
|
||||
mode : "1bit",
|
||||
inverted : true,
|
||||
transparent : true,
|
||||
output: "raw"
|
||||
};
|
||||
var PNG = require('png-js');
|
||||
var IMAGE_BYTES = 76;
|
||||
|
||||
var iconTests = [];
|
||||
var iconImages = []; // array of converted icons
|
||||
var iconIndices = {}; // maps filename -> index in iconImages
|
||||
|
||||
var promises = [];
|
||||
|
||||
icons.forEach(icon => {
|
||||
var index = iconIndices[icon.icon];
|
||||
if (index===undefined) { // need a new icon
|
||||
index = iconImages.length;
|
||||
iconIndices[icon.icon] = index;
|
||||
iconImages.push(""); // placeholder
|
||||
// create image
|
||||
console.log("Loading "+icon.icon);
|
||||
var png = new PNG(require("fs").readFileSync(__dirname+"/"+icon.icon));
|
||||
if (png.width!=24 || png.height!=24) {
|
||||
console.warn(icon.icon+" should be 24x24px");
|
||||
}
|
||||
|
||||
promises.push(new Promise(r => {
|
||||
png.decode(function (pixels) {
|
||||
var rgba = new Uint8Array(pixels);
|
||||
var isTransparent = false;
|
||||
for (var i=0;i<rgba.length;i+=4)
|
||||
if (rgba[i+3]<255) isTransparent=true;
|
||||
if (!isTransparent) { // make it transparent
|
||||
for (var i=0;i<rgba.length;i+=4)
|
||||
rgba[i+3] = 255-rgba[i];
|
||||
}
|
||||
|
||||
imgOptions.width = png.width;
|
||||
imgOptions.height = png.height;
|
||||
var img = imageconverter.RGBAtoString(rgba, imgOptions);
|
||||
iconImages[index] = img;
|
||||
console.log("Loaded "+icon.icon);
|
||||
if (img.length != IMAGE_BYTES) throw new Error("Image size should be 76 bytes");
|
||||
r(); // done
|
||||
});
|
||||
}));
|
||||
//
|
||||
}
|
||||
icon.index = index;
|
||||
});
|
||||
|
||||
Promise.all(promises).then(function() {
|
||||
// Allocate a big array of icons
|
||||
var iconData = new Uint8Array(IMAGE_BYTES * iconImages.length);
|
||||
iconImages.forEach((img,idx) => {
|
||||
// Yay, more JS. Why is it so hard to get the bytes???
|
||||
iconData.set(Array.prototype.slice.call(Buffer.from(img,"binary")), idx*IMAGE_BYTES)
|
||||
});
|
||||
|
||||
console.log("Saving images");
|
||||
require("fs").writeFileSync(__dirname+"/../icons.img", Buffer.from(iconData,"binary"));
|
||||
|
||||
console.log("Saving library");
|
||||
require("fs").writeFileSync(__dirname+"/../lib.js", `exports.getImage = function(msg) {
|
||||
if (msg.img) return atob(msg.img);
|
||||
let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||
if (msg.id=="music") s="music";
|
||||
let match = ${JSON.stringify(","+icons.map(icon=>icon.app+"|"+icon.index).join(",")+",")}.match(new RegExp(\`,\${s}\\\\|(\\\\d+)\`))
|
||||
return require("Storage").read("messageicons.img", (match===null)?0:match[1]*${IMAGE_BYTES}, ${IMAGE_BYTES});
|
||||
};
|
||||
|
||||
exports.getColor = function(msg,options) {
|
||||
options = options||{};
|
||||
var st = options.settings || require('Storage').readJSON("messages.settings.json", 1) || {};
|
||||
if (options.default===undefined) options.default=g.theme.fg;
|
||||
if (st.iconColorMode == 'mono') return options.default;
|
||||
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||
return {
|
||||
// generic colors, using B2-safe colors
|
||||
// DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used
|
||||
"airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/
|
||||
"mail": "#ff0",
|
||||
"music": "#f0f",
|
||||
"phone": "#0f0",
|
||||
"sms message": "#0ff",
|
||||
// brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos)
|
||||
// all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?)
|
||||
"bibel": "#54342c",
|
||||
"bring": "#455a64",
|
||||
"discord": "#5865f2", // https://discord.com/branding
|
||||
"etar": "#36a18b",
|
||||
"facebook": "#1877f2", // https://www.facebook.com/brand/resources/facebookapp/logo
|
||||
"gmail": "#ea4335",
|
||||
"gmx": "#1c449b",
|
||||
"google": "#4285F4",
|
||||
"google home": "#fbbc05",
|
||||
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
||||
"instagram": "#ff0069", // https://about.instagram.com/brand/gradient
|
||||
"lieferando": "#ff8000",
|
||||
"linkedin": "#0a66c2", // https://brand.linkedin.com/
|
||||
"messenger": "#0078ff",
|
||||
"mastodon": "#563acc", // https://www.joinmastodon.org/branding
|
||||
"mattermost": "#00f",
|
||||
"n26": "#36a18b",
|
||||
"nextbike": "#00f",
|
||||
"newpipe": "#f00",
|
||||
"nina": "#e57004",
|
||||
"opentasks": "#409f8f",
|
||||
"outlook mail": "#0078d4", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products
|
||||
"paypal": "#003087",
|
||||
"pocket": "#ef4154f", // https://blog.getpocket.com/press/
|
||||
"post & dhl": "#f2c101",
|
||||
"reddit": "#ff4500", // https://www.redditinc.com/brand
|
||||
"signal": "#3a76f0", // https://github.com/signalapp/Signal-Desktop/blob/main/images/signal-logo.svg
|
||||
"skype": "#0078d4", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products
|
||||
"slack": "#e51670",
|
||||
"snapchat": "#ff0",
|
||||
"steam": "#171a21",
|
||||
"teams": "#6264a7", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products
|
||||
"telegram": "#0088cc",
|
||||
"telegram foss": "#0088cc",
|
||||
"to do": "#3999e5",
|
||||
"twitch": "#9146ff", // https://brand.twitch.tv/
|
||||
"twitter": "#1d9bf0", // https://about.twitter.com/en/who-we-are/brand-toolkit
|
||||
"vlc": "#ff8800",
|
||||
"whatsapp": "#4fce5d",
|
||||
"wordfeud": "#e7d3c7",
|
||||
"youtube": "#f00", // https://www.youtube.com/howyoutubeworks/resources/brand-resources/#logos-icons-and-colors
|
||||
}[s]||options.default;
|
||||
};
|
||||
`);
|
||||
});
|
After Width: | Height: | Size: 227 B |
After Width: | Height: | Size: 269 B |
|
@ -0,0 +1,111 @@
|
|||
[
|
||||
{ "app":"default", "icon":"default.png" },
|
||||
{ "app":"airbnb", "icon":"airbnb.png" },
|
||||
{ "app":"alarm", "icon":"alarm.png" },
|
||||
{ "app":"alarmclockreceiver", "icon":"alarm.png" },
|
||||
{ "app":"amazon shopping", "icon":"amazon.png" },
|
||||
{ "app":"bibel", "icon":"bibel.png" },
|
||||
{ "app":"bitwarden", "icon":"security.png" },
|
||||
{ "app":"1password", "icon":"security.png" },
|
||||
{ "app":"lastpass", "icon":"security.png" },
|
||||
{ "app":"dashlane", "icon":"security.png" },
|
||||
{ "app":"bring", "icon":"bring.png" },
|
||||
{ "app":"calendar", "icon":"etar.png" },
|
||||
{ "app":"etar", "icon":"etar.png" },
|
||||
{ "app":"chat", "icon":"google chat.png" },
|
||||
{ "app":"chrome", "icon":"chrome.png" },
|
||||
{ "app":"corona-warn", "icon":"coronavirus.png" },
|
||||
{ "app":"bmo", "icon":"bank.png" },
|
||||
{ "app":"desjardins", "icon":"bank.png" },
|
||||
{ "app":"rbc mobile", "icon":"bank.png" },
|
||||
{ "app":"nbc", "icon":"bank.png" },
|
||||
{ "app":"rabobank", "icon":"bank.png" },
|
||||
{ "app":"scotiabank", "icon":"bank.png" },
|
||||
{ "app":"td (canada)", "icon":"bank.png" },
|
||||
{ "app":"discord", "icon":"discord.png" },
|
||||
{ "app":"drive", "icon":"google drive.png" },
|
||||
{ "app":"element", "icon":"matrix element.png" },
|
||||
{ "app":"facebook", "icon":"facebook.png" },
|
||||
{ "app":"messenger", "icon":"facebook messenger.png" },
|
||||
{ "app":"firefox", "icon":"firefox.png" },
|
||||
{ "app":"firefox beta", "icon":"firefox.png" },
|
||||
{ "app":"firefox nightly", "icon":"firefox.png" },
|
||||
{ "app":"f-droid", "icon":"security.png" },
|
||||
{ "app":"neo store", "icon":"security.png" },
|
||||
{ "app":"aurora droid", "icon":"security.png" },
|
||||
{ "app":"github", "icon":"github.png" },
|
||||
{ "app":"gitlab", "icon":"gitlab.png" },
|
||||
{ "app":"gmx", "icon":"gmx.png" },
|
||||
{ "app":"google", "icon":"google.png" },
|
||||
{ "app":"google home", "icon":"google home.png" },
|
||||
{ "app":"google play store", "icon":"google play store.png" },
|
||||
{ "app":"home assistant", "icon":"home assistant.png" },
|
||||
{ "app":"instagram", "icon":"instagram.png" },
|
||||
{ "app":"kalender", "icon":"kalender.png" },
|
||||
{ "app":"keep notes", "icon":"google keep.png" },
|
||||
{ "app":"lieferando", "icon":"lieferando.png" },
|
||||
{ "app":"linkedin", "icon":"linkedin.png" },
|
||||
{ "app":"maps", "icon":"map.png" },
|
||||
{ "app":"organic maps", "icon":"map.png" },
|
||||
{ "app":"osmand", "icon":"map.png" },
|
||||
{ "app":"mastodon", "icon":"mastodon.png" },
|
||||
{ "app":"fedilab", "icon":"mastodon.png" },
|
||||
{ "app":"tooot", "icon":"mastodon.png" },
|
||||
{ "app":"tusky", "icon":"mastodon.png" },
|
||||
{ "app":"mattermost", "icon":"mattermost.png" },
|
||||
{ "app":"n26", "icon":"n26.png" },
|
||||
{ "app":"netflix", "icon":"netflix.png" },
|
||||
{ "app":"news", "icon":"news.png" },
|
||||
{ "app":"cbc news", "icon":"news.png" },
|
||||
{ "app":"rc info", "icon":"news.png" },
|
||||
{ "app":"reuters", "icon":"news.png" },
|
||||
{ "app":"ap news", "icon":"news.png" },
|
||||
{ "app":"la presse", "icon":"news.png" },
|
||||
{ "app":"nbc news", "icon":"news.png" },
|
||||
{ "app":"nextbike", "icon":"nextbike.png" },
|
||||
{ "app":"nina", "icon":"nina.png" },
|
||||
{ "app":"outlook mail", "icon":"outlook.png" },
|
||||
{ "app":"paypal", "icon":"paypal.png" },
|
||||
{ "app":"phone", "icon":"phone.png" },
|
||||
{ "app":"plex", "icon":"plex.png" },
|
||||
{ "app":"pocket", "icon":"pocket.png" },
|
||||
{ "app":"post & dhl", "icon":"delivery.png" },
|
||||
{ "app":"proton mail", "icon":"protonmail.png" },
|
||||
{ "app":"reddit", "icon":"reddit.png" },
|
||||
{ "app":"sync pro", "icon":"reddit.png" },
|
||||
{ "app":"sync dev", "icon":"reddit.png" },
|
||||
{ "app":"boost", "icon":"reddit.png" },
|
||||
{ "app":"infinity", "icon":"reddit.png" },
|
||||
{ "app":"slide", "icon":"reddit.png" },
|
||||
{ "app":"signal", "icon":"signal.png" },
|
||||
{ "app":"skype", "icon":"skype.png" },
|
||||
{ "app":"slack", "icon":"slack.png" },
|
||||
{ "app":"snapchat", "icon":"snapchat.png" },
|
||||
{ "app":"starbucks", "icon":"cafe.png" },
|
||||
{ "app":"steam", "icon":"steam.png" },
|
||||
{ "app":"teams", "icon":"teams.png" },
|
||||
{ "app":"telegram", "icon":"telegram.png" },
|
||||
{ "app":"telegram foss", "icon":"telegram.png" },
|
||||
{ "app":"threema", "icon":"threema.png" },
|
||||
{ "app":"tiktok", "icon":"tiktok.png" },
|
||||
{ "app":"to do", "icon":"task.png" },
|
||||
{ "app":"opentasks", "icon":"task.png" },
|
||||
{ "app":"tasks", "icon":"task.png" },
|
||||
{ "app":"transit", "icon":"transit.png" },
|
||||
{ "app":"twitch", "icon":"twitch.png" },
|
||||
{ "app":"twitter", "icon":"twitter.png" },
|
||||
{ "app":"uber", "icon":"taxi.png" },
|
||||
{ "app":"lyft", "icon":"taxi.png" },
|
||||
{ "app":"vlc", "icon":"vlc.png" },
|
||||
{ "app":"warnapp", "icon":"warnapp.png" },
|
||||
{ "app":"whatsapp", "icon":"whatsapp.png" },
|
||||
{ "app":"wordfeud", "icon":"wordfeud.png" },
|
||||
{ "app":"youtube", "icon":"youtube.png" },
|
||||
{ "app":"newpipe", "icon":"youtube.png" },
|
||||
{ "app":"zoom", "icon":"videoconf.png" },
|
||||
{ "app":"meet", "icon":"videoconf.png" },
|
||||
{ "app":"music", "icon":"music.png" },
|
||||
{ "app":"sms message", "icon":"default.png" },
|
||||
{ "app":"mail", "icon":"default.png" },
|
||||
{ "app":"gmail", "icon":"default.png" }
|
||||
]
|
After Width: | Height: | Size: 249 B |
After Width: | Height: | Size: 209 B |
After Width: | Height: | Size: 188 B |
After Width: | Height: | Size: 228 B |
After Width: | Height: | Size: 261 B |
After Width: | Height: | Size: 247 B |
After Width: | Height: | Size: 213 B |
|
@ -1,80 +1,9 @@
|
|||
exports.getImage = function(msg) {
|
||||
/*
|
||||
* icons should be 24x24px or less with 1bpp colors and 'Transparency to Color'
|
||||
* http://www.espruino.com/Image+Converter
|
||||
*/
|
||||
if (msg.img) return atob(msg.img);
|
||||
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||
if (s=="airbnb") return atob("GBgBAAAAADwAAH4AAMMAAIMAAYGAAQGAAwDAAwDABjxgBn5gDMMwDMMwGMMYGMMYMGYMMGYMIDwEIBgEIDwEMH4MHee4D4HwAAAA"); // icons/airbnb.png
|
||||
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBgBAAAAAAAAAgBABwDgHn54Of+cE8PIBwDgDhhwDBgwHBg4GBgYGBgYGBgYGA4YHAc4DAEwDgBwBwDgA8PAAf+AAH4AAAAAAAAA"); // icons/alarm.png
|
||||
if (s=="amazon shopping") return atob("GBgBAAAAAP8AAf+AA//AA+fAA8PAAIPAAD/AAP/AA//AA+PAB8PAB8fAB8fgB//gA//gA/3AAPCecAAeOAAeDwH0B//kAf+AAAAA"); // icons/amazon.png
|
||||
if (s=="bibel") return atob("GBgBAAAAA//wD//4D//4H//4H/f4H/f4H+P4H4D4H4D4H/f4H/f4H/f4H/f4H/f4H//4H//4H//4GAAAEAAAEAAACAAAB//4AAAA");
|
||||
if (s=="bitwarden" || s=="1password" || s=="lastpass" || s=="dashlane") return atob("GBgBAAAAABgAAP8AA//AD4/wHg/4GA/4GA/4GA/4GA/4GA/4GA/4H/AYH/AYH/A4D/AwD/BwB/BgB/DgA/HAAfeAAP8AADwAAAAA"); // icons/security.png
|
||||
if (s=="bring") return atob("GBgBAAAAAAAAAAAAAAAAAHwAAFoAAf+AA/+AA/+AA/+AA/eAA+eAA0+AAx+AA7+AA/+AA//AA/+AAf8AAAIAAAAAAAAAAAAAAAAA");
|
||||
if (s=="calendar" || s=="etar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
|
||||
if (s=="chat") return atob("GBgBAAAAAf/8A//+A//+A//+OAB+e/8+e/++e/++e/++e/++e/++e/++ef+8fAAAf//Af//Af//Af//Af/+AcAAAYAAAQAAAAAAA"); // icons/google chat.png
|
||||
if (s=="chrome") return atob("GBgBAAAAAAAAAP8AA//AB+fgDwDwHgB4HAA4Pj/8OmYcO8McMYEMMYEMOMMcOGccOD4cHAw4Hgx4DxjwB//gA//AAP8AAAAAAAAA"); // icons/chrome.png
|
||||
if (s=="corona-warn") return atob("GBgBAAAAAAAAABgAABgABhhgDn5wD//wA8PAA+fAB2bgBgBgPpl8Ppl8BgBgB2bgA+fAA8PAD//wDn5wBhhgABgAABgAAAAAAAAA"); // icons/coronavirus.png
|
||||
if (s=="bmo" || s=="desjardins" || s=="rbc mobile" || s=="nbc" || s=="rabobank" || s=="scotiabank" || s=="td (canada)") return atob("GBgBAAAAADgAAP4AAe8AB4PAHgDwP//4P//4AAAAAAAADjjgDjjgDjjgDjjgDjjgDjjgDjjgAAAAAAAAP//4P//4AAAAAAAAAAAA"); // icons/bank.png
|
||||
if (s=="discord") return atob("GBgBAAAAAAAAAAAAAAAAA4HAD//wH//4H//4P//8P//8P//8fn5+fDw+fDw+fn5+f//+f//+ff++PgB8DgBwAAAAAAAAAAAAAAAA"); // icons/discord.png
|
||||
if (s=="drive") return atob("GBgBAAAAAAAAAH8AAH8AAT+AA7/AA9/AB8/gB+/gD+fwD+fwH8P4P8P8P4H8fwAAf3/+Pn/8Pv/8HP/4Df/wC//wAAAAAAAAAAAA"); // icons/google drive.png
|
||||
if (s=="element") return atob("GBgBAAAAAHwAAH4AAH8AAAeAAePAB+HAD+DgHgDgPADuOADucAAOcAAOdwAcdwA8BwB4BwfwA4fgA8eAAeAAAP4AAH4AAD4AAAAA"); // icons/matrix element.png
|
||||
if (s=="facebook") return atob("GBgBAAAAAAAAAH4AAf+AB//gD//wD/DwH+D4H+P4P+f8P+f8P+f8PwD8PwD8PwD8H+f4H+f4D+fwD+fwB+fgAeeAAOcAAAAAAAAA"); // icons/facebook.png
|
||||
if (s=="messenger") return atob("GBgBAAAAAAAAAP8AA//AB//gD//wH//4H//4P//8P9+8P458PwB8PgD8PnH8Pfv8H//4H//4D//wB//gB//AB/8AAwAAAAAAAAAA"); // icons/facebook messenger.png
|
||||
if (s=="firefox" || s=="firefox beta" || s=="firefox nightly") return atob("GBgBAAAAAAAAAAMAAAcAAAeABA/ADY/gH4P4H4H4H8H8P/H8P+D8PwD8PwD8PwD8H4H4H8P4H//4D//wB//gA//AAP8AAAAAAAAA"); // icons/firefox.png
|
||||
if (s=="f-droid" || s=="neo store" || s=="aurora droid") return atob("GBgBAAAAQAACYAAGP//8H//4H//4HH44HH44H//4AAAAH//4H8P4H734H374HsN4Hvl4Hv14Hvl4HsN4H374H734H8P4D//wAAAA"); // icons/security.png
|
||||
if (s=="github") return atob("GBgBAAAAAAAAAH4AAf+AB//gD//wDv9wHgB4HgB4PAA8PAA8PAA8PAA8PAA8PgB8HwD4G8P4DcPwDgPwB4PgAcOAAAAAAAAAAAAA"); // icons/github.png
|
||||
if (s=="gitlab") return atob("GBgBAAAABAAgDAAwDAAwHgB4HgB4PgB8PwD8P//8f//+f//+f//+f//+f//+f//+P//8H//4D//wA//AAf+AAP8AADwAABgAAAAA"); // icons/gitlab.png
|
||||
if (s=="gmx") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEJmfmd8Zuc85v847/88Z9s8fttmHIHiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
if (s=="google") return atob("GBgBAAAAAP8AA//AB//gD//gH+fAP4CAPwAAPgAAfAAAfA/+fA/+fA/+fA/+fAA+PgA+PwB8P4D8H+f4D//4B//wA//AAP8AAAAA"); // icons/google.png
|
||||
if (s=="google home") return atob("GBgBAAAAABgAADwAAH4AAf4AA/zAB/vgD/fwH+f4P4H8fwD+fgB+fAA+eAA+cAA+bAA+HAA+PAA+ff++ff++ff++ff++Pf+8AAAA"); // icons/google home.png
|
||||
if (s=="google play store") return atob("GBgBAAAAAAAAAH4AAP8AAMMAAMMAP//8P//8MAAMMAAMMGAMMHgMMH4MMH8MMH4MMHgMMGAMMAAMMAAMP//8H//4AAAAAAAAAAAA"); // icons/google play store.png
|
||||
if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA=");
|
||||
if (s=="instagram") return atob("GBgBAAAAD//wH//4OAAccAAOYABmYDxmYP8GYeeGYYGGY4HGYwDGYwDGY4HGYYGGYeeGYP8GYDwGYAAGcAAOOAAcH//4D//wAAAA"); // icons/instagram.png
|
||||
if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA");
|
||||
if (s=="keep notes") return atob("GBgBAAAAAAAAH//4P//8P8P8Pzz8P378Pv98Pv98Pv98Pv98P378Pzz8P738P4H8P738P738P4GMP8OYP/+wP//gH//AAAAAAAAA"); // icons/google keep.png
|
||||
if (s=="lieferando") return atob("GBgBAAAAADwAAH4AAP/gAf/wA//wB//wD//wH//4H/98Pt58ft5+Ptx8DtxwDtxwDhxwDhhwDhhwDzhwD75wD75wD75wB77gAAAA"); // icons/lieferando.png
|
||||
if (s=="linkedin") return atob("GBgBAAAAf//+f//+f//+ef/+cf/+cf/+f//+f//+ccw+ccAeccAecccOcceOcceOcceOcceOcceOcceOec+ef//+f//+f//+AAAA"); // icons/linkedin.png
|
||||
if (s=="maps" || s=="organic maps" || s=="osmand") return atob("GBgBAAAAAAAAAAAAAeAYD/z4H//4GMeYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGeMYH//4Hz/wGAeAAAAAAAAAAAAA"); // icons/map.png
|
||||
if (s=="mastodon" || s=="fedilab" || s=="tooot" || s=="tusky") return atob("GBgBAAAAB//gD//4H//4P//8PBg8PAA8fOMeeOeeeOeeOOeeOOecOP+cOP+cP//8P//4P//4P//gHwAAH4AAD+cAB/8AAf4AAAAA"); // icons/mastodon.png
|
||||
if (s=="mattermost") return atob("GBgBAAAAAPAAA+EAB4GADgOQHAeYOA+cOB+MeB+OcD+GcD+GcD+GeD8OeB4OeAAOfAAePgA8P4B8H/f4D//wB//gA//AAP8AAAAA"); // icons/mattermost.png
|
||||
if (s=="n26") return atob("GBgBAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAOIAAOIAAPIAANoAANoAAM4AAMYAAMYAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAA");
|
||||
if (s=="netflix") return atob("GBgBAAAAA8PAA+PAAePAAePAAfPAAvPAA/PAAvvAAn/AA/nAA3/AA/7AA5/AA/5AA99AA8/AA89AA8+AA8eAA8eAA8fAA8PAAAAA"); // icons/netflix.png
|
||||
if (s=="news" || s=="cbc news" || s=="rc info" || s=="reuters" || s=="ap news" || s=="la presse" || s=="nbc news") return atob("GBgBAAAAAAAAAAAALaW0P//8P//8P//8P//8MAAMMAAMMAAMP//8P//8MBwcMBwcMB/8MB/8MBwcMBwcP//8P//8AAAAAAAAAAAA"); // icons/news.png
|
||||
if (s=="nextbike") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAACAfgDAPwDAP4HAH4N4H8f8D82GMd8CMDsDMGMDMGGGGMHOD4D8AAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA");
|
||||
if (s=="outlook mail") return atob("GBgBAAAAAAAAAP/8AP/8AP/8AJjMf/jMf//8f//8cHjMd3jMZz/+Zz/+d3jecHj+f//mf/eGf/PGAwDmAwA+A//+Af/+AAAAAAAA"); // icons/outlook.png
|
||||
if (s=="paypal") return atob("GBgBAAAAA/+AA//gA//wB//wB//wB//wB//wB//wB//gD//gD//ID/+ID/wwD4BwD5/gD74AH7gAHzAAHzAAHzAAAHAAAHAAAAAA"); // icons/paypal.png
|
||||
if (s=="phone") return atob("GBgBAAAAAAAAH4AAP8AAP8AAP8AAH8AAH8AAH8AAH4AADwAADwAABwAAA4AAA8HwAeP8AP/8AH/8AD/8AA/8AAP8AAB4AAAAAAAA"); // icons/phone.png
|
||||
if (s=="plex") return atob("GBgBAAAAB/gAB/gAA/wAAf4AAf4AAP8AAH+AAH+AAD/AAB/gAB/gAB/gAB/gAD/AAH+AAH+AAP8AAf4AAf4AA/wAB/gAB/gAAAAA"); // icons/plex.png
|
||||
if (s=="pocket") return atob("GBgBAAAAAAAAP//8f//+f//+f//+f//+f//+fP8+eH4efDw+fhh+fwD+f4H+P8P8P+f8H//4H//4D//wB//gAf+AAH4AAAAAAAAA"); // icons/pocket.png
|
||||
if (s=="post & dhl") return atob("GBgBAAAAAAAAAAAAAAAAP/+Af/+AYAGAYAGAYAHwYAH4YAGMYAGGYAH+YAH+bwH+f//+ef+eGYGYH4H4DwDwAAAAAAAAAAAAAAAA"); // icons/delivery.png
|
||||
if (s=="proton mail") return atob("GBgBAAAAAAAAAAAAQAACYAAGcAAOeAAePABeXgDebwHed4Pee/fefe/efh/ef//ef//ef//ef//ef//ef//eP//cAAAAAAAAAAAA"); // icons/protonmail.png
|
||||
if (s=="reddit" || s=="sync pro" || s=="sync dev" || s=="boost" || s=="infinity" || s=="slide") return atob("GBgBAAAAAAAAAAYwAAX4AAh4AAgwAAgAAAgAAH4AAf+AN//sf//+fn5+PDw8HDw4Hn54H//4H//4DzzwB4HgAf+AAH4AAAAAAAAA"); // icons/reddit.png
|
||||
if (s=="signal") return atob("GBgBAAAAAL0AAYGABH4gCf+QE//IB//gL//0b//2H//4X//6X//6X//6X//6H//4b//2L//0D//gL//ID/+QYH4gVYGAcL0AAAAA"); // icons/signal.png
|
||||
if (s=="skype") return atob("GBgBAAAAB8AAH/8AP//AP//gf8fwfwD4fgB4fjx8fj/8Pg/8PwH8P4B8P/h8Pnx+Pjx+Hhh+HwD+D8P+B//8A//8AP/4AAPgAAAA"); // icons/skype.png
|
||||
if (s=="slack") return atob("GBgBAAAAAOcAAeeAAeeAAeeAAGeAAAeAP+ecf+eef+e+f+e+AAAAAAAAfef+fef+eef+Oef8AeAAAeYAAeeAAeeAAeeAAOcAAAAA"); // icons/slack.png
|
||||
if (s=="snapchat") return atob("GBgBAAAAAAAAAAAAAH4AAf+AAYGAAwDAAwDAAwDADwDwDwDwDgBwBwDgBwDgDgBwHAA4OAAcHAA4D4HwB//gAH4AAAAAAAAAAAAA"); // icons/snapchat.png
|
||||
if (s=="starbucks") return atob("GBgBAAAAAAAAAAAAD//4D//8DADMDADMDADMDAD8DAD4DADADADADADADADADgHAB/+AA/8AAAAAAAAAP//wP//wAAAAAAAAAAAA"); // icons/cafe.png
|
||||
if (s=="steam") return atob("GBgBAAAAAAAAAf+AA//AD//wD//wH/g4P/OcP/RcP+RcP+ReH8OeB4A+AAH+AMP8IC/8OS/8HN/4Dj/wD//wA//AAf+AAAAAAAAA"); // icons/steam.png
|
||||
if (s=="teams") return atob("GBgBAAAAAAgAAD4AADcYAGM8AGNmP/dmP/48MDAYMD/+PP/+PPBmPPBmPPBmPPBmP/BmP/BmH+B+AYD4AMDAAOOAAH8AABwAAAAA"); // icons/teams.png
|
||||
if (s=="telegram" || s=="telegram foss") return atob("GBgBAAAAAAAAAAAAAAAeAAB+AAP+AA/+AD/+Af9+B/z+H/n8f+P8f8f8Dw/8AB/8AB/8AB/4AAf4AAP4AAD4AABwAAAAAAAAAAAA"); // icons/telegram.png
|
||||
if (s=="threema") return atob("GBgBAAAAAP8AA//AB//gD//wH8P4H9v4H734P5n8P4H8P4H8H4H4H4H4D//wD//gD//AH/8AHDwAAAAAAAAABhhgDzzwBhhgAAAA"); // icons/threema.png
|
||||
if (s=="tiktok") return atob("GBgBAAAAAAAAAAcAAAcAAAeAAAfAAAfwAAf4AAf4AMd4A8cAB8cAD8cADwcAHgcAHgcAHg8ADw8AD/4AB/4AA/wAAfAAAAAAAAAA"); // icons/tiktok.png
|
||||
if (s=="to do" || s=="opentasks" || s=="tasks") return atob("GBgBAAAAAHwAAf+AA//ID4GcHwA8HAB4PADwOAHgcAPGcAeOcY8Oc94OcfwOcPgOOHAcOCAcHAA4DgB4D4HwB//gAf+AAH4AAAAA"); // icons/task.png
|
||||
if (s=="transit") return atob("GBgBAAAAD//wP//8P//8f//+f/j+ffA+eOA+eOMef+cefef+eOe+fecef+e+eOf+eOcefAcefA++fx/+f//+P//8P//8D//wAAAA"); // icons/transit.png
|
||||
if (s=="twitch") return atob("GBgBAAAAA//8B//8DgAMHgAMPhjMPhjMPhjMPhjMPhjMPgAMPgAMPgAYPgAwP+fgP+/AP/+AP/8AP/4AAeAAAcAAAYAAAQAAAAAA"); // icons/twitch.png
|
||||
if (s=="twitter") return atob("GBgBAAAAAAAAAAAAAAPAIAf8MA/4PA/8Pg/4H//4H//4P//4P//wH//wD//wD//gD//AA//AAf+AB/8AP/wAD/AAAAAAAAAAAAAA"); // icons/twitter.png
|
||||
if (s=="uber" || s=="lyft") return atob("GBgBAAAAAAAAAAAAAH4AAH4AB//gB//gDgBwDAAwDAAwH//4H//4GAAYG4HYG4HYG4HYGAAYH//4H//4HAA4HAA4AAAAAAAAAAAA"); // icons/taxi.png
|
||||
if (s=="vlc") return atob("GBgBAAAAABgAABgAADwAADwAAAAAAAAAAAAAAAAAAIEAAP8AAP8AAf+AAP8AAAAADAAwDAAwHAA4HwD4H//4P//8P//8P//8AAAA"); // icons/vlc.png
|
||||
if (s=="warnapp") return atob("GBgBAAAAAAAAAAAAAH4AAP8AA//AA//AD//gP//gf//4f//+/+P+/8H//8n//4n/fxh/fzg+Pj88Dn44AA4AAAwAAAwAAAgAAAAA");
|
||||
if (s=="whatsapp") return atob("GBgBAAAAAP8AA//AB4HwDgB4HAA4OAAcMYAMc8AOc8AGY8AGYcAGYeAGYPOGcH/OcD/OMA+MOAAcMAA4MgBwf8Pgf//AcP8AAAAA"); // icons/whatsapp.png
|
||||
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
|
||||
if (s=="youtube" || s=="newpipe") return atob("GBgBAAAAAAAAAAAAAAAAAAAAH//4P//8P//8f//+f8/+f8P+f8D+f8D+f8P+f8/+f//+P//8P//8H//4AAAAAAAAAAAAAAAAAAAA"); // icons/youtube.png
|
||||
if (s=="zoom" || s=="meet") return atob("GBgBAAAAAAAAAAAAP/+Af//Af//AcADicADmcADucAD+cAD+cAD+cAD+cAD+cAD+cADucADmcADif//Af//AP/+AAAAAAAAAAAAA"); // icons/videoconf.png
|
||||
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
|
||||
// if (s=="sms message" || s=="mail" || s=="gmail") // .. default icon (below)
|
||||
return atob("FhKBAH//+P//yf/+c//z5/+fz/z/n+f/Pz/+ef/8D///////////////////////f//4///A");
|
||||
let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||
if (msg.id=="music") s="music";
|
||||
let match = ",default|0,airbnb|1,alarm|2,alarmclockreceiver|2,amazon shopping|3,bibel|4,bitwarden|5,1password|5,lastpass|5,dashlane|5,bring|6,calendar|7,etar|7,chat|8,chrome|9,corona-warn|10,bmo|11,desjardins|11,rbc mobile|11,nbc|11,rabobank|11,scotiabank|11,td (canada)|11,discord|12,drive|13,element|14,facebook|15,messenger|16,firefox|17,firefox beta|17,firefox nightly|17,f-droid|5,neo store|5,aurora droid|5,github|18,gitlab|19,gmx|20,google|21,google home|22,google play store|23,home assistant|24,instagram|25,kalender|26,keep notes|27,lieferando|28,linkedin|29,maps|30,organic maps|30,osmand|30,mastodon|31,fedilab|31,tooot|31,tusky|31,mattermost|32,n26|33,netflix|34,news|35,cbc news|35,rc info|35,reuters|35,ap news|35,la presse|35,nbc news|35,nextbike|36,nina|37,outlook mail|38,paypal|39,phone|40,plex|41,pocket|42,post & dhl|43,proton mail|44,reddit|45,sync pro|45,sync dev|45,boost|45,infinity|45,slide|45,signal|46,skype|47,slack|48,snapchat|49,starbucks|50,steam|51,teams|52,telegram|53,telegram foss|53,threema|54,tiktok|55,to do|56,opentasks|56,tasks|56,transit|57,twitch|58,twitter|59,uber|60,lyft|60,vlc|61,warnapp|62,whatsapp|63,wordfeud|64,youtube|65,newpipe|65,zoom|66,meet|66,music|67,sms message|0,mail|0,gmail|0,".match(new RegExp(`,${s}\\|(\\d+)`))
|
||||
return require("Storage").read("messageicons.img", (match===null)?0:match[1]*76, 76);
|
||||
};
|
||||
|
||||
exports.getColor = function(msg,options) {
|
||||
|
@ -136,3 +65,4 @@ exports.getColor = function(msg,options) {
|
|||
"youtube": "#f00", // https://www.youtube.com/howyoutubeworks/resources/brand-resources/#logos-icons-and-colors
|
||||
}[s]||options.default;
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messageicons",
|
||||
"name": "Message Icons",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Library containing a list of icons and colors for apps",
|
||||
"icon": "app.png",
|
||||
"type": "module",
|
||||
|
@ -10,6 +10,7 @@
|
|||
"provides_modules" : ["messageicons"],
|
||||
"default": true,
|
||||
"storage": [
|
||||
{"name":"messageicons","url":"lib.js"}
|
||||
{"name":"messageicons","url":"lib.js"},
|
||||
{"name":"messageicons.img","url":"icons.img"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"id": "simplestpp",
|
||||
"name": "Simplest++ Clock",
|
||||
"shortName": "Simplest++",
|
||||
"version": "0.01",
|
||||
"description": "The simplest working clock, with fast load and clock_info, acts as a tutorial piece",
|
||||
"readme": "README.md",
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Added clock infos to expose timer functionality to clocks.
|
||||
0.04: Improvements of clock infos.
|
||||
0.05: Updated clkinfo icon.
|
||||
0.06: Ensure Timer supplies an image for clkinfo items
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
items: [
|
||||
{
|
||||
name: null,
|
||||
get: () => ({ text: getAlarmMinutesText() + (isAlarmEnabled() ? " min" : ""), img: null}),
|
||||
get: () => ({ text: getAlarmMinutesText() + (isAlarmEnabled() ? " min" : ""), img: smpltmrItems.img }),
|
||||
show: function() { smpltmrItems.items[0].emit("redraw"); },
|
||||
hide: function () {},
|
||||
run: function() { }
|
||||
|
@ -81,7 +81,7 @@
|
|||
offsets.forEach((o, i) => {
|
||||
smpltmrItems.items = smpltmrItems.items.concat({
|
||||
name: null,
|
||||
get: () => ({ text: (o > 0 ? "+" : "") + o + " min.", img: null}),
|
||||
get: () => ({ text: (o > 0 ? "+" : "") + o + " min.", img: smpltmrItems.img }),
|
||||
show: function() { smpltmrItems.items[i+1].emit("redraw"); },
|
||||
hide: function () {},
|
||||
run: function() {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "smpltmr",
|
||||
"name": "Simple Timer",
|
||||
"shortName": "Simple Timer",
|
||||
"version": "0.05",
|
||||
"version": "0.06",
|
||||
"description": "A very simple app to start a timer.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm,timer,clkinfo",
|
||||
|
|
|
@ -245,7 +245,7 @@ apps.forEach((app,appIdx) => {
|
|||
if (!STORAGE_KEYS.includes(key)) ERROR(`App ${app.id} file ${file.name} has unknown key ${key}`, {file:appDirRelative+file.url});
|
||||
}
|
||||
// warn if JS icon is the wrong size
|
||||
if (file.name == app.id+".img") {
|
||||
if (file.name == app.id+".img" && file.evaluate) {
|
||||
let icon;
|
||||
let match = fileContents.match(/^\s*E\.toArrayBuffer\(atob\(\"([^"]*)\"\)\)\s*$/);
|
||||
if (match==null) match = fileContents.match(/^\s*atob\(\"([^"]*)\"\)\s*$/);
|
||||
|
|
2
core
|
@ -1 +1 @@
|
|||
Subproject commit 2a89ea64f7874b9264572f68836fe8ecd0a6b191
|
||||
Subproject commit 376824068d90986c245b46970fd80ccdca44e431
|
|
@ -193,7 +193,15 @@
|
|||
"cyan": "Cyan",
|
||||
"orange": "Orange",
|
||||
"purple": "Violett",
|
||||
"grey": "Grau"
|
||||
"grey": "Grau",
|
||||
"New Moon": "Neumond",
|
||||
"Waxing Crescent": "Zunehmender Sichelmond ",
|
||||
"First Quarter": "Zunehmender Halbmond",
|
||||
"Waxing Gibbous": "Zunehmender Mond",
|
||||
"Full Moon": "Vollmond,",
|
||||
"Waning Gibbous": "Abnehmender Mond",
|
||||
"Last Quater": "Abnehmender Halbmond",
|
||||
"Waning Crescent": "Abnehmender Sichelmond"
|
||||
},
|
||||
"alarm": {
|
||||
"//": "App-specific overrides",
|
||||
|
|
|
@ -58,6 +58,7 @@ function ClockFace(options) {
|
|||
|
||||
ClockFace.prototype.tick = function() {
|
||||
"ram"
|
||||
if (this._removed) return;
|
||||
const time = new Date();
|
||||
const now = {
|
||||
d: `${time.getFullYear()}-${time.getMonth()}-${time.getDate()}`,
|
||||
|
@ -131,6 +132,7 @@ ClockFace.prototype.resume = function() {
|
|||
this.tick();
|
||||
};
|
||||
ClockFace.prototype.remove = function() {
|
||||
this._removed = true;
|
||||
if (this._timeout) clearTimeout(this._timeout);
|
||||
Bangle.removeListener("lcdPower", this._onLcd);
|
||||
if (this._remove) this._remove.apply(this);
|
||||
|
|
|
@ -61,12 +61,15 @@ if (stepGoal == undefined) {
|
|||
|
||||
exports.load = function() {
|
||||
// info used for drawing...
|
||||
var hrm = "--";
|
||||
var hrm = 0;
|
||||
var alt = "--";
|
||||
// callbacks (needed for easy removal of listeners)
|
||||
function batteryUpdateHandler() { bangleItems[0].emit("redraw"); }
|
||||
function stepUpdateHandler() { bangleItems[1].emit("redraw"); }
|
||||
function hrmUpdateHandler() { bangleItems[2].emit("redraw"); }
|
||||
function hrmUpdateHandler(e) {
|
||||
if (e && e.confidence>60) hrm = Math.round(e.bpm);
|
||||
bangleItems[2].emit("redraw");
|
||||
}
|
||||
function altUpdateHandler() {
|
||||
Bangle.getPressure().then(data=>{
|
||||
if (!data) return;
|
||||
|
@ -99,12 +102,12 @@ exports.load = function() {
|
|||
},
|
||||
{ name : "HRM",
|
||||
hasRange : true,
|
||||
get : () => { let v = Math.round(Bangle.getHealthStatus("last").bpm); return {
|
||||
text : v + " bpm", v : v, min : 40, max : 200,
|
||||
get : () => { return {
|
||||
text : (hrm||"--") + " bpm", v : hrm, min : 40, max : 200,
|
||||
img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAADAAADAAAHAAAHjAAHjgAPngH9n/n82/gA+AAA8AAA8AAAcAAAYAAAYAAAAAAAAAAAAAAAAAAAAAA==")
|
||||
}},
|
||||
show : function() { Bangle.setHRMPower(1,"clkinfo"); Bangle.on("HRM", hrmUpdateHandler); hrm = Math.round(Bangle.getHealthStatus("last").bpm); hrmUpdateHandler(); },
|
||||
hide : function() { Bangle.setHRMPower(0,"clkinfo"); Bangle.removeListener("HRM", hrmUpdateHandler); hrm = "--"; },
|
||||
show : function() { Bangle.setHRMPower(1,"clkinfo"); Bangle.on("HRM", hrmUpdateHandler); hrm = Math.round(Bangle.getHealthStatus().bpm||Bangle.getHealthStatus("last").bpm); hrmUpdateHandler(); },
|
||||
hide : function() { Bangle.setHRMPower(0,"clkinfo"); Bangle.removeListener("HRM", hrmUpdateHandler); hrm = 0; },
|
||||
}
|
||||
],
|
||||
}];
|
||||
|
|
|
@ -279,7 +279,7 @@ function hoursLater(date, h) {
|
|||
// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
|
||||
|
||||
SunCalc.getMoonTimes = function (date, lat, lng, inUTC) {
|
||||
var t = new Date(date);
|
||||
var t = typeof(date) === "object" ? date : new Date(date);
|
||||
if (inUTC) t.setUTCHours(0, 0, 0, 0);
|
||||
else t.setHours(0, 0, 0, 0);
|
||||
|
||||
|
|