ws4kp-linhanced/server/scripts/modules/hourly.mjs

161 lines
5.4 KiB
JavaScript
Raw Normal View History

2020-10-20 20:04:51 -05:00
// hourly forecast list
2022-11-22 16:19:10 -06:00
import STATUS from './status.mjs';
import { DateTime } from '../vendor/auto/luxon.mjs';
2025-10-22 00:22:29 +00:00
import { temperature as temperatureUnit, windSpeed as windUnit } from './utils/units.mjs';
2022-11-22 16:19:10 -06:00
import { directionToNSEW } from './utils/calc.mjs';
2022-11-22 16:29:10 -06:00
import WeatherDisplay from './weatherdisplay.mjs';
import { registerDisplay, timeZone } from './navigation.mjs';
import calculateScrollTiming from './utils/scroll-timing.mjs';
import { getSmallIconFromWmoCode } from './icons.mjs';
2020-10-20 20:04:51 -05:00
class Hourly extends WeatherDisplay {
constructor(navId, elemId, defaultActive) {
2022-11-21 21:50:22 -06:00
super(navId, elemId, 'Hourly Forecast', defaultActive);
this.scrollCache = {
displayHeight: 0,
contentHeight: 0,
maxOffset: 0,
hourlyLines: null,
};
2020-10-20 20:04:51 -05:00
}
2025-04-02 11:10:58 -05:00
async getData(weatherParameters, refresh) {
const superResponse = super.getData(weatherParameters, refresh);
this.data = parseForecast(this.weatherParameters);
if (!this.data?.length) {
if (this.isEnabled) this.setStatus(STATUS.failed);
this.getDataCallback(undefined);
return;
}
this.getDataCallback();
if (!superResponse) return;
this.setStatus(STATUS.loaded);
this.drawLongCanvas();
2020-10-20 20:04:51 -05:00
}
2020-10-29 16:44:28 -05:00
async drawLongCanvas() {
2022-07-29 16:12:42 -05:00
const list = this.elem.querySelector('.hourly-lines');
list.innerHTML = '';
2020-10-20 20:04:51 -05:00
const startingHour = DateTime.local().setZone(timeZone());
const shortData = this.data.slice(0, 24);
const lines = shortData.map((data, index) => {
2020-10-29 16:44:28 -05:00
const hour = startingHour.plus({ hours: index });
const fillValues = {
hour: hour.toLocaleString({ weekday: 'short', hour: 'numeric' }),
temp: data.temperature.toString().padStart(3),
like: data.apparentTemperature.toString().padStart(3),
wind: data.windSpeed > 0
? data.windDirection + (Array(6 - data.windDirection.length - Math.round(data.windSpeed).toString().length).join(' ')) + Math.round(data.windSpeed).toString()
: 'Calm',
icon: { type: 'img', src: data.icon },
};
2020-10-20 20:04:51 -05:00
const filledRow = this.fillTemplate('hourly-row', fillValues);
if (data.apparentTemperature < data.temperature) {
filledRow.querySelector('.like').classList.add('wind-chill');
} else if (data.apparentTemperature > data.temperature) {
filledRow.querySelector('.like').classList.add('heat-index');
}
return filledRow;
2022-07-29 16:12:42 -05:00
});
2020-10-20 20:04:51 -05:00
2022-07-29 16:12:42 -05:00
list.append(...lines);
this.setTiming(list);
2022-07-29 16:12:42 -05:00
}
2020-10-20 20:04:51 -05:00
2022-07-29 16:12:42 -05:00
drawCanvas() {
super.drawCanvas();
2020-10-20 20:04:51 -05:00
this.finishDraw();
}
2022-07-29 16:12:42 -05:00
showCanvas() {
this.drawCanvas();
2020-10-20 20:04:51 -05:00
super.showCanvas();
}
screenIndexChange() {
this.baseCountChange(this.navBaseCount);
}
baseCountChange(count) {
const hourlyLines = this.elem.querySelector('.hourly-lines');
if (!hourlyLines) return;
if (this.scrollCache.hourlyLines !== hourlyLines || this.scrollCache.displayHeight === 0) {
this.scrollCache.displayHeight = this.elem.querySelector('.main').offsetHeight;
this.scrollCache.contentHeight = hourlyLines.offsetHeight;
this.scrollCache.maxOffset = Math.max(0, this.scrollCache.contentHeight - this.scrollCache.displayHeight);
this.scrollCache.hourlyLines = hourlyLines;
hourlyLines.style.willChange = 'transform';
hourlyLines.style.backfaceVisibility = 'hidden';
}
let offsetY = Math.min(this.scrollCache.maxOffset, (count - this.scrollTiming.initialCounts) * this.scrollTiming.pixelsPerCount);
2020-10-20 20:04:51 -05:00
if (offsetY < 0) offsetY = 0;
hourlyLines.style.transform = `translateY(-${Math.round(offsetY)}px)`;
2020-10-20 20:04:51 -05:00
}
async getHourlyData(stillWaiting) {
2022-12-12 13:53:33 -06:00
if (stillWaiting) this.stillWaitingCallbacks.push(stillWaiting);
this.setAutoReload();
2022-12-07 15:36:02 -06:00
return new Promise((resolve) => {
if (this.data) resolve(this.data);
this.getDataCallbacks.push(() => resolve(this.data));
});
}
setTiming(list) {
const container = this.elem.querySelector('.main');
const timingConfig = calculateScrollTiming(list, container);
this.timing.baseDelay = timingConfig.baseDelay;
this.timing.delay = timingConfig.delay;
this.scrollTiming = timingConfig.scrollTiming;
this.calcNavTiming();
}
2020-10-29 16:44:28 -05:00
}
2022-11-22 16:19:10 -06:00
const parseForecast = (weatherParameters) => {
2025-02-23 23:29:39 -06:00
const temperatureConverter = temperatureUnit();
2025-10-22 00:22:29 +00:00
const windConverter = windUnit();
const currentTime = new Date();
const todayKey = currentTime.toLocaleDateString('en-CA', { timeZone: weatherParameters.timeZone });
const tomorrowKey = DateTime.fromISO(todayKey).plus({ days: 1 }).toISODate();
const availableTimes = [
...(weatherParameters.forecast[todayKey]?.hours ?? []),
...(weatherParameters.forecast[tomorrowKey]?.hours ?? []),
];
if (!availableTimes.length) return [];
let closestIndex = 0;
let minDiff = Math.abs(new Date(availableTimes[0].time) - currentTime);
availableTimes.forEach((entry, index) => {
const diff = Math.abs(new Date(entry.time) - currentTime);
if (diff < minDiff) {
minDiff = diff;
closestIndex = index;
}
});
2025-02-23 23:29:39 -06:00
return availableTimes.slice(closestIndex).map((hour) => ({
temperature: temperatureConverter(hour.temperature_2m),
2025-02-23 23:29:39 -06:00
temperatureUnit: temperatureConverter.units,
apparentTemperature: temperatureConverter(hour.apparent_temperature),
windSpeed: windConverter(hour.wind_speed_10m),
2025-10-22 00:22:29 +00:00
windUnit: windConverter.units,
windDirection: directionToNSEW(hour.wind_direction_10m ?? 0),
probabilityOfPrecipitation: Math.round(hour.precipitation_probability ?? 0),
skyCover: Math.round(hour.cloud_cover ?? 0),
icon: getSmallIconFromWmoCode(hour.weather_code, Boolean(hour.is_day)),
dewpoint: temperatureConverter(hour.dew_point_2m),
2022-12-09 13:51:51 -06:00
}));
};
const display = new Hourly(3, 'hourly', true);
2022-12-07 15:36:02 -06:00
registerDisplay(display);
export default display.getHourlyData.bind(display);