diff --git a/apps/weatherClock/ChangeLog b/apps/weatherClock/ChangeLog index a6a12c297..f31e15729 100644 --- a/apps/weatherClock/ChangeLog +++ b/apps/weatherClock/ChangeLog @@ -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. \ No newline at end of file diff --git a/apps/weatherClock/README.md b/apps/weatherClock/README.md index f1f146440..a7f44f7f7 100644 --- a/apps/weatherClock/README.md +++ b/apps/weatherClock/README.md @@ -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. diff --git a/apps/weatherClock/app.js b/apps/weatherClock/app.js index 91d0ab36f..4896a9f49 100644 --- a/apps/weatherClock/app.js +++ b/apps/weatherClock/app.js @@ -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(); diff --git a/apps/weatherClock/metadata.json b/apps/weatherClock/metadata.json index cf8bd899e..270591c74 100644 --- a/apps/weatherClock/metadata.json +++ b/apps/weatherClock/metadata.json @@ -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"}] } diff --git a/apps/weatherClock/settings.js b/apps/weatherClock/settings.js new file mode 100644 index 000000000..0aa7330c1 --- /dev/null +++ b/apps/weatherClock/settings.js @@ -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(); + }, + } + }); +});