ws4kp/server/scripts/modules/extendedforecast.js

161 lines
4.4 KiB
JavaScript
Raw Normal View History

2020-09-04 18:02:20 +00:00
// display extended forecast graphically
// technically uses the same data as the local forecast, we'll let the browser do the caching of that
2022-08-05 03:03:59 +00:00
/* globals WeatherDisplay, utils, STATUS, UNITS, icons, navigation, luxon */
2020-09-04 18:02:20 +00:00
// eslint-disable-next-line no-unused-vars
class ExtendedForecast extends WeatherDisplay {
2020-10-29 21:44:28 +00:00
constructor(navId, elemId) {
2022-11-22 03:50:22 +00:00
super(navId, elemId, 'Extended Forecast', true);
2020-09-04 18:02:20 +00:00
// set timings
this.timing.totalScreens = 2;
}
2020-10-29 21:44:28 +00:00
async getData(_weatherParameters) {
super.getData(_weatherParameters);
const weatherParameters = _weatherParameters ?? this.weatherParameters;
2020-09-04 18:02:20 +00:00
// request us or si units
let units = 'us';
if (navigation.units() === UNITS.metric) units = 'si';
let forecast;
try {
2020-10-29 21:44:28 +00:00
forecast = await utils.fetch.json(weatherParameters.forecast, {
2020-09-04 18:02:20 +00:00
data: {
units,
},
});
} catch (e) {
console.error('Unable to get extended forecast');
2020-09-23 16:49:15 +00:00
console.error(e.status, e.responseJSON);
2020-09-09 19:29:03 +00:00
this.setStatus(STATUS.failed);
2020-09-04 18:02:20 +00:00
return;
}
// we only get here if there was no error above
2020-10-29 21:44:28 +00:00
this.data = ExtendedForecast.parse(forecast.properties.periods);
2020-09-09 19:29:03 +00:00
this.screenIndex = 0;
2020-09-17 21:34:38 +00:00
this.setStatus(STATUS.loaded);
2020-09-04 18:02:20 +00:00
}
// the api provides the forecast in 12 hour increments, flatten to day increments with high and low temperatures
2020-10-29 21:44:28 +00:00
static parse(fullForecast) {
2020-09-04 18:02:20 +00:00
// create a list of days starting with today
2020-10-29 21:44:28 +00:00
const Days = [0, 1, 2, 3, 4, 5, 6];
2020-09-04 18:02:20 +00:00
2020-10-29 21:44:28 +00:00
const dates = Days.map((shift) => {
const date = luxon.DateTime.local().startOf('day').plus({ days: shift });
return date.toLocaleString({ weekday: 'short' });
2020-09-04 18:02:20 +00:00
});
// track the destination forecast index
let destIndex = 0;
const forecast = [];
2020-10-29 21:44:28 +00:00
fullForecast.forEach((period) => {
2020-09-04 18:02:20 +00:00
// create the destination object if necessary
2020-10-29 21:44:28 +00:00
if (!forecast[destIndex]) {
forecast.push({
dayName: '', low: undefined, high: undefined, text: undefined, icon: undefined,
});
}
2020-09-04 18:02:20 +00:00
// get the object to modify/populate
const fDay = forecast[destIndex];
// high temperature will always be last in the source array so it will overwrite the low values assigned below
fDay.icon = icons.getWeatherIconFromIconLink(period.icon);
2020-10-29 21:44:28 +00:00
fDay.text = ExtendedForecast.shortenExtendedForecastText(period.shortForecast);
2020-09-04 18:02:20 +00:00
fDay.dayName = dates[destIndex];
2020-09-23 16:49:15 +00:00
// preload the icon
utils.image.preload(fDay.icon);
2020-09-04 18:02:20 +00:00
if (period.isDaytime) {
// day time is the high temperature
fDay.high = period.temperature;
2020-10-29 21:44:28 +00:00
destIndex += 1;
2020-09-04 18:02:20 +00:00
} else {
// low temperature
fDay.low = period.temperature;
}
});
return forecast;
}
2020-10-29 21:44:28 +00:00
static shortenExtendedForecastText(long) {
2022-08-05 03:03:59 +00:00
const regexList = [
[/ and /ig, ' '],
[/Slight /ig, ''],
[/Chance /ig, ''],
[/Very /ig, ''],
[/Patchy /ig, ''],
[/Areas /ig, ''],
[/Dense /ig, ''],
[/Thunderstorm/g, 'T\'Storm'],
];
// run all regexes
const short = regexList.reduce((working, [regex, replace]) => working.replace(regex, replace), long);
2020-09-04 18:02:20 +00:00
let conditions = short.split(' ');
if (short.indexOf('then') !== -1) {
conditions = short.split(' then ');
conditions = conditions[1].split(' ');
}
let short1 = conditions[0].substr(0, 10);
let short2 = '';
if (conditions[1]) {
if (!short1.endsWith('.')) {
short2 = conditions[1].substr(0, 10);
} else {
short1 = short1.replace(/\./, '');
}
if (short2 === 'Blowing') {
short2 = '';
}
}
2022-08-05 03:03:59 +00:00
let result = short1;
2020-09-04 18:02:20 +00:00
if (short2 !== '') {
2022-08-05 03:03:59 +00:00
result += ` ${short2}`;
2020-09-04 18:02:20 +00:00
}
2022-08-05 03:03:59 +00:00
return result;
2020-09-04 18:02:20 +00:00
}
async drawCanvas() {
super.drawCanvas();
// determine bounds
// grab the first three or second set of three array elements
2020-10-29 21:44:28 +00:00
const forecast = this.data.slice(0 + 3 * this.screenIndex, 3 + this.screenIndex * 3);
2020-09-04 18:02:20 +00:00
2022-08-05 03:03:59 +00:00
// create each day template
const days = forecast.map((Day) => {
const fill = {};
fill.date = Day.dayName;
2020-09-04 18:02:20 +00:00
2020-10-29 21:44:28 +00:00
let { low } = Day;
2020-09-04 18:02:20 +00:00
if (low !== undefined) {
2020-10-29 21:44:28 +00:00
if (navigation.units() === UNITS.metric) low = utils.units.fahrenheitToCelsius(low);
2022-08-05 03:03:59 +00:00
fill['value-lo'] = Math.round(low);
2020-09-04 18:02:20 +00:00
}
2020-10-29 21:44:28 +00:00
let { high } = Day;
if (navigation.units() === UNITS.metric) high = utils.units.fahrenheitToCelsius(high);
2022-08-05 03:03:59 +00:00
fill['value-hi'] = Math.round(high);
fill.condition = Day.text;
2020-09-04 18:02:20 +00:00
// draw the icon
2022-08-05 03:03:59 +00:00
fill.icon = { type: 'img', src: Day.icon };
// return the filled template
return this.fillTemplate('day', fill);
});
2020-09-04 18:02:20 +00:00
2022-08-05 03:03:59 +00:00
// empty and update the container
const dayContainer = this.elem.querySelector('.day-container');
dayContainer.innerHTML = '';
dayContainer.append(...days);
2020-09-04 18:02:20 +00:00
this.finishDraw();
}
2020-10-29 21:44:28 +00:00
}