1
0
Fork 0

weatherclock v0.06

master
lunctis-viribus 2023-02-27 21:38:14 +01:00
parent 97a6beab55
commit 5ca69aa0a4
6 changed files with 87 additions and 485 deletions

View File

@ -158,7 +158,7 @@ exports.getColor = function(code) {
*/ */
exports.drawIcon = function(cond, x, y, r, ovr) { exports.drawIcon = function(cond, x, y, r, ovr) {
var palette; var palette;
var monochrome=1; var monochrome = B2 ? 0 : 1;
if(!ovr) { if(!ovr) {
ovr = g; ovr = g;
monochrome = 0; monochrome = 0;

View File

@ -3,4 +3,4 @@
0.03: Minor layout extra spaces. 0.03: Minor layout extra spaces.
0.04: Layout now compatible with Bangle.js 2. 0.04: Layout now compatible with Bangle.js 2.
0.05: Use weather condition code for icon selection. 0.05: Use weather condition code for icon selection.
0.06: Dev13-New settings to optionally hide elements. Images placed into functions for performance. 0.06: Dev15-New settings to optionally hide elements. Images placed into functions for performance.

View File

@ -2,8 +2,8 @@ const Layout = require("Layout");
const storage = require('Storage'); const storage = require('Storage');
const locale = require("locale"); const locale = require("locale");
const SETTINGS_FILE = "weatherClock.json"; const SETTINGS_FILE = "weatherClock.json";
let settings; let s;
const weather = require('weatherClock'); const w = require('weather');
// weather icons from https://icons8.com/icon/set/weather/color // weather icons from https://icons8.com/icon/set/weather/color
function getSun() { function getSun() {
@ -39,30 +39,28 @@ sent from gadget bridge.
*/ */
function chooseIcon(condition) { function chooseIcon(condition) {
condition = condition.toLowerCase(); condition = condition.toLowerCase();
if (condition.includes("thunderstorm")) return getStorm; if (condition.includes("thunderstorm")||
condition.includes("squalls")||
condition.includes("tornado")) return getStorm;
if (condition.includes("freezing")||condition.includes("snow")|| if (condition.includes("freezing")||condition.includes("snow")||
condition.includes("sleet")) { condition.includes("sleet")) {
return getSnow; return getSnow;
} }
if (condition.includes("drizzle")|| if (condition.includes("drizzle")||
condition.includes("shower")) { condition.includes("shower")||
return getRain; condition.includes("rain")) return getRain;
}
if (condition.includes("rain")) return getRain;
if (condition.includes("clear")) return getSun; if (condition.includes("clear")) return getSun;
if (condition.includes("few clouds")) return getPartSun;
if (condition.includes("scattered clouds")) return getCloud;
if (condition.includes("clouds")) return getCloud; if (condition.includes("clouds")) return getCloud;
if (condition.includes("mist") || if (condition.includes("few clouds")||
condition.includes("scattered clouds")||
condition.includes("mist")||
condition.includes("smoke")|| condition.includes("smoke")||
condition.includes("haze")|| condition.includes("haze")||
condition.includes("sand")|| condition.includes("sand")||
condition.includes("dust")|| condition.includes("dust")||
condition.includes("fog")|| condition.includes("fog")||
condition.includes("ash") || condition.includes("ash")) {
condition.includes("squalls") || return getPartSun;
condition.includes("tornado")) {
return getCloud;
} }
return getCloud; return getCloud;
} }
@ -76,14 +74,18 @@ function chooseIconByCode(code) {
switch (codeGroup) { switch (codeGroup) {
case 2: return getStorm; case 2: return getStorm;
case 3: return getRain; case 3: return getRain;
case 5: return getRain; case 5:
switch (code) {
case 511: return getSnow;
default: return getRain;
}
case 6: return getSnow; case 6: return getSnow;
case 7: return getCloud; case 7: return getPartSun;
case 8: case 8:
switch (code) { switch (code) {
case 800: return getSun; case 800: return getSun;
case 801: return getPartSun; case 804: return getCloud;
default: return getCloud; default: return getPartSun;
} }
default: return getCloud; default: return getCloud;
} }
@ -111,62 +113,62 @@ function queueDraw() {
function draw() { function draw() {
var date = new Date(); var date = new Date();
clockLayout.time.label = locale.time(date, 1); cLayout.time.label = locale.time(date, 1);
clockLayout.date.label = settings.date ? locale.date(date, 1).toUpperCase() : ""; cLayout.dow.label = s.day ? locale.dow(date, 1).toUpperCase() + " " : "";
clockLayout.dow.label = settings.day ? locale.dow(date, 1).toUpperCase() + " " : ""; cLayout.date.label = s.date ? locale.date(date, 1).toUpperCase() : "";
let current = weather.get(); let curr = w.get(); // Get weather from weather app.
if(current){ if(curr){
const temp = locale.temp(current.temp-273.15).match(/^(\D*\d*)(.*)$/); const temp = locale.temp(curr.temp-273.15).match(/^(\D*\d*)(.*)$/);
clockLayout.temp.label = temp[1] + " " + temp[2]; cLayout.temp.label = temp[1] + " " + temp[2];
const code = current.code || -1; const code = curr.code || -1;
if (code > 0) { if (code > 0) {
let srcIconsCode = settings.src ? weatherIcon(current.code) : chooseIconByCode(current.code); let showIconC = s.src ? wDrawIcon(curr.code) : chooseIconByCode(curr.code);
clockLayout.weatherIcon.src = settings.icon ? srcIconsCode : getDummy; cLayout.wIcon.src = s.icon ? showIconC : getDummy;
} else { } else {
let srcIconsTxt = settings.src ? weatherIcon(current.txt) : chooseIcon(current.txt); let showIconT = s.src ? wDrawIcon(curr.txt) : chooseIcon(curr.txt);
clockLayout.weatherIcon.src = settings.icon ? srcIconsTxt : getDummy; cLayout.wIcon.src = s.icon ? showIconT : getDummy;
} }
const wind = locale.speed(current.wind).match(/^(\D*\d*)(.*)$/); const wind = locale.speed(curr.wind).match(/^(\D*\d*)(.*)$/);
clockLayout.wind.label = wind[1] + " " + wind[2] + " " + (current.wrose||'').toUpperCase(); cLayout.wind.label = wind[1] + " " + wind[2] + " " + (curr.wrose||'').toUpperCase();
} }
else{ else{
clockLayout.temp.label = "Err"; cLayout.temp.label = "Err";
clockLayout.wind.label = "No Data"; cLayout.wind.label = "No Data";
clockLayout.weatherIcon.src = settings.icon ? getErr : getDummy; cLayout.wIcon.src = s.icon ? getErr : getDummy;
} }
clockLayout.clear(); cLayout.clear();
clockLayout.render(); cLayout.render();
// queue draw in one minute // queue draw in one minute
queueDraw(); queueDraw();
} }
function loadSettings() { function loadSettings() {
settings = storage.readJSON(SETTINGS_FILE,1)||{}; s = storage.readJSON(SETTINGS_FILE,1)||{};
settings.src = settings.src === undefined ? false : settings.src; s.src = s.src === undefined ? false : s.src;
settings.icon = settings.icon === undefined ? true : settings.icon; s.icon = s.icon === undefined ? true : s.icon;
settings.day = settings.day === undefined ? true : settings.day; s.day = s.day === undefined ? true : s.day;
settings.date = settings.date === undefined ? true : settings.date; s.date = s.date === undefined ? true : s.date;
settings.wind = settings.wind === undefined ? true : settings.wind; s.wind = s.wind === undefined ? true : s.wind;
} }
loadSettings(); loadSettings();
function weatherIcon(code) { function wDrawIcon(code) {
var ovr = Graphics.createArrayBuffer(50,50,8,{msb:true}); var ovr = Graphics.createArrayBuffer(50,50,8,{msb:true});
if (typeof code == "number") weather.drawIcon({code:code},24,24,24,ovr); if (typeof code == "number") w.drawIcon({code:code},24,24,24,ovr);
if (typeof code == "string") weather.drawIcon({code},24,24,24,ovr); if (typeof code == "string") w.drawIcon({txt:code},24,24,24,ovr);
var img = ovr.asImage(); var img = ovr.asImage();
img.transparent = 0; img.transparent = 0;
return img; return img;
} }
let srcIcons = settings.src ? weatherIcon(800) : getSun; let srcIcons = s.src ? wDrawIcon(800) : getSun;
let srcWeather = settings.icon ? srcIcons : getDummy; let srcWeather = s.icon ? srcIcons : getDummy;
let fontTemp = settings.wind ? "10%" : "20%"; let fontTemp = s.wind ? "10%" : "20%";
let fontWind = settings.wind ? "10%" : "0%"; let fontWind = s.wind ? "10%" : "0%";
let labelDay = settings.day ? "THU" : ""; let labelDay = s.day ? "THU" : "";
let labelDate = settings.date ? "01/01/1970" : ""; let labelDate = s.date ? "01/01/1970" : "";
var clockLayout = new Layout( { var cLayout = new Layout( {
type:"v", c: [ type:"v", c: [
{type:"txt", font:"35%", halign: 0, fillx:1, pad: 8, label:"00:00", id:"time" }, {type:"txt", font:"35%", halign: 0, fillx:1, pad: 8, label:"00:00", id:"time" },
{type: "h", fillx: 1, c: [ {type: "h", fillx: 1, c: [
@ -177,7 +179,7 @@ var clockLayout = new Layout( {
] ]
}, },
{type: "h", valign : 1, fillx:1, c: [ {type: "h", valign : 1, fillx:1, c: [
{type: "img", filly: 1, pad: 8, id: "weatherIcon", src: srcWeather}, {type: "img", filly: 1, pad: 8, id: "wIcon", src: srcWeather},
{type: "v", fillx:1, c: [ {type: "v", fillx:1, c: [
{type: "h", c: [ {type: "h", c: [
{type: "txt", font: fontTemp, id: "temp", label: "000 °C"}, {type: "txt", font: fontTemp, id: "temp", label: "000 °C"},
@ -194,5 +196,5 @@ g.clear();
Bangle.setUI("clock"); // Show launcher when middle button pressed Bangle.setUI("clock"); // Show launcher when middle button pressed
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
clockLayout.render(); cLayout.render();
draw(); draw();

View File

@ -1,399 +0,0 @@
const storage = require('Storage');
const B2 = process.env.HWVERSION===2;
let expiryTimeout;
function scheduleExpiry(json) {
if (expiryTimeout) {
clearTimeout(expiryTimeout);
expiryTimeout = undefined;
}
let expiry = "expiry" in json ? json.expiry : 2*3600000;
if (json.weather && json.weather.time && expiry) {
let t = json.weather.time + expiry - Date.now();
expiryTimeout = setTimeout(update, t);
}
}
function update(weatherEvent) {
let json = storage.readJSON('weather.json')||{};
if (weatherEvent) {
let weather = weatherEvent.clone();
delete weather.t;
weather.time = Date.now();
if (weather.wdir != null) {
// Convert numeric direction into human-readable label
let deg = weather.wdir;
while (deg<0 || deg>360) {
deg = (deg+360)%360;
}
weather.wrose = ['n','ne','e','se','s','sw','w','nw','n'][Math.floor((deg+22.5)/45)];
}
json.weather = weather;
}
else {
delete json.weather;
}
storage.write('weather.json', json);
scheduleExpiry(json);
exports.emit("update", json.weather);
}
const _GB = global.GB;
global.GB = (event) => {
if (event.t==="weather") update(event);
if (_GB) setTimeout(_GB, 0, event);
};
exports.get = function() {
return (storage.readJSON('weather.json')||{}).weather;
}
scheduleExpiry(storage.readJSON('weather.json')||{});
function getPalette(monochrome, ovr) {
var palette;
if(monochrome) {
palette = {
sun: '#FFF',
cloud: '#FFF',
bgCloud: '#FFF',
rain: '#FFF',
lightning: '#FFF',
snow: '#FFF',
mist: '#FFF',
background: '#000'
};
} else {
if (B2) {
if (ovr.theme.dark) {
palette = {
sun: '#FF0',
cloud: '#FFF',
bgCloud: '#777', // dithers on B2, but that's ok
rain: '#0FF',
lightning: '#FF0',
snow: '#FFF',
mist: '#FFF'
};
} else {
palette = {
sun: '#FF0',
cloud: '#777', // dithers on B2, but that's ok
bgCloud: '#000',
rain: '#00F',
lightning: '#FF0',
snow: '#0FF',
mist: '#0FF'
};
}
} else {
if (ovr.theme.dark) {
palette = {
sun: '#FE0',
cloud: '#BBB',
bgCloud: '#777',
rain: '#0CF',
lightning: '#FE0',
snow: '#FFF',
mist: '#FFF'
};
} else {
palette = {
sun: '#FC0',
cloud: '#000',
bgCloud: '#777',
rain: '#07F',
lightning: '#FC0',
snow: '#CCC',
mist: '#CCC'
};
}
}
}
return palette;
}
exports.getColor = function(code) {
const codeGroup = Math.round(code / 100);
const palette = getPalette(0, g);
const cloud = g.blendColor(palette.cloud, palette.bgCloud, .5); //theme independent
switch (codeGroup) {
case 2: return g.blendColor(cloud, palette.lightning, .5);
case 3: return palette.rain;
case 5:
switch (code) {
case 511: return palette.snow;
case 520: return g.blendColor(palette.rain, palette.sun, .5);
case 521: return g.blendColor(palette.rain, palette.sun, .5);
case 522: return g.blendColor(palette.rain, palette.sun, .5);
case 531: return g.blendColor(palette.rain, palette.sun, .5);
default: return palette.rain;
}
case 6: return palette.snow;
case 7: return palette.mist;
case 8:
switch (code) {
case 800: return palette.sun;
case 801: return palette.sun;
case 802: return cloud;
default: return cloud;
}
default: return cloud;
}
}
/**
*
* @param cond Weather condition, as one of:
* {number} code: (Preferred form) https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
* {string} weather description (in English: breaks for other languages!)
* {object} use cond.code if present, or fall back to cond.txt
* @param x Left
* @param y Top
* @param r Icon Size
* @param ovr Graphics instance (or undefined for g)
*/
exports.drawIcon = function(cond, x, y, r, ovr) {
var palette;
var monochrome=0;
if(!ovr) {
ovr = g;
monochrome=0;
}
palette = getPalette(monochrome, ovr);
function drawSun(x, y, r) {
ovr.setColor(palette.sun);
ovr.fillCircle(x, y, r);
}
function drawCloud(x, y, r, c) {
const u = r/12;
if (c==null) c = palette.cloud;
ovr.setColor(c);
ovr.fillCircle(x-8*u, y+3*u, 4*u);
ovr.fillCircle(x-4*u, y-2*u, 5*u);
ovr.fillCircle(x+4*u, y+0*u, 4*u);
ovr.fillCircle(x+9*u, y+4*u, 3*u);
ovr.fillPoly([
x-8*u, y+7*u,
x-8*u, y+3*u,
x-4*u, y-2*u,
x+4*u, y+0*u,
x+9*u, y+4*u,
x+9*u, y+7*u,
]);
}
function drawBrokenClouds(x, y, r) {
drawCloud(x+1/8*r, y-1/8*r, 7/8*r, palette.bgCloud);
if(monochrome)
drawCloud(x-1/8*r, y+2/16*r, r, palette.background);
drawCloud(x-1/8*r, y+1/8*r, 7/8*r);
}
function drawFewClouds(x, y, r) {
drawSun(x+3/8*r, y-1/8*r, 5/8*r);
if(monochrome)
drawCloud(x-1/8*r, y+2/16*r, r, palette.background);
drawCloud(x-1/8*r, y+1/8*r, 7/8*r);
}
function drawRainLines(x, y, r) {
ovr.setColor(palette.rain);
const y1 = y+1/2*r;
const y2 = y+1*r;
const poly = ovr.fillPolyAA ? p => ovr.fillPolyAA(p) : p => ovr.fillPoly(p);
poly([
x-6/12*r, y1,
x-8/12*r, y2,
x-7/12*r, y2,
x-5/12*r, y1,
]);
poly([
x-2/12*r, y1,
x-4/12*r, y2,
x-3/12*r, y2,
x-1/12*r, y1,
]);
poly([
x+2/12*r, y1,
x+0/12*r, y2,
x+1/12*r, y2,
x+3/12*r, y1,
]);
}
function drawShowerRain(x, y, r) {
drawFewClouds(x, y-1/3*r, r);
drawRainLines(x, y, r);
}
function drawRain(x, y, r) {
drawBrokenClouds(x, y-1/3*r, r);
drawRainLines(x, y, r);
}
function drawThunderstorm(x, y, r) {
function drawLightning(x, y, r) {
ovr.setColor(palette.lightning);
ovr.fillPoly([
x-2/6*r, y-r,
x-4/6*r, y+1/6*r,
x-1/6*r, y+1/6*r,
x-3/6*r, y+1*r,
x+3/6*r, y-1/6*r,
x+0/6*r, y-1/6*r,
x+3/6*r, y-r,
]);
}
if(monochrome) drawBrokenClouds(x, y-1/3*r, r);
drawLightning(x-1/12*r, y+1/2*r, 1/2*r);
drawBrokenClouds(x, y-1/3*r, r);
}
function drawSnow(x, y, r) {
function rotatePoints(points, pivotX, pivotY, angle) {
for(let i = 0; i<points.length; i += 2) {
const x = points[i];
const y = points[i+1];
points[i] = Math.cos(angle)*(x-pivotX)-Math.sin(angle)*(y-pivotY)+
pivotX;
points[i+1] = Math.sin(angle)*(x-pivotX)+Math.cos(angle)*(y-pivotY)+
pivotY;
}
}
ovr.setColor(palette.snow);
const w = 1/12*r;
for(let i = 0; i<=6; ++i) {
const points = [
x+w, y,
x-w, y,
x-w, y+r,
x+w, y+r,
];
rotatePoints(points, x, y, i/3*Math.PI);
ovr.fillPoly(points);
for(let j = -1; j<=1; j += 2) {
const points = [
x+w, y+7/12*r,
x-w, y+7/12*r,
x-w, y+r,
x+w, y+r,
];
rotatePoints(points, x, y+7/12*r, j/3*Math.PI);
rotatePoints(points, x, y, i/3*Math.PI);
ovr.fillPoly(points);
}
}
}
function drawMist(x, y, r) {
const layers = [
[-0.4, 0.5],
[-0.8, 0.3],
[-0.2, 0.9],
[-0.9, 0.7],
[-0.2, 0.3],
];
ovr.setColor(palette.mist);
for(let i = 0; i<5; ++i) {
ovr.fillRect(x+layers[i][0]*r, y+(0.4*i-0.9)*r, x+layers[i][1]*r,
y+(0.4*i-0.7)*r-1);
ovr.fillCircle(x+layers[i][0]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5);
ovr.fillCircle(x+layers[i][1]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5);
}
}
function drawUnknown(x, y, r) {
drawCloud(x, y, r, palette.bgCloud);
ovr.setColor(ovr.theme.fg).setFontAlign(0, 0).setFont('Vector', r*2).drawString("?", x+r/10, y+r/6);
}
/*
* Choose weather icon to display based on weather description
*/
function chooseIconByTxt(txt) {
if (!txt) return () => {};
txt = txt.toLowerCase();
if (txt.includes("thunderstorm")) return drawThunderstorm;
if (txt.includes("freezing")||txt.includes("snow")||
txt.includes("sleet")) {
return drawSnow;
}
if (txt.includes("drizzle")||
txt.includes("shower")) {
return drawRain;
}
if (txt.includes("rain")) return drawShowerRain;
if (txt.includes("clear")) return drawSun;
if (txt.includes("few clouds")) return drawFewClouds;
if (txt.includes("scattered clouds")) return drawCloud;
if (txt.includes("clouds")) return drawBrokenClouds;
if (txt.includes("mist") ||
txt.includes("smoke") ||
txt.includes("haze") ||
txt.includes("sand") ||
txt.includes("dust") ||
txt.includes("fog") ||
txt.includes("ash") ||
txt.includes("squalls") ||
txt.includes("tornado")) {
return drawMist;
}
return drawUnknown;
}
/*
* Choose weather icon to display based on weather conditition code
* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
*/
function chooseIconByCode(code) {
const codeGroup = Math.round(code / 100);
switch (codeGroup) {
case 2: return drawThunderstorm;
case 3: return drawRain;
case 5:
switch (code) {
case 511: return drawSnow;
case 520: return drawShowerRain;
case 521: return drawShowerRain;
case 522: return drawShowerRain;
case 531: return drawShowerRain;
default: return drawRain;
}
case 6: return drawSnow;
case 7: return drawMist;
case 8:
switch (code) {
case 800: return drawSun;
case 801: return drawFewClouds;
case 802: return drawCloud;
default: return drawBrokenClouds;
}
default: return drawUnknown;
}
}
function chooseIcon(cond) {
if (typeof (cond)==="object") {
if ("code" in cond) return chooseIconByCode(cond.code);
if ("txt" in cond) return chooseIconByTxt(cond.txt);
} else if (typeof (cond)==="number") {
return chooseIconByCode(cond.code);
} else if (typeof (cond)==="string") {
return chooseIconByTxt(cond.txt);
}
return drawUnknown;
}
chooseIcon(cond)(x, y, r);
};

View File

@ -15,8 +15,7 @@
"storage": [ "storage": [
{"name":"weatherClock.app.js","url":"app.js"}, {"name":"weatherClock.app.js","url":"app.js"},
{"name":"weatherClock.img","url":"app-icon.js","evaluate":true}, {"name":"weatherClock.img","url":"app-icon.js","evaluate":true},
{"name":"weatherClock.settings.js","url":"settings.js"}, {"name":"weatherClock.settings.js","url":"settings.js"}
{"name":"weatherClock","url":"lib.js"}
], ],
"data": [{"name":"weatherClock.json"}] "data": [{"name":"weatherClock.json"}]
} }

View File

@ -5,11 +5,11 @@
const storage = require('Storage'); const storage = require('Storage');
let settings = storage.readJSON(SETTINGS_FILE, 1) || {}; let settings = storage.readJSON(SETTINGS_FILE, 1) || {};
let s = {}; let s = {};
s.src = (settings.src === undefined ? false : settings.src);
s.icon = (settings.icon === undefined ? true : settings.icon);
s.day = (settings.day === undefined ? true : settings.day);
s.date = (settings.date === undefined ? true : settings.date); s.date = (settings.date === undefined ? true : settings.date);
s.day = (settings.day === undefined ? true : settings.day);
s.icon = (settings.icon === undefined ? true : settings.icon);
s.wind = (settings.wind === undefined ? true : settings.wind); s.wind = (settings.wind === undefined ? true : settings.wind);
s.src = (settings.src === undefined ? false : settings.src);
function save() { function save() {
settings = s settings = s
@ -19,35 +19,35 @@
E.showMenu({ E.showMenu({
'': { 'title': 'Weather Clock' }, '': { 'title': 'Weather Clock' },
'< Back': back, '< Back': back,
'Weather Icon': { 'Show date': {
value: !!s.icon,
onchange: v => {
s.icon = v;
save();
},
},
'Day Of Week': {
value: !!s.day,
onchange: v => {
s.day = v;
save();
},
},
'Date': {
value: !!s.date, value: !!s.date,
onchange: v => { onchange: v => {
s.date = v; s.date = v;
save(); save();
}, },
}, },
'Wind Speed': { 'Show day Of Week': {
value: !!s.day,
onchange: v => {
s.day = v;
save();
},
},
'Show weather Icon': {
value: !!s.icon,
onchange: v => {
s.icon = v;
save();
},
},
'Show wind Speed': {
value: !!s.wind, value: !!s.wind,
onchange: v => { onchange: v => {
s.wind = v; s.wind = v;
save(); save();
}, },
}, },
'Icons from weather app': { 'Use weather app icons': {
value: !!s.src, value: !!s.src,
onchange: v => { onchange: v => {
s.src = v; s.src = v;