@yachteye/signalk-weather-plugin
Version:
YachtEye open-weather plugin
598 lines (597 loc) • 30.4 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const _1 = require(".");
const openweather_1 = __importDefault(require("./openweather"));
const utils = require('@signalk/nmea0183-utilities');
/**
* Update the SignalK graph with the latest weather data.
*/
class Updater {
constructor(app, pluginId) {
/** Path for the local current temperature. */
this.pathCurrentTemp = 'resources.weather.actual.current.temperature';
/** Path for the sunrise/sunset values of today, provided by the Makkah plugin. */
this.pathSunTimes = 'resources.astronomical.salahTimes';
/** OpenWeather source value for a SK Update. */
this.source = { label: 'OpenWeather', type: 'web' };
this.app = app;
this.pluginId = pluginId;
}
/**
* Handle the current weather information: condition, temperature, wind speed/direction, sunrise/sunset.
* Also included: city name/ID, country-code, perceived temperature, air pressure, humidity, cloudiness, precipitation.
* @param w Current weather information (metric units assumed).
* @param pathStart Path to store the data, default is 'resources.weather.actual' for the ship's position.
* @returns void
*/
handleCurrentWeatherResult(w, pathStart = 'resources.weather.actual') {
const isLocal = pathStart.includes('.weather.actual');
const timestampISOString = new Date(w.dt * 1000).toISOString();
this.app.debug(`handleCurrentWeatherResult(): age of current weather data= ${(((Date.now() / 1000) - w.dt) / 60).toFixed(1)} minutes.`);
const latestTimestamp = this.app.getSelfPath(this.pathCurrentTemp);
if (isLocal && latestTimestamp !== undefined && latestTimestamp.timestamp === timestampISOString) {
this.app.debug(`handleCurrentWeatherResult(): no new weather data available ${latestTimestamp}.`);
return;
}
const delta = {
context: 'vessels.self',
updates: [],
};
if (w.name && w.id !== undefined && w.sys && w.sys.country) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [
{ path: `${pathStart}.cityName`, value: w.name },
{ path: `${pathStart}.cityId`, value: w.id },
{ path: `${pathStart}.countryCode`, value: w.sys.country },
],
};
delta.updates.push(update);
}
// For consistency we prefer to use the sun times of the Makkah calculation for the local weather.
const sunTimes = this.app.getSelfPath(this.pathSunTimes);
if (isLocal && sunTimes && sunTimes.value !== null && typeof sunTimes.value === 'object') {
if ('sunrise' in sunTimes.value) {
const update = {
source: { label: `YachtEye - Makkah Pointer` },
timestamp: sunTimes.timestamp,
values: [
{ path: `${pathStart}.sunrise`, value: sunTimes.value.sunrise }, // @todo: remove in the future.
{ path: `${pathStart}.current.sunrise`, value: sunTimes.value.sunrise },
],
};
delta.updates.push(update);
// this.app.debug(`Sunrise times: MP=${sunTimes.value.sunrise} / OW=${new Date(w.sys.sunrise * 1000).toISOString()}`);
}
if ('sunset' in sunTimes.value) {
const update = {
source: { label: `YachtEye - Makkah Pointer` },
timestamp: sunTimes.timestamp,
values: [
{ path: `${pathStart}.sunset`, value: sunTimes.value.sunset }, // @todo: remove in the future.
{ path: `${pathStart}.current.sunset`, value: sunTimes.value.sunset },
],
};
delta.updates.push(update);
// this.app.debug(`Sunset times: MP=${sunTimes.value.sunset} / OW=${new Date(w.sys.sunset * 1000).toISOString()}`);
}
}
else {
// Use the sunrise/sunset values of Open Weather.
const tzOffsetSeconds = this.app.getSelfPath('environment.time.timezoneOffset');
let tzMinutes = 0;
if (isLocal && tzOffsetSeconds !== undefined && typeof tzOffsetSeconds.value === 'number') {
tzMinutes = tzOffsetSeconds.value / 60;
}
else if (w.timezone !== undefined) {
tzMinutes = w.timezone / 60;
}
if (w.sys.sunrise > 0 && w.sys.sunset > 0) {
const owSunrise = new Date(w.sys.sunrise * 1000);
const owSunset = new Date(w.sys.sunset * 1000);
owSunrise.setUTCMinutes(owSunrise.getUTCMinutes() + tzMinutes);
owSunset.setUTCMinutes(owSunset.getUTCMinutes() + tzMinutes);
const update = {
source: this.source,
timestamp: timestampISOString,
values: [
{ path: `${pathStart}.current.sunrise`, value: `${owSunrise.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", timeZone: "UTC" })}` },
{ path: `${pathStart}.current.sunset`, value: `${owSunset.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", timeZone: "UTC" })}` },
],
};
delta.updates.push(update);
}
}
if (w.main.temp !== undefined) {
const temperature = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: `${pathStart}.current.temperature`, value: (0, _1.roundToDecimalPlaces)(utils.transform(w.main.temp, 'C', 'K'), 2) }],
};
delta.updates.push(temperature);
}
if (w.main.feels_like !== undefined) {
const temperature = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: `${pathStart}.current.perceivedTemperature`, value: (0, _1.roundToDecimalPlaces)(utils.transform(w.main.feels_like, 'C', 'K'), 2) }],
};
delta.updates.push(temperature);
}
// Not used by YE atm, but save it anyway.
if (w.main.pressure !== undefined) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: `${pathStart}.current.airpressure`, value: hPaToPa(w.main.pressure) }],
};
delta.updates.push(update);
}
// Not used by YE atm, but save it anyway.
if (w.clouds && w.clouds.all !== undefined) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: `${pathStart}.current.cloudiness`, value: w.clouds.all / 100 }],
};
delta.updates.push(update);
}
// Merge rain and snow.
let precipitation1h = 0;
precipitation1h += w.rain && w.rain['1h'] !== undefined ? w.rain['1h'] : 0;
precipitation1h += w.snow && w.snow['1h'] !== undefined ? w.snow['1h'] : 0;
let precipitation3h = 0;
precipitation3h += w.rain && w.rain['3h'] !== undefined ? w.rain['3h'] : 0;
precipitation3h += w.snow && w.snow['3h'] !== undefined ? w.snow['3h'] : 0;
// Not used by YE atm.
// const precipitation: ISignalKUpdate = {
// source: this.source,
// timestamp: timestampISOString,
// values: [
// { path: `${pathStart}.current.precipitationLastHour`, value: precipitation1h },
// { path: `${pathStart}.current.precipitationLast3Hours`, value: precipitation3h },
// ],
// };
// delta.updates.push(precipitation);
// Not used by YE atm, but save it anyway.
if (w.main.humidity !== undefined) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: `${pathStart}.current.humidity`, value: w.main.humidity / 100 }],
};
delta.updates.push(update);
}
if (w.wind !== undefined && w.wind.speed !== undefined && w.wind.deg !== undefined) {
const wind = {
source: this.source,
timestamp: timestampISOString,
values: [
{ path: `${pathStart}.current.windspeed`, value: w.wind.speed },
{ path: `${pathStart}.current.winddirection`, value: (0, _1.roundToDecimalPlaces)(utils.transform(w.wind.deg, 'deg', 'rad'), 5) },
],
};
delta.updates.push(wind);
}
if (w.weather && w.weather.length >= 1) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: `${pathStart}.current.conditionId`, value: w.weather[0].id }],
};
delta.updates.push(update);
}
this.app.handleMessage(this.pluginId, delta);
}
/**
* Daily forecast: weather condition, temperature, min/max temperature and wind speed/direction.
* @param fc Daily forecast data (standard units assumed).
* @param pathStart Path to use, default is 'resources.weather.actual.forecast'.
*/
handleDailyForecastResult(fc, pathStart = 'resources.weather.actual.forecast') {
this.app.debug(`handleDailyForecastResult(): location=${fc.city.name}, ${fc.city.country}, #days=${fc.cnt}.`);
const timestampISOString = new Date().toISOString();
const delta = {
context: 'vessels.self',
updates: [],
};
for (let idx = 0; idx < fc.cnt; idx++) {
const day = fc.list[idx];
const fcDate = new Date(day.dt * 1000);
const path = this.datePath(fcDate, pathStart);
// const update: ISignalKUpdate = {
// source: this.source,
// values: [{ path: path + 'timeOfForecast', value: fcDate.toISOString() }],
// };
// delta.updates.push(update);
if (day.weather && day.weather.length >= 1) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path + 'conditionId', value: day.weather[0].id }],
};
delta.updates.push(update);
}
// if (full && day.temp.night !== undefined) {
// const update: ISignalKUpdate = {
// source: this.source,
// values: [{ path: path + 'temperatureNight', value: day.temp.night }],
// };
// delta.updates.push(update);
// }
if (day.temp.day !== undefined) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path + 'temperature', value: day.temp.day }],
};
delta.updates.push(update);
}
// if (full && day.temp.day !== undefined) {
// const update: ISignalKUpdate = {
// source: this.source,
// values: [{ path: path + 'temperatureNoon', value: day.temp.day }],
// };
// delta.updates.push(update);
// }
// if (full && day.temp.eve !== undefined) {
// const update: ISignalKUpdate = {
// source: this.source,
// values: [{ path: path + 'temperatureEvening', value: day.temp.eve }],
// };
// delta.updates.push(update);
// }
if (day.temp.min !== undefined && day.temp.max !== undefined) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [
{ path: path + 'temperatureMin', value: day.temp.min },
{ path: path + 'temperatureMax', value: day.temp.max },
],
};
delta.updates.push(update);
}
if (day.speed !== undefined && day.deg !== undefined) {
const update = {
source: this.source,
timestamp: timestampISOString,
values: [
{ path: path + 'windspeed', value: day.speed },
{ path: path + 'winddirection', value: (0, _1.roundToDecimalPlaces)(utils.transform(day.deg, 'deg', 'rad'), 5) },
],
};
delta.updates.push(update);
}
// Add morning, afternoon and evening temperatures for today and tomorrow.
// We would also like to have the condition for morning/afternoon/evening, but that is not available.
// if (idx === 0 || idx === 1) {
// if (day.temp.morn !== undefined) {
// const update: ISignalKUpdate = {
// source: this.source,
// values: [{ path: path + 'morning.temperature', value: day.temp.morn }], // Used by the iOS apps, but not critical.
// };
// delta.updates.push(update);
// }
// if (day.temp.day !== undefined) {
// const update: ISignalKUpdate = {
// source: this.source,
// values: [{ path: path + 'afternoon.temperature', value: day.temp.day }], // Used by the iOS apps, but not critical.
// };
// delta.updates.push(update);
// }
// if (day.temp.eve !== undefined) {
// const update: ISignalKUpdate = {
// source: this.source,
// values: [{ path: path + 'evening.temperature', value: day.temp.eve }], // Used by the iOS apps, but not critical.
// };
// delta.updates.push(update);
// }
// }
}
this.app.handleMessage(this.pluginId, delta);
}
/**
* Handle 3 Hour forecast data: we support temperature, weather condition and wind speed/direction.
* @param fc Three-hourly forecast data (standard units assumed).
* @param pathStart The first part of the path to use in the graph.
* @param interpolateHourly True to interpolate hourly values (future).
*/
handleThreeHourForecastResult(fc, pathStart, interpolateHourly) {
this.app.debug(`handleThreeHourForecastResult(): location=${fc.city.name}, ${fc.city.country}, #elements=${fc.cnt}.`);
if (interpolateHourly === true) {
throw new Error('handleThreeHourForecastResult(): Hourly interpolation is not yet supported.');
}
const timestampISOString = new Date().toISOString();
const delta = {
context: 'vessels.self',
updates: [],
};
for (let idx = 0; idx < fc.cnt; idx++) {
const item = fc.list[idx];
const forecastHour = new Date(item.dt * 1000);
if (item.main.temp !== undefined) {
const path = this.makeHourPath(pathStart, forecastHour, 'temperature');
const update = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path, value: item.main.temp }],
};
delta.updates.push(update);
}
if (item.weather && item.weather.length >= 1) {
const path = this.makeHourPath(pathStart, forecastHour, 'conditionId');
const update = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path, value: item.weather[0].id }],
};
delta.updates.push(update);
}
if (item.wind.deg !== undefined && item.wind.speed !== undefined) {
const pathDirection = this.makeHourPath(pathStart, forecastHour, 'winddirection');
const pathSpeed = this.makeHourPath(pathStart, forecastHour, 'windspeed');
const update = {
source: this.source,
timestamp: timestampISOString,
values: [
{ path: pathSpeed, value: item.wind.speed },
{ path: pathDirection, value: (0, _1.roundToDecimalPlaces)(utils.transform(item.wind.deg, 'deg', 'rad'), 5) }
],
};
delta.updates.push(update);
}
// let precipitation3h = 0;
// precipitation3h += item.rain && item.rain['3h'] !== undefined ? item.rain['3h'] : 0;
// precipitation3h += item.snow && item.snow['3h'] !== undefined ? item.snow['3h'] : 0;
// const path = this.makeHourPath(pathStart, forecastHour, 'precipitationLast3Hours');
// const precipitation: ISignalKUpdate = {
// source: this.source,
// timestamp: timestampISOString,
// values: [
// { path: path, value: precipitation3h },
// ],
// };
// delta.updates.push(precipitation);
}
// @ts-ignore
if (interpolateHourly === true) {
for (let idx = 1; idx < fc.cnt; idx++) {
const previousItem = fc.list[idx - 1];
const item = fc.list[idx];
const forecastHour1 = new Date((previousItem.dt + 60 * 60) * 1000);
const forecastHour2 = new Date((previousItem.dt + 2 * 60 * 60) * 1000);
if (item.main.temp !== undefined && previousItem.main.temp !== undefined) {
const temp1 = this.interpolateNumber(item.main.temp, previousItem.main.temp, 1, 2);
const path1 = this.makeHourPath(pathStart, forecastHour1, 'temperature');
const update1 = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path1, value: (0, _1.roundToDecimalPlaces)(temp1, 2) }],
};
delta.updates.push(update1);
const temp2 = this.interpolateNumber(item.main.temp, previousItem.main.temp, 2, 1);
const path2 = this.makeHourPath(pathStart, forecastHour2, 'temperature');
const update2 = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path2, value: (0, _1.roundToDecimalPlaces)(temp2, 2) }],
};
delta.updates.push(update2);
}
if (item.weather && item.weather.length >= 1 && previousItem.weather && previousItem.weather.length >= 1) {
const condition1 = this.interpolateWeatherCondition(previousItem.weather[0].id, item.weather[0].id, 2, 1);
const path1 = this.makeHourPath(pathStart, forecastHour1, 'conditionId');
const update1 = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path1, value: condition1 }],
};
delta.updates.push(update1);
const condition2 = this.interpolateWeatherCondition(previousItem.weather[0].id, item.weather[0].id, 1, 2);
const path2 = this.makeHourPath(pathStart, forecastHour2, 'conditionId');
const update2 = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path2, value: condition2 }],
};
delta.updates.push(update2);
}
if (item.wind !== undefined && previousItem.wind !== undefined) {
if (item.wind.speed !== undefined && previousItem.wind.speed !== undefined) {
const speed1 = this.interpolateNumber(item.wind.speed, previousItem.wind.speed, 1, 2);
const path1 = this.makeHourPath(pathStart, forecastHour1, 'windspeed');
const update1 = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path1, value: (0, _1.roundToDecimalPlaces)(speed1, 2) }],
};
delta.updates.push(update1);
const speed2 = this.interpolateNumber(item.wind.speed, previousItem.wind.speed, 2, 1);
const path2 = this.makeHourPath(pathStart, forecastHour2, 'windspeed');
const update2 = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path2, value: (0, _1.roundToDecimalPlaces)(speed2, 2) }],
};
delta.updates.push(update2);
}
if (item.wind.deg !== undefined && previousItem.wind.deg !== undefined) {
const deg1 = this.interpolateAngle(item.wind.deg, previousItem.wind.deg, 1, 2);
const path1 = this.makeHourPath(pathStart, forecastHour1, 'winddirection');
const update1 = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path1, value: (0, _1.roundToDecimalPlaces)(utils.transform(deg1, 'deg', 'rad'), 5) }],
};
delta.updates.push(update1);
const deg2 = this.interpolateAngle(item.wind.deg, previousItem.wind.deg, 2, 1);
const path2 = this.makeHourPath(pathStart, forecastHour2, 'winddirection');
const update2 = {
source: this.source,
timestamp: timestampISOString,
values: [{ path: path2, value: (0, _1.roundToDecimalPlaces)(utils.transform(deg2, 'deg', 'rad'), 5) }],
};
delta.updates.push(update2);
this.app.debug(`Wind interpolation: ${previousItem.dt_txt} ${previousItem.wind.deg} ${deg1.toFixed(4)} ${deg2.toFixed(4)} ${item.wind.deg}`);
}
}
// Interpolate precipitation. For now we just distribute the 3hr value equally over three hours.
let precipitation3h = 0;
precipitation3h += item.rain && item.rain['3h'] !== undefined ? item.rain['3h'] : 0;
precipitation3h += item.snow && item.snow['3h'] !== undefined ? item.snow['3h'] : 0;
const path1 = this.makeHourPath(pathStart, forecastHour1, 'precipitationLastHour');
const update1 = {
source: this.source,
values: [{ path: path1, value: (0, _1.roundToDecimalPlaces)(precipitation3h / 3, 2) }],
};
delta.updates.push(update1);
const path2 = this.makeHourPath(pathStart, forecastHour2, 'precipitationLastHour');
const update2 = {
source: this.source,
values: [{ path: path2, value: (0, _1.roundToDecimalPlaces)(precipitation3h / 3, 2) }],
};
delta.updates.push(update2);
const path3 = this.makeHourPath(pathStart, new Date(item.dt * 1000), 'precipitationLastHour');
const update3 = {
source: this.source,
values: [{ path: path3, value: (0, _1.roundToDecimalPlaces)(precipitation3h / 3, 2) }],
};
delta.updates.push(update3);
}
}
this.app.handleMessage(this.pluginId, delta);
}
/**
* Contruct a Path as: Prefix + Year + Month + Day. E.g. 'resources.weather.actual.forecast.2025-01-23.'.
* @param dt Date to use.
* @param prefix Prefix to use.
* @returns Path value.
*/
datePath(dt, prefix) {
const path = `${prefix}.${dt.getUTCFullYear().toString(10)}-${(dt.getUTCMonth() + 1).toString(10).padStart(2, '0')}-${dt.getUTCDate().toString(10).padStart(2, '0')}.`;
return path;
}
makeDatePath(prefix, date, postfix) {
return `${this.datePath(date, prefix)}${postfix}`;
}
/**
* Create a path with yyyy-mm-ddThh:mm, e.g. 'resources.weather.actual.forecast.2023-08-31T12:00.conditionId'.
* @param prefix Prefix to use, e.g. 'resources.weather.actual.forecast'.
* @param date The date to use (the UTC values are to be used).
* @param postfix Postfix to use, e.g. 'conditionId'.
* @returns The path.
*/
makeHourPath(prefix, date, postfix) {
const monthString = (date.getUTCMonth() + 1).toString(10).padStart(2, '0');
const dayString = date.getUTCDate().toString(10).padStart(2, '0');
const hourString = date.getUTCHours().toString(10).padStart(2, '0');
const path = `${prefix}.${date.getUTCFullYear().toString(10)}-${monthString}-${dayString}T${hourString}:00.${postfix}`;
return path;
}
/**
* Interpolate a number value.
* @param number1 Number value 1.
* @param number2 Number value 2.
* @param w1 Weight for Number value 1.
* @param w2 Weight for Number value 2.
* @returns Interpolated value.
*/
interpolateNumber(number1, number2, w1, w2) {
const temp = (number1 * w1 + number2 * w2) / (w1 + w2);
return temp;
}
/**
* Interpolate angle values.
* @param angle1 angle in degrees
* @param angle2 angle in degrees
* @param w1 Weight for angle1.
* @param w2 Weight for angle2.
* @returns Interpolated angle in degrees.
*/
interpolateAngle(angle1, angle2, w1, w2) {
// this.app.debug(`interpolateAngle(): ${angle1} * ${w1}, ${angle2} * ${w2}`);
angle1 *= Math.PI / 180;
angle2 *= Math.PI / 180;
const sin1 = Math.sin(angle1);
const cos1 = Math.cos(angle1);
const sin2 = Math.sin(angle2);
const cos2 = Math.cos(angle2);
const averageSine = (sin1 * w1 + sin2 * w2) / (w1 + w2);
const averageCosine = (cos1 * w1 + cos2 * w2) / (w1 + w2);
if (Math.abs(averageSine) < 0.1 && Math.abs(averageCosine) < 0.1) {
this.app.debug(`Could not interpolate angles ${angle1} ${angle2}`);
return 0;
}
const angle = Math.atan2(averageSine, averageCosine) * 180.0 / Math.PI;
return angle < 0.0 ? angle + 360 : angle;
}
interpolateWeatherCondition(c1, c2, w1, w2) {
if (c1 === c2) {
return c1;
}
// Storm.
if (openweather_1.default.thunderstormConditionValues.includes(c1) && openweather_1.default.thunderstormConditionValues.includes(c2)) {
const c = Math.round((w1 * c1 + w2 * c2) / (w1 + w2));
if (openweather_1.default.thunderstormConditionValues.includes(c)) {
return c;
}
return w1 > w2 ? c1 : c2;
}
// Drizzle.
if (openweather_1.default.drizzleConditionValues.includes(c1) && openweather_1.default.drizzleConditionValues.includes(c2)) {
const c = Math.round((w1 * c1 + w2 * c2) / (w1 + w2));
if (openweather_1.default.drizzleConditionValues.includes(c)) {
return c;
}
return w1 > w2 ? c1 : c2;
}
// Rain.
if (openweather_1.default.rainConditionValues.includes(c1) && openweather_1.default.rainConditionValues.includes(c2)) {
const c = Math.round((w1 * c1 + w2 * c2) / (w1 + w2));
if (openweather_1.default.rainConditionValues.includes(c)) {
return c;
}
return w1 > w2 ? c1 : c2;
}
// Snow.
if (openweather_1.default.snowConditionValues.includes(c1) && openweather_1.default.snowConditionValues.includes(c2)) {
const c = Math.round((w1 * c1 + w2 * c2) / (w1 + w2));
if (openweather_1.default.snowConditionValues.includes(c)) {
return c;
}
return w1 > w2 ? c1 : c2;
}
// Atmosphere.
if (openweather_1.default.atmosphereConditionValues.includes(c1) && openweather_1.default.atmosphereConditionValues.includes(c2)) {
// Interpolation is not sensible for this condition.
}
// Clear and Clouds.
if (openweather_1.default.clearAndCloudConditionValues.includes(c1) && openweather_1.default.clearAndCloudConditionValues.includes(c2)) {
const c = Math.round((w1 * c1 + w2 * c2) / (w1 + w2));
if (openweather_1.default.clearAndCloudConditionValues.includes(c)) {
return c;
}
return w1 > w2 ? c1 : c2;
}
return w1 > w2 ? c1 : c2;
}
}
exports.default = Updater;
/**
* Convert hectoPascal to Pascal.
* @param pressure pressure in hPa.
* @returns
*/
const hPaToPa = (pressure) => {
// @todo A PR for this functionality in utils is in progress.
const pressurePascal = utils.transform(pressure, 'hPa', 'Pa');
if (pressurePascal === pressure) {
return (pressure * 100.0);
}
return pressurePascal;
};