mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-23 03:59:30 -07:00
Format and populate personal weather data
This commit is contained in:
@@ -1,15 +1,11 @@
|
|||||||
// current weather conditions display
|
// current weather conditions display
|
||||||
import STATUS from './status.mjs';
|
import STATUS from './status.mjs';
|
||||||
import { safeJson } from './utils/fetch.mjs';
|
import { safeJson } from './utils/fetch.mjs';
|
||||||
import { directionToNSEW } from './utils/calc.mjs';
|
|
||||||
import { locationCleanup } from './utils/string.mjs';
|
|
||||||
import WeatherDisplay from './weatherdisplay.mjs';
|
import WeatherDisplay from './weatherdisplay.mjs';
|
||||||
import { registerDisplay } from './navigation.mjs';
|
import { registerDisplay } from './navigation.mjs';
|
||||||
import {
|
import {
|
||||||
temperature, windSpeed, pressure, distanceMeters, distanceKilometers,
|
temperature, pressure, distanceMm, windSpeed,
|
||||||
} from './utils/units.mjs';
|
} from './utils/units.mjs';
|
||||||
import { debugFlag } from './utils/debug.mjs';
|
|
||||||
import Setting from './utils/setting.mjs';
|
|
||||||
|
|
||||||
class PersonalWeather extends WeatherDisplay {
|
class PersonalWeather extends WeatherDisplay {
|
||||||
constructor(navId, elemId) {
|
constructor(navId, elemId) {
|
||||||
@@ -28,7 +24,7 @@ class PersonalWeather extends WeatherDisplay {
|
|||||||
retryCount: 3,
|
retryCount: 3,
|
||||||
stillWaiting: () => this.stillWaiting(),
|
stillWaiting: () => this.stillWaiting(),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
console.error(`Unexpected error getting personal weather station data from: ${dataUrl}: ${error.message}`);
|
console.error(`Unexpected error getting personal weather station data from: ${dataUrl}: ${error.message}`);
|
||||||
}
|
}
|
||||||
// test for data received
|
// test for data received
|
||||||
@@ -54,34 +50,15 @@ class PersonalWeather extends WeatherDisplay {
|
|||||||
async drawCanvas() {
|
async drawCanvas() {
|
||||||
super.drawCanvas();
|
super.drawCanvas();
|
||||||
|
|
||||||
const wind = (typeof this.data.WindSpeed === 'number') ? this.data.WindDirection.padEnd(3, '') + this.data.WindSpeed.toString().padStart(3, ' ') : this.data.WindSpeed;
|
|
||||||
|
|
||||||
// get location (city name) from StationInfo if available (allows for overrides)
|
|
||||||
const location = (StationInfo[this.data.station.properties.stationIdentifier]?.city ?? locationCleanup(this.data.station.properties.name)).substr(0, 20);
|
|
||||||
|
|
||||||
const fill = {
|
const fill = {
|
||||||
temp: this.data.Temperature + String.fromCharCode(176),
|
temp: this.data.Temperature + String.fromCharCode(176),
|
||||||
condition,
|
wind: `${this.data.WindSpeed} ${this.data.WindUnit}`,
|
||||||
wind,
|
deviceName: this.data.device_name,
|
||||||
location,
|
deviceLocation: this.data.device_location,
|
||||||
humidity: `${this.data.Humidity}%`,
|
humidity: `${this.data.Humidity}%`,
|
||||||
dewpoint: this.data.DewPoint + String.fromCharCode(176),
|
pressure: `${this.data.Pressure} ${this.data.PressureUnit}`,
|
||||||
ceiling: (this.data.Ceiling === 0 ? 'Unlimited' : this.data.Ceiling + this.data.CeilingUnit),
|
|
||||||
visibility: this.data.Visibility + this.data.VisibilityUnit,
|
|
||||||
pressure: `${this.data.Pressure} ${this.data.PressureDirection}`,
|
|
||||||
icon: { type: 'img', src: this.data.Icon },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.data.WindGust !== '-') fill['wind-gusts'] = `Gusts to ${this.data.WindGust}`;
|
|
||||||
|
|
||||||
if (this.data.observations.heatIndex.value && this.data.HeatIndex !== this.data.Temperature) {
|
|
||||||
fill['heat-index-label'] = 'Heat Index:';
|
|
||||||
fill['heat-index'] = this.data.HeatIndex + String.fromCharCode(176);
|
|
||||||
} else if (this.data.observations.windChill.value && this.data.WindChill !== '' && this.data.WindChill < this.data.Temperature) {
|
|
||||||
fill['heat-index-label'] = 'Wind Chill:';
|
|
||||||
fill['heat-index'] = this.data.WindChill + String.fromCharCode(176);
|
|
||||||
}
|
|
||||||
|
|
||||||
const area = this.elem.querySelector('.main');
|
const area = this.elem.querySelector('.main');
|
||||||
|
|
||||||
area.innerHTML = '';
|
area.innerHTML = '';
|
||||||
@@ -106,50 +83,24 @@ class PersonalWeather extends WeatherDisplay {
|
|||||||
|
|
||||||
// format the received data
|
// format the received data
|
||||||
const parseData = (data) => {
|
const parseData = (data) => {
|
||||||
// get the unit converter
|
// get the unit converters
|
||||||
const windConverter = windSpeed('us');
|
|
||||||
const temperatureConverter = temperature('us');
|
const temperatureConverter = temperature('us');
|
||||||
const metersConverter = distanceMeters('us');
|
|
||||||
const kilometersConverter = distanceKilometers('us');
|
|
||||||
const pressureConverter = pressure('us');
|
const pressureConverter = pressure('us');
|
||||||
|
const inConverter = distanceMm('us');
|
||||||
|
const windConverter = windSpeed('us');
|
||||||
|
|
||||||
const observations = data.features[0].properties;
|
data.Pressure = pressureConverter(data.baromrelin * 10000) / 100;
|
||||||
// values from api are provided in metric
|
|
||||||
data.observations = observations;
|
|
||||||
data.Temperature = temperatureConverter(observations.temperature.value);
|
|
||||||
data.TemperatureUnit = temperatureConverter.units;
|
|
||||||
data.DewPoint = temperatureConverter(observations.dewpoint.value);
|
|
||||||
data.Ceiling = metersConverter(observations.cloudLayers[0]?.base?.value ?? 0);
|
|
||||||
data.CeilingUnit = metersConverter.units;
|
|
||||||
data.Visibility = kilometersConverter(observations.visibility.value);
|
|
||||||
data.VisibilityUnit = kilometersConverter.units;
|
|
||||||
data.Pressure = pressureConverter(observations.barometricPressure.value);
|
|
||||||
data.PressureUnit = pressureConverter.units;
|
data.PressureUnit = pressureConverter.units;
|
||||||
data.HeatIndex = temperatureConverter(observations.heatIndex.value);
|
data.Humidity = data.humidity;
|
||||||
data.WindChill = temperatureConverter(observations.windChill.value);
|
data.Temperature = temperatureConverter(data.tempf);
|
||||||
data.WindSpeed = windConverter(observations.windSpeed.value);
|
data.WindSpeed = windConverter(data.windspeedmph);
|
||||||
data.WindDirection = directionToNSEW(observations.windDirection.value);
|
|
||||||
data.WindGust = windConverter(observations.windGust.value);
|
|
||||||
data.WindUnit = windConverter.units;
|
data.WindUnit = windConverter.units;
|
||||||
data.Humidity = Math.round(observations.relativeHumidity.value);
|
data.DailyRain = inConverter(data.dailyrainin);
|
||||||
|
data.DailyRainUnit = inConverter.units;
|
||||||
// Get the large icon, but provide a fallback if it returns false
|
|
||||||
const iconResult = getLargeIcon(observations.icon);
|
|
||||||
data.Icon = iconResult || observations.icon; // Use original icon if getLargeIcon returns false
|
|
||||||
|
|
||||||
data.PressureDirection = '';
|
|
||||||
data.TextConditions = observations.textDescription;
|
|
||||||
|
|
||||||
// set wind speed of 0 as calm
|
// set wind speed of 0 as calm
|
||||||
if (data.WindSpeed === 0) data.WindSpeed = 'Calm';
|
if (data.WindSpeed === 0) data.WindSpeed = 'Calm';
|
||||||
|
|
||||||
// if two measurements are available, use the difference (in pascals) to determine pressure trend
|
|
||||||
if (data.features.length > 1 && data.features[1].properties.barometricPressure?.value) {
|
|
||||||
const pressureDiff = (observations.barometricPressure.value - data.features[1].properties.barometricPressure.value);
|
|
||||||
if (pressureDiff > 150) data.PressureDirection = 'R';
|
|
||||||
if (pressureDiff < -150) data.PressureDirection = 'F';
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const fahrenheitToCelsius = (Fahrenheit) => Math.round((Fahrenheit - 32) * 5 / 9
|
|||||||
const kilometersToMiles = (Kilometers) => Math.round(Kilometers / 1.609_34);
|
const kilometersToMiles = (Kilometers) => Math.round(Kilometers / 1.609_34);
|
||||||
const metersToFeet = (Meters) => Math.round(Meters / 0.3048);
|
const metersToFeet = (Meters) => Math.round(Meters / 0.3048);
|
||||||
const pascalToInHg = (Pascal) => round2(Pascal * 0.000_295_3, 2);
|
const pascalToInHg = (Pascal) => round2(Pascal * 0.000_295_3, 2);
|
||||||
|
const mmToIn = (mm) => round2(mm / 25.4);
|
||||||
|
|
||||||
// each module/page/slide creates it's own unit converter as needed by providing the base units available
|
// each module/page/slide creates it's own unit converter as needed by providing the base units available
|
||||||
// the factory function then returns an appropriate converter or pass-thru function for use on the page
|
// the factory function then returns an appropriate converter or pass-thru function for use on the page
|
||||||
@@ -98,6 +99,23 @@ const distanceKilometers = (defaultUnit = 'si') => {
|
|||||||
return converter;
|
return converter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// millimeters (annoying with camel case)
|
||||||
|
const distanceMm = (defaultUnit = 'si') => {
|
||||||
|
// default to passthru
|
||||||
|
let converter = passthru();
|
||||||
|
// change the converter if there is a mismatch
|
||||||
|
if (defaultUnit !== settings.units.value) {
|
||||||
|
converter = convert((value) => Math.round(mmToIn(value)));
|
||||||
|
}
|
||||||
|
// append units
|
||||||
|
if (settings.units.value === 'si') {
|
||||||
|
converter.units = ' mm.';
|
||||||
|
} else {
|
||||||
|
converter.units = ' in.';
|
||||||
|
}
|
||||||
|
return converter;
|
||||||
|
};
|
||||||
|
|
||||||
const pressure = (defaultUnit = 'si') => {
|
const pressure = (defaultUnit = 'si') => {
|
||||||
// default to passthru (millibar)
|
// default to passthru (millibar)
|
||||||
let converter = passthru(100);
|
let converter = passthru(100);
|
||||||
@@ -121,6 +139,7 @@ export {
|
|||||||
distanceMeters,
|
distanceMeters,
|
||||||
distanceKilometers,
|
distanceKilometers,
|
||||||
pressure,
|
pressure,
|
||||||
|
distanceMm,
|
||||||
|
|
||||||
// formatter
|
// formatter
|
||||||
round2,
|
round2,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,9 +1,8 @@
|
|||||||
@use 'shared/_colors' as c;
|
@use 'shared/_colors'as c;
|
||||||
@use 'shared/_utils' as u;
|
@use 'shared/_utils'as u;
|
||||||
|
|
||||||
// also shared with personal weather
|
// also shared with personal weather
|
||||||
.weather-display .main.current-weather,
|
.weather-display .main.current-weather {
|
||||||
.weather-display .main.personal-weather {
|
|
||||||
&.main {
|
&.main {
|
||||||
|
|
||||||
.col {
|
.col {
|
||||||
@@ -94,4 +93,4 @@
|
|||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
57
server/styles/scss/_personal-weather.scss
Normal file
57
server/styles/scss/_personal-weather.scss
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
@use 'shared/_colors'as c;
|
||||||
|
@use 'shared/_utils'as u;
|
||||||
|
|
||||||
|
// also shared with personal weather
|
||||||
|
.weather-display .main.personal-weather {
|
||||||
|
&.main {
|
||||||
|
|
||||||
|
@include u.text-shadow();
|
||||||
|
|
||||||
|
font-family: "Star4000 Large";
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 24px;
|
||||||
|
top: 20px;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.label,
|
||||||
|
.value {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
float: right;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.temp {
|
||||||
|
font-family: 'Star4000 Large';
|
||||||
|
font-size: 24pt;
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deviceName,
|
||||||
|
.deviceLocation {
|
||||||
|
color: c.$title-color;
|
||||||
|
max-height: 32px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-top: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
@use 'page';
|
@use 'page';
|
||||||
@use 'weather-display';
|
@use 'weather-display';
|
||||||
@use 'current-weather';
|
@use 'current-weather';
|
||||||
|
@use 'personal-weather';
|
||||||
@use 'extended-forecast';
|
@use 'extended-forecast';
|
||||||
@use 'hourly';
|
@use 'hourly';
|
||||||
@use 'hourly-graph';
|
@use 'hourly-graph';
|
||||||
|
|||||||
@@ -1,40 +1,24 @@
|
|||||||
<%- include('header.ejs', {titleDual:{ top: 'Personal' , bottom: 'Weather Station' }, noaaLogo: false, hasTime: true}) %>
|
<%- include('header.ejs', {titleDual:{ top: 'Personal' , bottom: 'Weather Station' }, noaaLogo: false, hasTime: true}) %>
|
||||||
<div class="main has-scroll has-box personal-weather">
|
<div class="main has-scroll has-box personal-weather">
|
||||||
<div class="weather template">
|
<div class="weather template">
|
||||||
<div class="left col">
|
<div class="deviceName value"></div>
|
||||||
<div class="temp center"></div>
|
<div class="deviceLocation value"></div>
|
||||||
<div class="wind-container">
|
<div class="temp value"></div>
|
||||||
<div class="wind-label">Wind:</div>
|
<div class="row">
|
||||||
<div class="wind"></div>
|
<div class="label">Humidity:</div>
|
||||||
</div>
|
<div class="humidity value"></div>
|
||||||
<div class="wind-gusts"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="right col">
|
<div class="row">
|
||||||
<div class="location"></div>
|
<div class="label">Wind:</div>
|
||||||
<div class="row">
|
<div class="wind value"></div>
|
||||||
<div class="label">Humidity:</div>
|
</div>
|
||||||
<div class="humidity value"></div>
|
<div class="row">
|
||||||
</div>
|
<div class="label">Pressure:</div>
|
||||||
<div class="row">
|
<div class="pressure value"></div>
|
||||||
<div class="label">Dewpoint:</div>
|
</div>
|
||||||
<div class="dewpoint value"></div>
|
<div class="row">
|
||||||
</div>
|
<div class="heat-index-label label"></div>
|
||||||
<div class="row">
|
<div class="heat-index value"></div>
|
||||||
<div class="label">Ceiling:</div>
|
|
||||||
<div class="ceiling value"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="label">Visibility:</div>
|
|
||||||
<div class="visibility value"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="label">Pressure:</div>
|
|
||||||
<div class="pressure value"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="heat-index-label label"></div>
|
|
||||||
<div class="heat-index value"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user