Compare commits

..

17 Commits

Author SHA1 Message Date
Matt Walsh
8b076db25d 6.2.4 2025-10-17 00:51:09 +00:00
Matt Walsh
807932fe3c Merge branch 'ios-regex' close #137 2025-10-17 00:49:59 +00:00
Matt Walsh
7bb024eff5 6.2.3 2025-10-17 00:36:14 +00:00
Matt Walsh
f4a1a3a1d8 add hazards before custom scroll options close #149 2025-10-17 00:35:26 +00:00
Matt Walsh
9a5efe9d48 update dependencies 2025-10-17 00:14:59 +00:00
Matt Walsh
58e0611a46 6.2.2 2025-10-16 19:00:30 -05:00
Matt Walsh
9ed496c892 better formatting for headend info 2025-10-16 19:00:20 -05:00
Matt Walsh
31315d1ace add com.chrome.devtools.json 2025-10-16 18:36:41 -05:00
Matt Walsh
77838e1a81 use locally stored weather parameters in spc outlook close #150 2025-10-15 00:29:23 +00:00
Matt Walsh
64d6484bd8 Merge pull request #151 from bparkin1283/patch-1
Update README.md clarifying displays if you're within one of the high…
2025-10-09 11:17:13 -05:00
bparkin1283
20cab8c25e Update README.md clarifying displays if you're within one of the highlight areas 2025-10-09 10:55:33 -05:00
Matt Walsh
b4de17ccd0 update dependencies 2025-10-02 21:50:28 -05:00
Matt Walsh
0fd90feb7a update community notes 2025-10-02 21:37:36 -05:00
Matt Walsh
8c3b596b69 add build script for travel cities #146 2025-10-02 21:26:45 -05:00
Matt Walsh
e57b9bcb20 6.2.1 2025-09-24 22:33:59 -05:00
Matt Walsh
e27750e915 fix load order on scroll when compiled 2025-09-24 22:33:47 -05:00
Matt Walsh
14b1891efd direct check of regex lookbehind capability 2025-09-11 08:47:16 -05:00
18 changed files with 980 additions and 3313 deletions

View File

@@ -179,7 +179,7 @@ I've made several changes to this Weather Star 4000 simulation compared to the o
* Radar displays the timestamp of the image. * Radar displays the timestamp of the image.
* A new hour-by-hour graph of the temperature, cloud cover and precipitation chances for the next 24 hours. * A new hour-by-hour graph of the temperature, cloud cover and precipitation chances for the next 24 hours.
* A new hourly forecast display for the next 24 hours is available, and is shown in the style of the travel cities forecast. (off by default because it duplicates the hourly graph) * A new hourly forecast display for the next 24 hours is available, and is shown in the style of the travel cities forecast. (off by default because it duplicates the hourly graph)
* The SPC Outlook is shown in the style of the old air quality screen. This shows the probability of severe weather over the next 3 days at your location. * The SPC Outlook is shown in the style of the old air quality screen. This shows the probability of severe weather over the next 3 days at your location. SPC outlook only displays if you're within one of the highlight areas over the next 3 day. You can view the [maps](https://www.weather.gov/crh/outlooks) and pick a location within one of the risk categories to see if the screen is working for you.
* The "Local Forecast" and "Extended Forecast" provide several additional days of information compared to the original format in the 90s. * The "Local Forecast" and "Extended Forecast" provide several additional days of information compared to the original format in the 90s.
* The original music has been replaced. More info in [Music](#music). * The original music has been replaced. More info in [Music](#music).
* Marine forecast (tides) is not available as it is not reliably part of the new API. * Marine forecast (tides) is not available as it is not reliably part of the new API.
@@ -321,6 +321,7 @@ Thanks to the WeatherStar+ community for providing these discussions to further
* [ws4channels](https://github.com/rice9797/ws4channels) A Dockerized Node.js application to stream WeatherStar 4000 data into Channels DVR using Puppeteer and FFmpeg. * [ws4channels](https://github.com/rice9797/ws4channels) A Dockerized Node.js application to stream WeatherStar 4000 data into Channels DVR using Puppeteer and FFmpeg.
* [SSL Certificates](https://github.com/netbymatt/ws4kp/issues/135) Discussion about how to host with an SSL certificate (enables geolocation). * [SSL Certificates](https://github.com/netbymatt/ws4kp/issues/135) Discussion about how to host with an SSL certificate (enables geolocation).
* [Changing playlists](https://github.com/netbymatt/ws4kp/issues/138) Possible ways to automatically change the playlist on a schedule. * [Changing playlists](https://github.com/netbymatt/ws4kp/issues/138) Possible ways to automatically change the playlist on a schedule.
* [Customize Travel Forecast Cities](https://github.com/netbymatt/ws4kp/issues/146#issuecomment-3363940202)
## Customization ## Customization

View File

@@ -84,8 +84,8 @@
"Latitude": 29.7633, "Latitude": 29.7633,
"Longitude": -95.3633, "Longitude": -95.3633,
"point": { "point": {
"x": 65, "x": 63,
"y": 97, "y": 95,
"wfo": "HGX" "wfo": "HGX"
} }
}, },

View File

@@ -5,8 +5,8 @@ import rename from 'gulp-rename';
const clean = () => deleteAsync(['./server/scripts/vendor/auto/**']); const clean = () => deleteAsync(['./server/scripts/vendor/auto/**']);
const vendorFiles = [ const vendorFiles = [
'./node_modules/luxon/build/es6/luxon.js', './node_modules/luxon/build/es6/luxon.mjs',
'./node_modules/luxon/build/es6/luxon.js.map', './node_modules/luxon/build/es6/luxon.mjs.map',
'./node_modules/nosleep.js/dist/NoSleep.js', './node_modules/nosleep.js/dist/NoSleep.js',
'./node_modules/suncalc/suncalc.js', './node_modules/suncalc/suncalc.js',
'./node_modules/swiped-events/src/swiped-events.js', './node_modules/swiped-events/src/swiped-events.js',
@@ -23,7 +23,6 @@ const copy = () => src(vendorFiles)
path.dirname = path.dirname.toLowerCase(); path.dirname = path.dirname.toLowerCase();
path.basename = path.basename.toLowerCase(); path.basename = path.basename.toLowerCase();
path.extname = path.extname.toLowerCase(); path.extname = path.extname.toLowerCase();
if (path.basename === 'luxon') path.extname = '.mjs';
})) }))
.pipe(dest('./server/scripts/vendor/auto')); .pipe(dest('./server/scripts/vendor/auto'));

View File

@@ -8,6 +8,7 @@ import {
import playlist from './src/playlist.mjs'; import playlist from './src/playlist.mjs';
import OVERRIDES from './src/overrides.mjs'; import OVERRIDES from './src/overrides.mjs';
import cache from './proxy/cache.mjs'; import cache from './proxy/cache.mjs';
import devTools from './src/com.chrome.devtools.mjs';
const travelCities = JSON.parse(await readFile('./datagenerators/output/travelcities.json')); const travelCities = JSON.parse(await readFile('./datagenerators/output/travelcities.json'));
const regionalCities = JSON.parse(await readFile('./datagenerators/output/regionalcities.json')); const regionalCities = JSON.parse(await readFile('./datagenerators/output/regionalcities.json'));
@@ -168,6 +169,7 @@ if (process.env?.DIST === '1') {
app.use('/geoip', geoip); app.use('/geoip', geoip);
app.use('/resources', express.static('./server/scripts/modules')); app.use('/resources', express.static('./server/scripts/modules'));
app.get('/', index); app.get('/', index);
app.get('/.well-known/appspecific/com.chrome.devtools.json', devTools);
app.get('*name', express.static('./server', staticOptions)); app.get('*name', express.static('./server', staticOptions));
} }

4150
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "ws4kp", "name": "ws4kp",
"version": "6.2.0", "version": "6.2.4",
"description": "Welcome to the WeatherStar 4000+ project page!", "description": "Welcome to the WeatherStar 4000+ project page!",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
@@ -8,6 +8,7 @@
"start": "node index.mjs", "start": "node index.mjs",
"stop": "pkill -f 'node index.mjs' || echo 'No process found'", "stop": "pkill -f 'node index.mjs' || echo 'No process found'",
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build:travelcities": "node datagenerators/travelcities.mjs",
"build:css": "sass --style=compressed ./server/styles/scss/main.scss ./server/styles/main.css", "build:css": "sass --style=compressed ./server/styles/scss/main.scss ./server/styles/main.css",
"build": "gulp buildDist", "build": "gulp buildDist",
"lint": "eslint ./server/scripts/**/*.mjs ./proxy/**/*.mjs ./src/**/*.mjs *.mjs", "lint": "eslint ./server/scripts/**/*.mjs ./proxy/**/*.mjs ./src/**/*.mjs *.mjs",
@@ -50,13 +51,12 @@
"swiped-events": "^1.1.4", "swiped-events": "^1.1.4",
"terser-webpack-plugin": "^5.3.6", "terser-webpack-plugin": "^5.3.6",
"webpack": "^5.99.9", "webpack": "^5.99.9",
"webpack-stream": "^7.0.0" "webpack-stream": "^7.0.0",
"metar-taf-parser": "^9.0.0"
}, },
"dependencies": { "dependencies": {
"dotenv": "^17.0.1", "dotenv": "^17.0.1",
"ejs": "^3.1.5", "ejs": "^3.1.5",
"express": "^5.1.0", "express": "^5.1.0"
"metar-taf-parser": "^9.0.0",
"npm": "^11.6.0"
} }
} }

View File

@@ -12,9 +12,14 @@ const secondsToTicks = (seconds) => Math.ceil((seconds * 1000) / TICK_INTERVAL_M
const DEFAULT_UPDATE = secondsToTicks(4.0); // 4 second default for each current conditions const DEFAULT_UPDATE = secondsToTicks(4.0); // 4 second default for each current conditions
// items on page // items on page
const mainScroll = document.querySelector('#container>.scroll'); let mainScroll;
const fixedScroll = document.querySelector('#container>.scroll .fixed'); let fixedScroll;
const header = document.querySelector('#container>.scroll .scroll-header'); let header;
document.addEventListener('DOMContentLoaded', () => {
mainScroll = document.querySelector('#container>.scroll');
fixedScroll = document.querySelector('#container>.scroll .fixed');
header = document.querySelector('#container>.scroll .scroll-header');
});
// local variables // local variables
let interval; let interval;
@@ -293,4 +298,5 @@ export {
hide, hide,
screenCount, screenCount,
atDefault, atDefault,
hazards,
}; };

View File

@@ -1,5 +1,5 @@
import Setting from './utils/setting.mjs'; import Setting from './utils/setting.mjs';
import { reset as resetScroll, addScreen as addScroll } from './currentweatherscroll.mjs'; import { reset as resetScroll, addScreen as addScroll, hazards } from './currentweatherscroll.mjs';
import { json } from './utils/fetch.mjs'; import { json } from './utils/fetch.mjs';
let firstRun = true; let firstRun = true;
@@ -42,8 +42,9 @@ const parseFeed = (textInput) => {
return; return;
} }
// add single text scroll // add single text scroll after hazards if present
resetScroll(); resetScroll();
addScroll(hazards);
addScroll( addScroll(
() => ( () => (
{ {
@@ -81,6 +82,8 @@ const getFeed = async (url) => {
// reset the scroll, then add the screens // reset the scroll, then add the screens
resetScroll(); resetScroll();
// add the hazards scroll first
addScroll(hazards);
titles.forEach((title) => { titles.forEach((title) => {
// data is provided to the screen handler, so we return a function // data is provided to the screen handler, so we return a function
addScroll( addScroll(

View File

@@ -57,6 +57,7 @@ class SpcOutlook extends WeatherDisplay {
} }
async getData(weatherParameters, refresh) { async getData(weatherParameters, refresh) {
if (weatherParameters) this.weatherParameters = weatherParameters;
if (!super.getData(weatherParameters, refresh)) return; if (!super.getData(weatherParameters, refresh)) return;
// SPC outlook data does not need to be reloaded on a location change, only during silent refresh // SPC outlook data does not need to be reloaded on a location change, only during silent refresh
@@ -93,7 +94,7 @@ class SpcOutlook extends WeatherDisplay {
} }
} }
// parse the data // parse the data
this.data = testAllPoints([weatherParameters.longitude, weatherParameters.latitude], this.rawOutlookData); this.data = testAllPoints([this.weatherParameters.longitude, this.weatherParameters.latitude], this.rawOutlookData);
// check if there's a "risk" for any of the three days, otherwise skip the SPC Outlook screen // check if there's a "risk" for any of the three days, otherwise skip the SPC Outlook screen
if (this.data.reduce((prev, cur) => prev || !!cur, false)) { if (this.data.reduce((prev, cur) => prev || !!cur, false)) {

View File

@@ -5,21 +5,22 @@ import en from '../../vendor/auto/locale/en.js';
// metar-taf-parser requires regex lookbehind // metar-taf-parser requires regex lookbehind
// this does not work in iOS < 16.4 // this does not work in iOS < 16.4
// this is a detection algorithm for iOS versions // this is a detection algorithm for missing lookbehind support
const isIos = /iP(ad|od|hone)/i.test(window.navigator.userAgent); const supportsRegexLookAheadLookBehindCheck = () => {
let iosVersionOk = false; try {
if (isIos) { return (
// regex match the version string // deliberately using RegExp for broader browser support during check
const iosVersionRaw = /OS (\d+)_(\d+)/.exec(window.navigator.userAgent); /* eslint-disable prefer-regex-literals */
// check for match 'hibyehihi'
if (iosVersionRaw) { .replace(new RegExp('(?<=hi)hi', 'g'), 'hello')
// break into parts .replace(new RegExp('hi(?!bye)', 'g'), 'hey') === 'hibyeheyhello'
const iosVersionMajor = parseInt(iosVersionRaw[1], 10); /* eslint-enable prefer-regex-literals */
const iosVersionMinor = parseInt(iosVersionRaw[2], 10); );
if (iosVersionMajor > 16) iosVersionOk = true; } catch {
if (iosVersionMajor === 16 && iosVersionMinor >= 4) iosVersionOk = true; return false;
} }
} };
const supportsRegexLookAheadLookBehind = supportsRegexLookAheadLookBehindCheck();
/** /**
* Augment observation data by parsing METAR when API fields are missing * Augment observation data by parsing METAR when API fields are missing
@@ -27,8 +28,8 @@ if (isIos) {
* @returns {Object} - Augmented observation with parsed METAR data filled in * @returns {Object} - Augmented observation with parsed METAR data filled in
*/ */
const augmentObservationWithMetar = (observation) => { const augmentObservationWithMetar = (observation) => {
// check for a metar message and for unusable ios versions // check for a metar message and for regex lookbehind support
if (!observation?.rawMessage || (isIos && !iosVersionOk)) { if (!observation?.rawMessage || (!supportsRegexLookAheadLookBehind)) {
return observation; return observation;
} }

File diff suppressed because one or more lines are too long

View File

@@ -8127,7 +8127,7 @@ function friendlyDateTime(dateTimeish) {
} }
} }
const VERSION = "3.7.1"; const VERSION = "3.7.2";
export { DateTime, Duration, FixedOffsetZone, IANAZone, Info, Interval, InvalidZone, Settings, SystemZone, VERSION, Zone }; export { DateTime, Duration, FixedOffsetZone, IANAZone, Info, Interval, InvalidZone, Settings, SystemZone, VERSION, Zone };
//# sourceMappingURL=luxon.js.map //# sourceMappingURL=luxon.mjs.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,25 @@
import path from 'path';
// get values for devtools json
const uuid = 'd2bd1130-560f-4c8e-b2c5-e91073784964';
const root = path.resolve('server');
const DEVTOOLS_CONFIG = {
workspace: {
uuid,
root,
},
};
const devTools = (req, res) => {
// test for localhost
if (['127.0.0.1', '::1', '::ffff:127.0.0.1'].includes(req.ip)) {
console.log(DEVTOOLS_CONFIG);
res.json(DEVTOOLS_CONFIG);
} else {
// not localhost
res.status(404).send('File not found');
}
};
export default devTools;

View File

@@ -188,14 +188,22 @@
<div class='heading'>Headend Information</div> <div class='heading'>Headend Information</div>
<div id="divInfo"> <div id="divInfo">
Location: <span id="spanCity"></span> <span id="spanState"></span><br /> <div class="header">Location:</div>
Station Id: <span id="spanStationId"></span><br /> <div class="header"><span id="spanCity"></span> <span id="spanState"></span></div>
Radar Id: <span id="spanRadarId"></span><br /> <div class="header">Station Id:</div>
Zone Id: <span id="spanZoneId"></span><br /> <div class="header"><span id="spanStationId"></span></div>
Office Id: <span id="spanOfficeId"></span><br /> <div class="header">Radar Id:</div>
Grid X,Y: <span id="spanGridPoint"></span><br /> <div class="header"><span id="spanRadarId"></span></div>
Music: <span id="musicTrack">Not playing</span><br /> <div class="header">Zone Id:</div>
Ws4kp Version: <span><%- version %></span> <div class="header"><span id="spanZoneId"></span></div>
<div class="header">Office Id:</div>
<div class="header"><span id="spanOfficeId"></span></div>
<div class="header">Grid X,Y:</div>
<div class="header"><span id="spanGridPoint"></span></div>
<div class="header">Music:</div>
<div class="header"><span id="musicTrack">Not playing</span></div>
<div class="header">Ws4kp Version:</div>
<div class="header"><span><%- version %></span></div>
</div> </div>
</div> </div>

View File

@@ -73,7 +73,10 @@
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
} },
"cSpell.words": [
"hibyehihi"
]
}, },
"extensions": { "extensions": {
"recommendations": [ "recommendations": [