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.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.04: Layout now compatible with Bangle.js 2
0.05: Use weather condition code for icon selection
0.04: Layout now compatible with Bangle.js 2.
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
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.
## 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.

View File

@ -1,22 +1,36 @@
const Layout = require("Layout");
const storage = require('Storage');
const storage = require("Storage");
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
var sunIcon = require("heatshrink").decompress(atob("mEwwhC/AH4AbhvQC6vd7ouVC4IwUCwIwUFwQwQCYgAHDZQXc9wACC6QWDDAgXN7wXF9oXPCwowDC5guGGAYXMCw4wCC5RGJJAZGTJBiNISIylQVJrLCC5owGF65fXR7AwBC5jvhC7JIILxapDFxAXOGAy9KC4owGBAQXODAgHDC54AHC8T0FAAQSOGg4qPGA4WUGAIuVC7AA/AH4AEA="));
var partSunIcon = require("heatshrink").decompress(atob("mEwwhC/AH4AY6AWVhvdC6vd7owUFwIABFiYAFGR4Xa93u9oXTCwIYDC6HeC4fuC56MBC4ySOIwpIQXYQXHmYABRpwXECwQYKF5HjC4kwL5gQCAYYwO7wqFAAowK7wWKJBgXLJBPd6YX/AAoVMAAM/Cw0DC5yRHCx5JGFyAwGCyIwFC/4XyR4inXa64wRFwowQCw4A/AH4AkA"));
var cloudIcon = require("heatshrink").decompress(atob("mEwwhC/AH4A/AH4AtgczmYWWDCgWDmcwIKAuEGBoSGGCAWKC7BIKIxYX6CpgABn4tUSJIWPJIwuQGAwWRGAoX/C+SPEU67XXGCIuFGCAWHAH4A/AH4A/ADg="));
var snowIcon = require("heatshrink").decompress(atob("mEwwhC/AH4AhxGAC9YUBC4QZRhAVBAIWIC6QAEI6IYEI5cIBgwWOC64NCKohHPNox3RBgqnQEo7XPHpKONR5AXYAH4ASLa4XWXILiBC6r5LDBgWWDBRrKC5hsCEacIHawvMCIwvQC5QvQFAROEfZ5ADLJ4YGCywvVI7CPGC9IA/AH4AF"));
var rainIcon = require("heatshrink").decompress(atob("mEwwhC/AH4AFgczmYWWDCgWDmcwIKAuEGBoSGGCAWKC7BIKIxYX6CpgABn4tUSJIWPJIwuQGAwWRGAoX/C+SPEU67XXGCIuFGCAWHAGeIBJEIwAVJhGIC5AJBC5QMJEJQMEC44JBC6QSCC54FHLxgNBBgYSEDgKpPMhQXneSwuUAH4A/AA4="));
var stormIcon = require("heatshrink").decompress(atob("mEwwhC/AFEzmcwCyoYUgYXDmYuVGAY0OFwocHC6pNLCxYXYJBQXuCxhhJRpgYKCyBKFFyIXFCyJIFC/4XaO66nU3eza6k7C4IWFGBwXBCwwwO3ewC5AZMC6RaCIxZiI3e7AYYwRCQIIBC4QwPIQIpDC5owDhYREIxgAEFIouNC4orDFyBGBGAcLC6BaFhYWRLSRIFISQXcCyqhRAH4Az"));
// Weather icons from https://icons8.com/icon/set/weather/color
function getSun() {
return require("heatshrink").decompress(atob("mEwwhC/AH4AbhvQC6vd7ouVC4IwUCwIwUFwQwQCYgAHDZQXc9wACC6QWDDAgXN7wXF9oXPCwowDC5guGGAYXMCw4wCC5RGJJAZGTJBiNISIylQVJrLCC5owGF65fXR7AwBC5jvhC7JIILxapDFxAXOGAy9KC4owGBAQXODAgHDC54AHC8T0FAAQSOGg4qPGA4WUGAIuVC7AA/AH4AEA="));
}
function getPartSun() {
return require("heatshrink").decompress(atob("mEwwhC/AH4AY6AWVhvdC6vd7owUFwIABFiYAFGR4Xa93u9oXTCwIYDC6HeC4fuC56MBC4ySOIwpIQXYQXHmYABRpwXECwQYKF5HjC4kwL5gQCAYYwO7wqFAAowK7wWKJBgXLJBPd6YX/AAoVMAAM/Cw0DC5yRHCx5JGFyAwGCyIwFC/4XyR4inXa64wRFwowQCw4A/AH4AkA"));
}
function getCloud() {
return require("heatshrink").decompress(atob("mEwwhC/AH4A/AH4AtgczmYWWDCgWDmcwIKAuEGBoSGGCAWKC7BIKIxYX6CpgABn4tUSJIWPJIwuQGAwWRGAoX/C+SPEU67XXGCIuFGCAWHAH4A/AH4A/ADg="));
}
function getSnow() {
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
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.
@ -25,32 +39,30 @@ sent from gadget bridge.
*/
function chooseIcon(condition) {
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")||
condition.includes("sleet")) {
return snowIcon;
return getSnow;
}
if (condition.includes("drizzle")||
condition.includes("shower")) {
return rainIcon;
condition.includes("shower")||
condition.includes("rain")) return getRain;
if (condition.includes("clear")) return getSun;
if (condition.includes("clouds")) return getCloud;
if (condition.includes("few clouds")||
condition.includes("scattered clouds")||
condition.includes("mist")||
condition.includes("smoke")||
condition.includes("haze")||
condition.includes("sand")||
condition.includes("dust")||
condition.includes("fog")||
condition.includes("ash")) {
return getPartSun;
}
if (condition.includes("rain")) return rainIcon;
if (condition.includes("clear")) return sunIcon;
if (condition.includes("few clouds")) return partSunIcon;
if (condition.includes("scattered clouds")) return cloudIcon;
if (condition.includes("clouds")) return cloudIcon;
if (condition.includes("mist") ||
condition.includes("smoke") ||
condition.includes("haze") ||
condition.includes("sand") ||
condition.includes("dust") ||
condition.includes("fog") ||
condition.includes("ash") ||
condition.includes("squalls") ||
condition.includes("tornado")) {
return cloudIcon;
}
return cloudIcon;
return getCloud;
}
/*
@ -60,55 +72,29 @@ function chooseIcon(condition) {
function chooseIconByCode(code) {
const codeGroup = Math.round(code / 100);
switch (codeGroup) {
case 2: return stormIcon;
case 3: return rainIcon;
case 5: return rainIcon;
case 6: return snowIcon;
case 7: return cloudIcon;
case 2: return getStorm;
case 3: return getRain;
case 5:
switch (code) {
case 511: return getSnow;
default: return getRain;
}
case 6: return getSnow;
case 7: return getPartSun;
case 8:
switch (code) {
case 800: return sunIcon;
case 801: return partSunIcon;
default: return cloudIcon;
case 800: return getSun;
case 804: return getCloud;
default: return getPartSun;
}
default: return cloudIcon;
default: return getCloud;
}
}
/**
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
// Timeout used to update every minute
var drawTimeout;
// schedule a draw for the next minute
// Schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
@ -119,37 +105,85 @@ function queueDraw() {
function draw() {
var date = new Date();
clockLayout.time.label = locale.time(date, 1);
clockLayout.date.label = locale.date(date, 1).toUpperCase();
clockLayout.dow.label = locale.dow(date, 1).toUpperCase() + " ";
var weatherJson = getWeather();
if(weatherJson && weatherJson.weather){
var currentWeather = weatherJson.weather;
const temp = locale.temp(currentWeather.temp-273.15).match(/^(\D*\d*)(.*)$/);
clockLayout.temp.label = temp[1] + " " + temp[2];
const code = currentWeather.code || -1;
cLayout.time.label = locale.time(date, 1);
cLayout.dow.label = s.day ? locale.dow(date, 1).toUpperCase() + " " : "";
cLayout.date.label = s.date ? locale.date(date, 1).toUpperCase() : "";
let curr = w.get(); // Get weather from weather app.
if(curr){
const temp = locale.temp(curr.temp-273.15).match(/^(\D*\d*)(.*)$/);
cLayout.temp.label = temp[1] + " " + temp[2];
const code = curr.code || -1;
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 {
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*)(.*)$/);
clockLayout.wind.label = wind[1] + " " + wind[2] + " " + (currentWeather.wrose||'').toUpperCase();
const wind = locale.speed(curr.wind).match(/^(\D*\d*)(.*)$/);
cLayout.wind.label = wind[1] + " " + wind[2] + " " + (curr.wrose||"").toUpperCase();
}
else{
clockLayout.temp.label = "Err";
clockLayout.wind.label = "No Data";
clockLayout.weatherIcon.src = errIcon;
cLayout.temp.label = "Err";
cLayout.wind.label = "No Data";
cLayout.wIcon.src = s.icon ? getErr : getDummy;
}
clockLayout.clear();
clockLayout.render();
// queue draw in one minute
cLayout.clear();
cLayout.render();
// Queue draw in one minute
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();
Bangle.setUI("clock"); // Show launcher when middle button pressed
Bangle.loadWidgets();
Bangle.drawWidgets();
clockLayout.render();
cLayout.render();
draw();

View File

@ -1,9 +1,11 @@
{
"id": "weatherClock",
"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).",
"icon": "app.png",
"dependencies": {"weather":"app"},
"screenshots": [{"url":"screens/screen1.png"}],
"type": "clock",
"tags": "clock, weather",
@ -12,6 +14,8 @@
"readme": "README.md",
"storage": [
{"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();
},
}
});
});