Compare commits

...

15 Commits

Author SHA1 Message Date
Matt Walsh
878dea1ee6 4.1.4 2022-11-17 08:37:18 -06:00
Matt Walsh
b7c2ba22a4 keydown repeat 2022-11-17 08:36:41 -06:00
Matt Walsh
52efea0b67 run example in readme close #12 2022-09-05 19:54:35 -05:00
Matt Walsh
f50f8f558c update dependencies 2022-07-30 20:56:09 -05:00
Matt Walsh
16541dc21a eslint cleanup 2022-05-03 15:21:05 -05:00
Matt Walsh
93450932b4 update dependencies 2022-05-03 09:04:50 -05:00
Matt Walsh
a35d7280e4 update dependencies 2022-03-31 12:22:10 -05:00
Matt Walsh
ef6586f00a update dist 2022-03-01 16:21:19 -06:00
Matt Walsh
c68b6bddda 4.1.3 2022-03-01 16:19:22 -06:00
Matt Walsh
650ace0edb code cleanup 2022-03-01 16:19:08 -06:00
Matt Walsh
89d722d313 clean up eslint config 2022-03-01 16:04:20 -06:00
Matt Walsh
994b55530c fix dependency major version changes 2022-03-01 15:54:19 -06:00
Matt Walsh
d0509f14ae update dependencies 2022-03-01 15:40:43 -06:00
Matt Walsh
4c3ace6395 remove gulp-bump 2021-09-29 15:36:19 -05:00
Matt Walsh
3762b77d4c remove gulp-bump 2021-09-29 15:36:04 -05:00
20 changed files with 3437 additions and 4393 deletions

View File

@@ -22,27 +22,18 @@ module.exports = {
'no-tabs': 0,
'no-console': 0,
'max-len': 0,
'linebreak-style': 0,
quotes: [
'no-use-before-define': [
'error',
'single',
{
variables: false,
},
],
semi: [
'no-param-reassign': [
'error',
'always',
{
props: false,
},
],
'no-prototype-builtins': 0,
'comma-dangle': ['error', 'always-multiline'],
'block-scoped-var': ['error'],
'default-case': ['error'],
'default-param-last': ['error'],
'dot-location': ['error', 'property'],
eqeqeq: ['error'],
'no-eval': ['error'],
'no-eq-null': ['error'],
'no-floating-decimal': ['error'],
'no-trailing-spaces': ['error'],
'brace-style': [2, '1tbs', { allowSingleLine: true }],
'no-mixed-operators': [
'error',
{

6
.vscode/launch.json vendored
View File

@@ -36,5 +36,11 @@
"program": "${workspaceFolder}/index.js",
"outputCapture": "std",
}
],
"compounds": [
{
"name": "Compound",
"configurations": ["Frontend", "Server"]
}
]
}

View File

@@ -16,6 +16,16 @@ This project is based on the work of [Mike Battaglia](https://github.com/vbguyny
* [Icon](https://twcclassics.com/downloads.html) sets
* Countless photos and videos of WeatherStar 4000 forecasts used as references.
## Run Your WeatherStar
There are a lot of CORS considerations and issues with api.weather.gov that are easiest to deal with by running a local server to see this in action (or use the live link above). You'll need Node.js >12.0 to run the local server.
```
git clone https://github.com/netbymatt/ws4kp.git
cd ws4kp
node index.js
```
Open your web browser: http://localhost:8080/
## Why the fork?
The fork is a result of wanting a more manageable, modern code base to work with. Part of it is an exercise in my education in JavaScript. There are several technical changes that were made behind the scenes.

View File

@@ -6,14 +6,12 @@ const https = require('https');
// url parsing
const queryString = require('querystring');
// return an express router
module.exports = (req, res) => {
// add out-going headers
const headers = {};
headers['user-agent'] = '(WeatherStar 4000+, ws4000@netbymatt.com)';
headers['accept'] = req.headers.accept;
headers.accept = req.headers.accept;
// get query paramaters if the exist
const queryParams = Object.keys(req.query).reduce((acc, key) => {
@@ -22,16 +20,16 @@ module.exports = (req, res) => {
// add the paramter to the resulting object
acc[key] = req.query[key];
return acc;
},{});
}, {});
let query = queryString.encode(queryParams);
if (query.length > 0) query = '?' + query;
if (query.length > 0) query = `?${query}`;
// get the page
https.get('https://api.weather.gov' + req.path + query, {
https.get(`https://api.weather.gov${req.path}${query}`, {
headers,
}, getRes => {
}, (getRes) => {
// pull some info
const {statusCode} = getRes;
const { statusCode } = getRes;
// pass the status code through
res.status(statusCode);
@@ -39,8 +37,7 @@ module.exports = (req, res) => {
res.header('content-type', getRes.headers['content-type']);
// pipe to response
getRes.pipe(res);
}).on('error', e=>{
}).on('error', (e) => {
console.error(e);
});
};
};

2
dist/index.html vendored
View File

@@ -1 +1 @@
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"><head><meta charset="utf-8"><link rel="preload" href="fonts/Star4000.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star 4 Radar.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Extended.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Large Compressed.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Large.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Small.woff" as="font" crossorigin="anonymous"><title>WeatherStar 4000+</title><meta name="description" content="Web based WeatherStar 4000 simulator that reports current and forecast weather conditions plus a few extras!"><meta name="keywords" content="WeatherStar 4000+"><meta name="author" content="Matt Walsh"><meta name="application-name" content="WeatherStar 4000+"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><link rel="manifest" href="manifest.json"><link rel="icon" href="images/Logo192.png"><link rel="stylesheet" type="text/css" href="resources/ws.min.css?_=4.1.1"><script type="text/javascript" src="resources/data.min.js?_=4.1.1"></script><script type="text/javascript" src="resources/ws.min.js?_=4.1.1"></script></head><body><div id="divQuery"><form id="frmGetLatLng"><input id="txtAddress" type="text" value="" placeholder="Zip or City, State"><button id="btnGetGps" type="button" title="Get GPS Location"><img id="imgGetGps" src="images/nav/ic_gps_fixed_black_18dp_1x.png"></button> <input id="btnGetLatLng" type="submit" value="GO"> <input id="btnClearQuery" type="reset" value="Reset"></form><div id="divLat"></div><div id="divLng"></div></div><br><img id="imgPause1x" src="images/nav/ic_pause_white_24dp_1x.png"> <img id="imgPause2x" src="images/nav/ic_pause_white_24dp_2x.png"><div id="version" style="display:none">4.1.1</div><div id="divTwc"><div id="container"><div id="loading" width="640" height="480"><div><div class="title">WeatherStar 4000+</div><div class="instructions">Enter your location above to continue</div></div></div></div><div id="divTwcBottom"><div id="divTwcBottomLeft"><img id="NavigateMenu" class="navButton" src="images/nav/ic_menu_white_24dp_1x.png" title="Menu"> <img id="NavigatePrevious" class="navButton" src="images/nav/ic_skip_previous_white_24dp_1x.png" title="Previous"> <img id="NavigateNext" class="navButton" src="images/nav/ic_skip_next_white_24dp_1x.png" title="Next"> <img id="NavigatePlay" class="navButton" src="images/nav/ic_play_arrow_white_24dp_1x.png" title="Play"></div><div id="divTwcBottomMiddle"><img id="NavigateRefresh" class="navButton" src="images/nav/ic_refresh_white_24dp_1x.png" title="Refresh"></div><div id="divTwcBottomRight"><img id="ToggleFullScreen" class="navButton" src="images/nav/ic_fullscreen_exit_white_24dp_1x.png" title="Exit Fullscreen"></div></div></div><br><div class="info"><a href="https://github.com/netbymatt/ws4kp#weatherstar-4000">More information</a></div><div class="heading">Selected displays</div><div id="enabledDisplays"></div><div id="divInfo">Location: <span id="spanCity"></span> <span id="spanState"></span><br>Station Id: <span id="spanStationId"></span><br>Radar Id: <span id="spanRadarId"></span><br>Zone Id: <span id="spanZoneId"></span><br></div><div id="divRefresh">Last Update: <span id="spanLastRefresh">(None)</span><br><input id="chkAutoRefresh" name="chkAutoRefresh" type="checkbox"><label id="lblRefreshCountDown" for="chkAutoRefresh">Auto Refresh: <span id="spanRefreshCountDown">--:--</span></label></div><div id="divUnits">Units: <input id="radEnglish" name="radUnits" type="radio" value="ENGLISH"><label for="radEnglish">English</label> <input id="radMetric" name="radUnits" type="radio" value="METRIC"><label for="radMetric">Metric</label></div></body></html>
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"><head><meta charset="utf-8"><link rel="preload" href="fonts/Star4000.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star 4 Radar.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Extended.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Large Compressed.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Large.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Small.woff" as="font" crossorigin="anonymous"><title>WeatherStar 4000+</title><meta name="description" content="Web based WeatherStar 4000 simulator that reports current and forecast weather conditions plus a few extras!"><meta name="keywords" content="WeatherStar 4000+"><meta name="author" content="Matt Walsh"><meta name="application-name" content="WeatherStar 4000+"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><link rel="manifest" href="manifest.json"><link rel="icon" href="images/Logo192.png"><link rel="stylesheet" type="text/css" href="resources/ws.min.css?_=4.1.3"><script type="text/javascript" src="resources/data.min.js?_=4.1.3"></script><script type="text/javascript" src="resources/ws.min.js?_=4.1.3"></script></head><body><div id="divQuery"><form id="frmGetLatLng"><input id="txtAddress" type="text" value="" placeholder="Zip or City, State"><button id="btnGetGps" type="button" title="Get GPS Location"><img id="imgGetGps" src="images/nav/ic_gps_fixed_black_18dp_1x.png"></button> <input id="btnGetLatLng" type="submit" value="GO"> <input id="btnClearQuery" type="reset" value="Reset"></form><div id="divLat"></div><div id="divLng"></div></div><br><img id="imgPause1x" src="images/nav/ic_pause_white_24dp_1x.png"> <img id="imgPause2x" src="images/nav/ic_pause_white_24dp_2x.png"><div id="version" style="display:none">4.1.3</div><div id="divTwc"><div id="container"><div id="loading" width="640" height="480"><div><div class="title">WeatherStar 4000+</div><div class="instructions">Enter your location above to continue</div></div></div></div><div id="divTwcBottom"><div id="divTwcBottomLeft"><img id="NavigateMenu" class="navButton" src="images/nav/ic_menu_white_24dp_1x.png" title="Menu"> <img id="NavigatePrevious" class="navButton" src="images/nav/ic_skip_previous_white_24dp_1x.png" title="Previous"> <img id="NavigateNext" class="navButton" src="images/nav/ic_skip_next_white_24dp_1x.png" title="Next"> <img id="NavigatePlay" class="navButton" src="images/nav/ic_play_arrow_white_24dp_1x.png" title="Play"></div><div id="divTwcBottomMiddle"><img id="NavigateRefresh" class="navButton" src="images/nav/ic_refresh_white_24dp_1x.png" title="Refresh"></div><div id="divTwcBottomRight"><img id="ToggleFullScreen" class="navButton" src="images/nav/ic_fullscreen_exit_white_24dp_1x.png" title="Exit Fullscreen"></div></div></div><br><div class="info"><a href="https://github.com/netbymatt/ws4kp#weatherstar-4000">More information</a></div><div class="heading">Selected displays</div><div id="enabledDisplays"></div><div id="divInfo">Location: <span id="spanCity"></span> <span id="spanState"></span><br>Station Id: <span id="spanStationId"></span><br>Radar Id: <span id="spanRadarId"></span><br>Zone Id: <span id="spanZoneId"></span><br></div><div id="divRefresh">Last Update: <span id="spanLastRefresh">(None)</span><br><input id="chkAutoRefresh" name="chkAutoRefresh" type="checkbox"><label id="lblRefreshCountDown" for="chkAutoRefresh">Auto Refresh: <span id="spanRefreshCountDown">--:--</span></label></div><div id="divUnits">Units: <input id="radEnglish" name="radUnits" type="radio" value="ENGLISH"><label for="radEnglish">English</label> <input id="radMetric" name="radUnits" type="radio" value="METRIC"><label for="radMetric">Metric</label></div></body></html>

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,4 @@
/* eslint-disable import/no-extraneous-dependencies */
const fs = require('fs');
const gulp = require('gulp');
const concat = require('gulp-concat');
const terser = require('gulp-terser');

View File

@@ -1,17 +1,4 @@
const gulp = require('gulp');
const bump = require('gulp-bump');
gulp.task('update-vendor', require('./gulp/update-vendor'));
gulp.task('publish-frontend', require('./gulp/publish-frontend'));
gulp.task('bump_patch', () => gulp.src('./package.json')
.pipe(bump())
.pipe(gulp.dest('./')));
gulp.task('bump_minor', () => gulp.src('./package.json')
.pipe(bump({ type: 'minor' }))
.pipe(gulp.dest('./')));
gulp.task('bump_major', () => gulp.src('./package.json')
.pipe(bump({ type: 'major' }))
.pipe(gulp.dest('./')));

5698
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "ws4kp",
"version": "4.1.2",
"version": "4.1.4",
"description": "Welcome to the WeatherStar 4000+ project page!",
"main": "index.js",
"scripts": {
@@ -19,12 +19,11 @@
"devDependencies": {
"del": "^6.0.0",
"ejs": "^3.1.5",
"eslint": "^7.17.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint": "^8.10.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.22.1",
"express": "^4.17.1",
"gulp": "^4.0.2",
"gulp-bump": "^3.2.0",
"gulp-clean-css": "^4.3.0",
"gulp-concat": "^2.6.1",
"gulp-ejs": "^5.1.0",
@@ -34,7 +33,7 @@
"gulp-terser": "^2.0.0",
"jquery": "^3.6.0",
"jquery-touchswipe": "^1.6.19",
"luxon": "^1.25.0",
"luxon": "^3.0.0",
"nosleep.js": "^0.12.0",
"suncalc": "^1.8.0",
"swiped-events": "^1.1.4"

View File

@@ -354,6 +354,10 @@ const index = (() => {
const documentKeydown = (e) => {
const code = (e.keyCode || e.which);
// 200ms repeat
if ((Date.now() - documentKeydown.lastButton ?? 0) < 200) return false;
documentKeydown.lastButton = Date.now();
if (document.fullscreenElement || document.activeElement === document.body) {
switch (code) {
case 32: // Space

View File

@@ -247,6 +247,27 @@ class Almanac extends WeatherDisplay {
const [FullMoonImage, LastMoonImage, NewMoonImage, FirstMoonImage] = await Promise.all(this.moonImages);
switch (this.screenIndex) {
case 1: {
this.context.drawImage(await this.backgroundImage1, 0, 0);
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90);
draw.horizontalGradientSingle(this.context, 0, 90, 52, 399, draw.sideColor1, draw.sideColor2);
draw.horizontalGradientSingle(this.context, 584, 90, 640, 399, draw.sideColor1, draw.sideColor2);
draw.titleText(this.context, 'Almanac', 'Outlook');
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 320, 180, '30 Day Outlook', 2, 'center');
const DateRange = `MID-${info.outlook.thisMonth.toUpperCase()} TO MID-${info.outlook.nextMonth.toUpperCase()}`;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 320, 220, DateRange, 2, 'center');
const Temperature = info.outlook.temperature;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 300, `Temperatures: ${Temperature}`, 2);
const Precipitation = info.outlook.precipitation;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 380, `Precipitation: ${Precipitation}`, 2);
break;
}
case 0:
default:
// sun and moon data
@@ -292,27 +313,6 @@ class Almanac extends WeatherDisplay {
this.context.drawImage(image, 75 + Index * 130, 270);
});
break;
case 1: {
this.context.drawImage(await this.backgroundImage1, 0, 0);
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90);
draw.horizontalGradientSingle(this.context, 0, 90, 52, 399, draw.sideColor1, draw.sideColor2);
draw.horizontalGradientSingle(this.context, 584, 90, 640, 399, draw.sideColor1, draw.sideColor2);
draw.titleText(this.context, 'Almanac', 'Outlook');
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 320, 180, '30 Day Outlook', 2, 'center');
const DateRange = `MID-${info.outlook.thisMonth.toUpperCase()} TO MID-${info.outlook.nextMonth.toUpperCase()}`;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 320, 220, DateRange, 2, 'center');
const Temperature = info.outlook.temperature;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 300, `Temperatures: ${Temperature}`, 2);
const Precipitation = info.outlook.precipitation;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 380, `Precipitation: ${Precipitation}`, 2);
}
}
this.finishDraw();

View File

@@ -24,6 +24,7 @@ class CurrentWeather extends WeatherDisplay {
stationNum += 1;
try {
// station observations
// eslint-disable-next-line no-await-in-loop
observations = await utils.fetch.json(`${station.id}/observations`, {
cors: true,
data: {
@@ -38,8 +39,6 @@ class CurrentWeather extends WeatherDisplay {
observations = undefined;
throw new Error(`Unable to get observations: ${station.properties.stationIdentifier}, trying next station`);
}
// TODO: add retry for further stations if observations are unavailable
} catch (e) {
console.error(e);
}

View File

@@ -74,7 +74,6 @@ const draw = (() => {
context.strokeRect(x, y, width, height);
};
// TODO: implement full themes support
const theme = 1; // classic
const topColor1 = 'rgb(192, 91, 2)';
const topColor2 = 'rgb(72, 34, 64)';

View File

@@ -36,9 +36,9 @@ class Radar extends WeatherDisplay {
this.backgroundImage = utils.image.load('images/BackGround4_1.png');
}
async getData(weatherParameters) {
super.getData(weatherParameters);
if (!weatherParameters) weatherParameters = this.weatherParameters;
async getData(_weatherParameters) {
super.getData(_weatherParameters);
const weatherParameters = _weatherParameters ?? this.weatherParameters;
// ALASKA AND HAWAII AREN'T SUPPORTED!
if (weatherParameters.state === 'AK' || weatherParameters.state === 'HI') {
@@ -152,6 +152,7 @@ class Radar extends WeatherDisplay {
day,
hour,
minute,
}, {
zone: 'UTC',
}).setZone();
} else {

View File

@@ -256,8 +256,8 @@ const utils = (() => {
let corsUrl = _url;
if (params.cors === true) corsUrl = rewriteUrl(_url);
const url = new URL(corsUrl);
// force url to secure
url.protocol = 'https:';
// match the security protocol
url.protocol = window.location.protocol;
// add parameters if necessary
if (params.data) {
Object.keys(params.data).forEach((key) => {

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -125,6 +125,7 @@ function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
// returns set time for the given sun altitude
function getSetJ(h, lw, phi, dec, n, M, L) {
@@ -135,13 +136,18 @@ function getSetJ(h, lw, phi, dec, n, M, L) {
}
// calculates sun times for a given date and latitude/longitude
// calculates sun times for a given date, latitude/longitude, and, optionally,
// the observer height (in meters) relative to the horizon
SunCalc.getTimes = function (date, lat, lng) {
SunCalc.getTimes = function (date, lat, lng, height) {
height = height || 0;
var lw = rad * -lng,
phi = rad * lat,
dh = observerAngle(height),
d = toDays(date),
n = julianCycle(d, lw),
ds = approxTransit(0, lw, n),
@@ -152,7 +158,7 @@ SunCalc.getTimes = function (date, lat, lng) {
Jnoon = solarTransitJ(ds, M, L),
i, len, time, Jset, Jrise;
i, len, time, h0, Jset, Jrise;
var result = {
@@ -162,8 +168,9 @@ SunCalc.getTimes = function (date, lat, lng) {
for (i = 0, len = times.length; i < len; i += 1) {
time = times[i];
h0 = (time[0] + dh) * rad;
Jset = getSetJ(time[0] * rad, lw, phi, dec, n, M, L);
Jset = getSetJ(h0, lw, phi, dec, n, M, L);
Jrise = Jnoon - (Jset - Jnoon);
result[time[1]] = fromJulian(Jrise);

View File

@@ -75,6 +75,7 @@
var eventData = {
dir: eventType.replace(/swiped-/, ''),
touchType: (changedTouches[0] || {}).touchType || 'direct',
xStart: parseInt(xDown, 10),
xEnd: parseInt((changedTouches[0] || {}).clientX || -1, 10),
yStart: parseInt(yDown, 10),