added functional current, hourly, and daily forecasts via OpenWeatherMap One Call API

This commit is contained in:
Bryan Zhu 2020-07-01 05:08:04 -04:00
parent 1d2f929d3f
commit 85ed1b85ae
5 changed files with 244 additions and 168 deletions

View File

@ -60,13 +60,13 @@ WeatherProvider.register("openweathermap", {
fetchWeatherData() { fetchWeatherData() {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
if (!data || !data.list || !data.list.length) { if (!data) {
// Did not receive usable new data. // Did not receive usable new data.
// Maybe this needs a better check? // Maybe this needs a better check?
return; return;
} }
this.setFetchedLocation(`${data.lat},${data.lon}`); this.setFetchedLocation(`(${data.lat},${data.lon})`);
const wData = this.generateWeatherObjectsFromOnecall(data); const wData = this.generateWeatherObjectsFromOnecall(data);
this.setWeatherData(wData); this.setWeatherData(wData);
@ -121,156 +121,13 @@ WeatherProvider.register("openweathermap", {
*/ */
generateWeatherObjectsFromOnecall(data) { generateWeatherObjectsFromOnecall(data) {
if (this.config.weatherEndpoint === "/onecall") { if (this.config.weatherEndpoint === "/onecall") {
return this.fetchOneCall(data); return this.fetchOnecall(data);
} }
// if weatherEndpoint does not match onecall, what should be returned? // if weatherEndpoint does not match onecall, what should be returned?
const wData = {current: new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits), hours: [], days: []}; const wData = {current: new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits), hours: [], days: []};
return wData; return wData;
}, },
/*
* fetch One Call forecast information (available for free subscription).
* factors in timezone offsets.
* minutely forecasts are excluded for the moment, see getParams().
*/
fetchOnecall(data) {
let precip = false;
// get current weather
const current = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
if (!isNaN(data.current)) {
current.date = moment(data.current.dt, "X").utcOffset(data.timezone_offset/60);
current.windSpeed = data.current.wind_speed;
current.windDirection = data.current.wind_deg;
current.sunrise = moment(data.current.sunrise, "X").utcOffset(data.timezone_offset/60);
current.sunset = moment(data.current.sunset, "X").utcOffset(data.timezone_offset/60);
current.temperature = data.current.temp;
current.weatherType = this.convertWeatherType(data.current.weather[0].icon);
current.humidity = data.current.humidity;
if (current.hasOwnProperty("rain") && !isNaN(current.rain["1h"])) {
if (this.config.units === "imperial") {
weather.rain = current.rain["1h"] / 25.4;
} else {
weather.rain = current.rain["1h"];
}
precip = true;
}
if (current.hasOwnProperty("snow") && !isNaN(current.snow["1h"])) {
if (this.config.units === "imperial") {
weather.snow = current.snow["1h"] / 25.4;
} else {
weather.snow = current.snow["1h"];
}
precip = true;
}
if (precip) {
current.precipitation = current.rain+current.snow;
}
current.feelsLikeTemp = data.current.feels_like;
}
let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
// let onecallDailyFormat = "MMM DD"
// let onecallHourlyFormat = "HH"
// let onecallMinutelyFormat = "HH:mm"
// if (this.config.timeFormat === 12) {
// if (this.config.showPeriod === true) {
// if (this.config.showPeriodUpper === true) {
// onecallHourlyFormat = "hhA"
// onecallMinutelyFormat = "hh:mmA"
// } else {
// onecallHourlyFormat = "hha"
// onecallMinutelyFormat = "hh:mma"
// }
// } else {
// onecallHourlyFormat = "hh"
// onecallMinutelyFormat = "hh:mm"
// }
// }
// get hourly weather
const hours = [];
if (!isNaN(data.hourly)) {
for (const hour of data.hourly) {
weather.date = moment(hour.dt, "X").utcOffset(data.timezone_offset/60);
// weather.date = moment(hour.dt, "X").utcOffset(data.timezone_offset/60).format(onecallDailyFormat+","+onecallHourlyFormat);
weather.temperature = hour.temp;
weather.feelsLikeTemp = hour.feels_like;
weather.humidity = hour.humidity;
weather.windSpeed = hour.wind_speed;
weather.windDirection = hour.wind_deg;
weather.weatherType = this.convertWeatherType(hour.weather[0].icon);
precip = false;
if (hour.hasOwnProperty("rain") && !isNaN(hour.rain["1h"])) {
if (this.config.units === "imperial") {
weather.rain = hour.rain["1h"] / 25.4;
} else {
weather.rain = hour.rain["1h"];
}
precip = true;
}
if (hour.hasOwnProperty("snow") && !isNaN(hour.snow["1h"])) {
if (this.config.units === "imperial") {
weather.snow = hour.snow["1h"] / 25.4;
} else {
weather.snow = hour.snow["1h"];
}
precip = true;
}
if (precip) {
weather.precipitation = weather.rain+weather.snow;
}
hours.push(weather);
weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
}
}
// get daily weather
const days = [];
if (!isNaN(data.daily)) {
for (const day of data.daily) {
weather.date = moment(day.dt, "X").utcOffset(data.timezone_offset/60);
weather.sunrise = moment(day.sunrise, "X").utcOffset(data.timezone_offset/60);
weather.sunset = moment(day.sunset, "X").utcOffset(data.timezone_offset/60);
// weather.date = moment(day.dt, "X").utcOffset(data.timezone_offset/60).format(onecallDailyFormat);
// weather.sunrise = moment(day.sunrise, "X").utcOffset(data.timezone_offset/60).format(onecallMinutelyFormat);
// weather.sunset = moment(day.sunset, "X").utcOffset(data.timezone_offset/60).format(onecallMinutelyFormat);
weather.minTemperature = day.temp.min;
weather.maxTemperature = day.temp.max;
weather.humidity = day.humidity;
weather.windSpeed = day.wind_speed;
weather.windDirection = day.wind_deg;
weather.weatherType = this.convertWeatherType(day.weather[0].icon);
precip = false;
if (!isNaN(day.rain)) {
if (this.config.units === "imperial") {
weather.rain = day.rain / 25.4;
} else {
weather.rain = day.rain;
}
precip = true;
}
if (!isNaN(day.snow)) {
if (this.config.units === "imperial") {
weather.snow = day.snow / 25.4;
} else {
weather.snow = day.snow;
}
precip = true;
}
if (precip) {
weather.precipitation = weather.rain+weather.snow;
}
days.push(weather);
weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
}
}
return {current: current, hours: hours, days: days};
},
/* /*
* fetch forecast information for 3-hourly forecast (available for free subscription). * fetch forecast information for 3-hourly forecast (available for free subscription).
*/ */
@ -397,6 +254,129 @@ WeatherProvider.register("openweathermap", {
return days; return days;
}, },
/*
* Fetch One Call forecast information (available for free subscription).
* Factors in timezone offsets.
* Minutely forecasts are excluded for the moment, see getParams().
*/
fetchOnecall(data) {
let precip = false;
// get current weather, if requested
const current = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
if (data.hasOwnProperty("current")) {
current.date = moment(data.current.dt, "X").utcOffset(data.timezone_offset/60);
current.windSpeed = data.current.wind_speed;
current.windDirection = data.current.wind_deg;
current.sunrise = moment(data.current.sunrise, "X").utcOffset(data.timezone_offset/60);
current.sunset = moment(data.current.sunset, "X").utcOffset(data.timezone_offset/60);
current.temperature = data.current.temp;
current.weatherType = this.convertWeatherType(data.current.weather[0].icon);
current.humidity = data.current.humidity;
if (data.current.hasOwnProperty("rain") && !isNaN(data.current["rain"]["1h"])) {
if (this.config.units === "imperial") {
current.rain = data.current["rain"]["1h"] / 25.4;
} else {
current.rain = data.current["rain"]["1h"];
}
precip = true;
}
if (data.current.hasOwnProperty("snow") && !isNaN(data.current["snow"]["1h"])) {
if (this.config.units === "imperial") {
current.snow = data.current["snow"]["1h"] / 25.4;
} else {
current.snow = data.current["snow"]["1h"];
}
precip = true;
}
if (precip) {
current.precipitation = current.rain+current.snow;
}
current.feelsLikeTemp = data.current.feels_like;
}
let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
// get hourly weather, if requested
const hours = [];
if (data.hasOwnProperty("hourly")) {
for (const hour of data.hourly) {
weather.date = moment(hour.dt, "X").utcOffset(data.timezone_offset/60);
// weather.date = moment(hour.dt, "X").utcOffset(data.timezone_offset/60).format(onecallDailyFormat+","+onecallHourlyFormat);
weather.temperature = hour.temp;
weather.feelsLikeTemp = hour.feels_like;
weather.humidity = hour.humidity;
weather.windSpeed = hour.wind_speed;
weather.windDirection = hour.wind_deg;
weather.weatherType = this.convertWeatherType(hour.weather[0].icon);
precip = false;
if (hour.hasOwnProperty("rain") && !isNaN(hour.rain["1h"])) {
if (this.config.units === "imperial") {
weather.rain = hour.rain["1h"] / 25.4;
} else {
weather.rain = hour.rain["1h"];
}
precip = true;
}
if (hour.hasOwnProperty("snow") && !isNaN(hour.snow["1h"])) {
if (this.config.units === "imperial") {
weather.snow = hour.snow["1h"] / 25.4;
} else {
weather.snow = hour.snow["1h"];
}
precip = true;
}
if (precip) {
weather.precipitation = weather.rain+weather.snow;
}
hours.push(weather);
weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
}
}
// get daily weather, if requested
const days = [];
if (data.hasOwnProperty("daily")) {
for (const day of data.daily) {
weather.date = moment(day.dt, "X").utcOffset(data.timezone_offset/60);
weather.sunrise = moment(day.sunrise, "X").utcOffset(data.timezone_offset/60);
weather.sunset = moment(day.sunset, "X").utcOffset(data.timezone_offset/60);
weather.minTemperature = day.temp.min;
weather.maxTemperature = day.temp.max;
weather.humidity = day.humidity;
weather.windSpeed = day.wind_speed;
weather.windDirection = day.wind_deg;
weather.weatherType = this.convertWeatherType(day.weather[0].icon);
precip = false;
if (!isNaN(day.rain)) {
if (this.config.units === "imperial") {
weather.rain = day.rain / 25.4;
} else {
weather.rain = day.rain;
}
precip = true;
}
if (!isNaN(day.snow)) {
if (this.config.units === "imperial") {
weather.snow = day.snow / 25.4;
} else {
weather.snow = day.snow;
}
precip = true;
}
if (precip) {
weather.precipitation = weather.rain+weather.snow;
}
days.push(weather);
weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
}
}
return {current: current, hours: hours, days: days};
},
/* /*
* Convert the OpenWeatherMap icons to a more usable name. * Convert the OpenWeatherMap icons to a more usable name.
*/ */

View File

@ -0,0 +1,83 @@
{% if wData %}
{% set current = wData.current %}
{% if not config.onlyTemp %}
<div class="normal medium">
<span class="wi wi-strong-wind dimmed"></span>
<span>
{% if config.useBeaufort %}
{{ current.beaufortWindSpeed() | round }}
{% else %}
{{ current.windSpeed | round }}
{% endif %}
{% if config.showWindDirection %}
<sup>
{% if config.showWindDirectionAsArrow %}
<i class="fa fa-long-arrow-up" style="transform:rotate({{ current.windDirection }}deg);"></i>
{% else %}
{{ current.cardinalWindDirection() | translate }}
{% endif %}
&nbsp;
</sup>
{% endif %}
</span>
{% if config.showHumidity and current.humidity %}
<span>{{ current.humidity | decimalSymbol }}</span><sup>&nbsp;<i class="wi wi-humidity humidityIcon"></i></sup>
{% endif %}
{% if config.showSun %}
<span class="wi dimmed wi-{{ current.nextSunAction() }}"></span>
<span>
{% if current.nextSunAction() === "sunset" %}
{{ current.sunset | formatTime }}
{% else %}
{{ current.sunrise | formatTime }}
{% endif %}
</span>
{% endif %}
</div>
{% endif %}
<div class="large light">
<span class="wi weathericon wi-{{current.weatherType}}"></span>
<span class="bright">
{{ current.temperature | roundValue | unit("temperature") | decimalSymbol }}
</span>
</div>
<div class="normal light indoor">
{% if config.showIndoorTemperature and indoor.temperature %}
<div>
<span class="fa fa-home"></span>
<span class="bright">
{{ indoor.temperature | roundValue | unit("temperature") | decimalSymbol }}
</span>
</div>
{% endif %}
{% if config.showIndoorHumidity and indoor.humidity %}
<div>
<span class="fa fa-tint"></span>
<span class="bright">
{{ indoor.humidity | roundValue | unit("humidity") | decimalSymbol }}
</span>
</div>
{% endif %}
</div>
{% if (config.showFeelsLike or config.showPrecipitationAmount) and not config.onlyTemp %}
<div class="normal medium">
{% if config.showFeelsLike %}
<span class="dimmed">
{{ "FEELS" | translate }} {{ current.feelsLike() | roundValue | unit("temperature") | decimalSymbol }}
</span>
{% endif %}
{% if config.showPrecipitationAmount %}
<span class="dimmed">
{{ "PRECIP" | translate }} {{ current.precipitation | unit("precip") }}
</span>
{% endif %}
</div>
{% endif %}
{% else %}
<div class="dimmed light small">
{{ "LOADING" | translate | safe }}
</div>
{% endif %}
<!-- Uncomment the line below to see the contents of the `wData` object. -->
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{wData | dump}}</div> -->

View File

@ -0,0 +1,32 @@
{% if wData %}
{% set numSteps = wData.days | calcNumEntries %}
{% set currentStep = 0 %}
<table class="{{ config.tableClass }}">
{% set days = wData.days.slice(0, numSteps) %}
{% for day in days %}
<tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}>
<td class="day">{{ day.date.format('ddd') }}</td>
<td class="bright weather-icon"><span class="wi weathericon wi-{{ day.weatherType }}"></span></td>
<td class="align-right bright max-temp">
{{ day.maxTemperature | roundValue | unit("temperature") }}
</td>
<td class="align-right min-temp">
{{ day.minTemperature | roundValue | unit("temperature") }}
</td>
{% if config.showPrecipitationAmount %}
<td class="align-right bright precipitation">
{{ day.precipitation | unit("precip") }}
</td>
{% endif %}
</tr>
{% set currentStep = currentStep + 1 %}
{% endfor %}
</table>
{% else %}
<div class="dimmed light small">
{{ "LOADING" | translate | safe }}
</div>
{% endif %}
<!-- Uncomment the line below to see the contents of the `wData` object. -->
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{wData | dump}}</div> -->

View File

@ -5,9 +5,9 @@
{% set hours = wData.hours.slice(0, numSteps) %} {% set hours = wData.hours.slice(0, numSteps) %}
{% for hour in hours %} {% for hour in hours %}
<tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}> <tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}>
<td class="day">{{ hour.date | formatTimeMoment }}</td> <td class="day">{{ hour.date | formatTime }}</td>
<td class="bright weather-icon"><span class="wi weathericon wi-{{ hour.weatherType }}"></span></td> <td class="bright weather-icon"><span class="wi weathericon wi-{{ hour.weatherType }}"></span></td>
<td class="align-right bright max-temp"> <td class="align-right bright">
{{ hour.temperature | roundValue | unit("temperature") }} {{ hour.temperature | roundValue | unit("temperature") }}
</td> </td>
{% if config.showPrecipitationAmount %} {% if config.showPrecipitationAmount %}

View File

@ -193,25 +193,6 @@ Module.register("weather", {
return date.format("HH:mm"); return date.format("HH:mm");
}.bind(this) }.bind(this)
); );
this.nunjucksEnvironment().addFilter(
"formatTimeMoment",
function (date) {
if (this.config.timeFormat !== 24) {
if (this.config.showPeriod) {
if (this.config.showPeriodUpper) {
return date.format("h:mm A");
} else {
return date.format("h:mm a");
}
} else {
return date.format("h:mm");
}
}
return date.format("HH:mm");
}.bind(this)
);
this.nunjucksEnvironment().addFilter( this.nunjucksEnvironment().addFilter(
"unit", "unit",
@ -230,7 +211,7 @@ Module.register("weather", {
} }
} }
} else if (type === "precip") { } else if (type === "precip") {
if (isNaN(value) || value === 0 || value.toFixed(2) === "0.00") { if (value === null || isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
value = ""; value = "";
} else { } else {
if (this.config.weatherProvider === "ukmetoffice" || this.config.weatherProvider === "ukmetofficedatahub") { if (this.config.weatherProvider === "ukmetoffice" || this.config.weatherProvider === "ukmetofficedatahub") {