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

161 lines
6 KiB
JavaScript
Raw Normal View History

2020-09-04 13:02:20 -05:00
// current weather conditions display
2022-11-22 16:19:10 -06:00
import STATUS from './status.mjs';
2025-05-29 08:30:01 -05:00
import { preloadImg } from './utils/image.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';
2022-12-06 16:14:56 -06:00
import { registerDisplay } from './navigation.mjs';
2022-12-06 16:25:28 -06:00
import {
temperature, windSpeed, pressure, distanceKilometers, distanceMeters,
2022-12-06 16:25:28 -06:00
} from './utils/units.mjs';
import { getConditionText } from './utils/weather.mjs';
import { getLargeIconFromWmoCode } from './icons.mjs';
2020-09-04 13:02:20 -05:00
class CurrentWeather extends WeatherDisplay {
2020-10-29 16:44:28 -05:00
constructor(navId, elemId) {
2022-11-21 21:50:22 -06:00
super(navId, elemId, 'Current Conditions', true);
2020-09-04 13:02:20 -05:00
}
2025-04-02 20:52:33 -05:00
async getData(weatherParameters, refresh) {
const superResult = super.getData(weatherParameters, refresh);
this.data = parseData(this.weatherParameters);
2020-09-04 13:02:20 -05:00
if (!this.data) {
2022-12-14 13:08:49 -06:00
if (this.isEnabled) this.setStatus(STATUS.failed);
2022-12-08 14:41:15 -06:00
this.getDataCallback(undefined);
2020-09-04 13:02:20 -05:00
return;
}
2020-09-23 11:49:15 -05:00
2020-10-20 20:04:51 -05:00
this.getDataCallback();
2022-12-12 13:53:33 -06:00
if (!superResult) return;
this.timing.totalScreens = 1;
preloadImg(this.data.Icon);
2022-12-12 13:53:33 -06:00
this.setStatus(STATUS.loaded);
2020-09-04 13:02:20 -05:00
}
2020-10-29 16:44:28 -05:00
async drawCanvas() {
2020-09-24 22:44:51 -05:00
super.drawCanvas();
2020-09-04 13:02:20 -05:00
let condition = getConditionText(this.data.TextConditions);
2023-01-17 11:26:57 -06:00
if (condition.length > 15) {
condition = shortConditions(condition);
2020-09-04 13:02:20 -05:00
}
2022-08-02 21:39:27 -05:00
const wind = this.data.WindSpeed > 0
? this.data.WindDirection.padEnd(3, '') + this.data.WindSpeed.toString().padStart(3, ' ')
: 'Calm';
2025-07-15 22:00:33 -05:00
2023-01-17 11:26:57 -06:00
const fill = {
temp: this.data.Temperature + String.fromCharCode(176),
condition,
2025-06-27 15:35:15 -05:00
wind,
location: this.data.city.substr(0, 20),
2023-01-17 11:26:57 -06:00
humidity: `${this.data.Humidity}%`,
dewpoint: this.data.DewPoint + String.fromCharCode(176),
ceiling: this.data.Ceiling === 0 ? 'Unlimited' : `${this.data.Ceiling}${this.data.CeilingUnit}`,
visibility: `${this.data.Visibility}${this.data.VisibilityUnit}`,
2023-01-17 11:26:57 -06:00
pressure: `${this.data.Pressure} ${this.data.PressureDirection}`,
icon: { type: 'img', src: this.data.Icon },
};
if (this.data.WindGust > 0) fill['wind-gusts'] = `Gusts to ${this.data.WindGust}`;
2020-09-04 13:38:58 -05:00
2022-08-02 21:39:27 -05:00
const area = this.elem.querySelector('.main');
area.innerHTML = '';
area.append(this.fillTemplate('weather', fill));
2020-09-04 13:38:58 -05:00
2020-09-04 13:02:20 -05:00
this.finishDraw();
}
2022-12-12 13:53:33 -06:00
async getCurrentWeather(stillWaiting) {
this.setAutoReload();
2022-12-12 13:53:33 -06:00
if (stillWaiting) this.stillWaitingCallbacks.push(stillWaiting);
2020-10-20 20:04:51 -05:00
return new Promise((resolve) => {
2025-11-05 05:24:04 +00:00
if (this.data) resolve({ data: this.data, parameters: this.weatherParameters });
this.getDataCallbacks.push(() => resolve({ data: this.data, parameters: this.weatherParameters }));
2020-10-20 20:04:51 -05:00
});
2020-09-24 22:44:51 -05:00
}
2020-10-29 16:44:28 -05:00
}
2022-12-09 13:51:51 -06:00
const shortConditions = (_condition) => {
let condition = _condition;
condition = condition.replace(/Light/g, 'L');
condition = condition.replace(/Heavy/g, 'H');
condition = condition.replace(/Partly/g, 'P');
condition = condition.replace(/Mostly/g, 'M');
condition = condition.replace(/Thunderstorm/g, 'T\'storm');
condition = condition.replace(/ and /g, ' ');
condition = condition.replace(/Freezing Rain/g, 'Frz Rn');
condition = condition.replace(/Freezing/g, 'Frz');
condition = condition.replace(/ with /g, '/');
return condition;
};
const getCurrentWeatherByHourFromTime = (data) => {
const currentTime = new Date();
const currentDateKey = currentTime.toLocaleDateString('en-CA', { timeZone: data.timeZone });
const availableTimes = data.forecast[currentDateKey]?.hours ?? Object.values(data.forecast)[0]?.hours ?? [];
if (availableTimes.length === 0) return null;
2023-01-17 11:26:57 -06:00
const closestTime = availableTimes.reduce((prev, curr) => {
const prevDiff = Math.abs(new Date(prev.time) - currentTime);
const currDiff = Math.abs(new Date(curr.time) - currentTime);
return currDiff < prevDiff ? curr : prev;
});
const threeHoursAgo = new Date(currentTime.getTime() - 3 * 60 * 60 * 1000);
const previousHour = availableTimes
.filter((entry) => new Date(entry.time) <= currentTime && new Date(entry.time) >= threeHoursAgo)
.reduce((prev, curr) => {
const prevDiff = Math.abs(new Date(prev.time) - threeHoursAgo);
const currDiff = Math.abs(new Date(curr.time) - threeHoursAgo);
return currDiff < prevDiff ? curr : prev;
}, availableTimes[0]);
const diff = (closestTime.pressure_msl ?? 0) - (previousHour.pressure_msl ?? 0);
let pressureTrend = 'Steady';
if (diff > 0.5) pressureTrend = 'Rising';
if (diff < -0.5) pressureTrend = 'Falling';
closestTime.pressureTrend = pressureTrend;
closestTime.uv_index_max = data.forecast[currentDateKey]?.uv_index_max ?? closestTime.uv_index ?? 0;
return closestTime;
};
const parseData = (weatherParameters) => {
const currentForecast = getCurrentWeatherByHourFromTime(weatherParameters);
if (!currentForecast) return null;
const temperatureConverter = temperature();
const windConverter = windSpeed();
const pressureConverter = pressure();
const ceilingConverter = distanceMeters();
const visibilityConverter = distanceKilometers();
const ceilingMeters = Math.max(0, ((currentForecast.temperature_2m ?? 0) - (currentForecast.dew_point_2m ?? 0)) * 68);
return {
city: weatherParameters.city,
timeZone: weatherParameters.timeZone,
Temperature: temperatureConverter(currentForecast.temperature_2m),
TemperatureUnit: temperatureConverter.units,
DewPoint: temperatureConverter(currentForecast.dew_point_2m),
Ceiling: ceilingConverter(ceilingMeters),
CeilingUnit: ceilingConverter.units,
Visibility: visibilityConverter(currentForecast.visibility),
VisibilityUnit: visibilityConverter.units,
WindSpeed: windConverter(currentForecast.wind_speed_10m),
WindDirection: directionToNSEW(currentForecast.wind_direction_10m ?? 0),
Pressure: pressureConverter((currentForecast.pressure_msl ?? 0) * 100),
PressureDirection: currentForecast.pressureTrend,
Humidity: Math.round(currentForecast.relative_humidity_2m ?? 0),
WindGust: windConverter(currentForecast.wind_gusts_10m),
WindUnit: windConverter.units,
TextConditions: Number(currentForecast.weather_code ?? 0),
Icon: getLargeIconFromWmoCode(currentForecast.weather_code, Boolean(currentForecast.is_day)),
};
};
2022-12-14 16:28:33 -06:00
const display = new CurrentWeather(1, 'current-weather');
2022-12-06 16:14:56 -06:00
registerDisplay(display);
export default display.getCurrentWeather.bind(display);