Weather: Add support for only extended weather data without forecast
Android only support for now. With extended data we can parse feels like temperature with GB same as on iOS.master^2
parent
42bf5b3cbd
commit
c58d503157
|
|
@ -77,7 +77,7 @@ Adds:
|
||||||
|
|
||||||
* Expiration time span can be set after which the local weather data is considered as invalid
|
* Expiration time span can be set after which the local weather data is considered as invalid
|
||||||
* Automatic weather data request interval can be set (weather data are pushed to Bangle automatically, but this can help in cases when it fails) (requires Gadgetbridge v0.86+)
|
* Automatic weather data request interval can be set (weather data are pushed to Bangle automatically, but this can help in cases when it fails) (requires Gadgetbridge v0.86+)
|
||||||
* Forecast weather data can be enabled (this requires other App to use the data, Weather App itself doesn't show the forecast data) (requires Gadgetbridge v0.86+)
|
* Extended or forecast weather data can be enabled (this requires other App to use this data, Weather App itself doesn't show the forecast data) (requires Gadgetbridge v0.86+)
|
||||||
* Widget can be hidden
|
* Widget can be hidden
|
||||||
* To change the units for wind speed, you can install the [`Languages app`](https://banglejs.com/apps/?id=locale) which
|
* To change the units for wind speed, you can install the [`Languages app`](https://banglejs.com/apps/?id=locale) which
|
||||||
allows you to choose the units used for speed/distance/temperature and so on.
|
allows you to choose the units used for speed/distance/temperature and so on.
|
||||||
|
|
@ -89,15 +89,17 @@ allows you to choose the units used for speed/distance/temperature and so on.
|
||||||
|
|
||||||
## Weather App API
|
## Weather App API
|
||||||
|
|
||||||
|
Note: except `getWeather()` and `get()` it is android only for now
|
||||||
|
|
||||||
Weather App can provide weather and forecast data to other Apps.
|
Weather App can provide weather and forecast data to other Apps.
|
||||||
|
|
||||||
* Get weather data without forecast:
|
* Get weather data without forecast/extended:
|
||||||
```javascript
|
```javascript
|
||||||
const weather = require("weather");
|
const weather = require("weather");
|
||||||
weatherData = weather.getWeather(false); // or weather.get()
|
weatherData = weather.getWeather(false); // or weather.get()
|
||||||
```
|
```
|
||||||
|
|
||||||
* Get weather data with forecast (needs forecast enabled in settings):
|
* Get weather data with forecast/extended (needs forecast/extended enabled in settings):
|
||||||
```javascript
|
```javascript
|
||||||
const weather = require("weather");
|
const weather = require("weather");
|
||||||
weatherData = weather.getWeather(true);
|
weatherData = weather.getWeather(true);
|
||||||
|
|
|
||||||
|
|
@ -61,13 +61,13 @@ function update(weatherEvent) {
|
||||||
storage.write("weather.json", json);
|
storage.write("weather.json", json);
|
||||||
exports.emit("update", weather);
|
exports.emit("update", weather);
|
||||||
|
|
||||||
// Request forecast if supported by GadgetBridge and set in settings
|
// Request extended/forecast if supported by Weather Provider and set in settings
|
||||||
if (weatherEvent.v != null && (weatherSetting.forecast ?? false)) {
|
if (weatherEvent.v != null && (weatherSetting.dataType ?? "basic") !== "basic") {
|
||||||
updateWeather(true);
|
updateWeather(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either GadgetBridge doesn't support v2 and/or we don't need forecast, so we use this event for refresh scheduling
|
// Either Weather Provider doesn't support v2 and/or we don't need extended/forecast, so we use this event for refresh scheduling
|
||||||
if (weatherEvent.v == null || (weatherSetting.forecast ?? false) === false) {
|
if (weatherEvent.v == null || (weatherSetting.dataType ?? "basic") === "basic") {
|
||||||
weatherSetting.time = Date.now();
|
weatherSetting.time = Date.now();
|
||||||
storage.write("weatherSetting.json", weatherSetting);
|
storage.write("weatherSetting.json", weatherSetting);
|
||||||
|
|
||||||
|
|
@ -93,6 +93,12 @@ function update(weatherEvent) {
|
||||||
// Store simpler weather for apps that doesn't need forecast or backward compatibility
|
// Store simpler weather for apps that doesn't need forecast or backward compatibility
|
||||||
const weather = downgradeWeatherV2(weather2);
|
const weather = downgradeWeatherV2(weather2);
|
||||||
storage.write("weather.json", weather);
|
storage.write("weather.json", weather);
|
||||||
|
exports.emit("update", weather);
|
||||||
|
} else if (weather1.weather != null && weather1.weather.feels === undefined) {
|
||||||
|
// Grab feels like temperature as we have it in v2
|
||||||
|
weather1.weather.feels = decodeWeatherV2FeelsLike(weatherEvent);
|
||||||
|
storage.write("weather.json", weather);
|
||||||
|
exports.emit("update", weather);
|
||||||
}
|
}
|
||||||
|
|
||||||
cloned = undefined; // Clear memory
|
cloned = undefined; // Clear memory
|
||||||
|
|
@ -111,15 +117,15 @@ function updateWeather(force) {
|
||||||
|
|
||||||
// More than 5 minutes
|
// More than 5 minutes
|
||||||
if (force || Date.now() - lastFetch >= 5 * 60 * 1000) {
|
if (force || Date.now() - lastFetch >= 5 * 60 * 1000) {
|
||||||
Bluetooth.println("");
|
Bluetooth.println(""); // This empty line is important for correct communication with Weather Provider
|
||||||
Bluetooth.println(JSON.stringify({ t: "weather", v: 2, f: settings.forecast ?? false }));
|
Bluetooth.println(JSON.stringify({ t: "weather", v: 2, f: settings.dataType === "forecast" }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWeather(forecast) {
|
function getWeather(extended) {
|
||||||
const weatherSetting = storage.readJSON("weatherSetting.json") || {};
|
const weatherSetting = storage.readJSON("weatherSetting.json") || {};
|
||||||
|
|
||||||
if (forecast === false || !(weatherSetting.forecast ?? false)) {
|
if (extended === false || (weatherSetting.dataType ?? "basic") === "basic") {
|
||||||
// biome-ignore lint/complexity/useOptionalChain: not supported by Espruino
|
// biome-ignore lint/complexity/useOptionalChain: not supported by Espruino
|
||||||
return (storage.readJSON("weather.json") ?? {}).weather;
|
return (storage.readJSON("weather.json") ?? {}).weather;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -169,7 +175,7 @@ function decodeWeatherV2(jsonData, canDestroyArgument, parseForecast) {
|
||||||
return { t: "weather2", v: 2, time: time };
|
return { t: "weather2", v: 2, time: time };
|
||||||
}
|
}
|
||||||
|
|
||||||
// This needs to be kept in sync with GadgetBridge
|
// This needs to be kept in sync with Weather Provider
|
||||||
const weatherCodes = [
|
const weatherCodes = [
|
||||||
[200, 201, 202, 210, 211, 212, 221, 230, 231, 232],
|
[200, 201, 202, 210, 211, 212, 221, 230, 231, 232],
|
||||||
[300, 301, 302, 310, 311, 312, 313, 314, 321],
|
[300, 301, 302, 310, 311, 312, 313, 314, 321],
|
||||||
|
|
@ -214,7 +220,7 @@ function decodeWeatherV2(jsonData, canDestroyArgument, parseForecast) {
|
||||||
moonrise: dataView.getUint32(27, true),
|
moonrise: dataView.getUint32(27, true),
|
||||||
moonset: dataView.getUint32(31, true),
|
moonset: dataView.getUint32(31, true),
|
||||||
moonphase: dataView.getUint16(35, true),
|
moonphase: dataView.getUint16(35, true),
|
||||||
feel: dataView.getInt8(37, true),
|
feels: dataView.getInt8(37, true),
|
||||||
};
|
};
|
||||||
weather.wrose = windDirection(weather.wdir);
|
weather.wrose = windDirection(weather.wdir);
|
||||||
|
|
||||||
|
|
@ -256,6 +262,16 @@ function decodeWeatherV2(jsonData, canDestroyArgument, parseForecast) {
|
||||||
return weather;
|
return weather;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decodeWeatherV2FeelsLike(jsonData) {
|
||||||
|
if (jsonData == null || jsonData.d == null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = E.toArrayBuffer(atob(jsonData.d));
|
||||||
|
|
||||||
|
return new DataView(buffer).getInt8(37, true);
|
||||||
|
}
|
||||||
|
|
||||||
function downgradeWeatherV2(weather2) {
|
function downgradeWeatherV2(weather2) {
|
||||||
const json = { t: "weather" };
|
const json = { t: "weather" };
|
||||||
|
|
||||||
|
|
@ -274,6 +290,7 @@ function downgradeWeatherV2(weather2) {
|
||||||
wdir: weather2.wdir,
|
wdir: weather2.wdir,
|
||||||
wrose: weather2.wrose,
|
wrose: weather2.wrose,
|
||||||
loc: weather2.loc,
|
loc: weather2.loc,
|
||||||
|
feels: weather2.feels,
|
||||||
};
|
};
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
storage.write("weatherSetting.json", settings);
|
storage.write("weatherSetting.json", settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DATA_TYPE = ["basic", "extended", "forecast"];
|
||||||
|
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"": { "title": "Weather" },
|
"": { "title": "Weather" },
|
||||||
"Expiry": {
|
"Expiry": {
|
||||||
|
|
@ -45,11 +47,14 @@
|
||||||
},
|
},
|
||||||
onchange: (x) => save("refresh", x),
|
onchange: (x) => save("refresh", x),
|
||||||
},
|
},
|
||||||
Forecast: {
|
"Data type": {
|
||||||
value: "forecast" in settings ? settings.forecast : false,
|
value: DATA_TYPE.indexOf(settings.dataType ?? "basic"),
|
||||||
onchange: () => {
|
format: (v) => DATA_TYPE[v],
|
||||||
settings.forecast = !settings.forecast;
|
min: 0,
|
||||||
save("forecast", settings.forecast);
|
max: DATA_TYPE.length - 1,
|
||||||
|
onchange: (v) => {
|
||||||
|
settings.dataType = DATA_TYPE[v];
|
||||||
|
save("dataType", settings.dataType);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Hide Widget": {
|
"Hide Widget": {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue