Compare commits

...

8 Commits

Author SHA1 Message Date
Matt Walsh
0a794eae36 6.5.0 2026-02-16 21:30:23 -06:00
Matt Walsh
8c83736aba remove rss feeds close #184 2026-02-16 21:30:13 -06:00
Matt Walsh
872162080d 6.4.3 2026-02-16 20:40:25 -06:00
Matt Walsh
69d2b0f40b more robust backfilling of current weather properties close #177 2026-02-16 20:40:13 -06:00
Matt Walsh
37193112a7 6.4.2 2026-01-22 22:12:38 -06:00
Matt Walsh
0d9c445919 Fix hourly graph y axis labels close #176 2026-01-22 22:12:32 -06:00
Matt Walsh
6c9fb4cf68 6.4.1 2026-01-17 21:02:33 -06:00
Matt Walsh
59b10ae222 fix icon parsing close #175 2026-01-17 21:02:26 -06:00
10 changed files with 13 additions and 148 deletions

View File

@@ -336,9 +336,6 @@ When using Docker:
* **Static deployment**: Mount your `custom.js` file to `/usr/share/nginx/html/scripts/custom.js`
* **Server deployment**: Mount your `custom.js` file to `/app/server/scripts/custom.js`
### RSS feeds and custom scroll
If you would like your Weatherstar to have custom scrolling text in the bottom blue bar, or show headlines from an rss feed turn on the setting for `Enable RSS Feed/Text` and then enter a URL or text in the resulting text box. Then press set.
## Issue reporting and feature requests
Please do not report issues with api.weather.gov being down. It's a new service and not considered fully operational yet. I've also observed that the API can go down on a regional basis (based on NWS office locations). This means that you may have problems getting data for, say, Chicago right now, but Dallas and others are working just fine.

View File

@@ -86,7 +86,6 @@ const mjsSources = [
'server/scripts/modules/travelforecast.mjs',
'server/scripts/modules/progress.mjs',
'server/scripts/modules/media.mjs',
'server/scripts/modules/custom-rss-feed.mjs',
'server/scripts/index.mjs',
];

4
package-lock.json generated
View File

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

View File

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

View File

@@ -324,7 +324,7 @@ const backfill = (data) => {
// backfill each property
Object.keys(sortedData[0].properties).forEach((key) => {
// qualify the key (must have value)
if (Object.hasOwn(sortedData[0].properties[key], 'value')) {
if (Object.hasOwn(sortedData[0].properties?.[key] ?? {}, 'value')) {
// backfill this property
result[key] = backfillProperty(sortedData, key);
} else {

View File

@@ -1,135 +0,0 @@
import Setting from './utils/setting.mjs';
import { reset as resetScroll, addScreen as addScroll, hazards } from './currentweatherscroll.mjs';
import { json } from './utils/fetch.mjs';
let firstRun = true;
const parser = new DOMParser();
// change of enable handler
const changeEnable = (newValue) => {
let newDisplay;
if (newValue) {
// add the feed to the scroll
parseFeed(customFeed.value);
// show the string box
newDisplay = 'block';
} else {
// set scroll back to original
resetScroll();
// hide the string entry
newDisplay = 'none';
}
const stringEntry = document.getElementById('settings-customFeed-label');
if (stringEntry) {
stringEntry.style.display = newDisplay;
}
};
// parse the feed/text provided
const parseFeed = (textInput) => {
// skip getting the feed on first run
if (firstRun) return;
// test validity
if (textInput === undefined || textInput === '') {
resetScroll();
}
// test for url
if (textInput.match(/https?:\/\//)) {
getFeed(textInput);
return;
}
// add single text scroll after hazards if present
resetScroll();
addScroll(hazards);
addScroll(
() => (
{
type: 'scroll',
text: textInput,
}),
// keep the existing scroll
true,
);
};
// get the rss feed and then swap out the current weather scroll
const getFeed = async (url) => {
// get the text as a string
// it needs to be proxied, use a free service
const rssResponse = await json(`https://api.allorigins.win/get?url=${url}`);
// this returns a data url
// a few sanity checks
if (rssResponse.status.content_type.indexOf('xml') < 0) return;
// determine return type
const isBase64 = rssResponse.status.content_type.substring(0, 8) !== 'text/xml';
// base 64 decode everything after the comma
const rss = isBase64 ? atob(rssResponse.contents.split('base64,')[1]) : rssResponse.contents;
// parse the rss
const doc = parser.parseFromString(rss, 'text/xml');
// get the title
const rssTitle = doc.querySelector('channel title').textContent;
// get each item
const titles = [...doc.querySelectorAll('item title')].map((t) => t.textContent);
// reset the scroll, then add the screens
resetScroll();
// add the hazards scroll first
addScroll(hazards);
titles.forEach((title) => {
// data is provided to the screen handler, so we return a function
addScroll(
() => ({
header: rssTitle,
type: 'scroll',
text: title,
}),
// false parameter does not include the default weather scrolls
false,
);
});
};
// change the feed source and re-load if necessary
const changeFeed = (newValue) => {
// first pass through won't have custom feed enable ready
if (firstRun) return;
if (customFeedEnable.value) {
parseFeed(newValue);
}
};
const customFeed = new Setting('customFeed', {
name: 'Custom RSS Feed',
defaultValue: '',
type: 'string',
changeAction: changeFeed,
placeholder: 'Text or URL',
});
const customFeedEnable = new Setting('customFeedEnable', {
name: 'Enable RSS Feed/Text',
defaultValue: false,
changeAction: changeEnable,
});
// initialize the custom feed inputs on the page
document.addEventListener('DOMContentLoaded', () => {
// add the controls to the page
const settingsSection = document.querySelector('#settings');
settingsSection.append(customFeedEnable.generate(), customFeed.generate());
// clear the first run value
firstRun = false;
// call change enable with the current value to show/hide the url box
// and make the call to get the feed if enabled
changeEnable(customFeedEnable.value);
});

View File

@@ -94,7 +94,7 @@ class HourlyGraph extends WeatherDisplay {
// calculate temperature scale for min and max of dewpoint and temperature
const minScale = Math.min(...this.data.dewpoint, ...this.data.temperature);
const maxScale = Math.max(...this.data.dewpoint, ...this.data.temperature);
const thirdScale = (minScale + maxScale) / 3;
const thirdScale = (maxScale - minScale) / 3;
const midScale1 = Math.round(minScale + thirdScale);
const midScale2 = Math.round(minScale + (thirdScale * 2));
const tempScale = calcScale(minScale, availableHeight - 10, maxScale, 10);

View File

@@ -13,7 +13,7 @@ const largeIcon = (link, _isNightTime) => {
} catch (error) {
console.warn(`largeIcon: ${error.message}`);
// Return a fallback icon to prevent downstream errors
return addPath(`No-Data.gif?${conditionIcon}${isNightTime ? '-n' : ''}`);
return addPath(`No-Data-Large.gif?${conditionIcon}${isNightTime ? '-n' : ''}`);
}
// find the icon
@@ -102,6 +102,8 @@ const largeIcon = (link, _isNightTime) => {
case 'snow_fzra':
case 'snow_fzra-n':
case 'winter_mix':
case 'winter_mix-n':
return addPath('Freezing-Rain-Snow.gif');
case 'fzra':
@@ -141,6 +143,8 @@ const largeIcon = (link, _isNightTime) => {
return addPath('Thunderstorm.gif');
case 'wind_skc':
case 'wind_':
case 'wind_-n':
return addPath('Windy.gif');
case 'wind_skc-n':
@@ -169,7 +173,7 @@ const largeIcon = (link, _isNightTime) => {
default: {
console.warn(`Unknown weather condition '${conditionIcon}' from ${link}; using fallback icon`);
// Return a reasonable fallback instead of false to prevent downstream errors
return addPath(`No-Data.gif?${conditionIcon}${isNightTime ? '-n' : ''}`);
return addPath(`No-Data-Large.gif?${conditionIcon}${isNightTime ? '-n' : ''}`);
}
}
};

View File

@@ -133,6 +133,7 @@ const smallIcon = (link, _isNightTime) => {
case 'wind_few':
case 'wind_few-n':
case 'wind_':
return addPath('Wind.gif');
case 'wind_sct':
@@ -170,7 +171,7 @@ const smallIcon = (link, _isNightTime) => {
case 'blizzard':
case 'blizzard-n':
return addPath('Blowing Snow.gif');
return addPath('Blowing-Snow.gif');
default:
console.warn(`Unknown weather condition '${conditionIcon}' from ${link}; using fallback icon`);

View File

@@ -62,7 +62,6 @@
<script type="module" src="scripts/modules/radar.mjs"></script>
<script type="module" src="scripts/modules/settings.mjs"></script>
<script type="module" src="scripts/modules/media.mjs"></script>
<script type="module" src="scripts/modules/custom-rss-feed.mjs"></script>
<script type="module" src="scripts/index.mjs"></script>
<% } %>