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
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