Skip to content

Commit 71a3608

Browse files
authored
Merge pull request #3912 from RKBoss6/Weather-Feels-Like-Updates
[Weather] Add feels-like temperature display
2 parents c13b57a + 54f8807 commit 71a3608

File tree

5 files changed

+73
-44
lines changed

5 files changed

+73
-44
lines changed

apps/weather/ChangeLog

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@
2424
0.25: Added monochrome parameter to drawIcon in lib
2525
0.26: Expose update function (for use by iOS integration)
2626
0.27: Add UV index display
27-
0.28: Fix UV positioning, hide when 0
27+
0.28: Fix UV positioning, hide when 0
28+
0.29: Add feels-like temperature data and clock_info

apps/weather/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ You can view the full report through the app:
1212

1313
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.
1414

15+
1516
## Android Setup
1617

1718
1. Install [Gadgetbridge for Android](https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/) on your phone.

apps/weather/app.js

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,59 +9,66 @@ Bangle.loadWidgets();
99
var layout = new Layout({type:"v", bgCol: g.theme.bg, c: [
1010
{filly: 1},
1111
{type: "h", filly: 0, c: [
12-
{type: "v", width: g.getWidth()/2, c: [ // Vertical container for icon
13-
{type: "custom", fillx: 1, height: g.getHeight()/2 - 30, valign: -1, txt: "unknown", id: "icon",
14-
render: l => weather.drawIcon(l, l.x+l.w/2, l.y+l.h/2, l.w/2-5)},
15-
]},
16-
{type: "v", fillx: 1, c: [
17-
{type: "h", pad: 2, c: [
18-
{type: "txt", font: "18%", id: "temp", label: "000"},
19-
{type: "txt", font: "12%", valign: -1, id: "tempUnit", label: "°C"},
20-
]},
21-
{filly: 1},
22-
{type: "txt", font: "6x8", pad: 2, halign: 1, label: /*LANG*/"Humidity"},
23-
{type: "txt", font: "9%", pad: 2, halign: 1, id: "hum", label: "000%"},
24-
{type: "txt", font: "6x8", pad: [2, 2, 2, 2], halign: -1, label: /*LANG*/"Wind"},
25-
{type: "h", pad: [0, 2, 2, 2], halign: -1, c: [
26-
{type: "txt", font: "9%", pad: 2, id: "wind", label: "00"},
27-
{type: "txt", font: "6x8", pad: 2, valign: -1, id: "windUnit", label: "km/h"},
28-
]},
29-
{type: "custom", fillx: 1, height: 15, id: "uvDisplay",
12+
{type: "v", width: g.getWidth()/2, c: [ // Vertical container for icon + UV
13+
{type: "custom", fillx: 1, height: (g.getHeight()/2)-10, valign: -1, txt: "unknown", id: "icon",bgCol:g.theme.bg,
14+
render: l => weather.drawIcon(l, l.x+l.w/2, l.y+l.h/2, l.w/3)},
15+
{type: "custom", fillx: 1, height: 20, id: "uvDisplay",
3016
render: l => {
31-
if (!current || current.uv === undefined || current.uv === 0) return;
17+
if (!current || current.uv === undefined) return;
3218
const uv = Math.min(parseInt(current.uv), 11); // Cap at 11
3319

3420
// UV color thresholds: [max_value, color] based on WHO standards
3521
const colors = [[2,"#0F0"], [5,"#FF0"], [7,"#F80"], [10,"#F00"], [11,"#F0F"]];
3622
const color = colors.find(c => uv <= c[0])[1];
37-
const blockH = 8, blockW = 3;
3823

39-
// Draw UV title and blocks on same line
24+
// Setup and measure label
4025
g.setFont("6x8").setFontAlign(-1, 0);
41-
const label = "UV";
26+
const label = "UV: ";
4227
const labelW = g.stringWidth(label);
4328

44-
const x = l.x + 2;
45-
const y = l.y + l.h / 2;
29+
// Calculate centered position (4px block + 1px spacing) * blocks - last spacing
30+
const totalW = labelW + uv * 5 - (uv > 0 ? 1 : 0);
31+
const x = l.x + (l.w - totalW) / 2;
32+
const y = l.y + l.h+6;
4633

47-
// Draw title
34+
// Draw label
4835
g.setColor(g.theme.fg).drawString(label, x, y);
4936

50-
// Draw UV blocks after title
37+
// Draw UV blocks
5138
g.setColor(color);
5239
for (let i = 0; i < uv; i++) {
53-
const blockX = x + labelW + 4 + i * (blockW + 2);
54-
g.fillRect(blockX, y - blockH/2, blockX + blockW, y + blockW/2);
40+
g.fillRect(x + labelW + i * 5, y - 3, x + labelW + i * 5 + 3, y + 3);
5541
}
56-
57-
// Reset graphics state to prevent interference
58-
g.reset();
5942
}
6043
},
6144
]},
45+
{type: "v", fillx: 1, c: [
46+
{pad:5},
47+
{type: "h", pad: 2, c: [
48+
{type: "txt", font: "18%", id: "temp", label: "000"},
49+
{type: "txt", font: "12%", valign: -1, id: "tempUnit", label: "°C"},
50+
]},
51+
{filly: 1},
52+
{type: "h", pad: 1, c: [
53+
{type: "txt", font: "6x8", pad: 2, halign: 1, label: /*LANG*/"Feels:"},
54+
{type: "txt", font: "9%", pad: 2, halign: 1, id: "feelslike", label: "35°F"},
55+
]},
56+
{filly: 1},
57+
{type: "h", pad: 2, c: [
58+
{type: "txt", font: "6x8", pad: 2, halign: 1, label: /*LANG*/"Hum:"},
59+
{type: "txt", font: "9%", pad: 2, halign: 1, id: "hum", label: "000%"},
60+
]},
61+
62+
{filly: 1},
63+
{type: "txt", font: "6x8", pad: 2, halign: -1, label: /*LANG*/"Wind"},
64+
{type: "h", halign: -1, c: [
65+
{type: "txt", font: "9%", pad: 2, id: "wind", label: "00"},
66+
{type: "txt", font: "6x8", pad: 2, valign: -1, id: "windUnit", label: "km/h"},
67+
]},
68+
]},
6269
]},
6370
{filly: 1},
64-
{type: "txt", font: "9%", wrap: true, height: g.getHeight()*0.18, fillx: 1, id: "cond", label: /*LANG*/"Weather condition"},
71+
{type: "txt", font: "9%",wrap: true, height: g.getHeight()*0.18, fillx: 1, id: "cond", label: /*LANG*/"Weather condition"},
6572
{filly: 1},
6673
{type: "h", c: [
6774
{type: "txt", font: "6x8", pad: 4, id: "loc", label: "Toronto"},
@@ -83,8 +90,15 @@ function draw() {
8390
layout.icon.txt = current.txt;
8491
layout.icon.code = current.code;
8592
const temp = locale.temp(current.temp-273.15).match(/^(\D*\d*)(.*)$/);
93+
const feelsLikeTemp=locale.temp(current.feels-273.15).match(/^(\D*\d*)(.*)$/);
8694
layout.temp.label = temp[1];
8795
layout.tempUnit.label = temp[2];
96+
if (!current || current.feels === undefined){
97+
layout.feelslike.label = "N/A";
98+
}else{
99+
layout.feelslike.label = feelsLikeTemp[1]+feelsLikeTemp[2];
100+
}
101+
88102
layout.hum.label = current.hum+"%";
89103
const wind = locale.speed(current.wind).match(/^(\D*\d*)(.*)$/);
90104
layout.wind.label = wind[1];
@@ -93,15 +107,13 @@ function draw() {
93107
layout.loc.label = current.loc;
94108
layout.updateTime.label = `${formatDuration(Date.now() - current.time)} ago`; // How to autotranslate this and similar?
95109
layout.update();
96-
layout.forgetLazyState();
97110
layout.render();
98111
}
99112

100113
function drawUpdateTime() {
101114
if (!current || !current.time) return;
102115
layout.updateTime.label = `${formatDuration(Date.now() - current.time)} ago`;
103116
layout.update();
104-
layout.render();
105117
}
106118

107119
function update() {
@@ -112,9 +124,9 @@ function update() {
112124
} else {
113125
layout.forgetLazyState();
114126
if (NRF.getSecurityStatus().connected) {
115-
E.showMessage(/*LANG*/"Weather\nunknown\n\nIs Gadgetbridge\nweather\nreporting set\nup on your\nphone?");
127+
E.showMessage(/*LANG*/"Weather Data Expired");
116128
} else {
117-
E.showMessage(/*LANG*/"Weather\nunknown\n\nGadgetbridge\nnot connected");
129+
E.showMessage(/*LANG*/"Weather\nunknown\n\nPhone\nnot connected");
118130
NRF.on("connect", update);
119131
}
120132
}
@@ -143,4 +155,4 @@ Bangle.setUI("clock");
143155
// This matters for widgets that hide themselves for clocks, like widclk or widclose
144156
delete Bangle.CLOCK;
145157

146-
Bangle.drawWidgets();
158+
Bangle.drawWidgets();

apps/weather/clkinfo.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
(function() {
23
var weather;
34
var weatherLib = require("weather");
@@ -6,6 +7,7 @@
67
weather = weatherLib.get();
78
if(weather){
89
weather.temp = require("locale").temp(weather.temp-273.15);
10+
weather.feels = require("locale").temp(weather.feels-273.15);
911
weather.hum = weather.hum + "%";
1012
weather.wind = require("locale").speed(weather.wind).match(/^(\D*\d*)(.*)$/);
1113
weather.wind = Math.round(weather.wind[1]) + "kph";
@@ -14,6 +16,7 @@
1416
temp: "?",
1517
hum: "?",
1618
wind: "?",
19+
feels: "?",
1720
txt: "?",
1821
};
1922
}
@@ -50,7 +53,7 @@
5053
weatherLib.on("update", this.updater);
5154
},
5255
hide: function () { weatherLib.removeListener("update", this.updater); }
53-
,run : function() {load("weather.app.js");}
56+
,run : function() {load("weather.app.js");}
5457
},
5558
{
5659
name: "condition",
@@ -62,7 +65,7 @@
6265
weatherLib.on("update", this.updater);
6366
},
6467
hide: function () { weatherLib.removeListener("update", this.updater); }
65-
,run : function() {load("weather.app.js");}
68+
,run : function() {load("weather.app.js");}
6669
},
6770
{
6871
name: "temperature",
@@ -74,7 +77,19 @@
7477
weatherLib.on("update", this.updater);
7578
},
7679
hide: function () { weatherLib.removeListener("update", this.updater); }
77-
,run : function() {load("weather.app.js");}
80+
,run : function() {load("weather.app.js");}
81+
},
82+
{
83+
name: "feelsLike",
84+
hasRange : true,
85+
get: () => ({ text: weather.feels, img: atob("GBiBAAAAAAHAAAPgAAfgAAfgAAfg4APhsAfxEB/5EB/5ED/9ED/9ED/9ED/9ED/9EB/9UB/7UA/yyAf26Afk7AfmyAfjGAfh8AAAAA=="),
86+
v: parseInt(weather.temp), min: -30, max: 55}),
87+
show: function() {
88+
this.updater = _updater.bind(this);
89+
weatherLib.on("update", this.updater);
90+
},
91+
hide: function () { weatherLib.removeListener("update", this.updater); }
92+
,run : function() {load("weather.app.js");}
7893
},
7994
{
8095
name: "humidity",
@@ -86,7 +101,7 @@
86101
weatherLib.on("update", this.updater);
87102
},
88103
hide: function () { weatherLib.removeListener("update", this.updater); }
89-
,run : function() {load("weather.app.js");}
104+
,run : function() {load("weather.app.js");}
90105
},
91106
{
92107
name: "wind",
@@ -98,7 +113,7 @@
98113
weatherLib.on("update", this.updater);
99114
},
100115
hide: function () { weatherLib.removeListener("update", this.updater); }
101-
,run : function() {load("weather.app.js");}
116+
,run : function() {load("weather.app.js");}
102117
},
103118
]
104119
};

apps/weather/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "weather",
33
"name": "Weather",
4-
"version": "0.28",
4+
"version": "0.29",
55
"description": "Show Gadgetbridge/iOS weather report",
66
"icon": "icon.png",
77
"screenshots": [{"url":"screenshot.png"}],

0 commit comments

Comments
 (0)