diff --git a/server/datahandler.js b/server/datahandler.js
index b06cec3..6bfbf91 100644
--- a/server/datahandler.js
+++ b/server/datahandler.js
@@ -109,7 +109,8 @@ const callbacks = {
), 'callback_af*'),
ecc: koffi.register(rds => (
- value = rdsparser.get_ecc(rds)
+ value = rdsparser.get_ecc(rds),
+ dataToSend.ecc = value
), 'callback_ecc*'),
country: koffi.register(rds => (
@@ -218,6 +219,7 @@ var dataToSend = {
ta: 0,
ms: -1,
pty: 0,
+ ecc: null,
af: [],
rt0: '',
rt1: '',
diff --git a/server/endpoints.js b/server/endpoints.js
index 5e71f3f..2d4adb4 100644
--- a/server/endpoints.js
+++ b/server/endpoints.js
@@ -4,6 +4,7 @@ const router = express.Router();
const fs = require('fs');
const { SerialPort } = require('serialport')
const path = require('path');
+const https = require('https');
// File Imports
const { parseAudioDevice } = require('./stream/parser');
@@ -124,7 +125,7 @@ router.get('/setup', (req, res) => {
});
router.get('/rds', (req, res) => {
- res.send('Please connect using a WebSocket compatible app to obtain RDS stream.');
+ res.send('Please c onnect using a WebSocket compatible app to obtain RDS stream.');
});
router.get('/rdsspy', (req, res) => {
@@ -258,5 +259,63 @@ router.get('/ping', (req, res) => {
res.send('pong');
});
+router.get('/log_fmlist', (req, res) => {
+ const clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
+ const postData = JSON.stringify({
+ station: {
+ freq: dataHandler.dataToSend.freq,
+ pi: dataHandler.dataToSend.pi,
+ id: dataHandler.dataToSend.txInfo.id,
+ rds_ps: dataHandler.dataToSend.ps,
+ signal: dataHandler.dataToSend.sig,
+ tp: dataHandler.dataToSend.tp,
+ ta: dataHandler.dataToSend.ta,
+ af_list: dataHandler.dataToSend.af,
+ },
+ server: {
+ uuid: serverConfig.identification.token,
+ latitude: serverConfig.identification.lat,
+ longitude: serverConfig.identification.lon,
+ address: serverConfig.identification.proxyIp.length > 1 ? serverConfig.identification.proxyIp : ('Matches request IP with port ' + serverConfig.webserver.port),
+ webserver_name: serverConfig.identification.tunerName,
+ },
+ client: {
+ request_ip: clientIp
+ },
+ log_msg: `PS: ${dataHandler.dataToSend.ps}, PI: ${dataHandler.dataToSend.pi}, Signal: ${dataHandler.dataToSend.sig.toFixed(0)} dBf`
+ });
+
+ const options = {
+ hostname: 'api.fmlist.org',
+ path: '/fmdx.org/slog.php',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Content-Length': postData.length,
+ }
+ };
+
+ const request = https.request(options, (response) => {
+ let data = '';
+
+ response.on('data', (chunk) => { // Collect the response data
+ data += chunk;
+ });
+
+ response.on('end', () => {
+ res.status(200).send(data);
+ });
+ });
+
+ request.on('error', (error) => {
+ console.error('Error sending POST request:', error);
+ res.status(500).send(error);
+ });
+
+ request.write(postData);
+ request.end();
+});
+
+
module.exports = router;
diff --git a/server/tx_search.js b/server/tx_search.js
index 3a20dcc..c03f872 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -61,7 +61,8 @@ function processData(data, piCode, rdsPs) {
if (esMode && (distance.distanceKm > 500)) {
weightDistance = Math.abs(distance.distanceKm-1500);
}
- const score = (10*Math.log10(station.erp*1000)) / weightDistance; // Calculate score
+ let erp = (station.erp && station.erp > 0) ? station.erp : 1;
+ const score = (10*Math.log10(erp*1000)) / weightDistance; // Calculate score
if (score > maxScore) {
maxScore = score;
txAzimuth = distance.azimuth;
@@ -78,7 +79,7 @@ function processData(data, piCode, rdsPs) {
return {
station: matchingStation.station.replace("R.", "Radio "),
pol: matchingStation.pol.toUpperCase(),
- erp: matchingStation.erp,
+ erp: (matchingStation.erp && matchingStation.erp > 0) ? matchingStation.erp : '?',
city: matchingCity.name,
itu: matchingCity.itu,
distance: maxDistance.toFixed(0),
diff --git a/web/css/breadcrumbs.css b/web/css/breadcrumbs.css
index d3e1c65..0338bec 100644
--- a/web/css/breadcrumbs.css
+++ b/web/css/breadcrumbs.css
@@ -63,7 +63,6 @@ label {
font-weight: bold;
text-transform: uppercase;
display: block;
- text-align: left;
color: var(--color-4);
}
diff --git a/web/css/dropdown.css b/web/css/dropdown.css
index 86657cf..d6e133c 100644
--- a/web/css/dropdown.css
+++ b/web/css/dropdown.css
@@ -4,6 +4,7 @@
background: var(--color-4);
position: relative;
margin-right: 20px;
+ border-radius: 15px;
/*border-bottom: 4px solid var(--color-2);*/
}
@media (max-width: 400px) {
@@ -47,9 +48,13 @@
background-color: var(--color-main-bright);
}
- .dropdown.opened > input {
+ .dropdown.opened.dropdown-up > input {
border-radius: 0 0 15px 15px;
}
+
+ .dropdown.opened > input {
+ border-radius: 15px 15px 0 0;
+ }
.dropdown input:focus {
outline: none;
}
diff --git a/web/css/entry.css b/web/css/entry.css
index f08ac8d..cfb7f69 100644
--- a/web/css/entry.css
+++ b/web/css/entry.css
@@ -3,6 +3,7 @@
@import url("main.css"); /* Root stuff that affects the entire webpage (body, wrapper etc.) */
@import url("breadcrumbs.css"); /* Stuff that applies to random elements only once/twice */
@import url("buttons.css"); /* Buttons, inputs, select boxes, checkboxes... */
+@import url("toast.css"); /* Toast notifications */
@import url("dropdown.css"); /* Custom dropdown menus */
@import url("panels.css"); /* Different panels and their sizes */
@import url("modal.css"); /* Modal window */
diff --git a/web/index.ejs b/web/index.ejs
index 142d7b9..82236da 100644
--- a/web/index.ejs
+++ b/web/index.ejs
@@ -124,7 +124,7 @@
<% if (antennas.enabled == true) { %>
-
+
<% if(antennas.ant1.enabled == true) { %>- <%= antennas.ant1.name %>
<% } %>
@@ -164,7 +164,7 @@
<% } %>
<% if (device == 'tef' && bwSwitch == true) { %>
-
+
-
+
<% if (!noPlugins) { %>
diff --git a/web/js/dropdown.js b/web/js/dropdown.js
index 767f195..a35e99d 100644
--- a/web/js/dropdown.js
+++ b/web/js/dropdown.js
@@ -24,7 +24,7 @@ $(document).ready(function() {
switch($currentDropdown.attr('id')) {
case 'data-ant':
socket.send("Z" + $(event.currentTarget).attr('data-value'));
- tuneTo(getCurrentFreq()); // Reset RDS when change antenna input
+ resetRDS(getCurrentFreq()); // Reset RDS when change antenna input
break;
case 'data-bw':
legacyBwValue = $(event.currentTarget).attr('data-value2') || "";
diff --git a/web/js/main.js b/web/js/main.js
index cefecf5..27d684b 100644
--- a/web/js/main.js
+++ b/web/js/main.js
@@ -240,7 +240,7 @@ function sendPingRequest() {
socket = new WebSocket(socketAddress);
socket.onopen = () => {
- console.log("Main/UI reconnected successfully.");
+ sendToast('info', 'Connected', 'Reconnected successfully!', false, false);
};
socket.onmessage = (event) => {
handleWebSocketMessage(event);
@@ -421,18 +421,31 @@ function updateCanvas(parsedData, signalChart) {
}, 1000 / 15);
}
+let reconnectTimer = null;
+let dataTimeout = null;
+const TIMEOUT_DURATION = 5000; // 5 seconds timeout for lost connection
+
+const resetDataTimeout = () => {
+ clearTimeout(dataTimeout);
+ dataTimeout = setTimeout(() => {
+ sendToast('warning', 'Connection lost', 'Attempting to reconnect...', false, false);
+ }, TIMEOUT_DURATION);
+};
+
socket.onmessage = (event) => {
- if (event.data == 'KICK') {
- console.log('Kick initiated.')
+ if (event.data === 'KICK') {
+ console.log('Kick initiated.');
setTimeout(() => {
- window.location.href = '/403';
+ window.location.href = '/403';
}, 500);
return;
}
parsedData = JSON.parse(event.data);
+ resetDataTimeout();
updatePanels(parsedData);
+
const sum = signalData.reduce((acc, strNum) => acc + parseFloat(strNum), 0);
const averageSignal = sum / signalData.length;
data.push(averageSignal);
@@ -582,6 +595,10 @@ function tuneTo(freq) {
socket.send("T" + ((freq).toFixed(1) * 1000));
}
+function resetRDS(freq) {
+ socket.send("T" + ((freq).toFixed(3) * 1000));
+}
+
async function copyPs() {
var frequency = $('#data-frequency').text();
var pi = $('#data-pi').text();
@@ -653,11 +670,13 @@ function copyToClipboard(textToCopy) {
}
function findOnMaps() {
- var frequency = parseFloat($('#data-frequency').text()).toFixed(1);
+ var frequency = parseFloat($('#data-frequency').text());
var pi = $('#data-pi').text();
var latitude = localStorage.getItem('qthLongitude');
var longitude = localStorage.getItem('qthLatitude');
+ frequency > 74 ? frequency = frequency.toFixed(1) : null;
+
var url = `https://maps.fmdx.org/#qth=${longitude},${latitude}&freq=${frequency}&findPi=${pi}`;
window.open(url, "_blank");
}
@@ -991,3 +1010,17 @@ function fillPresets() {
});
}
}
+
+//FMLIST logging
+$('#log-fmlist').on('click', function() {
+ $.ajax({
+ url: './log_fmlist',
+ method: 'GET',
+ success: function(response) {
+ sendToast('success', 'Log successful', response, false, true);
+ },
+ error: function(xhr) {
+ sendToast('error', 'Log failed', xhr.statusText, false, true);
+ }
+ });
+});
diff --git a/web/js/settings.js b/web/js/settings.js
index d156332..df997f6 100644
--- a/web/js/settings.js
+++ b/web/js/settings.js
@@ -79,7 +79,9 @@ $(document).ready(() => {
data: $(this).serialize(),
success: function (data) {
// Update the content on the page with the message from the response
- $('#login-message').text(data.message);
+ sendToast('success', 'Login success!', data.message, false, true);
+
+ //$('#login-message').text(data.message);
setTimeout(function () {
location.reload(true);
}, 1750);
@@ -88,7 +90,7 @@ $(document).ready(() => {
// Handle error response
if (xhr.status === 403) {
// Update the content on the page with the message from the error response
- $('#login-message').text(xhr.responseJSON.message);
+ sendToast('error', 'Login failed!', xhr.responseJSON.message, false, true);
} else {
// Handle other types of errors if needed
console.error('Error:', status, error);
@@ -105,7 +107,7 @@ $(document).ready(() => {
type: 'GET', // Assuming the logout is a GET request, adjust accordingly
url: './logout',
success: function (data) {
- $('#login-message').text(data.message);
+ sendToast('success', 'Logout success!', data.message, false, true);
setTimeout(function () {
location.reload(true);
}, 1000);
@@ -114,7 +116,7 @@ $(document).ready(() => {
// Handle error response
if (xhr.status === 403) {
// Update the content on the page with the message from the error response
- $('#login-message').text(xhr.responseJSON.message);
+ sendToast('error', 'Logout failed!', xhr.responseJSON.message, false, true);
} else {
// Handle other types of errors if needed
console.error('Error:', status, error);
diff --git a/web/js/webserver.js b/web/js/webserver.js
index 6484944..c2ac43e 100644
--- a/web/js/webserver.js
+++ b/web/js/webserver.js
@@ -2,4 +2,5 @@ $.getScript('./js/main.js');
$.getScript('./js/dropdown.js');
$.getScript('./js/modal.js');
$.getScript('./js/settings.js');
-$.getScript('./js/chat.js');
\ No newline at end of file
+$.getScript('./js/chat.js');
+$.getScript('./js/toast.js');
\ No newline at end of file
diff --git a/web/setup.ejs b/web/setup.ejs
index 26aa86e..e6a7010 100644
--- a/web/setup.ejs
+++ b/web/setup.ejs
@@ -12,6 +12,7 @@
+
<% if (isAdminAuthenticated) { %>
diff --git a/web/wizard.ejs b/web/wizard.ejs
index e9fe5e2..6bc46a0 100644
--- a/web/wizard.ejs
+++ b/web/wizard.ejs
@@ -12,6 +12,7 @@
+
<% if (isAdminAuthenticated) { %>