Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions api/skywarn.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
// JSON API for SkywarnPlus-NG alert line refresh (browser polling)
error_reporting(E_ERROR | E_WARNING | E_PARSE);
require_once('../include/apiInit.php');

header('Content-Type: application/json; charset=UTF-8');

if(!readOk()) {
echo json_encode(['error' => 'forbidden']);
exit;
}

if(strtolower($gCfg[skywarn_master_enable] ?? '') !== 'yes') {
echo json_encode(['error' => 'skywarn_disabled']);
exit;
}

echo json_encode(['html' => getSkywarnAlertHtml()]);
10 changes: 9 additions & 1 deletion cfg/CfgView.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ function showCfgs($cfgs) {
$hdrCols = ['ID', 'Name', 'Value', 'Default Value', 'Last Updated'];
$out = $html->tableOpen($hdrCols, null, 'favs', null);
$nullVal = '-';
$skywarnOn = strtolower($cfgs[skywarn_master_enable] ?? '') === 'yes';
foreach($cfgs as $id => $val) {
if(!$skywarnOn && ($id === skywarn_poll_enable || $id === skywarn_poll_minutes))
continue;
$name = $gCfgName[$id];
$updated = $gCfgUpdated[$id] ?? null;
if($updated) {
Expand Down Expand Up @@ -84,9 +87,14 @@ function showForms($cfg) {
echo htmlForm($form) . BR;
} else {
// Show Edit request form
global $gCfg;
$skywarnOn = strtolower($gCfg[skywarn_master_enable] ?? '') === 'yes';
$list = [];
foreach($gCfgName as $k => $name)
foreach($gCfgName as $k => $name) {
if(!$skywarnOn && ($k === skywarn_poll_enable || $k === skywarn_poll_minutes))
continue;
$list[$k] = $name;
}
$form->fieldsetLegend = EDIT_CFG;
$form->submit = [EDIT_CFG, DEFAULT_CFG];
$form->id = 'editCfgForm';
Expand Down
9 changes: 9 additions & 0 deletions cfg/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
h2("Configuration Parameters");
$view->showCfgs($gCfg);
p("Node Number and AMI Cfgs default to values in /etc/asterisk/ rpt.conf and manager.conf if not set here.");
p("SkywarnPlus cfgs integrate weather alerts from " . $html->a('https://github.com/hardenedpenguin/SkywarnPlus-NG', null, 'SkywarnPlus-NG', null, '_blank') . ". Enable and set API URL (e.g. http://localhost:8100). When SkywarnPlus is enabled, optional cfgs appear for polling that URL (default every 3 minutes) so the alert line updates without reloading the page.", 'w800', false);
$view->showForms($cfg ?? null);
echo '</div>' . BR. NL;

Expand Down Expand Up @@ -207,6 +208,14 @@ function processForm($Submit, $cfg, &$msg) {
} else {
// Currently none of the cfgs should need to contain html
$val = strip_tags($val);
if($id === skywarn_poll_minutes) {
$n = (int)trim($val);
if($n < 1 || $n > 1440) {
$msg[] = error('SkywarnPlus poll interval must be between 1 and 1440 minutes');
return;
}
$val = (string)$n;
}
// Convert array cfgs from csv / validate enumerated cfgs
if(is_array($gCfgDef[$id])) {
$val = csvToArray($val);
Expand Down
11 changes: 11 additions & 0 deletions css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,17 @@ font-size:12px;
background-color:#448;
color:#fff;
}
.skywarn-wrapper {
display:block;
width:100%;
clear:both;
margin:0.5em 0;
}
p.skywarn-simple {
margin:0.3em 0;
font-size:13px;
}

.greenborder {
border:solid 3px rgb(39,144,39);
border-radius:15px;
Expand Down
24 changes: 21 additions & 3 deletions include/CfgModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
define('amiuser', 10);
define('amipass', 11);
define('cmdbuttons', 12);
define('skywarn_master_enable', 13);
define('skywarn_api_url', 14);
define('skywarn_poll_enable', 15);
define('skywarn_poll_minutes', 16);

// Global Cfgs Default Values
$gCfgDef = [
Expand All @@ -32,7 +36,11 @@
amiport => '',
amiuser => '',
amipass => '',
cmdbuttons => []
cmdbuttons => [],
skywarn_master_enable => 'no',
skywarn_api_url => 'http://localhost:8100',
skywarn_poll_enable => 'yes',
skywarn_poll_minutes => '3'
];

$gCfgName = [
Expand All @@ -47,9 +55,15 @@
amiport => 'AMI Port',
amiuser => 'AMI User',
amipass => 'AMI Pass',
cmdbuttons => 'Custom Cmd Buttons'
cmdbuttons => 'Custom Cmd Buttons',
skywarn_master_enable => 'SkywarnPlus Enable',
skywarn_api_url => 'SkywarnPlus API URL',
skywarn_poll_enable => 'Poll SkywarnPlus API',
skywarn_poll_minutes => 'SkywarnPlus Poll Interval (minutes)'
];

$skywarnEnableVals = ['no' => 'Off', 'yes' => 'On'];

$publicPermissionVals = [
PERMISSION_NONE => 'None (No Access)',
PERMISSION_READ_ONLY => 'Read Only',
Expand All @@ -71,7 +85,11 @@
amiport => null,
amiuser => null,
amipass => null,
cmdbuttons => null
cmdbuttons => null,
skywarn_master_enable => $skywarnEnableVals,
skywarn_api_url => null,
skywarn_poll_enable => $skywarnEnableVals,
skywarn_poll_minutes => null
];

// Global Cfgs structure
Expand Down
46 changes: 46 additions & 0 deletions include/viewUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,52 @@ function showConnStatusTable() {
';
}

/**
* Fetch SkywarnPlus-NG status HTML (same output as main page alert line). For main page and api/skywarn.php.
*/
function getSkywarnAlertHtml() {
global $gCfg;
$apiUrl = trim($gCfg[skywarn_api_url] ?? '');
if(!$apiUrl) {
return 'SkyWarn+NG: API URL not configured';
}
$statusUrl = rtrim($apiUrl, '/') . '/api/status';
$ctx = stream_context_create(['http' => ['timeout' => 5]]);
$json = @file_get_contents($statusUrl, false, $ctx);
if($json === false) {
return 'SkyWarn+NG: API Offline';
}
$data = json_decode($json, true);
if(!is_array($data)) {
return 'SkyWarn+NG: API Error';
}
if(!empty($data['error'])) {
return 'SkyWarn+NG: ' . htmlspecialchars($data['error'], ENT_QUOTES, 'UTF-8');
}
if(empty($data['has_alerts']) || empty($data['alerts'])) {
return 'SkyWarn+NG: No Alerts';
}
$alerts = array_slice($data['alerts'], 0, 3);
$severityColors = [
'Extreme' => '#FF0000',
'Severe' => '#FF6600',
'Moderate'=> '#FFCC00',
'Minor' => '#FFFF00'
];
$alertParts = [];
foreach($alerts as $alert) {
$event = htmlspecialchars($alert['event'] ?? 'Unknown', ENT_QUOTES, 'UTF-8');
$severity = $alert['severity'] ?? 'Unknown';
$color = $severityColors[$severity] ?? '#FF0000';
$alertParts[] = '<span style="color: ' . $color . ';">' . $event . '</span>';
}
return 'SkyWarn+NG: ' . implode(', ', $alertParts);
}

function showSkywarnAlerts() {
echo '<p id="skywarn-msg" class="skywarn-simple">' . getSkywarnAlertHtml() . '</p>' . NL;
}

function showNodeCtrlForm() {
global $node, $remNode, $favsFile, $asdir, $gCfg;
if(modifyOk())
Expand Down
13 changes: 13 additions & 0 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@

showConnStatusTable();
showNodeCtrlForm();
if(strtolower($gCfg[skywarn_master_enable] ?? '') === 'yes') {
echo '<div class="skywarn-wrapper">';
showSkywarnAlerts();
echo '</div>';
if(strtolower($gCfg[skywarn_poll_enable] ?? '') === 'yes') {
$pollMins = (int)($gCfg[skywarn_poll_minutes] ?? 3);
if($pollMins < 1)
$pollMins = 1;
elseif($pollMins > 1440)
$pollMins = 1440;
echo '<script>initSkywarnPoll(' . $pollMins . ');</script>' . NL;
}
}

h2('Favorites');
// Read in favorites.ini
Expand Down
22 changes: 22 additions & 0 deletions js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,28 @@ function asInit(url) {
statsTmr = setTimeout(getStats, 250);
}

/**
* Poll SkywarnPlus-NG API via AllScan (mins = interval in minutes; default 3 from Cfgs).
*/
function initSkywarnPoll(mins) {
if(typeof fetch === 'undefined' || !mins || mins < 1)
return;
var ms = mins * 60 * 1000;
var url = apiDir + 'skywarn.php';
setInterval(function() {
fetch(url, {credentials: 'same-origin', cache: 'no-store'})
.then(function(r) { return r.json(); })
.then(function(d) {
if(d && d.html !== undefined) {
var el = document.getElementById('skywarn-msg');
if(el)
el.innerHTML = d.html;
}
})
.catch(function() {});
}, ms);
}

function initEventSrc() {
if(source) {
window.removeEventListener('beforeunload', closeEventSrc);
Expand Down
69 changes: 69 additions & 0 deletions skywarnplus-ng-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SkywarnPlus-NG support in AllScan

This guide explains how to turn on weather alerts from [SkywarnPlus-NG](https://github.com/hardenedpenguin/SkywarnPlus-NG) and adjust AllScan so the main page shows live alert text from your node’s SkywarnPlus API.

## What you need first

- **SkywarnPlus-NG** installed and running on your node (or another host on your network), exposing its HTTP API. A typical default is `http://localhost:8100` when SkywarnPlus-NG listens on the same machine as AllScan.
- **AllScan** with a working login; only **admin** users can change global configuration on the **Cfgs** tab.

AllScan does not install or configure SkywarnPlus-NG itself—it only **reads** the JSON status from the URL you set.

## Where to configure

1. Sign in to AllScan.
2. Open **Cfgs** (configuration management).
3. Use **Edit Cfg** to change the parameters below.

SkywarnPlus-related settings appear in the configuration table and in the **Cfg Name** dropdown when you edit settings.

## Main switch and API URL

| Setting (label in AllScan) | Purpose |
|----------------------------|---------|
| **SkywarnPlus Enable** | **Off** / **On**. When **On**, AllScan shows a Skywarn line on the **main** page and can optionally poll that API in the background. |
| **SkywarnPlus API URL** | Base URL of your SkywarnPlus-NG service (no path required). Example: `http://localhost:8100` or `http://192.168.1.50:8100`. AllScan appends `/api/status` to fetch alerts. |

**Defaults (new installs):** SkywarnPlus is **Off**; the API URL defaults to `http://localhost:8100` in the configuration model—set it to match your real service.

After you set **SkywarnPlus Enable** to **On** and save, reload the main AllScan page. You should see a line such as “SkyWarn+NG: …” above the Favorites section (or status messages if the API is unreachable).

## Polling options (visible when SkywarnPlus is enabled)

When **SkywarnPlus Enable** is **On**, two extra options appear in Cfgs:

| Setting | Purpose |
|---------|---------|
| **Poll SkywarnPlus API** | **Off** / **On**. When **On**, the browser asks AllScan periodically for updated alert text **without** reloading the whole page. When **Off**, alerts update only when you load or refresh the page. |
| **SkywarnPlus Poll Interval (minutes)** | How often to refresh the alert line when polling is **On**. Allowed range: **1–1440** minutes. **Default: 3** minutes. |

Polling uses your normal AllScan session; you must be allowed to use the site (same rules as viewing the main page).

## Quick setup checklist

1. Confirm SkywarnPlus-NG is running and reachable from the web server (e.g. `curl http://127.0.0.1:8100/api/status` on the node).
2. In AllScan **Cfgs**, set **SkywarnPlus API URL** to that base URL.
3. Set **SkywarnPlus Enable** to **On** and save.
4. Optional: leave **Poll SkywarnPlus API** **On** and set **SkywarnPlus Poll Interval (minutes)** as you like (default 3).
5. Open the main AllScan page and confirm the Skywarn line appears and updates (wait one poll interval if you enabled polling).

## Status messages you might see

AllScan shows plain-language status on the main page, for example:

- **API URL not configured** — Set **SkywarnPlus API URL** in Cfgs.
- **API Offline** — AllScan could not open the status URL (service down, wrong host/port, or firewall).
- **API Error** — The response was not valid JSON.
- **No Alerts** — The API responded successfully; there are no active alerts in the payload AllScan uses.
- Otherwise, up to three alert events may be listed with severity-based coloring.

## Resetting to defaults

Use **Edit Cfg**, choose the parameter, then **Set to Default Value** where available, or set:

- **SkywarnPlus Enable:** Off
- **SkywarnPlus API URL:** `http://localhost:8100`
- **Poll SkywarnPlus API:** On
- **SkywarnPlus Poll Interval (minutes):** 3

Save after changes.