Compare commits

...

12 Commits

Author SHA1 Message Date
Matt Walsh
7900e59aab 6.2.7 2025-11-05 05:24:14 +00:00
Matt Walsh
9b422dd697 expose more data for scroll messages 2025-11-05 05:24:04 +00:00
Matt Walsh
e4ce0b6cc6 update bug template 2025-11-04 05:49:10 +00:00
Matt Walsh
b0e5018179 6.2.6 2025-10-22 00:22:44 +00:00
Matt Walsh
6422589b5c fix wind speed on hourly close #157 2025-10-22 00:22:29 +00:00
Matt Walsh
407da90f8a 6.2.5 2025-10-21 04:15:53 +00:00
Matt Walsh
3a0e6aa345 Merge branch 'geolookup-latlongquery' 2025-10-21 04:15:38 +00:00
Matt Walsh
650dda7b61 allow for latLon only in query string #154 2025-10-21 04:12:21 +00:00
Matt Walsh
8f1e8ffb74 Merge branch 'rmitchellscott-static-redirect-index' 2025-10-21 03:26:25 +00:00
Mitchell Scott
93af84cbd8 fix: geolookup if only latLonQuery is provided 2025-10-20 13:37:18 -06:00
Mitchell Scott
117f66e9d0 fix(static builds): duplicate query params 2025-10-20 13:28:45 -06:00
Mitchell Scott
bca9376edc fix: nginx query parameter redirect works like node.js 2025-10-20 11:42:42 -06:00
10 changed files with 91 additions and 16 deletions

View File

@@ -12,3 +12,4 @@ Please do not report issues with api.weather.gov being down. It's a new service
Please include:
* Web browser and OS
* Headend Information text block from the very bottom of the web page
* How you're running Weatherstar (Node, Dockerfile, Dockerfile.server, etc.)

View File

@@ -10,8 +10,10 @@ server {
add_header X-Weatherstar true always;
include /etc/nginx/includes/wsqs_redirect.conf;
location / {
index redirect.html index.html index.htm;
index index.html index.htm;
try_files $uri $uri/ =404;
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ws4kp",
"version": "6.2.4",
"version": "6.2.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ws4kp",
"version": "6.2.4",
"version": "6.2.7",
"license": "MIT",
"dependencies": {
"dotenv": "^17.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "ws4kp",
"version": "6.2.4",
"version": "6.2.7",
"description": "Welcome to the WeatherStar 4000+ project page!",
"main": "index.mjs",
"type": "module",

View File

@@ -106,17 +106,34 @@ const init = async () => {
// attempt to parse the url parameters
const parsedParameters = parseQueryString();
const loadFromParsed = parsedParameters.latLonQuery && parsedParameters.latLon;
const loadFromParsed = !!parsedParameters.latLon;
// Auto load the parsed parameters and fall back to the previous query
const query = parsedParameters.latLonQuery ?? localStorage.getItem('latLonQuery');
const latLon = parsedParameters.latLon ?? localStorage.getItem('latLon');
const fromGPS = localStorage.getItem('latLonFromGPS') && !loadFromParsed;
if (query && latLon && !fromGPS) {
if (parsedParameters.latLonQuery && !parsedParameters.latLon) {
const txtAddress = document.querySelector(TXT_ADDRESS_SELECTOR);
txtAddress.value = query;
loadData(JSON.parse(latLon));
txtAddress.value = parsedParameters.latLonQuery;
const geometry = await geocodeLatLonQuery(parsedParameters.latLonQuery);
if (geometry) {
doRedirectToGeometry(geometry);
}
} else if (latLon && !fromGPS) {
// update in-page search box if using cached data, or parsed parameter
if ((query && !loadFromParsed) || (parsedParameters.latLonQuery && loadFromParsed)) {
const txtAddress = document.querySelector(TXT_ADDRESS_SELECTOR);
txtAddress.value = query;
}
// use lat-long lookup if that's all that was provided in the query string
if (loadFromParsed && parsedParameters.latLon && !parsedParameters.latLonQuery) {
const { lat, lon } = JSON.parse(latLon);
getForecastFromLatLon(lat, lon, true);
} else {
// otherwise use pre-stored data
loadData(JSON.parse(latLon));
}
}
if (fromGPS) {
btnGetGpsClick();
@@ -162,6 +179,26 @@ const init = async () => {
document.querySelector('#container').addEventListener('swiped-right', () => swipeCallBack('right'));
};
const geocodeLatLonQuery = async (query) => {
try {
const data = await json('https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find', {
data: {
text: query,
f: 'json',
},
});
const loc = data.locations?.[0];
if (loc) {
return loc.feature.geometry;
}
return null;
} catch (error) {
console.error('Geocoding failed:', error);
return null;
}
};
const autocompleteOnSelect = async (suggestion) => {
// Note: it's fine that this uses json instead of safeJson since it's infrequent and user-initiated
const data = await json('https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find', {

View File

@@ -231,7 +231,7 @@ class CurrentWeather extends WeatherDisplay {
this.setAutoReload();
if (stillWaiting) this.stillWaitingCallbacks.push(stillWaiting);
return new Promise((resolve) => {
if (this.data) resolve(this.data);
if (this.data) resolve({ data: this.data, parameters: this.weatherParameters });
// data not available, put it into the data callback queue
this.getDataCallbacks.push(() => resolve(this.data));
});

View File

@@ -84,7 +84,7 @@ const incrementInterval = (force) => {
const drawScreen = async () => {
// get the conditions
const data = await getCurrentWeather();
const { data, parameters } = await getCurrentWeather();
// create a data object (empty if no valid current weather conditions)
const scrollData = data || {};
@@ -100,7 +100,7 @@ const drawScreen = async () => {
// if we have no current weather and no hazards, there's nothing to display
if (!data && (!scrollData.hazards || scrollData.hazards.length === 0)) return;
const thisScreen = workingScreens[screenIndex](scrollData);
const thisScreen = workingScreens[screenIndex](scrollData, parameters);
// update classes on the scroll area
mainScroll.classList.forEach((cls) => { if (cls !== 'scroll') mainScroll.classList.remove(cls); });

View File

@@ -3,7 +3,7 @@
import STATUS from './status.mjs';
import { DateTime, Interval, Duration } from '../vendor/auto/luxon.mjs';
import { safeJson } from './utils/fetch.mjs';
import { temperature as temperatureUnit, distanceKilometers } from './utils/units.mjs';
import { temperature as temperatureUnit, windSpeed as windUnit } from './utils/units.mjs';
import { getHourlyIcon } from './icons.mjs';
import { directionToNSEW } from './utils/calc.mjs';
import WeatherDisplay from './weatherdisplay.mjs';
@@ -191,7 +191,7 @@ class Hourly extends WeatherDisplay {
const parseForecast = async (data) => {
// get unit converters
const temperatureConverter = temperatureUnit();
const distanceConverter = distanceKilometers();
const windConverter = windUnit();
// parse data
const temperature = expand(data.temperature.values);
@@ -210,8 +210,8 @@ const parseForecast = async (data) => {
temperature: temperatureConverter(temperature[idx]),
temperatureUnit: temperatureConverter.units,
apparentTemperature: temperatureConverter(apparentTemperature[idx]),
windSpeed: distanceConverter(windSpeed[idx]),
windUnit: distanceConverter.units,
windSpeed: windConverter(windSpeed[idx]),
windUnit: windConverter.units,
windDirection: directionToNSEW(windDirection[idx]),
probabilityOfPrecipitation: probabilityOfPrecipitation[idx],
skyCover: skyCover[idx],

View File

@@ -109,6 +109,7 @@ const getWeather = async (latLon, haveDataCallback) => {
weatherParameters.forecast = point.properties.forecast;
weatherParameters.forecastGridData = point.properties.forecastGridData;
weatherParameters.stations = stations.features;
weatherParameters.relativeLocation = point.properties.relativeLocation.properties;
// update the main process for display purposes
populateWeatherParameters(weatherParameters, point.properties);

View File

@@ -12,6 +12,9 @@ url_encode() {
# build query string from WSQS_ env vars
while IFS='=' read -r key val; do
# Skip empty lines
[ -z "$key" ] && continue
# Remove WSQS_ prefix and convert underscores to hyphens
key="${key#WSQS_}"
key="${key//_/-}"
@@ -23,11 +26,16 @@ while IFS='=' read -r key val; do
QS="${key}=${encoded_val}"
fi
done << EOF
$(env | grep '^WSQS_')
$(env | grep '^WSQS_' || true)
EOF
mkdir -p /etc/nginx/includes
if [ -n "$QS" ]; then
# Escape the query string for use in JavaScript (escape backslashes and single quotes)
QS_ESCAPED=$(printf '%s' "$QS" | sed "s/\\\\/\\\\\\\\/g; s/'/\\\'/g")
# Generate redirect.html with JavaScript logic
cat > "$ROOT/redirect.html" <<EOF
<!DOCTYPE html>
<html>
@@ -35,10 +43,36 @@ if [ -n "$QS" ]; then
<meta charset="utf-8" />
<title>Redirecting</title>
<meta http-equiv="refresh" content="0;url=/index.html?$QS" />
<script>
(function() {
var wsqsParams = '$QS_ESCAPED';
var currentParams = window.location.search.substring(1);
var targetParams = currentParams || wsqsParams;
window.location.replace('/index.html?' + targetParams);
})();
</script>
</head>
<body></body>
</html>
EOF
# Generate nginx config for conditional redirects
cat > /etc/nginx/includes/wsqs_redirect.conf <<'EOF'
location = / {
if ($args = '') {
rewrite ^ /redirect.html last;
}
rewrite ^/$ /index.html?$args? redirect;
}
location = /index.html {
if ($args = '') {
rewrite ^ /redirect.html last;
}
}
EOF
else
touch /etc/nginx/includes/wsqs_redirect.conf
fi
exec nginx -g 'daemon off;'