diff --git a/apps/weather/README.md b/apps/weather/README.md index f4b6615fc0..7b29f90ddc 100644 --- a/apps/weather/README.md +++ b/apps/weather/README.md @@ -12,6 +12,7 @@ You can view the full report through the app: Use the iOS shortcut [here](https://www.icloud.com/shortcuts/73be0ce1076446f3bdc45a5707de5c4d). The shortcut uses Apple Weather for weather updates, and sends a notification, which is read by Bangle.js. To push weather every hour, or interval, you will need to create a shortcut automation for every time you want to push the weather. + ## Android Setup 1. Install [Gadgetbridge for Android](https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/) on your phone. diff --git a/apps/weather/app.js b/apps/weather/app.js index 557d3cbc35..2736578be0 100644 --- a/apps/weather/app.js +++ b/apps/weather/app.js @@ -9,59 +9,67 @@ Bangle.loadWidgets(); var layout = new Layout({type:"v", bgCol: g.theme.bg, c: [ {filly: 1}, {type: "h", filly: 0, c: [ - {type: "v", width: g.getWidth()/2, c: [ // Vertical container for icon + {type: "v", width: g.getWidth()/2, c: [ // Vertical container for icon + UV {type: "custom", fillx: 1, height: g.getHeight()/2 - 30, valign: -1, txt: "unknown", id: "icon", - render: l => weather.drawIcon(l, l.x+l.w/2, l.y+l.h/2, l.w/2-5)}, - ]}, - {type: "v", fillx: 1, c: [ - {type: "h", pad: 2, c: [ - {type: "txt", font: "18%", id: "temp", label: "000"}, - {type: "txt", font: "12%", valign: -1, id: "tempUnit", label: "°C"}, - ]}, - {filly: 1}, - {type: "txt", font: "6x8", pad: 2, halign: 1, label: /*LANG*/"Humidity"}, - {type: "txt", font: "9%", pad: 2, halign: 1, id: "hum", label: "000%"}, - {type: "txt", font: "6x8", pad: [2, 2, 2, 2], halign: -1, label: /*LANG*/"Wind"}, - {type: "h", pad: [0, 2, 2, 2], halign: -1, c: [ - {type: "txt", font: "9%", pad: 2, id: "wind", label: "00"}, - {type: "txt", font: "6x8", pad: 2, valign: -1, id: "windUnit", label: "km/h"}, - ]}, - {type: "custom", fillx: 1, height: 15, id: "uvDisplay", + render: l => weather.drawIcon(l, l.x+l.w/2, l.y+l.h/2.1, l.w/2.1-10)}, + {type: "custom", fillx: 1, height: 20, id: "uvDisplay", render: l => { - if (!current || current.uv === undefined || current.uv === 0) return; + if (!current || current.uv === undefined) return; const uv = Math.min(parseInt(current.uv), 11); // Cap at 11 // UV color thresholds: [max_value, color] based on WHO standards const colors = [[2,"#0F0"], [5,"#FF0"], [7,"#F80"], [10,"#F00"], [11,"#F0F"]]; const color = colors.find(c => uv <= c[0])[1]; - const blockH = 8, blockW = 3; - // Draw UV title and blocks on same line + // Setup and measure label g.setFont("6x8").setFontAlign(-1, 0); - const label = "UV"; + const label = "UV: "; const labelW = g.stringWidth(label); - const x = l.x + 2; - const y = l.y + l.h / 2; + // Calculate centered position (4px block + 1px spacing) * blocks - last spacing + const totalW = labelW + uv * 5 - (uv > 0 ? 1 : 0); + const x = l.x + (l.w - totalW) / 2; + const y = l.y + l.h+6; - // Draw title + // Draw label g.setColor(g.theme.fg).drawString(label, x, y); - // Draw UV blocks after title + // Draw UV blocks g.setColor(color); for (let i = 0; i < uv; i++) { - const blockX = x + labelW + 4 + i * (blockW + 2); - g.fillRect(blockX, y - blockH/2, blockX + blockW, y + blockW/2); + g.fillRect(x + labelW + i * 5, y - 3, x + labelW + i * 5 + 3, y + 3); } - - // Reset graphics state to prevent interference - g.reset(); } }, ]}, + {type: "v", fillx: 1, c: [ + {pad:5}, + + {type: "h", pad: 2, c: [ + {type: "txt", font: "18%", id: "temp", label: "000"}, + {type: "txt", font: "12%", valign: -1, id: "tempUnit", label: "°C"}, + ]}, + {filly: 1}, + {type: "h", pad: 1, c: [ + {type: "txt", font: "6x8", pad: 2, halign: 1, id: "feelsLikeLabel",label: /*LANG*/"Feels:"}, + {type: "txt", font: "9%", pad: 2, halign: 1, id: "feelsLike", label: "35°F"}, + ]}, + {filly: 1}, + {type: "h", pad: 2, c: [ + {type: "txt", font: "6x8", pad: 2, halign: 1, label: /*LANG*/"Hum:"}, + {type: "txt", font: "9%", pad: 2, halign: 1, id: "hum", label: "000%"}, + ]}, + + {filly: 1}, + {type: "txt", font: "6x8", pad: 2, halign: -1, label: /*LANG*/"Wind"}, + {type: "h", halign: -1, c: [ + {type: "txt", font: "9%", pad: 2, id: "wind", label: "00"}, + {type: "txt", font: "6x8", pad: 2, valign: -1, id: "windUnit", label: "km/h"}, + ]}, + ]}, ]}, {filly: 1}, - {type: "txt", font: "9%", wrap: true, height: g.getHeight()*0.18, fillx: 1, id: "cond", label: /*LANG*/"Weather condition"}, + {type: "txt", font: "9%",wrap: true, height: g.getHeight()*0.18, fillx: 1, id: "cond", label: /*LANG*/"Weather condition"}, {filly: 1}, {type: "h", c: [ {type: "txt", font: "6x8", pad: 4, id: "loc", label: "Toronto"}, @@ -83,8 +91,17 @@ function draw() { layout.icon.txt = current.txt; layout.icon.code = current.code; const temp = locale.temp(current.temp-273.15).match(/^(\D*\d*)(.*)$/); + const feelsLikeTemp=locale.temp(current.feels-273.15).match(/^(\D*\d*)(.*)$/); layout.temp.label = temp[1]; layout.tempUnit.label = temp[2]; + if (!current || current.feels === undefined){ + layout.feelsLike.label = ""; + layout.feelsLikeLabel.label=""; + }else{ + layout.feelsLike.label = feelsLikeTemp[1]+feelsLikeTemp[2]; + layout.feelsLikeLabel.label="Feels: "; + } + layout.hum.label = current.hum+"%"; const wind = locale.speed(current.wind).match(/^(\D*\d*)(.*)$/); layout.wind.label = wind[1]; @@ -92,8 +109,8 @@ function draw() { layout.cond.label = current.txt.charAt(0).toUpperCase()+(current.txt||'').slice(1); layout.loc.label = current.loc; layout.updateTime.label = `${formatDuration(Date.now() - current.time)} ago`; // How to autotranslate this and similar? + //layout.clear(layout.feelsLike); layout.update(); - layout.forgetLazyState(); layout.render(); } @@ -112,9 +129,9 @@ function update() { } else { layout.forgetLazyState(); if (NRF.getSecurityStatus().connected) { - E.showMessage(/*LANG*/"Weather\nunknown\n\nIs Gadgetbridge\nweather\nreporting set\nup on your\nphone?"); + E.showMessage(/*LANG*/"Weather data\nexpired.\n\nRe-push weather\ndata from your\nphone"); } else { - E.showMessage(/*LANG*/"Weather\nunknown\n\nGadgetbridge\nnot connected"); + E.showMessage(/*LANG*/"Weather data\n has expired."); NRF.on("connect", update); } } @@ -143,4 +160,4 @@ Bangle.setUI("clock"); // This matters for widgets that hide themselves for clocks, like widclk or widclose delete Bangle.CLOCK; -Bangle.drawWidgets(); \ No newline at end of file +Bangle.drawWidgets(); diff --git a/apps/weather/clkinfo.js b/apps/weather/clkinfo.js index 2f36a600cd..eaf9556e04 100644 --- a/apps/weather/clkinfo.js +++ b/apps/weather/clkinfo.js @@ -1,3 +1,4 @@ + (function() { var weather; var weatherLib = require("weather"); @@ -6,6 +7,7 @@ weather = weatherLib.get(); if(weather){ weather.temp = require("locale").temp(weather.temp-273.15); + weather.feels = require("locale").temp(weather.feels-273.15); weather.hum = weather.hum + "%"; weather.wind = require("locale").speed(weather.wind).match(/^(\D*\d*)(.*)$/); weather.wind = Math.round(weather.wind[1]) + "kph"; @@ -14,6 +16,7 @@ temp: "?", hum: "?", wind: "?", + feels: "?", txt: "?", }; } @@ -50,7 +53,7 @@ weatherLib.on("update", this.updater); }, hide: function () { weatherLib.removeListener("update", this.updater); } - ,run : function() {load("weather.app.js");} + ,run : function() {load("weather.app.js");} }, { name: "condition", @@ -62,7 +65,7 @@ weatherLib.on("update", this.updater); }, hide: function () { weatherLib.removeListener("update", this.updater); } - ,run : function() {load("weather.app.js");} + ,run : function() {load("weather.app.js");} }, { name: "temperature", @@ -74,7 +77,19 @@ weatherLib.on("update", this.updater); }, hide: function () { weatherLib.removeListener("update", this.updater); } - ,run : function() {load("weather.app.js");} + ,run : function() {load("weather.app.js");} + }, + { + name: "feelsLike", + hasRange : true, + get: () => ({ text: weather.feels, img: atob("GBiBAAAAAAHAAAPgAAfgAAfgAAfg4APhsAfxEB/5EB/5ED/9ED/9ED/9ED/9ED/9EB/9UB/7UA/yyAf26Afk7AfmyAfjGAfh8AAAAA=="), + v: parseInt(weather.temp), min: -30, max: 55}), + show: function() { + this.updater = _updater.bind(this); + weatherLib.on("update", this.updater); + }, + hide: function () { weatherLib.removeListener("update", this.updater); } + ,run : function() {load("weather.app.js");} }, { name: "humidity", @@ -86,7 +101,7 @@ weatherLib.on("update", this.updater); }, hide: function () { weatherLib.removeListener("update", this.updater); } - ,run : function() {load("weather.app.js");} + ,run : function() {load("weather.app.js");} }, { name: "wind", @@ -98,7 +113,7 @@ weatherLib.on("update", this.updater); }, hide: function () { weatherLib.removeListener("update", this.updater); } - ,run : function() {load("weather.app.js");} + ,run : function() {load("weather.app.js");} }, ] };