Weather: Unify formatting and more modern style of javascript

master^2
Rengyr 2025-05-17 18:29:26 +02:00
parent bc375c512b
commit dcec8ead09
No known key found for this signature in database
5 changed files with 124 additions and 126 deletions

View File

@ -16,24 +16,24 @@ var layout = new Layout({type:"v", bgCol: g.theme.bg, c: [
render: l => { render: l => {
if (!current || current.uv === undefined) return; if (!current || current.uv === undefined) return;
const uv = Math.min(parseInt(current.uv), 11); // Cap at 11 const uv = Math.min(parseInt(current.uv), 11); // Cap at 11
// UV color thresholds: [max_value, color] based on WHO standards // UV color thresholds: [max_value, color] based on WHO standards
const colors = [[2,"#0F0"], [5,"#FF0"], [7,"#F80"], [10,"#F00"], [11,"#F0F"]]; const colors = [[2,"#0F0"], [5,"#FF0"], [7,"#F80"], [10,"#F00"], [11,"#F0F"]];
const color = colors.find(c => uv <= c[0])[1]; const color = colors.find(c => uv <= c[0])[1];
// Setup and measure label // Setup and measure label
g.setFont("6x8").setFontAlign(-1, 0); g.setFont("6x8").setFontAlign(-1, 0);
const label = "UV: "; const label = "UV: ";
const labelW = g.stringWidth(label); const labelW = g.stringWidth(label);
// Calculate centered position (4px block + 1px spacing) * blocks - last spacing // Calculate centered position (4px block + 1px spacing) * blocks - last spacing
const totalW = labelW + uv * 5 - (uv > 0 ? 1 : 0); const totalW = labelW + uv * 5 - (uv > 0 ? 1 : 0);
const x = l.x + (l.w - totalW) / 2; const x = l.x + (l.w - totalW) / 2;
const y = l.y + l.h+6; const y = l.y + l.h+6;
// Draw label // Draw label
g.setColor(g.theme.fg).drawString(label, x, y); g.setColor(g.theme.fg).drawString(label, x, y);
// Draw UV blocks // Draw UV blocks
g.setColor(color); g.setColor(color);
for (let i = 0; i < uv; i++) { for (let i = 0; i < uv; i++) {
@ -58,7 +58,7 @@ var layout = new Layout({type:"v", bgCol: g.theme.bg, c: [
{type: "txt", font: "6x8", pad: 2, halign: 1, label: /*LANG*/"Hum:"}, {type: "txt", font: "6x8", pad: 2, halign: 1, label: /*LANG*/"Hum:"},
{type: "txt", font: "9%", pad: 2, halign: 1, id: "hum", label: "000%"}, {type: "txt", font: "9%", pad: 2, halign: 1, id: "hum", label: "000%"},
]}, ]},
{filly: 1}, {filly: 1},
{type: "txt", font: "6x8", pad: 2, halign: -1, label: /*LANG*/"Wind"}, {type: "txt", font: "6x8", pad: 2, halign: -1, label: /*LANG*/"Wind"},
{type: "h", halign: -1, c: [ {type: "h", halign: -1, c: [
@ -79,7 +79,7 @@ var layout = new Layout({type:"v", bgCol: g.theme.bg, c: [
]}, {lazy: true}); ]}, {lazy: true});
function formatDuration(millis) { function formatDuration(millis) {
let pluralize = (n, w) => n + " " + w + (n == 1 ? "" : "s"); let pluralize = (n, w) => `${n} ${w}${n === 1 ? "" : "s"}`;
if (millis < 60000) return /*LANG*/"< 1 minute"; if (millis < 60000) return /*LANG*/"< 1 minute";
if (millis < 3600000) return pluralize(Math.floor(millis/60000), /*LANG*/"minute"); if (millis < 3600000) return pluralize(Math.floor(millis/60000), /*LANG*/"minute");
if (millis < 86400000) return pluralize(Math.floor(millis/3600000), /*LANG*/"hour"); if (millis < 86400000) return pluralize(Math.floor(millis/3600000), /*LANG*/"hour");
@ -98,11 +98,11 @@ function draw() {
}else{ }else{
layout.feelslike.label = feelsLikeTemp[1]+feelsLikeTemp[2]; layout.feelslike.label = feelsLikeTemp[1]+feelsLikeTemp[2];
} }
layout.hum.label = current.hum+"%"; layout.hum.label = `${current.hum}%`;
const wind = locale.speed(current.wind).match(/^(\D*\d*)(.*)$/); const wind = locale.speed(current.wind).match(/^(\D*\d*)(.*)$/);
layout.wind.label = wind[1]; layout.wind.label = wind[1];
layout.windUnit.label = wind[2] + " " + (current.wrose||'').toUpperCase(); layout.windUnit.label = `${wind[2]} ${(current.wrose||'').toUpperCase()}`;
layout.cond.label = current.txt.charAt(0).toUpperCase()+(current.txt||'').slice(1); layout.cond.label = current.txt.charAt(0).toUpperCase()+(current.txt||'').slice(1);
layout.loc.label = current.loc; layout.loc.label = current.loc;
layout.updateTime.label = `${formatDuration(Date.now() - current.time)} ago`; // How to autotranslate this and similar? layout.updateTime.label = `${formatDuration(Date.now() - current.time)} ago`; // How to autotranslate this and similar?

View File

@ -1,5 +1,4 @@
(() => {
(function() {
var weather; var weather;
var weatherLib = require("weather"); var weatherLib = require("weather");
@ -8,9 +7,9 @@
if(weather){ if(weather){
weather.temp = require("locale").temp(weather.temp-273.15); weather.temp = require("locale").temp(weather.temp-273.15);
weather.feels = require("locale").temp(weather.feels-273.15); weather.feels = require("locale").temp(weather.feels-273.15);
weather.hum = weather.hum + "%"; weather.hum = `${weather.hum}%`;
weather.wind = require("locale").speed(weather.wind).match(/^(\D*\d*)(.*)$/); weather.wind = require("locale").speed(weather.wind).match(/^(\D*\d*)(.*)$/);
weather.wind = Math.round(weather.wind[1]) + "kph"; weather.wind = `${Math.round(weather.wind[1])}kph`;
} else { } else {
weather = { weather = {
temp: "?", temp: "?",

View File

@ -1,5 +1,5 @@
const storage = require('Storage'); const storage = require("Storage");
const B2 = process.env.HWVERSION===2; const B2 = process.env.HWVERSION === 2;
let expiryTimeout; let expiryTimeout;
function scheduleExpiry(json) { function scheduleExpiry(json) {
@ -7,7 +7,7 @@ function scheduleExpiry(json) {
clearTimeout(expiryTimeout); clearTimeout(expiryTimeout);
expiryTimeout = undefined; expiryTimeout = undefined;
} }
let expiry = "expiry" in json ? json.expiry : 2*3600000; let expiry = "expiry" in json ? json.expiry : 2 * 3600000;
if (json.weather && json.weather.time && expiry) { if (json.weather && json.weather.time && expiry) {
let t = json.weather.time + expiry - Date.now(); let t = json.weather.time + expiry - Date.now();
expiryTimeout = setTimeout(update, t); expiryTimeout = setTimeout(update, t);
@ -15,7 +15,7 @@ function scheduleExpiry(json) {
} }
function update(weatherEvent) { function update(weatherEvent) {
let json = storage.readJSON('weather.json')||{}; let json = storage.readJSON("weather.json") || {};
if (weatherEvent) { if (weatherEvent) {
let weather = weatherEvent.clone(); let weather = weatherEvent.clone();
@ -24,92 +24,90 @@ function update(weatherEvent) {
if (weather.wdir != null) { if (weather.wdir != null) {
// Convert numeric direction into human-readable label // Convert numeric direction into human-readable label
let deg = weather.wdir; let deg = weather.wdir;
while (deg<0 || deg>360) { while (deg < 0 || deg > 360) {
deg = (deg+360)%360; deg = (deg + 360) % 360;
} }
weather.wrose = ['n','ne','e','se','s','sw','w','nw','n'][Math.floor((deg+22.5)/45)]; weather.wrose = ["n", "ne", "e", "se", "s", "sw", "w", "nw", "n"][Math.floor((deg + 22.5) / 45)];
} }
json.weather = weather; json.weather = weather;
} } else {
else {
delete json.weather; delete json.weather;
} }
storage.write("weather.json", json);
storage.write('weather.json', json);
scheduleExpiry(json); scheduleExpiry(json);
exports.emit("update", json.weather); exports.emit("update", json.weather);
} }
exports.update = update; exports.update = update;
const _GB = global.GB; const _GB = global.GB;
global.GB = (event) => { global.GB = (event) => {
if (event.t==="weather") update(event); if (event.t === "weather") update(event);
if (_GB) setTimeout(_GB, 0, event); if (_GB) setTimeout(_GB, 0, event);
}; };
exports.get = function() { exports.get = () => {
return (storage.readJSON('weather.json')||{}).weather; return (storage.readJSON("weather.json") || {}).weather;
} };
scheduleExpiry(storage.readJSON('weather.json')||{}); scheduleExpiry(storage.readJSON("weather.json") || {});
function getPalette(monochrome, ovr) { function getPalette(monochrome, ovr) {
var palette; var palette;
if(monochrome) { if (monochrome) {
palette = { palette = {
sun: '#FFF', sun: "#FFF",
cloud: '#FFF', cloud: "#FFF",
bgCloud: '#FFF', bgCloud: "#FFF",
rain: '#FFF', rain: "#FFF",
lightning: '#FFF', lightning: "#FFF",
snow: '#FFF', snow: "#FFF",
mist: '#FFF', mist: "#FFF",
background: '#000' background: "#000",
}; };
} else { } else {
if (B2) { if (B2) {
if (ovr.theme.dark) { if (ovr.theme.dark) {
palette = { palette = {
sun: '#FF0', sun: "#FF0",
cloud: '#FFF', cloud: "#FFF",
bgCloud: '#777', // dithers on B2, but that's ok bgCloud: "#777", // dithers on B2, but that's ok
rain: '#0FF', rain: "#0FF",
lightning: '#FF0', lightning: "#FF0",
snow: '#FFF', snow: "#FFF",
mist: '#FFF' mist: "#FFF",
}; };
} else { } else {
palette = { palette = {
sun: '#FF0', sun: "#FF0",
cloud: '#777', // dithers on B2, but that's ok cloud: "#777", // dithers on B2, but that's ok
bgCloud: '#000', bgCloud: "#000",
rain: '#00F', rain: "#00F",
lightning: '#FF0', lightning: "#FF0",
snow: '#0FF', snow: "#0FF",
mist: '#0FF' mist: "#0FF",
}; };
} }
} else { } else {
if (ovr.theme.dark) { if (ovr.theme.dark) {
palette = { palette = {
sun: '#FE0', sun: "#FE0",
cloud: '#BBB', cloud: "#BBB",
bgCloud: '#777', bgCloud: "#777",
rain: '#0CF', rain: "#0CF",
lightning: '#FE0', lightning: "#FE0",
snow: '#FFF', snow: "#FFF",
mist: '#FFF' mist: "#FFF",
}; };
} else { } else {
palette = { palette = {
sun: '#FC0', sun: "#FC0",
cloud: '#000', cloud: "#000",
bgCloud: '#777', bgCloud: "#777",
rain: '#07F', rain: "#07F",
lightning: '#FC0', lightning: "#FC0",
snow: '#CCC', snow: "#CCC",
mist: '#CCC' mist: "#CCC",
}; };
} }
} }
@ -117,10 +115,10 @@ function getPalette(monochrome, ovr) {
return palette; return palette;
} }
exports.getColor = function(code) { exports.getColor = (code) => {
const codeGroup = Math.round(code / 100); const codeGroup = Math.round(code / 100);
const palette = getPalette(0, g); const palette = getPalette(0, g);
const cloud = g.blendColor(palette.cloud, palette.bgCloud, .5); //theme independent const cloud = g.blendColor(palette.cloud, palette.bgCloud, 0.5); //theme independent
switch (codeGroup) { switch (codeGroup) {
case 2: return g.blendColor(cloud, palette.lightning, .5); case 2: return g.blendColor(cloud, palette.lightning, .5);
case 3: return palette.rain; case 3: return palette.rain;
@ -144,7 +142,7 @@ exports.getColor = function(code) {
} }
default: return cloud; default: return cloud;
} }
} };
/** /**
* *
@ -158,9 +156,9 @@ exports.getColor = function(code) {
* @param ovr Graphics instance (or undefined for g) * @param ovr Graphics instance (or undefined for g)
* @param monochrome If true, produce a monochromatic icon * @param monochrome If true, produce a monochromatic icon
*/ */
exports.drawIcon = function(cond, x, y, r, ovr, monochrome) { exports.drawIcon = (cond, x, y, r, ovr, monochrome) => {
var palette; var palette;
if(!ovr) ovr = g; if (!ovr) ovr = g;
palette = getPalette(monochrome, ovr); palette = getPalette(monochrome, ovr);
@ -257,7 +255,7 @@ exports.drawIcon = function(cond, x, y, r, ovr, monochrome) {
function drawSnow(x, y, r) { function drawSnow(x, y, r) {
function rotatePoints(points, pivotX, pivotY, angle) { function rotatePoints(points, pivotX, pivotY, angle) {
for(let i = 0; i<points.length; i += 2) { for (let i = 0; i < points.length; i += 2) {
const x = points[i]; const x = points[i];
const y = points[i+1]; const y = points[i+1];
points[i] = Math.cos(angle)*(x-pivotX)-Math.sin(angle)*(y-pivotY)+ points[i] = Math.cos(angle)*(x-pivotX)-Math.sin(angle)*(y-pivotY)+
@ -269,7 +267,7 @@ exports.drawIcon = function(cond, x, y, r, ovr, monochrome) {
ovr.setColor(palette.snow); ovr.setColor(palette.snow);
const w = 1/12*r; const w = 1/12*r;
for(let i = 0; i<=6; ++i) { for (let i = 0; i <= 6; ++i) {
const points = [ const points = [
x+w, y, x+w, y,
x-w, y, x-w, y,
@ -279,7 +277,7 @@ exports.drawIcon = function(cond, x, y, r, ovr, monochrome) {
rotatePoints(points, x, y, i/3*Math.PI); rotatePoints(points, x, y, i/3*Math.PI);
ovr.fillPoly(points); ovr.fillPoly(points);
for(let j = -1; j<=1; j += 2) { for (let j = -1; j <= 1; j += 2) {
const points = [ const points = [
x+w, y+7/12*r, x+w, y+7/12*r,
x-w, y+7/12*r, x-w, y+7/12*r,
@ -317,8 +315,8 @@ exports.drawIcon = function(cond, x, y, r, ovr, monochrome) {
} }
/* /*
* Choose weather icon to display based on weather description * Choose weather icon to display based on weather description
*/ */
function chooseIconByTxt(txt) { function chooseIconByTxt(txt) {
if (!txt) return () => {}; if (!txt) return () => {};
txt = txt.toLowerCase(); txt = txt.toLowerCase();
@ -336,7 +334,8 @@ exports.drawIcon = function(cond, x, y, r, ovr, monochrome) {
if (txt.includes("few clouds")) return drawFewClouds; if (txt.includes("few clouds")) return drawFewClouds;
if (txt.includes("scattered clouds")) return drawCloud; if (txt.includes("scattered clouds")) return drawCloud;
if (txt.includes("clouds")) return drawBrokenClouds; if (txt.includes("clouds")) return drawBrokenClouds;
if (txt.includes("mist") || if (
txt.includes("mist") ||
txt.includes("smoke") || txt.includes("smoke") ||
txt.includes("haze") || txt.includes("haze") ||
txt.includes("sand") || txt.includes("sand") ||
@ -344,16 +343,17 @@ exports.drawIcon = function(cond, x, y, r, ovr, monochrome) {
txt.includes("fog") || txt.includes("fog") ||
txt.includes("ash") || txt.includes("ash") ||
txt.includes("squalls") || txt.includes("squalls") ||
txt.includes("tornado")) { txt.includes("tornado")
) {
return drawMist; return drawMist;
} }
return drawUnknown; return drawUnknown;
} }
/* /*
* Choose weather icon to display based on weather conditition code * Choose weather icon to display based on weather condition code
* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2 * https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
*/ */
function chooseIconByCode(code) { function chooseIconByCode(code) {
const codeGroup = Math.round(code / 100); const codeGroup = Math.round(code / 100);
switch (codeGroup) { switch (codeGroup) {
@ -382,16 +382,15 @@ exports.drawIcon = function(cond, x, y, r, ovr, monochrome) {
} }
function chooseIcon(cond) { function chooseIcon(cond) {
if (typeof (cond)==="object") { if (typeof cond === "object") {
if ("code" in cond) return chooseIconByCode(cond.code); if ("code" in cond) return chooseIconByCode(cond.code);
if ("txt" in cond) return chooseIconByTxt(cond.txt); if ("txt" in cond) return chooseIconByTxt(cond.txt);
} else if (typeof (cond)==="number") { } else if (typeof cond === "number") {
return chooseIconByCode(cond.code); return chooseIconByCode(cond.code);
} else if (typeof (cond)==="string") { } else if (typeof cond === "string") {
return chooseIconByTxt(cond.txt); return chooseIconByTxt(cond.txt);
} }
return drawUnknown; return drawUnknown;
} }
chooseIcon(cond)(x, y, r); chooseIcon(cond)(x, y, r);
}; };

View File

@ -1,31 +1,31 @@
(function(back) { (back) => {
const storage = require('Storage'); const storage = require("Storage");
let settings = storage.readJSON('weather.json', 1) || {}; let settings = storage.readJSON("weather.json", 1) || {};
function save(key, value) { function save(key, value) {
settings[key] = value; settings[key] = value;
storage.write('weather.json', settings); storage.write("weather.json", settings);
} }
E.showMenu({ E.showMenu({
'': { 'title': 'Weather' }, "": { "title": "Weather" },
'Expiry': { "Expiry": {
value: "expiry" in settings ? settings["expiry"] : 2*3600000, value: "expiry" in settings ? settings.expiry : 2 * 3600000,
min: 0, min: 0,
max : 24*3600000, max: 24 * 3600000,
step: 15*60000, step: 15 * 60000,
format: x => { format: (x) => {
if (x == 0) return "none"; if (x === 0) return "none";
if (x < 3600000) return Math.floor(x/60000) + "m"; if (x < 3600000) return `${Math.floor(x / 60000)}m`;
if (x < 86400000) return Math.floor(x/36000)/100 + "h"; if (x < 86400000) return `${Math.floor(x / 36000) / 100}h`;
}, },
onchange: x => save('expiry', x), onchange: (x) => save("expiry", x),
}, },
'Hide Widget': { "Hide Widget": {
value: "hide" in settings ? settings.hide : false, value: "hide" in settings ? settings.hide : false,
onchange: () => { onchange: () => {
settings.hide = !settings.hide settings.hide = !settings.hide;
save('hide', settings.hide); save("hide", settings.hide);
}, },
}, },
'< Back': back, "< Back": back,
}); });
}) };

View File

@ -1,53 +1,53 @@
(() => { (() => {
const weather = require('weather'); const weather = require("weather");
var dirty = false; var dirty = false;
let settings; let settings;
function loadSettings() { function loadSettings() {
settings = require('Storage').readJSON('weather.json', 1) || {}; settings = require("Storage").readJSON("weather.json", 1) || {};
} }
function setting(key) { function setting(key) {
if (!settings) { loadSettings(); } if (!settings) { loadSettings(); }
const DEFAULTS = { const DEFAULTS = {
'expiry': 2*3600000, "expiry": 2*3600000,
'hide': false "hide": false
}; };
return (key in settings) ? settings[key] : DEFAULTS[key]; return (key in settings) ? settings[key] : DEFAULTS[key];
} }
weather.on("update", w => { weather.on("update", w => {
if (setting('hide')) return; if (setting("hide")) return;
if (w) { if (w) {
if (!WIDGETS["weather"].width) { if (!WIDGETS.weather.width) {
WIDGETS["weather"].width = 20; WIDGETS.weather.width = 20;
Bangle.drawWidgets(); Bangle.drawWidgets();
} else if (Bangle.isLCDOn()) { } else if (Bangle.isLCDOn()) {
WIDGETS["weather"].draw(); WIDGETS.weather.draw();
} else { } else {
dirty = true; dirty = true;
} }
} }
else { else {
WIDGETS["weather"].width = 0; WIDGETS.weather.width = 0;
Bangle.drawWidgets(); Bangle.drawWidgets();
} }
}); });
Bangle.on('lcdPower', on => { Bangle.on("lcdPower", on => {
if (on && dirty && !setting('hide')) { if (on && dirty && !setting("hide")) {
WIDGETS["weather"].draw(); WIDGETS.weather.draw();
dirty = false; dirty = false;
} }
}); });
WIDGETS["weather"] = { WIDGETS.weather = {
area: "tl", area: "tl",
width: weather.get() && !setting('hide') ? 20 : 0, width: weather.get() && !setting("hide") ? 20 : 0,
draw: function() { draw: function() {
if (setting('hide')) return; if (setting("hide")) return;
const w = weather.get(); const w = weather.get();
if (!w) return; if (!w) return;
g.reset(); g.reset();
@ -56,17 +56,17 @@
weather.drawIcon(w, this.x+10, this.y+8, 7.5); weather.drawIcon(w, this.x+10, this.y+8, 7.5);
} }
if (w.temp) { if (w.temp) {
let t = require('locale').temp(w.temp-273.15); // applies conversion let t = require("locale").temp(w.temp-273.15); // applies conversion
t = t.match(/[\d\-]*/)[0]; // but we have no room for units t = t.match(/[\d\-]*/)[0]; // but we have no room for units
g.reset(); g.reset();
g.setFontAlign(0, 1); // center horizontally at bottom of widget g.setFontAlign(0, 1); // center horizontally at bottom of widget
g.setFont('6x8', 1); g.setFont("6x8", 1);
g.drawString(t, this.x+10, this.y+24); g.drawString(t, this.x+10, this.y+24);
} }
}, },
reload:function() { reload:() => {
loadSettings(); loadSettings();
WIDGETS["weather"].redraw(); WIDGETS.weather.redraw();
}, },
}; };
})(); })();