Merge branch 'lunctis-viribus-master'

master
Gordon Williams 2023-03-01 08:44:59 +00:00
commit a030f607cc
5 changed files with 203 additions and 104 deletions

View File

@ -1,5 +1,6 @@
0.01: New App! 0.01: New App!
0.02: Minor layout format tweak so it uses less memory and draws ok on Bangle.js 1 (#1012) 0.02: Minor layout format tweak so it uses less memory and draws ok on Bangle.js 1 (#1012)
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: WeatherClock icons now reflect weather conditions better. Add settings menu to hide elements and to use weather icons of Weather app. Images placed into functions for performance.

View File

@ -1,12 +1,14 @@
# Weather Clock # Weather Clock
A clock which displays the current weather conditions. Temperature, wind speed, and an icon indicating the weather conditions are displayed. A clock which displays the current weather conditions. Time, day of week, date, temperature, wind speed, and an icon indicating the weather condition are displayed.
As of Weather Clock v0.06 the date, day of week, temperature, weather icon and/or wind speed can be hidden in Settings. The icons can be changed to those of the Weather app.
Standard widgets are displayed. Standard widgets are displayed.
## Requirements ## Requirements
**This clock requires Gadgetbridge and the weather app in order to get weather data!** **This clock requires Gadgetbridge and the Weather app in order to get weather data!**
See the [Bangle.js Gadgetbridge documentation](https://www.espruino.com/Gadgetbridge) for instructions on setting up Gadgetbridge and weather. See the [Bangle.js Gadgetbridge documentation](https://www.espruino.com/Gadgetbridge) for instructions on setting up Gadgetbridge and weather.

View File

@ -1,22 +1,36 @@
const Layout = require("Layout"); 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";
let s;
const w = require("weather");
// weather icons from https://icons8.com/icon/set/weather/color // Weather icons from https://icons8.com/icon/set/weather/color
var sunIcon = require("heatshrink").decompress(atob("mEwwhC/AH4AbhvQC6vd7ouVC4IwUCwIwUFwQwQCYgAHDZQXc9wACC6QWDDAgXN7wXF9oXPCwowDC5guGGAYXMCw4wCC5RGJJAZGTJBiNISIylQVJrLCC5owGF65fXR7AwBC5jvhC7JIILxapDFxAXOGAy9KC4owGBAQXODAgHDC54AHC8T0FAAQSOGg4qPGA4WUGAIuVC7AA/AH4AEA=")); function getSun() {
return require("heatshrink").decompress(atob("mEwwhC/AH4AbhvQC6vd7ouVC4IwUCwIwUFwQwQCYgAHDZQXc9wACC6QWDDAgXN7wXF9oXPCwowDC5guGGAYXMCw4wCC5RGJJAZGTJBiNISIylQVJrLCC5owGF65fXR7AwBC5jvhC7JIILxapDFxAXOGAy9KC4owGBAQXODAgHDC54AHC8T0FAAQSOGg4qPGA4WUGAIuVC7AA/AH4AEA="));
var partSunIcon = require("heatshrink").decompress(atob("mEwwhC/AH4AY6AWVhvdC6vd7owUFwIABFiYAFGR4Xa93u9oXTCwIYDC6HeC4fuC56MBC4ySOIwpIQXYQXHmYABRpwXECwQYKF5HjC4kwL5gQCAYYwO7wqFAAowK7wWKJBgXLJBPd6YX/AAoVMAAM/Cw0DC5yRHCx5JGFyAwGCyIwFC/4XyR4inXa64wRFwowQCw4A/AH4AkA")); }
function getPartSun() {
var cloudIcon = require("heatshrink").decompress(atob("mEwwhC/AH4A/AH4AtgczmYWWDCgWDmcwIKAuEGBoSGGCAWKC7BIKIxYX6CpgABn4tUSJIWPJIwuQGAwWRGAoX/C+SPEU67XXGCIuFGCAWHAH4A/AH4A/ADg=")); return require("heatshrink").decompress(atob("mEwwhC/AH4AY6AWVhvdC6vd7owUFwIABFiYAFGR4Xa93u9oXTCwIYDC6HeC4fuC56MBC4ySOIwpIQXYQXHmYABRpwXECwQYKF5HjC4kwL5gQCAYYwO7wqFAAowK7wWKJBgXLJBPd6YX/AAoVMAAM/Cw0DC5yRHCx5JGFyAwGCyIwFC/4XyR4inXa64wRFwowQCw4A/AH4AkA"));
}
var snowIcon = require("heatshrink").decompress(atob("mEwwhC/AH4AhxGAC9YUBC4QZRhAVBAIWIC6QAEI6IYEI5cIBgwWOC64NCKohHPNox3RBgqnQEo7XPHpKONR5AXYAH4ASLa4XWXILiBC6r5LDBgWWDBRrKC5hsCEacIHawvMCIwvQC5QvQFAROEfZ5ADLJ4YGCywvVI7CPGC9IA/AH4AF")); function getCloud() {
return require("heatshrink").decompress(atob("mEwwhC/AH4A/AH4AtgczmYWWDCgWDmcwIKAuEGBoSGGCAWKC7BIKIxYX6CpgABn4tUSJIWPJIwuQGAwWRGAoX/C+SPEU67XXGCIuFGCAWHAH4A/AH4A/ADg="));
var rainIcon = require("heatshrink").decompress(atob("mEwwhC/AH4AFgczmYWWDCgWDmcwIKAuEGBoSGGCAWKC7BIKIxYX6CpgABn4tUSJIWPJIwuQGAwWRGAoX/C+SPEU67XXGCIuFGCAWHAGeIBJEIwAVJhGIC5AJBC5QMJEJQMEC44JBC6QSCC54FHLxgNBBgYSEDgKpPMhQXneSwuUAH4A/AA4=")); }
function getSnow() {
var stormIcon = require("heatshrink").decompress(atob("mEwwhC/AFEzmcwCyoYUgYXDmYuVGAY0OFwocHC6pNLCxYXYJBQXuCxhhJRpgYKCyBKFFyIXFCyJIFC/4XaO66nU3eza6k7C4IWFGBwXBCwwwO3ewC5AZMC6RaCIxZiI3e7AYYwRCQIIBC4QwPIQIpDC5owDhYREIxgAEFIouNC4orDFyBGBGAcLC6BaFhYWRLSRIFISQXcCyqhRAH4Az")); return require("heatshrink").decompress(atob("mEwwhC/AH4AhxGAC9YUBC4QZRhAVBAIWIC6QAEI6IYEI5cIBgwWOC64NCKohHPNox3RBgqnQEo7XPHpKONR5AXYAH4ASLa4XWXILiBC6r5LDBgWWDBRrKC5hsCEacIHawvMCIwvQC5QvQFAROEfZ5ADLJ4YGCywvVI7CPGC9IA/AH4AF"));
}
function getRain() {
return require("heatshrink").decompress(atob("mEwwhC/AH4AFgczmYWWDCgWDmcwIKAuEGBoSGGCAWKC7BIKIxYX6CpgABn4tUSJIWPJIwuQGAwWRGAoX/C+SPEU67XXGCIuFGCAWHAGeIBJEIwAVJhGIC5AJBC5QMJEJQMEC44JBC6QSCC54FHLxgNBBgYSEDgKpPMhQXneSwuUAH4A/AA4="));
}
function getStorm() {
return require("heatshrink").decompress(atob("mEwwhC/AFEzmcwCyoYUgYXDmYuVGAY0OFwocHC6pNLCxYXYJBQXuCxhhJRpgYKCyBKFFyIXFCyJIFC/4XaO66nU3eza6k7C4IWFGBwXBCwwwO3ewC5AZMC6RaCIxZiI3e7AYYwRCQIIBC4QwPIQIpDC5owDhYREIxgAEFIouNC4orDFyBGBGAcLC6BaFhYWRLSRIFISQXcCyqhRAH4Az"));
}
// err icon - https://icons8.com/icons/set/error // err icon - https://icons8.com/icons/set/error
var errIcon = require("heatshrink").decompress(atob("mEwwkBiIA/AH4AZUAIWUiAXBWqgXXdIYuVGCgXBgICCIyYXCJCQTDC6QrEMCQSEJCQRFC6ApGJCCiDDQSpQFAYXEJBqNGJCA/EC4ZIOEwgXFJBgNEAhKlNAgxIKBgoXEJBjsLC5TsIeRycMBhRrMMBKzQEozjOBxAgHGww+IA6wfSH4hnIC47OMSJqlRIJAXCACIXaGoQARPwwuTAH4A/ABw")); function getErr() {
return require("heatshrink").decompress(atob("mEwwkBiIA/AH4AZUAIWUiAXBWqgXXdIYuVGCgXBgICCIyYXCJCQTDC6QrEMCQSEJCQRFC6ApGJCCiDDQSpQFAYXEJBqNGJCA/EC4ZIOEwgXFJBgNEAhKlNAgxIKBgoXEJBjsLC5TsIeRycMBhRrMMBKzQEozjOBxAgHGww+IA6wfSH4hnIC47OMSJqlRIJAXCACIXaGoQARPwwuTAH4A/ABw"));
}
function getDummy() {
return require("heatshrink").decompress(atob("gMBwMAwA"));
}
/** /**
Choose weather icon to display based on condition. Choose weather icon to display based on condition.
@ -25,32 +39,30 @@ sent from gadget bridge.
*/ */
function chooseIcon(condition) { function chooseIcon(condition) {
condition = condition.toLowerCase(); condition = condition.toLowerCase();
if (condition.includes("thunderstorm")) return stormIcon; 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 snowIcon; return getSnow;
} }
if (condition.includes("drizzle")|| if (condition.includes("drizzle")||
condition.includes("shower")) { condition.includes("shower")||
return rainIcon; condition.includes("rain")) return getRain;
} if (condition.includes("clear")) return getSun;
if (condition.includes("rain")) return rainIcon; if (condition.includes("clouds")) return getCloud;
if (condition.includes("clear")) return sunIcon; if (condition.includes("few clouds")||
if (condition.includes("few clouds")) return partSunIcon; condition.includes("scattered clouds")||
if (condition.includes("scattered clouds")) return cloudIcon; condition.includes("mist")||
if (condition.includes("clouds")) return cloudIcon;
if (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 cloudIcon;
} }
return cloudIcon; return getCloud;
} }
/* /*
@ -60,55 +72,29 @@ function chooseIcon(condition) {
function chooseIconByCode(code) { function chooseIconByCode(code) {
const codeGroup = Math.round(code / 100); const codeGroup = Math.round(code / 100);
switch (codeGroup) { switch (codeGroup) {
case 2: return stormIcon; case 2: return getStorm;
case 3: return rainIcon; case 3: return getRain;
case 5: return rainIcon; case 5:
case 6: return snowIcon; switch (code) {
case 7: return cloudIcon; case 511: return getSnow;
default: return getRain;
}
case 6: return getSnow;
case 7: return getPartSun;
case 8: case 8:
switch (code) { switch (code) {
case 800: return sunIcon; case 800: return getSun;
case 801: return partSunIcon; case 804: return getCloud;
default: return cloudIcon; default: return getPartSun;
} }
default: return cloudIcon; default: return getCloud;
} }
} }
/** // Timeout used to update every minute
Get weather stored in json file by weather app.
*/
function getWeather() {
let jsonWeather = storage.readJSON('weather.json');
return jsonWeather;
}
var clockLayout = new Layout( {
type:"v", c: [
{type:"txt", font:"35%", halign: 0, fillx:1, pad: 8, label:"00:00", id:"time" },
{type: "h", fillx: 1, c: [
{type:"txt", font:"10%", label:"THU", id:"dow" },
{type:"txt", font:"10%", label:"01/01/1970", id:"date" }
]
},
{type: "h", valign : 1, fillx:1, c: [
{type: "img", filly: 1, id: "weatherIcon", src: sunIcon},
{type: "v", fillx:1, c: [
{type: "h", c: [
{type: "txt", font: "10%", id: "temp", label: "000 °C"},
]},
{type: "h", c: [
{type: "txt", font: "10%", id: "wind", label: "00 km/h"},
]}
]
},
]}]
});
// timeout used to update every minute
var drawTimeout; var drawTimeout;
// schedule a draw for the next minute // Schedule a draw for the next minute
function queueDraw() { function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() { drawTimeout = setTimeout(function() {
@ -119,37 +105,85 @@ 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 = locale.date(date, 1).toUpperCase(); cLayout.dow.label = s.day ? locale.dow(date, 1).toUpperCase() + " " : "";
clockLayout.dow.label = locale.dow(date, 1).toUpperCase() + " "; cLayout.date.label = s.date ? locale.date(date, 1).toUpperCase() : "";
var weatherJson = getWeather(); let curr = w.get(); // Get weather from weather app.
if(weatherJson && weatherJson.weather){ if(curr){
var currentWeather = weatherJson.weather; const temp = locale.temp(curr.temp-273.15).match(/^(\D*\d*)(.*)$/);
const temp = locale.temp(currentWeather.temp-273.15).match(/^(\D*\d*)(.*)$/); cLayout.temp.label = temp[1] + " " + temp[2];
clockLayout.temp.label = temp[1] + " " + temp[2]; const code = curr.code || -1;
const code = currentWeather.code || -1;
if (code > 0) { if (code > 0) {
clockLayout.weatherIcon.src = chooseIconByCode(code); let showIconC = s.src ? wDrawIcon(curr.code) : chooseIconByCode(curr.code);
cLayout.wIcon.src = s.icon ? showIconC : getDummy;
} else { } else {
clockLayout.weatherIcon.src = chooseIcon(currentWeather.txt); let showIconT = s.src ? wDrawIcon(curr.txt) : chooseIcon(curr.txt);
cLayout.wIcon.src = s.icon ? showIconT : getDummy;
} }
const wind = locale.speed(currentWeather.wind).match(/^(\D*\d*)(.*)$/); const wind = locale.speed(curr.wind).match(/^(\D*\d*)(.*)$/);
clockLayout.wind.label = wind[1] + " " + wind[2] + " " + (currentWeather.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 = errIcon; 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();
} }
// Load settings from file
s = storage.readJSON(SETTINGS_FILE,1)||{};
s.src = s.src === undefined ? false : s.src;
s.icon = s.icon === undefined ? true : s.icon;
s.day = s.day === undefined ? true : s.day;
s.date = s.date === undefined ? true : s.date;
s.wind = s.wind === undefined ? true : s.wind;
function wDrawIcon(code) {
var ovr = Graphics.createArrayBuffer(50,50,8,{msb:true});
if (typeof code == "number") w.drawIcon({code:code},24,24,24,ovr);
if (typeof code == "string") w.drawIcon({txt:code},24,24,24,ovr);
var img = ovr.asImage();
img.transparent = 0;
return img;
}
let srcIcons = s.src ? wDrawIcon(800) : getSun;
let srcWeather = s.icon ? srcIcons : getDummy;
let fontTemp = s.wind ? "10%" : "20%";
let fontWind = s.wind ? "10%" : "0%";
let labelDay = s.day ? "THU" : "";
let labelDate = s.date ? "01/01/1970" : "";
var cLayout = new Layout( {
type:"v", c: [
{type:"txt", font:"35%", halign: 0, fillx:1, pad: 8, label:"00:00", id:"time" },
{type: "h", fillx: 1, c: [
{type: "h", c: [
{type:"txt", font:"10%", label:labelDay, id:"dow" },
{type:"txt", font:"10%", label:labelDate, id:"date" }
]},
]
},
{type: "h", valign : 1, fillx:1, c: [
{type: "img", filly: 1, pad: 8, id: "wIcon", src: srcWeather},
{type: "v", fillx:1, c: [
{type: "h", c: [
{type: "txt", font: fontTemp, id: "temp", label: "000 °C"},
]},
{type: "h", c: [
{type: "txt", font: fontWind, id: "wind", label: "00 km/h"},
]}
]
},
]}]
});
g.clear(); 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,9 +1,11 @@
{ {
"id": "weatherClock", "id": "weatherClock",
"name": "Weather Clock", "name": "Weather Clock",
"version": "0.05", "shortName": "Weather Clock",
"version": "0.06",
"description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).", "description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).",
"icon": "app.png", "icon": "app.png",
"dependencies": {"weather":"app"},
"screenshots": [{"url":"screens/screen1.png"}], "screenshots": [{"url":"screens/screen1.png"}],
"type": "clock", "type": "clock",
"tags": "clock, weather", "tags": "clock, weather",
@ -12,6 +14,8 @@
"readme": "README.md", "readme": "README.md",
"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"}
],
"data": [{"name":"weatherClock.json"}]
} }

View File

@ -0,0 +1,58 @@
(function(back) {
const SETTINGS_FILE = "weatherClock.json";
// Load settings file
const storage = require('Storage');
let settings = storage.readJSON(SETTINGS_FILE, 1) || {};
let s = {};
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.src = (settings.src === undefined ? false : settings.src);
function save() {
settings = s
storage.write(SETTINGS_FILE, settings)
}
E.showMenu({
'': { 'title': 'Weather Clock' },
'< Back': back,
'Show date': {
value: !!s.date,
onchange: v => {
s.date = v;
save();
},
},
'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,
onchange: v => {
s.wind = v;
save();
},
},
'Use weather app icons': {
value: !!s.src,
onchange: v => {
s.src = v;
save();
},
}
});
});